| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * linux/drivers/mmc/host/sdhci_f_sdh30.c |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2013 - 2015 Fujitsu Semiconductor, Ltd |
|---|
| 5 | 6 | * Vincent Yang <vincent.yang@tw.fujitsu.com> |
|---|
| 6 | 7 | * Copyright (C) 2015 Linaro Ltd Andy Green <andy.green@linaro.org> |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is free software: you can redistribute it and/or modify |
|---|
| 9 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 10 | | - * the Free Software Foundation, version 2 of the License. |
|---|
| 11 | 8 | */ |
|---|
| 12 | 9 | |
|---|
| 13 | 10 | #include <linux/acpi.h> |
|---|
| .. | .. |
|---|
| 19 | 16 | #include <linux/clk.h> |
|---|
| 20 | 17 | |
|---|
| 21 | 18 | #include "sdhci-pltfm.h" |
|---|
| 22 | | - |
|---|
| 23 | | -/* F_SDH30 extended Controller registers */ |
|---|
| 24 | | -#define F_SDH30_AHB_CONFIG 0x100 |
|---|
| 25 | | -#define F_SDH30_AHB_BIGED 0x00000040 |
|---|
| 26 | | -#define F_SDH30_BUSLOCK_DMA 0x00000020 |
|---|
| 27 | | -#define F_SDH30_BUSLOCK_EN 0x00000010 |
|---|
| 28 | | -#define F_SDH30_SIN 0x00000008 |
|---|
| 29 | | -#define F_SDH30_AHB_INCR_16 0x00000004 |
|---|
| 30 | | -#define F_SDH30_AHB_INCR_8 0x00000002 |
|---|
| 31 | | -#define F_SDH30_AHB_INCR_4 0x00000001 |
|---|
| 32 | | - |
|---|
| 33 | | -#define F_SDH30_TUNING_SETTING 0x108 |
|---|
| 34 | | -#define F_SDH30_CMD_CHK_DIS 0x00010000 |
|---|
| 35 | | - |
|---|
| 36 | | -#define F_SDH30_IO_CONTROL2 0x114 |
|---|
| 37 | | -#define F_SDH30_CRES_O_DN 0x00080000 |
|---|
| 38 | | -#define F_SDH30_MSEL_O_1_8 0x00040000 |
|---|
| 39 | | - |
|---|
| 40 | | -#define F_SDH30_ESD_CONTROL 0x124 |
|---|
| 41 | | -#define F_SDH30_EMMC_RST 0x00000002 |
|---|
| 42 | | -#define F_SDH30_EMMC_HS200 0x01000000 |
|---|
| 43 | | - |
|---|
| 44 | | -#define F_SDH30_CMD_DAT_DELAY 0x200 |
|---|
| 45 | | - |
|---|
| 46 | | -#define F_SDH30_MIN_CLOCK 400000 |
|---|
| 19 | +#include "sdhci_f_sdh30.h" |
|---|
| 47 | 20 | |
|---|
| 48 | 21 | struct f_sdhost_priv { |
|---|
| 49 | 22 | struct clk *clk_iface; |
|---|
| .. | .. |
|---|
| 53 | 26 | bool enable_cmd_dat_delay; |
|---|
| 54 | 27 | }; |
|---|
| 55 | 28 | |
|---|
| 29 | +static void *sdhci_f_sdhost_priv(struct sdhci_host *host) |
|---|
| 30 | +{ |
|---|
| 31 | + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
|---|
| 32 | + |
|---|
| 33 | + return sdhci_pltfm_priv(pltfm_host); |
|---|
| 34 | +} |
|---|
| 35 | + |
|---|
| 56 | 36 | static void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host) |
|---|
| 57 | 37 | { |
|---|
| 58 | | - struct f_sdhost_priv *priv = sdhci_priv(host); |
|---|
| 38 | + struct f_sdhost_priv *priv = sdhci_f_sdhost_priv(host); |
|---|
| 59 | 39 | u32 ctrl = 0; |
|---|
| 60 | 40 | |
|---|
| 61 | 41 | usleep_range(2500, 3000); |
|---|
| .. | .. |
|---|
| 88 | 68 | |
|---|
| 89 | 69 | static void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask) |
|---|
| 90 | 70 | { |
|---|
| 91 | | - struct f_sdhost_priv *priv = sdhci_priv(host); |
|---|
| 71 | + struct f_sdhost_priv *priv = sdhci_f_sdhost_priv(host); |
|---|
| 92 | 72 | u32 ctl; |
|---|
| 93 | 73 | |
|---|
| 94 | 74 | if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0) |
|---|
| .. | .. |
|---|
| 112 | 92 | .set_uhs_signaling = sdhci_set_uhs_signaling, |
|---|
| 113 | 93 | }; |
|---|
| 114 | 94 | |
|---|
| 95 | +static const struct sdhci_pltfm_data sdhci_f_sdh30_pltfm_data = { |
|---|
| 96 | + .ops = &sdhci_f_sdh30_ops, |
|---|
| 97 | + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|---|
| 98 | + | SDHCI_QUIRK_INVERTED_WRITE_PROTECT, |
|---|
| 99 | + .quirks2 = SDHCI_QUIRK2_SUPPORT_SINGLE |
|---|
| 100 | + | SDHCI_QUIRK2_TUNING_WORK_AROUND, |
|---|
| 101 | +}; |
|---|
| 102 | + |
|---|
| 115 | 103 | static int sdhci_f_sdh30_probe(struct platform_device *pdev) |
|---|
| 116 | 104 | { |
|---|
| 117 | 105 | struct sdhci_host *host; |
|---|
| 118 | 106 | struct device *dev = &pdev->dev; |
|---|
| 119 | | - struct resource *res; |
|---|
| 120 | | - int irq, ctrl = 0, ret = 0; |
|---|
| 107 | + int ctrl = 0, ret = 0; |
|---|
| 121 | 108 | struct f_sdhost_priv *priv; |
|---|
| 109 | + struct sdhci_pltfm_host *pltfm_host; |
|---|
| 122 | 110 | u32 reg = 0; |
|---|
| 123 | 111 | |
|---|
| 124 | | - irq = platform_get_irq(pdev, 0); |
|---|
| 125 | | - if (irq < 0) { |
|---|
| 126 | | - dev_err(dev, "%s: no irq specified\n", __func__); |
|---|
| 127 | | - return irq; |
|---|
| 128 | | - } |
|---|
| 129 | | - |
|---|
| 130 | | - host = sdhci_alloc_host(dev, sizeof(struct f_sdhost_priv)); |
|---|
| 112 | + host = sdhci_pltfm_init(pdev, &sdhci_f_sdh30_pltfm_data, |
|---|
| 113 | + sizeof(struct f_sdhost_priv)); |
|---|
| 131 | 114 | if (IS_ERR(host)) |
|---|
| 132 | 115 | return PTR_ERR(host); |
|---|
| 133 | 116 | |
|---|
| 134 | | - priv = sdhci_priv(host); |
|---|
| 117 | + pltfm_host = sdhci_priv(host); |
|---|
| 118 | + priv = sdhci_pltfm_priv(pltfm_host); |
|---|
| 135 | 119 | priv->dev = dev; |
|---|
| 136 | | - |
|---|
| 137 | | - host->quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | |
|---|
| 138 | | - SDHCI_QUIRK_INVERTED_WRITE_PROTECT; |
|---|
| 139 | | - host->quirks2 = SDHCI_QUIRK2_SUPPORT_SINGLE | |
|---|
| 140 | | - SDHCI_QUIRK2_TUNING_WORK_AROUND; |
|---|
| 141 | 120 | |
|---|
| 142 | 121 | priv->enable_cmd_dat_delay = device_property_read_bool(dev, |
|---|
| 143 | 122 | "fujitsu,cmd-dat-delay-select"); |
|---|
| .. | .. |
|---|
| 145 | 124 | ret = mmc_of_parse(host->mmc); |
|---|
| 146 | 125 | if (ret) |
|---|
| 147 | 126 | goto err; |
|---|
| 148 | | - |
|---|
| 149 | | - platform_set_drvdata(pdev, host); |
|---|
| 150 | | - |
|---|
| 151 | | - host->hw_name = "f_sdh30"; |
|---|
| 152 | | - host->ops = &sdhci_f_sdh30_ops; |
|---|
| 153 | | - host->irq = irq; |
|---|
| 154 | | - |
|---|
| 155 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 156 | | - host->ioaddr = devm_ioremap_resource(&pdev->dev, res); |
|---|
| 157 | | - if (IS_ERR(host->ioaddr)) { |
|---|
| 158 | | - ret = PTR_ERR(host->ioaddr); |
|---|
| 159 | | - goto err; |
|---|
| 160 | | - } |
|---|
| 161 | 127 | |
|---|
| 162 | 128 | if (dev_of_node(dev)) { |
|---|
| 163 | 129 | sdhci_get_of_property(pdev); |
|---|
| .. | .. |
|---|
| 199 | 165 | if (reg & SDHCI_CAN_DO_8BIT) |
|---|
| 200 | 166 | priv->vendor_hs200 = F_SDH30_EMMC_HS200; |
|---|
| 201 | 167 | |
|---|
| 168 | + if (!(reg & SDHCI_TIMEOUT_CLK_MASK)) |
|---|
| 169 | + host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; |
|---|
| 170 | + |
|---|
| 202 | 171 | ret = sdhci_add_host(host); |
|---|
| 203 | 172 | if (ret) |
|---|
| 204 | 173 | goto err_add_host; |
|---|
| .. | .. |
|---|
| 210 | 179 | err_clk: |
|---|
| 211 | 180 | clk_disable_unprepare(priv->clk_iface); |
|---|
| 212 | 181 | err: |
|---|
| 213 | | - sdhci_free_host(host); |
|---|
| 182 | + sdhci_pltfm_free(pdev); |
|---|
| 183 | + |
|---|
| 214 | 184 | return ret; |
|---|
| 215 | 185 | } |
|---|
| 216 | 186 | |
|---|
| 217 | 187 | static int sdhci_f_sdh30_remove(struct platform_device *pdev) |
|---|
| 218 | 188 | { |
|---|
| 219 | 189 | struct sdhci_host *host = platform_get_drvdata(pdev); |
|---|
| 220 | | - struct f_sdhost_priv *priv = sdhci_priv(host); |
|---|
| 190 | + struct f_sdhost_priv *priv = sdhci_f_sdhost_priv(host); |
|---|
| 191 | + struct clk *clk_iface = priv->clk_iface; |
|---|
| 192 | + struct clk *clk = priv->clk; |
|---|
| 221 | 193 | |
|---|
| 222 | | - sdhci_remove_host(host, readl(host->ioaddr + SDHCI_INT_STATUS) == |
|---|
| 223 | | - 0xffffffff); |
|---|
| 194 | + sdhci_pltfm_unregister(pdev); |
|---|
| 224 | 195 | |
|---|
| 225 | | - clk_disable_unprepare(priv->clk_iface); |
|---|
| 226 | | - clk_disable_unprepare(priv->clk); |
|---|
| 227 | | - |
|---|
| 228 | | - sdhci_free_host(host); |
|---|
| 229 | | - platform_set_drvdata(pdev, NULL); |
|---|
| 196 | + clk_disable_unprepare(clk_iface); |
|---|
| 197 | + clk_disable_unprepare(clk); |
|---|
| 230 | 198 | |
|---|
| 231 | 199 | return 0; |
|---|
| 232 | 200 | } |
|---|