diff --git a/sys/arm/annapurna/alpine/alpine_serdes.c b/sys/arm/annapurna/alpine/alpine_serdes.c new file mode 100644 index 000000000000..882b01a8da4f --- /dev/null +++ b/sys/arm/annapurna/alpine/alpine_serdes.c @@ -0,0 +1,225 @@ +/*- + * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates + * All rights reserved. + * + * Developed by Semihalf. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "al_serdes.h" +#include "alpine_serdes.h" + +#define SERDES_NUM_GROUPS 5 + +static void *serdes_base; +static uint32_t serdes_grp_offset[] = {0, 0x400, 0x800, 0xc00, 0x2000}; + +static struct alpine_serdes_eth_group_mode { + struct mtx lock; + enum alpine_serdes_eth_mode mode; + bool mode_set; +} alpine_serdes_eth_group_mode[SERDES_NUM_GROUPS]; + +static int al_serdes_probe(device_t dev); +static int al_serdes_attach(device_t dev); +static int al_serdes_detach(device_t dev); + +static struct resource_spec al_serdes_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +struct al_serdes_softc { + struct resource *res; +}; + +static device_method_t al_serdes_methods[] = { + DEVMETHOD(device_probe, al_serdes_probe), + DEVMETHOD(device_attach, al_serdes_attach), + DEVMETHOD(device_detach, al_serdes_detach), + + DEVMETHOD_END +}; + +static driver_t al_serdes_driver = { + "serdes", + al_serdes_methods, + sizeof(struct al_serdes_softc) +}; + +static devclass_t al_serdes_devclass; + +DRIVER_MODULE(al_serdes, simplebus, al_serdes_driver, + al_serdes_devclass, 0, 0); +DRIVER_MODULE(al_serdes, ofwbus, al_serdes_driver, + al_serdes_devclass, 0, 0); + +static int +al_serdes_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "annapurna-labs,al-serdes")) + return (ENXIO); + + device_set_desc(dev, "Alpine Serdes"); + + return (BUS_PROBE_DEFAULT); +} + +static int +al_serdes_attach(device_t dev) +{ + struct al_serdes_softc *sc; + int err; + + sc = device_get_softc(dev); + + err = bus_alloc_resources(dev, al_serdes_spec, &sc->res); + if (err != 0) { + device_printf(dev, "could not allocate resources\n"); + return (err); + } + + /* Initialize Serdes group locks and mode */ + for (int i = 0; i < nitems(alpine_serdes_eth_group_mode); i++) { + mtx_init(&alpine_serdes_eth_group_mode[i].lock, "AlSerdesMtx", + NULL, MTX_DEF); + alpine_serdes_eth_group_mode[i].mode_set = false; + } + + serdes_base = (void *)rman_get_bushandle(sc->res); + + return (0); +} + +static int +al_serdes_detach(device_t dev) +{ + struct al_serdes_softc *sc; + + sc = device_get_softc(dev); + + bus_release_resources(dev, al_serdes_spec, &sc->res); + + for (int i = 0; i < nitems(alpine_serdes_eth_group_mode); i++) { + mtx_destroy(&alpine_serdes_eth_group_mode[i].lock); + alpine_serdes_eth_group_mode[i].mode_set = false; + } + + return (0); +} + +void * +alpine_serdes_resource_get(uint32_t group) +{ + void *base; + + base = NULL; + if (group >= SERDES_NUM_GROUPS) + return (NULL); + + if (serdes_base != NULL) + base = (void *)((uintptr_t)serdes_base + + serdes_grp_offset[group]); + + return (base); +} + +int +alpine_serdes_eth_mode_set(uint32_t group, enum alpine_serdes_eth_mode mode) +{ + struct alpine_serdes_eth_group_mode *group_mode; + + group_mode = &alpine_serdes_eth_group_mode[group]; + + if (serdes_base == NULL) + return (EINVAL); + + if (group >= SERDES_NUM_GROUPS) + return (EINVAL); + + mtx_lock(&group_mode->lock); + + if (!group_mode->mode_set || (group_mode->mode != mode)) { + struct al_serdes_grp_obj obj; + + al_serdes_handle_grp_init(alpine_serdes_resource_get(group), + group, &obj); + + if (mode == ALPINE_SERDES_ETH_MODE_SGMII) + obj.mode_set_sgmii(&obj); + else + obj.mode_set_kr(&obj); + + group_mode->mode = mode; + group_mode->mode_set = true; + } + + mtx_unlock(&group_mode->lock); + + return (0); +} + +void +alpine_serdes_eth_group_lock(uint32_t group) +{ + struct alpine_serdes_eth_group_mode *group_mode; + + group_mode = &alpine_serdes_eth_group_mode[group]; + + if (mtx_initialized(&group_mode->lock) == 0) + return; + + mtx_lock(&group_mode->lock); +} + +void +alpine_serdes_eth_group_unlock(uint32_t group) +{ + struct alpine_serdes_eth_group_mode *group_mode; + + group_mode = &alpine_serdes_eth_group_mode[group]; + + if (mtx_initialized(&group_mode->lock) == 0) + return; + + mtx_unlock(&group_mode->lock); +} diff --git a/sys/arm/annapurna/alpine/alpine_serdes.h b/sys/arm/annapurna/alpine/alpine_serdes.h new file mode 100644 index 000000000000..034bdadfb15e --- /dev/null +++ b/sys/arm/annapurna/alpine/alpine_serdes.h @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates + * All rights reserved. + * + * Developed by Semihalf. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __ALPINE_SERDES_H__ +#define __ALPINE_SERDES_H__ + +/* SerDes ETH mode */ +enum alpine_serdes_eth_mode { + ALPINE_SERDES_ETH_MODE_SGMII, + ALPINE_SERDES_ETH_MODE_KR, +}; + +/* + * Get SerDes group regs base, to be used in relevant Alpine drivers. + * Valid group is 0..3. + * Returns virtual base address of the group regs base. + */ +void *alpine_serdes_resource_get(uint32_t group); + +/* + * Set SerDes ETH mode for an entire group, unless already set + * Valid group is 0..3. + * Returns 0 upon success. + */ +int alpine_serdes_eth_mode_set(uint32_t group, + enum alpine_serdes_eth_mode mode); + +/* Lock the all serdes group for using common registers */ +void alpine_serdes_eth_group_lock(uint32_t group); + +/* Unlock the all serdes group for using common registers */ +void alpine_serdes_eth_group_unlock(uint32_t group); + +#endif /* __ALPINE_SERDES_H__ */ diff --git a/sys/boot/fdt/dts/arm/annapurna-alpine.dts b/sys/boot/fdt/dts/arm/annapurna-alpine.dts index d14899682622..0bca6059501f 100644 --- a/sys/boot/fdt/dts/arm/annapurna-alpine.dts +++ b/sys/boot/fdt/dts/arm/annapurna-alpine.dts @@ -159,6 +159,12 @@ interrupt-parent = <&MPIC>; }; + /* SerDes */ + serdes { + compatible = "annapurna-labs,al-serdes"; + reg = <0x28c0000 0x1000>; + }; + serial0: serial@2883000 { compatible = "ns16550"; reg = <0x2883000 0x20>; diff --git a/sys/conf/files.arm b/sys/conf/files.arm index c612d9b6239c..c4c0f0aa4778 100644 --- a/sys/conf/files.arm +++ b/sys/conf/files.arm @@ -15,6 +15,9 @@ arm/annapurna/alpine/alpine_ccu.c optional al_ccu fdt arm/annapurna/alpine/alpine_nb_service.c optional al_nb_service fdt arm/annapurna/alpine/alpine_pci.c optional al_pci fdt arm/annapurna/alpine/alpine_pci_msix.c optional al_pci fdt +arm/annapurna/alpine/alpine_serdes.c optional al_serdes fdt \ + no-depend \ + compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}" arm/arm/autoconf.c standard arm/arm/bcopy_page.S standard arm/arm/bcopyinout.S standard diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 index 85d3e8a2896a..2feac5041985 100644 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -42,6 +42,9 @@ arm/annapurna/alpine/alpine_ccu.c optional al_ccu fdt arm/annapurna/alpine/alpine_nb_service.c optional al_nb_service fdt arm/annapurna/alpine/alpine_pci.c optional al_pci fdt arm/annapurna/alpine/alpine_pci_msix.c optional al_pci fdt +arm/annapurna/alpine/alpine_serdes.c optional al_serdes fdt \ + no-depend \ + compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}" arm/arm/generic_timer.c standard arm/arm/gic.c standard arm/arm/gic_fdt.c optional fdt