root/drivers/mmc/host/sdhci-of-aspeed.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. aspeed_sdc_configure_8bit_mode
  2. aspeed_sdhci_set_clock
  3. aspeed_sdhci_get_max_clock
  4. aspeed_sdhci_set_bus_width
  5. aspeed_sdhci_calculate_slot
  6. aspeed_sdhci_probe
  7. aspeed_sdhci_remove
  8. aspeed_sdc_probe
  9. aspeed_sdc_remove
  10. aspeed_sdc_init
  11. aspeed_sdc_exit

   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 = 1; 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 const struct sdhci_ops aspeed_sdhci_ops = {
 115         .set_clock = aspeed_sdhci_set_clock,
 116         .get_max_clock = aspeed_sdhci_get_max_clock,
 117         .set_bus_width = aspeed_sdhci_set_bus_width,
 118         .get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
 119         .reset = sdhci_reset,
 120         .set_uhs_signaling = sdhci_set_uhs_signaling,
 121 };
 122 
 123 static const struct sdhci_pltfm_data aspeed_sdhci_pdata = {
 124         .ops = &aspeed_sdhci_ops,
 125         .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
 126 };
 127 
 128 static inline int aspeed_sdhci_calculate_slot(struct aspeed_sdhci *dev,
 129                                               struct resource *res)
 130 {
 131         resource_size_t delta;
 132 
 133         if (!res || resource_type(res) != IORESOURCE_MEM)
 134                 return -EINVAL;
 135 
 136         if (res->start < dev->parent->res->start)
 137                 return -EINVAL;
 138 
 139         delta = res->start - dev->parent->res->start;
 140         if (delta & (0x100 - 1))
 141                 return -EINVAL;
 142 
 143         return (delta / 0x100) - 1;
 144 }
 145 
 146 static int aspeed_sdhci_probe(struct platform_device *pdev)
 147 {
 148         struct sdhci_pltfm_host *pltfm_host;
 149         struct aspeed_sdhci *dev;
 150         struct sdhci_host *host;
 151         struct resource *res;
 152         int slot;
 153         int ret;
 154 
 155         host = sdhci_pltfm_init(pdev, &aspeed_sdhci_pdata, sizeof(*dev));
 156         if (IS_ERR(host))
 157                 return PTR_ERR(host);
 158 
 159         pltfm_host = sdhci_priv(host);
 160         dev = sdhci_pltfm_priv(pltfm_host);
 161         dev->parent = dev_get_drvdata(pdev->dev.parent);
 162 
 163         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 164         slot = aspeed_sdhci_calculate_slot(dev, res);
 165 
 166         if (slot < 0)
 167                 return slot;
 168         else if (slot >= 2)
 169                 return -EINVAL;
 170 
 171         dev_info(&pdev->dev, "Configuring for slot %d\n", slot);
 172         dev->width_mask = !slot ? ASPEED_SDC_S0MMC8 : ASPEED_SDC_S1MMC8;
 173 
 174         sdhci_get_of_property(pdev);
 175 
 176         pltfm_host->clk = devm_clk_get(&pdev->dev, NULL);
 177         if (IS_ERR(pltfm_host->clk))
 178                 return PTR_ERR(pltfm_host->clk);
 179 
 180         ret = clk_prepare_enable(pltfm_host->clk);
 181         if (ret) {
 182                 dev_err(&pdev->dev, "Unable to enable SDIO clock\n");
 183                 goto err_pltfm_free;
 184         }
 185 
 186         ret = mmc_of_parse(host->mmc);
 187         if (ret)
 188                 goto err_sdhci_add;
 189 
 190         ret = sdhci_add_host(host);
 191         if (ret)
 192                 goto err_sdhci_add;
 193 
 194         return 0;
 195 
 196 err_sdhci_add:
 197         clk_disable_unprepare(pltfm_host->clk);
 198 err_pltfm_free:
 199         sdhci_pltfm_free(pdev);
 200         return ret;
 201 }
 202 
 203 static int aspeed_sdhci_remove(struct platform_device *pdev)
 204 {
 205         struct sdhci_pltfm_host *pltfm_host;
 206         struct sdhci_host *host;
 207         int dead = 0;
 208 
 209         host = platform_get_drvdata(pdev);
 210         pltfm_host = sdhci_priv(host);
 211 
 212         sdhci_remove_host(host, dead);
 213 
 214         clk_disable_unprepare(pltfm_host->clk);
 215 
 216         sdhci_pltfm_free(pdev);
 217 
 218         return 0;
 219 }
 220 
 221 static const struct of_device_id aspeed_sdhci_of_match[] = {
 222         { .compatible = "aspeed,ast2400-sdhci", },
 223         { .compatible = "aspeed,ast2500-sdhci", },
 224         { .compatible = "aspeed,ast2600-sdhci", },
 225         { }
 226 };
 227 
 228 static struct platform_driver aspeed_sdhci_driver = {
 229         .driver         = {
 230                 .name   = "sdhci-aspeed",
 231                 .of_match_table = aspeed_sdhci_of_match,
 232         },
 233         .probe          = aspeed_sdhci_probe,
 234         .remove         = aspeed_sdhci_remove,
 235 };
 236 
 237 static int aspeed_sdc_probe(struct platform_device *pdev)
 238 
 239 {
 240         struct device_node *parent, *child;
 241         struct aspeed_sdc *sdc;
 242         int ret;
 243 
 244         sdc = devm_kzalloc(&pdev->dev, sizeof(*sdc), GFP_KERNEL);
 245         if (!sdc)
 246                 return -ENOMEM;
 247 
 248         spin_lock_init(&sdc->lock);
 249 
 250         sdc->clk = devm_clk_get(&pdev->dev, NULL);
 251         if (IS_ERR(sdc->clk))
 252                 return PTR_ERR(sdc->clk);
 253 
 254         ret = clk_prepare_enable(sdc->clk);
 255         if (ret) {
 256                 dev_err(&pdev->dev, "Unable to enable SDCLK\n");
 257                 return ret;
 258         }
 259 
 260         sdc->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 261         sdc->regs = devm_ioremap_resource(&pdev->dev, sdc->res);
 262         if (IS_ERR(sdc->regs)) {
 263                 ret = PTR_ERR(sdc->regs);
 264                 goto err_clk;
 265         }
 266 
 267         dev_set_drvdata(&pdev->dev, sdc);
 268 
 269         parent = pdev->dev.of_node;
 270         for_each_available_child_of_node(parent, child) {
 271                 struct platform_device *cpdev;
 272 
 273                 cpdev = of_platform_device_create(child, NULL, &pdev->dev);
 274                 if (!cpdev) {
 275                         of_node_put(child);
 276                         ret = -ENODEV;
 277                         goto err_clk;
 278                 }
 279         }
 280 
 281         return 0;
 282 
 283 err_clk:
 284         clk_disable_unprepare(sdc->clk);
 285         return ret;
 286 }
 287 
 288 static int aspeed_sdc_remove(struct platform_device *pdev)
 289 {
 290         struct aspeed_sdc *sdc = dev_get_drvdata(&pdev->dev);
 291 
 292         clk_disable_unprepare(sdc->clk);
 293 
 294         return 0;
 295 }
 296 
 297 static const struct of_device_id aspeed_sdc_of_match[] = {
 298         { .compatible = "aspeed,ast2400-sd-controller", },
 299         { .compatible = "aspeed,ast2500-sd-controller", },
 300         { .compatible = "aspeed,ast2600-sd-controller", },
 301         { }
 302 };
 303 
 304 MODULE_DEVICE_TABLE(of, aspeed_sdc_of_match);
 305 
 306 static struct platform_driver aspeed_sdc_driver = {
 307         .driver         = {
 308                 .name   = "sd-controller-aspeed",
 309                 .pm     = &sdhci_pltfm_pmops,
 310                 .of_match_table = aspeed_sdc_of_match,
 311         },
 312         .probe          = aspeed_sdc_probe,
 313         .remove         = aspeed_sdc_remove,
 314 };
 315 
 316 static int __init aspeed_sdc_init(void)
 317 {
 318         int rc;
 319 
 320         rc = platform_driver_register(&aspeed_sdhci_driver);
 321         if (rc < 0)
 322                 return rc;
 323 
 324         rc = platform_driver_register(&aspeed_sdc_driver);
 325         if (rc < 0)
 326                 platform_driver_unregister(&aspeed_sdhci_driver);
 327 
 328         return rc;
 329 }
 330 module_init(aspeed_sdc_init);
 331 
 332 static void __exit aspeed_sdc_exit(void)
 333 {
 334         platform_driver_unregister(&aspeed_sdc_driver);
 335         platform_driver_unregister(&aspeed_sdhci_driver);
 336 }
 337 module_exit(aspeed_sdc_exit);
 338 
 339 MODULE_DESCRIPTION("Driver for the ASPEED SD/SDIO/SDHCI Controllers");
 340 MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
 341 MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
 342 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */