Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.1-rc4 235 lines 5.7 kB view raw
1/* 2 * SDHCI support for SiRF primaII and marco SoCs 3 * 4 * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. 5 * 6 * Licensed under GPLv2 or later. 7 */ 8 9#include <linux/delay.h> 10#include <linux/device.h> 11#include <linux/mmc/host.h> 12#include <linux/module.h> 13#include <linux/of.h> 14#include <linux/mmc/slot-gpio.h> 15#include "sdhci-pltfm.h" 16 17#define SDHCI_CLK_DELAY_SETTING 0x4C 18#define SDHCI_SIRF_8BITBUS BIT(3) 19#define SIRF_TUNING_COUNT 16384 20 21static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width) 22{ 23 u8 ctrl; 24 25 ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); 26 ctrl &= ~(SDHCI_CTRL_4BITBUS | SDHCI_SIRF_8BITBUS); 27 28 /* 29 * CSR atlas7 and prima2 SD host version is not 3.0 30 * 8bit-width enable bit of CSR SD hosts is 3, 31 * while stardard hosts use bit 5 32 */ 33 if (width == MMC_BUS_WIDTH_8) 34 ctrl |= SDHCI_SIRF_8BITBUS; 35 else if (width == MMC_BUS_WIDTH_4) 36 ctrl |= SDHCI_CTRL_4BITBUS; 37 38 sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); 39} 40 41static u32 sdhci_sirf_readl_le(struct sdhci_host *host, int reg) 42{ 43 u32 val = readl(host->ioaddr + reg); 44 45 if (unlikely((reg == SDHCI_CAPABILITIES_1) && 46 (host->mmc->caps & MMC_CAP_UHS_SDR50))) { 47 /* fake CAP_1 register */ 48 val = SDHCI_SUPPORT_DDR50 | 49 SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING; 50 } 51 52 if (unlikely(reg == SDHCI_SLOT_INT_STATUS)) { 53 u32 prss = val; 54 /* fake chips as V3.0 host conreoller */ 55 prss &= ~(0xFF << 16); 56 val = prss | (SDHCI_SPEC_300 << 16); 57 } 58 return val; 59} 60 61static u16 sdhci_sirf_readw_le(struct sdhci_host *host, int reg) 62{ 63 u16 ret = 0; 64 65 ret = readw(host->ioaddr + reg); 66 67 if (unlikely(reg == SDHCI_HOST_VERSION)) { 68 ret = readw(host->ioaddr + SDHCI_HOST_VERSION); 69 ret |= SDHCI_SPEC_300; 70 } 71 72 return ret; 73} 74 75static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode) 76{ 77 int tuning_seq_cnt = 3; 78 int phase; 79 u8 tuned_phase_cnt = 0; 80 int rc = 0, longest_range = 0; 81 int start = -1, end = 0, tuning_value = -1, range = 0; 82 u16 clock_setting; 83 struct mmc_host *mmc = host->mmc; 84 85 clock_setting = sdhci_readw(host, SDHCI_CLK_DELAY_SETTING); 86 clock_setting &= ~0x3fff; 87 88retry: 89 phase = 0; 90 tuned_phase_cnt = 0; 91 do { 92 sdhci_writel(host, 93 clock_setting | phase, 94 SDHCI_CLK_DELAY_SETTING); 95 96 if (!mmc_send_tuning(mmc, opcode, NULL)) { 97 /* Tuning is successful at this tuning point */ 98 tuned_phase_cnt++; 99 dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n", 100 mmc_hostname(mmc), phase); 101 if (start == -1) 102 start = phase; 103 end = phase; 104 range++; 105 if (phase == (SIRF_TUNING_COUNT - 1) 106 && range > longest_range) 107 tuning_value = (start + end) / 2; 108 } else { 109 dev_dbg(mmc_dev(mmc), "%s: Found bad phase = %d\n", 110 mmc_hostname(mmc), phase); 111 if (range > longest_range) { 112 tuning_value = (start + end) / 2; 113 longest_range = range; 114 } 115 start = -1; 116 end = range = 0; 117 } 118 } while (++phase < SIRF_TUNING_COUNT); 119 120 if (tuned_phase_cnt && tuning_value > 0) { 121 /* 122 * Finally set the selected phase in delay 123 * line hw block. 124 */ 125 phase = tuning_value; 126 sdhci_writel(host, 127 clock_setting | phase, 128 SDHCI_CLK_DELAY_SETTING); 129 130 dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n", 131 mmc_hostname(mmc), phase); 132 } else { 133 if (--tuning_seq_cnt) 134 goto retry; 135 /* Tuning failed */ 136 dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n", 137 mmc_hostname(mmc)); 138 rc = -EIO; 139 } 140 141 return rc; 142} 143 144static const struct sdhci_ops sdhci_sirf_ops = { 145 .read_l = sdhci_sirf_readl_le, 146 .read_w = sdhci_sirf_readw_le, 147 .platform_execute_tuning = sdhci_sirf_execute_tuning, 148 .set_clock = sdhci_set_clock, 149 .get_max_clock = sdhci_pltfm_clk_get_max_clock, 150 .set_bus_width = sdhci_sirf_set_bus_width, 151 .reset = sdhci_reset, 152 .set_uhs_signaling = sdhci_set_uhs_signaling, 153}; 154 155static const struct sdhci_pltfm_data sdhci_sirf_pdata = { 156 .ops = &sdhci_sirf_ops, 157 .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 158 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 159 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | 160 SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS, 161 .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 162}; 163 164static int sdhci_sirf_probe(struct platform_device *pdev) 165{ 166 struct sdhci_host *host; 167 struct sdhci_pltfm_host *pltfm_host; 168 struct clk *clk; 169 int ret; 170 171 clk = devm_clk_get(&pdev->dev, NULL); 172 if (IS_ERR(clk)) { 173 dev_err(&pdev->dev, "unable to get clock"); 174 return PTR_ERR(clk); 175 } 176 177 host = sdhci_pltfm_init(pdev, &sdhci_sirf_pdata, 0); 178 if (IS_ERR(host)) 179 return PTR_ERR(host); 180 181 pltfm_host = sdhci_priv(host); 182 pltfm_host->clk = clk; 183 184 sdhci_get_of_property(pdev); 185 186 ret = clk_prepare_enable(pltfm_host->clk); 187 if (ret) 188 goto err_clk_prepare; 189 190 ret = sdhci_add_host(host); 191 if (ret) 192 goto err_sdhci_add; 193 194 /* 195 * We must request the IRQ after sdhci_add_host(), as the tasklet only 196 * gets setup in sdhci_add_host() and we oops. 197 */ 198 ret = mmc_gpiod_request_cd(host->mmc, "cd", 0, false, 0, NULL); 199 if (ret == -EPROBE_DEFER) 200 goto err_request_cd; 201 if (!ret) 202 mmc_gpiod_request_cd_irq(host->mmc); 203 204 return 0; 205 206err_request_cd: 207 sdhci_remove_host(host, 0); 208err_sdhci_add: 209 clk_disable_unprepare(pltfm_host->clk); 210err_clk_prepare: 211 sdhci_pltfm_free(pdev); 212 return ret; 213} 214 215static const struct of_device_id sdhci_sirf_of_match[] = { 216 { .compatible = "sirf,prima2-sdhc" }, 217 { } 218}; 219MODULE_DEVICE_TABLE(of, sdhci_sirf_of_match); 220 221static struct platform_driver sdhci_sirf_driver = { 222 .driver = { 223 .name = "sdhci-sirf", 224 .of_match_table = sdhci_sirf_of_match, 225 .pm = &sdhci_pltfm_pmops, 226 }, 227 .probe = sdhci_sirf_probe, 228 .remove = sdhci_pltfm_unregister, 229}; 230 231module_platform_driver(sdhci_sirf_driver); 232 233MODULE_DESCRIPTION("SDHCI driver for SiRFprimaII/SiRFmarco"); 234MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); 235MODULE_LICENSE("GPL v2");