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