linux/drivers/clk/x86/clk-fch.c
<<
>>
Prefs
   1// SPDX-License-Identifier: MIT
   2/*
   3 * clock framework for AMD Stoney based clocks
   4 *
   5 * Copyright 2018 Advanced Micro Devices, Inc.
   6 */
   7
   8#include <linux/clk.h>
   9#include <linux/clkdev.h>
  10#include <linux/clk-provider.h>
  11#include <linux/platform_data/clk-fch.h>
  12#include <linux/platform_device.h>
  13
  14/* Clock Driving Strength 2 register */
  15#define CLKDRVSTR2      0x28
  16/* Clock Control 1 register */
  17#define MISCCLKCNTL1    0x40
  18/* Auxiliary clock1 enable bit */
  19#define OSCCLKENB       2
  20/* 25Mhz auxiliary output clock freq bit */
  21#define OSCOUT1CLK25MHZ 16
  22
  23#define ST_CLK_48M      0
  24#define ST_CLK_25M      1
  25#define ST_CLK_MUX      2
  26#define ST_CLK_GATE     3
  27#define ST_MAX_CLKS     4
  28
  29#define RV_CLK_48M      0
  30#define RV_CLK_GATE     1
  31#define RV_MAX_CLKS     2
  32
  33static const char * const clk_oscout1_parents[] = { "clk48MHz", "clk25MHz" };
  34static struct clk_hw *hws[ST_MAX_CLKS];
  35
  36static int fch_clk_probe(struct platform_device *pdev)
  37{
  38        struct fch_clk_data *fch_data;
  39
  40        fch_data = dev_get_platdata(&pdev->dev);
  41        if (!fch_data || !fch_data->base)
  42                return -EINVAL;
  43
  44        if (!fch_data->is_rv) {
  45                hws[ST_CLK_48M] = clk_hw_register_fixed_rate(NULL, "clk48MHz",
  46                        NULL, 0, 48000000);
  47                hws[ST_CLK_25M] = clk_hw_register_fixed_rate(NULL, "clk25MHz",
  48                        NULL, 0, 25000000);
  49
  50                hws[ST_CLK_MUX] = clk_hw_register_mux(NULL, "oscout1_mux",
  51                        clk_oscout1_parents, ARRAY_SIZE(clk_oscout1_parents),
  52                        0, fch_data->base + CLKDRVSTR2, OSCOUT1CLK25MHZ, 3, 0,
  53                        NULL);
  54
  55                clk_set_parent(hws[ST_CLK_MUX]->clk, hws[ST_CLK_48M]->clk);
  56
  57                hws[ST_CLK_GATE] = clk_hw_register_gate(NULL, "oscout1",
  58                        "oscout1_mux", 0, fch_data->base + MISCCLKCNTL1,
  59                        OSCCLKENB, CLK_GATE_SET_TO_DISABLE, NULL);
  60
  61                devm_clk_hw_register_clkdev(&pdev->dev, hws[ST_CLK_GATE],
  62                        "oscout1", NULL);
  63        } else {
  64                hws[RV_CLK_48M] = clk_hw_register_fixed_rate(NULL, "clk48MHz",
  65                        NULL, 0, 48000000);
  66
  67                hws[RV_CLK_GATE] = clk_hw_register_gate(NULL, "oscout1",
  68                        "clk48MHz", 0, fch_data->base + MISCCLKCNTL1,
  69                        OSCCLKENB, CLK_GATE_SET_TO_DISABLE, NULL);
  70
  71                devm_clk_hw_register_clkdev(&pdev->dev, hws[RV_CLK_GATE],
  72                        "oscout1", NULL);
  73        }
  74
  75        return 0;
  76}
  77
  78static int fch_clk_remove(struct platform_device *pdev)
  79{
  80        int i, clks;
  81        struct fch_clk_data *fch_data;
  82
  83        fch_data = dev_get_platdata(&pdev->dev);
  84
  85        clks = fch_data->is_rv ? RV_MAX_CLKS : ST_MAX_CLKS;
  86
  87        for (i = 0; i < clks; i++)
  88                clk_hw_unregister(hws[i]);
  89
  90        return 0;
  91}
  92
  93static struct platform_driver fch_clk_driver = {
  94        .driver = {
  95                .name = "clk-fch",
  96                .suppress_bind_attrs = true,
  97        },
  98        .probe = fch_clk_probe,
  99        .remove = fch_clk_remove,
 100};
 101builtin_platform_driver(fch_clk_driver);
 102