1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2016 Imagination Technologies
4  */
5 
6 #include <common.h>
7 #include <clk-uclass.h>
8 #include <dm.h>
9 #include <dt-bindings/clock/boston-clock.h>
10 #include <regmap.h>
11 #include <syscon.h>
12 #include <linux/bitops.h>
13 
14 struct 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 
ext_field(uint32_t val,uint32_t mask)24 static uint32_t ext_field(uint32_t val, uint32_t mask)
25 {
26 	return (val & mask) >> (ffs(mask) - 1);
27 }
28 
clk_boston_get_rate(struct clk * clk)29 static ulong clk_boston_get_rate(struct clk *clk)
30 {
31 	struct clk_boston *state = dev_get_plat(clk->dev);
32 	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 
57 const struct clk_ops clk_boston_ops = {
58 	.get_rate = clk_boston_get_rate,
59 };
60 
clk_boston_of_to_plat(struct udevice * dev)61 static int clk_boston_of_to_plat(struct udevice *dev)
62 {
63 	struct clk_boston *state = dev_get_plat(dev);
64 	struct udevice *syscon;
65 	int err;
66 
67 	err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
68 					   "regmap", &syscon);
69 	if (err) {
70 		pr_err("unable to find syscon device\n");
71 		return err;
72 	}
73 
74 	state->regmap = syscon_get_regmap(syscon);
75 	if (!state->regmap) {
76 		pr_err("unable to find regmap\n");
77 		return -ENODEV;
78 	}
79 
80 	return 0;
81 }
82 
83 static const struct udevice_id clk_boston_match[] = {
84 	{
85 		.compatible = "img,boston-clock",
86 	},
87 	{ /* sentinel */ }
88 };
89 
90 U_BOOT_DRIVER(clk_boston) = {
91 	.name = "boston_clock",
92 	.id = UCLASS_CLK,
93 	.of_match = clk_boston_match,
94 	.of_to_plat = clk_boston_of_to_plat,
95 	.plat_auto	= sizeof(struct clk_boston),
96 	.ops = &clk_boston_ops,
97 };
98