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#include <linux/printk.h>
14
15struct clk_boston {
16	struct regmap *regmap;
17};
18
19#define BOSTON_PLAT_MMCMDIV		0x30
20# define BOSTON_PLAT_MMCMDIV_CLK0DIV	(0xff << 0)
21# define BOSTON_PLAT_MMCMDIV_INPUT	(0xff << 8)
22# define BOSTON_PLAT_MMCMDIV_MUL	(0xff << 16)
23# define BOSTON_PLAT_MMCMDIV_CLK1DIV	(0xff << 24)
24
25static uint32_t ext_field(uint32_t val, uint32_t mask)
26{
27	return (val & mask) >> (ffs(mask) - 1);
28}
29
30static ulong clk_boston_get_rate(struct clk *clk)
31{
32	struct clk_boston *state = dev_get_plat(clk->dev);
33	uint32_t in_rate, mul, div;
34	uint mmcmdiv;
35	int err;
36
37	err = regmap_read(state->regmap, BOSTON_PLAT_MMCMDIV, &mmcmdiv);
38	if (err)
39		return 0;
40
41	in_rate = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_INPUT);
42	mul = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_MUL);
43
44	switch (clk->id) {
45	case BOSTON_CLK_SYS:
46		div = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_CLK0DIV);
47		break;
48	case BOSTON_CLK_CPU:
49		div = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_CLK1DIV);
50		break;
51	default:
52		return 0;
53	}
54
55	return (in_rate * mul * 1000000) / div;
56}
57
58const struct clk_ops clk_boston_ops = {
59	.get_rate = clk_boston_get_rate,
60};
61
62static int clk_boston_of_to_plat(struct udevice *dev)
63{
64	struct clk_boston *state = dev_get_plat(dev);
65	struct udevice *syscon;
66	int err;
67
68	err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
69					   "regmap", &syscon);
70	if (err) {
71		pr_err("unable to find syscon device\n");
72		return err;
73	}
74
75	state->regmap = syscon_get_regmap(syscon);
76	if (!state->regmap) {
77		pr_err("unable to find regmap\n");
78		return -ENODEV;
79	}
80
81	return 0;
82}
83
84static const struct udevice_id clk_boston_match[] = {
85	{
86		.compatible = "img,boston-clock",
87	},
88	{ /* sentinel */ }
89};
90
91U_BOOT_DRIVER(clk_boston) = {
92	.name = "boston_clock",
93	.id = UCLASS_CLK,
94	.of_match = clk_boston_match,
95	.of_to_plat = clk_boston_of_to_plat,
96	.plat_auto	= sizeof(struct clk_boston),
97	.ops = &clk_boston_ops,
98};
99