]> Git Repo - J-linux.git/blob - drivers/clk/clk-twl.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / clk / clk-twl.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Clock driver for twl device.
4  *
5  * inspired by the driver for the Palmas device
6  */
7
8 #include <linux/clk-provider.h>
9 #include <linux/mfd/twl.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/slab.h>
13
14 #define VREG_STATE              2
15 #define VREG_GRP                0
16 #define TWL6030_CFG_STATE_OFF   0x00
17 #define TWL6030_CFG_STATE_ON    0x01
18 #define TWL6030_CFG_STATE_MASK  0x03
19 #define TWL6030_CFG_STATE_GRP_SHIFT     5
20 #define TWL6030_CFG_STATE_APP_SHIFT     2
21 #define TWL6030_CFG_STATE_APP_MASK      (0x03 << TWL6030_CFG_STATE_APP_SHIFT)
22 #define TWL6030_CFG_STATE_APP(v)        (((v) & TWL6030_CFG_STATE_APP_MASK) >>\
23                                                 TWL6030_CFG_STATE_APP_SHIFT)
24 #define P1_GRP BIT(0) /* processor power group */
25 #define P2_GRP BIT(1)
26 #define P3_GRP BIT(2)
27 #define ALL_GRP (P1_GRP | P2_GRP | P3_GRP)
28
29 enum twl_type {
30         TWL_TYPE_6030,
31         TWL_TYPE_6032,
32 };
33
34 struct twl_clock_info {
35         struct device *dev;
36         enum twl_type type;
37         u8 base;
38         struct clk_hw hw;
39 };
40
41 static inline int
42 twlclk_read(struct twl_clock_info *info, unsigned int slave_subgp,
43             unsigned int offset)
44 {
45         u8 value;
46         int status;
47
48         status = twl_i2c_read_u8(slave_subgp, &value,
49                                  info->base + offset);
50         return (status < 0) ? status : value;
51 }
52
53 static inline int
54 twlclk_write(struct twl_clock_info *info, unsigned int slave_subgp,
55              unsigned int offset, u8 value)
56 {
57         return twl_i2c_write_u8(slave_subgp, value,
58                                 info->base + offset);
59 }
60
61 static inline struct twl_clock_info *to_twl_clks_info(struct clk_hw *hw)
62 {
63         return container_of(hw, struct twl_clock_info, hw);
64 }
65
66 static unsigned long twl_clks_recalc_rate(struct clk_hw *hw,
67                                           unsigned long parent_rate)
68 {
69         return 32768;
70 }
71
72 static int twl6032_clks_prepare(struct clk_hw *hw)
73 {
74         struct twl_clock_info *cinfo = to_twl_clks_info(hw);
75
76         if (cinfo->type == TWL_TYPE_6030) {
77                 int grp;
78
79                 grp = twlclk_read(cinfo, TWL_MODULE_PM_RECEIVER, VREG_GRP);
80                 if (grp < 0)
81                         return grp;
82
83                 return twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,
84                                     grp << TWL6030_CFG_STATE_GRP_SHIFT |
85                                     TWL6030_CFG_STATE_ON);
86         }
87
88         return twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,
89                             TWL6030_CFG_STATE_ON);
90 }
91
92 static void twl6032_clks_unprepare(struct clk_hw *hw)
93 {
94         struct twl_clock_info *cinfo = to_twl_clks_info(hw);
95         int ret;
96
97         if (cinfo->type == TWL_TYPE_6030)
98                 ret = twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,
99                                    ALL_GRP << TWL6030_CFG_STATE_GRP_SHIFT |
100                                    TWL6030_CFG_STATE_OFF);
101         else
102                 ret = twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,
103                                    TWL6030_CFG_STATE_OFF);
104
105         if (ret < 0)
106                 dev_err(cinfo->dev, "clk unprepare failed\n");
107 }
108
109 static const struct clk_ops twl6032_clks_ops = {
110         .prepare        = twl6032_clks_prepare,
111         .unprepare      = twl6032_clks_unprepare,
112         .recalc_rate    = twl_clks_recalc_rate,
113 };
114
115 struct twl_clks_data {
116         struct clk_init_data init;
117         u8 base;
118 };
119
120 static const struct twl_clks_data twl6032_clks[] = {
121         {
122                 .init = {
123                         .name = "clk32kg",
124                         .ops = &twl6032_clks_ops,
125                         .flags = CLK_IGNORE_UNUSED,
126                 },
127                 .base = 0x8C,
128         },
129         {
130                 .init = {
131                         .name = "clk32kaudio",
132                         .ops = &twl6032_clks_ops,
133                         .flags = CLK_IGNORE_UNUSED,
134                 },
135                 .base = 0x8F,
136         },
137         {
138                 /* sentinel */
139         }
140 };
141
142 static int twl_clks_probe(struct platform_device *pdev)
143 {
144         struct clk_hw_onecell_data *clk_data;
145         const struct twl_clks_data *hw_data;
146
147         struct twl_clock_info *cinfo;
148         int ret;
149         int i;
150         int count;
151
152         hw_data = twl6032_clks;
153         for (count = 0; hw_data[count].init.name; count++)
154                 ;
155
156         clk_data = devm_kzalloc(&pdev->dev,
157                                 struct_size(clk_data, hws, count),
158                                 GFP_KERNEL);
159         if (!clk_data)
160                 return -ENOMEM;
161
162         clk_data->num = count;
163         cinfo = devm_kcalloc(&pdev->dev, count, sizeof(*cinfo), GFP_KERNEL);
164         if (!cinfo)
165                 return -ENOMEM;
166
167         for (i = 0; i < count; i++) {
168                 cinfo[i].base = hw_data[i].base;
169                 cinfo[i].dev = &pdev->dev;
170                 cinfo[i].type = platform_get_device_id(pdev)->driver_data;
171                 cinfo[i].hw.init = &hw_data[i].init;
172                 ret = devm_clk_hw_register(&pdev->dev, &cinfo[i].hw);
173                 if (ret) {
174                         return dev_err_probe(&pdev->dev, ret,
175                                              "Fail to register clock %s\n",
176                                              hw_data[i].init.name);
177                 }
178                 clk_data->hws[i] = &cinfo[i].hw;
179         }
180
181         ret = devm_of_clk_add_hw_provider(&pdev->dev,
182                                           of_clk_hw_onecell_get, clk_data);
183         if (ret < 0)
184                 return dev_err_probe(&pdev->dev, ret,
185                                      "Fail to add clock driver\n");
186
187         return 0;
188 }
189
190 static const struct platform_device_id twl_clks_id[] = {
191         {
192                 .name = "twl6030-clk",
193                 .driver_data = TWL_TYPE_6030,
194         }, {
195                 .name = "twl6032-clk",
196                 .driver_data = TWL_TYPE_6032,
197         }, {
198                 /* sentinel */
199         }
200 };
201 MODULE_DEVICE_TABLE(platform, twl_clks_id);
202
203 static struct platform_driver twl_clks_driver = {
204         .driver = {
205                 .name = "twl-clk",
206         },
207         .probe = twl_clks_probe,
208         .id_table = twl_clks_id,
209 };
210
211 module_platform_driver(twl_clks_driver);
212
213 MODULE_DESCRIPTION("Clock driver for TWL Series Devices");
214 MODULE_LICENSE("GPL");
This page took 0.037665 seconds and 4 git commands to generate.