mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
MMC core:
- Fix ABI regression of MMC BLK ioctl - Remove the unused MMC_DATA_STREAM flag - Enable asynchronous system PM for the host device - Minor fixes and clean-ups SDHCI host: Throughout the years, the numbers of SDHCI variants have increased and so has also the numbers of SDHCI callbacks/quirks. The purpose of these callbacks/quirks were to enable SDHCI to deal with variant specific requirements, but unfortunate this method didn't scale. Instead we have ended up with a mess. Not only did the code become suboptimal but also highly fragile. Lately many discussions of how to move forward with SDHCI has taken place at the MMC mailing list. Step by step, we aim to turn SDHCI's common code into a set of library functions. This will enable for optimizations and allow some of the existing callbacks/quirks to be removed, which also should help to make the code less fragile. Therefore I am also really pleased to announce that Adrian Hunter (Intel) has volunteered to step in as the maintainer for SDHCI. Future wise, I hope the community around SDHCI will continue to grow and that this release cycle can be the starting point of moving SDHCI into a better shape. As a matter of fact, already in this cycle the re-factoring has begun, but of course there are also fixes and new features included. Some highlights: - sdhci-iproc: Add support for Broadcom's BCM2835 eMMC IP - sdhci-acpi: Add support for QCOM controllers - sdhci-pic32: Add new SDHCI variant for PIC32MZDA Other hosts: - atmel-mci: Fix a NULL pointer dereference - mediatek: Add SD write-protect support - mmc_spi: Fix card detect in GPIO case - tmio/sdhi: Add r8a7795 support - tmio/sdhi: Some fixes and clean-ups - dw_mmc: Add HW reset support - dw_mmc: Some fixes and clean-ups - sunxi: Add support for MMC DDR52 mode -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJW7+1kAAoJEP4mhCVzWIwpFREP+wcm8CLPoIz0VamZ8wocU5/S NK+EINCrVF4AbvcTj78A/FE099P5e3kXW84cZP5fCGSk7MvTizlIP0kDT73XkYbf FV1xQYAq5E9PtWfswyLqEiYcHVFzwhxwHRy8pXnJ7bummM8OvZYSnao4kdxzW3Mr j2lELoUVYma5hnoXBMrq04kjwPukdrFXYMz4BulE0aoCrfp2+CoKcDbykh97zWbu B7ta2NG5EuszXepgsAPSGbFDuFXJ4EeDtZNzowom6ifkKusNa+42lOyBA+WiHBZ6 kfSOyRHJ/URuGifir7hCXLvr2GVpFF3d399olDawCXEVCIhkrZ4SKJ25kLdABz9E IrikhpBlRIhquAW4clNnPNYWqGTsOT1Coj+6E/9XUHa8C8gGEV7W3j8nNzMCT2O4 GMynfyXKIJ6JnuM03qqv3kmATmbRI3fulR6OTNObgAWSGeZLuxa1bIYDVXhtr2nQ Spl4nViOnZdfdNz4LkqS6vuGKscKViWFtt/wLi+gnKtbGfffb8CGrucd13vQ7fiK OP9WZoWLQ+YlItXDee02vn4ccH463KjIIKJdLsvmSHz+lfPnXRI2KR37+j7sczvo V3msYYkbjB1hghAunR94Zc3RXwV28R1/mqcOGzXv2A+Q1J4g//Q+gS8DGzXzlaRV 58OJRqiyTWMktZ0TZM0V =MFMb -----END PGP SIGNATURE----- Merge tag 'mmc-v4.6' of git://git.linaro.org/people/ulf.hansson/mmc Pull MMC updates from Ulf Hansson: "MMC core: - Fix ABI regression of MMC BLK ioctl - Remove the unused MMC_DATA_STREAM flag - Enable asynchronous system PM for the host device - Minor fixes and clean-ups SDHCI host: Throughout the years, the numbers of SDHCI variants have increased and so has also the numbers of SDHCI callbacks/quirks. The purpose of these callbacks/quirks were to enable SDHCI to deal with variant specific requirements, but unfortunate this method didn't scale. Instead we have ended up with a mess. Not only did the code become suboptimal but also highly fragile. Lately many discussions of how to move forward with SDHCI has taken place at the MMC mailing list. Step by step, we aim to turn SDHCI's common code into a set of library functions. This will enable for optimizations and allow some of the existing callbacks and quirks to be removed, which also should help to make the code less fragile. Therefore I am also really pleased to announce that Adrian Hunter (Intel) has volunteered to step in as the maintainer for SDHCI. Future wise, I hope the community around SDHCI will continue to grow and that this release cycle can be the starting point of moving SDHCI into a better shape. As a matter of fact, already in this cycle the re-factoring has begun, but of course there are also fixes and new features included. Some highlights: - sdhci-iproc: Add support for Broadcom's BCM2835 eMMC IP - sdhci-acpi: Add support for QCOM controllers - sdhci-pic32: Add new SDHCI variant for PIC32MZDA Other hosts: - atmel-mci: Fix a NULL pointer dereference - mediatek: Add SD write-protect support - mmc_spi: Fix card detect in GPIO case - tmio/sdhi: Add r8a7795 support - tmio/sdhi: Some fixes and clean-ups - dw_mmc: Add HW reset support - dw_mmc: Some fixes and clean-ups - sunxi: Add support for MMC DDR52 mode" * tag 'mmc-v4.6' of git://git.linaro.org/people/ulf.hansson/mmc: (123 commits) mmc: sdhci-of-at91: fix wake-up issue when using runtime pm mmc: sdhci-pci: Do not set DMA mask in enable_dma() mmc: sdhci-acpi: Remove enable_dma() hook mmc: sdhci: Set DMA mask when adding host mmc: block: fix ABI regression of mmc_blk_ioctl mmc: atmel-mci: Check pdata for NULL before dereferencing it at DMA config mmc: core: remove redundant memset of sdio_read_cccr mmc: core: remove redundant memset of mmc_decode_cid mmc: of_mmc_spi: fix unused warning mmc: sdhci-of-arasan: add phy support for sdhci-of-arasan mmc: sdhci-of-arasan: fix missing sdhci_pltfm_free for err handling mmc: sdhci-of-arasan: remove disable clk_ahb from sdhci_arasan_resume Documentation: bindings: add description of phy for sdhci-of-arasan mmc: sdhci: Fix override of timeout clk wrt max_busy_timeout mmc: mmci: Remove unnecessary header file mmc: sdhci-acpi: add QCOM controllers mmc: tegra: implement memcomp pad calibration mmc: mediatek: Use mmc_regulator_set_vqmmc in start_signal_voltage_switch mmc: mediatek: Change signal voltage error to dev_dbg() mmc: sh_mmcif, tmio: Use ARCH_RENESAS ...
This commit is contained in:
commit
e531cdf50a
@ -1,11 +1,12 @@
|
||||
Device Tree Bindings for the Arasan SDHCI Controller
|
||||
|
||||
The bindings follow the mmc[1], clock[2] and interrupt[3] bindings. Only
|
||||
deviations are documented here.
|
||||
The bindings follow the mmc[1], clock[2], interrupt[3] and phy[4] bindings.
|
||||
Only deviations are documented here.
|
||||
|
||||
[1] Documentation/devicetree/bindings/mmc/mmc.txt
|
||||
[2] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
[3] Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
[4] Documentation/devicetree/bindings/phy/phy-bindings.txt
|
||||
|
||||
Required Properties:
|
||||
- compatible: Compatibility string. Must be 'arasan,sdhci-8.9a' or
|
||||
@ -17,6 +18,10 @@ Required Properties:
|
||||
- interrupt-parent: Phandle for the interrupt controller that services
|
||||
interrupts for this device.
|
||||
|
||||
Required Properties for "arasan,sdhci-5.1":
|
||||
- phys: From PHY bindings: Phandle for the Generic PHY for arasan.
|
||||
- phy-names: MUST be "phy_arasan".
|
||||
|
||||
Example:
|
||||
sdhci@e0100000 {
|
||||
compatible = "arasan,sdhci-8.9a";
|
||||
@ -26,3 +31,14 @@ Example:
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <0 24 4>;
|
||||
} ;
|
||||
|
||||
sdhci@e2800000 {
|
||||
compatible = "arasan,sdhci-5.1";
|
||||
reg = <0xe2800000 0x1000>;
|
||||
clock-names = "clk_xin", "clk_ahb";
|
||||
clocks = <&cru 8>, <&cru 18>;
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <0 24 4>;
|
||||
phys = <&emmc_phy>;
|
||||
phy-names = "phy_arasan";
|
||||
} ;
|
||||
|
@ -4,7 +4,10 @@ This file documents differences between the core properties described
|
||||
by mmc.txt and the properties that represent the IPROC SDHCI controller.
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "brcm,sdhci-iproc-cygnus".
|
||||
- compatible : Should be one of the following
|
||||
"brcm,bcm2835-sdhci"
|
||||
"brcm,sdhci-iproc-cygnus"
|
||||
|
||||
- clocks : The clock feeding the SDHCI controller.
|
||||
|
||||
Optional properties:
|
||||
|
@ -0,0 +1,29 @@
|
||||
* Microchip PIC32 SDHCI Controller
|
||||
|
||||
This file documents differences between the core properties in mmc.txt
|
||||
and the properties used by the sdhci-pic32 driver.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "microchip,pic32mzda-sdhci"
|
||||
- interrupts: Should contain interrupt
|
||||
- clock-names: Should be "base_clk", "sys_clk".
|
||||
See: Documentation/devicetree/bindings/resource-names.txt
|
||||
- clocks: Phandle to the clock.
|
||||
See: Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
- pinctrl-names: A pinctrl state names "default" must be defined.
|
||||
- pinctrl-0: Phandle referencing pin configuration of the SDHCI controller.
|
||||
See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
|
||||
|
||||
Example:
|
||||
|
||||
sdhci@1f8ec000 {
|
||||
compatible = "microchip,pic32mzda-sdhci";
|
||||
reg = <0x1f8ec000 0x100>;
|
||||
interrupts = <191 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&REFCLKO4>, <&PBCLK5>;
|
||||
clock-names = "base_clk", "sys_clk";
|
||||
bus-width = <4>;
|
||||
cap-sd-highspeed;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_sdhc1>;
|
||||
};
|
@ -22,6 +22,7 @@ Required properties:
|
||||
"renesas,sdhi-r8a7792" - SDHI IP on R8A7792 SoC
|
||||
"renesas,sdhi-r8a7793" - SDHI IP on R8A7793 SoC
|
||||
"renesas,sdhi-r8a7794" - SDHI IP on R8A7794 SoC
|
||||
"renesas,sdhi-r8a7795" - SDHI IP on R8A7795 SoC
|
||||
|
||||
Optional properties:
|
||||
- toshiba,mmc-wrprotect-disable: write-protect detection is unavailable
|
||||
|
@ -9836,10 +9836,12 @@ S: Maintained
|
||||
F: drivers/mmc/host/sdricoh_cs.c
|
||||
|
||||
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
|
||||
M: Adrian Hunter <adrian.hunter@intel.com>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Orphan
|
||||
F: drivers/mmc/host/sdhci.*
|
||||
F: drivers/mmc/host/sdhci-pltfm.[ch]
|
||||
T: git git://git.infradead.org/users/ahunter/linux-sdhci.git
|
||||
S: Maintained
|
||||
F: drivers/mmc/host/sdhci*
|
||||
F: include/linux/mmc/sdhci*
|
||||
|
||||
SECURE COMPUTING
|
||||
M: Kees Cook <keescook@chromium.org>
|
||||
|
@ -589,6 +589,14 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||
struct mmc_card *card;
|
||||
int err = 0, ioc_err = 0;
|
||||
|
||||
/*
|
||||
* The caller must have CAP_SYS_RAWIO, and must be calling this on the
|
||||
* whole block device, not on a partition. This prevents overspray
|
||||
* between sibling partitions.
|
||||
*/
|
||||
if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
|
||||
return -EPERM;
|
||||
|
||||
idata = mmc_blk_ioctl_copy_from_user(ic_ptr);
|
||||
if (IS_ERR(idata))
|
||||
return PTR_ERR(idata);
|
||||
@ -631,6 +639,14 @@ static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev,
|
||||
int i, err = 0, ioc_err = 0;
|
||||
__u64 num_of_cmds;
|
||||
|
||||
/*
|
||||
* The caller must have CAP_SYS_RAWIO, and must be calling this on the
|
||||
* whole block device, not on a partition. This prevents overspray
|
||||
* between sibling partitions.
|
||||
*/
|
||||
if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(&num_of_cmds, &user->num_of_cmds,
|
||||
sizeof(num_of_cmds)))
|
||||
return -EFAULT;
|
||||
@ -688,14 +704,6 @@ cmd_err:
|
||||
static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
/*
|
||||
* The caller must have CAP_SYS_RAWIO, and must be calling this on the
|
||||
* whole block device, not on a partition. This prevents overspray
|
||||
* between sibling partitions.
|
||||
*/
|
||||
if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
|
||||
return -EPERM;
|
||||
|
||||
switch (cmd) {
|
||||
case MMC_IOC_CMD:
|
||||
return mmc_blk_ioctl_cmd(bdev,
|
||||
@ -1362,8 +1370,8 @@ static int mmc_blk_err_check(struct mmc_card *card,
|
||||
|
||||
if (brq->data.error) {
|
||||
if (need_retune && !brq->retune_retry_done) {
|
||||
pr_info("%s: retrying because a re-tune was needed\n",
|
||||
req->rq_disk->disk_name);
|
||||
pr_debug("%s: retrying because a re-tune was needed\n",
|
||||
req->rq_disk->disk_name);
|
||||
brq->retune_retry_done = 1;
|
||||
return MMC_BLK_RETRY;
|
||||
}
|
||||
@ -1524,13 +1532,13 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||
}
|
||||
if (rq_data_dir(req) == READ) {
|
||||
brq->cmd.opcode = readcmd;
|
||||
brq->data.flags |= MMC_DATA_READ;
|
||||
brq->data.flags = MMC_DATA_READ;
|
||||
if (brq->mrq.stop)
|
||||
brq->stop.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 |
|
||||
MMC_CMD_AC;
|
||||
} else {
|
||||
brq->cmd.opcode = writecmd;
|
||||
brq->data.flags |= MMC_DATA_WRITE;
|
||||
brq->data.flags = MMC_DATA_WRITE;
|
||||
if (brq->mrq.stop)
|
||||
brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B |
|
||||
MMC_CMD_AC;
|
||||
@ -1799,7 +1807,7 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
|
||||
|
||||
brq->data.blksz = 512;
|
||||
brq->data.blocks = packed->blocks + hdr_blocks;
|
||||
brq->data.flags |= MMC_DATA_WRITE;
|
||||
brq->data.flags = MMC_DATA_WRITE;
|
||||
|
||||
brq->stop.opcode = MMC_STOP_TRANSMISSION;
|
||||
brq->stop.arg = 0;
|
||||
|
@ -2829,6 +2829,7 @@ static int mtf_testlist_show(struct seq_file *sf, void *data)
|
||||
|
||||
mutex_lock(&mmc_test_lock);
|
||||
|
||||
seq_printf(sf, "0:\tRun all tests\n");
|
||||
for (i = 0; i < ARRAY_SIZE(mmc_test_cases); i++)
|
||||
seq_printf(sf, "%d:\t%s\n", i+1, mmc_test_cases[i].name);
|
||||
|
||||
|
@ -1033,7 +1033,7 @@ static inline void mmc_set_ios(struct mmc_host *host)
|
||||
"width %u timing %u\n",
|
||||
mmc_hostname(host), ios->clock, ios->bus_mode,
|
||||
ios->power_mode, ios->chip_select, ios->vdd,
|
||||
ios->bus_width, ios->timing);
|
||||
1 << ios->bus_width, ios->timing);
|
||||
|
||||
host->ops->set_ios(host, ios);
|
||||
}
|
||||
@ -1079,7 +1079,8 @@ int mmc_execute_tuning(struct mmc_card *card)
|
||||
err = host->ops->execute_tuning(host, opcode);
|
||||
|
||||
if (err)
|
||||
pr_err("%s: tuning execution failed\n", mmc_hostname(host));
|
||||
pr_err("%s: tuning execution failed: %d\n",
|
||||
mmc_hostname(host), err);
|
||||
else
|
||||
mmc_retune_enable(host);
|
||||
|
||||
@ -1204,8 +1205,9 @@ EXPORT_SYMBOL(mmc_vddrange_to_ocrmask);
|
||||
* @np: The device node need to be parsed.
|
||||
* @mask: mask of voltages available for MMC/SD/SDIO
|
||||
*
|
||||
* 1. Return zero on success.
|
||||
* 2. Return negative errno: voltage-range is invalid.
|
||||
* Parse the "voltage-ranges" DT property, returning zero if it is not
|
||||
* found, negative errno if the voltage-range specification is invalid,
|
||||
* or one if the voltage-range is specified and successfully parsed.
|
||||
*/
|
||||
int mmc_of_parse_voltage(struct device_node *np, u32 *mask)
|
||||
{
|
||||
@ -1214,8 +1216,12 @@ int mmc_of_parse_voltage(struct device_node *np, u32 *mask)
|
||||
|
||||
voltage_ranges = of_get_property(np, "voltage-ranges", &num_ranges);
|
||||
num_ranges = num_ranges / sizeof(*voltage_ranges) / 2;
|
||||
if (!voltage_ranges || !num_ranges) {
|
||||
pr_info("%s: voltage-ranges unspecified\n", np->full_name);
|
||||
if (!voltage_ranges) {
|
||||
pr_debug("%s: voltage-ranges unspecified\n", np->full_name);
|
||||
return 0;
|
||||
}
|
||||
if (!num_ranges) {
|
||||
pr_err("%s: voltage-ranges empty\n", np->full_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -1234,7 +1240,7 @@ int mmc_of_parse_voltage(struct device_node *np, u32 *mask)
|
||||
*mask |= ocr_mask;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_of_parse_voltage);
|
||||
|
||||
@ -2532,7 +2538,7 @@ int mmc_detect_card_removed(struct mmc_host *host)
|
||||
if (!card)
|
||||
return 1;
|
||||
|
||||
if (host->caps & MMC_CAP_NONREMOVABLE)
|
||||
if (!mmc_card_is_removable(host))
|
||||
return 0;
|
||||
|
||||
ret = mmc_card_removed(card);
|
||||
@ -2570,7 +2576,7 @@ void mmc_rescan(struct work_struct *work)
|
||||
return;
|
||||
|
||||
/* If there is a non-removable card registered, only scan once */
|
||||
if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)
|
||||
if (!mmc_card_is_removable(host) && host->rescan_entered)
|
||||
return;
|
||||
host->rescan_entered = 1;
|
||||
|
||||
@ -2587,8 +2593,7 @@ void mmc_rescan(struct work_struct *work)
|
||||
* if there is a _removable_ card registered, check whether it is
|
||||
* still present
|
||||
*/
|
||||
if (host->bus_ops && !host->bus_dead
|
||||
&& !(host->caps & MMC_CAP_NONREMOVABLE))
|
||||
if (host->bus_ops && !host->bus_dead && mmc_card_is_removable(host))
|
||||
host->bus_ops->detect(host);
|
||||
|
||||
host->detect_change = 0;
|
||||
@ -2613,7 +2618,7 @@ void mmc_rescan(struct work_struct *work)
|
||||
mmc_bus_put(host);
|
||||
|
||||
mmc_claim_host(host);
|
||||
if (!(host->caps & MMC_CAP_NONREMOVABLE) && host->ops->get_cd &&
|
||||
if (mmc_card_is_removable(host) && host->ops->get_cd &&
|
||||
host->ops->get_cd(host) == 0) {
|
||||
mmc_power_off(host);
|
||||
mmc_release_host(host);
|
||||
|
@ -220,7 +220,7 @@ static int mmc_clock_opt_set(void *data, u64 val)
|
||||
struct mmc_host *host = data;
|
||||
|
||||
/* We need this check due to input value is u64 */
|
||||
if (val > host->f_max)
|
||||
if (val != 0 && (val > host->f_max || val < host->f_min))
|
||||
return -EINVAL;
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
@ -339,6 +339,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
||||
host->class_dev.parent = dev;
|
||||
host->class_dev.class = &mmc_host_class;
|
||||
device_initialize(&host->class_dev);
|
||||
device_enable_async_suspend(&host->class_dev);
|
||||
|
||||
if (mmc_gpio_alloc(host)) {
|
||||
put_device(&host->class_dev);
|
||||
|
@ -501,7 +501,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
card->ext_csd.raw_bkops_status =
|
||||
ext_csd[EXT_CSD_BKOPS_STATUS];
|
||||
if (!card->ext_csd.man_bkops_en)
|
||||
pr_info("%s: MAN_BKOPS_EN bit is not set\n",
|
||||
pr_debug("%s: MAN_BKOPS_EN bit is not set\n",
|
||||
mmc_hostname(card->host));
|
||||
}
|
||||
|
||||
@ -945,7 +945,7 @@ static int mmc_select_bus_width(struct mmc_card *card)
|
||||
break;
|
||||
} else {
|
||||
pr_warn("%s: switch to bus width %d failed\n",
|
||||
mmc_hostname(host), ext_csd_bits[idx]);
|
||||
mmc_hostname(host), 1 << bus_width);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,6 @@ int mmc_send_status(struct mmc_card *card, u32 *status)
|
||||
|
||||
static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
BUG_ON(!host);
|
||||
@ -105,11 +104,7 @@ static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
|
||||
cmd.flags = MMC_RSP_NONE | MMC_CMD_AC;
|
||||
}
|
||||
|
||||
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
return mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
|
||||
}
|
||||
|
||||
int mmc_select_card(struct mmc_card *card)
|
||||
@ -244,7 +239,6 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
|
||||
|
||||
int mmc_set_relative_addr(struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
BUG_ON(!card);
|
||||
@ -254,11 +248,7 @@ int mmc_set_relative_addr(struct mmc_card *card)
|
||||
cmd.arg = card->rca << 16;
|
||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
||||
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
return mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -743,7 +733,7 @@ mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
|
||||
|
||||
int mmc_bus_test(struct mmc_card *card, u8 bus_width)
|
||||
{
|
||||
int err, width;
|
||||
int width;
|
||||
|
||||
if (bus_width == MMC_BUS_WIDTH_8)
|
||||
width = 8;
|
||||
@ -759,8 +749,7 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width)
|
||||
* is a problem. This improves chances that the test will work.
|
||||
*/
|
||||
mmc_send_bus_test(card, card->host, MMC_BUS_TEST_W, width);
|
||||
err = mmc_send_bus_test(card, card->host, MMC_BUS_TEST_R, width);
|
||||
return err;
|
||||
return mmc_send_bus_test(card, card->host, MMC_BUS_TEST_R, width);
|
||||
}
|
||||
|
||||
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
|
@ -74,8 +74,6 @@ void mmc_decode_cid(struct mmc_card *card)
|
||||
{
|
||||
u32 *resp = card->raw_cid;
|
||||
|
||||
memset(&card->cid, 0, sizeof(struct mmc_cid));
|
||||
|
||||
/*
|
||||
* SD doesn't currently have a version field so we will
|
||||
* have to assume we can parse this.
|
||||
|
@ -120,7 +120,6 @@ EXPORT_SYMBOL(mmc_wait_for_app_cmd);
|
||||
|
||||
int mmc_app_set_bus_width(struct mmc_card *card, int width)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
BUG_ON(!card);
|
||||
@ -140,11 +139,7 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
return mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES);
|
||||
}
|
||||
|
||||
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
||||
|
@ -106,8 +106,6 @@ static int sdio_read_cccr(struct mmc_card *card, u32 ocr)
|
||||
unsigned char data;
|
||||
unsigned char speed;
|
||||
|
||||
memset(&card->cccr, 0, sizeof(struct sdio_cccr));
|
||||
|
||||
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CCCR, 0, &data);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
@ -217,7 +217,6 @@ int sdio_reset(struct mmc_host *host)
|
||||
else
|
||||
abort |= 0x08;
|
||||
|
||||
ret = mmc_io_rw_direct_host(host, 1, 0, SDIO_CCCR_ABORT, abort, NULL);
|
||||
return ret;
|
||||
return mmc_io_rw_direct_host(host, 1, 0, SDIO_CCCR_ABORT, abort, NULL);
|
||||
}
|
||||
|
||||
|
@ -318,15 +318,15 @@ config MMC_SDHCI_F_SDH30
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_IPROC
|
||||
tristate "SDHCI platform support for the iProc SD/MMC Controller"
|
||||
depends on ARCH_BCM_IPROC || COMPILE_TEST
|
||||
tristate "SDHCI support for the BCM2835 & iProc SD/MMC Controller"
|
||||
depends on ARCH_BCM2835 || ARCH_BCM_IPROC || COMPILE_TEST
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
default ARCH_BCM_IPROC
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
help
|
||||
This selects the iProc SD/MMC controller.
|
||||
|
||||
If you have an IPROC platform with SD or MMC devices,
|
||||
If you have a BCM2835 or IPROC platform with SD or MMC devices,
|
||||
say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
@ -560,8 +560,8 @@ config MMC_TMIO
|
||||
|
||||
config MMC_SDHI
|
||||
tristate "SH-Mobile SDHI SD/SDIO controller support"
|
||||
depends on SUPERH || ARM
|
||||
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
||||
depends on SUPERH || ARM || ARM64
|
||||
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
|
||||
select MMC_TMIO_CORE
|
||||
help
|
||||
This provides support for the SDHI SD/SDIO controller found in
|
||||
@ -673,8 +673,8 @@ config MMC_DW_ROCKCHIP
|
||||
|
||||
config MMC_SH_MMCIF
|
||||
tristate "SuperH Internal MMCIF support"
|
||||
depends on MMC_BLOCK && HAS_DMA
|
||||
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
||||
depends on HAS_DMA
|
||||
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
|
||||
help
|
||||
This selects the MMC Host Interface controller (MMCIF).
|
||||
|
||||
@ -786,3 +786,14 @@ config MMC_MTK
|
||||
If you have a machine with a integrated SD/MMC card reader, say Y or M here.
|
||||
This is needed if support for any SD/SDIO/MMC devices is required.
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_MICROCHIP_PIC32
|
||||
tristate "Microchip PIC32MZDA SDHCI support"
|
||||
depends on MMC_SDHCI && PIC32MZDA && MMC_SDHCI_PLTFM
|
||||
help
|
||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||
for PIC32MZDA platform.
|
||||
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
@ -75,6 +75,7 @@ obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
|
||||
obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
|
||||
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
|
||||
|
||||
ifeq ($(CONFIG_CB710_DEBUG),y)
|
||||
CFLAGS-cb710-mmc += -DDEBUG
|
||||
|
@ -848,9 +848,7 @@ static u32 atmci_prepare_command(struct mmc_host *mmc,
|
||||
if (cmd->opcode == SD_IO_RW_EXTENDED) {
|
||||
cmdr |= ATMCI_CMDR_SDIO_BLOCK;
|
||||
} else {
|
||||
if (data->flags & MMC_DATA_STREAM)
|
||||
cmdr |= ATMCI_CMDR_STREAM;
|
||||
else if (data->blocks > 1)
|
||||
if (data->blocks > 1)
|
||||
cmdr |= ATMCI_CMDR_MULTI_BLOCK;
|
||||
else
|
||||
cmdr |= ATMCI_CMDR_BLOCK;
|
||||
@ -1371,10 +1369,7 @@ static void atmci_start_request(struct atmel_mci *host,
|
||||
host->stop_cmdr |= ATMCI_CMDR_STOP_XFER;
|
||||
if (!(data->flags & MMC_DATA_WRITE))
|
||||
host->stop_cmdr |= ATMCI_CMDR_TRDIR_READ;
|
||||
if (data->flags & MMC_DATA_STREAM)
|
||||
host->stop_cmdr |= ATMCI_CMDR_STREAM;
|
||||
else
|
||||
host->stop_cmdr |= ATMCI_CMDR_MULTI_BLOCK;
|
||||
host->stop_cmdr |= ATMCI_CMDR_MULTI_BLOCK;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2443,7 +2438,7 @@ static int atmci_configure_dma(struct atmel_mci *host)
|
||||
struct mci_platform_data *pdata = host->pdev->dev.platform_data;
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
if (!pdata->dma_filter)
|
||||
if (!pdata || !pdata->dma_filter)
|
||||
return -ENODEV;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
|
@ -126,9 +126,6 @@ static int sdh_setup_data(struct sdh_host *host, struct mmc_data *data)
|
||||
length = data->blksz * data->blocks;
|
||||
bfin_write_SDH_DATA_LGTH(length);
|
||||
|
||||
if (data->flags & MMC_DATA_STREAM)
|
||||
data_ctl |= DTX_MODE;
|
||||
|
||||
if (data->flags & MMC_DATA_READ)
|
||||
data_ctl |= DTX_DIR;
|
||||
/* Only supports power-of-2 block size */
|
||||
|
@ -346,10 +346,6 @@ static void mmc_davinci_start_command(struct mmc_davinci_host *host,
|
||||
if (cmd->data)
|
||||
cmd_reg |= MMCCMD_WDATX;
|
||||
|
||||
/* Setting whether stream or block transfer */
|
||||
if (cmd->flags & MMC_DATA_STREAM)
|
||||
cmd_reg |= MMCCMD_STRMTP;
|
||||
|
||||
/* Setting whether data read or write */
|
||||
if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE)
|
||||
cmd_reg |= MMCCMD_DTRW;
|
||||
@ -568,8 +564,7 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req)
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "%s %s, %d blocks of %d bytes\n",
|
||||
(data->flags & MMC_DATA_STREAM) ? "stream" : "block",
|
||||
dev_dbg(mmc_dev(host->mmc), "%s, %d blocks of %d bytes\n",
|
||||
(data->flags & MMC_DATA_WRITE) ? "write" : "read",
|
||||
data->blocks, data->blksz);
|
||||
dev_dbg(mmc_dev(host->mmc), " DTO %d cycles + %d ns\n",
|
||||
@ -584,22 +579,18 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req)
|
||||
writel(data->blksz, host->base + DAVINCI_MMCBLEN);
|
||||
|
||||
/* Configure the FIFO */
|
||||
switch (data->flags & MMC_DATA_WRITE) {
|
||||
case MMC_DATA_WRITE:
|
||||
if (data->flags & MMC_DATA_WRITE) {
|
||||
host->data_dir = DAVINCI_MMC_DATADIR_WRITE;
|
||||
writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR | MMCFIFOCTL_FIFORST,
|
||||
host->base + DAVINCI_MMCFIFOCTL);
|
||||
writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR,
|
||||
host->base + DAVINCI_MMCFIFOCTL);
|
||||
break;
|
||||
|
||||
default:
|
||||
} else {
|
||||
host->data_dir = DAVINCI_MMC_DATADIR_READ;
|
||||
writel(fifo_lev | MMCFIFOCTL_FIFODIR_RD | MMCFIFOCTL_FIFORST,
|
||||
host->base + DAVINCI_MMCFIFOCTL);
|
||||
writel(fifo_lev | MMCFIFOCTL_FIFODIR_RD,
|
||||
host->base + DAVINCI_MMCFIFOCTL);
|
||||
break;
|
||||
}
|
||||
|
||||
host->buffer = NULL;
|
||||
|
@ -145,6 +145,16 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
|
||||
mci_writel(host, CLKSEL64, clksel);
|
||||
else
|
||||
mci_writel(host, CLKSEL, clksel);
|
||||
|
||||
/*
|
||||
* Exynos4412 and Exynos5250 extends the use of CMD register with the
|
||||
* use of bit 29 (which is reserved on standard MSHC controllers) for
|
||||
* optionally bypassing the HOLD register for command and data. The
|
||||
* HOLD register should be bypassed in case there is no phase shift
|
||||
* applied on CMD/DATA that is sent to the card.
|
||||
*/
|
||||
if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel))
|
||||
set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->cur_slot->flags);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
@ -202,26 +212,6 @@ static int dw_mci_exynos_resume_noirq(struct device *dev)
|
||||
#define dw_mci_exynos_resume_noirq NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
|
||||
{
|
||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||
/*
|
||||
* Exynos4412 and Exynos5250 extends the use of CMD register with the
|
||||
* use of bit 29 (which is reserved on standard MSHC controllers) for
|
||||
* optionally bypassing the HOLD register for command and data. The
|
||||
* HOLD register should be bypassed in case there is no phase shift
|
||||
* applied on CMD/DATA that is sent to the card.
|
||||
*/
|
||||
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
|
||||
if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL64)))
|
||||
*cmdr |= SDMMC_CMD_USE_HOLD_REG;
|
||||
} else {
|
||||
if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL)))
|
||||
*cmdr |= SDMMC_CMD_USE_HOLD_REG;
|
||||
}
|
||||
}
|
||||
|
||||
static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing)
|
||||
{
|
||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||
@ -500,7 +490,6 @@ static const struct dw_mci_drv_data exynos_drv_data = {
|
||||
.caps = exynos_dwmmc_caps,
|
||||
.init = dw_mci_exynos_priv_init,
|
||||
.setup_clock = dw_mci_exynos_setup_clock,
|
||||
.prepare_command = dw_mci_exynos_prepare_command,
|
||||
.set_ios = dw_mci_exynos_set_ios,
|
||||
.parse_dt = dw_mci_exynos_parse_dt,
|
||||
.execute_tuning = dw_mci_exynos_execute_tuning,
|
||||
|
@ -26,19 +26,6 @@
|
||||
#include "dw_mmc.h"
|
||||
#include "dw_mmc-pltfm.h"
|
||||
|
||||
static void dw_mci_pltfm_prepare_command(struct dw_mci *host, u32 *cmdr)
|
||||
{
|
||||
*cmdr |= SDMMC_CMD_USE_HOLD_REG;
|
||||
}
|
||||
|
||||
static const struct dw_mci_drv_data socfpga_drv_data = {
|
||||
.prepare_command = dw_mci_pltfm_prepare_command,
|
||||
};
|
||||
|
||||
static const struct dw_mci_drv_data pistachio_drv_data = {
|
||||
.prepare_command = dw_mci_pltfm_prepare_command,
|
||||
};
|
||||
|
||||
int dw_mci_pltfm_register(struct platform_device *pdev,
|
||||
const struct dw_mci_drv_data *drv_data)
|
||||
{
|
||||
@ -94,10 +81,8 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);
|
||||
|
||||
static const struct of_device_id dw_mci_pltfm_match[] = {
|
||||
{ .compatible = "snps,dw-mshc", },
|
||||
{ .compatible = "altr,socfpga-dw-mshc",
|
||||
.data = &socfpga_drv_data },
|
||||
{ .compatible = "img,pistachio-dw-mshc",
|
||||
.data = &pistachio_drv_data },
|
||||
{ .compatible = "altr,socfpga-dw-mshc", },
|
||||
{ .compatible = "img,pistachio-dw-mshc", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
|
||||
|
@ -26,11 +26,6 @@ struct dw_mci_rockchip_priv_data {
|
||||
int default_sample_phase;
|
||||
};
|
||||
|
||||
static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
|
||||
{
|
||||
*cmdr |= SDMMC_CMD_USE_HOLD_REG;
|
||||
}
|
||||
|
||||
static int dw_mci_rk3288_setup_clock(struct dw_mci *host)
|
||||
{
|
||||
host->bus_hz /= RK3288_CLKGEN_DIV;
|
||||
@ -240,12 +235,10 @@ static int dw_mci_rockchip_init(struct dw_mci *host)
|
||||
}
|
||||
|
||||
static const struct dw_mci_drv_data rk2928_drv_data = {
|
||||
.prepare_command = dw_mci_rockchip_prepare_command,
|
||||
.init = dw_mci_rockchip_init,
|
||||
};
|
||||
|
||||
static const struct dw_mci_drv_data rk3288_drv_data = {
|
||||
.prepare_command = dw_mci_rockchip_prepare_command,
|
||||
.set_ios = dw_mci_rk3288_set_ios,
|
||||
.execute_tuning = dw_mci_rk3288_execute_tuning,
|
||||
.parse_dt = dw_mci_rk3288_parse_dt,
|
||||
|
@ -234,7 +234,6 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
|
||||
struct mmc_data *data;
|
||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||
struct dw_mci *host = slot->host;
|
||||
const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
|
||||
u32 cmdr;
|
||||
|
||||
cmd->error = -EINPROGRESS;
|
||||
@ -290,14 +289,12 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
|
||||
data = cmd->data;
|
||||
if (data) {
|
||||
cmdr |= SDMMC_CMD_DAT_EXP;
|
||||
if (data->flags & MMC_DATA_STREAM)
|
||||
cmdr |= SDMMC_CMD_STRM_MODE;
|
||||
if (data->flags & MMC_DATA_WRITE)
|
||||
cmdr |= SDMMC_CMD_DAT_WR;
|
||||
}
|
||||
|
||||
if (drv_data && drv_data->prepare_command)
|
||||
drv_data->prepare_command(slot->host, &cmdr);
|
||||
if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &slot->flags))
|
||||
cmdr |= SDMMC_CMD_USE_HOLD_REG;
|
||||
|
||||
return cmdr;
|
||||
}
|
||||
@ -1450,12 +1447,11 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
|
||||
{
|
||||
int present;
|
||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||
struct dw_mci_board *brd = slot->host->pdata;
|
||||
struct dw_mci *host = slot->host;
|
||||
int gpio_cd = mmc_gpio_get_cd(mmc);
|
||||
|
||||
/* Use platform get_cd function, else try onboard card detect */
|
||||
if ((brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) ||
|
||||
if ((mmc->caps & MMC_CAP_NEEDS_POLL) ||
|
||||
(mmc->caps & MMC_CAP_NONREMOVABLE))
|
||||
present = 1;
|
||||
else if (!IS_ERR_VALUE(gpio_cd))
|
||||
@ -1477,6 +1473,34 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
|
||||
return present;
|
||||
}
|
||||
|
||||
static void dw_mci_hw_reset(struct mmc_host *mmc)
|
||||
{
|
||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||
struct dw_mci *host = slot->host;
|
||||
int reset;
|
||||
|
||||
if (host->use_dma == TRANS_MODE_IDMAC)
|
||||
dw_mci_idmac_reset(host);
|
||||
|
||||
if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_DMA_RESET |
|
||||
SDMMC_CTRL_FIFO_RESET))
|
||||
return;
|
||||
|
||||
/*
|
||||
* According to eMMC spec, card reset procedure:
|
||||
* tRstW >= 1us: RST_n pulse width
|
||||
* tRSCA >= 200us: RST_n to Command time
|
||||
* tRSTH >= 1us: RST_n high period
|
||||
*/
|
||||
reset = mci_readl(host, RST_N);
|
||||
reset &= ~(SDMMC_RST_HWACTIVE << slot->id);
|
||||
mci_writel(host, RST_N, reset);
|
||||
usleep_range(1, 2);
|
||||
reset |= SDMMC_RST_HWACTIVE << slot->id;
|
||||
mci_writel(host, RST_N, reset);
|
||||
usleep_range(200, 300);
|
||||
}
|
||||
|
||||
static void dw_mci_init_card(struct mmc_host *mmc, struct mmc_card *card)
|
||||
{
|
||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||
@ -1563,6 +1587,7 @@ static const struct mmc_host_ops dw_mci_ops = {
|
||||
.set_ios = dw_mci_set_ios,
|
||||
.get_ro = dw_mci_get_ro,
|
||||
.get_cd = dw_mci_get_cd,
|
||||
.hw_reset = dw_mci_hw_reset,
|
||||
.enable_sdio_irq = dw_mci_enable_sdio_irq,
|
||||
.execute_tuning = dw_mci_execute_tuning,
|
||||
.card_busy = dw_mci_card_busy,
|
||||
@ -2840,23 +2865,13 @@ static void dw_mci_dto_timer(unsigned long arg)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct dw_mci_of_quirks {
|
||||
char *quirk;
|
||||
int id;
|
||||
} of_quirks[] = {
|
||||
{
|
||||
.quirk = "broken-cd",
|
||||
.id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION,
|
||||
},
|
||||
};
|
||||
|
||||
static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_board *pdata;
|
||||
struct device *dev = host->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||
int idx, ret;
|
||||
int ret;
|
||||
u32 clock_frequency;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
@ -2864,17 +2879,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* find out number of slots supported */
|
||||
if (of_property_read_u32(dev->of_node, "num-slots",
|
||||
&pdata->num_slots)) {
|
||||
dev_info(dev,
|
||||
"num-slots property not found, assuming 1 slot is available\n");
|
||||
pdata->num_slots = 1;
|
||||
}
|
||||
|
||||
/* get quirks */
|
||||
for (idx = 0; idx < ARRAY_SIZE(of_quirks); idx++)
|
||||
if (of_get_property(np, of_quirks[idx].quirk, NULL))
|
||||
pdata->quirks |= of_quirks[idx].id;
|
||||
of_property_read_u32(np, "num-slots", &pdata->num_slots);
|
||||
|
||||
if (of_property_read_u32(np, "fifo-depth", &pdata->fifo_depth))
|
||||
dev_info(dev,
|
||||
@ -2908,18 +2913,19 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
|
||||
|
||||
static void dw_mci_enable_cd(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_board *brd = host->pdata;
|
||||
unsigned long irqflags;
|
||||
u32 temp;
|
||||
int i;
|
||||
struct dw_mci_slot *slot;
|
||||
|
||||
/* No need for CD if broken card detection */
|
||||
if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
|
||||
return;
|
||||
|
||||
/* No need for CD if all slots have a non-error GPIO */
|
||||
/*
|
||||
* No need for CD if all slots have a non-error GPIO
|
||||
* as well as broken card detection is found.
|
||||
*/
|
||||
for (i = 0; i < host->num_slots; i++) {
|
||||
struct dw_mci_slot *slot = host->slot[i];
|
||||
slot = host->slot[i];
|
||||
if (slot->mmc->caps & MMC_CAP_NEEDS_POLL)
|
||||
return;
|
||||
|
||||
if (IS_ERR_VALUE(mmc_gpio_get_cd(slot->mmc)))
|
||||
break;
|
||||
@ -2949,12 +2955,6 @@ int dw_mci_probe(struct dw_mci *host)
|
||||
}
|
||||
}
|
||||
|
||||
if (host->pdata->num_slots < 1) {
|
||||
dev_err(host->dev,
|
||||
"Platform data must supply num_slots.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
host->biu_clk = devm_clk_get(host->dev, "biu");
|
||||
if (IS_ERR(host->biu_clk)) {
|
||||
dev_dbg(host->dev, "biu clock not available\n");
|
||||
@ -3052,8 +3052,10 @@ int dw_mci_probe(struct dw_mci *host)
|
||||
}
|
||||
|
||||
/* Reset all blocks */
|
||||
if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS))
|
||||
return -ENODEV;
|
||||
if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS)) {
|
||||
ret = -ENODEV;
|
||||
goto err_clk_ciu;
|
||||
}
|
||||
|
||||
host->dma_ops = host->pdata->dma_ops;
|
||||
dw_mci_init_dma(host);
|
||||
@ -3111,13 +3113,20 @@ int dw_mci_probe(struct dw_mci *host)
|
||||
if (host->pdata->num_slots)
|
||||
host->num_slots = host->pdata->num_slots;
|
||||
else
|
||||
host->num_slots = SDMMC_GET_SLOT_NUM(mci_readl(host, HCON));
|
||||
host->num_slots = 1;
|
||||
|
||||
if (host->num_slots < 1 ||
|
||||
host->num_slots > SDMMC_GET_SLOT_NUM(mci_readl(host, HCON))) {
|
||||
dev_err(host->dev,
|
||||
"Platform data must supply correct num_slots.\n");
|
||||
ret = -ENODEV;
|
||||
goto err_clk_ciu;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable interrupts for command done, data over, data empty,
|
||||
* receive ready and error such as transmit, receive timeout, crc error
|
||||
*/
|
||||
mci_writel(host, RINTSTS, 0xFFFFFFFF);
|
||||
mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
|
||||
SDMMC_INT_TXDR | SDMMC_INT_RXDR |
|
||||
DW_MCI_ERROR_FLAGS);
|
||||
|
@ -46,6 +46,7 @@
|
||||
#define SDMMC_VERID 0x06c
|
||||
#define SDMMC_HCON 0x070
|
||||
#define SDMMC_UHS_REG 0x074
|
||||
#define SDMMC_RST_N 0x078
|
||||
#define SDMMC_BMOD 0x080
|
||||
#define SDMMC_PLDMND 0x084
|
||||
#define SDMMC_DBADDR 0x088
|
||||
@ -169,6 +170,8 @@
|
||||
#define SDMMC_IDMAC_ENABLE BIT(7)
|
||||
#define SDMMC_IDMAC_FB BIT(1)
|
||||
#define SDMMC_IDMAC_SWRESET BIT(0)
|
||||
/* H/W reset */
|
||||
#define SDMMC_RST_HWACTIVE 0x1
|
||||
/* Version ID register define */
|
||||
#define SDMMC_GET_VERID(x) ((x) & 0xFFFF)
|
||||
/* Card read threshold */
|
||||
@ -265,6 +268,7 @@ struct dw_mci_slot {
|
||||
#define DW_MMC_CARD_PRESENT 0
|
||||
#define DW_MMC_CARD_NEED_INIT 1
|
||||
#define DW_MMC_CARD_NO_LOW_PWR 2
|
||||
#define DW_MMC_CARD_NO_USE_HOLD 3
|
||||
int id;
|
||||
int sdio_id;
|
||||
};
|
||||
@ -274,7 +278,6 @@ struct dw_mci_slot {
|
||||
* @caps: mmc subsystem specified capabilities of the controller(s).
|
||||
* @init: early implementation specific initialization.
|
||||
* @setup_clock: implementation specific clock configuration.
|
||||
* @prepare_command: handle CMD register extensions.
|
||||
* @set_ios: handle bus specific extensions.
|
||||
* @parse_dt: parse implementation specific device tree properties.
|
||||
* @execute_tuning: implementation specific tuning procedure.
|
||||
@ -287,7 +290,6 @@ struct dw_mci_drv_data {
|
||||
unsigned long *caps;
|
||||
int (*init)(struct dw_mci *host);
|
||||
int (*setup_clock)(struct dw_mci *host);
|
||||
void (*prepare_command)(struct dw_mci *host, u32 *cmdr);
|
||||
void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
|
||||
int (*parse_dt)(struct dw_mci *host);
|
||||
int (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode);
|
||||
|
@ -660,8 +660,6 @@ static void jz4740_mmc_send_command(struct jz4740_mmc_host *host,
|
||||
cmdat |= JZ_MMC_CMDAT_DATA_EN;
|
||||
if (cmd->data->flags & MMC_DATA_WRITE)
|
||||
cmdat |= JZ_MMC_CMDAT_WRITE;
|
||||
if (cmd->data->flags & MMC_DATA_STREAM)
|
||||
cmdat |= JZ_MMC_CMDAT_STREAM;
|
||||
if (host->use_dma)
|
||||
cmdat |= JZ_MMC_CMDAT_DMA_EN;
|
||||
|
||||
|
@ -1442,6 +1442,12 @@ static int mmc_spi_probe(struct spi_device *spi)
|
||||
host->pdata->cd_debounce);
|
||||
if (status != 0)
|
||||
goto fail_add_host;
|
||||
|
||||
/* The platform has a CD GPIO signal that may support
|
||||
* interrupts, so let mmc_gpiod_request_cd_irq() decide
|
||||
* if polling is needed or not.
|
||||
*/
|
||||
mmc->caps &= ~MMC_CAP_NEEDS_POLL;
|
||||
mmc_gpiod_request_cd_irq(mmc);
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,6 @@
|
||||
|
||||
#include <asm/div64.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/sizes.h>
|
||||
|
||||
#include "mmci.h"
|
||||
#include "mmci_qcom_dml.h"
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/sd.h>
|
||||
#include <linux/mmc/sdio.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
|
||||
#define MAX_BD_NUM 1024
|
||||
|
||||
@ -1020,26 +1021,19 @@ static void msdc_set_buswidth(struct msdc_host *host, u32 width)
|
||||
static int msdc_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct msdc_host *host = mmc_priv(mmc);
|
||||
int min_uv, max_uv;
|
||||
int ret = 0;
|
||||
|
||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
|
||||
min_uv = 3300000;
|
||||
max_uv = 3300000;
|
||||
} else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
|
||||
min_uv = 1800000;
|
||||
max_uv = 1800000;
|
||||
} else {
|
||||
if (ios->signal_voltage != MMC_SIGNAL_VOLTAGE_330 &&
|
||||
ios->signal_voltage != MMC_SIGNAL_VOLTAGE_180) {
|
||||
dev_err(host->dev, "Unsupported signal voltage!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv);
|
||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||
if (ret) {
|
||||
dev_err(host->dev,
|
||||
"Regulator set error %d: %d - %d\n",
|
||||
ret, min_uv, max_uv);
|
||||
dev_dbg(host->dev, "Regulator set error %d (%d)\n",
|
||||
ret, ios->signal_voltage);
|
||||
} else {
|
||||
/* Apply different pinctrl settings for different signal voltage */
|
||||
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
|
||||
@ -1452,6 +1446,7 @@ static struct mmc_host_ops mt_msdc_ops = {
|
||||
.pre_req = msdc_pre_req,
|
||||
.request = msdc_ops_request,
|
||||
.set_ios = msdc_ops_set_ios,
|
||||
.get_ro = mmc_gpio_get_ro,
|
||||
.start_signal_voltage_switch = msdc_ops_switch_volt,
|
||||
.card_busy = msdc_card_busy,
|
||||
.execute_tuning = msdc_execute_tuning,
|
||||
|
@ -307,9 +307,6 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
|
||||
enum dma_transfer_direction slave_dirn;
|
||||
int i, nents;
|
||||
|
||||
if (data->flags & MMC_DATA_STREAM)
|
||||
nob = 0xffff;
|
||||
|
||||
host->data = data;
|
||||
data->bytes_xfered = 0;
|
||||
|
||||
|
@ -74,7 +74,6 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi)
|
||||
const u32 *voltage_ranges;
|
||||
int num_ranges;
|
||||
int i;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (dev->platform_data || !np)
|
||||
return dev->platform_data;
|
||||
@ -97,7 +96,6 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi)
|
||||
mask = mmc_vddrange_to_ocrmask(be32_to_cpu(voltage_ranges[j]),
|
||||
be32_to_cpu(voltage_ranges[j + 1]));
|
||||
if (!mask) {
|
||||
ret = -EINVAL;
|
||||
dev_err(dev, "OF: voltage-range #%d is invalid\n", i);
|
||||
goto err_ocr;
|
||||
}
|
||||
|
@ -503,8 +503,11 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
|
||||
host->pbias = devm_regulator_get_optional(host->dev, "pbias");
|
||||
if (IS_ERR(host->pbias)) {
|
||||
ret = PTR_ERR(host->pbias);
|
||||
if ((ret != -ENODEV) && host->dev->of_node)
|
||||
if ((ret != -ENODEV) && host->dev->of_node) {
|
||||
dev_err(host->dev,
|
||||
"SD card detect fail? enable CONFIG_REGULATOR_PBIAS\n");
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(host->dev, "unable to get pbias regulator %ld\n",
|
||||
PTR_ERR(host->pbias));
|
||||
host->pbias = NULL;
|
||||
@ -2159,7 +2162,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||
&rx_req, &pdev->dev, "rx");
|
||||
|
||||
if (!host->rx_chan) {
|
||||
dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req);
|
||||
dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel\n");
|
||||
ret = -ENXIO;
|
||||
goto err_irq;
|
||||
}
|
||||
@ -2169,7 +2172,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||
&tx_req, &pdev->dev, "tx");
|
||||
|
||||
if (!host->tx_chan) {
|
||||
dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req);
|
||||
dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel\n");
|
||||
ret = -ENXIO;
|
||||
goto err_irq;
|
||||
}
|
||||
|
@ -191,9 +191,6 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
|
||||
|
||||
host->data = data;
|
||||
|
||||
if (data->flags & MMC_DATA_STREAM)
|
||||
nob = 0xffff;
|
||||
|
||||
writel(nob, host->base + MMC_NOB);
|
||||
writel(data->blksz, host->base + MMC_BLKLEN);
|
||||
|
||||
@ -443,9 +440,6 @@ static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
cmdat |= CMDAT_DATAEN | CMDAT_DMAEN;
|
||||
if (mrq->data->flags & MMC_DATA_WRITE)
|
||||
cmdat |= CMDAT_WRITE;
|
||||
|
||||
if (mrq->data->flags & MMC_DATA_STREAM)
|
||||
cmdat |= CMDAT_STREAM;
|
||||
}
|
||||
|
||||
pxamci_start_cmd(host, mrq->cmd, cmdat);
|
||||
|
@ -1014,8 +1014,7 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
|
||||
if (host->bus_width == MMC_BUS_WIDTH_4)
|
||||
dcon |= S3C2410_SDIDCON_WIDEBUS;
|
||||
|
||||
if (!(data->flags & MMC_DATA_STREAM))
|
||||
dcon |= S3C2410_SDIDCON_BLOCKMODE;
|
||||
dcon |= S3C2410_SDIDCON_BLOCKMODE;
|
||||
|
||||
if (data->flags & MMC_DATA_WRITE) {
|
||||
dcon |= S3C2410_SDIDCON_TXAFTERRESP;
|
||||
|
@ -75,7 +75,6 @@ struct sdhci_acpi_host {
|
||||
const struct sdhci_acpi_slot *slot;
|
||||
struct platform_device *pdev;
|
||||
bool use_runtime_pm;
|
||||
bool dma_setup;
|
||||
};
|
||||
|
||||
static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag)
|
||||
@ -83,33 +82,6 @@ static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag)
|
||||
return c->slot && (c->slot->flags & flag);
|
||||
}
|
||||
|
||||
static int sdhci_acpi_enable_dma(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_acpi_host *c = sdhci_priv(host);
|
||||
struct device *dev = &c->pdev->dev;
|
||||
int err = -1;
|
||||
|
||||
if (c->dma_setup)
|
||||
return 0;
|
||||
|
||||
if (host->flags & SDHCI_USE_64_BIT_DMA) {
|
||||
if (host->quirks2 & SDHCI_QUIRK2_BROKEN_64_BIT_DMA) {
|
||||
host->flags &= ~SDHCI_USE_64_BIT_DMA;
|
||||
} else {
|
||||
err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
|
||||
if (err)
|
||||
dev_warn(dev, "Failed to set 64-bit DMA mask\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
|
||||
|
||||
c->dma_setup = !err;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void sdhci_acpi_int_hw_reset(struct sdhci_host *host)
|
||||
{
|
||||
u8 reg;
|
||||
@ -127,7 +99,6 @@ static void sdhci_acpi_int_hw_reset(struct sdhci_host *host)
|
||||
|
||||
static const struct sdhci_ops sdhci_acpi_ops_dflt = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.enable_dma = sdhci_acpi_enable_dma,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
@ -135,7 +106,6 @@ static const struct sdhci_ops sdhci_acpi_ops_dflt = {
|
||||
|
||||
static const struct sdhci_ops sdhci_acpi_ops_int = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.enable_dma = sdhci_acpi_enable_dma,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
@ -264,6 +234,17 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
|
||||
.probe_slot = sdhci_acpi_sd_probe_slot,
|
||||
};
|
||||
|
||||
static const struct sdhci_acpi_slot sdhci_acpi_slot_qcom_sd_3v = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION,
|
||||
.quirks2 = SDHCI_QUIRK2_NO_1_8_V,
|
||||
.caps = MMC_CAP_NONREMOVABLE,
|
||||
};
|
||||
|
||||
static const struct sdhci_acpi_slot sdhci_acpi_slot_qcom_sd = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION,
|
||||
.caps = MMC_CAP_NONREMOVABLE,
|
||||
};
|
||||
|
||||
struct sdhci_acpi_uid_slot {
|
||||
const char *hid;
|
||||
const char *uid;
|
||||
@ -284,6 +265,8 @@ static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {
|
||||
{ "INT344D" , NULL, &sdhci_acpi_slot_int_sdio },
|
||||
{ "PNP0FFF" , "3" , &sdhci_acpi_slot_int_sd },
|
||||
{ "PNP0D40" },
|
||||
{ "QCOM8051", NULL, &sdhci_acpi_slot_qcom_sd_3v },
|
||||
{ "QCOM8052", NULL, &sdhci_acpi_slot_qcom_sd },
|
||||
{ },
|
||||
};
|
||||
|
||||
@ -298,6 +281,8 @@ static const struct acpi_device_id sdhci_acpi_ids[] = {
|
||||
{ "INT3436" },
|
||||
{ "INT344D" },
|
||||
{ "PNP0D40" },
|
||||
{ "QCOM8051" },
|
||||
{ "QCOM8052" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids);
|
||||
@ -418,6 +403,8 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
|
||||
pm_runtime_enable(dev);
|
||||
}
|
||||
|
||||
device_enable_async_suspend(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
|
@ -74,7 +74,7 @@ static inline u32 bcm2835_sdhci_readl(struct sdhci_host *host, int reg)
|
||||
static void bcm2835_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct bcm2835_sdhci *bcm2835_host = pltfm_host->priv;
|
||||
struct bcm2835_sdhci *bcm2835_host = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 oldval = (reg == SDHCI_COMMAND) ? bcm2835_host->shadow :
|
||||
bcm2835_sdhci_readl(host, reg & ~3);
|
||||
u32 word_num = (reg >> 1) & 1;
|
||||
@ -152,20 +152,12 @@ static int bcm2835_sdhci_probe(struct platform_device *pdev)
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
int ret;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &bcm2835_sdhci_pdata, 0);
|
||||
host = sdhci_pltfm_init(pdev, &bcm2835_sdhci_pdata,
|
||||
sizeof(*bcm2835_host));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
bcm2835_host = devm_kzalloc(&pdev->dev, sizeof(*bcm2835_host),
|
||||
GFP_KERNEL);
|
||||
if (!bcm2835_host) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"failed to allocate bcm2835_sdhci\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = bcm2835_host;
|
||||
|
||||
pltfm_host->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(pltfm_host->clk)) {
|
||||
|
@ -260,7 +260,7 @@ static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, i
|
||||
static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 val = readl(host->ioaddr + reg);
|
||||
|
||||
if (unlikely(reg == SDHCI_PRESENT_STATE)) {
|
||||
@ -338,7 +338,7 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
|
||||
static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 data;
|
||||
|
||||
if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
|
||||
@ -388,7 +388,7 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
|
||||
static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
u16 ret = 0;
|
||||
u32 val;
|
||||
|
||||
@ -448,7 +448,7 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
|
||||
static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 new_val = 0;
|
||||
|
||||
switch (reg) {
|
||||
@ -556,7 +556,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
||||
static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 new_val;
|
||||
u32 mask;
|
||||
|
||||
@ -633,7 +633,7 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
|
||||
unsigned int clock)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
unsigned int host_clock = pltfm_host->clock;
|
||||
int pre_div = 2;
|
||||
int div = 1;
|
||||
@ -692,7 +692,7 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
|
||||
static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
struct esdhc_platform_data *boarddata = &imx_data->boarddata;
|
||||
|
||||
switch (boarddata->wp_type) {
|
||||
@ -794,7 +794,7 @@ static int esdhc_change_pinstate(struct sdhci_host *host,
|
||||
unsigned int uhs)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
struct pinctrl_state *pinctrl;
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs);
|
||||
@ -864,7 +864,7 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
|
||||
{
|
||||
u32 m;
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
struct esdhc_platform_data *boarddata = &imx_data->boarddata;
|
||||
|
||||
/* disable ddr mode and disable HS400 mode */
|
||||
@ -917,7 +917,7 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask)
|
||||
static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
return esdhc_is_usdhc(imx_data) ? 1 << 28 : 1 << 27;
|
||||
}
|
||||
@ -925,7 +925,7 @@ static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host)
|
||||
static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
/* use maximum timeout counter */
|
||||
sdhci_writeb(host, esdhc_is_usdhc(imx_data) ? 0xF : 0xE,
|
||||
@ -1100,21 +1100,17 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
||||
int err;
|
||||
struct pltfm_imx_data *imx_data;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata, 0);
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata,
|
||||
sizeof(*imx_data));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
|
||||
imx_data = devm_kzalloc(&pdev->dev, sizeof(*imx_data), GFP_KERNEL);
|
||||
if (!imx_data) {
|
||||
err = -ENOMEM;
|
||||
goto free_sdhci;
|
||||
}
|
||||
imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
imx_data->socdata = of_id ? of_id->data : (struct esdhc_soc_data *)
|
||||
pdev->id_entry->driver_data;
|
||||
pltfm_host->priv = imx_data;
|
||||
|
||||
imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
|
||||
if (IS_ERR(imx_data->clk_ipg)) {
|
||||
@ -1241,7 +1237,7 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
@ -1264,7 +1260,7 @@ static int sdhci_esdhc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
ret = sdhci_runtime_suspend_host(host);
|
||||
@ -1282,7 +1278,7 @@ static int sdhci_esdhc_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
if (!sdhci_sdio_irq_enabled(host)) {
|
||||
clk_prepare_enable(imx_data->clk_per);
|
||||
|
@ -26,6 +26,7 @@ struct sdhci_iproc_data {
|
||||
const struct sdhci_pltfm_data *pdata;
|
||||
u32 caps;
|
||||
u32 caps1;
|
||||
u32 mmc_caps;
|
||||
};
|
||||
|
||||
struct sdhci_iproc_host {
|
||||
@ -165,9 +166,25 @@ static const struct sdhci_iproc_data iproc_data = {
|
||||
.pdata = &sdhci_iproc_pltfm_data,
|
||||
.caps = 0x05E90000,
|
||||
.caps1 = 0x00000064,
|
||||
.mmc_caps = MMC_CAP_1_8V_DDR,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||
SDHCI_QUIRK_MISSING_CAPS,
|
||||
.ops = &sdhci_iproc_ops,
|
||||
};
|
||||
|
||||
static const struct sdhci_iproc_data bcm2835_data = {
|
||||
.pdata = &sdhci_bcm2835_pltfm_data,
|
||||
.caps = SDHCI_CAN_VDD_330,
|
||||
.caps1 = 0x00000000,
|
||||
.mmc_caps = 0x00000000,
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_iproc_of_match[] = {
|
||||
{ .compatible = "brcm,bcm2835-sdhci", .data = &bcm2835_data },
|
||||
{ .compatible = "brcm,sdhci-iproc-cygnus", .data = &iproc_data },
|
||||
{ }
|
||||
};
|
||||
@ -199,32 +216,37 @@ static int sdhci_iproc_probe(struct platform_device *pdev)
|
||||
mmc_of_parse(host->mmc);
|
||||
sdhci_get_of_property(pdev);
|
||||
|
||||
/* Enable EMMC 1/8V DDR capable */
|
||||
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
||||
host->mmc->caps |= iproc_host->data->mmc_caps;
|
||||
|
||||
pltfm_host->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(pltfm_host->clk)) {
|
||||
ret = PTR_ERR(pltfm_host->clk);
|
||||
goto err;
|
||||
}
|
||||
ret = clk_prepare_enable(pltfm_host->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable host clk\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (iproc_host->data->pdata->quirks & SDHCI_QUIRK_MISSING_CAPS) {
|
||||
host->caps = iproc_host->data->caps;
|
||||
host->caps1 = iproc_host->data->caps1;
|
||||
}
|
||||
|
||||
return sdhci_add_host(host);
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk:
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
err:
|
||||
sdhci_pltfm_free(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_iproc_remove(struct platform_device *pdev)
|
||||
{
|
||||
return sdhci_pltfm_unregister(pdev);
|
||||
}
|
||||
|
||||
static struct platform_driver sdhci_iproc_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-iproc",
|
||||
@ -232,7 +254,7 @@ static struct platform_driver sdhci_iproc_driver = {
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
},
|
||||
.probe = sdhci_iproc_probe,
|
||||
.remove = sdhci_iproc_remove,
|
||||
.remove = sdhci_pltfm_unregister,
|
||||
};
|
||||
module_platform_driver(sdhci_iproc_driver);
|
||||
|
||||
|
@ -60,7 +60,6 @@ struct sdhci_msm_host {
|
||||
struct clk *pclk; /* SDHC peripheral bus clock */
|
||||
struct clk *bus_clk; /* SDHC bus voter clock */
|
||||
struct mmc_host *mmc;
|
||||
struct sdhci_pltfm_data sdhci_msm_pdata;
|
||||
};
|
||||
|
||||
/* Platform specific tuning */
|
||||
@ -418,7 +417,7 @@ static const struct of_device_id sdhci_msm_dt_match[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);
|
||||
|
||||
static struct sdhci_ops sdhci_msm_ops = {
|
||||
static const struct sdhci_ops sdhci_msm_ops = {
|
||||
.platform_execute_tuning = sdhci_msm_execute_tuning,
|
||||
.reset = sdhci_reset,
|
||||
.set_clock = sdhci_set_clock,
|
||||
@ -426,6 +425,12 @@ static struct sdhci_ops sdhci_msm_ops = {
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_msm_pdata = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||
SDHCI_QUIRK_SINGLE_POWER_WRITE,
|
||||
.ops = &sdhci_msm_ops,
|
||||
};
|
||||
|
||||
static int sdhci_msm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
@ -437,17 +442,12 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
||||
u32 core_version, caps;
|
||||
u8 core_major;
|
||||
|
||||
msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL);
|
||||
if (!msm_host)
|
||||
return -ENOMEM;
|
||||
|
||||
msm_host->sdhci_msm_pdata.ops = &sdhci_msm_ops;
|
||||
host = sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata, 0);
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_msm_pdata, sizeof(*msm_host));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = msm_host;
|
||||
msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
msm_host->mmc = host->mmc;
|
||||
msm_host->pdev = pdev;
|
||||
|
||||
@ -522,9 +522,6 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
||||
/* Set HC_MODE_EN bit in HC_MODE register */
|
||||
writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
|
||||
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||
host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE;
|
||||
|
||||
host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
|
||||
dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n",
|
||||
host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >>
|
||||
@ -570,16 +567,16 @@ static int sdhci_msm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = pltfm_host->priv;
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
int dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) ==
|
||||
0xffffffff);
|
||||
|
||||
sdhci_remove_host(host, dead);
|
||||
sdhci_pltfm_free(pdev);
|
||||
clk_disable_unprepare(msm_host->clk);
|
||||
clk_disable_unprepare(msm_host->pclk);
|
||||
if (!IS_ERR(msm_host->bus_clk))
|
||||
clk_disable_unprepare(msm_host->bus_clk);
|
||||
sdhci_pltfm_free(pdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
#define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c
|
||||
@ -32,9 +33,11 @@
|
||||
/**
|
||||
* struct sdhci_arasan_data
|
||||
* @clk_ahb: Pointer to the AHB clock
|
||||
* @phy: Pointer to the generic phy
|
||||
*/
|
||||
struct sdhci_arasan_data {
|
||||
struct clk *clk_ahb;
|
||||
struct phy *phy;
|
||||
};
|
||||
|
||||
static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host)
|
||||
@ -81,13 +84,22 @@ static int sdhci_arasan_suspend(struct device *dev)
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
|
||||
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
ret = sdhci_suspend_host(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!IS_ERR(sdhci_arasan->phy)) {
|
||||
ret = phy_power_off(sdhci_arasan->phy);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot power off phy.\n");
|
||||
sdhci_resume_host(host);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
clk_disable(pltfm_host->clk);
|
||||
clk_disable(sdhci_arasan->clk_ahb);
|
||||
|
||||
@ -106,7 +118,7 @@ static int sdhci_arasan_resume(struct device *dev)
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
|
||||
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(sdhci_arasan->clk_ahb);
|
||||
@ -118,10 +130,17 @@ static int sdhci_arasan_resume(struct device *dev)
|
||||
ret = clk_enable(pltfm_host->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot enable SD clock.\n");
|
||||
clk_disable(sdhci_arasan->clk_ahb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!IS_ERR(sdhci_arasan->phy)) {
|
||||
ret = phy_power_on(sdhci_arasan->phy);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot power on phy.\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return sdhci_resume_host(host);
|
||||
}
|
||||
#endif /* ! CONFIG_PM_SLEEP */
|
||||
@ -137,27 +156,32 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_arasan_data *sdhci_arasan;
|
||||
|
||||
sdhci_arasan = devm_kzalloc(&pdev->dev, sizeof(*sdhci_arasan),
|
||||
GFP_KERNEL);
|
||||
if (!sdhci_arasan)
|
||||
return -ENOMEM;
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata,
|
||||
sizeof(*sdhci_arasan));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb");
|
||||
if (IS_ERR(sdhci_arasan->clk_ahb)) {
|
||||
dev_err(&pdev->dev, "clk_ahb clock not found.\n");
|
||||
return PTR_ERR(sdhci_arasan->clk_ahb);
|
||||
ret = PTR_ERR(sdhci_arasan->clk_ahb);
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
clk_xin = devm_clk_get(&pdev->dev, "clk_xin");
|
||||
if (IS_ERR(clk_xin)) {
|
||||
dev_err(&pdev->dev, "clk_xin clock not found.\n");
|
||||
return PTR_ERR(clk_xin);
|
||||
ret = PTR_ERR(clk_xin);
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(sdhci_arasan->clk_ahb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Unable to enable AHB clock.\n");
|
||||
return ret;
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk_xin);
|
||||
@ -166,20 +190,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||
goto clk_dis_ahb;
|
||||
}
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 0);
|
||||
if (IS_ERR(host)) {
|
||||
ret = PTR_ERR(host);
|
||||
goto clk_disable_all;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node, "arasan,sdhci-4.9a")) {
|
||||
host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
|
||||
host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23;
|
||||
}
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = sdhci_arasan;
|
||||
pltfm_host->clk = clk_xin;
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
@ -188,31 +199,69 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||
goto clk_disable_all;
|
||||
}
|
||||
|
||||
sdhci_arasan->phy = ERR_PTR(-ENODEV);
|
||||
if (of_device_is_compatible(pdev->dev.of_node,
|
||||
"arasan,sdhci-5.1")) {
|
||||
sdhci_arasan->phy = devm_phy_get(&pdev->dev,
|
||||
"phy_arasan");
|
||||
if (IS_ERR(sdhci_arasan->phy)) {
|
||||
ret = PTR_ERR(sdhci_arasan->phy);
|
||||
dev_err(&pdev->dev, "No phy for arasan,sdhci-5.1.\n");
|
||||
goto clk_disable_all;
|
||||
}
|
||||
|
||||
ret = phy_init(sdhci_arasan->phy);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "phy_init err.\n");
|
||||
goto clk_disable_all;
|
||||
}
|
||||
|
||||
ret = phy_power_on(sdhci_arasan->phy);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "phy_power_on err.\n");
|
||||
goto err_phy_power;
|
||||
}
|
||||
}
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto err_pltfm_free;
|
||||
goto err_add_host;
|
||||
|
||||
return 0;
|
||||
|
||||
err_pltfm_free:
|
||||
sdhci_pltfm_free(pdev);
|
||||
err_add_host:
|
||||
if (!IS_ERR(sdhci_arasan->phy))
|
||||
phy_power_off(sdhci_arasan->phy);
|
||||
err_phy_power:
|
||||
if (!IS_ERR(sdhci_arasan->phy))
|
||||
phy_exit(sdhci_arasan->phy);
|
||||
clk_disable_all:
|
||||
clk_disable_unprepare(clk_xin);
|
||||
clk_dis_ahb:
|
||||
clk_disable_unprepare(sdhci_arasan->clk_ahb);
|
||||
|
||||
err_pltfm_free:
|
||||
sdhci_pltfm_free(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_arasan_remove(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
|
||||
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
||||
struct clk *clk_ahb = sdhci_arasan->clk_ahb;
|
||||
|
||||
clk_disable_unprepare(sdhci_arasan->clk_ahb);
|
||||
if (!IS_ERR(sdhci_arasan->phy)) {
|
||||
phy_power_off(sdhci_arasan->phy);
|
||||
phy_exit(sdhci_arasan->phy);
|
||||
}
|
||||
|
||||
return sdhci_pltfm_unregister(pdev);
|
||||
ret = sdhci_pltfm_unregister(pdev);
|
||||
|
||||
clk_disable_unprepare(clk_ahb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id sdhci_arasan_of_match[] = {
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
@ -58,7 +59,7 @@ static int sdhci_at91_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_at91_priv *priv = pltfm_host->priv;
|
||||
struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
ret = sdhci_runtime_suspend_host(host);
|
||||
@ -74,7 +75,7 @@ static int sdhci_at91_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_at91_priv *priv = pltfm_host->priv;
|
||||
struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(priv->mainck);
|
||||
@ -124,11 +125,12 @@ static int sdhci_at91_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
soc_data = match->data;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(&pdev->dev, "unable to allocate private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
host = sdhci_pltfm_init(pdev, soc_data, sizeof(*priv));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
priv = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
priv->mainck = devm_clk_get(&pdev->dev, "baseclk");
|
||||
if (IS_ERR(priv->mainck)) {
|
||||
@ -148,10 +150,6 @@ static int sdhci_at91_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(priv->gck);
|
||||
}
|
||||
|
||||
host = sdhci_pltfm_init(pdev, soc_data, 0);
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
/*
|
||||
* The mult clock is provided by as a generated clock by the PMC
|
||||
* controller. In order to set the rate of gck, we have to get the
|
||||
@ -191,9 +189,6 @@ static int sdhci_at91_probe(struct platform_device *pdev)
|
||||
clk_prepare_enable(priv->mainck);
|
||||
clk_prepare_enable(priv->gck);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = priv;
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret)
|
||||
goto clocks_disable_unprepare;
|
||||
@ -210,6 +205,25 @@ static int sdhci_at91_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto pm_runtime_disable;
|
||||
|
||||
/*
|
||||
* When calling sdhci_runtime_suspend_host(), the sdhci layer makes
|
||||
* the assumption that all the clocks of the controller are disabled.
|
||||
* It means we can't get irq from it when it is runtime suspended.
|
||||
* For that reason, it is not planned to wake-up on a card detect irq
|
||||
* from the controller.
|
||||
* If we want to use runtime PM and to be able to wake-up on card
|
||||
* insertion, we have to use a GPIO for the card detection or we can
|
||||
* use polling. Be aware that using polling will resume/suspend the
|
||||
* controller between each attempt.
|
||||
* Disable SDHCI_QUIRK_BROKEN_CARD_DETECTION to be sure nobody tries
|
||||
* to enable polling via device tree with broken-cd property.
|
||||
*/
|
||||
if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) &&
|
||||
IS_ERR_VALUE(mmc_gpio_get_cd(host->mmc))) {
|
||||
host->mmc->caps |= MMC_CAP_NEEDS_POLL;
|
||||
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||
}
|
||||
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
@ -231,7 +245,10 @@ static int sdhci_at91_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_at91_priv *priv = pltfm_host->priv;
|
||||
struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
struct clk *gck = priv->gck;
|
||||
struct clk *hclock = priv->hclock;
|
||||
struct clk *mainck = priv->mainck;
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
@ -239,9 +256,9 @@ static int sdhci_at91_remove(struct platform_device *pdev)
|
||||
|
||||
sdhci_pltfm_unregister(pdev);
|
||||
|
||||
clk_disable_unprepare(priv->gck);
|
||||
clk_disable_unprepare(priv->hclock);
|
||||
clk_disable_unprepare(priv->mainck);
|
||||
clk_disable_unprepare(gck);
|
||||
clk_disable_unprepare(hclock);
|
||||
clk_disable_unprepare(mainck);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ static u32 esdhc_readl_fixup(struct sdhci_host *host,
|
||||
int spec_reg, u32 value)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_esdhc *esdhc = pltfm_host->priv;
|
||||
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 ret;
|
||||
|
||||
/*
|
||||
@ -354,7 +354,7 @@ static void esdhc_le_writeb(struct sdhci_host *host, u8 val, int reg)
|
||||
static void esdhc_of_adma_workaround(struct sdhci_host *host, u32 intmask)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_esdhc *esdhc = pltfm_host->priv;
|
||||
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
bool applicable;
|
||||
dma_addr_t dmastart;
|
||||
dma_addr_t dmanow;
|
||||
@ -404,7 +404,7 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
|
||||
static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_esdhc *esdhc = pltfm_host->priv;
|
||||
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
int pre_div = 1;
|
||||
int div = 1;
|
||||
u32 temp;
|
||||
@ -569,15 +569,12 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
|
||||
u16 host_ver;
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
esdhc = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_esdhc),
|
||||
GFP_KERNEL);
|
||||
esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
host_ver = sdhci_readw(host, SDHCI_HOST_VERSION);
|
||||
esdhc->vendor_ver = (host_ver & SDHCI_VENDOR_VER_MASK) >>
|
||||
SDHCI_VENDOR_VER_SHIFT;
|
||||
esdhc->spec_ver = host_ver & SDHCI_SPEC_VER_MASK;
|
||||
|
||||
pltfm_host->priv = esdhc;
|
||||
}
|
||||
|
||||
static int sdhci_esdhc_probe(struct platform_device *pdev)
|
||||
@ -591,9 +588,11 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
|
||||
np = pdev->dev.of_node;
|
||||
|
||||
if (of_get_property(np, "little-endian", NULL))
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_esdhc_le_pdata, 0);
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_esdhc_le_pdata,
|
||||
sizeof(struct sdhci_esdhc));
|
||||
else
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_esdhc_be_pdata, 0);
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_esdhc_be_pdata,
|
||||
sizeof(struct sdhci_esdhc));
|
||||
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
@ -603,7 +602,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
|
||||
sdhci_get_of_property(pdev);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
esdhc = pltfm_host->priv;
|
||||
esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
if (esdhc->vendor_ver == VENDOR_V_22)
|
||||
host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23;
|
||||
|
||||
|
@ -1302,7 +1302,6 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pci_slot *slot;
|
||||
struct pci_dev *pdev;
|
||||
int ret = -1;
|
||||
|
||||
slot = sdhci_priv(host);
|
||||
pdev = slot->chip->pdev;
|
||||
@ -1314,20 +1313,6 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host)
|
||||
"doesn't fully claim to support it.\n");
|
||||
}
|
||||
|
||||
if (host->flags & SDHCI_USE_64_BIT_DMA) {
|
||||
if (host->quirks2 & SDHCI_QUIRK2_BROKEN_64_BIT_DMA) {
|
||||
host->flags &= ~SDHCI_USE_64_BIT_DMA;
|
||||
} else {
|
||||
ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
|
||||
if (ret)
|
||||
dev_warn(&pdev->dev, "Failed to set 64-bit DMA mask\n");
|
||||
}
|
||||
}
|
||||
if (ret)
|
||||
ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
return 0;
|
||||
|
257
drivers/mmc/host/sdhci-pic32.c
Normal file
257
drivers/mmc/host/sdhci-pic32.c
Normal file
@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Support of SDHCI platform devices for Microchip PIC32.
|
||||
*
|
||||
* Copyright (C) 2015 Microchip
|
||||
* Andrei Pistirica, Paul Thacker
|
||||
*
|
||||
* Inspired by sdhci-pltfm.c
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/io.h>
|
||||
#include "sdhci.h"
|
||||
#include "sdhci-pltfm.h"
|
||||
#include <linux/platform_data/sdhci-pic32.h>
|
||||
|
||||
#define SDH_SHARED_BUS_CTRL 0x000000E0
|
||||
#define SDH_SHARED_BUS_NR_CLK_PINS_MASK 0x7
|
||||
#define SDH_SHARED_BUS_NR_IRQ_PINS_MASK 0x30
|
||||
#define SDH_SHARED_BUS_CLK_PINS 0x10
|
||||
#define SDH_SHARED_BUS_IRQ_PINS 0x14
|
||||
#define SDH_CAPS_SDH_SLOT_TYPE_MASK 0xC0000000
|
||||
#define SDH_SLOT_TYPE_REMOVABLE 0x0
|
||||
#define SDH_SLOT_TYPE_EMBEDDED 0x1
|
||||
#define SDH_SLOT_TYPE_SHARED_BUS 0x2
|
||||
#define SDHCI_CTRL_CDSSEL 0x80
|
||||
#define SDHCI_CTRL_CDTLVL 0x40
|
||||
|
||||
#define ADMA_FIFO_RD_THSHLD 512
|
||||
#define ADMA_FIFO_WR_THSHLD 512
|
||||
|
||||
struct pic32_sdhci_priv {
|
||||
struct platform_device *pdev;
|
||||
struct clk *sys_clk;
|
||||
struct clk *base_clk;
|
||||
};
|
||||
|
||||
static unsigned int pic32_sdhci_get_max_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct pic32_sdhci_priv *sdhci_pdata = sdhci_priv(host);
|
||||
|
||||
return clk_get_rate(sdhci_pdata->base_clk);
|
||||
}
|
||||
|
||||
static void pic32_sdhci_set_bus_width(struct sdhci_host *host, int width)
|
||||
{
|
||||
u8 ctrl;
|
||||
|
||||
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
||||
if (width == MMC_BUS_WIDTH_8) {
|
||||
ctrl &= ~SDHCI_CTRL_4BITBUS;
|
||||
if (host->version >= SDHCI_SPEC_300)
|
||||
ctrl |= SDHCI_CTRL_8BITBUS;
|
||||
} else {
|
||||
if (host->version >= SDHCI_SPEC_300)
|
||||
ctrl &= ~SDHCI_CTRL_8BITBUS;
|
||||
if (width == MMC_BUS_WIDTH_4)
|
||||
ctrl |= SDHCI_CTRL_4BITBUS;
|
||||
else
|
||||
ctrl &= ~SDHCI_CTRL_4BITBUS;
|
||||
}
|
||||
|
||||
/* CD select and test bits must be set for errata workaround. */
|
||||
ctrl &= ~SDHCI_CTRL_CDTLVL;
|
||||
ctrl |= SDHCI_CTRL_CDSSEL;
|
||||
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
||||
}
|
||||
|
||||
static unsigned int pic32_sdhci_get_ro(struct sdhci_host *host)
|
||||
{
|
||||
/*
|
||||
* The SDHCI_WRITE_PROTECT bit is unstable on current hardware so we
|
||||
* can't depend on its value in any way.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops pic32_sdhci_ops = {
|
||||
.get_max_clock = pic32_sdhci_get_max_clock,
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_bus_width = pic32_sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
.get_ro = pic32_sdhci_get_ro,
|
||||
};
|
||||
|
||||
static struct sdhci_pltfm_data sdhci_pic32_pdata = {
|
||||
.ops = &pic32_sdhci_ops,
|
||||
.quirks = SDHCI_QUIRK_NO_HISPD_BIT,
|
||||
.quirks2 = SDHCI_QUIRK2_NO_1_8_V,
|
||||
};
|
||||
|
||||
static void pic32_sdhci_shared_bus(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
u32 bus = readl(host->ioaddr + SDH_SHARED_BUS_CTRL);
|
||||
u32 clk_pins = (bus & SDH_SHARED_BUS_NR_CLK_PINS_MASK) >> 0;
|
||||
u32 irq_pins = (bus & SDH_SHARED_BUS_NR_IRQ_PINS_MASK) >> 4;
|
||||
|
||||
/* select first clock */
|
||||
if (clk_pins & 1)
|
||||
bus |= (1 << SDH_SHARED_BUS_CLK_PINS);
|
||||
|
||||
/* select first interrupt */
|
||||
if (irq_pins & 1)
|
||||
bus |= (1 << SDH_SHARED_BUS_IRQ_PINS);
|
||||
|
||||
writel(bus, host->ioaddr + SDH_SHARED_BUS_CTRL);
|
||||
}
|
||||
|
||||
static int pic32_sdhci_probe_platform(struct platform_device *pdev,
|
||||
struct pic32_sdhci_priv *pdata)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 caps_slot_type;
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
|
||||
/* Check card slot connected on shared bus. */
|
||||
host->caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
|
||||
caps_slot_type = (host->caps & SDH_CAPS_SDH_SLOT_TYPE_MASK) >> 30;
|
||||
if (caps_slot_type == SDH_SLOT_TYPE_SHARED_BUS)
|
||||
pic32_sdhci_shared_bus(pdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pic32_sdhci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct pic32_sdhci_priv *sdhci_pdata;
|
||||
struct pic32_sdhci_platform_data *plat_data;
|
||||
int ret;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_pic32_pdata,
|
||||
sizeof(struct pic32_sdhci_priv));
|
||||
if (IS_ERR(host)) {
|
||||
ret = PTR_ERR(host);
|
||||
goto err;
|
||||
}
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
sdhci_pdata = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
plat_data = pdev->dev.platform_data;
|
||||
if (plat_data && plat_data->setup_dma) {
|
||||
ret = plat_data->setup_dma(ADMA_FIFO_RD_THSHLD,
|
||||
ADMA_FIFO_WR_THSHLD);
|
||||
if (ret)
|
||||
goto err_host;
|
||||
}
|
||||
|
||||
sdhci_pdata->sys_clk = devm_clk_get(&pdev->dev, "sys_clk");
|
||||
if (IS_ERR(sdhci_pdata->sys_clk)) {
|
||||
ret = PTR_ERR(sdhci_pdata->sys_clk);
|
||||
dev_err(&pdev->dev, "Error getting clock\n");
|
||||
goto err_host;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(sdhci_pdata->sys_clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Error enabling clock\n");
|
||||
goto err_host;
|
||||
}
|
||||
|
||||
sdhci_pdata->base_clk = devm_clk_get(&pdev->dev, "base_clk");
|
||||
if (IS_ERR(sdhci_pdata->base_clk)) {
|
||||
ret = PTR_ERR(sdhci_pdata->base_clk);
|
||||
dev_err(&pdev->dev, "Error getting clock\n");
|
||||
goto err_sys_clk;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(sdhci_pdata->base_clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Error enabling clock\n");
|
||||
goto err_base_clk;
|
||||
}
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret)
|
||||
goto err_base_clk;
|
||||
|
||||
ret = pic32_sdhci_probe_platform(pdev, sdhci_pdata);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to probe platform!\n");
|
||||
goto err_base_clk;
|
||||
}
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "error adding host\n");
|
||||
goto err_base_clk;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "Successfully added sdhci host\n");
|
||||
return 0;
|
||||
|
||||
err_base_clk:
|
||||
clk_disable_unprepare(sdhci_pdata->base_clk);
|
||||
err_sys_clk:
|
||||
clk_disable_unprepare(sdhci_pdata->sys_clk);
|
||||
err_host:
|
||||
sdhci_pltfm_free(pdev);
|
||||
err:
|
||||
dev_err(&pdev->dev, "pic32-sdhci probe failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pic32_sdhci_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct pic32_sdhci_priv *sdhci_pdata = sdhci_priv(host);
|
||||
u32 scratch;
|
||||
|
||||
scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
|
||||
sdhci_remove_host(host, scratch == (u32)~0);
|
||||
clk_disable_unprepare(sdhci_pdata->base_clk);
|
||||
clk_disable_unprepare(sdhci_pdata->sys_clk);
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id pic32_sdhci_id_table[] = {
|
||||
{ .compatible = "microchip,pic32mzda-sdhci" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pic32_sdhci_id_table);
|
||||
|
||||
static struct platform_driver pic32_sdhci_driver = {
|
||||
.driver = {
|
||||
.name = "pic32-sdhci",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(pic32_sdhci_id_table),
|
||||
},
|
||||
.probe = pic32_sdhci_probe,
|
||||
.remove = pic32_sdhci_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(pic32_sdhci_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Microchip PIC32 SDHCI driver");
|
||||
MODULE_AUTHOR("Pistirica Sorin Andrei & Sandeep Sheriker");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -23,7 +23,6 @@ struct sdhci_pltfm_data {
|
||||
|
||||
struct sdhci_pltfm_host {
|
||||
struct clk *clk;
|
||||
void *priv; /* to handle quirks across io-accessor calls */
|
||||
|
||||
/* migrate from sdhci_of_host */
|
||||
unsigned int clock;
|
||||
|
@ -177,7 +177,6 @@ static int sdhci_pxav2_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = NULL;
|
||||
|
||||
clk = clk_get(dev, "PXA-SDHCLK");
|
||||
if (IS_ERR(clk)) {
|
||||
|
@ -132,11 +132,15 @@ static int armada_38x_quirks(struct platform_device *pdev,
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_pxa *pxa = pltfm_host->priv;
|
||||
struct sdhci_pxa *pxa = sdhci_pltfm_priv(pltfm_host);
|
||||
struct resource *res;
|
||||
|
||||
host->quirks &= ~SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
|
||||
host->quirks |= SDHCI_QUIRK_MISSING_CAPS;
|
||||
|
||||
host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
|
||||
host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"conf-sdio3");
|
||||
if (res) {
|
||||
@ -150,7 +154,6 @@ static int armada_38x_quirks(struct platform_device *pdev,
|
||||
* Configuration register, if the adjustment is not done,
|
||||
* remove them from the capabilities.
|
||||
*/
|
||||
host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
|
||||
host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50);
|
||||
|
||||
dev_warn(&pdev->dev, "conf-sdio3 register not found: disabling SDR50 and DDR50 modes.\nConsider updating your dtb\n");
|
||||
@ -161,7 +164,6 @@ static int armada_38x_quirks(struct platform_device *pdev,
|
||||
* controller has different capabilities than the ones shown
|
||||
* in its registers
|
||||
*/
|
||||
host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
|
||||
if (of_property_read_bool(np, "no-1-8-v")) {
|
||||
host->caps &= ~SDHCI_CAN_VDD_180;
|
||||
host->mmc->caps &= ~MMC_CAP_1_8V_DDR;
|
||||
@ -201,7 +203,7 @@ static void pxav3_reset(struct sdhci_host *host, u8 mask)
|
||||
static void pxav3_gen_init_74_clocks(struct sdhci_host *host, u8 power_mode)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_pxa *pxa = pltfm_host->priv;
|
||||
struct sdhci_pxa *pxa = sdhci_pltfm_priv(pltfm_host);
|
||||
u16 tmp;
|
||||
int count;
|
||||
|
||||
@ -250,7 +252,7 @@ static void pxav3_gen_init_74_clocks(struct sdhci_host *host, u8 power_mode)
|
||||
static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_pxa *pxa = pltfm_host->priv;
|
||||
struct sdhci_pxa *pxa = sdhci_pltfm_priv(pltfm_host);
|
||||
u16 ctrl_2;
|
||||
|
||||
/*
|
||||
@ -370,16 +372,12 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
||||
const struct of_device_id *match;
|
||||
int ret;
|
||||
|
||||
pxa = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_pxa), GFP_KERNEL);
|
||||
if (!pxa)
|
||||
return -ENOMEM;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_pxav3_pdata, 0);
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_pxav3_pdata, sizeof(*pxa));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = pxa;
|
||||
pxa = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
pxa->clk_io = devm_clk_get(dev, "io");
|
||||
if (IS_ERR(pxa->clk_io))
|
||||
@ -486,7 +484,7 @@ static int sdhci_pxav3_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_pxa *pxa = pltfm_host->priv;
|
||||
struct sdhci_pxa *pxa = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
@ -535,7 +533,7 @@ static int sdhci_pxav3_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_pxa *pxa = pltfm_host->priv;
|
||||
struct sdhci_pxa *pxa = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
ret = sdhci_runtime_suspend_host(host);
|
||||
@ -553,7 +551,7 @@ static int sdhci_pxav3_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_pxa *pxa = pltfm_host->priv;
|
||||
struct sdhci_pxa *pxa = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
clk_prepare_enable(pxa->clk_io);
|
||||
if (!IS_ERR(pxa->clk_core))
|
||||
|
@ -251,7 +251,7 @@ static int sdhci_st_set_dll_for_clock(struct sdhci_host *host)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct st_mmc_platform_data *pdata = pltfm_host->priv;
|
||||
struct st_mmc_platform_data *pdata = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
if (host->clock > CLK_TO_CHECK_DLL_LOCK) {
|
||||
st_mmcss_set_dll(pdata->top_ioaddr);
|
||||
@ -265,7 +265,7 @@ static void sdhci_st_set_uhs_signaling(struct sdhci_host *host,
|
||||
unsigned int uhs)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct st_mmc_platform_data *pdata = pltfm_host->priv;
|
||||
struct st_mmc_platform_data *pdata = sdhci_pltfm_priv(pltfm_host);
|
||||
u16 ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
int ret = 0;
|
||||
|
||||
@ -357,10 +357,7 @@ static int sdhci_st_probe(struct platform_device *pdev)
|
||||
int ret = 0;
|
||||
u16 host_version;
|
||||
struct resource *res;
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
struct reset_control *rstc;
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, "mmc");
|
||||
if (IS_ERR(clk)) {
|
||||
@ -368,19 +365,23 @@ static int sdhci_st_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
pdata->rstc = devm_reset_control_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(pdata->rstc))
|
||||
pdata->rstc = NULL;
|
||||
rstc = devm_reset_control_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(rstc))
|
||||
rstc = NULL;
|
||||
else
|
||||
reset_control_deassert(pdata->rstc);
|
||||
reset_control_deassert(rstc);
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_st_pdata, 0);
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_st_pdata, sizeof(*pdata));
|
||||
if (IS_ERR(host)) {
|
||||
dev_err(&pdev->dev, "Failed sdhci_pltfm_init\n");
|
||||
ret = PTR_ERR(host);
|
||||
goto err_pltfm_init;
|
||||
}
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pdata = sdhci_pltfm_priv(pltfm_host);
|
||||
pdata->rstc = rstc;
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed mmc_of_parse\n");
|
||||
@ -398,8 +399,6 @@ static int sdhci_st_probe(struct platform_device *pdev)
|
||||
pdata->top_ioaddr = NULL;
|
||||
}
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = pdata;
|
||||
pltfm_host->clk = clk;
|
||||
|
||||
/* Configure the Arasan HC inside the flashSS */
|
||||
@ -427,8 +426,8 @@ err_out:
|
||||
err_of:
|
||||
sdhci_pltfm_free(pdev);
|
||||
err_pltfm_init:
|
||||
if (pdata->rstc)
|
||||
reset_control_assert(pdata->rstc);
|
||||
if (rstc)
|
||||
reset_control_assert(rstc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -437,13 +436,14 @@ static int sdhci_st_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct st_mmc_platform_data *pdata = pltfm_host->priv;
|
||||
struct st_mmc_platform_data *pdata = sdhci_pltfm_priv(pltfm_host);
|
||||
struct reset_control *rstc = pdata->rstc;
|
||||
int ret;
|
||||
|
||||
ret = sdhci_pltfm_unregister(pdev);
|
||||
|
||||
if (pdata->rstc)
|
||||
reset_control_assert(pdata->rstc);
|
||||
if (rstc)
|
||||
reset_control_assert(rstc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -453,7 +453,7 @@ static int sdhci_st_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct st_mmc_platform_data *pdata = pltfm_host->priv;
|
||||
struct st_mmc_platform_data *pdata = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret = sdhci_suspend_host(host);
|
||||
|
||||
if (ret)
|
||||
@ -471,7 +471,7 @@ static int sdhci_st_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct st_mmc_platform_data *pdata = pltfm_host->priv;
|
||||
struct st_mmc_platform_data *pdata = sdhci_pltfm_priv(pltfm_host);
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
clk_prepare_enable(pltfm_host->clk);
|
||||
|
@ -12,6 +12,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
@ -42,12 +43,17 @@
|
||||
#define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20
|
||||
#define SDHCI_MISC_CTRL_ENABLE_DDR50 0x200
|
||||
|
||||
#define SDHCI_TEGRA_AUTO_CAL_CONFIG 0x1e4
|
||||
#define SDHCI_AUTO_CAL_START BIT(31)
|
||||
#define SDHCI_AUTO_CAL_ENABLE BIT(29)
|
||||
|
||||
#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
|
||||
#define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
|
||||
#define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2)
|
||||
#define NVQUIRK_ENABLE_SDR50 BIT(3)
|
||||
#define NVQUIRK_ENABLE_SDR104 BIT(4)
|
||||
#define NVQUIRK_ENABLE_DDR50 BIT(5)
|
||||
#define NVQUIRK_HAS_PADCALIB BIT(6)
|
||||
|
||||
struct sdhci_tegra_soc_data {
|
||||
const struct sdhci_pltfm_data *pdata;
|
||||
@ -58,12 +64,13 @@ struct sdhci_tegra {
|
||||
const struct sdhci_tegra_soc_data *soc_data;
|
||||
struct gpio_desc *power_gpio;
|
||||
bool ddr_signaling;
|
||||
bool pad_calib_required;
|
||||
};
|
||||
|
||||
static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = pltfm_host->priv;
|
||||
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
|
||||
const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
|
||||
|
||||
if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
|
||||
@ -99,7 +106,7 @@ static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
|
||||
static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = pltfm_host->priv;
|
||||
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
|
||||
const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
|
||||
|
||||
/* Seems like we're getting spurious timeout and crc errors, so
|
||||
@ -131,7 +138,7 @@ static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
|
||||
static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = pltfm_host->priv;
|
||||
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
|
||||
const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
|
||||
u32 misc_ctrl, clk_ctrl;
|
||||
|
||||
@ -147,10 +154,16 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
|
||||
/* Advertise UHS modes as supported by host */
|
||||
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
|
||||
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50;
|
||||
else
|
||||
misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR50;
|
||||
if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
|
||||
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
|
||||
else
|
||||
misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_DDR50;
|
||||
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104)
|
||||
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104;
|
||||
else
|
||||
misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104;
|
||||
sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
|
||||
|
||||
clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
|
||||
@ -159,6 +172,9 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
|
||||
clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE;
|
||||
sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
|
||||
|
||||
if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
|
||||
tegra_host->pad_calib_required = true;
|
||||
|
||||
tegra_host->ddr_signaling = false;
|
||||
}
|
||||
|
||||
@ -181,27 +197,43 @@ static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width)
|
||||
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
||||
}
|
||||
|
||||
static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
mdelay(1);
|
||||
|
||||
val = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
|
||||
val |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START;
|
||||
sdhci_writel(host,val, SDHCI_TEGRA_AUTO_CAL_CONFIG);
|
||||
}
|
||||
|
||||
static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = pltfm_host->priv;
|
||||
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
|
||||
unsigned long host_clk;
|
||||
|
||||
if (!clock)
|
||||
return;
|
||||
return sdhci_set_clock(host, clock);
|
||||
|
||||
host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
|
||||
clk_set_rate(pltfm_host->clk, host_clk);
|
||||
host->max_clk = clk_get_rate(pltfm_host->clk);
|
||||
|
||||
return sdhci_set_clock(host, clock);
|
||||
sdhci_set_clock(host, clock);
|
||||
|
||||
if (tegra_host->pad_calib_required) {
|
||||
tegra_sdhci_pad_autocalib(host);
|
||||
tegra_host->pad_calib_required = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
|
||||
unsigned timing)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = pltfm_host->priv;
|
||||
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
if (timing == MMC_TIMING_UHS_DDR50)
|
||||
tegra_host->ddr_signaling = true;
|
||||
@ -264,6 +296,16 @@ static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
|
||||
return mmc_send_tuning(host->mmc, opcode, NULL);
|
||||
}
|
||||
|
||||
static void tegra_sdhci_voltage_switch(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
|
||||
const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
|
||||
|
||||
if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
|
||||
tegra_host->pad_calib_required = true;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops tegra_sdhci_ops = {
|
||||
.get_ro = tegra_sdhci_get_ro,
|
||||
.read_w = tegra_sdhci_readw,
|
||||
@ -273,6 +315,7 @@ static const struct sdhci_ops tegra_sdhci_ops = {
|
||||
.reset = tegra_sdhci_reset,
|
||||
.platform_execute_tuning = tegra_sdhci_execute_tuning,
|
||||
.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
|
||||
.voltage_switch = tegra_sdhci_voltage_switch,
|
||||
.get_max_clock = tegra_sdhci_get_max_clock,
|
||||
};
|
||||
|
||||
@ -306,7 +349,8 @@ static const struct sdhci_tegra_soc_data soc_data_tegra30 = {
|
||||
.pdata = &sdhci_tegra30_pdata,
|
||||
.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 |
|
||||
NVQUIRK_ENABLE_SDR50 |
|
||||
NVQUIRK_ENABLE_SDR104,
|
||||
NVQUIRK_ENABLE_SDR104 |
|
||||
NVQUIRK_HAS_PADCALIB,
|
||||
};
|
||||
|
||||
static const struct sdhci_ops tegra114_sdhci_ops = {
|
||||
@ -319,6 +363,7 @@ static const struct sdhci_ops tegra114_sdhci_ops = {
|
||||
.reset = tegra_sdhci_reset,
|
||||
.platform_execute_tuning = tegra_sdhci_execute_tuning,
|
||||
.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
|
||||
.voltage_switch = tegra_sdhci_voltage_switch,
|
||||
.get_max_clock = tegra_sdhci_get_max_clock,
|
||||
};
|
||||
|
||||
@ -335,9 +380,14 @@ static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
|
||||
|
||||
static const struct sdhci_tegra_soc_data soc_data_tegra114 = {
|
||||
.pdata = &sdhci_tegra114_pdata,
|
||||
};
|
||||
|
||||
static const struct sdhci_tegra_soc_data soc_data_tegra124 = {
|
||||
.pdata = &sdhci_tegra114_pdata,
|
||||
.nvquirks = NVQUIRK_ENABLE_SDR50 |
|
||||
NVQUIRK_ENABLE_DDR50 |
|
||||
NVQUIRK_ENABLE_SDR104,
|
||||
NVQUIRK_ENABLE_SDR104 |
|
||||
NVQUIRK_HAS_PADCALIB,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
|
||||
@ -357,7 +407,7 @@ static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
|
||||
|
||||
static const struct of_device_id sdhci_tegra_dt_match[] = {
|
||||
{ .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 },
|
||||
{ .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 },
|
||||
{ .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 },
|
||||
{ .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
|
||||
{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
|
||||
{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
|
||||
@ -380,20 +430,15 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
soc_data = match->data;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, soc_data->pdata, 0);
|
||||
host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
pltfm_host = sdhci_priv(host);
|
||||
|
||||
tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL);
|
||||
if (!tegra_host) {
|
||||
dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n");
|
||||
rc = -ENOMEM;
|
||||
goto err_alloc_tegra_host;
|
||||
}
|
||||
tegra_host = sdhci_pltfm_priv(pltfm_host);
|
||||
tegra_host->ddr_signaling = false;
|
||||
tegra_host->pad_calib_required = false;
|
||||
tegra_host->soc_data = soc_data;
|
||||
pltfm_host->priv = tegra_host;
|
||||
|
||||
rc = mmc_of_parse(host->mmc);
|
||||
if (rc)
|
||||
@ -429,7 +474,6 @@ err_add_host:
|
||||
err_clk_get:
|
||||
err_power_req:
|
||||
err_parse_dt:
|
||||
err_alloc_tegra_host:
|
||||
sdhci_pltfm_free(pdev);
|
||||
return rc;
|
||||
}
|
||||
|
@ -53,8 +53,6 @@ static void sdhci_finish_data(struct sdhci_host *);
|
||||
static void sdhci_finish_command(struct sdhci_host *);
|
||||
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
|
||||
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
|
||||
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
|
||||
struct mmc_data *data);
|
||||
static int sdhci_do_get_cd(struct sdhci_host *host);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
@ -428,6 +426,31 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
|
||||
DBG("PIO transfer complete.\n");
|
||||
}
|
||||
|
||||
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
|
||||
struct mmc_data *data, int cookie)
|
||||
{
|
||||
int sg_count;
|
||||
|
||||
/*
|
||||
* If the data buffers are already mapped, return the previous
|
||||
* dma_map_sg() result.
|
||||
*/
|
||||
if (data->host_cookie == COOKIE_PRE_MAPPED)
|
||||
return data->sg_count;
|
||||
|
||||
sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
||||
data->flags & MMC_DATA_WRITE ?
|
||||
DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
|
||||
if (sg_count == 0)
|
||||
return -ENOSPC;
|
||||
|
||||
data->sg_count = sg_count;
|
||||
data->host_cookie = cookie;
|
||||
|
||||
return sg_count;
|
||||
}
|
||||
|
||||
static char *sdhci_kmap_atomic(struct scatterlist *sg, unsigned long *flags)
|
||||
{
|
||||
local_irq_save(*flags);
|
||||
@ -462,41 +485,22 @@ static void sdhci_adma_mark_end(void *desc)
|
||||
dma_desc->cmd |= cpu_to_le16(ADMA2_END);
|
||||
}
|
||||
|
||||
static int sdhci_adma_table_pre(struct sdhci_host *host,
|
||||
struct mmc_data *data)
|
||||
static void sdhci_adma_table_pre(struct sdhci_host *host,
|
||||
struct mmc_data *data, int sg_count)
|
||||
{
|
||||
int direction;
|
||||
|
||||
void *desc;
|
||||
void *align;
|
||||
dma_addr_t addr;
|
||||
dma_addr_t align_addr;
|
||||
int len, offset;
|
||||
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
char *buffer;
|
||||
unsigned long flags;
|
||||
dma_addr_t addr, align_addr;
|
||||
void *desc, *align;
|
||||
char *buffer;
|
||||
int len, offset, i;
|
||||
|
||||
/*
|
||||
* The spec does not specify endianness of descriptor table.
|
||||
* We currently guess that it is LE.
|
||||
*/
|
||||
|
||||
if (data->flags & MMC_DATA_READ)
|
||||
direction = DMA_FROM_DEVICE;
|
||||
else
|
||||
direction = DMA_TO_DEVICE;
|
||||
|
||||
host->align_addr = dma_map_single(mmc_dev(host->mmc),
|
||||
host->align_buffer, host->align_buffer_sz, direction);
|
||||
if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
|
||||
goto fail;
|
||||
BUG_ON(host->align_addr & SDHCI_ADMA2_MASK);
|
||||
|
||||
host->sg_count = sdhci_pre_dma_transfer(host, data);
|
||||
if (host->sg_count < 0)
|
||||
goto unmap_align;
|
||||
host->sg_count = sg_count;
|
||||
|
||||
desc = host->adma_table;
|
||||
align = host->align_buffer;
|
||||
@ -508,10 +512,9 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
||||
len = sg_dma_len(sg);
|
||||
|
||||
/*
|
||||
* The SDHCI specification states that ADMA
|
||||
* addresses must be 32-bit aligned. If they
|
||||
* aren't, then we use a bounce buffer for
|
||||
* the (up to three) bytes that screw up the
|
||||
* The SDHCI specification states that ADMA addresses must
|
||||
* be 32-bit aligned. If they aren't, then we use a bounce
|
||||
* buffer for the (up to three) bytes that screw up the
|
||||
* alignment.
|
||||
*/
|
||||
offset = (SDHCI_ADMA2_ALIGN - (addr & SDHCI_ADMA2_MASK)) &
|
||||
@ -555,92 +558,56 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
||||
}
|
||||
|
||||
if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) {
|
||||
/*
|
||||
* Mark the last descriptor as the terminating descriptor
|
||||
*/
|
||||
/* Mark the last descriptor as the terminating descriptor */
|
||||
if (desc != host->adma_table) {
|
||||
desc -= host->desc_sz;
|
||||
sdhci_adma_mark_end(desc);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Add a terminating entry.
|
||||
*/
|
||||
|
||||
/* nop, end, valid */
|
||||
/* Add a terminating entry - nop, end, valid */
|
||||
sdhci_adma_write_desc(host, desc, 0, 0, ADMA2_NOP_END_VALID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Resync align buffer as we might have changed it.
|
||||
*/
|
||||
if (data->flags & MMC_DATA_WRITE) {
|
||||
dma_sync_single_for_device(mmc_dev(host->mmc),
|
||||
host->align_addr, host->align_buffer_sz, direction);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unmap_align:
|
||||
dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
|
||||
host->align_buffer_sz, direction);
|
||||
fail:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void sdhci_adma_table_post(struct sdhci_host *host,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
int direction;
|
||||
|
||||
struct scatterlist *sg;
|
||||
int i, size;
|
||||
void *align;
|
||||
char *buffer;
|
||||
unsigned long flags;
|
||||
bool has_unaligned;
|
||||
|
||||
if (data->flags & MMC_DATA_READ)
|
||||
direction = DMA_FROM_DEVICE;
|
||||
else
|
||||
direction = DMA_TO_DEVICE;
|
||||
if (data->flags & MMC_DATA_READ) {
|
||||
bool has_unaligned = false;
|
||||
|
||||
dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
|
||||
host->align_buffer_sz, direction);
|
||||
|
||||
/* Do a quick scan of the SG list for any unaligned mappings */
|
||||
has_unaligned = false;
|
||||
for_each_sg(data->sg, sg, host->sg_count, i)
|
||||
if (sg_dma_address(sg) & SDHCI_ADMA2_MASK) {
|
||||
has_unaligned = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (has_unaligned && data->flags & MMC_DATA_READ) {
|
||||
dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg,
|
||||
data->sg_len, direction);
|
||||
|
||||
align = host->align_buffer;
|
||||
|
||||
for_each_sg(data->sg, sg, host->sg_count, i) {
|
||||
/* Do a quick scan of the SG list for any unaligned mappings */
|
||||
for_each_sg(data->sg, sg, host->sg_count, i)
|
||||
if (sg_dma_address(sg) & SDHCI_ADMA2_MASK) {
|
||||
size = SDHCI_ADMA2_ALIGN -
|
||||
(sg_dma_address(sg) & SDHCI_ADMA2_MASK);
|
||||
has_unaligned = true;
|
||||
break;
|
||||
}
|
||||
|
||||
buffer = sdhci_kmap_atomic(sg, &flags);
|
||||
memcpy(buffer, align, size);
|
||||
sdhci_kunmap_atomic(buffer, &flags);
|
||||
if (has_unaligned) {
|
||||
dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg,
|
||||
data->sg_len, DMA_FROM_DEVICE);
|
||||
|
||||
align += SDHCI_ADMA2_ALIGN;
|
||||
align = host->align_buffer;
|
||||
|
||||
for_each_sg(data->sg, sg, host->sg_count, i) {
|
||||
if (sg_dma_address(sg) & SDHCI_ADMA2_MASK) {
|
||||
size = SDHCI_ADMA2_ALIGN -
|
||||
(sg_dma_address(sg) & SDHCI_ADMA2_MASK);
|
||||
|
||||
buffer = sdhci_kmap_atomic(sg, &flags);
|
||||
memcpy(buffer, align, size);
|
||||
sdhci_kunmap_atomic(buffer, &flags);
|
||||
|
||||
align += SDHCI_ADMA2_ALIGN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data->host_cookie == COOKIE_MAPPED) {
|
||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
||||
data->sg_len, direction);
|
||||
data->host_cookie = COOKIE_UNMAPPED;
|
||||
}
|
||||
}
|
||||
|
||||
static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
@ -666,9 +633,20 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
if (!data)
|
||||
target_timeout = cmd->busy_timeout * 1000;
|
||||
else {
|
||||
target_timeout = data->timeout_ns / 1000;
|
||||
if (host->clock)
|
||||
target_timeout += data->timeout_clks / host->clock;
|
||||
target_timeout = DIV_ROUND_UP(data->timeout_ns, 1000);
|
||||
if (host->clock && data->timeout_clks) {
|
||||
unsigned long long val;
|
||||
|
||||
/*
|
||||
* data->timeout_clks is in units of clock cycles.
|
||||
* host->clock is in Hz. target_timeout is in us.
|
||||
* Hence, us = 1000000 * cycles / Hz. Round up.
|
||||
*/
|
||||
val = 1000000 * data->timeout_clks;
|
||||
if (do_div(val, host->clock))
|
||||
target_timeout++;
|
||||
target_timeout += val;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -729,7 +707,6 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
{
|
||||
u8 ctrl;
|
||||
struct mmc_data *data = cmd->data;
|
||||
int ret;
|
||||
|
||||
WARN_ON(host->data);
|
||||
|
||||
@ -748,63 +725,48 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
host->data_early = 0;
|
||||
host->data->bytes_xfered = 0;
|
||||
|
||||
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA))
|
||||
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
||||
struct scatterlist *sg;
|
||||
unsigned int length_mask, offset_mask;
|
||||
int i;
|
||||
|
||||
host->flags |= SDHCI_REQ_USE_DMA;
|
||||
|
||||
/*
|
||||
* FIXME: This doesn't account for merging when mapping the
|
||||
* scatterlist.
|
||||
*/
|
||||
if (host->flags & SDHCI_REQ_USE_DMA) {
|
||||
int broken, i;
|
||||
struct scatterlist *sg;
|
||||
|
||||
broken = 0;
|
||||
/*
|
||||
* FIXME: This doesn't account for merging when mapping the
|
||||
* scatterlist.
|
||||
*
|
||||
* The assumption here being that alignment and lengths are
|
||||
* the same after DMA mapping to device address space.
|
||||
*/
|
||||
length_mask = 0;
|
||||
offset_mask = 0;
|
||||
if (host->flags & SDHCI_USE_ADMA) {
|
||||
if (host->quirks & SDHCI_QUIRK_32BIT_ADMA_SIZE)
|
||||
broken = 1;
|
||||
if (host->quirks & SDHCI_QUIRK_32BIT_ADMA_SIZE) {
|
||||
length_mask = 3;
|
||||
/*
|
||||
* As we use up to 3 byte chunks to work
|
||||
* around alignment problems, we need to
|
||||
* check the offset as well.
|
||||
*/
|
||||
offset_mask = 3;
|
||||
}
|
||||
} else {
|
||||
if (host->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE)
|
||||
broken = 1;
|
||||
length_mask = 3;
|
||||
if (host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR)
|
||||
offset_mask = 3;
|
||||
}
|
||||
|
||||
if (unlikely(broken)) {
|
||||
if (unlikely(length_mask | offset_mask)) {
|
||||
for_each_sg(data->sg, sg, data->sg_len, i) {
|
||||
if (sg->length & 0x3) {
|
||||
if (sg->length & length_mask) {
|
||||
DBG("Reverting to PIO because of transfer size (%d)\n",
|
||||
sg->length);
|
||||
sg->length);
|
||||
host->flags &= ~SDHCI_REQ_USE_DMA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The assumption here being that alignment is the same after
|
||||
* translation to device address space.
|
||||
*/
|
||||
if (host->flags & SDHCI_REQ_USE_DMA) {
|
||||
int broken, i;
|
||||
struct scatterlist *sg;
|
||||
|
||||
broken = 0;
|
||||
if (host->flags & SDHCI_USE_ADMA) {
|
||||
/*
|
||||
* As we use 3 byte chunks to work around
|
||||
* alignment problems, we need to check this
|
||||
* quirk.
|
||||
*/
|
||||
if (host->quirks & SDHCI_QUIRK_32BIT_ADMA_SIZE)
|
||||
broken = 1;
|
||||
} else {
|
||||
if (host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR)
|
||||
broken = 1;
|
||||
}
|
||||
|
||||
if (unlikely(broken)) {
|
||||
for_each_sg(data->sg, sg, data->sg_len, i) {
|
||||
if (sg->offset & 0x3) {
|
||||
if (sg->offset & offset_mask) {
|
||||
DBG("Reverting to PIO because of bad alignment\n");
|
||||
host->flags &= ~SDHCI_REQ_USE_DMA;
|
||||
break;
|
||||
@ -814,39 +776,27 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
}
|
||||
|
||||
if (host->flags & SDHCI_REQ_USE_DMA) {
|
||||
if (host->flags & SDHCI_USE_ADMA) {
|
||||
ret = sdhci_adma_table_pre(host, data);
|
||||
if (ret) {
|
||||
/*
|
||||
* This only happens when someone fed
|
||||
* us an invalid request.
|
||||
*/
|
||||
WARN_ON(1);
|
||||
host->flags &= ~SDHCI_REQ_USE_DMA;
|
||||
} else {
|
||||
sdhci_writel(host, host->adma_addr,
|
||||
SDHCI_ADMA_ADDRESS);
|
||||
if (host->flags & SDHCI_USE_64_BIT_DMA)
|
||||
sdhci_writel(host,
|
||||
(u64)host->adma_addr >> 32,
|
||||
SDHCI_ADMA_ADDRESS_HI);
|
||||
}
|
||||
} else {
|
||||
int sg_cnt;
|
||||
int sg_cnt = sdhci_pre_dma_transfer(host, data, COOKIE_MAPPED);
|
||||
|
||||
sg_cnt = sdhci_pre_dma_transfer(host, data);
|
||||
if (sg_cnt <= 0) {
|
||||
/*
|
||||
* This only happens when someone fed
|
||||
* us an invalid request.
|
||||
*/
|
||||
WARN_ON(1);
|
||||
host->flags &= ~SDHCI_REQ_USE_DMA;
|
||||
} else {
|
||||
WARN_ON(sg_cnt != 1);
|
||||
sdhci_writel(host, sg_dma_address(data->sg),
|
||||
SDHCI_DMA_ADDRESS);
|
||||
}
|
||||
if (sg_cnt <= 0) {
|
||||
/*
|
||||
* This only happens when someone fed
|
||||
* us an invalid request.
|
||||
*/
|
||||
WARN_ON(1);
|
||||
host->flags &= ~SDHCI_REQ_USE_DMA;
|
||||
} else if (host->flags & SDHCI_USE_ADMA) {
|
||||
sdhci_adma_table_pre(host, data, sg_cnt);
|
||||
|
||||
sdhci_writel(host, host->adma_addr, SDHCI_ADMA_ADDRESS);
|
||||
if (host->flags & SDHCI_USE_64_BIT_DMA)
|
||||
sdhci_writel(host,
|
||||
(u64)host->adma_addr >> 32,
|
||||
SDHCI_ADMA_ADDRESS_HI);
|
||||
} else {
|
||||
WARN_ON(sg_cnt != 1);
|
||||
sdhci_writel(host, sg_dma_address(data->sg),
|
||||
SDHCI_DMA_ADDRESS);
|
||||
}
|
||||
}
|
||||
|
||||
@ -946,19 +896,9 @@ static void sdhci_finish_data(struct sdhci_host *host)
|
||||
data = host->data;
|
||||
host->data = NULL;
|
||||
|
||||
if (host->flags & SDHCI_REQ_USE_DMA) {
|
||||
if (host->flags & SDHCI_USE_ADMA)
|
||||
sdhci_adma_table_post(host, data);
|
||||
else {
|
||||
if (data->host_cookie == COOKIE_MAPPED) {
|
||||
dma_unmap_sg(mmc_dev(host->mmc),
|
||||
data->sg, data->sg_len,
|
||||
(data->flags & MMC_DATA_READ) ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
data->host_cookie = COOKIE_UNMAPPED;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((host->flags & (SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA)) ==
|
||||
(SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA))
|
||||
sdhci_adma_table_post(host, data);
|
||||
|
||||
/*
|
||||
* The specification states that the block count register must
|
||||
@ -1003,6 +943,9 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
|
||||
WARN_ON(host->cmd);
|
||||
|
||||
/* Initially, a command has no error */
|
||||
cmd->error = 0;
|
||||
|
||||
/* Wait max 10 ms */
|
||||
timeout = 10;
|
||||
|
||||
@ -1097,8 +1040,6 @@ static void sdhci_finish_command(struct sdhci_host *host)
|
||||
}
|
||||
}
|
||||
|
||||
host->cmd->error = 0;
|
||||
|
||||
/* Finished CMD23, now send actual command. */
|
||||
if (host->cmd == host->mrq->sbc) {
|
||||
host->cmd = NULL;
|
||||
@ -2114,39 +2055,12 @@ static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
struct mmc_data *data = mrq->data;
|
||||
|
||||
if (host->flags & SDHCI_REQ_USE_DMA) {
|
||||
if (data->host_cookie == COOKIE_GIVEN ||
|
||||
data->host_cookie == COOKIE_MAPPED)
|
||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
||||
data->flags & MMC_DATA_WRITE ?
|
||||
DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
data->host_cookie = COOKIE_UNMAPPED;
|
||||
}
|
||||
}
|
||||
if (data->host_cookie != COOKIE_UNMAPPED)
|
||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
||||
data->flags & MMC_DATA_WRITE ?
|
||||
DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
|
||||
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
int sg_count;
|
||||
|
||||
if (data->host_cookie == COOKIE_MAPPED) {
|
||||
data->host_cookie = COOKIE_GIVEN;
|
||||
return data->sg_count;
|
||||
}
|
||||
|
||||
WARN_ON(data->host_cookie == COOKIE_GIVEN);
|
||||
|
||||
sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
||||
data->flags & MMC_DATA_WRITE ?
|
||||
DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
|
||||
if (sg_count == 0)
|
||||
return -ENOSPC;
|
||||
|
||||
data->sg_count = sg_count;
|
||||
data->host_cookie = COOKIE_MAPPED;
|
||||
|
||||
return sg_count;
|
||||
data->host_cookie = COOKIE_UNMAPPED;
|
||||
}
|
||||
|
||||
static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
||||
@ -2157,7 +2071,7 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
||||
mrq->data->host_cookie = COOKIE_UNMAPPED;
|
||||
|
||||
if (host->flags & SDHCI_REQ_USE_DMA)
|
||||
sdhci_pre_dma_transfer(host, mrq->data);
|
||||
sdhci_pre_dma_transfer(host, mrq->data, COOKIE_PRE_MAPPED);
|
||||
}
|
||||
|
||||
static void sdhci_card_event(struct mmc_host *mmc)
|
||||
@ -2237,6 +2151,22 @@ static void sdhci_tasklet_finish(unsigned long param)
|
||||
|
||||
mrq = host->mrq;
|
||||
|
||||
/*
|
||||
* Always unmap the data buffers if they were mapped by
|
||||
* sdhci_prepare_data() whenever we finish with a request.
|
||||
* This avoids leaking DMA mappings on error.
|
||||
*/
|
||||
if (host->flags & SDHCI_REQ_USE_DMA) {
|
||||
struct mmc_data *data = mrq->data;
|
||||
|
||||
if (data && data->host_cookie == COOKIE_MAPPED) {
|
||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
||||
(data->flags & MMC_DATA_READ) ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
data->host_cookie = COOKIE_UNMAPPED;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The controller needs a reset of internal state machines
|
||||
* upon error conditions.
|
||||
@ -2322,13 +2252,30 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask)
|
||||
return;
|
||||
}
|
||||
|
||||
if (intmask & SDHCI_INT_TIMEOUT)
|
||||
host->cmd->error = -ETIMEDOUT;
|
||||
else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT |
|
||||
SDHCI_INT_INDEX))
|
||||
host->cmd->error = -EILSEQ;
|
||||
if (intmask & (SDHCI_INT_TIMEOUT | SDHCI_INT_CRC |
|
||||
SDHCI_INT_END_BIT | SDHCI_INT_INDEX)) {
|
||||
if (intmask & SDHCI_INT_TIMEOUT)
|
||||
host->cmd->error = -ETIMEDOUT;
|
||||
else
|
||||
host->cmd->error = -EILSEQ;
|
||||
|
||||
/*
|
||||
* If this command initiates a data phase and a response
|
||||
* CRC error is signalled, the card can start transferring
|
||||
* data - the card may have received the command without
|
||||
* error. We must not terminate the mmc_request early.
|
||||
*
|
||||
* If the card did not receive the command or returned an
|
||||
* error which prevented it sending data, the data phase
|
||||
* will time out.
|
||||
*/
|
||||
if (host->cmd->data &&
|
||||
(intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) ==
|
||||
SDHCI_INT_CRC) {
|
||||
host->cmd = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (host->cmd->error) {
|
||||
tasklet_schedule(&host->finish_tasklet);
|
||||
return;
|
||||
}
|
||||
@ -2857,6 +2804,36 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev,
|
||||
|
||||
EXPORT_SYMBOL_GPL(sdhci_alloc_host);
|
||||
|
||||
static int sdhci_set_dma_mask(struct sdhci_host *host)
|
||||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
struct device *dev = mmc_dev(mmc);
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (host->quirks2 & SDHCI_QUIRK2_BROKEN_64_BIT_DMA)
|
||||
host->flags &= ~SDHCI_USE_64_BIT_DMA;
|
||||
|
||||
/* Try 64-bit mask if hardware is capable of it */
|
||||
if (host->flags & SDHCI_USE_64_BIT_DMA) {
|
||||
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
|
||||
if (ret) {
|
||||
pr_warn("%s: Failed to set 64-bit DMA mask.\n",
|
||||
mmc_hostname(mmc));
|
||||
host->flags &= ~SDHCI_USE_64_BIT_DMA;
|
||||
}
|
||||
}
|
||||
|
||||
/* 32-bit mask as default & fallback */
|
||||
if (ret) {
|
||||
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
pr_warn("%s: Failed to set 32-bit DMA mask.\n",
|
||||
mmc_hostname(mmc));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sdhci_add_host(struct sdhci_host *host)
|
||||
{
|
||||
struct mmc_host *mmc;
|
||||
@ -2928,17 +2905,21 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||
* SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to
|
||||
* implement.
|
||||
*/
|
||||
if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT)
|
||||
if (caps[0] & SDHCI_CAN_64BIT)
|
||||
host->flags |= SDHCI_USE_64_BIT_DMA;
|
||||
|
||||
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
||||
if (host->ops->enable_dma) {
|
||||
if (host->ops->enable_dma(host)) {
|
||||
pr_warn("%s: No suitable DMA available - falling back to PIO\n",
|
||||
mmc_hostname(mmc));
|
||||
host->flags &=
|
||||
~(SDHCI_USE_SDMA | SDHCI_USE_ADMA);
|
||||
}
|
||||
ret = sdhci_set_dma_mask(host);
|
||||
|
||||
if (!ret && host->ops->enable_dma)
|
||||
ret = host->ops->enable_dma(host);
|
||||
|
||||
if (ret) {
|
||||
pr_warn("%s: No suitable DMA available - falling back to PIO\n",
|
||||
mmc_hostname(mmc));
|
||||
host->flags &= ~(SDHCI_USE_SDMA | SDHCI_USE_ADMA);
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2947,6 +2928,9 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||
host->flags &= ~SDHCI_USE_SDMA;
|
||||
|
||||
if (host->flags & SDHCI_USE_ADMA) {
|
||||
dma_addr_t dma;
|
||||
void *buf;
|
||||
|
||||
/*
|
||||
* The DMA descriptor table size is calculated as the maximum
|
||||
* number of segments times 2, to allow for an alignment
|
||||
@ -2962,33 +2946,27 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||
SDHCI_ADMA2_32_DESC_SZ;
|
||||
host->desc_sz = SDHCI_ADMA2_32_DESC_SZ;
|
||||
}
|
||||
host->adma_table = dma_alloc_coherent(mmc_dev(mmc),
|
||||
host->adma_table_sz,
|
||||
&host->adma_addr,
|
||||
GFP_KERNEL);
|
||||
|
||||
host->align_buffer_sz = SDHCI_MAX_SEGS * SDHCI_ADMA2_ALIGN;
|
||||
host->align_buffer = kmalloc(host->align_buffer_sz, GFP_KERNEL);
|
||||
if (!host->adma_table || !host->align_buffer) {
|
||||
if (host->adma_table)
|
||||
dma_free_coherent(mmc_dev(mmc),
|
||||
host->adma_table_sz,
|
||||
host->adma_table,
|
||||
host->adma_addr);
|
||||
kfree(host->align_buffer);
|
||||
buf = dma_alloc_coherent(mmc_dev(mmc), host->align_buffer_sz +
|
||||
host->adma_table_sz, &dma, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
pr_warn("%s: Unable to allocate ADMA buffers - falling back to standard DMA\n",
|
||||
mmc_hostname(mmc));
|
||||
host->flags &= ~SDHCI_USE_ADMA;
|
||||
host->adma_table = NULL;
|
||||
host->align_buffer = NULL;
|
||||
} else if (host->adma_addr & (SDHCI_ADMA2_DESC_ALIGN - 1)) {
|
||||
} else if ((dma + host->align_buffer_sz) &
|
||||
(SDHCI_ADMA2_DESC_ALIGN - 1)) {
|
||||
pr_warn("%s: unable to allocate aligned ADMA descriptor\n",
|
||||
mmc_hostname(mmc));
|
||||
host->flags &= ~SDHCI_USE_ADMA;
|
||||
dma_free_coherent(mmc_dev(mmc), host->adma_table_sz,
|
||||
host->adma_table, host->adma_addr);
|
||||
kfree(host->align_buffer);
|
||||
host->adma_table = NULL;
|
||||
host->align_buffer = NULL;
|
||||
dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz +
|
||||
host->adma_table_sz, buf, dma);
|
||||
} else {
|
||||
host->align_buffer = buf;
|
||||
host->align_addr = dma;
|
||||
|
||||
host->adma_table = buf + host->align_buffer_sz;
|
||||
host->adma_addr = dma + host->align_buffer_sz;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3072,14 +3050,14 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||
if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
|
||||
host->timeout_clk *= 1000;
|
||||
|
||||
if (override_timeout_clk)
|
||||
host->timeout_clk = override_timeout_clk;
|
||||
|
||||
mmc->max_busy_timeout = host->ops->get_max_timeout_count ?
|
||||
host->ops->get_max_timeout_count(host) : 1 << 27;
|
||||
mmc->max_busy_timeout /= host->timeout_clk;
|
||||
}
|
||||
|
||||
if (override_timeout_clk)
|
||||
host->timeout_clk = override_timeout_clk;
|
||||
|
||||
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
|
||||
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
|
||||
|
||||
@ -3449,10 +3427,10 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
|
||||
if (!IS_ERR(mmc->supply.vqmmc))
|
||||
regulator_disable(mmc->supply.vqmmc);
|
||||
|
||||
if (host->adma_table)
|
||||
dma_free_coherent(mmc_dev(mmc), host->adma_table_sz,
|
||||
host->adma_table, host->adma_addr);
|
||||
kfree(host->align_buffer);
|
||||
if (host->align_buffer)
|
||||
dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz +
|
||||
host->adma_table_sz, host->align_buffer,
|
||||
host->align_addr);
|
||||
|
||||
host->adma_table = NULL;
|
||||
host->align_buffer = NULL;
|
||||
|
@ -316,8 +316,8 @@ struct sdhci_adma2_64_desc {
|
||||
|
||||
enum sdhci_cookie {
|
||||
COOKIE_UNMAPPED,
|
||||
COOKIE_MAPPED,
|
||||
COOKIE_GIVEN,
|
||||
COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */
|
||||
COOKIE_MAPPED, /* mapped by sdhci_prepare_data() */
|
||||
};
|
||||
|
||||
struct sdhci_host {
|
||||
|
@ -398,10 +398,10 @@ static struct mmc_host_ops sdricoh_ops = {
|
||||
static int sdricoh_init_mmc(struct pci_dev *pci_dev,
|
||||
struct pcmcia_device *pcmcia_dev)
|
||||
{
|
||||
int result = 0;
|
||||
void __iomem *iobase = NULL;
|
||||
struct mmc_host *mmc = NULL;
|
||||
struct sdricoh_host *host = NULL;
|
||||
int result;
|
||||
void __iomem *iobase;
|
||||
struct mmc_host *mmc;
|
||||
struct sdricoh_host *host;
|
||||
struct device *dev = &pcmcia_dev->dev;
|
||||
/* map iomem */
|
||||
if (pci_resource_len(pci_dev, SDRICOH_PCI_REGION) !=
|
||||
@ -419,7 +419,7 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev,
|
||||
if (readl(iobase + R104_VERSION) != 0x4000) {
|
||||
dev_dbg(dev, "no supported mmc controller found\n");
|
||||
result = -ENODEV;
|
||||
goto err;
|
||||
goto unmap_io;
|
||||
}
|
||||
/* allocate privdata */
|
||||
mmc = pcmcia_dev->priv =
|
||||
@ -427,7 +427,7 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev,
|
||||
if (!mmc) {
|
||||
dev_err(dev, "mmc_alloc_host failed\n");
|
||||
result = -ENOMEM;
|
||||
goto err;
|
||||
goto unmap_io;
|
||||
}
|
||||
host = mmc_priv(mmc);
|
||||
|
||||
@ -451,8 +451,7 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev,
|
||||
if (sdricoh_reset(host)) {
|
||||
dev_dbg(dev, "could not reset\n");
|
||||
result = -EIO;
|
||||
goto err;
|
||||
|
||||
goto free_host;
|
||||
}
|
||||
|
||||
result = mmc_add_host(mmc);
|
||||
@ -461,13 +460,10 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev,
|
||||
dev_dbg(dev, "mmc host registered\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
err:
|
||||
if (iobase)
|
||||
pci_iounmap(pci_dev, iobase);
|
||||
if (mmc)
|
||||
mmc_free_host(mmc);
|
||||
|
||||
free_host:
|
||||
mmc_free_host(mmc);
|
||||
unmap_io:
|
||||
pci_iounmap(pci_dev, iobase);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1395,7 +1395,7 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
|
||||
|
||||
static void sh_mmcif_timeout_work(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *d = container_of(work, struct delayed_work, work);
|
||||
struct delayed_work *d = to_delayed_work(work);
|
||||
struct sh_mmcif_host *host = container_of(d, struct sh_mmcif_host, timeout_work);
|
||||
struct mmc_request *mrq = host->mrq;
|
||||
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||
|
@ -1,6 +1,8 @@
|
||||
/*
|
||||
* SuperH Mobile SDHI
|
||||
*
|
||||
* Copyright (C) 2016 Sang Engineering, Wolfram Sang
|
||||
* Copyright (C) 2015-16 Renesas Electronics Corporation
|
||||
* Copyright (C) 2009 Magnus Damm
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@ -43,6 +45,7 @@ struct sh_mobile_sdhi_of_data {
|
||||
unsigned long capabilities2;
|
||||
enum dma_slave_buswidth dma_buswidth;
|
||||
dma_addr_t dma_rx_offset;
|
||||
unsigned bus_shift;
|
||||
};
|
||||
|
||||
static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
|
||||
@ -59,12 +62,19 @@ static const struct sh_mobile_sdhi_of_data of_rcar_gen1_compatible = {
|
||||
|
||||
static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = {
|
||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
|
||||
TMIO_MMC_CLK_ACTUAL,
|
||||
TMIO_MMC_CLK_ACTUAL | TMIO_MMC_FAST_CLK_CHG,
|
||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
|
||||
.dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES,
|
||||
.dma_rx_offset = 0x2000,
|
||||
};
|
||||
|
||||
static const struct sh_mobile_sdhi_of_data of_rcar_gen3_compatible = {
|
||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
|
||||
TMIO_MMC_CLK_ACTUAL | TMIO_MMC_FAST_CLK_CHG,
|
||||
.capabilities = MMC_CAP_SD_HIGHSPEED,
|
||||
.bus_shift = 2,
|
||||
};
|
||||
|
||||
static const struct of_device_id sh_mobile_sdhi_of_match[] = {
|
||||
{ .compatible = "renesas,sdhi-shmobile" },
|
||||
{ .compatible = "renesas,sdhi-sh7372" },
|
||||
@ -78,6 +88,7 @@ static const struct of_device_id sh_mobile_sdhi_of_match[] = {
|
||||
{ .compatible = "renesas,sdhi-r8a7792", .data = &of_rcar_gen2_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
|
||||
@ -103,6 +114,15 @@ static void sh_mobile_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
|
||||
case 0xCB0D:
|
||||
val = (width == 32) ? 0x0000 : 0x0001;
|
||||
break;
|
||||
case 0xCC10: /* Gen3, SD only */
|
||||
case 0xCD10: /* Gen3, SD + MMC */
|
||||
if (width == 64)
|
||||
val = 0x0000;
|
||||
else if (width == 32)
|
||||
val = 0x0101;
|
||||
else
|
||||
val = 0x0001;
|
||||
break;
|
||||
default:
|
||||
/* nothing to do */
|
||||
return;
|
||||
@ -163,6 +183,7 @@ static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)
|
||||
case CTL_SD_MEM_CARD_OPT:
|
||||
case CTL_TRANSACTION_CTL:
|
||||
case CTL_DMA_ENABLE:
|
||||
case EXT_ACC:
|
||||
return sh_mobile_sdhi_wait_idle(host);
|
||||
}
|
||||
|
||||
@ -213,10 +234,8 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
|
||||
if (priv == NULL) {
|
||||
dev_err(&pdev->dev, "kzalloc failed\n");
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mmc_data = &priv->mmc_data;
|
||||
dma_priv = &priv->dma_priv;
|
||||
@ -234,16 +253,26 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||
goto eprobe;
|
||||
}
|
||||
|
||||
if (of_id && of_id->data) {
|
||||
const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
|
||||
|
||||
mmc_data->flags |= of_data->tmio_flags;
|
||||
mmc_data->capabilities |= of_data->capabilities;
|
||||
mmc_data->capabilities2 |= of_data->capabilities2;
|
||||
mmc_data->dma_rx_offset = of_data->dma_rx_offset;
|
||||
dma_priv->dma_buswidth = of_data->dma_buswidth;
|
||||
host->bus_shift = of_data->bus_shift;
|
||||
}
|
||||
|
||||
host->dma = dma_priv;
|
||||
host->write16_hook = sh_mobile_sdhi_write16_hook;
|
||||
host->clk_enable = sh_mobile_sdhi_clk_enable;
|
||||
host->clk_disable = sh_mobile_sdhi_clk_disable;
|
||||
host->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk;
|
||||
/* SD control register space size is 0x100, 0x200 for bus_shift=1 */
|
||||
if (resource_size(res) > 0x100)
|
||||
|
||||
/* Orginally registers were 16 bit apart, could be 32 or 64 nowadays */
|
||||
if (!host->bus_shift && resource_size(res) > 0x100) /* old way to determine the shift */
|
||||
host->bus_shift = 1;
|
||||
else
|
||||
host->bus_shift = 0;
|
||||
|
||||
if (mmd)
|
||||
*mmc_data = *mmd;
|
||||
@ -275,15 +304,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||
*/
|
||||
mmc_data->flags |= TMIO_MMC_SDIO_STATUS_QUIRK;
|
||||
|
||||
if (of_id && of_id->data) {
|
||||
const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
|
||||
mmc_data->flags |= of_data->tmio_flags;
|
||||
mmc_data->capabilities |= of_data->capabilities;
|
||||
mmc_data->capabilities2 |= of_data->capabilities2;
|
||||
mmc_data->dma_rx_offset = of_data->dma_rx_offset;
|
||||
dma_priv->dma_buswidth = of_data->dma_buswidth;
|
||||
}
|
||||
|
||||
ret = tmio_mmc_host_probe(host, mmc_data);
|
||||
if (ret < 0)
|
||||
goto efree;
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_gpio.h>
|
||||
@ -214,6 +215,7 @@
|
||||
#define SDXC_CLK_25M 1
|
||||
#define SDXC_CLK_50M 2
|
||||
#define SDXC_CLK_50M_DDR 3
|
||||
#define SDXC_CLK_50M_DDR_8BIT 4
|
||||
|
||||
struct sunxi_mmc_clk_delay {
|
||||
u32 output;
|
||||
@ -256,6 +258,9 @@ struct sunxi_mmc_host {
|
||||
struct mmc_request *mrq;
|
||||
struct mmc_request *manual_stop_mrq;
|
||||
int ferror;
|
||||
|
||||
/* vqmmc */
|
||||
bool vqmmc_enabled;
|
||||
};
|
||||
|
||||
static int sunxi_mmc_reset_host(struct sunxi_mmc_host *host)
|
||||
@ -284,16 +289,28 @@ static int sunxi_mmc_init_host(struct mmc_host *mmc)
|
||||
if (sunxi_mmc_reset_host(host))
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* Burst 8 transfers, RX trigger level: 7, TX trigger level: 8
|
||||
*
|
||||
* TODO: sun9i has a larger FIFO and supports higher trigger values
|
||||
*/
|
||||
mmc_writel(host, REG_FTRGL, 0x20070008);
|
||||
/* Maximum timeout value */
|
||||
mmc_writel(host, REG_TMOUT, 0xffffffff);
|
||||
/* Unmask SDIO interrupt if needed */
|
||||
mmc_writel(host, REG_IMASK, host->sdio_imask);
|
||||
/* Clear all pending interrupts */
|
||||
mmc_writel(host, REG_RINTR, 0xffffffff);
|
||||
/* Debug register? undocumented */
|
||||
mmc_writel(host, REG_DBGC, 0xdeb);
|
||||
/* Enable CEATA support */
|
||||
mmc_writel(host, REG_FUNS, SDXC_CEATA_ON);
|
||||
/* Set DMA descriptor list base address */
|
||||
mmc_writel(host, REG_DLBA, host->sg_dma);
|
||||
|
||||
rval = mmc_readl(host, REG_GCTRL);
|
||||
rval |= SDXC_INTERRUPT_ENABLE_BIT;
|
||||
/* Undocumented, but found in Allwinner code */
|
||||
rval &= ~SDXC_ACCESS_DONE_DIRECT;
|
||||
mmc_writel(host, REG_GCTRL, rval);
|
||||
|
||||
@ -640,11 +657,17 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
|
||||
struct mmc_ios *ios)
|
||||
{
|
||||
u32 rate, oclk_dly, rval, sclk_dly;
|
||||
u32 clock = ios->clock;
|
||||
int ret;
|
||||
|
||||
rate = clk_round_rate(host->clk_mmc, ios->clock);
|
||||
/* 8 bit DDR requires a higher module clock */
|
||||
if (ios->timing == MMC_TIMING_MMC_DDR52 &&
|
||||
ios->bus_width == MMC_BUS_WIDTH_8)
|
||||
clock <<= 1;
|
||||
|
||||
rate = clk_round_rate(host->clk_mmc, clock);
|
||||
dev_dbg(mmc_dev(host->mmc), "setting clk to %d, rounded %d\n",
|
||||
ios->clock, rate);
|
||||
clock, rate);
|
||||
|
||||
/* setting clock rate */
|
||||
ret = clk_set_rate(host->clk_mmc, rate);
|
||||
@ -661,6 +684,12 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
|
||||
/* clear internal divider */
|
||||
rval = mmc_readl(host, REG_CLKCR);
|
||||
rval &= ~0xff;
|
||||
/* set internal divider for 8 bit eMMC DDR, so card clock is right */
|
||||
if (ios->timing == MMC_TIMING_MMC_DDR52 &&
|
||||
ios->bus_width == MMC_BUS_WIDTH_8) {
|
||||
rval |= 1;
|
||||
rate >>= 1;
|
||||
}
|
||||
mmc_writel(host, REG_CLKCR, rval);
|
||||
|
||||
/* determine delays */
|
||||
@ -670,13 +699,17 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
|
||||
} else if (rate <= 25000000) {
|
||||
oclk_dly = host->clk_delays[SDXC_CLK_25M].output;
|
||||
sclk_dly = host->clk_delays[SDXC_CLK_25M].sample;
|
||||
} else if (rate <= 50000000) {
|
||||
if (ios->timing == MMC_TIMING_UHS_DDR50) {
|
||||
oclk_dly = host->clk_delays[SDXC_CLK_50M_DDR].output;
|
||||
sclk_dly = host->clk_delays[SDXC_CLK_50M_DDR].sample;
|
||||
} else {
|
||||
} else if (rate <= 52000000) {
|
||||
if (ios->timing != MMC_TIMING_UHS_DDR50 &&
|
||||
ios->timing != MMC_TIMING_MMC_DDR52) {
|
||||
oclk_dly = host->clk_delays[SDXC_CLK_50M].output;
|
||||
sclk_dly = host->clk_delays[SDXC_CLK_50M].sample;
|
||||
} else if (ios->bus_width == MMC_BUS_WIDTH_8) {
|
||||
oclk_dly = host->clk_delays[SDXC_CLK_50M_DDR_8BIT].output;
|
||||
sclk_dly = host->clk_delays[SDXC_CLK_50M_DDR_8BIT].sample;
|
||||
} else {
|
||||
oclk_dly = host->clk_delays[SDXC_CLK_50M_DDR].output;
|
||||
sclk_dly = host->clk_delays[SDXC_CLK_50M_DDR].sample;
|
||||
}
|
||||
} else {
|
||||
return -EINVAL;
|
||||
@ -699,7 +732,20 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
break;
|
||||
|
||||
case MMC_POWER_UP:
|
||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
|
||||
host->ferror = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
|
||||
ios->vdd);
|
||||
if (host->ferror)
|
||||
return;
|
||||
|
||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||
host->ferror = regulator_enable(mmc->supply.vqmmc);
|
||||
if (host->ferror) {
|
||||
dev_err(mmc_dev(mmc),
|
||||
"failed to enable vqmmc\n");
|
||||
return;
|
||||
}
|
||||
host->vqmmc_enabled = true;
|
||||
}
|
||||
|
||||
host->ferror = sunxi_mmc_init_host(mmc);
|
||||
if (host->ferror)
|
||||
@ -712,6 +758,9 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
dev_dbg(mmc_dev(mmc), "power off!\n");
|
||||
sunxi_mmc_reset_host(host);
|
||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
|
||||
if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled)
|
||||
regulator_disable(mmc->supply.vqmmc);
|
||||
host->vqmmc_enabled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -730,7 +779,8 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
|
||||
/* set ddr mode */
|
||||
rval = mmc_readl(host, REG_GCTRL);
|
||||
if (ios->timing == MMC_TIMING_UHS_DDR50)
|
||||
if (ios->timing == MMC_TIMING_UHS_DDR50 ||
|
||||
ios->timing == MMC_TIMING_MMC_DDR52)
|
||||
rval |= SDXC_DDR_MODE;
|
||||
else
|
||||
rval &= ~SDXC_DDR_MODE;
|
||||
@ -743,6 +793,19 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
}
|
||||
}
|
||||
|
||||
static int sunxi_mmc_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
/* vqmmc regulator is available */
|
||||
if (!IS_ERR(mmc->supply.vqmmc))
|
||||
return mmc_regulator_set_vqmmc(mmc, ios);
|
||||
|
||||
/* no vqmmc regulator, assume fixed regulator at 3/3.3V */
|
||||
if (mmc->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330)
|
||||
return 0;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void sunxi_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||
{
|
||||
struct sunxi_mmc_host *host = mmc_priv(mmc);
|
||||
@ -815,11 +878,6 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
|
||||
if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC) {
|
||||
cmd_val |= SDXC_DATA_EXPIRE | SDXC_WAIT_PRE_OVER;
|
||||
if (cmd->data->flags & MMC_DATA_STREAM) {
|
||||
imask |= SDXC_AUTO_COMMAND_DONE;
|
||||
cmd_val |= SDXC_SEQUENCE_MODE |
|
||||
SDXC_SEND_AUTO_STOP;
|
||||
}
|
||||
|
||||
if (cmd->data->stop) {
|
||||
imask |= SDXC_AUTO_COMMAND_DONE;
|
||||
@ -894,6 +952,7 @@ static struct mmc_host_ops sunxi_mmc_ops = {
|
||||
.get_ro = mmc_gpio_get_ro,
|
||||
.get_cd = mmc_gpio_get_cd,
|
||||
.enable_sdio_irq = sunxi_mmc_enable_sdio_irq,
|
||||
.start_signal_voltage_switch = sunxi_mmc_volt_switch,
|
||||
.hw_reset = sunxi_mmc_hw_reset,
|
||||
.card_busy = sunxi_mmc_card_busy,
|
||||
};
|
||||
@ -903,6 +962,8 @@ static const struct sunxi_mmc_clk_delay sunxi_mmc_clk_delays[] = {
|
||||
[SDXC_CLK_25M] = { .output = 180, .sample = 75 },
|
||||
[SDXC_CLK_50M] = { .output = 90, .sample = 120 },
|
||||
[SDXC_CLK_50M_DDR] = { .output = 60, .sample = 120 },
|
||||
/* Value from A83T "new timing mode". Works but might not be right. */
|
||||
[SDXC_CLK_50M_DDR_8BIT] = { .output = 90, .sample = 180 },
|
||||
};
|
||||
|
||||
static const struct sunxi_mmc_clk_delay sun9i_mmc_clk_delays[] = {
|
||||
@ -910,6 +971,7 @@ static const struct sunxi_mmc_clk_delay sun9i_mmc_clk_delays[] = {
|
||||
[SDXC_CLK_25M] = { .output = 180, .sample = 75 },
|
||||
[SDXC_CLK_50M] = { .output = 150, .sample = 120 },
|
||||
[SDXC_CLK_50M_DDR] = { .output = 90, .sample = 120 },
|
||||
[SDXC_CLK_50M_DDR_8BIT] = { .output = 90, .sample = 120 },
|
||||
};
|
||||
|
||||
static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
|
||||
@ -1060,10 +1122,11 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
|
||||
mmc->max_segs = PAGE_SIZE / sizeof(struct sunxi_idma_des);
|
||||
mmc->max_seg_size = (1 << host->idma_des_size_bits);
|
||||
mmc->max_req_size = mmc->max_seg_size * mmc->max_segs;
|
||||
/* 400kHz ~ 50MHz */
|
||||
/* 400kHz ~ 52MHz */
|
||||
mmc->f_min = 400000;
|
||||
mmc->f_max = 50000000;
|
||||
mmc->f_max = 52000000;
|
||||
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
|
||||
MMC_CAP_1_8V_DDR |
|
||||
MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ;
|
||||
|
||||
ret = mmc_of_parse(mmc);
|
||||
|
@ -94,10 +94,7 @@ static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host)
|
||||
desc = NULL;
|
||||
ret = cookie;
|
||||
}
|
||||
dev_dbg(&host->pdev->dev, "%s(): mapped %d -> %d, cookie %d, rq %p\n",
|
||||
__func__, host->sg_len, ret, cookie, host->mrq);
|
||||
}
|
||||
|
||||
pio:
|
||||
if (!desc) {
|
||||
/* DMA failed, fall back to PIO */
|
||||
@ -115,9 +112,6 @@ pio:
|
||||
dev_warn(&host->pdev->dev,
|
||||
"DMA failed: %d, falling back to PIO\n", ret);
|
||||
}
|
||||
|
||||
dev_dbg(&host->pdev->dev, "%s(): desc %p, sg[%d]\n", __func__,
|
||||
desc, host->sg_len);
|
||||
}
|
||||
|
||||
static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host)
|
||||
@ -174,10 +168,7 @@ static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host)
|
||||
desc = NULL;
|
||||
ret = cookie;
|
||||
}
|
||||
dev_dbg(&host->pdev->dev, "%s(): mapped %d -> %d, cookie %d, rq %p\n",
|
||||
__func__, host->sg_len, ret, cookie, host->mrq);
|
||||
}
|
||||
|
||||
pio:
|
||||
if (!desc) {
|
||||
/* DMA failed, fall back to PIO */
|
||||
@ -195,8 +186,6 @@ pio:
|
||||
dev_warn(&host->pdev->dev,
|
||||
"DMA failed: %d, falling back to PIO\n", ret);
|
||||
}
|
||||
|
||||
dev_dbg(&host->pdev->dev, "%s(): desc %p\n", __func__, desc);
|
||||
}
|
||||
|
||||
void tmio_mmc_start_dma(struct tmio_mmc_host *host,
|
||||
|
@ -1,6 +1,8 @@
|
||||
/*
|
||||
* linux/drivers/mmc/host/tmio_mmc_pio.c
|
||||
*
|
||||
* Copyright (C) 2016 Sang Engineering, Wolfram Sang
|
||||
* Copyright (C) 2015-16 Renesas Electronics Corporation
|
||||
* Copyright (C) 2011 Guennadi Liakhovetski
|
||||
* Copyright (C) 2007 Ian Molton
|
||||
* Copyright (C) 2004 Ian Molton
|
||||
@ -159,42 +161,44 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host,
|
||||
|
||||
if (new_clock) {
|
||||
for (clock = host->mmc->f_min, clk = 0x80000080;
|
||||
new_clock >= (clock<<1); clk >>= 1)
|
||||
new_clock >= (clock << 1);
|
||||
clk >>= 1)
|
||||
clock <<= 1;
|
||||
|
||||
/* 1/1 clock is option */
|
||||
if ((host->pdata->flags & TMIO_MMC_CLK_ACTUAL) &&
|
||||
((clk >> 22) & 0x1))
|
||||
((clk >> 22) & 0x1))
|
||||
clk |= 0xff;
|
||||
}
|
||||
|
||||
if (host->set_clk_div)
|
||||
host->set_clk_div(host->pdev, (clk>>22) & 1);
|
||||
host->set_clk_div(host->pdev, (clk >> 22) & 1);
|
||||
|
||||
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & 0x1ff);
|
||||
msleep(10);
|
||||
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
|
||||
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_FAST_CLK_CHG))
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
|
||||
{
|
||||
/* implicit BUG_ON(!res) */
|
||||
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
|
||||
sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000);
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~0x0100 &
|
||||
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
|
||||
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
|
||||
msleep(10);
|
||||
msleep(host->pdata->flags & TMIO_MMC_FAST_CLK_CHG ? 5 : 10);
|
||||
}
|
||||
|
||||
static void tmio_mmc_clk_start(struct tmio_mmc_host *host)
|
||||
{
|
||||
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |
|
||||
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN |
|
||||
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
|
||||
msleep(10);
|
||||
msleep(host->pdata->flags & TMIO_MMC_FAST_CLK_CHG ? 1 : 10);
|
||||
|
||||
/* implicit BUG_ON(!res) */
|
||||
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
|
||||
sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100);
|
||||
msleep(10);
|
||||
@ -205,7 +209,6 @@ static void tmio_mmc_reset(struct tmio_mmc_host *host)
|
||||
{
|
||||
/* FIXME - should we set stop clock reg here */
|
||||
sd_ctrl_write16(host, CTL_RESET_SD, 0x0000);
|
||||
/* implicit BUG_ON(!res) */
|
||||
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
|
||||
sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);
|
||||
msleep(10);
|
||||
|
@ -1630,7 +1630,7 @@ static irqreturn_t usdhi6_cd(int irq, void *dev_id)
|
||||
*/
|
||||
static void usdhi6_timeout_work(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *d = container_of(work, struct delayed_work, work);
|
||||
struct delayed_work *d = to_delayed_work(work);
|
||||
struct usdhi6_host *host = container_of(d, struct usdhi6_host, timeout_work);
|
||||
struct mmc_request *mrq = host->mrq;
|
||||
struct mmc_data *data = mrq ? mrq->data : NULL;
|
||||
|
@ -65,6 +65,10 @@
|
||||
* Some controllers can support SDIO IRQ signalling.
|
||||
*/
|
||||
#define TMIO_MMC_SDIO_IRQ (1 << 2)
|
||||
|
||||
/* Some controllers don't need to wait 10ms for clock changes */
|
||||
#define TMIO_MMC_FAST_CLK_CHG (1 << 3)
|
||||
|
||||
/*
|
||||
* Some controllers require waiting for the SD bus to become
|
||||
* idle before writing to some registers.
|
||||
|
@ -113,7 +113,6 @@ struct mmc_data {
|
||||
|
||||
#define MMC_DATA_WRITE (1 << 8)
|
||||
#define MMC_DATA_READ (1 << 9)
|
||||
#define MMC_DATA_STREAM (1 << 10)
|
||||
|
||||
unsigned int bytes_xfered;
|
||||
|
||||
|
@ -235,21 +235,11 @@ struct dw_mci_dma_ops {
|
||||
};
|
||||
|
||||
/* IP Quirks/flags. */
|
||||
/* Unreliable card detection */
|
||||
#define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(0)
|
||||
/* Timer for broken data transfer over scheme */
|
||||
#define DW_MCI_QUIRK_BROKEN_DTO BIT(1)
|
||||
#define DW_MCI_QUIRK_BROKEN_DTO BIT(0)
|
||||
|
||||
struct dma_pdata;
|
||||
|
||||
struct block_settings {
|
||||
unsigned short max_segs; /* see blk_queue_max_segments */
|
||||
unsigned int max_blk_size; /* maximum size of one mmc block */
|
||||
unsigned int max_blk_count; /* maximum number of blocks in one req*/
|
||||
unsigned int max_req_size; /* maximum number of bytes in one req*/
|
||||
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
|
||||
};
|
||||
|
||||
/* Board platform data */
|
||||
struct dw_mci_board {
|
||||
u32 num_slots;
|
||||
|
@ -1,6 +1,8 @@
|
||||
/*
|
||||
* include/linux/mmc/tmio.h
|
||||
*
|
||||
* Copyright (C) 2016 Sang Engineering, Wolfram Sang
|
||||
* Copyright (C) 2015-16 Renesas Electronics Corporation
|
||||
* Copyright (C) 2007 Ian Molton
|
||||
* Copyright (C) 2004 Ian Molton
|
||||
*
|
||||
@ -61,6 +63,9 @@
|
||||
#define TMIO_STAT_CMD_BUSY 0x40000000
|
||||
#define TMIO_STAT_ILL_ACCESS 0x80000000
|
||||
|
||||
#define CLK_CTL_DIV_MASK 0xff
|
||||
#define CLK_CTL_SCLKEN BIT(8)
|
||||
|
||||
#define TMIO_BBS 512 /* Boot block size */
|
||||
|
||||
#endif /* LINUX_MMC_TMIO_H */
|
||||
|
Loading…
Reference in New Issue
Block a user