^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0+
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Marvell Armada 37xx SoC Time Base Generator clocks
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2016 Marvell
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Gregory CLEMENT <gregory.clement@free-electrons.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/clk-provider.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/clk.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/of.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/of_address.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #define NUM_TBG 4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #define TBG_CTRL0 0x4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #define TBG_CTRL1 0x8
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #define TBG_CTRL7 0x20
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #define TBG_CTRL8 0x30
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #define TBG_DIV_MASK 0x1FF
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #define TBG_A_REFDIV 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #define TBG_B_REFDIV 16
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) #define TBG_A_FBDIV 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #define TBG_B_FBDIV 18
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #define TBG_A_VCODIV_SE 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) #define TBG_B_VCODIV_SE 16
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) #define TBG_A_VCODIV_DIFF 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) #define TBG_B_VCODIV_DIFF 17
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) struct tbg_def {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) char *name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) u32 refdiv_offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) u32 fbdiv_offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) u32 vcodiv_reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) u32 vcodiv_offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) static const struct tbg_def tbg[NUM_TBG] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) {"TBG-A-P", TBG_A_REFDIV, TBG_A_FBDIV, TBG_CTRL8, TBG_A_VCODIV_DIFF},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) {"TBG-B-P", TBG_B_REFDIV, TBG_B_FBDIV, TBG_CTRL8, TBG_B_VCODIV_DIFF},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) {"TBG-A-S", TBG_A_REFDIV, TBG_A_FBDIV, TBG_CTRL1, TBG_A_VCODIV_SE},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) {"TBG-B-S", TBG_B_REFDIV, TBG_B_FBDIV, TBG_CTRL1, TBG_B_VCODIV_SE},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) static unsigned int tbg_get_mult(void __iomem *reg, const struct tbg_def *ptbg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) u32 val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) val = readl(reg + TBG_CTRL0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) return ((val >> ptbg->fbdiv_offset) & TBG_DIV_MASK) << 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) static unsigned int tbg_get_div(void __iomem *reg, const struct tbg_def *ptbg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) u32 val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) unsigned int div;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) val = readl(reg + TBG_CTRL7);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) div = (val >> ptbg->refdiv_offset) & TBG_DIV_MASK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) if (div == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) div = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) val = readl(reg + ptbg->vcodiv_reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) div *= 1 << ((val >> ptbg->vcodiv_offset) & TBG_DIV_MASK);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) return div;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) static int armada_3700_tbg_clock_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) struct device_node *np = pdev->dev.of_node;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) struct clk_hw_onecell_data *hw_tbg_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) struct device *dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) const char *parent_name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) struct resource *res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) struct clk *parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) void __iomem *reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) int i, ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) hw_tbg_data = devm_kzalloc(&pdev->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) struct_size(hw_tbg_data, hws, NUM_TBG),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) if (!hw_tbg_data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) hw_tbg_data->num = NUM_TBG;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) platform_set_drvdata(pdev, hw_tbg_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) parent = clk_get(dev, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) if (IS_ERR(parent)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) dev_err(dev, "Could get the clock parent\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) parent_name = __clk_get_name(parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) clk_put(parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) reg = devm_ioremap_resource(dev, res);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) if (IS_ERR(reg))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) return PTR_ERR(reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) for (i = 0; i < NUM_TBG; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) const char *name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) unsigned int mult, div;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) name = tbg[i].name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) mult = tbg_get_mult(reg, &tbg[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) div = tbg_get_div(reg, &tbg[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) hw_tbg_data->hws[i] = clk_hw_register_fixed_factor(NULL, name,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) parent_name, 0, mult, div);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) if (IS_ERR(hw_tbg_data->hws[i]))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) dev_err(dev, "Can't register TBG clock %s\n", name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, hw_tbg_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) static int armada_3700_tbg_clock_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) struct clk_hw_onecell_data *hw_tbg_data = platform_get_drvdata(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) of_clk_del_provider(pdev->dev.of_node);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) for (i = 0; i < hw_tbg_data->num; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) clk_hw_unregister_fixed_factor(hw_tbg_data->hws[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) static const struct of_device_id armada_3700_tbg_clock_of_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) { .compatible = "marvell,armada-3700-tbg-clock", },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) { }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) static struct platform_driver armada_3700_tbg_clock_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) .probe = armada_3700_tbg_clock_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) .remove = armada_3700_tbg_clock_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) .name = "marvell-armada-3700-tbg-clock",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) .of_match_table = armada_3700_tbg_clock_of_match,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) builtin_platform_driver(armada_3700_tbg_clock_driver);