xref: /freebsd/sys/arm/annapurna/alpine/alpine_serdes.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
1246d07a7SWojciech Macek /*-
2246d07a7SWojciech Macek  * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
3246d07a7SWojciech Macek  * All rights reserved.
4246d07a7SWojciech Macek  *
5246d07a7SWojciech Macek  * Developed by Semihalf.
6246d07a7SWojciech Macek  *
7246d07a7SWojciech Macek  * Redistribution and use in source and binary forms, with or without
8246d07a7SWojciech Macek  * modification, are permitted provided that the following conditions
9246d07a7SWojciech Macek  * are met:
10246d07a7SWojciech Macek  * 1. Redistributions of source code must retain the above copyright
11246d07a7SWojciech Macek  *    notice, this list of conditions and the following disclaimer.
12246d07a7SWojciech Macek  * 2. Redistributions in binary form must reproduce the above copyright
13246d07a7SWojciech Macek  *    notice, this list of conditions and the following disclaimer in the
14246d07a7SWojciech Macek  *    documentation and/or other materials provided with the distribution.
15246d07a7SWojciech Macek  *
16246d07a7SWojciech Macek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17246d07a7SWojciech Macek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18246d07a7SWojciech Macek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19246d07a7SWojciech Macek  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20246d07a7SWojciech Macek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21246d07a7SWojciech Macek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22246d07a7SWojciech Macek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23246d07a7SWojciech Macek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24246d07a7SWojciech Macek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25246d07a7SWojciech Macek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26246d07a7SWojciech Macek  * SUCH DAMAGE.
27246d07a7SWojciech Macek  */
28246d07a7SWojciech Macek 
29246d07a7SWojciech Macek #include <sys/param.h>
30246d07a7SWojciech Macek #include <sys/systm.h>
31246d07a7SWojciech Macek #include <sys/kernel.h>
32246d07a7SWojciech Macek #include <sys/module.h>
33246d07a7SWojciech Macek #include <sys/bus.h>
34246d07a7SWojciech Macek #include <sys/rman.h>
35246d07a7SWojciech Macek #include <sys/conf.h>
36246d07a7SWojciech Macek #include <sys/resource.h>
37246d07a7SWojciech Macek 
38246d07a7SWojciech Macek #include <machine/bus.h>
39246d07a7SWojciech Macek #include <dev/ofw/ofw_bus_subr.h>
40246d07a7SWojciech Macek 
41246d07a7SWojciech Macek #include "al_serdes.h"
42246d07a7SWojciech Macek #include "alpine_serdes.h"
43246d07a7SWojciech Macek 
44246d07a7SWojciech Macek #define SERDES_NUM_GROUPS	5
45246d07a7SWojciech Macek 
46246d07a7SWojciech Macek static void *serdes_base;
47246d07a7SWojciech Macek static uint32_t serdes_grp_offset[] = {0, 0x400, 0x800, 0xc00, 0x2000};
48246d07a7SWojciech Macek 
49246d07a7SWojciech Macek static struct alpine_serdes_eth_group_mode {
50246d07a7SWojciech Macek 	struct mtx			lock;
51246d07a7SWojciech Macek 	enum alpine_serdes_eth_mode	mode;
52246d07a7SWojciech Macek 	bool				mode_set;
53246d07a7SWojciech Macek } alpine_serdes_eth_group_mode[SERDES_NUM_GROUPS];
54246d07a7SWojciech Macek 
55246d07a7SWojciech Macek static int al_serdes_probe(device_t dev);
56246d07a7SWojciech Macek static int al_serdes_attach(device_t dev);
57246d07a7SWojciech Macek static int al_serdes_detach(device_t dev);
58246d07a7SWojciech Macek 
59246d07a7SWojciech Macek static struct resource_spec al_serdes_spec[] = {
60246d07a7SWojciech Macek 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
61246d07a7SWojciech Macek 	{ -1, 0 }
62246d07a7SWojciech Macek };
63246d07a7SWojciech Macek 
64246d07a7SWojciech Macek struct al_serdes_softc {
65246d07a7SWojciech Macek 	struct resource *res;
66246d07a7SWojciech Macek };
67246d07a7SWojciech Macek 
68246d07a7SWojciech Macek static device_method_t al_serdes_methods[] = {
69246d07a7SWojciech Macek 	DEVMETHOD(device_probe,		al_serdes_probe),
70246d07a7SWojciech Macek 	DEVMETHOD(device_attach,	al_serdes_attach),
71246d07a7SWojciech Macek 	DEVMETHOD(device_detach,	al_serdes_detach),
72246d07a7SWojciech Macek 
73246d07a7SWojciech Macek 	DEVMETHOD_END
74246d07a7SWojciech Macek };
75246d07a7SWojciech Macek 
76246d07a7SWojciech Macek static driver_t al_serdes_driver = {
77246d07a7SWojciech Macek 	"serdes",
78246d07a7SWojciech Macek 	al_serdes_methods,
79246d07a7SWojciech Macek 	sizeof(struct al_serdes_softc)
80246d07a7SWojciech Macek };
81246d07a7SWojciech Macek 
82*b596f9b8SJohn Baldwin DRIVER_MODULE(al_serdes, simplebus, al_serdes_driver, 0, 0);
83*b596f9b8SJohn Baldwin DRIVER_MODULE(al_serdes, ofwbus, al_serdes_driver, 0, 0);
84246d07a7SWojciech Macek 
85246d07a7SWojciech Macek static int
al_serdes_probe(device_t dev)86246d07a7SWojciech Macek al_serdes_probe(device_t dev)
87246d07a7SWojciech Macek {
88246d07a7SWojciech Macek 
89246d07a7SWojciech Macek 	if (!ofw_bus_status_okay(dev))
90246d07a7SWojciech Macek 		return (ENXIO);
91246d07a7SWojciech Macek 
92246d07a7SWojciech Macek 	if (!ofw_bus_is_compatible(dev, "annapurna-labs,al-serdes"))
93246d07a7SWojciech Macek 		return (ENXIO);
94246d07a7SWojciech Macek 
95246d07a7SWojciech Macek 	device_set_desc(dev, "Alpine Serdes");
96246d07a7SWojciech Macek 
97246d07a7SWojciech Macek 	return (BUS_PROBE_DEFAULT);
98246d07a7SWojciech Macek }
99246d07a7SWojciech Macek 
100246d07a7SWojciech Macek static int
al_serdes_attach(device_t dev)101246d07a7SWojciech Macek al_serdes_attach(device_t dev)
102246d07a7SWojciech Macek {
103246d07a7SWojciech Macek 	struct al_serdes_softc *sc;
104246d07a7SWojciech Macek 	int err;
105246d07a7SWojciech Macek 
106246d07a7SWojciech Macek 	sc = device_get_softc(dev);
107246d07a7SWojciech Macek 
108246d07a7SWojciech Macek 	err = bus_alloc_resources(dev, al_serdes_spec, &sc->res);
109246d07a7SWojciech Macek 	if (err != 0) {
110246d07a7SWojciech Macek 		device_printf(dev, "could not allocate resources\n");
111246d07a7SWojciech Macek 		return (err);
112246d07a7SWojciech Macek 	}
113246d07a7SWojciech Macek 
114246d07a7SWojciech Macek 	/* Initialize Serdes group locks and mode */
115246d07a7SWojciech Macek 	for (int i = 0; i < nitems(alpine_serdes_eth_group_mode); i++) {
116246d07a7SWojciech Macek 		mtx_init(&alpine_serdes_eth_group_mode[i].lock, "AlSerdesMtx",
117246d07a7SWojciech Macek 		    NULL, MTX_DEF);
118246d07a7SWojciech Macek 		alpine_serdes_eth_group_mode[i].mode_set = false;
119246d07a7SWojciech Macek 	}
120246d07a7SWojciech Macek 
121246d07a7SWojciech Macek 	serdes_base = (void *)rman_get_bushandle(sc->res);
122246d07a7SWojciech Macek 
123246d07a7SWojciech Macek 	return (0);
124246d07a7SWojciech Macek }
125246d07a7SWojciech Macek 
126246d07a7SWojciech Macek static int
al_serdes_detach(device_t dev)127246d07a7SWojciech Macek al_serdes_detach(device_t dev)
128246d07a7SWojciech Macek {
129246d07a7SWojciech Macek 	struct al_serdes_softc *sc;
130246d07a7SWojciech Macek 
131246d07a7SWojciech Macek 	sc = device_get_softc(dev);
132246d07a7SWojciech Macek 
133246d07a7SWojciech Macek 	bus_release_resources(dev, al_serdes_spec, &sc->res);
134246d07a7SWojciech Macek 
135246d07a7SWojciech Macek 	for (int i = 0; i < nitems(alpine_serdes_eth_group_mode); i++) {
136246d07a7SWojciech Macek 		mtx_destroy(&alpine_serdes_eth_group_mode[i].lock);
137246d07a7SWojciech Macek 		alpine_serdes_eth_group_mode[i].mode_set = false;
138246d07a7SWojciech Macek 	}
139246d07a7SWojciech Macek 
140246d07a7SWojciech Macek 	return (0);
141246d07a7SWojciech Macek }
142246d07a7SWojciech Macek 
143246d07a7SWojciech Macek void *
alpine_serdes_resource_get(uint32_t group)144246d07a7SWojciech Macek alpine_serdes_resource_get(uint32_t group)
145246d07a7SWojciech Macek {
146246d07a7SWojciech Macek 	void *base;
147246d07a7SWojciech Macek 
148246d07a7SWojciech Macek 	base = NULL;
149246d07a7SWojciech Macek 	if (group >= SERDES_NUM_GROUPS)
150246d07a7SWojciech Macek 		return (NULL);
151246d07a7SWojciech Macek 
152246d07a7SWojciech Macek 	if (serdes_base != NULL)
153246d07a7SWojciech Macek 		base = (void *)((uintptr_t)serdes_base +
154246d07a7SWojciech Macek 		    serdes_grp_offset[group]);
155246d07a7SWojciech Macek 
156246d07a7SWojciech Macek 	return (base);
157246d07a7SWojciech Macek }
158246d07a7SWojciech Macek 
159246d07a7SWojciech Macek int
alpine_serdes_eth_mode_set(uint32_t group,enum alpine_serdes_eth_mode mode)160246d07a7SWojciech Macek alpine_serdes_eth_mode_set(uint32_t group, enum alpine_serdes_eth_mode mode)
161246d07a7SWojciech Macek {
162246d07a7SWojciech Macek 	struct alpine_serdes_eth_group_mode *group_mode;
163246d07a7SWojciech Macek 
164246d07a7SWojciech Macek 	group_mode = &alpine_serdes_eth_group_mode[group];
165246d07a7SWojciech Macek 
166246d07a7SWojciech Macek 	if (serdes_base == NULL)
167246d07a7SWojciech Macek 		return (EINVAL);
168246d07a7SWojciech Macek 
169246d07a7SWojciech Macek 	if (group >= SERDES_NUM_GROUPS)
170246d07a7SWojciech Macek 		return (EINVAL);
171246d07a7SWojciech Macek 
172246d07a7SWojciech Macek 	mtx_lock(&group_mode->lock);
173246d07a7SWojciech Macek 
174246d07a7SWojciech Macek 	if (!group_mode->mode_set || (group_mode->mode != mode)) {
175246d07a7SWojciech Macek 		struct al_serdes_grp_obj obj;
176246d07a7SWojciech Macek 
177246d07a7SWojciech Macek 		al_serdes_handle_grp_init(alpine_serdes_resource_get(group),
178246d07a7SWojciech Macek 		    group, &obj);
179246d07a7SWojciech Macek 
180246d07a7SWojciech Macek 		if (mode == ALPINE_SERDES_ETH_MODE_SGMII)
181246d07a7SWojciech Macek 			obj.mode_set_sgmii(&obj);
182246d07a7SWojciech Macek 		else
183246d07a7SWojciech Macek 			obj.mode_set_kr(&obj);
184246d07a7SWojciech Macek 
185246d07a7SWojciech Macek 		group_mode->mode = mode;
186246d07a7SWojciech Macek 		group_mode->mode_set = true;
187246d07a7SWojciech Macek 	}
188246d07a7SWojciech Macek 
189246d07a7SWojciech Macek 	mtx_unlock(&group_mode->lock);
190246d07a7SWojciech Macek 
191246d07a7SWojciech Macek 	return (0);
192246d07a7SWojciech Macek }
193246d07a7SWojciech Macek 
194246d07a7SWojciech Macek void
alpine_serdes_eth_group_lock(uint32_t group)195246d07a7SWojciech Macek alpine_serdes_eth_group_lock(uint32_t group)
196246d07a7SWojciech Macek {
197246d07a7SWojciech Macek 	struct alpine_serdes_eth_group_mode *group_mode;
198246d07a7SWojciech Macek 
199246d07a7SWojciech Macek 	group_mode = &alpine_serdes_eth_group_mode[group];
200246d07a7SWojciech Macek 
201246d07a7SWojciech Macek 	if (mtx_initialized(&group_mode->lock) == 0)
202246d07a7SWojciech Macek 		return;
203246d07a7SWojciech Macek 
204246d07a7SWojciech Macek 	mtx_lock(&group_mode->lock);
205246d07a7SWojciech Macek }
206246d07a7SWojciech Macek 
207246d07a7SWojciech Macek void
alpine_serdes_eth_group_unlock(uint32_t group)208246d07a7SWojciech Macek alpine_serdes_eth_group_unlock(uint32_t group)
209246d07a7SWojciech Macek {
210246d07a7SWojciech Macek 	struct alpine_serdes_eth_group_mode *group_mode;
211246d07a7SWojciech Macek 
212246d07a7SWojciech Macek 	group_mode = &alpine_serdes_eth_group_mode[group];
213246d07a7SWojciech Macek 
214246d07a7SWojciech Macek 	if (mtx_initialized(&group_mode->lock) == 0)
215246d07a7SWojciech Macek 		return;
216246d07a7SWojciech Macek 
217246d07a7SWojciech Macek 	mtx_unlock(&group_mode->lock);
218246d07a7SWojciech Macek }
219