1 // SPDX-License-Identifier: GPL-2.0
3 * Clock driver for twl device.
5 * inspired by the driver for the Palmas device
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>
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 */
27 #define ALL_GRP (P1_GRP | P2_GRP | P3_GRP)
34 struct twl_clock_info
{
42 twlclk_read(struct twl_clock_info
*info
, unsigned int slave_subgp
,
48 status
= twl_i2c_read_u8(slave_subgp
, &value
,
50 return (status
< 0) ? status
: value
;
54 twlclk_write(struct twl_clock_info
*info
, unsigned int slave_subgp
,
55 unsigned int offset
, u8 value
)
57 return twl_i2c_write_u8(slave_subgp
, value
,
61 static inline struct twl_clock_info
*to_twl_clks_info(struct clk_hw
*hw
)
63 return container_of(hw
, struct twl_clock_info
, hw
);
66 static unsigned long twl_clks_recalc_rate(struct clk_hw
*hw
,
67 unsigned long parent_rate
)
72 static int twl6032_clks_prepare(struct clk_hw
*hw
)
74 struct twl_clock_info
*cinfo
= to_twl_clks_info(hw
);
76 if (cinfo
->type
== TWL_TYPE_6030
) {
79 grp
= twlclk_read(cinfo
, TWL_MODULE_PM_RECEIVER
, VREG_GRP
);
83 return twlclk_write(cinfo
, TWL_MODULE_PM_RECEIVER
, VREG_STATE
,
84 grp
<< TWL6030_CFG_STATE_GRP_SHIFT
|
85 TWL6030_CFG_STATE_ON
);
88 return twlclk_write(cinfo
, TWL_MODULE_PM_RECEIVER
, VREG_STATE
,
89 TWL6030_CFG_STATE_ON
);
92 static void twl6032_clks_unprepare(struct clk_hw
*hw
)
94 struct twl_clock_info
*cinfo
= to_twl_clks_info(hw
);
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
);
102 ret
= twlclk_write(cinfo
, TWL_MODULE_PM_RECEIVER
, VREG_STATE
,
103 TWL6030_CFG_STATE_OFF
);
106 dev_err(cinfo
->dev
, "clk unprepare failed\n");
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
,
115 struct twl_clks_data
{
116 struct clk_init_data init
;
120 static const struct twl_clks_data twl6032_clks
[] = {
124 .ops
= &twl6032_clks_ops
,
125 .flags
= CLK_IGNORE_UNUSED
,
131 .name
= "clk32kaudio",
132 .ops
= &twl6032_clks_ops
,
133 .flags
= CLK_IGNORE_UNUSED
,
142 static int twl_clks_probe(struct platform_device
*pdev
)
144 struct clk_hw_onecell_data
*clk_data
;
145 const struct twl_clks_data
*hw_data
;
147 struct twl_clock_info
*cinfo
;
152 hw_data
= twl6032_clks
;
153 for (count
= 0; hw_data
[count
].init
.name
; count
++)
156 clk_data
= devm_kzalloc(&pdev
->dev
,
157 struct_size(clk_data
, hws
, count
),
162 clk_data
->num
= count
;
163 cinfo
= devm_kcalloc(&pdev
->dev
, count
, sizeof(*cinfo
), GFP_KERNEL
);
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
);
174 return dev_err_probe(&pdev
->dev
, ret
,
175 "Fail to register clock %s\n",
176 hw_data
[i
].init
.name
);
178 clk_data
->hws
[i
] = &cinfo
[i
].hw
;
181 ret
= devm_of_clk_add_hw_provider(&pdev
->dev
,
182 of_clk_hw_onecell_get
, clk_data
);
184 return dev_err_probe(&pdev
->dev
, ret
,
185 "Fail to add clock driver\n");
190 static const struct platform_device_id twl_clks_id
[] = {
192 .name
= "twl6030-clk",
193 .driver_data
= TWL_TYPE_6030
,
195 .name
= "twl6032-clk",
196 .driver_data
= TWL_TYPE_6032
,
201 MODULE_DEVICE_TABLE(platform
, twl_clks_id
);
203 static struct platform_driver twl_clks_driver
= {
207 .probe
= twl_clks_probe
,
208 .id_table
= twl_clks_id
,
211 module_platform_driver(twl_clks_driver
);
213 MODULE_DESCRIPTION("Clock driver for TWL Series Devices");
214 MODULE_LICENSE("GPL");