aw_gmacclk.c revision 308324
1/*-
2 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: stable/11/sys/arm/allwinner/clk/aw_gmacclk.c 308324 2016-11-05 04:17:32Z mmel $
27 */
28
29/*
30 * Allwinner GMAC clock
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_gmacclk.c 308324 2016-11-05 04:17:32Z mmel $");
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/bus.h>
39#include <sys/rman.h>
40#include <sys/kernel.h>
41#include <sys/module.h>
42#include <machine/bus.h>
43
44#include <dev/ofw/ofw_bus.h>
45#include <dev/ofw/ofw_bus_subr.h>
46#include <dev/ofw/ofw_subr.h>
47
48#include <dev/extres/clk/clk_mux.h>
49#include <dev/extres/clk/clk_gate.h>
50
51#include "clkdev_if.h"
52
53#define	GMAC_CLK_PIT		(0x1 << 2)
54#define	GMAC_CLK_PIT_SHIFT	2
55#define	GMAC_CLK_PIT_MII	0
56#define	GMAC_CLK_PIT_RGMII	1
57#define	GMAC_CLK_SRC		(0x3 << 0)
58#define	GMAC_CLK_SRC_SHIFT	0
59#define	GMAC_CLK_SRC_MII	0
60#define	GMAC_CLK_SRC_EXT_RGMII	1
61#define	GMAC_CLK_SRC_RGMII	2
62
63#define	EMAC_TXC_DIV_CFG	(1 << 15)
64#define	EMAC_TXC_DIV_CFG_SHIFT	15
65#define	EMAC_TXC_DIV_CFG_125MHZ	0
66#define	EMAC_TXC_DIV_CFG_25MHZ	1
67#define	EMAC_PHY_SELECT		(1 << 16)
68#define	EMAC_PHY_SELECT_SHIFT	16
69#define	EMAC_PHY_SELECT_INT	0
70#define	EMAC_PHY_SELECT_EXT	1
71#define	EMAC_ETXDC		(0x7 << 10)
72#define	EMAC_ETXDC_SHIFT	10
73#define	EMAC_ERXDC		(0x1f << 5)
74#define	EMAC_ERXDC_SHIFT	5
75
76#define	CLK_IDX_MII		0
77#define	CLK_IDX_RGMII		1
78#define	CLK_IDX_COUNT		2
79
80enum aw_gmacclk_type {
81	GMACCLK_A20 = 1,
82	GMACCLK_A83T,
83};
84
85static struct ofw_compat_data compat_data[] = {
86	{ "allwinner,sun7i-a20-gmac-clk",	GMACCLK_A20 },
87	{ "allwinner,sun8i-a83t-emac-clk",	GMACCLK_A83T },
88	{ NULL, 0 }
89};
90
91struct aw_gmacclk_sc {
92	device_t	clkdev;
93	bus_addr_t	reg;
94	enum aw_gmacclk_type type;
95
96	int		rx_delay;
97	int		tx_delay;
98};
99
100#define	GMACCLK_READ(sc, val)	CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
101#define	GMACCLK_WRITE(sc, val)	CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
102#define	DEVICE_LOCK(sc)		CLKDEV_DEVICE_LOCK((sc)->clkdev)
103#define	DEVICE_UNLOCK(sc)	CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
104
105static int
106aw_gmacclk_init(struct clknode *clk, device_t dev)
107{
108	struct aw_gmacclk_sc *sc;
109	uint32_t val, index;
110
111	sc = clknode_get_softc(clk);
112
113	DEVICE_LOCK(sc);
114	GMACCLK_READ(sc, &val);
115	DEVICE_UNLOCK(sc);
116
117	switch ((val & GMAC_CLK_SRC) >> GMAC_CLK_SRC_SHIFT) {
118	case GMAC_CLK_SRC_MII:
119		index = CLK_IDX_MII;
120		break;
121	case GMAC_CLK_SRC_RGMII:
122		index = CLK_IDX_RGMII;
123		break;
124	default:
125		return (ENXIO);
126	}
127
128	clknode_init_parent_idx(clk, index);
129	return (0);
130}
131
132static int
133aw_gmacclk_set_mux(struct clknode *clk, int index)
134{
135	struct aw_gmacclk_sc *sc;
136	uint32_t val, clk_src, pit, txc_div;
137	int error;
138
139	sc = clknode_get_softc(clk);
140	error = 0;
141
142	switch (index) {
143	case CLK_IDX_MII:
144		clk_src = GMAC_CLK_SRC_MII;
145		pit = GMAC_CLK_PIT_MII;
146		txc_div = EMAC_TXC_DIV_CFG_25MHZ;
147		break;
148	case CLK_IDX_RGMII:
149		clk_src = GMAC_CLK_SRC_RGMII;
150		pit = GMAC_CLK_PIT_RGMII;
151		txc_div = EMAC_TXC_DIV_CFG_125MHZ;
152		break;
153	default:
154		return (ENXIO);
155	}
156
157	DEVICE_LOCK(sc);
158	GMACCLK_READ(sc, &val);
159	val &= ~(GMAC_CLK_SRC | GMAC_CLK_PIT);
160	val |= (clk_src << GMAC_CLK_SRC_SHIFT);
161	val |= (pit << GMAC_CLK_PIT_SHIFT);
162	if (sc->type == GMACCLK_A83T) {
163		val &= ~EMAC_TXC_DIV_CFG;
164		val |= (txc_div << EMAC_TXC_DIV_CFG_SHIFT);
165		val &= ~EMAC_PHY_SELECT;
166		val |= (EMAC_PHY_SELECT_EXT << EMAC_PHY_SELECT_SHIFT);
167		if (sc->tx_delay >= 0) {
168			val &= ~EMAC_ETXDC;
169			val |= (sc->tx_delay << EMAC_ETXDC_SHIFT);
170		}
171		if (sc->rx_delay >= 0) {
172			val &= ~EMAC_ERXDC;
173			val |= (sc->rx_delay << EMAC_ERXDC_SHIFT);
174		}
175	}
176	GMACCLK_WRITE(sc, val);
177	DEVICE_UNLOCK(sc);
178
179	return (0);
180}
181
182static clknode_method_t aw_gmacclk_clknode_methods[] = {
183	/* Device interface */
184	CLKNODEMETHOD(clknode_init,		aw_gmacclk_init),
185	CLKNODEMETHOD(clknode_set_mux,		aw_gmacclk_set_mux),
186	CLKNODEMETHOD_END
187};
188DEFINE_CLASS_1(aw_gmacclk_clknode, aw_gmacclk_clknode_class,
189    aw_gmacclk_clknode_methods, sizeof(struct aw_gmacclk_sc), clknode_class);
190
191static int
192aw_gmacclk_probe(device_t dev)
193{
194	if (!ofw_bus_status_okay(dev))
195		return (ENXIO);
196
197	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
198		return (ENXIO);
199
200	device_set_desc(dev, "Allwinner GMAC Clock");
201	return (BUS_PROBE_DEFAULT);
202}
203
204static int
205aw_gmacclk_attach(device_t dev)
206{
207	struct clknode_init_def def;
208	struct aw_gmacclk_sc *sc;
209	struct clkdom *clkdom;
210	struct clknode *clk;
211	clk_t clk_parent;
212	bus_addr_t paddr;
213	bus_size_t psize;
214	phandle_t node;
215	int error, ncells, i;
216
217	node = ofw_bus_get_node(dev);
218
219	if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
220		device_printf(dev, "cannot parse 'reg' property\n");
221		return (ENXIO);
222	}
223
224	error = ofw_bus_parse_xref_list_get_length(node, "clocks",
225	    "#clock-cells", &ncells);
226	if (error != 0 || ncells != CLK_IDX_COUNT) {
227		device_printf(dev, "couldn't find parent clocks\n");
228		return (ENXIO);
229	}
230
231	clkdom = clkdom_create(dev);
232
233	memset(&def, 0, sizeof(def));
234	error = clk_parse_ofw_clk_name(dev, node, &def.name);
235	if (error != 0) {
236		device_printf(dev, "cannot parse clock name\n");
237		error = ENXIO;
238		goto fail;
239	}
240	def.id = 1;
241	def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK);
242	for (i = 0; i < ncells; i++) {
243		error = clk_get_by_ofw_index(dev, 0, i, &clk_parent);
244		if (error != 0) {
245			device_printf(dev, "cannot get clock %d\n", error);
246			goto fail;
247		}
248		def.parent_names[i] = clk_get_name(clk_parent);
249		clk_release(clk_parent);
250	}
251	def.parent_cnt = ncells;
252
253	clk = clknode_create(clkdom, &aw_gmacclk_clknode_class, &def);
254	if (clk == NULL) {
255		device_printf(dev, "cannot create clknode\n");
256		error = ENXIO;
257		goto fail;
258	}
259
260	sc = clknode_get_softc(clk);
261	sc->reg = paddr;
262	sc->clkdev = device_get_parent(dev);
263	sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
264	sc->tx_delay = sc->rx_delay = -1;
265	OF_getencprop(node, "tx-delay", &sc->tx_delay, sizeof(sc->tx_delay));
266	OF_getencprop(node, "rx-delay", &sc->rx_delay, sizeof(sc->rx_delay));
267
268	clknode_register(clkdom, clk);
269
270	if (clkdom_finit(clkdom) != 0) {
271		device_printf(dev, "cannot finalize clkdom initialization\n");
272		error = ENXIO;
273		goto fail;
274	}
275
276	if (bootverbose)
277		clkdom_dump(clkdom);
278
279	return (0);
280
281fail:
282	return (error);
283}
284
285static device_method_t aw_gmacclk_methods[] = {
286	/* Device interface */
287	DEVMETHOD(device_probe,		aw_gmacclk_probe),
288	DEVMETHOD(device_attach,	aw_gmacclk_attach),
289
290	DEVMETHOD_END
291};
292
293static driver_t aw_gmacclk_driver = {
294	"aw_gmacclk",
295	aw_gmacclk_methods,
296	0
297};
298
299static devclass_t aw_gmacclk_devclass;
300
301EARLY_DRIVER_MODULE(aw_gmacclk, simplebus, aw_gmacclk_driver,
302    aw_gmacclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
303