blob: 030ff7cc58ec64d5b7ba5e83fdf0e03ab57aab33 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0
Paul Burton0399f442016-09-08 07:47:38 +01002/*
3 * Copyright (C) 2016 Imagination Technologies
Paul Burton0399f442016-09-08 07:47:38 +01004 */
5
Paul Burton0399f442016-09-08 07:47:38 +01006#include <clk-uclass.h>
7#include <dm.h>
8#include <dt-bindings/clock/boston-clock.h>
9#include <regmap.h>
10#include <syscon.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060011#include <linux/bitops.h>
Simon Glassbdd5f812023-09-14 18:21:46 -060012#include <linux/printk.h>
Paul Burton0399f442016-09-08 07:47:38 +010013
14struct clk_boston {
15 struct regmap *regmap;
16};
17
18#define BOSTON_PLAT_MMCMDIV 0x30
19# define BOSTON_PLAT_MMCMDIV_CLK0DIV (0xff << 0)
20# define BOSTON_PLAT_MMCMDIV_INPUT (0xff << 8)
21# define BOSTON_PLAT_MMCMDIV_MUL (0xff << 16)
22# define BOSTON_PLAT_MMCMDIV_CLK1DIV (0xff << 24)
23
24static uint32_t ext_field(uint32_t val, uint32_t mask)
25{
26 return (val & mask) >> (ffs(mask) - 1);
27}
28
29static ulong clk_boston_get_rate(struct clk *clk)
30{
Simon Glassfa20e932020-12-03 16:55:20 -070031 struct clk_boston *state = dev_get_plat(clk->dev);
Paul Burton0399f442016-09-08 07:47:38 +010032 uint32_t in_rate, mul, div;
33 uint mmcmdiv;
34 int err;
35
36 err = regmap_read(state->regmap, BOSTON_PLAT_MMCMDIV, &mmcmdiv);
37 if (err)
38 return 0;
39
40 in_rate = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_INPUT);
41 mul = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_MUL);
42
43 switch (clk->id) {
44 case BOSTON_CLK_SYS:
45 div = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_CLK0DIV);
46 break;
47 case BOSTON_CLK_CPU:
48 div = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_CLK1DIV);
49 break;
50 default:
51 return 0;
52 }
53
54 return (in_rate * mul * 1000000) / div;
55}
56
57const struct clk_ops clk_boston_ops = {
58 .get_rate = clk_boston_get_rate,
59};
60
Simon Glassaad29ae2020-12-03 16:55:21 -070061static int clk_boston_of_to_plat(struct udevice *dev)
Paul Burton0399f442016-09-08 07:47:38 +010062{
Simon Glassfa20e932020-12-03 16:55:20 -070063 struct clk_boston *state = dev_get_plat(dev);
Paul Burton0399f442016-09-08 07:47:38 +010064 struct udevice *syscon;
65 int err;
66
67 err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
68 "regmap", &syscon);
69 if (err) {
Masahiro Yamada81e10422017-09-16 14:10:41 +090070 pr_err("unable to find syscon device\n");
Paul Burton0399f442016-09-08 07:47:38 +010071 return err;
72 }
73
74 state->regmap = syscon_get_regmap(syscon);
75 if (!state->regmap) {
Masahiro Yamada81e10422017-09-16 14:10:41 +090076 pr_err("unable to find regmap\n");
Paul Burton0399f442016-09-08 07:47:38 +010077 return -ENODEV;
78 }
79
80 return 0;
81}
82
83static const struct udevice_id clk_boston_match[] = {
84 {
85 .compatible = "img,boston-clock",
86 },
87 { /* sentinel */ }
88};
89
90U_BOOT_DRIVER(clk_boston) = {
91 .name = "boston_clock",
92 .id = UCLASS_CLK,
93 .of_match = clk_boston_match,
Simon Glassaad29ae2020-12-03 16:55:21 -070094 .of_to_plat = clk_boston_of_to_plat,
Simon Glass71fa5b42020-12-03 16:55:18 -070095 .plat_auto = sizeof(struct clk_boston),
Paul Burton0399f442016-09-08 07:47:38 +010096 .ops = &clk_boston_ops,
97};