xref: /freebsd/sys/arm/ti/clk/ti_clk_clkctrl.c (revision d4eeb02986980bf33dd56c41ceb9fc5f180c0d47)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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  * $FreeBSD$
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/malloc.h>
37 
38 #include <dev/extres/clk/clk.h>
39 
40 #include <arm/ti/clk/ti_clk_clkctrl.h>
41 
42 #include "clkdev_if.h"
43 
44 #if 0
45 #define DPRINTF(dev, msg...) device_printf(dev, msg)
46 #else
47 #define DPRINTF(dev, msg...)
48 #endif
49 
50 /*
51  * clknode for clkctrl, implements gate and mux (for gpioc)
52  */
53 
54 #define GPIO_X_GDBCLK_MASK	0x00040000
55 #define IDLEST_MASK		0x00030000
56 #define MODULEMODE_MASK		0x00000003
57 
58 #define GPIOX_GDBCLK_ENABLE	0x00040000
59 #define GPIOX_GDBCLK_DISABLE	0x00000000
60 #define IDLEST_FUNC		0x00000000
61 #define IDLEST_TRANS		0x00010000
62 #define IDLEST_IDLE		0x00020000
63 #define IDLEST_DISABLE		0x00030000
64 
65 #define MODULEMODE_DISABLE	0x0
66 #define MODULEMODE_ENABLE	0x2
67 
68 struct ti_clkctrl_clknode_sc {
69 	device_t	dev;
70 	bool		gdbclk;
71 	/* omap4-cm range.host + ti,clkctrl reg[0] */
72 	uint32_t	register_offset;
73 };
74 
75 #define	WRITE4(_clk, off, val)						\
76 	CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
77 #define	READ4(_clk, off, val)						\
78 	CLKDEV_READ_4(clknode_get_device(_clk), off, val)
79 #define	DEVICE_LOCK(_clk)						\
80 	CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
81 #define	DEVICE_UNLOCK(_clk)						\
82 	CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
83 
84 static int
85 ti_clkctrl_init(struct clknode *clk, device_t dev)
86 {
87 	struct ti_clkctrl_clknode_sc *sc;
88 
89 	sc = clknode_get_softc(clk);
90 	sc->dev = dev;
91 
92 	clknode_init_parent_idx(clk, 0);
93 	return (0);
94 }
95 
96 static int
97 ti_clkctrl_set_gdbclk_gate(struct clknode *clk, bool enable)
98 {
99 	struct ti_clkctrl_clknode_sc *sc;
100 	uint32_t val, gpio_x_gdbclk;
101 	uint32_t timeout = 100;
102 
103 	sc = clknode_get_softc(clk);
104 
105 	READ4(clk, sc->register_offset, &val);
106 	DPRINTF(sc->dev, "val(%x) & (%x | %x = %x)\n",
107 	    val, GPIO_X_GDBCLK_MASK, MODULEMODE_MASK,
108 	    GPIO_X_GDBCLK_MASK | MODULEMODE_MASK);
109 
110 	if (enable) {
111 		val = val & MODULEMODE_MASK;
112 		val |= GPIOX_GDBCLK_ENABLE;
113 	} else {
114 		val = val & MODULEMODE_MASK;
115 		val |= GPIOX_GDBCLK_DISABLE;
116 	}
117 
118 	DPRINTF(sc->dev, "val %x\n", val);
119 	WRITE4(clk, sc->register_offset, val);
120 
121 	/* Wait */
122 	while (timeout) {
123 		READ4(clk, sc->register_offset, &val);
124 		gpio_x_gdbclk = val & GPIO_X_GDBCLK_MASK;
125 		if (enable && (gpio_x_gdbclk == GPIOX_GDBCLK_ENABLE))
126 			break;
127 		else if (!enable && (gpio_x_gdbclk == GPIOX_GDBCLK_DISABLE))
128 			break;
129 		DELAY(10);
130 		timeout--;
131 	}
132 	if (timeout == 0) {
133 		device_printf(sc->dev, "ti_clkctrl_set_gdbclk_gate: Timeout\n");
134 		return (1);
135 	}
136 
137 	return (0);
138 }
139 
140 static int
141 ti_clkctrl_set_gate(struct clknode *clk, bool enable)
142 {
143 	struct ti_clkctrl_clknode_sc *sc;
144 	uint32_t	val, idlest, module;
145 	uint32_t timeout=100;
146 	int err;
147 
148 	sc = clknode_get_softc(clk);
149 
150 	if (sc->gdbclk) {
151 		err = ti_clkctrl_set_gdbclk_gate(clk, enable);
152 		return (err);
153 	}
154 
155 	READ4(clk, sc->register_offset, &val);
156 
157 	if (enable)
158 		WRITE4(clk, sc->register_offset, MODULEMODE_ENABLE);
159 	else
160 		WRITE4(clk, sc->register_offset, MODULEMODE_DISABLE);
161 
162 	while (timeout) {
163 		READ4(clk, sc->register_offset, &val);
164 		idlest = val & IDLEST_MASK;
165 		module = val & MODULEMODE_MASK;
166 		if (enable &&
167 		    (idlest == IDLEST_FUNC || idlest == IDLEST_TRANS) &&
168 		    module == MODULEMODE_ENABLE)
169 			break;
170 		else if (!enable &&
171 		    idlest == IDLEST_DISABLE &&
172 		    module == MODULEMODE_DISABLE)
173 			break;
174 		DELAY(10);
175 		timeout--;
176 	}
177 
178 	if (timeout == 0) {
179 		device_printf(sc->dev, "ti_clkctrl_set_gate: Timeout\n");
180 		return (1);
181 	}
182 
183 	return (0);
184 }
185 
186 static clknode_method_t ti_clkctrl_clknode_methods[] = {
187 	/* Device interface */
188 	CLKNODEMETHOD(clknode_init,	ti_clkctrl_init),
189 	CLKNODEMETHOD(clknode_set_gate, ti_clkctrl_set_gate),
190 	CLKNODEMETHOD_END
191 };
192 
193 DEFINE_CLASS_1(ti_clkctrl_clknode, ti_clkctrl_clknode_class,
194     ti_clkctrl_clknode_methods, sizeof(struct ti_clkctrl_clknode_sc),
195     clknode_class);
196 
197 int
198 ti_clknode_clkctrl_register(struct clkdom *clkdom,
199     struct ti_clk_clkctrl_def *clkdef)
200 {
201 	struct clknode *clk;
202 	struct ti_clkctrl_clknode_sc *sc;
203 
204 	clk = clknode_create(clkdom, &ti_clkctrl_clknode_class,
205 	    &clkdef->clkdef);
206 
207 	if (clk == NULL) {
208 		return (1);
209 	}
210 
211 	sc = clknode_get_softc(clk);
212 	sc->register_offset = clkdef->register_offset;
213 	sc->gdbclk = clkdef->gdbclk;
214 
215 	if (clknode_register(clkdom, clk) == NULL) {
216 		return (2);
217 	}
218 	return (0);
219 }
220