1/*-
2 * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
3 * All rights reserved.
4 *
5 * Developed by Semihalf.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/kernel.h>
32#include <sys/module.h>
33#include <sys/bus.h>
34#include <sys/rman.h>
35#include <sys/conf.h>
36#include <sys/resource.h>
37
38#include <machine/bus.h>
39#include <dev/ofw/ofw_bus_subr.h>
40
41#include "al_serdes.h"
42#include "alpine_serdes.h"
43
44#define SERDES_NUM_GROUPS	5
45
46static void *serdes_base;
47static uint32_t serdes_grp_offset[] = {0, 0x400, 0x800, 0xc00, 0x2000};
48
49static struct alpine_serdes_eth_group_mode {
50	struct mtx			lock;
51	enum alpine_serdes_eth_mode	mode;
52	bool				mode_set;
53} alpine_serdes_eth_group_mode[SERDES_NUM_GROUPS];
54
55static int al_serdes_probe(device_t dev);
56static int al_serdes_attach(device_t dev);
57static int al_serdes_detach(device_t dev);
58
59static struct resource_spec al_serdes_spec[] = {
60	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
61	{ -1, 0 }
62};
63
64struct al_serdes_softc {
65	struct resource *res;
66};
67
68static device_method_t al_serdes_methods[] = {
69	DEVMETHOD(device_probe,		al_serdes_probe),
70	DEVMETHOD(device_attach,	al_serdes_attach),
71	DEVMETHOD(device_detach,	al_serdes_detach),
72
73	DEVMETHOD_END
74};
75
76static driver_t al_serdes_driver = {
77	"serdes",
78	al_serdes_methods,
79	sizeof(struct al_serdes_softc)
80};
81
82DRIVER_MODULE(al_serdes, simplebus, al_serdes_driver, 0, 0);
83DRIVER_MODULE(al_serdes, ofwbus, al_serdes_driver, 0, 0);
84
85static int
86al_serdes_probe(device_t dev)
87{
88
89	if (!ofw_bus_status_okay(dev))
90		return (ENXIO);
91
92	if (!ofw_bus_is_compatible(dev, "annapurna-labs,al-serdes"))
93		return (ENXIO);
94
95	device_set_desc(dev, "Alpine Serdes");
96
97	return (BUS_PROBE_DEFAULT);
98}
99
100static int
101al_serdes_attach(device_t dev)
102{
103	struct al_serdes_softc *sc;
104	int err;
105
106	sc = device_get_softc(dev);
107
108	err = bus_alloc_resources(dev, al_serdes_spec, &sc->res);
109	if (err != 0) {
110		device_printf(dev, "could not allocate resources\n");
111		return (err);
112	}
113
114	/* Initialize Serdes group locks and mode */
115	for (int i = 0; i < nitems(alpine_serdes_eth_group_mode); i++) {
116		mtx_init(&alpine_serdes_eth_group_mode[i].lock, "AlSerdesMtx",
117		    NULL, MTX_DEF);
118		alpine_serdes_eth_group_mode[i].mode_set = false;
119	}
120
121	serdes_base = (void *)rman_get_bushandle(sc->res);
122
123	return (0);
124}
125
126static int
127al_serdes_detach(device_t dev)
128{
129	struct al_serdes_softc *sc;
130
131	sc = device_get_softc(dev);
132
133	bus_release_resources(dev, al_serdes_spec, &sc->res);
134
135	for (int i = 0; i < nitems(alpine_serdes_eth_group_mode); i++) {
136		mtx_destroy(&alpine_serdes_eth_group_mode[i].lock);
137		alpine_serdes_eth_group_mode[i].mode_set = false;
138	}
139
140	return (0);
141}
142
143void *
144alpine_serdes_resource_get(uint32_t group)
145{
146	void *base;
147
148	base = NULL;
149	if (group >= SERDES_NUM_GROUPS)
150		return (NULL);
151
152	if (serdes_base != NULL)
153		base = (void *)((uintptr_t)serdes_base +
154		    serdes_grp_offset[group]);
155
156	return (base);
157}
158
159int
160alpine_serdes_eth_mode_set(uint32_t group, enum alpine_serdes_eth_mode mode)
161{
162	struct alpine_serdes_eth_group_mode *group_mode;
163
164	group_mode = &alpine_serdes_eth_group_mode[group];
165
166	if (serdes_base == NULL)
167		return (EINVAL);
168
169	if (group >= SERDES_NUM_GROUPS)
170		return (EINVAL);
171
172	mtx_lock(&group_mode->lock);
173
174	if (!group_mode->mode_set || (group_mode->mode != mode)) {
175		struct al_serdes_grp_obj obj;
176
177		al_serdes_handle_grp_init(alpine_serdes_resource_get(group),
178		    group, &obj);
179
180		if (mode == ALPINE_SERDES_ETH_MODE_SGMII)
181			obj.mode_set_sgmii(&obj);
182		else
183			obj.mode_set_kr(&obj);
184
185		group_mode->mode = mode;
186		group_mode->mode_set = true;
187	}
188
189	mtx_unlock(&group_mode->lock);
190
191	return (0);
192}
193
194void
195alpine_serdes_eth_group_lock(uint32_t group)
196{
197	struct alpine_serdes_eth_group_mode *group_mode;
198
199	group_mode = &alpine_serdes_eth_group_mode[group];
200
201	if (mtx_initialized(&group_mode->lock) == 0)
202		return;
203
204	mtx_lock(&group_mode->lock);
205}
206
207void
208alpine_serdes_eth_group_unlock(uint32_t group)
209{
210	struct alpine_serdes_eth_group_mode *group_mode;
211
212	group_mode = &alpine_serdes_eth_group_mode[group];
213
214	if (mtx_initialized(&group_mode->lock) == 0)
215		return;
216
217	mtx_unlock(&group_mode->lock);
218}
219