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