linux/drivers/mfd/sun6i-prcm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2014 Free Electrons
   4 *
   5 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
   6 *
   7 * Allwinner PRCM (Power/Reset/Clock Management) driver
   8 */
   9
  10#include <linux/mfd/core.h>
  11#include <linux/init.h>
  12#include <linux/of.h>
  13
  14#define SUN8I_CODEC_ANALOG_BASE 0x1c0
  15#define SUN8I_CODEC_ANALOG_SIZE 0x4
  16
  17struct prcm_data {
  18        int nsubdevs;
  19        const struct mfd_cell *subdevs;
  20};
  21
  22static const struct resource sun6i_a31_ar100_clk_res[] = {
  23        DEFINE_RES_MEM(0x0, 4)
  24};
  25
  26static const struct resource sun6i_a31_apb0_clk_res[] = {
  27        DEFINE_RES_MEM(0xc, 4)
  28};
  29
  30static const struct resource sun6i_a31_apb0_gates_clk_res[] = {
  31        DEFINE_RES_MEM(0x28, 4)
  32};
  33
  34static const struct resource sun6i_a31_ir_clk_res[] = {
  35        DEFINE_RES_MEM(0x54, 4)
  36};
  37
  38static const struct resource sun6i_a31_apb0_rstc_res[] = {
  39        DEFINE_RES_MEM(0xb0, 4)
  40};
  41
  42static const struct resource sun8i_codec_analog_res[] = {
  43        DEFINE_RES_MEM(SUN8I_CODEC_ANALOG_BASE, SUN8I_CODEC_ANALOG_SIZE),
  44};
  45
  46static const struct mfd_cell sun6i_a31_prcm_subdevs[] = {
  47        {
  48                .name = "sun6i-a31-ar100-clk",
  49                .of_compatible = "allwinner,sun6i-a31-ar100-clk",
  50                .num_resources = ARRAY_SIZE(sun6i_a31_ar100_clk_res),
  51                .resources = sun6i_a31_ar100_clk_res,
  52        },
  53        {
  54                .name = "sun6i-a31-apb0-clk",
  55                .of_compatible = "allwinner,sun6i-a31-apb0-clk",
  56                .num_resources = ARRAY_SIZE(sun6i_a31_apb0_clk_res),
  57                .resources = sun6i_a31_apb0_clk_res,
  58        },
  59        {
  60                .name = "sun6i-a31-apb0-gates-clk",
  61                .of_compatible = "allwinner,sun6i-a31-apb0-gates-clk",
  62                .num_resources = ARRAY_SIZE(sun6i_a31_apb0_gates_clk_res),
  63                .resources = sun6i_a31_apb0_gates_clk_res,
  64        },
  65        {
  66                .name = "sun6i-a31-ir-clk",
  67                .of_compatible = "allwinner,sun4i-a10-mod0-clk",
  68                .num_resources = ARRAY_SIZE(sun6i_a31_ir_clk_res),
  69                .resources = sun6i_a31_ir_clk_res,
  70        },
  71        {
  72                .name = "sun6i-a31-apb0-clock-reset",
  73                .of_compatible = "allwinner,sun6i-a31-clock-reset",
  74                .num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res),
  75                .resources = sun6i_a31_apb0_rstc_res,
  76        },
  77};
  78
  79static const struct mfd_cell sun8i_a23_prcm_subdevs[] = {
  80        {
  81                .name = "sun8i-a23-apb0-clk",
  82                .of_compatible = "allwinner,sun8i-a23-apb0-clk",
  83                .num_resources = ARRAY_SIZE(sun6i_a31_apb0_clk_res),
  84                .resources = sun6i_a31_apb0_clk_res,
  85        },
  86        {
  87                .name = "sun6i-a31-apb0-gates-clk",
  88                .of_compatible = "allwinner,sun8i-a23-apb0-gates-clk",
  89                .num_resources = ARRAY_SIZE(sun6i_a31_apb0_gates_clk_res),
  90                .resources = sun6i_a31_apb0_gates_clk_res,
  91        },
  92        {
  93                .name = "sun6i-a31-apb0-clock-reset",
  94                .of_compatible = "allwinner,sun6i-a31-clock-reset",
  95                .num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res),
  96                .resources = sun6i_a31_apb0_rstc_res,
  97        },
  98        {
  99                .name           = "sun8i-codec-analog",
 100                .of_compatible  = "allwinner,sun8i-a23-codec-analog",
 101                .num_resources  = ARRAY_SIZE(sun8i_codec_analog_res),
 102                .resources      = sun8i_codec_analog_res,
 103        },
 104};
 105
 106static const struct prcm_data sun6i_a31_prcm_data = {
 107        .nsubdevs = ARRAY_SIZE(sun6i_a31_prcm_subdevs),
 108        .subdevs = sun6i_a31_prcm_subdevs,
 109};
 110
 111static const struct prcm_data sun8i_a23_prcm_data = {
 112        .nsubdevs = ARRAY_SIZE(sun8i_a23_prcm_subdevs),
 113        .subdevs = sun8i_a23_prcm_subdevs,
 114};
 115
 116static const struct of_device_id sun6i_prcm_dt_ids[] = {
 117        {
 118                .compatible = "allwinner,sun6i-a31-prcm",
 119                .data = &sun6i_a31_prcm_data,
 120        },
 121        {
 122                .compatible = "allwinner,sun8i-a23-prcm",
 123                .data = &sun8i_a23_prcm_data,
 124        },
 125        { /* sentinel */ },
 126};
 127
 128static int sun6i_prcm_probe(struct platform_device *pdev)
 129{
 130        const struct of_device_id *match;
 131        const struct prcm_data *data;
 132        struct resource *res;
 133        int ret;
 134
 135        match = of_match_node(sun6i_prcm_dt_ids, pdev->dev.of_node);
 136        if (!match)
 137                return -EINVAL;
 138
 139        data = match->data;
 140
 141        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 142        if (!res) {
 143                dev_err(&pdev->dev, "no prcm memory region provided\n");
 144                return -ENOENT;
 145        }
 146
 147        ret = mfd_add_devices(&pdev->dev, 0, data->subdevs, data->nsubdevs,
 148                              res, -1, NULL);
 149        if (ret) {
 150                dev_err(&pdev->dev, "failed to add subdevices\n");
 151                return ret;
 152        }
 153
 154        return 0;
 155}
 156
 157static struct platform_driver sun6i_prcm_driver = {
 158        .driver = {
 159                .name = "sun6i-prcm",
 160                .of_match_table = sun6i_prcm_dt_ids,
 161        },
 162        .probe = sun6i_prcm_probe,
 163};
 164builtin_platform_driver(sun6i_prcm_driver);
 165