2 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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
27 * Allwinner GMAC clock
30 #include <sys/param.h>
31 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/module.h>
36 #include <machine/bus.h>
38 #include <dev/ofw/ofw_bus.h>
39 #include <dev/ofw/ofw_bus_subr.h>
40 #include <dev/ofw/ofw_subr.h>
42 #include <dev/clk/clk_mux.h>
43 #include <dev/clk/clk_gate.h>
45 #include "clkdev_if.h"
47 #define GMAC_CLK_PIT (0x1 << 2)
48 #define GMAC_CLK_PIT_SHIFT 2
49 #define GMAC_CLK_PIT_MII 0
50 #define GMAC_CLK_PIT_RGMII 1
51 #define GMAC_CLK_SRC (0x3 << 0)
52 #define GMAC_CLK_SRC_SHIFT 0
53 #define GMAC_CLK_SRC_MII 0
54 #define GMAC_CLK_SRC_EXT_RGMII 1
55 #define GMAC_CLK_SRC_RGMII 2
57 #define EMAC_TXC_DIV_CFG (1 << 15)
58 #define EMAC_TXC_DIV_CFG_SHIFT 15
59 #define EMAC_TXC_DIV_CFG_125MHZ 0
60 #define EMAC_TXC_DIV_CFG_25MHZ 1
61 #define EMAC_PHY_SELECT (1 << 16)
62 #define EMAC_PHY_SELECT_SHIFT 16
63 #define EMAC_PHY_SELECT_INT 0
64 #define EMAC_PHY_SELECT_EXT 1
65 #define EMAC_ETXDC (0x7 << 10)
66 #define EMAC_ETXDC_SHIFT 10
67 #define EMAC_ERXDC (0x1f << 5)
68 #define EMAC_ERXDC_SHIFT 5
71 #define CLK_IDX_RGMII 1
72 #define CLK_IDX_COUNT 2
74 static struct ofw_compat_data compat_data
[] = {
75 { "allwinner,sun7i-a20-gmac-clk", 1 },
79 struct aw_gmacclk_sc
{
87 #define GMACCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
88 #define GMACCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
89 #define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
90 #define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
93 aw_gmacclk_init(struct clknode
*clk
, device_t dev
)
95 struct aw_gmacclk_sc
*sc
;
98 sc
= clknode_get_softc(clk
);
101 GMACCLK_READ(sc
, &val
);
104 switch ((val
& GMAC_CLK_SRC
) >> GMAC_CLK_SRC_SHIFT
) {
105 case GMAC_CLK_SRC_MII
:
108 case GMAC_CLK_SRC_RGMII
:
109 index
= CLK_IDX_RGMII
;
115 clknode_init_parent_idx(clk
, index
);
120 aw_gmacclk_set_mux(struct clknode
*clk
, int index
)
122 struct aw_gmacclk_sc
*sc
;
123 uint32_t val
, clk_src
, pit
;
125 sc
= clknode_get_softc(clk
);
129 clk_src
= GMAC_CLK_SRC_MII
;
130 pit
= GMAC_CLK_PIT_MII
;
133 clk_src
= GMAC_CLK_SRC_RGMII
;
134 pit
= GMAC_CLK_PIT_RGMII
;
141 GMACCLK_READ(sc
, &val
);
142 val
&= ~(GMAC_CLK_SRC
| GMAC_CLK_PIT
);
143 val
|= (clk_src
<< GMAC_CLK_SRC_SHIFT
);
144 val
|= (pit
<< GMAC_CLK_PIT_SHIFT
);
145 GMACCLK_WRITE(sc
, val
);
151 static clknode_method_t aw_gmacclk_clknode_methods
[] = {
152 /* Device interface */
153 CLKNODEMETHOD(clknode_init
, aw_gmacclk_init
),
154 CLKNODEMETHOD(clknode_set_mux
, aw_gmacclk_set_mux
),
157 DEFINE_CLASS_1(aw_gmacclk_clknode
, aw_gmacclk_clknode_class
,
158 aw_gmacclk_clknode_methods
, sizeof(struct aw_gmacclk_sc
), clknode_class
);
161 aw_gmacclk_probe(device_t dev
)
163 if (!ofw_bus_status_okay(dev
))
166 if (ofw_bus_search_compatible(dev
, compat_data
)->ocd_data
== 0)
169 device_set_desc(dev
, "Allwinner GMAC Clock");
170 return (BUS_PROBE_DEFAULT
);
174 aw_gmacclk_attach(device_t dev
)
176 struct clknode_init_def def
;
177 struct aw_gmacclk_sc
*sc
;
178 struct clkdom
*clkdom
;
184 int error
, ncells
, i
;
186 node
= ofw_bus_get_node(dev
);
188 if (ofw_reg_to_paddr(node
, 0, &paddr
, &psize
, NULL
) != 0) {
189 device_printf(dev
, "cannot parse 'reg' property\n");
193 error
= ofw_bus_parse_xref_list_get_length(node
, "clocks",
194 "#clock-cells", &ncells
);
195 if (error
!= 0 || ncells
!= CLK_IDX_COUNT
) {
196 device_printf(dev
, "couldn't find parent clocks\n");
200 clkdom
= clkdom_create(dev
);
202 memset(&def
, 0, sizeof(def
));
203 error
= clk_parse_ofw_clk_name(dev
, node
, &def
.name
);
205 device_printf(dev
, "cannot parse clock name\n");
210 def
.parent_names
= malloc(sizeof(char *) * ncells
, M_OFWPROP
, M_WAITOK
);
211 for (i
= 0; i
< ncells
; i
++) {
212 error
= clk_get_by_ofw_index(dev
, 0, i
, &clk_parent
);
214 device_printf(dev
, "cannot get clock %d\n", error
);
217 def
.parent_names
[i
] = clk_get_name(clk_parent
);
218 clk_release(clk_parent
);
220 def
.parent_cnt
= ncells
;
222 clk
= clknode_create(clkdom
, &aw_gmacclk_clknode_class
, &def
);
224 device_printf(dev
, "cannot create clknode\n");
229 sc
= clknode_get_softc(clk
);
231 sc
->clkdev
= device_get_parent(dev
);
232 sc
->tx_delay
= sc
->rx_delay
= -1;
233 OF_getencprop(node
, "tx-delay", &sc
->tx_delay
, sizeof(sc
->tx_delay
));
234 OF_getencprop(node
, "rx-delay", &sc
->rx_delay
, sizeof(sc
->rx_delay
));
236 clknode_register(clkdom
, clk
);
238 if (clkdom_finit(clkdom
) != 0) {
239 device_printf(dev
, "cannot finalize clkdom initialization\n");
253 static device_method_t aw_gmacclk_methods
[] = {
254 /* Device interface */
255 DEVMETHOD(device_probe
, aw_gmacclk_probe
),
256 DEVMETHOD(device_attach
, aw_gmacclk_attach
),
261 static driver_t aw_gmacclk_driver
= {
267 EARLY_DRIVER_MODULE(aw_gmacclk
, simplebus
, aw_gmacclk_driver
, 0, 0,
268 BUS_PASS_RESOURCE
+ BUS_PASS_ORDER_MIDDLE
);