Commit | Line | Data |
---|---|---|
f707079d | 1 | // SPDX-License-Identifier: GPL-2.0 |
4a48998f | 2 | /* |
b21f13d8 SH |
3 | * Driver for the MMC / SD / SDIO cell found in: |
4 | * | |
5 | * TC6393XB TC6391XB TC6387XB T7L66XB ASIC3 | |
4a48998f | 6 | * |
87317c4d SH |
7 | * Copyright (C) 2017 Renesas Electronics Corporation |
8 | * Copyright (C) 2017 Horms Solutions, Simon Horman | |
b6147490 GL |
9 | * Copyright (C) 2007 Ian Molton |
10 | * Copyright (C) 2004 Ian Molton | |
4a48998f | 11 | */ |
e0bc6ff8 | 12 | |
0196c8db | 13 | #include <linux/delay.h> |
e0bc6ff8 | 14 | #include <linux/device.h> |
4a48998f IM |
15 | #include <linux/mfd/core.h> |
16 | #include <linux/mfd/tmio.h> | |
e0bc6ff8 GL |
17 | #include <linux/mmc/host.h> |
18 | #include <linux/module.h> | |
19 | #include <linux/pagemap.h> | |
20 | #include <linux/scatterlist.h> | |
6ff56e0d | 21 | |
b6147490 | 22 | #include "tmio_mmc.h" |
4a48998f | 23 | |
722fb61e MY |
24 | /* Registers specific to this variant */ |
25 | #define CTL_SDIO_REGS 0x100 | |
26 | #define CTL_CLK_AND_WAIT_CTL 0x138 | |
27 | #define CTL_RESET_SDIO 0x1e0 | |
28 | ||
0196c8db MY |
29 | static void tmio_mmc_clk_start(struct tmio_mmc_host *host) |
30 | { | |
31 | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | | |
32 | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); | |
33 | ||
34 | usleep_range(10000, 11000); | |
35 | sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100); | |
36 | usleep_range(10000, 11000); | |
37 | } | |
38 | ||
39 | static void tmio_mmc_clk_stop(struct tmio_mmc_host *host) | |
40 | { | |
41 | sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000); | |
42 | usleep_range(10000, 11000); | |
43 | ||
44 | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & | |
45 | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); | |
46 | ||
47 | usleep_range(10000, 11000); | |
48 | } | |
49 | ||
50 | static void tmio_mmc_set_clock(struct tmio_mmc_host *host, | |
51 | unsigned int new_clock) | |
52 | { | |
75586bb9 | 53 | unsigned int divisor; |
db4cea91 MY |
54 | u32 clk = 0; |
55 | int clk_sel; | |
0196c8db MY |
56 | |
57 | if (new_clock == 0) { | |
58 | tmio_mmc_clk_stop(host); | |
59 | return; | |
60 | } | |
61 | ||
db4cea91 | 62 | divisor = host->pdata->hclk / new_clock; |
0196c8db | 63 | |
4c595c05 WS |
64 | /* bit7 set: 1/512, ... bit0 set: 1/4, all bits clear: 1/2 */ |
65 | clk_sel = (divisor <= 1); | |
66 | clk = clk_sel ? 0 : (roundup_pow_of_two(divisor) >> 2); | |
0196c8db | 67 | |
db4cea91 | 68 | host->pdata->set_clk_div(host->pdev, clk_sel); |
0196c8db MY |
69 | |
70 | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & | |
71 | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); | |
72 | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & CLK_CTL_DIV_MASK); | |
73 | usleep_range(10000, 11000); | |
74 | ||
75 | tmio_mmc_clk_start(host); | |
76 | } | |
77 | ||
acb9fce7 MY |
78 | static void tmio_mmc_reset(struct tmio_mmc_host *host) |
79 | { | |
80 | /* FIXME - should we set stop clock reg here */ | |
81 | sd_ctrl_write16(host, CTL_RESET_SD, 0x0000); | |
82 | sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000); | |
83 | usleep_range(10000, 11000); | |
84 | sd_ctrl_write16(host, CTL_RESET_SD, 0x0001); | |
85 | sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001); | |
86 | usleep_range(10000, 11000); | |
87 | ||
88 | if (host->pdata->flags & TMIO_MMC_SDIO_IRQ) { | |
89 | sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask); | |
90 | sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001); | |
91 | } | |
92 | } | |
93 | ||
c8964481 UH |
94 | #ifdef CONFIG_PM_SLEEP |
95 | static int tmio_mmc_suspend(struct device *dev) | |
4a48998f | 96 | { |
c8964481 UH |
97 | struct platform_device *pdev = to_platform_device(dev); |
98 | const struct mfd_cell *cell = mfd_get_cell(pdev); | |
4a48998f IM |
99 | int ret; |
100 | ||
70a15e1a | 101 | ret = pm_runtime_force_suspend(dev); |
4a48998f IM |
102 | |
103 | /* Tell MFD core it can disable us now.*/ | |
104 | if (!ret && cell->disable) | |
c8964481 | 105 | cell->disable(pdev); |
4a48998f IM |
106 | |
107 | return ret; | |
108 | } | |
109 | ||
c8964481 | 110 | static int tmio_mmc_resume(struct device *dev) |
4a48998f | 111 | { |
c8964481 UH |
112 | struct platform_device *pdev = to_platform_device(dev); |
113 | const struct mfd_cell *cell = mfd_get_cell(pdev); | |
4a48998f IM |
114 | int ret = 0; |
115 | ||
4a48998f | 116 | /* Tell the MFD core we are ready to be enabled */ |
e6ee7182 | 117 | if (cell->resume) |
c8964481 | 118 | ret = cell->resume(pdev); |
4a48998f | 119 | |
e6ee7182 | 120 | if (!ret) |
70a15e1a | 121 | ret = pm_runtime_force_resume(dev); |
4a48998f | 122 | |
4a48998f IM |
123 | return ret; |
124 | } | |
4a48998f IM |
125 | #endif |
126 | ||
c3be1efd | 127 | static int tmio_mmc_probe(struct platform_device *pdev) |
4a48998f | 128 | { |
b6147490 | 129 | const struct mfd_cell *cell = mfd_get_cell(pdev); |
f0e46cc4 | 130 | struct tmio_mmc_data *pdata; |
4a48998f | 131 | struct tmio_mmc_host *host; |
3b159a6e | 132 | struct resource *res; |
8e7bfdb3 | 133 | int ret = -EINVAL, irq; |
4a48998f | 134 | |
b6147490 | 135 | if (pdev->num_resources != 2) |
4a48998f IM |
136 | goto out; |
137 | ||
ec71974f | 138 | pdata = pdev->dev.platform_data; |
d6c9b5ed | 139 | if (!pdata || !pdata->hclk) |
f0e46cc4 | 140 | goto out; |
d6c9b5ed | 141 | |
8e7bfdb3 MD |
142 | irq = platform_get_irq(pdev, 0); |
143 | if (irq < 0) { | |
144 | ret = irq; | |
145 | goto out; | |
146 | } | |
147 | ||
4a48998f IM |
148 | /* Tell the MFD core we are ready to be enabled */ |
149 | if (cell->enable) { | |
b6147490 | 150 | ret = cell->enable(pdev); |
4a48998f | 151 | if (ret) |
b6147490 | 152 | goto out; |
4a48998f IM |
153 | } |
154 | ||
3b159a6e | 155 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
25db67e2 IM |
156 | if (!res) { |
157 | ret = -EINVAL; | |
158 | goto cell_disable; | |
159 | } | |
3b159a6e | 160 | |
b21fc294 | 161 | host = tmio_mmc_host_alloc(pdev, pdata); |
8d09a133 MY |
162 | if (IS_ERR(host)) { |
163 | ret = PTR_ERR(host); | |
7ee422dc | 164 | goto cell_disable; |
8d09a133 | 165 | } |
4a48998f | 166 | |
7445bf9e KM |
167 | /* SD control register space size is 0x200, 0x400 for bus_shift=1 */ |
168 | host->bus_shift = resource_size(res) >> 10; | |
0196c8db | 169 | host->set_clock = tmio_mmc_set_clock; |
acb9fce7 | 170 | host->reset = tmio_mmc_reset; |
7445bf9e | 171 | |
b21fc294 MY |
172 | host->mmc->f_max = pdata->hclk; |
173 | host->mmc->f_min = pdata->hclk / 512; | |
174 | ||
bc45719c | 175 | ret = tmio_mmc_host_probe(host); |
94b110af KM |
176 | if (ret) |
177 | goto host_free; | |
178 | ||
de501af9 | 179 | ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_irq, |
f2218db8 SH |
180 | IRQF_TRIGGER_FALLING, |
181 | dev_name(&pdev->dev), host); | |
8e7bfdb3 MD |
182 | if (ret) |
183 | goto host_remove; | |
184 | ||
311f3ac7 | 185 | pr_info("%s at 0x%08lx irq %d\n", mmc_hostname(host->mmc), |
8e7bfdb3 | 186 | (unsigned long)host->ctl, irq); |
4a48998f | 187 | |
4a48998f IM |
188 | return 0; |
189 | ||
8e7bfdb3 MD |
190 | host_remove: |
191 | tmio_mmc_host_remove(host); | |
94b110af KM |
192 | host_free: |
193 | tmio_mmc_host_free(host); | |
7ee422dc MD |
194 | cell_disable: |
195 | if (cell->disable) | |
b6147490 | 196 | cell->disable(pdev); |
4a48998f IM |
197 | out: |
198 | return ret; | |
199 | } | |
200 | ||
6e0ee714 | 201 | static int tmio_mmc_remove(struct platform_device *pdev) |
4a48998f | 202 | { |
b6147490 | 203 | const struct mfd_cell *cell = mfd_get_cell(pdev); |
a3b05373 | 204 | struct tmio_mmc_host *host = platform_get_drvdata(pdev); |
4a48998f | 205 | |
a3b05373 MY |
206 | tmio_mmc_host_remove(host); |
207 | if (cell->disable) | |
208 | cell->disable(pdev); | |
4a48998f IM |
209 | |
210 | return 0; | |
211 | } | |
212 | ||
213 | /* ------------------- device registration ----------------------- */ | |
214 | ||
c8964481 UH |
215 | static const struct dev_pm_ops tmio_mmc_dev_pm_ops = { |
216 | SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_suspend, tmio_mmc_resume) | |
6ed23b80 | 217 | SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, |
f2218db8 | 218 | tmio_mmc_host_runtime_resume, NULL) |
c8964481 UH |
219 | }; |
220 | ||
4a48998f IM |
221 | static struct platform_driver tmio_mmc_driver = { |
222 | .driver = { | |
223 | .name = "tmio-mmc", | |
c8964481 | 224 | .pm = &tmio_mmc_dev_pm_ops, |
4a48998f IM |
225 | }, |
226 | .probe = tmio_mmc_probe, | |
0433c143 | 227 | .remove = tmio_mmc_remove, |
4a48998f IM |
228 | }; |
229 | ||
d1f81a64 | 230 | module_platform_driver(tmio_mmc_driver); |
4a48998f IM |
231 | |
232 | MODULE_DESCRIPTION("Toshiba TMIO SD/MMC driver"); | |
233 | MODULE_AUTHOR("Ian Molton <spyro@f2s.com>"); | |
234 | MODULE_LICENSE("GPL v2"); | |
235 | MODULE_ALIAS("platform:tmio-mmc"); |