5c05e3d78d5b011ade36b4f1e6362c90f1e79efd
[platform/kernel/u-boot.git] / drivers / clk / clk_boston.c
1 /*
2  * Copyright (C) 2016 Imagination Technologies
3  *
4  * SPDX-License-Identifier:     GPL-2.0
5  */
6
7 #include <common.h>
8 #include <clk-uclass.h>
9 #include <dm.h>
10 #include <dt-bindings/clock/boston-clock.h>
11 #include <regmap.h>
12 #include <syscon.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
24 static uint32_t ext_field(uint32_t val, uint32_t mask)
25 {
26         return (val & mask) >> (ffs(mask) - 1);
27 }
28
29 static ulong clk_boston_get_rate(struct clk *clk)
30 {
31         struct clk_boston *state = dev_get_platdata(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
61 static int clk_boston_ofdata_to_platdata(struct udevice *dev)
62 {
63         struct clk_boston *state = dev_get_platdata(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         .ofdata_to_platdata = clk_boston_ofdata_to_platdata,
95         .platdata_auto_alloc_size = sizeof(struct clk_boston),
96         .ops = &clk_boston_ops,
97 };