1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* Copyright (C) 2019 ASPEED Technology Inc. */ 3 /* Copyright (C) 2019 IBM Corp. */ 4 5 #include <linux/clk.h> 6 #include <linux/delay.h> 7 #include <linux/device.h> 8 #include <linux/io.h> 9 #include <linux/mmc/host.h> 10 #include <linux/module.h> 11 #include <linux/of_address.h> 12 #include <linux/of.h> 13 #include <linux/of_platform.h> 14 #include <linux/platform_device.h> 15 #include <linux/spinlock.h> 16 17 #include "sdhci-pltfm.h" 18 19 #define ASPEED_SDC_INFO 0x00 20 #define ASPEED_SDC_S1MMC8 BIT(25) 21 #define ASPEED_SDC_S0MMC8 BIT(24) 22 23 struct aspeed_sdc { 24 struct clk *clk; 25 struct resource *res; 26 27 spinlock_t lock; 28 void __iomem *regs; 29 }; 30 31 struct aspeed_sdhci { 32 struct aspeed_sdc *parent; 33 u32 width_mask; 34 }; 35 36 static void aspeed_sdc_configure_8bit_mode(struct aspeed_sdc *sdc, 37 struct aspeed_sdhci *sdhci, 38 bool bus8) 39 { 40 u32 info; 41 42 /* Set/clear 8 bit mode */ 43 spin_lock(&sdc->lock); 44 info = readl(sdc->regs + ASPEED_SDC_INFO); 45 if (bus8) 46 info |= sdhci->width_mask; 47 else 48 info &= ~sdhci->width_mask; 49 writel(info, sdc->regs + ASPEED_SDC_INFO); 50 spin_unlock(&sdc->lock); 51 } 52 53 static void aspeed_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) 54 { 55 struct sdhci_pltfm_host *pltfm_host; 56 unsigned long parent; 57 int div; 58 u16 clk; 59 60 pltfm_host = sdhci_priv(host); 61 parent = clk_get_rate(pltfm_host->clk); 62 63 sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); 64 65 if (clock == 0) 66 return; 67 68 if (WARN_ON(clock > host->max_clk)) 69 clock = host->max_clk; 70 71 for (div = 2; div < 256; div *= 2) { 72 if ((parent / div) <= clock) 73 break; 74 } 75 div >>= 1; 76 77 clk = div << SDHCI_DIVIDER_SHIFT; 78 79 sdhci_enable_clk(host, clk); 80 } 81 82 static unsigned int aspeed_sdhci_get_max_clock(struct sdhci_host *host) 83 { 84 if (host->mmc->f_max) 85 return host->mmc->f_max; 86 87 return sdhci_pltfm_clk_get_max_clock(host); 88 } 89 90 static void aspeed_sdhci_set_bus_width(struct sdhci_host *host, int width) 91 { 92 struct sdhci_pltfm_host *pltfm_priv; 93 struct aspeed_sdhci *aspeed_sdhci; 94 struct aspeed_sdc *aspeed_sdc; 95 u8 ctrl; 96 97 pltfm_priv = sdhci_priv(host); 98 aspeed_sdhci = sdhci_pltfm_priv(pltfm_priv); 99 aspeed_sdc = aspeed_sdhci->parent; 100 101 /* Set/clear 8-bit mode */ 102 aspeed_sdc_configure_8bit_mode(aspeed_sdc, aspeed_sdhci, 103 width == MMC_BUS_WIDTH_8); 104 105 /* Set/clear 1 or 4 bit mode */ 106 ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); 107 if (width == MMC_BUS_WIDTH_4) 108 ctrl |= SDHCI_CTRL_4BITBUS; 109 else 110 ctrl &= ~SDHCI_CTRL_4BITBUS; 111 sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); 112 } 113 114 static u32 aspeed_sdhci_readl(struct sdhci_host *host, int reg) 115 { 116 u32 val = readl(host->ioaddr + reg); 117 118 if (unlikely(reg == SDHCI_PRESENT_STATE) && 119 (host->mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH)) 120 val ^= SDHCI_CARD_PRESENT; 121 122 return val; 123 } 124 125 static const struct sdhci_ops aspeed_sdhci_ops = { 126 .read_l = aspeed_sdhci_readl, 127 .set_clock = aspeed_sdhci_set_clock, 128 .get_max_clock = aspeed_sdhci_get_max_clock, 129 .set_bus_width = aspeed_sdhci_set_bus_width, 130 .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, 131 .reset = sdhci_reset, 132 .set_uhs_signaling = sdhci_set_uhs_signaling, 133 }; 134 135 static const struct sdhci_pltfm_data aspeed_sdhci_pdata = { 136 .ops = &aspeed_sdhci_ops, 137 .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 138 }; 139 140 static inline int aspeed_sdhci_calculate_slot(struct aspeed_sdhci *dev, 141 struct resource *res) 142 { 143 resource_size_t delta; 144 145 if (!res || resource_type(res) != IORESOURCE_MEM) 146 return -EINVAL; 147 148 if (res->start < dev->parent->res->start) 149 return -EINVAL; 150 151 delta = res->start - dev->parent->res->start; 152 if (delta & (0x100 - 1)) 153 return -EINVAL; 154 155 return (delta / 0x100) - 1; 156 } 157 158 static int aspeed_sdhci_probe(struct platform_device *pdev) 159 { 160 struct sdhci_pltfm_host *pltfm_host; 161 struct aspeed_sdhci *dev; 162 struct sdhci_host *host; 163 struct resource *res; 164 int slot; 165 int ret; 166 167 host = sdhci_pltfm_init(pdev, &aspeed_sdhci_pdata, sizeof(*dev)); 168 if (IS_ERR(host)) 169 return PTR_ERR(host); 170 171 pltfm_host = sdhci_priv(host); 172 dev = sdhci_pltfm_priv(pltfm_host); 173 dev->parent = dev_get_drvdata(pdev->dev.parent); 174 175 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 176 slot = aspeed_sdhci_calculate_slot(dev, res); 177 178 if (slot < 0) 179 return slot; 180 else if (slot >= 2) 181 return -EINVAL; 182 183 dev_info(&pdev->dev, "Configuring for slot %d\n", slot); 184 dev->width_mask = !slot ? ASPEED_SDC_S0MMC8 : ASPEED_SDC_S1MMC8; 185 186 sdhci_get_of_property(pdev); 187 188 pltfm_host->clk = devm_clk_get(&pdev->dev, NULL); 189 if (IS_ERR(pltfm_host->clk)) 190 return PTR_ERR(pltfm_host->clk); 191 192 ret = clk_prepare_enable(pltfm_host->clk); 193 if (ret) { 194 dev_err(&pdev->dev, "Unable to enable SDIO clock\n"); 195 goto err_pltfm_free; 196 } 197 198 ret = mmc_of_parse(host->mmc); 199 if (ret) 200 goto err_sdhci_add; 201 202 ret = sdhci_add_host(host); 203 if (ret) 204 goto err_sdhci_add; 205 206 return 0; 207 208 err_sdhci_add: 209 clk_disable_unprepare(pltfm_host->clk); 210 err_pltfm_free: 211 sdhci_pltfm_free(pdev); 212 return ret; 213 } 214 215 static int aspeed_sdhci_remove(struct platform_device *pdev) 216 { 217 struct sdhci_pltfm_host *pltfm_host; 218 struct sdhci_host *host; 219 int dead = 0; 220 221 host = platform_get_drvdata(pdev); 222 pltfm_host = sdhci_priv(host); 223 224 sdhci_remove_host(host, dead); 225 226 clk_disable_unprepare(pltfm_host->clk); 227 228 sdhci_pltfm_free(pdev); 229 230 return 0; 231 } 232 233 static const struct of_device_id aspeed_sdhci_of_match[] = { 234 { .compatible = "aspeed,ast2400-sdhci", }, 235 { .compatible = "aspeed,ast2500-sdhci", }, 236 { .compatible = "aspeed,ast2600-sdhci", }, 237 { } 238 }; 239 240 static struct platform_driver aspeed_sdhci_driver = { 241 .driver = { 242 .name = "sdhci-aspeed", 243 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 244 .of_match_table = aspeed_sdhci_of_match, 245 }, 246 .probe = aspeed_sdhci_probe, 247 .remove = aspeed_sdhci_remove, 248 }; 249 250 static int aspeed_sdc_probe(struct platform_device *pdev) 251 252 { 253 struct device_node *parent, *child; 254 struct aspeed_sdc *sdc; 255 int ret; 256 257 sdc = devm_kzalloc(&pdev->dev, sizeof(*sdc), GFP_KERNEL); 258 if (!sdc) 259 return -ENOMEM; 260 261 spin_lock_init(&sdc->lock); 262 263 sdc->clk = devm_clk_get(&pdev->dev, NULL); 264 if (IS_ERR(sdc->clk)) 265 return PTR_ERR(sdc->clk); 266 267 ret = clk_prepare_enable(sdc->clk); 268 if (ret) { 269 dev_err(&pdev->dev, "Unable to enable SDCLK\n"); 270 return ret; 271 } 272 273 sdc->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 274 sdc->regs = devm_ioremap_resource(&pdev->dev, sdc->res); 275 if (IS_ERR(sdc->regs)) { 276 ret = PTR_ERR(sdc->regs); 277 goto err_clk; 278 } 279 280 dev_set_drvdata(&pdev->dev, sdc); 281 282 parent = pdev->dev.of_node; 283 for_each_available_child_of_node(parent, child) { 284 struct platform_device *cpdev; 285 286 cpdev = of_platform_device_create(child, NULL, &pdev->dev); 287 if (!cpdev) { 288 of_node_put(child); 289 ret = -ENODEV; 290 goto err_clk; 291 } 292 } 293 294 return 0; 295 296 err_clk: 297 clk_disable_unprepare(sdc->clk); 298 return ret; 299 } 300 301 static int aspeed_sdc_remove(struct platform_device *pdev) 302 { 303 struct aspeed_sdc *sdc = dev_get_drvdata(&pdev->dev); 304 305 clk_disable_unprepare(sdc->clk); 306 307 return 0; 308 } 309 310 static const struct of_device_id aspeed_sdc_of_match[] = { 311 { .compatible = "aspeed,ast2400-sd-controller", }, 312 { .compatible = "aspeed,ast2500-sd-controller", }, 313 { .compatible = "aspeed,ast2600-sd-controller", }, 314 { } 315 }; 316 317 MODULE_DEVICE_TABLE(of, aspeed_sdc_of_match); 318 319 static struct platform_driver aspeed_sdc_driver = { 320 .driver = { 321 .name = "sd-controller-aspeed", 322 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 323 .pm = &sdhci_pltfm_pmops, 324 .of_match_table = aspeed_sdc_of_match, 325 }, 326 .probe = aspeed_sdc_probe, 327 .remove = aspeed_sdc_remove, 328 }; 329 330 static int __init aspeed_sdc_init(void) 331 { 332 int rc; 333 334 rc = platform_driver_register(&aspeed_sdhci_driver); 335 if (rc < 0) 336 return rc; 337 338 rc = platform_driver_register(&aspeed_sdc_driver); 339 if (rc < 0) 340 platform_driver_unregister(&aspeed_sdhci_driver); 341 342 return rc; 343 } 344 module_init(aspeed_sdc_init); 345 346 static void __exit aspeed_sdc_exit(void) 347 { 348 platform_driver_unregister(&aspeed_sdc_driver); 349 platform_driver_unregister(&aspeed_sdhci_driver); 350 } 351 module_exit(aspeed_sdc_exit); 352 353 MODULE_DESCRIPTION("Driver for the ASPEED SD/SDIO/SDHCI Controllers"); 354 MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); 355 MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); 356 MODULE_LICENSE("GPL"); 357