linux/drivers/clk/mvebu/ap806-system-controller.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Marvell Armada AP806 System Controller
   4 *
   5 * Copyright (C) 2016 Marvell
   6 *
   7 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
   8 *
   9 */
  10
  11#define pr_fmt(fmt) "ap806-system-controller: " fmt
  12
  13#include "armada_ap_cp_helper.h"
  14#include <linux/clk-provider.h>
  15#include <linux/mfd/syscon.h>
  16#include <linux/init.h>
  17#include <linux/of.h>
  18#include <linux/platform_device.h>
  19#include <linux/regmap.h>
  20
  21#define AP806_SAR_REG                   0x400
  22#define AP806_SAR_CLKFREQ_MODE_MASK     0x1f
  23
  24#define AP806_CLK_NUM                   6
  25
  26static struct clk *ap806_clks[AP806_CLK_NUM];
  27
  28static struct clk_onecell_data ap806_clk_data = {
  29        .clks = ap806_clks,
  30        .clk_num = AP806_CLK_NUM,
  31};
  32
  33static int ap806_get_sar_clocks(unsigned int freq_mode,
  34                                unsigned int *cpuclk_freq,
  35                                unsigned int *dclk_freq)
  36{
  37        switch (freq_mode) {
  38        case 0x0:
  39                *cpuclk_freq = 2000;
  40                *dclk_freq = 600;
  41                break;
  42        case 0x1:
  43                *cpuclk_freq = 2000;
  44                *dclk_freq = 525;
  45                break;
  46        case 0x6:
  47                *cpuclk_freq = 1800;
  48                *dclk_freq = 600;
  49                break;
  50        case 0x7:
  51                *cpuclk_freq = 1800;
  52                *dclk_freq = 525;
  53                break;
  54        case 0x4:
  55                *cpuclk_freq = 1600;
  56                *dclk_freq = 400;
  57                break;
  58        case 0xB:
  59                *cpuclk_freq = 1600;
  60                *dclk_freq = 450;
  61                break;
  62        case 0xD:
  63                *cpuclk_freq = 1600;
  64                *dclk_freq = 525;
  65                break;
  66        case 0x1a:
  67                *cpuclk_freq = 1400;
  68                *dclk_freq = 400;
  69                break;
  70        case 0x14:
  71                *cpuclk_freq = 1300;
  72                *dclk_freq = 400;
  73                break;
  74        case 0x17:
  75                *cpuclk_freq = 1300;
  76                *dclk_freq = 325;
  77                break;
  78        case 0x19:
  79                *cpuclk_freq = 1200;
  80                *dclk_freq = 400;
  81                break;
  82        case 0x13:
  83                *cpuclk_freq = 1000;
  84                *dclk_freq = 325;
  85                break;
  86        case 0x1d:
  87                *cpuclk_freq = 1000;
  88                *dclk_freq = 400;
  89                break;
  90        case 0x1c:
  91                *cpuclk_freq = 800;
  92                *dclk_freq = 400;
  93                break;
  94        case 0x1b:
  95                *cpuclk_freq = 600;
  96                *dclk_freq = 400;
  97                break;
  98        default:
  99                return -EINVAL;
 100        }
 101
 102        return 0;
 103}
 104
 105static int ap807_get_sar_clocks(unsigned int freq_mode,
 106                                unsigned int *cpuclk_freq,
 107                                unsigned int *dclk_freq)
 108{
 109        switch (freq_mode) {
 110        case 0x0:
 111                *cpuclk_freq = 2000;
 112                *dclk_freq = 1200;
 113                break;
 114        case 0x6:
 115                *cpuclk_freq = 2200;
 116                *dclk_freq = 1200;
 117                break;
 118        case 0xD:
 119                *cpuclk_freq = 1600;
 120                *dclk_freq = 1200;
 121                break;
 122        default:
 123                return -EINVAL;
 124        }
 125
 126        return 0;
 127}
 128
 129static int ap806_syscon_common_probe(struct platform_device *pdev,
 130                                     struct device_node *syscon_node)
 131{
 132        unsigned int freq_mode, cpuclk_freq, dclk_freq;
 133        const char *name, *fixedclk_name;
 134        struct device *dev = &pdev->dev;
 135        struct device_node *np = dev->of_node;
 136        struct regmap *regmap;
 137        u32 reg;
 138        int ret;
 139
 140        regmap = syscon_node_to_regmap(syscon_node);
 141        if (IS_ERR(regmap)) {
 142                dev_err(dev, "cannot get regmap\n");
 143                return PTR_ERR(regmap);
 144        }
 145
 146        ret = regmap_read(regmap, AP806_SAR_REG, &reg);
 147        if (ret) {
 148                dev_err(dev, "cannot read from regmap\n");
 149                return ret;
 150        }
 151
 152        freq_mode = reg & AP806_SAR_CLKFREQ_MODE_MASK;
 153
 154        if (of_device_is_compatible(pdev->dev.of_node,
 155                                    "marvell,ap806-clock")) {
 156                ret = ap806_get_sar_clocks(freq_mode, &cpuclk_freq, &dclk_freq);
 157        } else if (of_device_is_compatible(pdev->dev.of_node,
 158                                           "marvell,ap807-clock")) {
 159                ret = ap807_get_sar_clocks(freq_mode, &cpuclk_freq, &dclk_freq);
 160        } else {
 161                dev_err(dev, "compatible not supported\n");
 162                return -EINVAL;
 163        }
 164
 165        if (ret) {
 166                dev_err(dev, "invalid Sample at Reset value\n");
 167                return ret;
 168        }
 169
 170        /* Convert to hertz */
 171        cpuclk_freq *= 1000 * 1000;
 172        dclk_freq *= 1000 * 1000;
 173
 174        /* CPU clocks depend on the Sample At Reset configuration */
 175        name = ap_cp_unique_name(dev, syscon_node, "pll-cluster-0");
 176        ap806_clks[0] = clk_register_fixed_rate(dev, name, NULL,
 177                                                0, cpuclk_freq);
 178        if (IS_ERR(ap806_clks[0])) {
 179                ret = PTR_ERR(ap806_clks[0]);
 180                goto fail0;
 181        }
 182
 183        name = ap_cp_unique_name(dev, syscon_node, "pll-cluster-1");
 184        ap806_clks[1] = clk_register_fixed_rate(dev, name, NULL, 0,
 185                                                cpuclk_freq);
 186        if (IS_ERR(ap806_clks[1])) {
 187                ret = PTR_ERR(ap806_clks[1]);
 188                goto fail1;
 189        }
 190
 191        /* Fixed clock is always 1200 Mhz */
 192        fixedclk_name = ap_cp_unique_name(dev, syscon_node, "fixed");
 193        ap806_clks[2] = clk_register_fixed_rate(dev, fixedclk_name, NULL,
 194                                                0, 1200 * 1000 * 1000);
 195        if (IS_ERR(ap806_clks[2])) {
 196                ret = PTR_ERR(ap806_clks[2]);
 197                goto fail2;
 198        }
 199
 200        /* MSS Clock is fixed clock divided by 6 */
 201        name = ap_cp_unique_name(dev, syscon_node, "mss");
 202        ap806_clks[3] = clk_register_fixed_factor(NULL, name, fixedclk_name,
 203                                                  0, 1, 6);
 204        if (IS_ERR(ap806_clks[3])) {
 205                ret = PTR_ERR(ap806_clks[3]);
 206                goto fail3;
 207        }
 208
 209        /* SDIO(/eMMC) Clock is fixed clock divided by 3 */
 210        name = ap_cp_unique_name(dev, syscon_node, "sdio");
 211        ap806_clks[4] = clk_register_fixed_factor(NULL, name,
 212                                                  fixedclk_name,
 213                                                  0, 1, 3);
 214        if (IS_ERR(ap806_clks[4])) {
 215                ret = PTR_ERR(ap806_clks[4]);
 216                goto fail4;
 217        }
 218
 219        /* AP-DCLK(HCLK) Clock is DDR clock divided by 2 */
 220        name = ap_cp_unique_name(dev, syscon_node, "ap-dclk");
 221        ap806_clks[5] = clk_register_fixed_rate(dev, name, NULL, 0, dclk_freq);
 222        if (IS_ERR(ap806_clks[5])) {
 223                ret = PTR_ERR(ap806_clks[5]);
 224                goto fail5;
 225        }
 226
 227        ret = of_clk_add_provider(np, of_clk_src_onecell_get, &ap806_clk_data);
 228        if (ret)
 229                goto fail_clk_add;
 230
 231        return 0;
 232
 233fail_clk_add:
 234        clk_unregister_fixed_factor(ap806_clks[5]);
 235fail5:
 236        clk_unregister_fixed_factor(ap806_clks[4]);
 237fail4:
 238        clk_unregister_fixed_factor(ap806_clks[3]);
 239fail3:
 240        clk_unregister_fixed_rate(ap806_clks[2]);
 241fail2:
 242        clk_unregister_fixed_rate(ap806_clks[1]);
 243fail1:
 244        clk_unregister_fixed_rate(ap806_clks[0]);
 245fail0:
 246        return ret;
 247}
 248
 249static int ap806_syscon_legacy_probe(struct platform_device *pdev)
 250{
 251        dev_warn(&pdev->dev, FW_WARN "Using legacy device tree binding\n");
 252        dev_warn(&pdev->dev, FW_WARN "Update your device tree:\n");
 253        dev_warn(&pdev->dev, FW_WARN
 254                 "This binding won't be supported in future kernel\n");
 255
 256        return ap806_syscon_common_probe(pdev, pdev->dev.of_node);
 257
 258}
 259
 260static int ap806_clock_probe(struct platform_device *pdev)
 261{
 262        return ap806_syscon_common_probe(pdev, pdev->dev.of_node->parent);
 263}
 264
 265static const struct of_device_id ap806_syscon_legacy_of_match[] = {
 266        { .compatible = "marvell,ap806-system-controller", },
 267        { }
 268};
 269
 270static struct platform_driver ap806_syscon_legacy_driver = {
 271        .probe = ap806_syscon_legacy_probe,
 272        .driver         = {
 273                .name   = "marvell-ap806-system-controller",
 274                .of_match_table = ap806_syscon_legacy_of_match,
 275                .suppress_bind_attrs = true,
 276        },
 277};
 278builtin_platform_driver(ap806_syscon_legacy_driver);
 279
 280static const struct of_device_id ap806_clock_of_match[] = {
 281        { .compatible = "marvell,ap806-clock", },
 282        { .compatible = "marvell,ap807-clock", },
 283        { }
 284};
 285
 286static struct platform_driver ap806_clock_driver = {
 287        .probe = ap806_clock_probe,
 288        .driver         = {
 289                .name   = "marvell-ap806-clock",
 290                .of_match_table = ap806_clock_of_match,
 291                .suppress_bind_attrs = true,
 292        },
 293};
 294builtin_platform_driver(ap806_clock_driver);
 295