xref: /linux/drivers/clk/clk-twl.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
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");
215