1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Texas Instruments sysc interconnect target driver
4  *
5  * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
6  */
7 
8 #include <clk.h>
9 #include <dm.h>
10 #include <dm/device_compat.h>
11 
12 enum ti_sysc_clocks {
13 	TI_SYSC_FCK,
14 	TI_SYSC_ICK,
15 	TI_SYSC_MAX_CLOCKS,
16 };
17 
18 static const char *const clock_names[] = {"fck", "ick"};
19 
20 struct ti_sysc_priv {
21 	int clocks_count;
22 	struct clk clocks[TI_SYSC_MAX_CLOCKS];
23 };
24 
25 static const struct udevice_id ti_sysc_ids[] = {
26 	{.compatible = "ti,sysc-omap2"},
27 	{.compatible = "ti,sysc-omap4"},
28 	{.compatible = "ti,sysc-omap4-simple"},
29 	{.compatible = "ti,sysc-omap3430-sr"},
30 	{.compatible = "ti,sysc-omap3630-sr"},
31 	{.compatible = "ti,sysc-omap4-sr"},
32 	{.compatible = "ti,sysc-omap3-sham"},
33 	{.compatible = "ti,sysc-omap-aes"},
34 	{.compatible = "ti,sysc-mcasp"},
35 	{.compatible = "ti,sysc-usb-host-fs"},
36 	{}
37 };
38 
ti_sysc_get_one_clock(struct udevice * dev,enum ti_sysc_clocks index)39 static int ti_sysc_get_one_clock(struct udevice *dev, enum ti_sysc_clocks index)
40 {
41 	struct ti_sysc_priv *priv = dev_get_priv(dev);
42 	const char *name;
43 	int err;
44 
45 	switch (index) {
46 	case TI_SYSC_FCK:
47 		break;
48 	case TI_SYSC_ICK:
49 		break;
50 	default:
51 		return -EINVAL;
52 	}
53 
54 	name = clock_names[index];
55 
56 	err = clk_get_by_name(dev, name, &priv->clocks[index]);
57 	if (err) {
58 		if (err == -ENODATA)
59 			return 0;
60 
61 		dev_err(dev, "failed to get %s clock\n", name);
62 		return err;
63 	}
64 
65 	return 0;
66 }
67 
ti_sysc_put_clocks(struct udevice * dev)68 static int ti_sysc_put_clocks(struct udevice *dev)
69 {
70 	struct ti_sysc_priv *priv = dev_get_priv(dev);
71 	int err;
72 
73 	err = clk_release_all(priv->clocks, priv->clocks_count);
74 	if (err)
75 		dev_err(dev, "failed to release all clocks\n");
76 
77 	return err;
78 }
79 
ti_sysc_get_clocks(struct udevice * dev)80 static int ti_sysc_get_clocks(struct udevice *dev)
81 {
82 	struct ti_sysc_priv *priv = dev_get_priv(dev);
83 	int i, err;
84 
85 	for (i = 0; i < TI_SYSC_MAX_CLOCKS; i++) {
86 		err = ti_sysc_get_one_clock(dev, i);
87 		if (!err)
88 			priv->clocks_count++;
89 		else if (err != -ENOENT)
90 			return err;
91 	}
92 
93 	return 0;
94 }
95 
ti_sysc_child_post_remove(struct udevice * dev)96 static int ti_sysc_child_post_remove(struct udevice *dev)
97 {
98 	struct ti_sysc_priv *priv = dev_get_priv(dev->parent);
99 	int i, err;
100 
101 	for (i = 0; i < priv->clocks_count; i++) {
102 		err = clk_disable(&priv->clocks[i]);
103 		if (err) {
104 			dev_err(dev->parent, "failed to disable %s clock\n",
105 				clock_names[i]);
106 			return err;
107 		}
108 	}
109 
110 	return 0;
111 }
112 
ti_sysc_child_pre_probe(struct udevice * dev)113 static int ti_sysc_child_pre_probe(struct udevice *dev)
114 {
115 	struct ti_sysc_priv *priv = dev_get_priv(dev->parent);
116 	int i, err;
117 
118 	for (i = 0; i < priv->clocks_count; i++) {
119 		err = clk_enable(&priv->clocks[i]);
120 		if (err) {
121 			dev_err(dev->parent, "failed to enable %s clock\n",
122 				clock_names[i]);
123 			return err;
124 		}
125 	}
126 
127 	return 0;
128 }
129 
ti_sysc_remove(struct udevice * dev)130 static int ti_sysc_remove(struct udevice *dev)
131 {
132 	return ti_sysc_put_clocks(dev);
133 }
134 
ti_sysc_probe(struct udevice * dev)135 static int ti_sysc_probe(struct udevice *dev)
136 {
137 	int err;
138 
139 	err = ti_sysc_get_clocks(dev);
140 	if (err)
141 		goto clocks_err;
142 
143 	return 0;
144 
145 clocks_err:
146 	ti_sysc_put_clocks(dev);
147 	return err;
148 }
149 
150 U_BOOT_DRIVER(ti_sysc) = {
151 	.name = "ti_sysc",
152 	.id = UCLASS_SIMPLE_BUS,
153 	.of_match = ti_sysc_ids,
154 	.probe = ti_sysc_probe,
155 	.remove = ti_sysc_remove,
156 	.child_pre_probe = ti_sysc_child_pre_probe,
157 	.child_post_remove = ti_sysc_child_post_remove,
158 	.priv_auto = sizeof(struct ti_sysc_priv)
159 };
160