1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * abx500 clock implementation for ux500 platform. |
4 | * |
5 | * Copyright (C) 2012 ST-Ericsson SA |
6 | * Author: Ulf Hansson <ulf.hansson@linaro.org> |
7 | */ |
8 | |
9 | #include <linux/err.h> |
10 | #include <linux/module.h> |
11 | #include <linux/device.h> |
12 | #include <linux/of.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/mfd/abx500/ab8500.h> |
15 | #include <linux/mfd/abx500/ab8500-sysctrl.h> |
16 | #include <linux/clkdev.h> |
17 | #include <linux/clk-provider.h> |
18 | #include <dt-bindings/clock/ste-ab8500.h> |
19 | #include "clk.h" |
20 | |
21 | #define AB8500_NUM_CLKS 6 |
22 | |
23 | static struct clk *ab8500_clks[AB8500_NUM_CLKS]; |
24 | static struct clk_onecell_data ab8500_clk_data; |
25 | |
26 | /* Clock definitions for ab8500 */ |
27 | static int ab8500_reg_clks(struct device *dev) |
28 | { |
29 | int ret; |
30 | struct clk *clk; |
31 | struct device_node *np = dev->of_node; |
32 | const char *intclk_parents[] = {"ab8500_sysclk" , "ulpclk" }; |
33 | u16 intclk_reg_sel[] = {0 , AB8500_SYSULPCLKCTRL1}; |
34 | u8 intclk_reg_mask[] = {0 , AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK}; |
35 | u8 intclk_reg_bits[] = { |
36 | 0 , |
37 | (1 << AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_SHIFT) |
38 | }; |
39 | |
40 | /* Enable SWAT */ |
41 | ret = ab8500_sysctrl_set(AB8500_SWATCTRL, AB8500_SWATCTRL_SWATENABLE); |
42 | if (ret) |
43 | return ret; |
44 | |
45 | /* ab8500_sysclk2 */ |
46 | clk = clk_reg_sysctrl_gate(dev , name: "ab8500_sysclk2" , parent_name: "ab8500_sysclk" , |
47 | AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ, |
48 | AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ, enable_delay_us: 0, flags: 0); |
49 | ab8500_clks[AB8500_SYSCLK_BUF2] = clk; |
50 | |
51 | /* ab8500_sysclk3 */ |
52 | clk = clk_reg_sysctrl_gate(dev , name: "ab8500_sysclk3" , parent_name: "ab8500_sysclk" , |
53 | AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ, |
54 | AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ, enable_delay_us: 0, flags: 0); |
55 | ab8500_clks[AB8500_SYSCLK_BUF3] = clk; |
56 | |
57 | /* ab8500_sysclk4 */ |
58 | clk = clk_reg_sysctrl_gate(dev , name: "ab8500_sysclk4" , parent_name: "ab8500_sysclk" , |
59 | AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ, |
60 | AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ, enable_delay_us: 0, flags: 0); |
61 | ab8500_clks[AB8500_SYSCLK_BUF4] = clk; |
62 | |
63 | /* ab_ulpclk */ |
64 | clk = clk_reg_sysctrl_gate_fixed_rate(dev, name: "ulpclk" , NULL, |
65 | AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_ULPCLKREQ, |
66 | AB8500_SYSULPCLKCTRL1_ULPCLKREQ, |
67 | rate: 38400000, enable_delay_us: 9000, flags: 0); |
68 | ab8500_clks[AB8500_SYSCLK_ULP] = clk; |
69 | |
70 | /* ab8500_intclk */ |
71 | clk = clk_reg_sysctrl_set_parent(dev , name: "intclk" , parent_names: intclk_parents, num_parents: 2, |
72 | reg_sel: intclk_reg_sel, reg_mask: intclk_reg_mask, reg_bits: intclk_reg_bits, flags: 0); |
73 | ab8500_clks[AB8500_SYSCLK_INT] = clk; |
74 | |
75 | /* ab8500_audioclk */ |
76 | clk = clk_reg_sysctrl_gate(dev , name: "audioclk" , parent_name: "intclk" , |
77 | AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_AUDIOCLKENA, |
78 | AB8500_SYSULPCLKCTRL1_AUDIOCLKENA, enable_delay_us: 0, flags: 0); |
79 | ab8500_clks[AB8500_SYSCLK_AUDIO] = clk; |
80 | |
81 | ab8500_clk_data.clks = ab8500_clks; |
82 | ab8500_clk_data.clk_num = ARRAY_SIZE(ab8500_clks); |
83 | of_clk_add_provider(np, clk_src_get: of_clk_src_onecell_get, data: &ab8500_clk_data); |
84 | |
85 | dev_info(dev, "registered clocks for ab850x\n" ); |
86 | |
87 | return 0; |
88 | } |
89 | |
90 | static int abx500_clk_probe(struct platform_device *pdev) |
91 | { |
92 | struct ab8500 *parent = dev_get_drvdata(dev: pdev->dev.parent); |
93 | int ret; |
94 | |
95 | if (is_ab8500(ab: parent) || is_ab8505(ab: parent)) { |
96 | ret = ab8500_reg_clks(dev: &pdev->dev); |
97 | } else { |
98 | dev_err(&pdev->dev, "non supported plf id\n" ); |
99 | return -ENODEV; |
100 | } |
101 | |
102 | return ret; |
103 | } |
104 | |
105 | static const struct of_device_id abx500_clk_match[] = { |
106 | { .compatible = "stericsson,ab8500-clk" , }, |
107 | {} |
108 | }; |
109 | |
110 | static struct platform_driver abx500_clk_driver = { |
111 | .driver = { |
112 | .name = "abx500-clk" , |
113 | .of_match_table = abx500_clk_match, |
114 | }, |
115 | .probe = abx500_clk_probe, |
116 | }; |
117 | |
118 | static int __init abx500_clk_init(void) |
119 | { |
120 | return platform_driver_register(&abx500_clk_driver); |
121 | } |
122 | arch_initcall(abx500_clk_init); |
123 | |
124 | MODULE_AUTHOR("Ulf Hansson <ulf.hansson@linaro.org" ); |
125 | MODULE_DESCRIPTION("ABX500 clk driver" ); |
126 | MODULE_LICENSE("GPL v2" ); |
127 | |