xref: /freebsd/sys/arm/ti/clk/ti_clk_clkctrl.c (revision 5f757f3f)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/bus.h>
31 #include <sys/malloc.h>
32 
33 #include <dev/clk/clk.h>
34 
35 #include <arm/ti/clk/ti_clk_clkctrl.h>
36 
37 #include "clkdev_if.h"
38 
39 #if 0
40 #define DPRINTF(dev, msg...) device_printf(dev, msg)
41 #else
42 #define DPRINTF(dev, msg...)
43 #endif
44 
45 /*
46  * clknode for clkctrl, implements gate and mux (for gpioc)
47  */
48 
49 #define GPIO_X_GDBCLK_MASK	0x00040000
50 #define IDLEST_MASK		0x00030000
51 #define MODULEMODE_MASK		0x00000003
52 
53 #define GPIOX_GDBCLK_ENABLE	0x00040000
54 #define GPIOX_GDBCLK_DISABLE	0x00000000
55 #define IDLEST_FUNC		0x00000000
56 #define IDLEST_TRANS		0x00010000
57 #define IDLEST_IDLE		0x00020000
58 #define IDLEST_DISABLE		0x00030000
59 
60 #define MODULEMODE_DISABLE	0x0
61 #define MODULEMODE_ENABLE	0x2
62 
63 struct ti_clkctrl_clknode_sc {
64 	device_t	dev;
65 	bool		gdbclk;
66 	/* omap4-cm range.host + ti,clkctrl reg[0] */
67 	uint32_t	register_offset;
68 };
69 
70 #define	WRITE4(_clk, off, val)						\
71 	CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
72 #define	READ4(_clk, off, val)						\
73 	CLKDEV_READ_4(clknode_get_device(_clk), off, val)
74 #define	DEVICE_LOCK(_clk)						\
75 	CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
76 #define	DEVICE_UNLOCK(_clk)						\
77 	CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
78 
79 static int
80 ti_clkctrl_init(struct clknode *clk, device_t dev)
81 {
82 	struct ti_clkctrl_clknode_sc *sc;
83 
84 	sc = clknode_get_softc(clk);
85 	sc->dev = dev;
86 
87 	clknode_init_parent_idx(clk, 0);
88 	return (0);
89 }
90 
91 static int
92 ti_clkctrl_set_gdbclk_gate(struct clknode *clk, bool enable)
93 {
94 	struct ti_clkctrl_clknode_sc *sc;
95 	uint32_t val, gpio_x_gdbclk;
96 	uint32_t timeout = 100;
97 
98 	sc = clknode_get_softc(clk);
99 
100 	READ4(clk, sc->register_offset, &val);
101 	DPRINTF(sc->dev, "val(%x) & (%x | %x = %x)\n",
102 	    val, GPIO_X_GDBCLK_MASK, MODULEMODE_MASK,
103 	    GPIO_X_GDBCLK_MASK | MODULEMODE_MASK);
104 
105 	if (enable) {
106 		val = val & MODULEMODE_MASK;
107 		val |= GPIOX_GDBCLK_ENABLE;
108 	} else {
109 		val = val & MODULEMODE_MASK;
110 		val |= GPIOX_GDBCLK_DISABLE;
111 	}
112 
113 	DPRINTF(sc->dev, "val %x\n", val);
114 	WRITE4(clk, sc->register_offset, val);
115 
116 	/* Wait */
117 	while (timeout) {
118 		READ4(clk, sc->register_offset, &val);
119 		gpio_x_gdbclk = val & GPIO_X_GDBCLK_MASK;
120 		if (enable && (gpio_x_gdbclk == GPIOX_GDBCLK_ENABLE))
121 			break;
122 		else if (!enable && (gpio_x_gdbclk == GPIOX_GDBCLK_DISABLE))
123 			break;
124 		DELAY(10);
125 		timeout--;
126 	}
127 	if (timeout == 0) {
128 		device_printf(sc->dev, "ti_clkctrl_set_gdbclk_gate: Timeout\n");
129 		return (1);
130 	}
131 
132 	return (0);
133 }
134 
135 static int
136 ti_clkctrl_set_gate(struct clknode *clk, bool enable)
137 {
138 	struct ti_clkctrl_clknode_sc *sc;
139 	uint32_t	val, idlest, module;
140 	uint32_t timeout=100;
141 	int err;
142 
143 	sc = clknode_get_softc(clk);
144 
145 	if (sc->gdbclk) {
146 		err = ti_clkctrl_set_gdbclk_gate(clk, enable);
147 		return (err);
148 	}
149 
150 	READ4(clk, sc->register_offset, &val);
151 
152 	if (enable)
153 		WRITE4(clk, sc->register_offset, MODULEMODE_ENABLE);
154 	else
155 		WRITE4(clk, sc->register_offset, MODULEMODE_DISABLE);
156 
157 	while (timeout) {
158 		READ4(clk, sc->register_offset, &val);
159 		idlest = val & IDLEST_MASK;
160 		module = val & MODULEMODE_MASK;
161 		if (enable &&
162 		    (idlest == IDLEST_FUNC || idlest == IDLEST_TRANS) &&
163 		    module == MODULEMODE_ENABLE)
164 			break;
165 		else if (!enable &&
166 		    idlest == IDLEST_DISABLE &&
167 		    module == MODULEMODE_DISABLE)
168 			break;
169 		DELAY(10);
170 		timeout--;
171 	}
172 
173 	if (timeout == 0) {
174 		device_printf(sc->dev, "ti_clkctrl_set_gate: Timeout\n");
175 		return (1);
176 	}
177 
178 	return (0);
179 }
180 
181 static clknode_method_t ti_clkctrl_clknode_methods[] = {
182 	/* Device interface */
183 	CLKNODEMETHOD(clknode_init,	ti_clkctrl_init),
184 	CLKNODEMETHOD(clknode_set_gate, ti_clkctrl_set_gate),
185 	CLKNODEMETHOD_END
186 };
187 
188 DEFINE_CLASS_1(ti_clkctrl_clknode, ti_clkctrl_clknode_class,
189     ti_clkctrl_clknode_methods, sizeof(struct ti_clkctrl_clknode_sc),
190     clknode_class);
191 
192 int
193 ti_clknode_clkctrl_register(struct clkdom *clkdom,
194     struct ti_clk_clkctrl_def *clkdef)
195 {
196 	struct clknode *clk;
197 	struct ti_clkctrl_clknode_sc *sc;
198 
199 	clk = clknode_create(clkdom, &ti_clkctrl_clknode_class,
200 	    &clkdef->clkdef);
201 
202 	if (clk == NULL) {
203 		return (1);
204 	}
205 
206 	sc = clknode_get_softc(clk);
207 	sc->register_offset = clkdef->register_offset;
208 	sc->gdbclk = clkdef->gdbclk;
209 
210 	if (clknode_register(clkdom, clk) == NULL) {
211 		return (2);
212 	}
213 	return (0);
214 }
215