OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | --- a/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt |
2 | +++ b/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt |
||
3 | @@ -13,6 +13,7 @@ Required properties: |
||
4 | * "allwinner,sun5i-a13-mmc" |
||
5 | * "allwinner,sun7i-a20-mmc" |
||
6 | * "allwinner,sun9i-a80-mmc" |
||
7 | + * "allwinner,sun50i-a64-emmc" |
||
8 | * "allwinner,sun50i-a64-mmc" |
||
9 | - reg : mmc controller base registers |
||
10 | - clocks : a list with 4 phandle + clock specifier pairs |
||
11 | --- a/drivers/mmc/host/sunxi-mmc.c |
||
12 | +++ b/drivers/mmc/host/sunxi-mmc.c |
||
13 | @@ -5,6 +5,7 @@ |
||
14 | * (C) Copyright 2013-2014 O2S GmbH <www.o2s.ch> |
||
15 | * (C) Copyright 2013-2014 David Lanzend�rfer <david.lanzendoerfer@o2s.ch> |
||
16 | * (C) Copyright 2013-2014 Hans de Goede <hdegoede@redhat.com> |
||
17 | + * (C) Copyright 2017 Sootech SA |
||
18 | * |
||
19 | * This program is free software; you can redistribute it and/or |
||
20 | * modify it under the terms of the GNU General Public License as |
||
21 | @@ -101,6 +102,7 @@ |
||
22 | (SDXC_SOFT_RESET | SDXC_FIFO_RESET | SDXC_DMA_RESET) |
||
23 | |||
24 | /* clock control bits */ |
||
25 | +#define SDXC_MASK_DATA0 BIT(31) |
||
26 | #define SDXC_CARD_CLOCK_ON BIT(16) |
||
27 | #define SDXC_LOW_POWER_ON BIT(17) |
||
28 | |||
29 | @@ -253,6 +255,11 @@ struct sunxi_mmc_cfg { |
||
30 | |||
31 | /* does the IP block support autocalibration? */ |
||
32 | bool can_calibrate; |
||
33 | + |
||
34 | + /* Does DATA0 needs to be masked while the clock is updated */ |
||
35 | + bool mask_data0; |
||
36 | + |
||
37 | + bool needs_new_timings; |
||
38 | }; |
||
39 | |||
40 | struct sunxi_mmc_host { |
||
41 | @@ -482,7 +489,7 @@ static void sunxi_mmc_dump_errinfo(struc |
||
42 | cmd->opcode == SD_IO_RW_DIRECT)) |
||
43 | return; |
||
44 | |||
45 | - dev_err(mmc_dev(host->mmc), |
||
46 | + dev_dbg(mmc_dev(host->mmc), |
||
47 | "smc %d err, cmd %d,%s%s%s%s%s%s%s%s%s%s !!\n", |
||
48 | host->mmc->index, cmd->opcode, |
||
49 | data ? (data->flags & MMC_DATA_WRITE ? " WR" : " RD") : "", |
||
50 | @@ -654,11 +661,16 @@ static int sunxi_mmc_oclk_onoff(struct s |
||
51 | unsigned long expire = jiffies + msecs_to_jiffies(750); |
||
52 | u32 rval; |
||
53 | |||
54 | + dev_dbg(mmc_dev(host->mmc), "%sabling the clock\n", |
||
55 | + oclk_en ? "en" : "dis"); |
||
56 | + |
||
57 | rval = mmc_readl(host, REG_CLKCR); |
||
58 | - rval &= ~(SDXC_CARD_CLOCK_ON | SDXC_LOW_POWER_ON); |
||
59 | + rval &= ~(SDXC_CARD_CLOCK_ON | SDXC_LOW_POWER_ON | SDXC_MASK_DATA0); |
||
60 | |||
61 | if (oclk_en) |
||
62 | rval |= SDXC_CARD_CLOCK_ON; |
||
63 | + if (host->cfg->mask_data0) |
||
64 | + rval |= SDXC_MASK_DATA0; |
||
65 | |||
66 | mmc_writel(host, REG_CLKCR, rval); |
||
67 | |||
68 | @@ -678,46 +690,29 @@ static int sunxi_mmc_oclk_onoff(struct s |
||
69 | return -EIO; |
||
70 | } |
||
71 | |||
72 | + if (host->cfg->mask_data0) { |
||
73 | + rval = mmc_readl(host, REG_CLKCR); |
||
74 | + mmc_writel(host, REG_CLKCR, rval & ~SDXC_MASK_DATA0); |
||
75 | + } |
||
76 | + |
||
77 | return 0; |
||
78 | } |
||
79 | |||
80 | static int sunxi_mmc_calibrate(struct sunxi_mmc_host *host, int reg_off) |
||
81 | { |
||
82 | - u32 reg = readl(host->reg_base + reg_off); |
||
83 | - u32 delay; |
||
84 | - unsigned long timeout; |
||
85 | - |
||
86 | if (!host->cfg->can_calibrate) |
||
87 | return 0; |
||
88 | |||
89 | - reg &= ~(SDXC_CAL_DL_MASK << SDXC_CAL_DL_SW_SHIFT); |
||
90 | - reg &= ~SDXC_CAL_DL_SW_EN; |
||
91 | - |
||
92 | - writel(reg | SDXC_CAL_START, host->reg_base + reg_off); |
||
93 | - |
||
94 | - dev_dbg(mmc_dev(host->mmc), "calibration started\n"); |
||
95 | - |
||
96 | - timeout = jiffies + HZ * SDXC_CAL_TIMEOUT; |
||
97 | - |
||
98 | - while (!((reg = readl(host->reg_base + reg_off)) & SDXC_CAL_DONE)) { |
||
99 | - if (time_before(jiffies, timeout)) |
||
100 | - cpu_relax(); |
||
101 | - else { |
||
102 | - reg &= ~SDXC_CAL_START; |
||
103 | - writel(reg, host->reg_base + reg_off); |
||
104 | - |
||
105 | - return -ETIMEDOUT; |
||
106 | - } |
||
107 | - } |
||
108 | - |
||
109 | - delay = (reg >> SDXC_CAL_DL_SHIFT) & SDXC_CAL_DL_MASK; |
||
110 | - |
||
111 | - reg &= ~SDXC_CAL_START; |
||
112 | - reg |= (delay << SDXC_CAL_DL_SW_SHIFT) | SDXC_CAL_DL_SW_EN; |
||
113 | - |
||
114 | - writel(reg, host->reg_base + reg_off); |
||
115 | - |
||
116 | - dev_dbg(mmc_dev(host->mmc), "calibration ended, reg is 0x%x\n", reg); |
||
117 | + /* |
||
118 | + * FIXME: |
||
119 | + * This is not clear how the calibration is supposed to work |
||
120 | + * yet. The best rate have been obtained by simply setting the |
||
121 | + * delay to 0, as Allwinner does in its BSP. |
||
122 | + * |
||
123 | + * The only mode that doesn't have such a delay is HS400, that |
||
124 | + * is in itself a TODO. |
||
125 | + */ |
||
126 | + writel(SDXC_CAL_DL_SW_EN, host->reg_base + reg_off); |
||
127 | |||
128 | return 0; |
||
129 | } |
||
130 | @@ -745,6 +740,7 @@ static int sunxi_mmc_clk_set_phase(struc |
||
131 | index = SDXC_CLK_50M_DDR; |
||
132 | } |
||
133 | } else { |
||
134 | + dev_dbg(mmc_dev(host->mmc), "Invalid clock... returning\n"); |
||
135 | return -EINVAL; |
||
136 | } |
||
137 | |||
138 | @@ -757,10 +753,21 @@ static int sunxi_mmc_clk_set_phase(struc |
||
139 | static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, |
||
140 | struct mmc_ios *ios) |
||
141 | { |
||
142 | + struct mmc_host *mmc = host->mmc; |
||
143 | long rate; |
||
144 | u32 rval, clock = ios->clock; |
||
145 | int ret; |
||
146 | |||
147 | + ret = sunxi_mmc_oclk_onoff(host, 0); |
||
148 | + if (ret) |
||
149 | + return ret; |
||
150 | + |
||
151 | + /* Our clock is gated now */ |
||
152 | + mmc->actual_clock = 0; |
||
153 | + |
||
154 | + if (!ios->clock) |
||
155 | + return 0; |
||
156 | + |
||
157 | /* 8 bit DDR requires a higher module clock */ |
||
158 | if (ios->timing == MMC_TIMING_MMC_DDR52 && |
||
159 | ios->bus_width == MMC_BUS_WIDTH_8) |
||
160 | @@ -768,25 +775,21 @@ static int sunxi_mmc_clk_set_rate(struct |
||
161 | |||
162 | rate = clk_round_rate(host->clk_mmc, clock); |
||
163 | if (rate < 0) { |
||
164 | - dev_err(mmc_dev(host->mmc), "error rounding clk to %d: %ld\n", |
||
165 | + dev_err(mmc_dev(mmc), "error rounding clk to %d: %ld\n", |
||
166 | clock, rate); |
||
167 | return rate; |
||
168 | } |
||
169 | - dev_dbg(mmc_dev(host->mmc), "setting clk to %d, rounded %ld\n", |
||
170 | + dev_dbg(mmc_dev(mmc), "setting clk to %d, rounded %ld\n", |
||
171 | clock, rate); |
||
172 | |||
173 | /* setting clock rate */ |
||
174 | ret = clk_set_rate(host->clk_mmc, rate); |
||
175 | if (ret) { |
||
176 | - dev_err(mmc_dev(host->mmc), "error setting clk to %ld: %d\n", |
||
177 | + dev_err(mmc_dev(mmc), "error setting clk to %ld: %d\n", |
||
178 | rate, ret); |
||
179 | return ret; |
||
180 | } |
||
181 | |||
182 | - ret = sunxi_mmc_oclk_onoff(host, 0); |
||
183 | - if (ret) |
||
184 | - return ret; |
||
185 | - |
||
186 | /* clear internal divider */ |
||
187 | rval = mmc_readl(host, REG_CLKCR); |
||
188 | rval &= ~0xff; |
||
189 | @@ -798,6 +801,13 @@ static int sunxi_mmc_clk_set_rate(struct |
||
190 | } |
||
191 | mmc_writel(host, REG_CLKCR, rval); |
||
192 | |||
193 | + if (host->cfg->needs_new_timings) { |
||
194 | + /* Don't touch the delay bits */ |
||
195 | + rval = mmc_readl(host, REG_SD_NTSR); |
||
196 | + rval |= SDXC_2X_TIMING_MODE; |
||
197 | + mmc_writel(host, REG_SD_NTSR, rval); |
||
198 | + } |
||
199 | + |
||
200 | ret = sunxi_mmc_clk_set_phase(host, ios, rate); |
||
201 | if (ret) |
||
202 | return ret; |
||
203 | @@ -806,9 +816,22 @@ static int sunxi_mmc_clk_set_rate(struct |
||
204 | if (ret) |
||
205 | return ret; |
||
206 | |||
207 | - /* TODO: enable calibrate on sdc2 SDXC_REG_DS_DL_REG of A64 */ |
||
208 | + /* |
||
209 | + * FIXME: |
||
210 | + * |
||
211 | + * In HS400 we'll also need to calibrate the data strobe |
||
212 | + * signal. This should only happen on the MMC2 controller (at |
||
213 | + * least on the A64). |
||
214 | + */ |
||
215 | + |
||
216 | + ret = sunxi_mmc_oclk_onoff(host, 1); |
||
217 | + if (ret) |
||
218 | + return ret; |
||
219 | |||
220 | - return sunxi_mmc_oclk_onoff(host, 1); |
||
221 | + /* And we just enabled our clock back */ |
||
222 | + mmc->actual_clock = rate; |
||
223 | + |
||
224 | + return 0; |
||
225 | } |
||
226 | |||
227 | static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) |
||
228 | @@ -822,10 +845,13 @@ static void sunxi_mmc_set_ios(struct mmc |
||
229 | break; |
||
230 | |||
231 | case MMC_POWER_UP: |
||
232 | - host->ferror = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, |
||
233 | - ios->vdd); |
||
234 | - if (host->ferror) |
||
235 | - return; |
||
236 | + if (!IS_ERR(mmc->supply.vmmc)) { |
||
237 | + host->ferror = mmc_regulator_set_ocr(mmc, |
||
238 | + mmc->supply.vmmc, |
||
239 | + ios->vdd); |
||
240 | + if (host->ferror) |
||
241 | + return; |
||
242 | + } |
||
243 | |||
244 | if (!IS_ERR(mmc->supply.vqmmc)) { |
||
245 | host->ferror = regulator_enable(mmc->supply.vqmmc); |
||
246 | @@ -847,7 +873,9 @@ static void sunxi_mmc_set_ios(struct mmc |
||
247 | case MMC_POWER_OFF: |
||
248 | dev_dbg(mmc_dev(mmc), "power off!\n"); |
||
249 | sunxi_mmc_reset_host(host); |
||
250 | - mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); |
||
251 | + if (!IS_ERR(mmc->supply.vmmc)) |
||
252 | + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); |
||
253 | + |
||
254 | if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) |
||
255 | regulator_disable(mmc->supply.vqmmc); |
||
256 | host->vqmmc_enabled = false; |
||
257 | @@ -877,7 +905,7 @@ static void sunxi_mmc_set_ios(struct mmc |
||
258 | mmc_writel(host, REG_GCTRL, rval); |
||
259 | |||
260 | /* set up clock */ |
||
261 | - if (ios->clock && ios->power_mode) { |
||
262 | + if (ios->power_mode) { |
||
263 | host->ferror = sunxi_mmc_clk_set_rate(host, ios); |
||
264 | /* Android code had a usleep_range(50000, 55000); here */ |
||
265 | } |
||
266 | @@ -1084,6 +1112,14 @@ static const struct sunxi_mmc_cfg sun50i |
||
267 | .idma_des_size_bits = 16, |
||
268 | .clk_delays = NULL, |
||
269 | .can_calibrate = true, |
||
270 | + .mask_data0 = true, |
||
271 | + .needs_new_timings = true, |
||
272 | +}; |
||
273 | + |
||
274 | +static const struct sunxi_mmc_cfg sun50i_a64_emmc_cfg = { |
||
275 | + .idma_des_size_bits = 13, |
||
276 | + .clk_delays = NULL, |
||
277 | + .can_calibrate = true, |
||
278 | }; |
||
279 | |||
280 | static const struct of_device_id sunxi_mmc_of_match[] = { |
||
281 | @@ -1092,6 +1128,7 @@ static const struct of_device_id sunxi_m |
||
282 | { .compatible = "allwinner,sun7i-a20-mmc", .data = &sun7i_a20_cfg }, |
||
283 | { .compatible = "allwinner,sun9i-a80-mmc", .data = &sun9i_a80_cfg }, |
||
284 | { .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg }, |
||
285 | + { .compatible = "allwinner,sun50i-a64-emmc", .data = &sun50i_a64_emmc_cfg }, |
||
286 | { /* sentinel */ } |
||
287 | }; |
||
288 | MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match); |