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