1 /*-
2  * Copyright (c) 2017 Emmanuel Vadot <manu@freebsd.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
18  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
20  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/param.h>
27 #include <sys/systm.h>
28 #include <sys/bus.h>
29 
30 #include <dev/clk/clk.h>
31 
32 #include <dev/clk/allwinner/aw_clk.h>
33 #include <dev/clk/allwinner/aw_clk_prediv_mux.h>
34 
35 #include "clkdev_if.h"
36 
37 /*
38  * clknode for clocks matching the formula :
39  *
40  * clk = clkin / prediv / div
41  *
42  * and where prediv is conditional
43  *
44  */
45 
46 struct aw_clk_prediv_mux_sc {
47 	uint32_t	offset;
48 
49 	uint32_t		mux_shift;
50 	uint32_t		mux_mask;
51 
52 	struct aw_clk_factor	div;
53 	struct aw_clk_factor	prediv;
54 
55 	uint32_t	flags;
56 };
57 
58 #define	WRITE4(_clk, off, val)						\
59 	CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
60 #define	READ4(_clk, off, val)						\
61 	CLKDEV_READ_4(clknode_get_device(_clk), off, val)
62 #define	MODIFY4(_clk, off, clr, set )					\
63 	CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set)
64 #define	DEVICE_LOCK(_clk)							\
65 	CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
66 #define	DEVICE_UNLOCK(_clk)						\
67 	CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
68 
69 static int
70 aw_clk_prediv_mux_init(struct clknode *clk, device_t dev)
71 {
72 	struct aw_clk_prediv_mux_sc *sc;
73 	uint32_t val;
74 
75 	sc = clknode_get_softc(clk);
76 
77 	DEVICE_LOCK(clk);
78 	READ4(clk, sc->offset, &val);
79 	DEVICE_UNLOCK(clk);
80 
81 	/* Init the current parent */
82 	val = (val & sc->mux_mask) >> sc->mux_shift;
83 	clknode_init_parent_idx(clk, val);
84 
85 	return (0);
86 }
87 
88 static int
89 aw_clk_prediv_mux_set_mux(struct clknode *clk, int index)
90 {
91 	struct aw_clk_prediv_mux_sc *sc;
92 	uint32_t val;
93 
94 	sc = clknode_get_softc(clk);
95 
96 	DEVICE_LOCK(clk);
97 	READ4(clk, sc->offset, &val);
98 	val &= ~sc->mux_mask;
99 	val |= index << sc->mux_shift;
100 	WRITE4(clk, sc->offset, val);
101 	DEVICE_UNLOCK(clk);
102 
103 	return (0);
104 }
105 
106 static int
107 aw_clk_prediv_mux_recalc(struct clknode *clk, uint64_t *freq)
108 {
109 	struct aw_clk_prediv_mux_sc *sc;
110 	uint32_t val, div, prediv;
111 
112 	sc = clknode_get_softc(clk);
113 
114 	DEVICE_LOCK(clk);
115 	READ4(clk, sc->offset, &val);
116 	DEVICE_UNLOCK(clk);
117 
118 	div = aw_clk_get_factor(val, &sc->div);
119 	prediv = aw_clk_get_factor(val, &sc->prediv);
120 
121 	*freq = *freq / prediv / div;
122 	return (0);
123 }
124 
125 static clknode_method_t aw_prediv_mux_clknode_methods[] = {
126 	/* Device interface */
127 	CLKNODEMETHOD(clknode_init,		aw_clk_prediv_mux_init),
128 	CLKNODEMETHOD(clknode_set_mux,		aw_clk_prediv_mux_set_mux),
129 	CLKNODEMETHOD(clknode_recalc_freq,	aw_clk_prediv_mux_recalc),
130 	CLKNODEMETHOD_END
131 };
132 
133 DEFINE_CLASS_1(aw_prediv_mux_clknode, aw_prediv_mux_clknode_class,
134     aw_prediv_mux_clknode_methods, sizeof(struct aw_clk_prediv_mux_sc),
135     clknode_class);
136 
137 int
138 aw_clk_prediv_mux_register(struct clkdom *clkdom, struct aw_clk_prediv_mux_def *clkdef)
139 {
140 	struct clknode *clk;
141 	struct aw_clk_prediv_mux_sc *sc;
142 
143 	clk = clknode_create(clkdom, &aw_prediv_mux_clknode_class, &clkdef->clkdef);
144 	if (clk == NULL)
145 		return (1);
146 
147 	sc = clknode_get_softc(clk);
148 
149 	sc->offset = clkdef->offset;
150 
151 	sc->mux_shift = clkdef->mux_shift;
152 	sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;
153 
154 	sc->div.shift = clkdef->div.shift;
155 	sc->div.mask = ((1 << clkdef->div.width) - 1) << sc->div.shift;
156 	sc->div.value = clkdef->div.value;
157 	sc->div.cond_shift = clkdef->div.cond_shift;
158 	sc->div.cond_mask = ((1 << clkdef->div.cond_width) - 1) << sc->div.shift;
159 	sc->div.cond_value = clkdef->div.cond_value;
160 	sc->div.flags = clkdef->div.flags;
161 
162 	sc->prediv.shift = clkdef->prediv.shift;
163 	sc->prediv.mask = ((1 << clkdef->prediv.width) - 1) << sc->prediv.shift;
164 	sc->prediv.value = clkdef->prediv.value;
165 	sc->prediv.cond_shift = clkdef->prediv.cond_shift;
166 	if (clkdef->prediv.cond_width != 0)
167 		sc->prediv.cond_mask = ((1 << clkdef->prediv.cond_width) - 1) << sc->prediv.shift;
168 	else
169 		sc->prediv.cond_mask = clkdef->prediv.cond_mask;
170 	sc->prediv.cond_value = clkdef->prediv.cond_value;
171 	sc->prediv.flags = clkdef->prediv.flags;
172 
173 	sc->flags = clkdef->flags;
174 
175 	clknode_register(clkdom, clk);
176 
177 	return (0);
178 }
179