arm: Remove old amlogic support

Remove the port for aml8726.
Kernel config was removed in r346096 and this port was never migrated
to GENERIC.
It is also impossible to obtain such hardware nowadays.

Reviewed by:	imp
Relnotes:	yes
Differential Revision:	https://reviews.freebsd.org/D27281
This commit is contained in:
manu 2020-11-24 17:51:10 +00:00
parent 0ae786cc32
commit b307f991e8
33 changed files with 0 additions and 11357 deletions

View File

@ -1,228 +0,0 @@
/*-
* Copyright 2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*/
/*
* Amlogic aml8726 clock control module driver.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <machine/bus.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/amlogic/aml8726/aml8726_soc.h>
#include <arm/amlogic/aml8726/aml8726_ccm.h>
struct aml8726_ccm_softc {
device_t dev;
struct aml8726_ccm_function *soc;
struct resource *res[1];
struct mtx mtx;
};
static struct resource_spec aml8726_ccm_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ -1, 0 }
};
#define AML_CCM_LOCK(sc) mtx_lock(&(sc)->mtx)
#define AML_CCM_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
#define AML_CCM_LOCK_INIT(sc) \
mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
"ccm", MTX_DEF)
#define AML_CCM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx);
#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val))
#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
static int
aml8726_ccm_configure_gates(struct aml8726_ccm_softc *sc)
{
struct aml8726_ccm_function *f;
struct aml8726_ccm_gate *g;
char *function_name;
char *functions;
phandle_t node;
ssize_t len;
uint32_t value;
node = ofw_bus_get_node(sc->dev);
len = OF_getprop_alloc(node, "functions",
(void **)&functions);
if (len < 0) {
device_printf(sc->dev, "missing functions attribute in FDT\n");
return (ENXIO);
}
function_name = functions;
while (len) {
for (f = sc->soc; f->name != NULL; f++)
if (strncmp(f->name, function_name, len) == 0)
break;
if (f->name == NULL) {
/* display message prior to queuing up next string */
device_printf(sc->dev,
"unknown function attribute %.*s in FDT\n",
len, function_name);
}
/* queue up next string */
while (*function_name && len) {
function_name++;
len--;
}
if (len) {
function_name++;
len--;
}
if (f->name == NULL)
continue;
AML_CCM_LOCK(sc);
/*
* Enable the clock gates necessary for the function.
*
* In some cases a clock may be shared across functions
* (meaning don't disable a clock without ensuring that
* it's not required by someone else).
*/
for (g = f->gates; g->bits != 0x00000000; g++) {
value = CSR_READ_4(sc, g->addr);
value |= g->bits;
CSR_WRITE_4(sc, g->addr, value);
}
AML_CCM_UNLOCK(sc);
}
OF_prop_free(functions);
return (0);
}
static int
aml8726_ccm_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-ccm"))
return (ENXIO);
device_set_desc(dev, "Amlogic aml8726 ccm");
return (BUS_PROBE_DEFAULT);
}
static int
aml8726_ccm_attach(device_t dev)
{
struct aml8726_ccm_softc *sc = device_get_softc(dev);
sc->dev = dev;
switch (aml8726_soc_hw_rev) {
case AML_SOC_HW_REV_M3:
sc->soc = aml8726_m3_ccm;
break;
case AML_SOC_HW_REV_M6:
sc->soc = aml8726_m6_ccm;
break;
case AML_SOC_HW_REV_M8:
sc->soc = aml8726_m8_ccm;
break;
case AML_SOC_HW_REV_M8B:
sc->soc = aml8726_m8b_ccm;
break;
default:
device_printf(dev, "unsupported SoC\n");
return (ENXIO);
/* NOTREACHED */
}
if (bus_alloc_resources(dev, aml8726_ccm_spec, sc->res)) {
device_printf(dev, "can not allocate resources for device\n");
return (ENXIO);
}
AML_CCM_LOCK_INIT(sc);
return (aml8726_ccm_configure_gates(sc));
}
static int
aml8726_ccm_detach(device_t dev)
{
struct aml8726_ccm_softc *sc = device_get_softc(dev);
AML_CCM_LOCK_DESTROY(sc);
bus_release_resources(dev, aml8726_ccm_spec, sc->res);
return (0);
}
static device_method_t aml8726_ccm_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aml8726_ccm_probe),
DEVMETHOD(device_attach, aml8726_ccm_attach),
DEVMETHOD(device_detach, aml8726_ccm_detach),
DEVMETHOD_END
};
static driver_t aml8726_ccm_driver = {
"ccm",
aml8726_ccm_methods,
sizeof(struct aml8726_ccm_softc),
};
static devclass_t aml8726_ccm_devclass;
EARLY_DRIVER_MODULE(ccm, simplebus, aml8726_ccm_driver,
aml8726_ccm_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_LATE);

View File

@ -1,320 +0,0 @@
/*-
* Copyright 2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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 _ARM_AMLOGIC_AML8726_CCM_H
#define _ARM_AMLOGIC_AML8726_CCM_H
struct aml8726_ccm_gate {
uint32_t addr;
uint32_t bits;
};
struct aml8726_ccm_function {
const char *name;
struct aml8726_ccm_gate *gates;
};
/*
* aml8726-m3
*/
static struct aml8726_ccm_gate aml8726_m3_ethernet[] = {
{ 4, 0x00000008 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m3_i2c[] = {
{ 0, 0x00000200 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m3_rng[] = {
{ 0, 0x00001000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m3_sdio[] = {
{ 0, 0x00020000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m3_sdxc[] = {
{ 0, 0x00004000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m3_uart_a[] = {
{ 0, 0x00002000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m3_uart_b[] = {
{ 4, 0x00010000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m3_uart_c[] = {
{ 8, 0x00008000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m3_usb_a[] = {
{ 4, 0x00200000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m3_usb_b[] = {
{ 4, 0x00400000 },
{ 0, 0x00000000 }
};
struct aml8726_ccm_function aml8726_m3_ccm[] = {
{ "ethernet", aml8726_m3_ethernet },
{ "i2c", aml8726_m3_i2c },
{ "rng", aml8726_m3_rng },
{ "sdio", aml8726_m3_sdio },
{ "sdxc", aml8726_m3_sdxc },
{ "uart-a", aml8726_m3_uart_a },
{ "uart-b", aml8726_m3_uart_b },
{ "uart-c", aml8726_m3_uart_c },
{ "usb-a", aml8726_m3_usb_a },
{ "usb-b", aml8726_m3_usb_b },
{ NULL }
};
/*
* aml8726-m6
*/
static struct aml8726_ccm_gate aml8726_m6_ethernet[] = {
{ 4, 0x00000008 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m6_i2c[] = {
{ 0, 0x00000200 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m6_rng[] = {
{ 0, 0x00001000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m6_sdio[] = {
{ 0, 0x00020000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m6_sdxc[] = {
{ 0, 0x00004000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m6_uart_a[] = {
{ 0, 0x00002000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m6_uart_b[] = {
{ 4, 0x00010000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m6_uart_c[] = {
{ 8, 0x00008000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m6_usb_a[] = {
{ 4, 0x00200000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m6_usb_b[] = {
{ 4, 0x00400000 },
{ 0, 0x00000000 }
};
struct aml8726_ccm_function aml8726_m6_ccm[] = {
{ "ethernet", aml8726_m6_ethernet },
{ "i2c", aml8726_m6_i2c },
{ "rng", aml8726_m6_rng },
{ "sdio", aml8726_m6_sdio },
{ "sdxc", aml8726_m6_sdxc },
{ "uart-a", aml8726_m6_uart_a },
{ "uart-b", aml8726_m6_uart_b },
{ "uart-c", aml8726_m6_uart_c },
{ "usb-a", aml8726_m6_usb_a },
{ "usb-b", aml8726_m6_usb_b },
{ NULL }
};
/*
* aml8726-m8
*/
static struct aml8726_ccm_gate aml8726_m8_ethernet[] = {
{ 4, 0x00000008 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8_i2c[] = {
{ 0, 0x00000200 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8_rng[] = {
{ 0, 0x00001000 },
{ 16, 0x00200000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8_sdio[] = {
{ 0, 0x00020000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8_sdxc[] = {
{ 0, 0x00004000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8_uart_a[] = {
{ 0, 0x00002000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8_uart_b[] = {
{ 4, 0x00010000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8_uart_c[] = {
{ 8, 0x00008000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8_usb_a[] = {
{ 4, 0x00200000 },
{ 4, 0x04000000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8_usb_b[] = {
{ 4, 0x00400000 },
{ 4, 0x04000000 },
{ 0, 0x00000000 }
};
struct aml8726_ccm_function aml8726_m8_ccm[] = {
{ "ethernet", aml8726_m8_ethernet },
{ "i2c", aml8726_m8_i2c },
{ "rng", aml8726_m8_rng },
{ "sdio", aml8726_m8_sdio },
{ "sdxc", aml8726_m8_sdxc },
{ "uart-a", aml8726_m8_uart_a },
{ "uart-b", aml8726_m8_uart_b },
{ "uart-c", aml8726_m8_uart_c },
{ "usb-a", aml8726_m8_usb_a },
{ "usb-b", aml8726_m8_usb_b },
{ NULL }
};
/*
* aml8726-m8b
*/
static struct aml8726_ccm_gate aml8726_m8b_ethernet[] = {
{ 4, 0x00000008 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8b_i2c[] = {
{ 0, 0x00000200 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8b_rng[] = {
{ 0, 0x00001000 },
{ 16, 0x00200000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8b_sdio[] = {
{ 0, 0x00020000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8b_sdxc[] = {
{ 0, 0x00004000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8b_uart_a[] = {
{ 0, 0x00002000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8b_uart_b[] = {
{ 4, 0x00010000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8b_uart_c[] = {
{ 8, 0x00008000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8b_usb_a[] = {
{ 4, 0x00200000 },
{ 4, 0x04000000 },
{ 0, 0x00000000 }
};
static struct aml8726_ccm_gate aml8726_m8b_usb_b[] = {
{ 4, 0x00400000 },
{ 4, 0x04000000 },
{ 0, 0x00000000 }
};
struct aml8726_ccm_function aml8726_m8b_ccm[] = {
{ "ethernet", aml8726_m8b_ethernet },
{ "i2c", aml8726_m8b_i2c },
{ "rng", aml8726_m8b_rng },
{ "sdio", aml8726_m8b_sdio },
{ "sdxc", aml8726_m8b_sdxc },
{ "uart-a", aml8726_m8b_uart_a },
{ "uart-b", aml8726_m8b_uart_b },
{ "uart-c", aml8726_m8b_uart_c },
{ "usb-a", aml8726_m8b_usb_a },
{ "usb-b", aml8726_m8b_usb_b },
{ NULL }
};
#endif /* _ARM_AMLOGIC_AML8726_CCM_H */

View File

@ -1,298 +0,0 @@
/*-
* Copyright 2014-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*
*/
/*
* Amlogic aml8726 clock measurement driver.
*
* This allows various clock rates to be determine at runtime.
* The measurements are done once and are not expected to change
* (i.e. FDT fixup provides clk81 as bus-frequency to the MMC
* and UART drivers which use the value when programming the
* hardware).
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/rman.h>
#include <sys/timetc.h>
#include <sys/timeet.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <machine/fdt.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/amlogic/aml8726/aml8726_soc.h>
#include <arm/amlogic/aml8726/aml8726_clkmsr.h>
static struct aml8726_clkmsr_clk {
const char * name;
uint32_t mux;
} aml8726_clkmsr_clks[] = {
{ "clk81", 7 },
};
#define AML_CLKMSR_CLK81 0
#define AML_CLKMSR_NCLKS nitems(aml8726_clkmsr_clks)
struct aml8726_clkmsr_softc {
device_t dev;
struct resource * res[1];
};
static struct resource_spec aml8726_clkmsr_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ -1, 0 }
};
/*
* Duration can range from 1uS to 65535 uS and should be chosen
* based on the expected frequency result so to maximize resolution
* and avoid overflowing the 16 bit result counter.
*/
#define AML_CLKMSR_DURATION 32
#define AML_CLKMSR_DUTY_REG 0
#define AML_CLKMSR_0_REG 4
#define AML_CLKMSR_0_BUSY (1U << 31)
#define AML_CLKMSR_0_MUX_MASK (0x3f << 20)
#define AML_CLKMSR_0_MUX_SHIFT 20
#define AML_CLKMSR_0_MUX_EN (1 << 19)
#define AML_CLKMSR_0_MEASURE (1 << 16)
#define AML_CLKMSR_0_DURATION_MASK 0xffff
#define AML_CLKMSR_0_DURATION_SHIFT 0
#define AML_CLKMSR_1_REG 8
#define AML_CLKMSR_2_REG 12
#define AML_CLKMSR_2_RESULT_MASK 0xffff
#define AML_CLKMSR_2_RESULT_SHIFT 0
#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val))
#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
#define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \
(BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
static int
aml8726_clkmsr_clock_frequency(struct aml8726_clkmsr_softc *sc, unsigned clock)
{
uint32_t value;
if (clock >= AML_CLKMSR_NCLKS)
return (0);
/*
* Locking is not used as this is only expected to be called from
* FDT fixup (which occurs prior to driver initialization) or attach.
*/
CSR_WRITE_4(sc, AML_CLKMSR_0_REG, 0);
CSR_BARRIER(sc, AML_CLKMSR_0_REG);
value = (aml8726_clkmsr_clks[clock].mux << AML_CLKMSR_0_MUX_SHIFT)
| ((AML_CLKMSR_DURATION - 1) << AML_CLKMSR_0_DURATION_SHIFT)
| AML_CLKMSR_0_MUX_EN
| AML_CLKMSR_0_MEASURE;
CSR_WRITE_4(sc, AML_CLKMSR_0_REG, value);
CSR_BARRIER(sc, AML_CLKMSR_0_REG);
while ((CSR_READ_4(sc, AML_CLKMSR_0_REG) & AML_CLKMSR_0_BUSY) != 0)
cpu_spinwait();
value &= ~AML_CLKMSR_0_MEASURE;
CSR_WRITE_4(sc, AML_CLKMSR_0_REG, value);
CSR_BARRIER(sc, AML_CLKMSR_0_REG);
value = (((CSR_READ_4(sc, AML_CLKMSR_2_REG) & AML_CLKMSR_2_RESULT_MASK)
>> AML_CLKMSR_2_RESULT_SHIFT) + AML_CLKMSR_DURATION / 2) /
AML_CLKMSR_DURATION;
return value;
}
static void
aml8726_clkmsr_fixup_clk81(struct aml8726_clkmsr_softc *sc, int freq)
{
pcell_t prop;
ssize_t len;
phandle_t clk_node;
phandle_t node;
node = ofw_bus_get_node(sc->dev);
len = OF_getencprop(node, "clocks", &prop, sizeof(prop));
if ((len / sizeof(prop)) != 1 || prop == 0 ||
(clk_node = OF_node_from_xref(prop)) == 0)
return;
len = OF_getencprop(clk_node, "clock-frequency", &prop, sizeof(prop));
if ((len / sizeof(prop)) != 1 || prop != 0)
return;
freq = cpu_to_fdt32(freq);
OF_setprop(clk_node, "clock-frequency", (void *)&freq, sizeof(freq));
}
static int
aml8726_clkmsr_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-clkmsr"))
return (ENXIO);
device_set_desc(dev, "Amlogic aml8726 clkmsr");
return (BUS_PROBE_DEFAULT);
}
static int
aml8726_clkmsr_attach(device_t dev)
{
struct aml8726_clkmsr_softc *sc = device_get_softc(dev);
int freq;
sc->dev = dev;
if (bus_alloc_resources(dev, aml8726_clkmsr_spec, sc->res)) {
device_printf(dev, "can not allocate resources for device\n");
return (ENXIO);
}
freq = aml8726_clkmsr_clock_frequency(sc, AML_CLKMSR_CLK81);
device_printf(sc->dev, "bus clock %u MHz\n", freq);
aml8726_clkmsr_fixup_clk81(sc, freq * 1000000);
return (0);
}
static int
aml8726_clkmsr_detach(device_t dev)
{
struct aml8726_clkmsr_softc *sc = device_get_softc(dev);
bus_release_resources(dev, aml8726_clkmsr_spec, sc->res);
return (0);
}
static device_method_t aml8726_clkmsr_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aml8726_clkmsr_probe),
DEVMETHOD(device_attach, aml8726_clkmsr_attach),
DEVMETHOD(device_detach, aml8726_clkmsr_detach),
DEVMETHOD_END
};
static driver_t aml8726_clkmsr_driver = {
"clkmsr",
aml8726_clkmsr_methods,
sizeof(struct aml8726_clkmsr_softc),
};
static devclass_t aml8726_clkmsr_devclass;
EARLY_DRIVER_MODULE(clkmsr, simplebus, aml8726_clkmsr_driver,
aml8726_clkmsr_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_EARLY);
int
aml8726_clkmsr_bus_frequency()
{
struct resource mem;
struct aml8726_clkmsr_softc sc;
phandle_t node;
u_long pbase, psize;
u_long start, size;
int freq;
KASSERT(aml8726_soc_hw_rev != AML_SOC_HW_REV_UNKNOWN,
("aml8726_soc_hw_rev isn't initialized"));
/*
* Try to access the clkmsr node directly i.e. through /aliases/.
*/
if ((node = OF_finddevice("clkmsr")) != -1)
if (fdt_is_compatible_strict(node, "amlogic,aml8726-clkmsr"))
goto moveon;
/*
* Find the node the long way.
*/
if ((node = OF_finddevice("/soc")) == -1)
return (0);
if ((node = fdt_find_compatible(node,
"amlogic,aml8726-clkmsr", 1)) == 0)
return (0);
moveon:
if (fdt_get_range(OF_parent(node), 0, &pbase, &psize) != 0
|| fdt_regsize(node, &start, &size) != 0)
return (0);
start += pbase;
memset(&mem, 0, sizeof(mem));
mem.r_bustag = fdtbus_bs_tag;
if (bus_space_map(mem.r_bustag, start, size, 0, &mem.r_bushandle) != 0)
return (0);
/*
* Build an incomplete (however sufficient for the purpose
* of calling aml8726_clkmsr_clock_frequency) softc.
*/
memset(&sc, 0, sizeof(sc));
sc.res[0] = &mem;
freq = aml8726_clkmsr_clock_frequency(&sc, AML_CLKMSR_CLK81) * 1000000;
bus_space_unmap(mem.r_bustag, mem.r_bushandle, size);
return (freq);
}

View File

@ -1,34 +0,0 @@
/*-
* Copyright 2014 John Wehle <john@feith.com>
* All rights reserved.
*
* 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 _ARM_AMLOGIC_AML8726_CLKMSR_H
#define _ARM_AMLOGIC_AML8726_CLKMSR_H
int aml8726_clkmsr_bus_frequency(void);
#endif /* _ARM_AMLOGIC_AML8726_CLKMSR_H */

View File

@ -1,469 +0,0 @@
/*-
* Copyright 2013-2014 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*/
/*
* Amlogic aml8726 frame buffer driver.
*
* The current implementation has limited flexibility.
* For example only progressive scan is supported when
* using HDMI and the resolution / frame rate is not
* negotiated.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <sys/fbio.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <machine/fdt.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/fb/fbreg.h>
#include <dev/vt/vt.h>
#include <arm/amlogic/aml8726/aml8726_fb.h>
#include "fb_if.h"
enum aml8726_fb_output {
aml8726_unknown_fb_output,
aml8726_cvbs_fb_output,
aml8726_hdmi_fb_output,
aml8726_lcd_fb_output
};
struct aml8726_fb_clk {
uint32_t freq;
uint32_t video_pre;
uint32_t video_post;
uint32_t video_x;
uint32_t hdmi_tx;
uint32_t encp;
uint32_t enci;
uint32_t enct;
uint32_t encl;
uint32_t vdac0;
uint32_t vdac1;
};
struct aml8726_fb_softc {
device_t dev;
struct resource *res[4];
struct mtx mtx;
void *ih_cookie;
struct fb_info info;
enum aml8726_fb_output output;
struct aml8726_fb_clk clk;
};
static struct resource_spec aml8726_fb_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE }, /* CANVAS */
{ SYS_RES_MEMORY, 1, RF_ACTIVE }, /* VIU */
{ SYS_RES_MEMORY, 2, RF_ACTIVE }, /* VPP */
{ SYS_RES_IRQ, 1, RF_ACTIVE }, /* INT_VIU_VSYNC */
{ -1, 0 }
};
#define AML_FB_LOCK(sc) mtx_lock(&(sc)->mtx)
#define AML_FB_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
#define AML_FB_LOCK_INIT(sc) \
mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
"fb", MTX_DEF)
#define AML_FB_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx);
#define CAV_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val))
#define CAV_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
#define CAV_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \
(BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
#define VIU_WRITE_4(sc, reg, val) bus_write_4((sc)->res[1], reg, (val))
#define VIU_READ_4(sc, reg) bus_read_4((sc)->res[1], reg)
#define VPP_WRITE_4(sc, reg, val) bus_write_4((sc)->res[2], reg, (val))
#define VPP_READ_4(sc, reg) bus_read_4((sc)->res[2], reg)
#define CLK_WRITE_4(sc, reg, val) bus_write_4((sc)->res[X], reg, (val))
#define CLK_READ_4(sc, reg) bus_read_4((sc)->res[X], reg)
#define AML_FB_CLK_FREQ_SD 1080
#define AML_FB_CLK_FREQ_HD 1488
static void
aml8726_fb_cfg_output(struct aml8726_fb_softc *sc)
{
/* XXX */
}
static void
aml8726_fb_cfg_video(struct aml8726_fb_softc *sc)
{
uint32_t value;
/*
* basic initialization
*
* The fifo depth is in units of 8 so programming 32
* sets the depth to 256.
*/
value = (32 << AML_VIU_OSD_FIFO_CTRL_DEPTH_SHIFT);
value |= AML_VIU_OSD_FIFO_CTRL_BURST_LEN_64;
value |= (4 << AML_VIU_OSD_FIFO_CTRL_HOLD_LINES_SHIFT);
VIU_WRITE_4(sc, AML_VIU_OSD1_FIFO_CTRL_REG, value);
VIU_WRITE_4(sc, AML_VIU_OSD2_FIFO_CTRL_REG, value);
value = VPP_READ_4(sc, AML_VPP_MISC_REG);
value &= ~AML_VPP_MISC_PREBLEND_EN;
value |= AML_VPP_MISC_POSTBLEND_EN;
value &= ~(AML_VPP_MISC_OSD1_POSTBLEND | AML_VPP_MISC_OSD2_POSTBLEND
| AML_VPP_MISC_VD1_POSTBLEND | AML_VPP_MISC_VD2_POSTBLEND);
VPP_WRITE_4(sc, AML_VPP_MISC_REG, value);
value = AML_VIU_OSD_CTRL_OSD_EN;
value |= (0xff << AML_VIU_OSD_CTRL_GLOBAL_ALPHA_SHIFT);
VIU_WRITE_4(sc, AML_VIU_OSD1_CTRL_REG, value);
VIU_WRITE_4(sc, AML_VIU_OSD2_CTRL_REG, value);
/* color mode for OSD1 block 0 */
value = (AML_CAV_OSD1_INDEX << AML_VIU_OSD_BLK_CFG_W0_INDEX_SHIFT)
| AML_VIU_OSD_BLK_CFG_W0_LITTLE_ENDIAN
| AML_VIU_OSD_BLK_CFG_W0_BLKMODE_24
| AML_VIU_OSD_BLK_CFG_W0_RGB_EN
| AML_VIU_OSD_BLK_CFG_W0_CMATRIX_RGB;
VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W0_REG, value);
/* geometry / scaling for OSD1 block 0 */
value = ((sc->info.fb_width - 1) << AML_VIU_OSD_BLK_CFG_W1_X_END_SHIFT)
& AML_VIU_OSD_BLK_CFG_W1_X_END_MASK;
value |= (0 << AML_VIU_OSD_BLK_CFG_W1_X_START_SHIFT)
& AML_VIU_OSD_BLK_CFG_W1_X_START_MASK;
VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W1_REG, value);
value = ((sc->info.fb_height - 1) << AML_VIU_OSD_BLK_CFG_W2_Y_END_SHIFT)
& AML_VIU_OSD_BLK_CFG_W2_Y_END_MASK;
value |= (0 << AML_VIU_OSD_BLK_CFG_W2_Y_START_SHIFT)
& AML_VIU_OSD_BLK_CFG_W2_Y_START_MASK;
VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W2_REG, value);
value = ((sc->info.fb_width - 1) << AML_VIU_OSD_BLK_CFG_W3_H_END_SHIFT)
& AML_VIU_OSD_BLK_CFG_W3_H_END_MASK;
value |= (0 << AML_VIU_OSD_BLK_CFG_W3_H_START_SHIFT)
& AML_VIU_OSD_BLK_CFG_W3_H_START_MASK;
VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W3_REG, value);
value = ((sc->info.fb_height - 1) << AML_VIU_OSD_BLK_CFG_W4_V_END_SHIFT)
& AML_VIU_OSD_BLK_CFG_W4_V_END_MASK;
value |= (0 << AML_VIU_OSD_BLK_CFG_W4_V_START_SHIFT)
& AML_VIU_OSD_BLK_CFG_W4_V_START_MASK;
VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W4_REG, value);
/* Enable the OSD block now that it's fully configured */
value = VIU_READ_4(sc, AML_VIU_OSD1_CTRL_REG);
value &= ~AML_VIU_OSD_CTRL_OSD_BLK_EN_MASK;
value |= 1 << AML_VIU_OSD_CTRL_OSD_BLK_EN_SHIFT;
VIU_WRITE_4(sc, AML_VIU_OSD1_CTRL_REG, value);
/* enable video processing of OSD1 */
value = VPP_READ_4(sc, AML_VPP_MISC_REG);
value |= AML_VPP_MISC_OSD1_POSTBLEND;
VPP_WRITE_4(sc, AML_VPP_MISC_REG, value);
}
static void
aml8726_fb_cfg_canvas(struct aml8726_fb_softc *sc)
{
uint32_t value;
uint32_t width;
/*
* The frame buffer address and width are programmed in units of 8
* (meaning they need to be aligned and the actual values divided
* by 8 prior to programming the hardware).
*/
width = (uint32_t)sc->info.fb_stride / 8;
/* lower bits of the width */
value = (width << AML_CAV_LUT_DATAL_WIDTH_SHIFT) &
AML_CAV_LUT_DATAL_WIDTH_MASK;
/* physical address */
value |= (uint32_t)sc->info.fb_pbase / 8;
CAV_WRITE_4(sc, AML_CAV_LUT_DATAL_REG, value);
/* upper bits of the width */
value = ((width >> AML_CAV_LUT_DATAL_WIDTH_WIDTH) <<
AML_CAV_LUT_DATAH_WIDTH_SHIFT) & AML_CAV_LUT_DATAH_WIDTH_MASK;
/* height */
value |= ((uint32_t)sc->info.fb_height <<
AML_CAV_LUT_DATAH_HEIGHT_SHIFT) & AML_CAV_LUT_DATAH_HEIGHT_MASK;
/* mode */
value |= AML_CAV_LUT_DATAH_BLKMODE_LINEAR;
CAV_WRITE_4(sc, AML_CAV_LUT_DATAH_REG, value);
CAV_WRITE_4(sc, AML_CAV_LUT_ADDR_REG, (AML_CAV_LUT_ADDR_WR_EN |
(AML_CAV_OSD1_INDEX << AML_CAV_LUT_ADDR_INDEX_SHIFT)));
CAV_BARRIER(sc, AML_CAV_LUT_ADDR_REG);
}
static void
aml8726_fb_intr(void *arg)
{
struct aml8726_fb_softc *sc = (struct aml8726_fb_softc *)arg;
AML_FB_LOCK(sc);
AML_FB_UNLOCK(sc);
}
static int
aml8726_fb_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-fb"))
return (ENXIO);
device_set_desc(dev, "Amlogic aml8726 FB");
return (BUS_PROBE_DEFAULT);
}
static int
aml8726_fb_attach(device_t dev)
{
struct aml8726_fb_softc *sc = device_get_softc(dev);
int error;
device_t child;
pcell_t prop;
phandle_t node;
sc->dev = dev;
sc->info.fb_name = device_get_nameunit(sc->dev);
node = ofw_bus_get_node(dev);
if (OF_getencprop(node, "width", &prop, sizeof(prop)) <= 0) {
device_printf(dev, "missing width attribute in FDT\n");
return (ENXIO);
}
if ((prop % 8) != 0) {
device_printf(dev,
"width attribute in FDT must be a multiple of 8\n");
return (ENXIO);
}
sc->info.fb_width = prop;
if (OF_getencprop(node, "height", &prop, sizeof(prop)) <= 0) {
device_printf(dev, "missing height attribute in FDT\n");
return (ENXIO);
}
sc->info.fb_height = prop;
if (OF_getencprop(node, "depth", &prop, sizeof(prop)) <= 0) {
device_printf(dev, "missing depth attribute in FDT\n");
return (ENXIO);
}
if (prop != 24) {
device_printf(dev,
"depth attribute in FDT is an unsupported value\n");
return (ENXIO);
}
sc->info.fb_depth = prop;
sc->info.fb_bpp = prop;
if (OF_getencprop(node, "linebytes", &prop, sizeof(prop)) <= 0) {
device_printf(dev, "missing linebytes attribute in FDT\n");
return (ENXIO);
}
if ((prop % 8) != 0) {
device_printf(dev,
"linebytes attribute in FDT must be a multiple of 8\n");
return (ENXIO);
}
if (prop < (sc->info.fb_width * 3)) {
device_printf(dev,
"linebytes attribute in FDT is too small\n");
return (ENXIO);
}
sc->info.fb_stride = prop;
if (OF_getencprop(node, "address", &prop, sizeof(prop)) <= 0) {
device_printf(dev, "missing address attribute in FDT\n");
return (ENXIO);
}
if ((prop % 8) != 0) {
device_printf(dev,
"address attribute in FDT must be a multiple of 8\n");
return (ENXIO);
}
sc->info.fb_pbase = prop;
sc->info.fb_size = sc->info.fb_height * sc->info.fb_stride;
sc->info.fb_vbase = (intptr_t)pmap_mapdev(sc->info.fb_pbase,
sc->info.fb_size);
if (bus_alloc_resources(dev, aml8726_fb_spec, sc->res)) {
device_printf(dev, "could not allocate resources for device\n");
pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size);
return (ENXIO);
}
aml8726_fb_cfg_output(sc);
aml8726_fb_cfg_video(sc);
aml8726_fb_cfg_canvas(sc);
AML_FB_LOCK_INIT(sc);
error = bus_setup_intr(dev, sc->res[3], INTR_TYPE_MISC | INTR_MPSAFE,
NULL, aml8726_fb_intr, sc, &sc->ih_cookie);
if (error) {
device_printf(dev, "could not setup interrupt handler\n");
goto fail;
}
child = device_add_child(dev, "fbd", device_get_unit(dev));
if (!child) {
device_printf(dev, "could not add fbd\n");
error = ENXIO;
goto fail;
}
error = device_probe_and_attach(child);
if (error) {
device_printf(dev, "could not attach fbd\n");
goto fail;
}
return (0);
fail:
if (sc->ih_cookie)
bus_teardown_intr(dev, sc->res[3], sc->ih_cookie);
AML_FB_LOCK_DESTROY(sc);
bus_release_resources(dev, aml8726_fb_spec, sc->res);
pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size);
return (error);
}
static int
aml8726_fb_detach(device_t dev)
{
struct aml8726_fb_softc *sc = device_get_softc(dev);
bus_generic_detach(dev);
bus_teardown_intr(dev, sc->res[3], sc->ih_cookie);
AML_FB_LOCK_DESTROY(sc);
bus_release_resources(dev, aml8726_fb_spec, sc->res);
pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size);
return (0);
}
static struct fb_info *
aml8726_fb_getinfo(device_t dev)
{
struct aml8726_fb_softc *sc = device_get_softc(dev);
return (&sc->info);
}
static device_method_t aml8726_fb_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aml8726_fb_probe),
DEVMETHOD(device_attach, aml8726_fb_attach),
DEVMETHOD(device_detach, aml8726_fb_detach),
/* FB interface */
DEVMETHOD(fb_getinfo, aml8726_fb_getinfo),
DEVMETHOD_END
};
static driver_t aml8726_fb_driver = {
"fb",
aml8726_fb_methods,
sizeof(struct aml8726_fb_softc),
};
static devclass_t aml8726_fb_devclass;
DRIVER_MODULE(fb, simplebus, aml8726_fb_driver, aml8726_fb_devclass, 0, 0);

View File

@ -1,162 +0,0 @@
/*-
* Copyright 2013-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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 _ARM_AMLOGIC_AML8726_FB_H
#define _ARM_AMLOGIC_AML8726_FB_H
#define AML_CAV_OSD1_INDEX 0x40
#define AML_CAV_LUT_DATAL_REG 0
#define AML_CAV_LUT_DATAL_WIDTH_MASK (7 << 29)
#define AML_CAV_LUT_DATAL_WIDTH_SHIFT 29
#define AML_CAV_LUT_DATAL_WIDTH_WIDTH 3
#define AML_CAV_LUT_DATAL_ADDR_MASK 0x1fffffff
#define AML_CAV_LUT_DATAL_ADDR_SHIFT 0
#define AML_CAV_LUT_DATAH_REG 4
#define AML_CAV_LUT_DATAH_BLKMODE_MASK (3 << 24)
#define AML_CAV_LUT_DATAH_BLKMODE_SHIFT 24
#define AML_CAV_LUT_DATAH_BLKMODE_LINEAR (0 << 24)
#define AML_CAV_LUT_DATAH_BLKMODE_32x32 (1 << 24)
#define AML_CAV_LUT_DATAH_BLKMODE_64x32 (2 << 24)
#define AML_CAV_LUT_DATAH_WRAP_X (1 << 23)
#define AML_CAV_LUT_DATAH_WRAP_Y (1 << 22)
#define AML_CAV_LUT_DATAH_HEIGHT_MASK (0x1fff << 9)
#define AML_CAV_LUT_DATAH_HEIGHT_SHIFT 9
#define AML_CAV_LUT_DATAH_WIDTH_MASK 0x1ff
#define AML_CAV_LUT_DATAH_WIDTH_SHIFT 0
#define AML_CAV_LUT_ADDR_REG 8
#define AML_CAV_LUT_ADDR_WR_EN (1 << 9)
#define AML_CAV_LUT_ADDR_RD_EN (1 << 8)
#define AML_CAV_LUT_ADDR_INDEX_MASK 0xff
#define AML_CAV_LUT_ADDR_INDEX_SHIFT 0
#define AML_VIU_OSD1_CTRL_REG 64
#define AML_VIU_OSD_CTRL_OSD_EN (1 << 21)
#define AML_VIU_OSD_CTRL_GLOBAL_ALPHA_MASK (0x1ff << 12)
#define AML_VIU_OSD_CTRL_GLOBAL_ALPHA_SHIFT 12
#define AML_VIU_OSD_CTRL_OSD_BLK_EN_MASK (0xf << 0)
#define AML_VIU_OSD_CTRL_OSD_BLK_EN_SHIFT 0
#define AML_VIU_OSD1_BLK0_CFG_W0_REG 108
#define AML_VIU_OSD1_BLK1_CFG_W0_REG 124
#define AML_VIU_OSD1_BLK2_CFG_W0_REG 140
#define AML_VIU_OSD1_BLK3_CFG_W0_REG 156
#define AML_VIU_OSD_BLK_CFG_W0_INDEX_MASK (0xff << 16)
#define AML_VIU_OSD_BLK_CFG_W0_INDEX_SHIFT 16
#define AML_VIU_OSD_BLK_CFG_W0_LITTLE_ENDIAN (1 << 15)
#define AML_VIU_OSD_BLK_CFG_W0_BLKMODE_24 (7 << 8)
#define AML_VIU_OSD_BLK_CFG_W0_RGB_EN (1 << 7)
#define AML_VIU_OSD_BLK_CFG_W0_CMATRIX_RGB (0 << 2)
#define AML_VIU_OSD1_BLK0_CFG_W1_REG 112
#define AML_VIU_OSD1_BLK1_CFG_W1_REG 128
#define AML_VIU_OSD1_BLK2_CFG_W1_REG 144
#define AML_VIU_OSD1_BLK3_CFG_W1_REG 160
#define AML_VIU_OSD_BLK_CFG_W1_X_END_MASK (0x1fff << 16)
#define AML_VIU_OSD_BLK_CFG_W1_X_END_SHIFT 16
#define AML_VIU_OSD_BLK_CFG_W1_X_START_MASK 0x1fff
#define AML_VIU_OSD_BLK_CFG_W1_X_START_SHIFT 0
#define AML_VIU_OSD1_BLK0_CFG_W2_REG 116
#define AML_VIU_OSD1_BLK1_CFG_W2_REG 132
#define AML_VIU_OSD1_BLK2_CFG_W2_REG 148
#define AML_VIU_OSD1_BLK3_CFG_W2_REG 164
#define AML_VIU_OSD_BLK_CFG_W2_Y_END_MASK (0x1fff << 16)
#define AML_VIU_OSD_BLK_CFG_W2_Y_END_SHIFT 16
#define AML_VIU_OSD_BLK_CFG_W2_Y_START_MASK 0x1fff
#define AML_VIU_OSD_BLK_CFG_W2_Y_START_SHIFT 0
#define AML_VIU_OSD1_BLK0_CFG_W3_REG 120
#define AML_VIU_OSD1_BLK1_CFG_W3_REG 136
#define AML_VIU_OSD1_BLK2_CFG_W3_REG 152
#define AML_VIU_OSD1_BLK3_CFG_W3_REG 168
#define AML_VIU_OSD_BLK_CFG_W3_H_END_MASK (0xfff << 16)
#define AML_VIU_OSD_BLK_CFG_W3_H_END_SHIFT 16
#define AML_VIU_OSD_BLK_CFG_W3_H_START_MASK 0xfff
#define AML_VIU_OSD_BLK_CFG_W3_H_START_SHIFT 0
#define AML_VIU_OSD1_BLK0_CFG_W4_REG 76
#define AML_VIU_OSD1_BLK1_CFG_W4_REG 80
#define AML_VIU_OSD1_BLK2_CFG_W4_REG 84
#define AML_VIU_OSD1_BLK3_CFG_W4_REG 88
#define AML_VIU_OSD_BLK_CFG_W4_V_END_MASK (0xfff << 16)
#define AML_VIU_OSD_BLK_CFG_W4_V_END_SHIFT 16
#define AML_VIU_OSD_BLK_CFG_W4_V_START_MASK 0xfff
#define AML_VIU_OSD_BLK_CFG_W4_V_START_SHIFT 0
#define AML_VIU_OSD1_FIFO_CTRL_REG 172
#define AML_VIU_OSD_FIFO_CTRL_DEPTH_MASK (0x3f << 12)
#define AML_VIU_OSD_FIFO_CTRL_DEPTH_SHIFT 12
#define AML_VIU_OSD_FIFO_CTRL_BURST_LEN_MASK (3 << 10)
#define AML_VIU_OSD_FIFO_CTRL_BURST_LEN_24 (0 << 10)
#define AML_VIU_OSD_FIFO_CTRL_BURST_LEN_32 (1 << 10)
#define AML_VIU_OSD_FIFO_CTRL_BURST_LEN_48 (2 << 10)
#define AML_VIU_OSD_FIFO_CTRL_BURST_LEN_64 (3 << 10)
#define AML_VIU_OSD_FIFO_CTRL_HOLD_LINES_MASK (0x1f << 5)
#define AML_VIU_OSD_FIFO_CTRL_HOLD_LINES_SHIFT 5
#define AML_VIU_OSD_FIFO_CTRL_URGENT (1 << 0)
/* OSD2 field meanings are the same as OSD1 */
#define AML_VIU_OSD2_CTRL_REG 192
#define AML_VIU_OSD2_BLK0_CFG_W0_REG 236
#define AML_VIU_OSD2_BLK1_CFG_W0_REG 252
#define AML_VIU_OSD2_BLK2_CFG_W0_REG 268
#define AML_VIU_OSD2_BLK3_CFG_W0_REG 284
#define AML_VIU_OSD2_BLK0_CFG_W1_REG 240
#define AML_VIU_OSD2_BLK1_CFG_W1_REG 256
#define AML_VIU_OSD2_BLK2_CFG_W1_REG 272
#define AML_VIU_OSD2_BLK3_CFG_W1_REG 288
#define AML_VIU_OSD2_BLK0_CFG_W2_REG 244
#define AML_VIU_OSD2_BLK1_CFG_W2_REG 260
#define AML_VIU_OSD2_BLK2_CFG_W2_REG 276
#define AML_VIU_OSD2_BLK3_CFG_W2_REG 292
#define AML_VIU_OSD2_BLK0_CFG_W3_REG 248
#define AML_VIU_OSD2_BLK1_CFG_W3_REG 264
#define AML_VIU_OSD2_BLK2_CFG_W3_REG 280
#define AML_VIU_OSD2_BLK3_CFG_W3_REG 296
#define AML_VIU_OSD2_BLK0_CFG_W4_REG 400
#define AML_VIU_OSD2_BLK1_CFG_W4_REG 404
#define AML_VIU_OSD2_BLK2_CFG_W4_REG 408
#define AML_VIU_OSD2_BLK3_CFG_W4_REG 412
#define AML_VIU_OSD2_FIFO_CTRL_REG 300
#define AML_VPP_MISC_REG 152
#define AML_VPP_MISC_OSD2_PREBLEND (1 << 17)
#define AML_VPP_MISC_OSD1_PREBLEND (1 << 16)
#define AML_VPP_MISC_VD2_PREBLEND (1 << 15)
#define AML_VPP_MISC_VD1_PREBLEND (1 << 14)
#define AML_VPP_MISC_OSD2_POSTBLEND (1 << 13)
#define AML_VPP_MISC_OSD1_POSTBLEND (1 << 12)
#define AML_VPP_MISC_VD2_POSTBLEND (1 << 11)
#define AML_VPP_MISC_VD1_POSTBLEND (1 << 10)
#define AML_VPP_MISC_POSTBLEND_EN (1 << 7)
#define AML_VPP_MISC_PREBLEND_EN (1 << 6)
#endif /* _ARM_AMLOGIC_AML8726_FB_H */

View File

@ -1,370 +0,0 @@
/*-
* Copyright 2013-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*/
/*
* Amlogic aml8726 GPIO driver.
*
* Note: The OEN register is active * low *. Setting a bit to zero
* enables the output driver, setting a bit to one disables the driver.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <sys/gpio.h>
#include <machine/bus.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/gpio/gpiobusvar.h>
#include "gpio_if.h"
struct aml8726_gpio_softc {
device_t dev;
struct resource *res[3];
struct mtx mtx;
uint32_t npins;
device_t busdev;
};
static struct resource_spec aml8726_gpio_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE }, /* oen */
{ SYS_RES_MEMORY, 1, RF_ACTIVE | RF_SHAREABLE }, /* output */
{ SYS_RES_MEMORY, 2, RF_ACTIVE }, /* input */
{ -1, 0 }
};
#define AML_GPIO_LOCK(sc) mtx_lock(&(sc)->mtx)
#define AML_GPIO_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
#define AML_GPIO_LOCK_INIT(sc) \
mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
"gpio", MTX_DEF)
#define AML_GPIO_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx);
#define AML_GPIO_OE_N_REG 0
#define AML_GPIO_OUT_REG 1
#define AML_GPIO_IN_REG 2
#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[reg], 0, (val))
#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[reg], 0)
static int
aml8726_gpio_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-gpio"))
return (ENXIO);
device_set_desc(dev, "Amlogic aml8726 GPIO");
return (BUS_PROBE_DEFAULT);
}
static int
aml8726_gpio_attach(device_t dev)
{
struct aml8726_gpio_softc *sc = device_get_softc(dev);
phandle_t node;
pcell_t prop;
sc->dev = dev;
node = ofw_bus_get_node(dev);
if (OF_getencprop(node, "pin-count",
&prop, sizeof(prop)) <= 0) {
device_printf(dev, "missing pin-count attribute in FDT\n");
return (ENXIO);
}
sc->npins = prop;
if (sc->npins > 32)
return (ENXIO);
if (bus_alloc_resources(dev, aml8726_gpio_spec, sc->res)) {
device_printf(dev, "can not allocate resources for device\n");
return (ENXIO);
}
/*
* The GPIOAO OUT bits occupy the upper word of the OEN register.
*/
if (rman_get_start(sc->res[1]) == rman_get_start(sc->res[0]))
if (sc->npins > 16) {
device_printf(dev,
"too many pins for overlapping OEN and OUT\n");
bus_release_resources(dev, aml8726_gpio_spec, sc->res);
return (ENXIO);
}
AML_GPIO_LOCK_INIT(sc);
sc->busdev = gpiobus_attach_bus(dev);
if (sc->busdev == NULL) {
AML_GPIO_LOCK_DESTROY(sc);
bus_release_resources(dev, aml8726_gpio_spec, sc->res);
return (ENXIO);
}
return (0);
}
static int
aml8726_gpio_detach(device_t dev)
{
struct aml8726_gpio_softc *sc = device_get_softc(dev);
gpiobus_detach_bus(dev);
AML_GPIO_LOCK_DESTROY(sc);
bus_release_resources(dev, aml8726_gpio_spec, sc->res);
return (0);
}
static device_t
aml8726_gpio_get_bus(device_t dev)
{
struct aml8726_gpio_softc *sc = device_get_softc(dev);
return (sc->busdev);
}
static int
aml8726_gpio_pin_max(device_t dev, int *maxpin)
{
struct aml8726_gpio_softc *sc = device_get_softc(dev);
*maxpin = (int)sc->npins;
return (0);
}
/* Get a specific pin's capabilities. */
static int
aml8726_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
{
struct aml8726_gpio_softc *sc = device_get_softc(dev);
if (pin >= sc->npins)
return (EINVAL);
*caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
return (0);
}
/* Get a specific pin's name. */
static int
aml8726_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
{
struct aml8726_gpio_softc *sc = device_get_softc(dev);
if (pin >= sc->npins)
return (EINVAL);
snprintf(name, GPIOMAXNAME, "%s.%u", ofw_bus_get_name(dev), pin);
return (0);
}
/* Get a specific pin's current in/out state. */
static int
aml8726_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
{
struct aml8726_gpio_softc *sc = device_get_softc(dev);
uint32_t mask = 1U << pin;
if (pin >= sc->npins)
return (EINVAL);
if ((CSR_READ_4(sc, AML_GPIO_OE_N_REG) & mask) == 0) {
/* output */
*flags = GPIO_PIN_OUTPUT;
} else
/* input */
*flags = GPIO_PIN_INPUT;
return (0);
}
/* Set a specific pin's in/out state. */
static int
aml8726_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
{
struct aml8726_gpio_softc *sc = device_get_softc(dev);
uint32_t mask = 1U << pin;
if (pin >= sc->npins)
return (EINVAL);
AML_GPIO_LOCK(sc);
if ((flags & GPIO_PIN_OUTPUT) != 0) {
/* Output. Turn on driver. */
CSR_WRITE_4(sc, AML_GPIO_OE_N_REG,
(CSR_READ_4(sc, AML_GPIO_OE_N_REG) & ~mask));
} else {
/* Input. Turn off driver. */
CSR_WRITE_4(sc, AML_GPIO_OE_N_REG,
(CSR_READ_4(sc, AML_GPIO_OE_N_REG) | mask));
}
AML_GPIO_UNLOCK(sc);
return (0);
}
/* Set a specific output pin's value. */
static int
aml8726_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
{
struct aml8726_gpio_softc *sc = device_get_softc(dev);
uint32_t mask;
if (pin >= sc->npins || value > 1)
return (EINVAL);
/*
* The GPIOAO OUT bits occupy the upper word of the OEN register.
*/
if (rman_get_start(sc->res[1]) == rman_get_start(sc->res[0]))
pin += 16;
mask = 1U << pin;
AML_GPIO_LOCK(sc);
CSR_WRITE_4(sc, AML_GPIO_OUT_REG,
((CSR_READ_4(sc, AML_GPIO_OUT_REG) & ~mask) | (value << pin)));
AML_GPIO_UNLOCK(sc);
return (0);
}
/* Get a specific pin's input value. */
static int
aml8726_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
{
struct aml8726_gpio_softc *sc = device_get_softc(dev);
uint32_t mask = 1U << pin;
if (pin >= sc->npins)
return (EINVAL);
*value = (CSR_READ_4(sc, AML_GPIO_IN_REG) & mask) ? 1 : 0;
return (0);
}
/* Toggle a pin's output value. */
static int
aml8726_gpio_pin_toggle(device_t dev, uint32_t pin)
{
struct aml8726_gpio_softc *sc = device_get_softc(dev);
uint32_t mask;
if (pin >= sc->npins)
return (EINVAL);
/*
* The GPIOAO OUT bits occupy the upper word of the OEN register.
*/
if (rman_get_start(sc->res[1]) == rman_get_start(sc->res[0]))
pin += 16;
mask = 1U << pin;
AML_GPIO_LOCK(sc);
CSR_WRITE_4(sc, AML_GPIO_OUT_REG,
CSR_READ_4(sc, AML_GPIO_OUT_REG) ^ mask);
AML_GPIO_UNLOCK(sc);
return (0);
}
static phandle_t
aml8726_gpio_get_node(device_t bus, device_t dev)
{
/* We only have one child, the GPIO bus, which needs our own node. */
return (ofw_bus_get_node(bus));
}
static device_method_t aml8726_gpio_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aml8726_gpio_probe),
DEVMETHOD(device_attach, aml8726_gpio_attach),
DEVMETHOD(device_detach, aml8726_gpio_detach),
/* GPIO interface */
DEVMETHOD(gpio_get_bus, aml8726_gpio_get_bus),
DEVMETHOD(gpio_pin_max, aml8726_gpio_pin_max),
DEVMETHOD(gpio_pin_getname, aml8726_gpio_pin_getname),
DEVMETHOD(gpio_pin_getflags, aml8726_gpio_pin_getflags),
DEVMETHOD(gpio_pin_getcaps, aml8726_gpio_pin_getcaps),
DEVMETHOD(gpio_pin_setflags, aml8726_gpio_pin_setflags),
DEVMETHOD(gpio_pin_get, aml8726_gpio_pin_get),
DEVMETHOD(gpio_pin_set, aml8726_gpio_pin_set),
DEVMETHOD(gpio_pin_toggle, aml8726_gpio_pin_toggle),
/* ofw_bus interface */
DEVMETHOD(ofw_bus_get_node, aml8726_gpio_get_node),
DEVMETHOD_END
};
static driver_t aml8726_gpio_driver = {
"gpio",
aml8726_gpio_methods,
sizeof(struct aml8726_gpio_softc),
};
static devclass_t aml8726_gpio_devclass;
DRIVER_MODULE(aml8726_gpio, simplebus, aml8726_gpio_driver,
aml8726_gpio_devclass, 0, 0);

View File

@ -1,282 +0,0 @@
/*-
* Copyright 2013-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*/
/*
* Amlogic aml8726 I2C driver.
*
* Currently this implementation doesn't take full advantage of the hardware.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/rman.h>
#include <machine/bus.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/iicbus/iiconf.h>
#include <dev/iicbus/iicbus.h>
#include "iicbb_if.h"
struct aml8726_iic_softc {
device_t dev;
struct resource *res[1];
struct mtx mtx;
device_t iicbb;
};
static struct resource_spec aml8726_iic_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ -1, 0 }
};
#define AML_I2C_LOCK(sc) mtx_lock(&(sc)->mtx)
#define AML_I2C_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
#define AML_I2C_LOCK_INIT(sc) \
mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
"i2c", MTX_DEF)
#define AML_I2C_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx);
#define AML_I2C_CTRL_REG 0
#define AML_I2C_MANUAL_SDA_I (1 << 26)
#define AML_I2C_MANUAL_SCL_I (1 << 25)
#define AML_I2C_MANUAL_SDA_O (1 << 24)
#define AML_I2C_MANUAL_SCL_O (1 << 23)
#define AML_I2C_MANUAL_EN (1 << 22)
#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val))
#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
static int
aml8726_iic_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "amlogic,meson6-i2c"))
return (ENXIO);
device_set_desc(dev, "Amlogic aml8726 I2C");
return (BUS_PROBE_DEFAULT);
}
static int
aml8726_iic_attach(device_t dev)
{
struct aml8726_iic_softc *sc = device_get_softc(dev);
int error;
sc->dev = dev;
if (bus_alloc_resources(dev, aml8726_iic_spec, sc->res)) {
device_printf(dev, "can not allocate resources for device\n");
return (ENXIO);
}
AML_I2C_LOCK_INIT(sc);
sc->iicbb = device_add_child(dev, "iicbb", -1);
if (sc->iicbb == NULL) {
device_printf(dev, "could not add iicbb\n");
error = ENXIO;
goto fail;
}
error = device_probe_and_attach(sc->iicbb);
if (error) {
device_printf(dev, "could not attach iicbb\n");
goto fail;
}
return (0);
fail:
AML_I2C_LOCK_DESTROY(sc);
bus_release_resources(dev, aml8726_iic_spec, sc->res);
return (error);
}
static int
aml8726_iic_detach(device_t dev)
{
struct aml8726_iic_softc *sc = device_get_softc(dev);
device_t child;
/*
* Detach the children before recursively deleting
* in case a child has a pointer to a grandchild
* which is used by the child's detach routine.
*
* Remember the child before detaching so we can
* delete it (bus_generic_detach indirectly zeroes
* sc->child_dev).
*/
child = sc->iicbb;
bus_generic_detach(dev);
if (child)
device_delete_child(dev, child);
AML_I2C_LOCK_DESTROY(sc);
bus_release_resources(dev, aml8726_iic_spec, sc->res);
return (0);
}
static void
aml8726_iic_child_detached(device_t dev, device_t child)
{
struct aml8726_iic_softc *sc = device_get_softc(dev);
if (child == sc->iicbb)
sc->iicbb = NULL;
}
static int
aml8726_iic_callback(device_t dev, int index, caddr_t data)
{
return (0);
}
static int
aml8726_iic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
{
struct aml8726_iic_softc *sc = device_get_softc(dev);
AML_I2C_LOCK(sc);
CSR_WRITE_4(sc, AML_I2C_CTRL_REG,
(CSR_READ_4(sc, AML_I2C_CTRL_REG) | AML_I2C_MANUAL_SDA_O |
AML_I2C_MANUAL_SCL_O | AML_I2C_MANUAL_EN));
AML_I2C_UNLOCK(sc);
/* Wait for 10 usec */
DELAY(10);
return (IIC_ENOADDR);
}
static int
aml8726_iic_getscl(device_t dev)
{
struct aml8726_iic_softc *sc = device_get_softc(dev);
return (CSR_READ_4(sc, AML_I2C_CTRL_REG) & AML_I2C_MANUAL_SCL_I);
}
static int
aml8726_iic_getsda(device_t dev)
{
struct aml8726_iic_softc *sc = device_get_softc(dev);
return (CSR_READ_4(sc, AML_I2C_CTRL_REG) & AML_I2C_MANUAL_SDA_I);
}
static void
aml8726_iic_setscl(device_t dev, int val)
{
struct aml8726_iic_softc *sc = device_get_softc(dev);
AML_I2C_LOCK(sc);
CSR_WRITE_4(sc, AML_I2C_CTRL_REG, ((CSR_READ_4(sc, AML_I2C_CTRL_REG) &
~AML_I2C_MANUAL_SCL_O) | (val ? AML_I2C_MANUAL_SCL_O : 0) |
AML_I2C_MANUAL_EN));
AML_I2C_UNLOCK(sc);
}
static void
aml8726_iic_setsda(device_t dev, int val)
{
struct aml8726_iic_softc *sc = device_get_softc(dev);
AML_I2C_LOCK(sc);
CSR_WRITE_4(sc, AML_I2C_CTRL_REG, ((CSR_READ_4(sc, AML_I2C_CTRL_REG) &
~AML_I2C_MANUAL_SDA_O) | (val ? AML_I2C_MANUAL_SDA_O : 0) |
AML_I2C_MANUAL_EN));
AML_I2C_UNLOCK(sc);
}
static device_method_t aml8726_iic_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aml8726_iic_probe),
DEVMETHOD(device_attach, aml8726_iic_attach),
DEVMETHOD(device_detach, aml8726_iic_detach),
/* bus interface */
DEVMETHOD(bus_child_detached, aml8726_iic_child_detached),
DEVMETHOD(bus_print_child, bus_generic_print_child),
DEVMETHOD(bus_driver_added, bus_generic_driver_added),
/* IICBB interface */
DEVMETHOD(iicbb_callback, aml8726_iic_callback),
DEVMETHOD(iicbb_reset, aml8726_iic_reset),
DEVMETHOD(iicbb_getscl, aml8726_iic_getscl),
DEVMETHOD(iicbb_getsda, aml8726_iic_getsda),
DEVMETHOD(iicbb_setscl, aml8726_iic_setscl),
DEVMETHOD(iicbb_setsda, aml8726_iic_setsda),
DEVMETHOD_END
};
static driver_t aml8726_iic_driver = {
"aml8726_iic",
aml8726_iic_methods,
sizeof(struct aml8726_iic_softc),
};
static devclass_t aml8726_iic_devclass;
DRIVER_MODULE(aml8726_iic, simplebus, aml8726_iic_driver,
aml8726_iic_devclass, 0, 0);
DRIVER_MODULE(iicbb, aml8726_iic, iicbb_driver, iicbb_devclass, 0, 0);
MODULE_DEPEND(aml8726_iic, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
MODULE_VERSION(aml8726_iic, 1);

View File

@ -1,142 +0,0 @@
/*-
* Copyright 2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*
*/
/*
* Amlogic aml8726 SoC identification.
*
* The SoC identification is used by some of the drivers in order to
* handle hardware differences so the identification needs to happen
* early in the boot process (e.g. before SMP startup).
*
* It's expected that the register addresses for identifying the SoC
* are set in stone.
*
* Currently missing an entry for the aml8726-m and doesn't distinguish
* between the m801, m802, m805, s802, s805, and s812 which are all
* variations of the aml8726-m8.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/clock.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include <arm/amlogic/aml8726/aml8726_soc.h>
uint32_t aml8726_soc_hw_rev = AML_SOC_HW_REV_UNKNOWN;
uint32_t aml8726_soc_metal_rev = AML_SOC_METAL_REV_UNKNOWN;
static const struct {
uint32_t hw_rev;
char *desc;
} aml8726_soc_desc[] = {
{ AML_SOC_HW_REV_M3, "aml8726-m3" },
{ AML_SOC_HW_REV_M6, "aml8726-m6" },
{ AML_SOC_HW_REV_M6TV, "aml8726-m6tv" },
{ AML_SOC_HW_REV_M6TVL, "aml8726-m6tvl" },
{ AML_SOC_HW_REV_M8, "aml8726-m8" },
{ AML_SOC_HW_REV_M8B, "aml8726-m8b" },
{ 0xff, NULL }
};
static const struct {
uint32_t metal_rev;
char *desc;
} aml8726_m8_soc_rev[] = {
{ AML_SOC_M8_METAL_REV_A, "A" },
{ AML_SOC_M8_METAL_REV_M2_A, "MarkII A" },
{ AML_SOC_M8_METAL_REV_B, "B" },
{ AML_SOC_M8_METAL_REV_C, "C" },
{ 0xff, NULL }
};
void
aml8726_identify_soc(void)
{
int err;
struct resource res;
memset(&res, 0, sizeof(res));
res.r_bustag = fdtbus_bs_tag;
err = bus_space_map(res.r_bustag, AML_SOC_CBUS_BASE_ADDR, 0x100000,
0, &res.r_bushandle);
if (err)
panic("Could not allocate resource for SoC identification\n");
aml8726_soc_hw_rev = bus_read_4(&res, AML_SOC_HW_REV_REG);
aml8726_soc_metal_rev = bus_read_4(&res, AML_SOC_METAL_REV_REG);
bus_space_unmap(res.r_bustag, res.r_bushandle, 0x100000);
}
static void
aml8726_identify_announce_soc(void *dummy)
{
int i;
for (i = 0; aml8726_soc_desc[i].desc; i++)
if (aml8726_soc_desc[i].hw_rev == aml8726_soc_hw_rev)
break;
if (aml8726_soc_desc[i].desc == NULL)
panic("Amlogic unknown aml8726 SoC %#x\n", aml8726_soc_hw_rev);
printf("Amlogic %s SoC", aml8726_soc_desc[i].desc);
if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8) {
for (i = 0; aml8726_m8_soc_rev[i].desc; i++)
if (aml8726_m8_soc_rev[i].metal_rev ==
aml8726_soc_metal_rev)
break;
if (aml8726_m8_soc_rev[i].desc == NULL)
printf(", unknown rev %#x", aml8726_soc_metal_rev);
else
printf(", rev %s", aml8726_m8_soc_rev[i].desc);
}
printf("\n");
}
SYSINIT(aml8726_identify_announce_soc, SI_SUB_CPU, SI_ORDER_SECOND,
aml8726_identify_announce_soc, NULL);

View File

@ -1,98 +0,0 @@
/*-
* Copyright (c) 2015 Ganbold Tsagaankhuu <ganbold@FreeBSD.org>
* All rights reserved.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <machine/bus.h>
#include <dev/dwc/if_dwc.h>
#include <dev/dwc/if_dwcvar.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "if_dwc_if.h"
static int
aml8726_if_dwc_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "amlogic,meson6-dwmac"))
return (ENXIO);
device_set_desc(dev, "Amlogic Meson Gigabit Ethernet Controller");
return (BUS_PROBE_DEFAULT);
}
static int
aml8726_if_dwc_init(device_t dev)
{
return (0);
}
static int
aml8726_if_dwc_mac_type(device_t dev)
{
return (DWC_GMAC_NORMAL_DESC);
}
static int
aml8726_if_dwc_mii_clk(device_t dev)
{
return (GMAC_MII_CLK_100_150M_DIV62);
}
static device_method_t aml8726_dwc_methods[] = {
DEVMETHOD(device_probe, aml8726_if_dwc_probe),
DEVMETHOD(if_dwc_init, aml8726_if_dwc_init),
DEVMETHOD(if_dwc_mac_type, aml8726_if_dwc_mac_type),
DEVMETHOD(if_dwc_mii_clk, aml8726_if_dwc_mii_clk),
DEVMETHOD_END
};
static devclass_t aml8726_dwc_devclass;
extern driver_t dwc_driver;
DEFINE_CLASS_1(dwc, aml8726_dwc_driver, aml8726_dwc_methods,
sizeof(struct dwc_softc), dwc_driver);
DRIVER_MODULE(aml8726_dwc, simplebus, aml8726_dwc_driver,
aml8726_dwc_devclass, 0, 0);
MODULE_DEPEND(aml8726_dwc, dwc, 1, 1, 1);

View File

@ -1,92 +0,0 @@
/*-
* Copyright 2013-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* Based on omap4_l2cache.c by Olivier Houchard
*
* 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$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <machine/bus.h>
#include <machine/pl310.h>
void
platform_pl310_init(struct pl310_softc *sc)
{
uint32_t aux;
aux = pl310_read4(sc, PL310_AUX_CTRL);
/*
* The Amlogic Linux platform code enables via AUX:
*
* Early BRESP
* Full Line of Zero (which must match processor setting)
* Data Prefetch
*
* and additionally on the m6 enables:
*
* Instruction Prefetch
*
* For the moment we only enable Data Prefetch ...
* further refinements can happen as things mature.
*/
/*
* Disable instruction prefetch.
*/
aux &= ~AUX_CTRL_INSTR_PREFETCH;
/*
* Enable data prefetch.
*/
aux |= AUX_CTRL_DATA_PREFETCH;
pl310_write4(sc, PL310_AUX_CTRL, aux);
}
void
platform_pl310_write_ctrl(struct pl310_softc *sc, uint32_t val)
{
pl310_write4(sc, PL310_CTRL, val);
}
void
platform_pl310_write_debug(struct pl310_softc *sc, uint32_t val)
{
pl310_write4(sc, PL310_DEBUG_CTRL, val);
}

View File

@ -1,196 +0,0 @@
/*-
* Copyright 2013-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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 "opt_global.h"
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_platform.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/devmap.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <machine/machdep.h>
#include <machine/platform.h>
#include <dev/fdt/fdt_common.h>
#include <arm/amlogic/aml8726/aml8726_soc.h>
#include <arm/amlogic/aml8726/aml8726_clkmsr.h>
#if defined(SOCDEV_PA) && defined(SOCDEV_VA)
vm_offset_t aml8726_aobus_kva_base = SOCDEV_VA;
#else
vm_offset_t aml8726_aobus_kva_base;
#endif
static void
aml8726_fixup_busfreq(void)
{
phandle_t node;
pcell_t freq, prop;
ssize_t len;
/*
* Set the bus-frequency for the SoC simple-bus if it
* needs updating (meaning the current frequency is zero).
*/
if ((freq = aml8726_clkmsr_bus_frequency()) == 0 ||
(node = OF_finddevice("/soc")) == 0 ||
fdt_is_compatible_strict(node, "simple-bus") == 0)
while (1);
freq = cpu_to_fdt32(freq);
len = OF_getencprop(node, "bus-frequency", &prop, sizeof(prop));
if ((len / sizeof(prop)) == 1 && prop == 0)
OF_setprop(node, "bus-frequency", (void *)&freq, sizeof(freq));
}
vm_offset_t
platform_lastaddr(void)
{
return (devmap_lastaddr());
}
void
platform_probe_and_attach(void)
{
}
void
platform_gpio_init(void)
{
/*
* The UART console driver used for debugging early boot code
* needs to know the virtual base address of the aobus. It's
* expected to equal SOCDEV_VA prior to initarm calling setttb
* ... afterwards it needs to be updated due to the new page
* tables.
*
* This means there's a deadzone in initarm between setttb
* and platform_gpio_init during which printf can't be used.
*/
aml8726_aobus_kva_base =
(vm_offset_t)devmap_ptov(0xc8100000, 0x100000);
/*
* The hardware mux used by clkmsr is unique to the SoC (though
* currently clk81 is at a fixed location, however that might
* change in the future).
*/
aml8726_identify_soc();
/*
* My aml8726-m3 development box which identifies the CPU as
* a Cortex A9-r2 rev 4 randomly locks up during boot when WFI
* is used.
*/
switch (aml8726_soc_hw_rev) {
case AML_SOC_HW_REV_M3:
cpufuncs.cf_sleep = (void *)cpufunc_nullop;
break;
default:
break;
}
/*
* This FDT fixup should arguably be called through fdt_fixup_table,
* however currently there's no mechanism to specify a fixup which
* should always be invoked.
*
* It needs to be called prior to the console being initialized which
* is why it's called here, rather than from platform_late_init.
*/
aml8726_fixup_busfreq();
}
void
platform_late_init(void)
{
}
/*
* Construct static devmap entries to map out the core
* peripherals using 1mb section mappings.
*/
int
platform_devmap_init(void)
{
devmap_add_entry(0xc1100000, 0x200000); /* cbus */
devmap_add_entry(0xc4200000, 0x100000); /* pl310 */
devmap_add_entry(0xc4300000, 0x100000); /* periph */
devmap_add_entry(0xc8000000, 0x100000); /* apbbus */
devmap_add_entry(0xc8100000, 0x100000); /* aobus */
devmap_add_entry(0xc9000000, 0x800000); /* ahbbus */
devmap_add_entry(0xd9000000, 0x100000); /* ahb */
devmap_add_entry(0xda000000, 0x100000); /* secbus */
return (0);
}
#ifndef INTRNG
#ifndef DEV_GIC
static int
fdt_pic_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig,
int *pol)
{
/*
* The single core chips have just an Amlogic PIC.
*/
if (!fdt_is_compatible_strict(node, "amlogic,aml8726-pic"))
return (ENXIO);
*interrupt = fdt32_to_cpu(intr[1]);
*trig = INTR_TRIGGER_EDGE;
*pol = INTR_POLARITY_HIGH;
return (0);
}
#endif
fdt_pic_decode_t fdt_pic_table[] = {
#ifdef DEV_GIC
&gic_decode_fdt,
#else
&fdt_pic_decode_ic,
#endif
NULL
};
#endif /* INTRNG */

View File

@ -1,41 +0,0 @@
/*-
* Copyright 2014 John Wehle <john@feith.com>
* All rights reserved.
*
* 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 _ARM_AMLOGIC_AML8726_MACHDEP_H
#define _ARM_AMLOGIC_AML8726_MACHDEP_H
/*
* The aobus kvm base is supplied as a global variable * only *
* for the convience of the aml8726 UART console driver which
* is available for use to debug early boot code.
*
* This variable should not be used elsewhere.
*/
extern vm_offset_t aml8726_aobus_kva_base;
#endif /* _ARM_AMLOGIC_AML8726_MACHDEP_H */

File diff suppressed because it is too large Load Diff

View File

@ -1,122 +0,0 @@
/*-
* Copyright 2013-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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 _ARM_AMLOGIC_AML8726_MMC_H
#define _ARM_AMLOGIC_AML8726_MMC_H
#define AML_MMC_ALIGN_DMA 4
#define AML_MMC_MAX_DMA 4096
/*
* Timeouts are in milliseconds
*
* Read and write are per section 4.6.2 of the:
*
* SD Specifications Part 1
* Physical Layer Simplified Specification
* Version 4.10
*/
#define AML_MMC_CMD_TIMEOUT 50
#define AML_MMC_READ_TIMEOUT 100
#define AML_MMC_WRITE_TIMEOUT 500
#define AML_MMC_MAX_TIMEOUT 5000
#define AML_MMC_CMD_ARGUMENT_REG 0
#define AML_MMC_CMD_SEND_REG 4
#define AML_MMC_CMD_REP_PKG_CNT_MASK (0xffU << 24)
#define AML_MMC_CMD_REP_PKG_CNT_SHIFT 24
#define AML_MMC_CMD_CMD_HAS_DATA (1 << 20)
#define AML_MMC_CMD_CHECK_DAT0_BUSY (1 << 19)
#define AML_MMC_CMD_RESP_CRC7_FROM_8 (1 << 18)
#define AML_MMC_CMD_RESP_HAS_DATA (1 << 17)
#define AML_MMC_CMD_RESP_NO_CRC7 (1 << 16)
#define AML_MMC_CMD_RESP_BITS_MASK (0xff << 8)
#define AML_MMC_CMD_RESP_BITS_SHIFT 8
#define AML_MMC_CMD_START_BIT (0 << 7)
#define AML_MMC_CMD_TRANS_BIT_HOST (1 << 6)
#define AML_MMC_CMD_INDEX_MASK 0x3f
#define AML_MMC_CMD_INDEX_SHIFT 0
#define AML_MMC_CONFIG_REG 8
#define AML_MMC_CONFIG_WR_CRC_STAT_MASK (7U << 29)
#define AML_MMC_CONFIG_WR_CRC_STAT_SHIFT 29
#define AML_MMC_CONFIG_WR_DELAY_MASK (0x3f << 23)
#define AML_MMC_CONFIG_WR_DELAY_SHIFT 23
#define AML_MMC_CONFIG_DMA_ENDIAN_MASK (3 << 21)
#define AML_MMC_CONFIG_DMA_ENDIAN_NC (0 << 21)
#define AML_MMC_CONFIG_DMA_ENDIAN_SB (1 << 21)
#define AML_MMC_CONFIG_DMA_ENDIAN_SW (2 << 21)
#define AML_MMC_CONFIG_DMA_ENDIAN_SBW (3 << 21)
#define AML_MMC_CONFIG_BUS_WIDTH_MASK (1 << 20)
#define AML_MMC_CONFIG_BUS_WIDTH_1 (0 << 20)
#define AML_MMC_CONFIG_BUS_WIDTH_4 (1 << 20)
#define AML_MMC_CONFIG_DATA_NEG_EDGE (1 << 19)
#define AML_MMC_CONFIG_DONT_DELAY_DATA (1 << 18)
#define AML_MMC_CONFIG_CMD_ARG_BITS_MASK (0x3f << 12)
#define AML_MMC_CONFIG_CMD_ARG_BITS_SHIFT 12
#define AML_MMC_CONFIG_CMD_POS_EDGE (1 << 11)
#define AML_MMC_CONFIG_CMD_NO_CRC (1 << 10)
#define AML_MMC_CONFIG_CMD_CLK_DIV_MASK 0x3ff
#define AML_MMC_CONFIG_CMD_CLK_DIV_SHIFT 0
#define AML_MMC_IRQ_STATUS_REG 12
#define AML_MMC_IRQ_STATUS_TIMER_CNT_MASK (0x1fffU << 19)
#define AML_MMC_IRQ_STATUS_TIMER_CNT_SHIFT 19
#define AML_MMC_IRQ_STATUS_TIMER_EN (1 << 18)
#define AML_MMC_IRQ_STATUS_TIMEOUT_IRQ (1 << 16)
#define AML_MMC_IRQ_STATUS_CMD_DONE_IRQ (1 << 9)
#define AML_MMC_IRQ_STATUS_WR_CRC16_OK (1 << 7)
#define AML_MMC_IRQ_STATUS_RD_CRC16_OK (1 << 6)
#define AML_MMC_IRQ_STATUS_RESP_CRC7_OK (1 << 5)
#define AML_MMC_IRQ_STATUS_CMD_BUSY (1 << 4)
#define AML_MMC_IRQ_STATUS_CLEAR_IRQ 0x10700
#define AML_MMC_IRQ_CONFIG_REG 16
#define AML_MMC_IRQ_CONFIG_SOFT_RESET (1 << 15)
#define AML_MMC_IRQ_CONFIG_CMD_DONE_EN (1 << 4)
#define AML_MMC_MULT_CONFIG_REG 20
#define AML_MMC_MULT_CONFIG_RESP_INDEX_MASK (0xf << 12)
#define AML_MMC_MULT_CONFIG_RESP_INDEX_SHIFT 12
#define AML_MMC_MULT_CONFIG_RESP_READOUT_EN (1 << 8)
#define AML_MMC_MULT_CONFIG_STREAM_8_MODE (1 << 5)
#define AML_MMC_MULT_CONFIG_STREAM_EN (1 << 4)
#define AML_MMC_MULT_CONFIG_PORT_MASK 3
#define AML_MMC_MULT_CONFIG_PORT_A 0
#define AML_MMC_MULT_CONFIG_PORT_B 1
#define AML_MMC_MULT_CONFIG_PORT_C 2
#define AML_MMC_DMA_ADDR_REG 24
#define AML_MMC_EXTENSION_REG 28
#define AML_MMC_EXTENSION_NO_CRC16 (1 << 30)
#define AML_MMC_EXTENSION_PKT_SIZE_MASK (0x3fff << 16)
#define AML_MMC_EXTENSION_PKT_SIZE_SHIFT 16
#endif /* _ARM_AMLOGIC_AML8726_MMC_H */

View File

@ -1,625 +0,0 @@
/*-
* Copyright 2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*/
/*
* Amlogic aml8726 multiprocessor support.
*
* Some processors require powering on which involves poking registers
* on the aobus and cbus ... it's expected that these locations are set
* in stone.
*
* Locking is not used as these routines should only be called by the BP
* during startup and should complete prior to the APs being released (the
* issue being to ensure that a register such as AML_SOC_CPU_CLK_CNTL_REG
* is not concurrently modified).
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <sys/smp.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <machine/cpu.h>
#include <machine/bus.h>
#include <machine/smp.h>
#include <machine/fdt.h>
#include <machine/intr.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/amlogic/aml8726/aml8726_soc.h>
static const char *scu_compatible[] = {
"arm,cortex-a5-scu",
"arm,cortex-a9-scu",
NULL
};
static const char *scu_errata_764369[] = {
"arm,cortex-a9-scu",
NULL
};
static const char *cpucfg_compatible[] = {
"amlogic,aml8726-cpuconfig",
NULL
};
static struct {
boolean_t errata_764369;
u_long scu_size;
struct resource scu_res;
u_long cpucfg_size;
struct resource cpucfg_res;
struct resource aobus_res;
struct resource cbus_res;
} aml8726_smp;
#define AML_SCU_CONTROL_REG 0
#define AML_SCU_CONTROL_ENABLE 1
#define AML_SCU_CONFIG_REG 4
#define AML_SCU_CONFIG_NCPU_MASK 0x3
#define AML_SCU_CPU_PWR_STATUS_REG 8
#define AML_SCU_CPU_PWR_STATUS_CPU3_MASK (3 << 24)
#define AML_SCU_CPU_PWR_STATUS_CPU2_MASK (3 << 16)
#define AML_SCU_CPU_PWR_STATUS_CPU1_MASK (3 << 8)
#define AML_SCU_CPU_PWR_STATUS_CPU0_MASK 3
#define AML_SCU_INV_TAGS_REG 12
#define AML_SCU_DIAG_CONTROL_REG 48
#define AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT 1
#define AML_CPUCONF_CONTROL_REG 0
#define AML_CPUCONF_CPU1_ADDR_REG 4
#define AML_CPUCONF_CPU2_ADDR_REG 8
#define AML_CPUCONF_CPU3_ADDR_REG 12
/* aobus */
#define AML_M8_CPU_PWR_CNTL0_REG 0xe0
#define AML_M8B_CPU_PWR_CNTL0_MODE_CPU3_MASK (3 << 22)
#define AML_M8B_CPU_PWR_CNTL0_MODE_CPU2_MASK (3 << 20)
#define AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK (3 << 18)
#define AML_M8_CPU_PWR_CNTL0_ISO_CPU3 (1 << 3)
#define AML_M8_CPU_PWR_CNTL0_ISO_CPU2 (1 << 2)
#define AML_M8_CPU_PWR_CNTL0_ISO_CPU1 (1 << 1)
#define AML_M8_CPU_PWR_CNTL1_REG 0xe4
#define AML_M8B_CPU_PWR_CNTL1_PWR_CPU3 (1 << 19)
#define AML_M8B_CPU_PWR_CNTL1_PWR_CPU2 (1 << 18)
#define AML_M8B_CPU_PWR_CNTL1_PWR_CPU1 (1 << 17)
#define AML_M8_CPU_PWR_CNTL1_MODE_CPU3_MASK (3 << 8)
#define AML_M8_CPU_PWR_CNTL1_MODE_CPU2_MASK (3 << 6)
#define AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK (3 << 4)
#define AML_M8B_CPU_PWR_MEM_PD0_REG 0xf4
#define AML_M8B_CPU_PWR_MEM_PD0_CPU3 (0xf << 20)
#define AML_M8B_CPU_PWR_MEM_PD0_CPU2 (0xf << 24)
#define AML_M8B_CPU_PWR_MEM_PD0_CPU1 (0xf << 28)
/* cbus */
#define AML_SOC_CPU_CLK_CNTL_REG 0x419c
#define AML_M8_CPU_CLK_CNTL_RESET_CPU3 (1 << 27)
#define AML_M8_CPU_CLK_CNTL_RESET_CPU2 (1 << 26)
#define AML_M8_CPU_CLK_CNTL_RESET_CPU1 (1 << 25)
#define SCU_WRITE_4(reg, value) bus_write_4(&aml8726_smp.scu_res, \
(reg), (value))
#define SCU_READ_4(reg) bus_read_4(&aml8726_smp.scu_res, (reg))
#define SCU_BARRIER(reg) bus_barrier(&aml8726_smp.scu_res, \
(reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
#define CPUCONF_WRITE_4(reg, value) bus_write_4(&aml8726_smp.cpucfg_res, \
(reg), (value))
#define CPUCONF_READ_4(reg) bus_read_4(&aml8726_smp.cpucfg_res, \
(reg))
#define CPUCONF_BARRIER(reg) bus_barrier(&aml8726_smp.cpucfg_res, \
(reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
#define AOBUS_WRITE_4(reg, value) bus_write_4(&aml8726_smp.aobus_res, \
(reg), (value))
#define AOBUS_READ_4(reg) bus_read_4(&aml8726_smp.aobus_res, \
(reg))
#define AOBUS_BARRIER(reg) bus_barrier(&aml8726_smp.aobus_res, \
(reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
#define CBUS_WRITE_4(reg, value) bus_write_4(&aml8726_smp.cbus_res, \
(reg), (value))
#define CBUS_READ_4(reg) bus_read_4(&aml8726_smp.cbus_res, \
(reg))
#define CBUS_BARRIER(reg) bus_barrier(&aml8726_smp.cbus_res, \
(reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
static phandle_t
find_node_for_device(const char *device, const char **compatible)
{
int i;
phandle_t node;
/*
* Try to access the node directly i.e. through /aliases/.
*/
if ((node = OF_finddevice(device)) != -1)
for (i = 0; compatible[i]; i++)
if (fdt_is_compatible_strict(node, compatible[i]))
return node;
/*
* Find the node the long way.
*/
for (i = 0; compatible[i]; i++) {
if ((node = OF_finddevice("/soc")) == -1)
return (0);
if ((node = fdt_find_compatible(node, compatible[i], 1)) != 0)
return node;
}
return (0);
}
static int
alloc_resource_for_node(phandle_t node, struct resource *res, u_long *size)
{
int err;
u_long pbase, psize;
u_long start;
if ((err = fdt_get_range(OF_parent(node), 0, &pbase, &psize)) != 0 ||
(err = fdt_regsize(node, &start, size)) != 0)
return (err);
start += pbase;
memset(res, 0, sizeof(*res));
res->r_bustag = fdtbus_bs_tag;
err = bus_space_map(res->r_bustag, start, *size, 0, &res->r_bushandle);
return (err);
}
static void
power_on_cpu(int cpu)
{
uint32_t scpsr;
uint32_t value;
if (cpu <= 0)
return;
/*
* Power on the CPU if the intricate details are known, otherwise
* just hope for the best (it may have already be powered on by
* the hardware / firmware).
*/
switch (aml8726_soc_hw_rev) {
case AML_SOC_HW_REV_M8:
case AML_SOC_HW_REV_M8B:
/*
* Set the SCU power status for the CPU to normal mode.
*/
scpsr = SCU_READ_4(AML_SCU_CPU_PWR_STATUS_REG);
scpsr &= ~(AML_SCU_CPU_PWR_STATUS_CPU1_MASK << ((cpu - 1) * 8));
SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr);
SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG);
if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
/*
* Reset may cause the current power status from the
* actual CPU to be written to the SCU (over-writing
* the value we've just written) so set it to normal
* mode as well.
*/
value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG);
value &= ~(AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK <<
((cpu - 1) * 2));
AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value);
AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG);
}
DELAY(5);
/*
* Assert reset.
*/
value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG);
value |= AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1);
CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value);
CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG);
if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
/*
* Release RAM pull-down.
*/
value = AOBUS_READ_4(AML_M8B_CPU_PWR_MEM_PD0_REG);
value &= ~((uint32_t)AML_M8B_CPU_PWR_MEM_PD0_CPU1 >>
((cpu - 1) * 4));
AOBUS_WRITE_4(AML_M8B_CPU_PWR_MEM_PD0_REG, value);
AOBUS_BARRIER(AML_M8B_CPU_PWR_MEM_PD0_REG);
}
/*
* Power on CPU.
*/
value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG);
value &= ~(AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK <<
((cpu - 1) * 2));
AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL1_REG, value);
AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL1_REG);
DELAY(10);
if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
/*
* Wait for power on confirmation.
*/
for ( ; ; ) {
value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG);
value &= AML_M8B_CPU_PWR_CNTL1_PWR_CPU1 <<
(cpu - 1);
if (value)
break;
DELAY(10);
}
}
/*
* Release peripheral clamp.
*/
value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG);
value &= ~(AML_M8_CPU_PWR_CNTL0_ISO_CPU1 << (cpu - 1));
AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value);
AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG);
/*
* Release reset.
*/
value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG);
value &= ~(AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1));
CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value);
CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG);
if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) {
/*
* The Amlogic Linux platform code sets the SCU power
* status for the CPU again for some reason so we
* follow suit (perhaps in case the reset caused
* a stale power status from the actual CPU to be
* written to the SCU).
*/
SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr);
SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG);
}
break;
default:
break;
}
}
void
platform_mp_setmaxid(void)
{
int err;
int i;
int ncpu;
phandle_t cpucfg_node;
phandle_t scu_node;
uint32_t value;
if (mp_ncpus != 0)
return;
ncpu = 1;
/*
* Is the hardware necessary for SMP present?
*/
if ((scu_node = find_node_for_device("scu", scu_compatible)) == 0)
goto moveon;
if ((cpucfg_node = find_node_for_device("cpuconfig",
cpucfg_compatible)) == 0)
goto moveon;
if (alloc_resource_for_node(scu_node, &aml8726_smp.scu_res,
&aml8726_smp.scu_size) != 0)
panic("Could not allocate resource for SCU");
if (alloc_resource_for_node(cpucfg_node, &aml8726_smp.cpucfg_res,
&aml8726_smp.cpucfg_size) != 0)
panic("Could not allocate resource for CPUCONFIG");
/*
* Strictly speaking the aobus and cbus may not be required in
* order to start an AP (it depends on the processor), however
* always mapping them in simplifies the code.
*/
aml8726_smp.aobus_res.r_bustag = fdtbus_bs_tag;
err = bus_space_map(aml8726_smp.aobus_res.r_bustag,
AML_SOC_AOBUS_BASE_ADDR, 0x100000,
0, &aml8726_smp.aobus_res.r_bushandle);
if (err)
panic("Could not allocate resource for AOBUS");
aml8726_smp.cbus_res.r_bustag = fdtbus_bs_tag;
err = bus_space_map(aml8726_smp.cbus_res.r_bustag,
AML_SOC_CBUS_BASE_ADDR, 0x100000,
0, &aml8726_smp.cbus_res.r_bushandle);
if (err)
panic("Could not allocate resource for CBUS");
aml8726_smp.errata_764369 = false;
for (i = 0; scu_errata_764369[i]; i++)
if (fdt_is_compatible_strict(scu_node, scu_errata_764369[i])) {
aml8726_smp.errata_764369 = true;
break;
}
/*
* Read the number of CPUs present.
*/
value = SCU_READ_4(AML_SCU_CONFIG_REG);
ncpu = (value & AML_SCU_CONFIG_NCPU_MASK) + 1;
moveon:
mp_ncpus = ncpu;
mp_maxid = ncpu - 1;
}
void
platform_mp_start_ap(void)
{
int i;
uint32_t reg;
uint32_t value;
vm_paddr_t paddr;
if (mp_ncpus < 2)
return;
/*
* Invalidate SCU cache tags. The 0x0000ffff constant invalidates
* all ways on all cores 0-3. Per the ARM docs, it's harmless to
* write to the bits for cores that are not present.
*/
SCU_WRITE_4(AML_SCU_INV_TAGS_REG, 0x0000ffff);
if (aml8726_smp.errata_764369) {
/*
* Erratum ARM/MP: 764369 (problems with cache maintenance).
* Setting the "disable-migratory bit" in the undocumented SCU
* Diagnostic Control Register helps work around the problem.
*/
value = SCU_READ_4(AML_SCU_DIAG_CONTROL_REG);
value |= AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT;
SCU_WRITE_4(AML_SCU_DIAG_CONTROL_REG, value);
}
/*
* Enable the SCU, then clean the cache on this core. After these
* two operations the cache tag ram in the SCU is coherent with
* the contents of the cache on this core. The other cores aren't
* running yet so their caches can't contain valid data yet, however
* we've initialized their SCU tag ram above, so they will be
* coherent from startup.
*/
value = SCU_READ_4(AML_SCU_CONTROL_REG);
value |= AML_SCU_CONTROL_ENABLE;
SCU_WRITE_4(AML_SCU_CONTROL_REG, value);
SCU_BARRIER(AML_SCU_CONTROL_REG);
dcache_wbinv_poc_all();
/* Set the boot address and power on each AP. */
paddr = pmap_kextract((vm_offset_t)mpentry);
for (i = 1; i < mp_ncpus; i++) {
reg = AML_CPUCONF_CPU1_ADDR_REG + ((i - 1) * 4);
CPUCONF_WRITE_4(reg, paddr);
CPUCONF_BARRIER(reg);
power_on_cpu(i);
}
/*
* Enable the APs.
*
* The Amlogic Linux platform code sets the lsb for some reason
* in addition to the enable bit for each AP so we follow suit
* (the lsb may be the enable bit for the BP, though in that case
* it should already be set since it's currently running).
*/
value = CPUCONF_READ_4(AML_CPUCONF_CONTROL_REG);
value |= 1;
for (i = 1; i < mp_ncpus; i++)
value |= (1 << i);
CPUCONF_WRITE_4(AML_CPUCONF_CONTROL_REG, value);
CPUCONF_BARRIER(AML_CPUCONF_CONTROL_REG);
/* Wakeup the now enabled APs */
dsb();
sev();
/*
* Free the resources which are not needed after startup.
*/
bus_space_unmap(aml8726_smp.scu_res.r_bustag,
aml8726_smp.scu_res.r_bushandle,
aml8726_smp.scu_size);
bus_space_unmap(aml8726_smp.cpucfg_res.r_bustag,
aml8726_smp.cpucfg_res.r_bushandle,
aml8726_smp.cpucfg_size);
bus_space_unmap(aml8726_smp.aobus_res.r_bustag,
aml8726_smp.aobus_res.r_bushandle,
0x100000);
bus_space_unmap(aml8726_smp.cbus_res.r_bustag,
aml8726_smp.cbus_res.r_bushandle,
0x100000);
memset(&aml8726_smp, 0, sizeof(aml8726_smp));
}
/*
* Stub drivers for cosmetic purposes.
*/
struct aml8726_scu_softc {
device_t dev;
};
static int
aml8726_scu_probe(device_t dev)
{
int i;
for (i = 0; scu_compatible[i]; i++)
if (ofw_bus_is_compatible(dev, scu_compatible[i]))
break;
if (!scu_compatible[i])
return (ENXIO);
device_set_desc(dev, "ARM Snoop Control Unit");
return (BUS_PROBE_DEFAULT);
}
static int
aml8726_scu_attach(device_t dev)
{
struct aml8726_scu_softc *sc = device_get_softc(dev);
sc->dev = dev;
return (0);
}
static int
aml8726_scu_detach(device_t dev)
{
return (0);
}
static device_method_t aml8726_scu_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aml8726_scu_probe),
DEVMETHOD(device_attach, aml8726_scu_attach),
DEVMETHOD(device_detach, aml8726_scu_detach),
DEVMETHOD_END
};
static driver_t aml8726_scu_driver = {
"scu",
aml8726_scu_methods,
sizeof(struct aml8726_scu_softc),
};
static devclass_t aml8726_scu_devclass;
EARLY_DRIVER_MODULE(scu, simplebus, aml8726_scu_driver, aml8726_scu_devclass,
0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
struct aml8726_cpucfg_softc {
device_t dev;
};
static int
aml8726_cpucfg_probe(device_t dev)
{
int i;
for (i = 0; cpucfg_compatible[i]; i++)
if (ofw_bus_is_compatible(dev, cpucfg_compatible[i]))
break;
if (!cpucfg_compatible[i])
return (ENXIO);
device_set_desc(dev, "Amlogic CPU Config");
return (BUS_PROBE_DEFAULT);
}
static int
aml8726_cpucfg_attach(device_t dev)
{
struct aml8726_cpucfg_softc *sc = device_get_softc(dev);
sc->dev = dev;
return (0);
}
static int
aml8726_cpucfg_detach(device_t dev)
{
return (0);
}
static device_method_t aml8726_cpucfg_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aml8726_cpucfg_probe),
DEVMETHOD(device_attach, aml8726_cpucfg_attach),
DEVMETHOD(device_detach, aml8726_cpucfg_detach),
DEVMETHOD_END
};
static driver_t aml8726_cpucfg_driver = {
"cpuconfig",
aml8726_cpucfg_methods,
sizeof(struct aml8726_cpucfg_softc),
};
static devclass_t aml8726_cpucfg_devclass;
EARLY_DRIVER_MODULE(cpuconfig, simplebus, aml8726_cpucfg_driver,
aml8726_cpucfg_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);

View File

@ -1,277 +0,0 @@
/*-
* Copyright 2013-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*/
/*
* Amlogic aml8726 PIC driver.
*
* The current implementation doesn't include support for FIQ.
*
* There is a set of four interrupt controllers per cpu located in adjacent
* memory addresses (the set for cpu 1 starts right after the set for cpu 0)
* ... this allows for interrupt handling to be spread across the cpus.
*
* The multicore chips also have a GIC ... typically they run SMP kernels
* which include the GIC driver in which case this driver is simply used
* to disable the PIC.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
struct aml8726_pic_softc {
device_t dev;
struct resource * res[1];
};
static struct resource_spec aml8726_pic_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ -1, 0 }
};
/*
* devclass_get_device / device_get_softc could be used
* to dynamically locate this, however the pic is a
* required device which can't be unloaded so there's
* no need for the overhead.
*/
static struct aml8726_pic_softc *aml8726_pic_sc = NULL;
#define AML_PIC_NCNTRLS 4
#define AML_PIC_IRQS_PER_CNTRL 32
#define AML_PIC_NIRQS (AML_PIC_NCNTRLS * AML_PIC_IRQS_PER_CNTRL)
#define AML_PIC_0_STAT_REG 0
#define AML_PIC_0_STAT_CLR_REG 4
#define AML_PIC_0_MASK_REG 8
#define AML_PIC_0_FIRQ_SEL 12
#define AML_PIC_1_STAT_REG 16
#define AML_PIC_1_STAT_CLR_REG 20
#define AML_PIC_1_MASK_REG 24
#define AML_PIC_1_FIRQ_SEL 28
#define AML_PIC_2_STAT_REG 32
#define AML_PIC_2_STAT_CLR_REG 36
#define AML_PIC_2_MASK_REG 40
#define AML_PIC_2_FIRQ_SEL 44
#define AML_PIC_3_STAT_REG 48
#define AML_PIC_3_STAT_CLR_REG 52
#define AML_PIC_3_MASK_REG 56
#define AML_PIC_3_FIRQ_SEL 60
#define AML_PIC_CTRL(x) ((x) >> 5)
#define AML_PIC_BIT(x) (1 << ((x) & 0x1f))
#define AML_PIC_STAT_REG(x) (AML_PIC_0_STAT_REG + AML_PIC_CTRL(x) * 16)
#define AML_PIC_STAT_CLR_REG(x) (AML_PIC_0_STAT_CLR_REG + AML_PIC_CTRL(x) * 16)
#define AML_PIC_MASK_REG(x) (AML_PIC_0_MASK_REG + AML_PIC_CTRL(x) * 16)
#define AML_PIC_FIRQ_SEL(x) (AML_PIC_0_FIRQ_REG + AML_PIC_CTRL(x) * 16)
#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val))
#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
#define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \
(BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
static void
aml8726_pic_eoi(void *arg)
{
uintptr_t nb = (uintptr_t) arg;
if (nb >= AML_PIC_NIRQS)
return;
arm_irq_memory_barrier(nb);
CSR_WRITE_4(aml8726_pic_sc, AML_PIC_STAT_CLR_REG(nb), AML_PIC_BIT(nb));
CSR_BARRIER(aml8726_pic_sc, AML_PIC_STAT_CLR_REG(nb));
}
static int
aml8726_pic_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-pic"))
return (ENXIO);
device_set_desc(dev, "Amlogic aml8726 PIC");
return (BUS_PROBE_DEFAULT);
}
static int
aml8726_pic_attach(device_t dev)
{
struct aml8726_pic_softc *sc = device_get_softc(dev);
int i;
/* There should be exactly one instance. */
if (aml8726_pic_sc != NULL)
return (ENXIO);
sc->dev = dev;
if (bus_alloc_resources(dev, aml8726_pic_spec, sc->res)) {
device_printf(dev, "could not allocate resources for device\n");
return (ENXIO);
}
/*
* Disable, clear, and set the interrupts to normal mode.
*/
for (i = 0; i < AML_PIC_NCNTRLS; i++) {
CSR_WRITE_4(sc, AML_PIC_0_MASK_REG + i * 16, 0);
CSR_WRITE_4(sc, AML_PIC_0_STAT_CLR_REG + i * 16, ~0u);
CSR_WRITE_4(sc, AML_PIC_0_FIRQ_SEL + i * 16, 0);
}
#ifndef DEV_GIC
arm_post_filter = aml8726_pic_eoi;
#else
device_printf(dev, "disabled in favor of gic\n");
#endif
aml8726_pic_sc = sc;
return (0);
}
static int
aml8726_pic_detach(device_t dev)
{
return (EBUSY);
}
static device_method_t aml8726_pic_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aml8726_pic_probe),
DEVMETHOD(device_attach, aml8726_pic_attach),
DEVMETHOD(device_detach, aml8726_pic_detach),
DEVMETHOD_END
};
static driver_t aml8726_pic_driver = {
"pic",
aml8726_pic_methods,
sizeof(struct aml8726_pic_softc),
};
static devclass_t aml8726_pic_devclass;
EARLY_DRIVER_MODULE(pic, simplebus, aml8726_pic_driver, aml8726_pic_devclass,
0, 0, BUS_PASS_INTERRUPT);
#ifndef DEV_GIC
int
arm_get_next_irq(int last)
{
uint32_t value;
int irq;
int start;
/*
* The extra complexity is simply so that all IRQs are checked
* round robin so a particularly busy interrupt can't prevent
* other interrupts from being serviced.
*/
start = (last + 1) % AML_PIC_NIRQS;
irq = start;
for ( ; ; ) {
value = CSR_READ_4(aml8726_pic_sc, AML_PIC_STAT_REG(irq));
for ( ; ; ) {
if ((value & AML_PIC_BIT(irq)) != 0)
return (irq);
irq = (irq + 1) % AML_PIC_NIRQS;
if (irq == start)
return (-1);
if ((irq % AML_PIC_IRQS_PER_CNTRL) == 0)
break;
}
}
}
void
arm_mask_irq(uintptr_t nb)
{
uint32_t mask;
if (nb >= AML_PIC_NIRQS)
return;
mask = CSR_READ_4(aml8726_pic_sc, AML_PIC_MASK_REG(nb));
mask &= ~AML_PIC_BIT(nb);
CSR_WRITE_4(aml8726_pic_sc, AML_PIC_MASK_REG(nb), mask);
CSR_BARRIER(aml8726_pic_sc, AML_PIC_MASK_REG(nb));
aml8726_pic_eoi((void *)nb);
}
void
arm_unmask_irq(uintptr_t nb)
{
uint32_t mask;
if (nb >= AML_PIC_NIRQS)
return;
arm_irq_memory_barrier(nb);
mask = CSR_READ_4(aml8726_pic_sc, AML_PIC_MASK_REG(nb));
mask |= AML_PIC_BIT(nb);
CSR_WRITE_4(aml8726_pic_sc, AML_PIC_MASK_REG(nb), mask);
CSR_BARRIER(aml8726_pic_sc, AML_PIC_MASK_REG(nb));
}
#endif

View File

@ -1,430 +0,0 @@
/*-
* Copyright 2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*/
/*
* Amlogic aml8726 pinctrl driver.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <machine/bus.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/fdt/fdt_pinctrl.h>
#include <arm/amlogic/aml8726/aml8726_soc.h>
#include <arm/amlogic/aml8726/aml8726_pinctrl.h>
struct aml8726_pinctrl_softc {
device_t dev;
struct {
struct aml8726_pinctrl_function *func;
struct aml8726_pinctrl_pkg_pin *ppin;
boolean_t pud_ctrl;
} soc;
struct resource *res[6];
struct mtx mtx;
};
static struct resource_spec aml8726_pinctrl_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE }, /* mux */
{ SYS_RES_MEMORY, 1, RF_ACTIVE | RF_SHAREABLE }, /* pu/pd */
{ SYS_RES_MEMORY, 2, RF_ACTIVE | RF_SHAREABLE }, /* pull enable */
{ SYS_RES_MEMORY, 3, RF_ACTIVE }, /* ao mux */
{ SYS_RES_MEMORY, 4, RF_ACTIVE | RF_SHAREABLE }, /* ao pu/pd */
{ SYS_RES_MEMORY, 5, RF_ACTIVE | RF_SHAREABLE }, /* ao pull enable */
{ -1, 0 }
};
#define AML_PINCTRL_LOCK(sc) mtx_lock(&(sc)->mtx)
#define AML_PINCTRL_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
#define AML_PINCTRL_LOCK_INIT(sc) \
mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
"pinctrl", MTX_DEF)
#define AML_PINCTRL_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx);
#define MUX_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val))
#define MUX_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
#define PUD_WRITE_4(sc, reg, val) bus_write_4((sc)->res[1], reg, (val))
#define PUD_READ_4(sc, reg) bus_read_4((sc)->res[1], reg)
#define PEN_WRITE_4(sc, reg, val) bus_write_4((sc)->res[2], reg, (val))
#define PEN_READ_4(sc, reg) bus_read_4((sc)->res[2], reg)
#define AOMUX_WRITE_4(sc, reg, val) bus_write_4((sc)->res[3], reg, (val))
#define AOMUX_READ_4(sc, reg) bus_read_4((sc)->res[3], reg)
#define AOPUD_WRITE_4(sc, reg, val) bus_write_4((sc)->res[4], reg, (val))
#define AOPUD_READ_4(sc, reg) bus_read_4((sc)->res[4], reg)
#define AOPEN_WRITE_4(sc, reg, val) bus_write_4((sc)->res[5], reg, (val))
#define AOPEN_READ_4(sc, reg) bus_read_4((sc)->res[5], reg)
static int
aml8726_pinctrl_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-pinctrl"))
return (ENXIO);
device_set_desc(dev, "Amlogic aml8726 pinctrl");
return (BUS_PROBE_DEFAULT);
}
static int
aml8726_pinctrl_attach(device_t dev)
{
struct aml8726_pinctrl_softc *sc = device_get_softc(dev);
sc->dev = dev;
sc->soc.pud_ctrl = false;
switch (aml8726_soc_hw_rev) {
case AML_SOC_HW_REV_M3:
sc->soc.func = aml8726_m3_pinctrl;
sc->soc.ppin = aml8726_m3_pkg_pin;
break;
case AML_SOC_HW_REV_M6:
sc->soc.func = aml8726_m6_pinctrl;
sc->soc.ppin = aml8726_m6_pkg_pin;
break;
case AML_SOC_HW_REV_M8:
sc->soc.func = aml8726_m8_pinctrl;
sc->soc.ppin = aml8726_m8_pkg_pin;
sc->soc.pud_ctrl = true;
break;
case AML_SOC_HW_REV_M8B:
sc->soc.func = aml8726_m8b_pinctrl;
sc->soc.ppin = aml8726_m8b_pkg_pin;
sc->soc.pud_ctrl = true;
break;
default:
device_printf(dev, "unsupported SoC\n");
return (ENXIO);
/* NOTREACHED */
}
if (bus_alloc_resources(dev, aml8726_pinctrl_spec, sc->res)) {
device_printf(dev, "could not allocate resources for device\n");
return (ENXIO);
}
AML_PINCTRL_LOCK_INIT(sc);
fdt_pinctrl_register(dev, "amlogic,pins");
fdt_pinctrl_configure_tree(dev);
return (0);
}
static int
aml8726_pinctrl_detach(device_t dev)
{
struct aml8726_pinctrl_softc *sc = device_get_softc(dev);
AML_PINCTRL_LOCK_DESTROY(sc);
bus_release_resources(dev, aml8726_pinctrl_spec, sc->res);
return (0);
}
static int
aml8726_pinctrl_configure_pins(device_t dev, phandle_t cfgxref)
{
struct aml8726_pinctrl_softc *sc = device_get_softc(dev);
struct aml8726_pinctrl_function *cf;
struct aml8726_pinctrl_function *f;
struct aml8726_pinctrl_pkg_pin *pp;
struct aml8726_pinctrl_pin *cp;
struct aml8726_pinctrl_pin *p;
enum aml8726_pinctrl_pull_mode pm;
char *function_name;
char *pins;
char *pin_name;
char *pull;
phandle_t node;
ssize_t len;
uint32_t value;
node = OF_node_from_xref(cfgxref);
len = OF_getprop_alloc(node, "amlogic,function",
(void **)&function_name);
if (len < 0) {
device_printf(dev,
"missing amlogic,function attribute in FDT\n");
return (ENXIO);
}
for (f = sc->soc.func; f->name != NULL; f++)
if (strncmp(f->name, function_name, len) == 0)
break;
if (f->name == NULL) {
device_printf(dev, "unknown function attribute %.*s in FDT\n",
len, function_name);
OF_prop_free(function_name);
return (ENXIO);
}
OF_prop_free(function_name);
len = OF_getprop_alloc(node, "amlogic,pull",
(void **)&pull);
pm = aml8726_unknown_pm;
if (len > 0) {
if (strncmp(pull, "enable", len) == 0)
pm = aml8726_enable_pm;
else if (strncmp(pull, "disable", len) == 0)
pm = aml8726_disable_pm;
else if (strncmp(pull, "down", len) == 0)
pm = aml8726_enable_down_pm;
else if (strncmp(pull, "up", len) == 0)
pm = aml8726_enable_up_pm;
else {
device_printf(dev,
"unknown pull attribute %.*s in FDT\n",
len, pull);
OF_prop_free(pull);
return (ENXIO);
}
}
OF_prop_free(pull);
/*
* Setting the pull direction isn't supported on all SoC.
*/
switch (pm) {
case aml8726_enable_down_pm:
case aml8726_enable_up_pm:
if (sc->soc.pud_ctrl == false) {
device_printf(dev,
"SoC doesn't support setting pull direction.\n");
return (ENXIO);
}
break;
default:
break;
}
len = OF_getprop_alloc(node, "amlogic,pins",
(void **)&pins);
if (len < 0) {
device_printf(dev, "missing amlogic,pins attribute in FDT\n");
return (ENXIO);
}
pin_name = pins;
while (len) {
for (p = f->pins; p->name != NULL; p++)
if (strncmp(p->name, pin_name, len) == 0)
break;
if (p->name == NULL) {
/* display message prior to queuing up next string */
device_printf(dev, "unknown pin attribute %.*s in FDT\n",
len, pin_name);
}
/* queue up next string */
while (*pin_name && len) {
pin_name++;
len--;
}
if (len) {
pin_name++;
len--;
}
if (p->name == NULL)
continue;
for (pp = sc->soc.ppin; pp->pkg_name != NULL; pp++)
if (strcmp(pp->pkg_name, p->pkg_name) == 0)
break;
if (pp->pkg_name == NULL) {
device_printf(dev,
"missing entry for package pin %s\n",
p->pkg_name);
continue;
}
if (pm != aml8726_unknown_pm && pp->pull_bits == 0x00000000) {
device_printf(dev,
"missing pull info for package pin %s\n",
p->pkg_name);
continue;
}
AML_PINCTRL_LOCK(sc);
/*
* First clear all other mux bits associated with this
* package pin. This may briefly configure the pin as
* GPIO ... however this should be fine since after
* reset the default GPIO mode is input.
*/
for (cf = sc->soc.func; cf->name != NULL; cf++)
for (cp = cf->pins; cp->name != NULL; cp++) {
if (cp == p)
continue;
if (strcmp(cp->pkg_name, p->pkg_name) != 0)
continue;
if (cp->mux_bits == 0)
continue;
if (pp->aobus == false) {
value = MUX_READ_4(sc, cp->mux_addr);
value &= ~cp->mux_bits;
MUX_WRITE_4(sc, cp->mux_addr, value);
} else {
value = AOMUX_READ_4(sc, cp->mux_addr);
value &= ~cp->mux_bits;
AOMUX_WRITE_4(sc, cp->mux_addr, value);
}
}
/*
* Now set the desired mux bits.
*
* In the case of GPIO there's no bits to set.
*/
if (p->mux_bits != 0) {
if (pp->aobus == false) {
value = MUX_READ_4(sc, p->mux_addr);
value |= p->mux_bits;
MUX_WRITE_4(sc, p->mux_addr, value);
} else {
value = AOMUX_READ_4(sc, p->mux_addr);
value |= p->mux_bits;
AOMUX_WRITE_4(sc, p->mux_addr, value);
}
}
/*
* Finally set the pull mode if it was specified.
*/
switch (pm) {
case aml8726_enable_down_pm:
case aml8726_enable_up_pm:
if (pp->aobus == false) {
value = PUD_READ_4(sc, pp->pull_addr);
if (pm == aml8726_enable_down_pm)
value &= ~pp->pull_bits;
else
value |= pp->pull_bits;
PUD_WRITE_4(sc, pp->pull_addr, value);
} else {
value = AOPUD_READ_4(sc, pp->pull_addr);
if (pm == aml8726_enable_down_pm)
value &= ~(pp->pull_bits << 16);
else
value |= (pp->pull_bits << 16);
AOPUD_WRITE_4(sc, pp->pull_addr, value);
}
/* FALLTHROUGH */
case aml8726_disable_pm:
case aml8726_enable_pm:
if (pp->aobus == false) {
value = PEN_READ_4(sc, pp->pull_addr);
if (pm == aml8726_disable_pm)
value &= ~pp->pull_bits;
else
value |= pp->pull_bits;
PEN_WRITE_4(sc, pp->pull_addr, value);
} else {
value = AOPEN_READ_4(sc, pp->pull_addr);
if (pm == aml8726_disable_pm)
value &= ~pp->pull_bits;
else
value |= pp->pull_bits;
AOPEN_WRITE_4(sc, pp->pull_addr, value);
}
break;
default:
break;
}
AML_PINCTRL_UNLOCK(sc);
}
OF_prop_free(pins);
return (0);
}
static device_method_t aml8726_pinctrl_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aml8726_pinctrl_probe),
DEVMETHOD(device_attach, aml8726_pinctrl_attach),
DEVMETHOD(device_detach, aml8726_pinctrl_detach),
/* fdt_pinctrl interface */
DEVMETHOD(fdt_pinctrl_configure,aml8726_pinctrl_configure_pins),
DEVMETHOD_END
};
static driver_t aml8726_pinctrl_driver = {
"pinctrl",
aml8726_pinctrl_methods,
sizeof(struct aml8726_pinctrl_softc),
};
static devclass_t aml8726_pinctrl_devclass;
EARLY_DRIVER_MODULE(pinctrl, simplebus, aml8726_pinctrl_driver,
aml8726_pinctrl_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_LATE);

File diff suppressed because it is too large Load Diff

View File

@ -1,152 +0,0 @@
/*-
* Copyright 2014 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*/
/*
* Amlogic aml8726 random number generator driver.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <sys/random.h>
#include <machine/bus.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
struct aml8726_rng_softc {
device_t dev;
struct resource *res[1];
struct callout co;
int ticks;
};
static struct resource_spec aml8726_rng_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ -1, 0 }
};
#define AML_RNG_0_REG 0
#define AML_RNG_1_REG 4
#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
static void
aml8726_rng_harvest(void *arg)
{
struct aml8726_rng_softc *sc = arg;
uint32_t rn[2];
rn[0] = CSR_READ_4(sc, AML_RNG_0_REG);
rn[1] = CSR_READ_4(sc, AML_RNG_1_REG);
random_harvest(rn, sizeof(rn), RANDOM_PURE_AML8726);
callout_reset(&sc->co, sc->ticks, aml8726_rng_harvest, sc);
}
static int
aml8726_rng_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-rng"))
return (ENXIO);
device_set_desc(dev, "Amlogic aml8726 RNG");
return (BUS_PROBE_DEFAULT);
}
static int
aml8726_rng_attach(device_t dev)
{
struct aml8726_rng_softc *sc = device_get_softc(dev);
sc->dev = dev;
if (bus_alloc_resources(dev, aml8726_rng_spec, sc->res)) {
device_printf(dev, "can not allocate resources for device\n");
return (ENXIO);
}
/* Install a periodic collector for the RNG */
if (hz > 100)
sc->ticks = hz / 100;
else
sc->ticks = 1;
callout_init(&sc->co, 1);
callout_reset(&sc->co, sc->ticks, aml8726_rng_harvest, sc);
return (0);
}
static int
aml8726_rng_detach(device_t dev)
{
struct aml8726_rng_softc *sc = device_get_softc(dev);
callout_drain(&sc->co);
bus_release_resources(dev, aml8726_rng_spec, sc->res);
return (0);
}
static device_method_t aml8726_rng_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aml8726_rng_probe),
DEVMETHOD(device_attach, aml8726_rng_attach),
DEVMETHOD(device_detach, aml8726_rng_detach),
DEVMETHOD_END
};
static driver_t aml8726_rng_driver = {
"rng",
aml8726_rng_methods,
sizeof(struct aml8726_rng_softc),
};
static devclass_t aml8726_rng_devclass;
DRIVER_MODULE(aml8726_rng, simplebus, aml8726_rng_driver,
aml8726_rng_devclass, 0, 0);
MODULE_DEPEND(aml8726_rng, random, 1, 1, 1);

View File

@ -1,488 +0,0 @@
/*-
* Copyright 2013-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*/
/*
* Amlogic aml8726 RTC driver.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/clock.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <sys/time.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/amlogic/aml8726/aml8726_soc.h>
#include "clock_if.h"
/*
* The RTC initialization various slightly between the different chips.
*
* aml8726-m1 aml8726-m3 aml8726-m6 (and later)
* init-always true true false
* xo-init 0x0004 0x3c0a 0x180a
* gpo-init 0x100000 0x100000 0x500000
*/
struct aml8726_rtc_init {
boolean_t always;
uint16_t xo;
uint32_t gpo;
};
struct aml8726_rtc_softc {
device_t dev;
struct aml8726_rtc_init init;
struct resource * res[2];
struct mtx mtx;
};
static struct resource_spec aml8726_rtc_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ SYS_RES_IRQ, 0, RF_ACTIVE },
{ -1, 0 }
};
#define AML_RTC_LOCK(sc) mtx_lock_spin(&(sc)->mtx)
#define AML_RTC_UNLOCK(sc) mtx_unlock_spin(&(sc)->mtx)
#define AML_RTC_LOCK_INIT(sc) \
mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
"rtc", MTX_SPIN)
#define AML_RTC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx);
#define AML_RTC_0_REG 0
#define AML_RTC_SCLK (1 << 0)
#define AML_RTC_SDI (1 << 2)
#define AML_RTC_SEN (1 << 1)
#define AML_RTC_AS (1 << 17)
#define AML_RTC_ABSY (1 << 22)
#define AML_RTC_IRQ_DIS (1 << 12)
#define AML_RTC_1_REG 4
#define AML_RTC_SDO (1 << 0)
#define AML_RTC_SRDY (1 << 1)
#define AML_RTC_2_REG 8
#define AML_RTC_3_REG 12
#define AML_RTC_MSR_BUSY (1 << 20)
#define AML_RTC_MSR_CA (1 << 17)
#define AML_RTC_MSR_DURATION_EN (1 << 16)
#define AML_RTC_MSR_DURATION_MASK 0xffff
#define AML_RTC_MSR_DURATION_SHIFT 0
#define AML_RTC_4_REG 16
#define AML_RTC_TIME_SREG 0
#define AML_RTC_GPO_SREG 1
#define AML_RTC_GPO_LEVEL (1 << 24)
#define AML_RTC_GPO_BUSY (1 << 23)
#define AML_RTC_GPO_ACTIVE_HIGH (1 << 22)
#define AML_RTC_GPO_CMD_MASK (3 << 20)
#define AML_RTC_GPO_CMD_SHIFT 20
#define AML_RTC_GPO_CMD_NOW (1 << 20)
#define AML_RTC_GPO_CMD_COUNT (2 << 20)
#define AML_RTC_GPO_CMD_PULSE (3 << 20)
#define AML_RTC_GPO_CNT_MASK 0xfffff
#define AML_RTC_GPO_CNT_SHIFT 0
#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val))
#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
#define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \
(BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
static int
aml8726_rtc_start_transfer(struct aml8726_rtc_softc *sc)
{
unsigned i;
/* idle the serial interface */
CSR_WRITE_4(sc, AML_RTC_0_REG, (CSR_READ_4(sc, AML_RTC_0_REG) &
~(AML_RTC_SCLK | AML_RTC_SEN | AML_RTC_SDI)));
CSR_BARRIER(sc, AML_RTC_0_REG);
/* see if it is ready for a new cycle */
for (i = 40; i; i--) {
DELAY(5);
if ( (CSR_READ_4(sc, AML_RTC_1_REG) & AML_RTC_SRDY) )
break;
}
if (i == 0)
return (EIO);
/* start the cycle */
CSR_WRITE_4(sc, AML_RTC_0_REG, (CSR_READ_4(sc, AML_RTC_0_REG) |
AML_RTC_SEN));
return (0);
}
static inline void
aml8726_rtc_sclk_pulse(struct aml8726_rtc_softc *sc)
{
DELAY(5);
CSR_WRITE_4(sc, AML_RTC_0_REG, (CSR_READ_4(sc, AML_RTC_0_REG) |
AML_RTC_SCLK));
CSR_BARRIER(sc, AML_RTC_0_REG);
DELAY(5);
CSR_WRITE_4(sc, AML_RTC_0_REG, (CSR_READ_4(sc, AML_RTC_0_REG) &
~AML_RTC_SCLK));
CSR_BARRIER(sc, AML_RTC_0_REG);
}
static inline void
aml8726_rtc_send_bit(struct aml8726_rtc_softc *sc, unsigned bit)
{
if (bit) {
CSR_WRITE_4(sc, AML_RTC_0_REG, (CSR_READ_4(sc, AML_RTC_0_REG) |
AML_RTC_SDI));
} else {
CSR_WRITE_4(sc, AML_RTC_0_REG, (CSR_READ_4(sc, AML_RTC_0_REG) &
~AML_RTC_SDI));
}
aml8726_rtc_sclk_pulse(sc);
}
static inline void
aml8726_rtc_send_addr(struct aml8726_rtc_softc *sc, u_char addr)
{
unsigned mask;
for (mask = 1 << 3; mask; mask >>= 1) {
if (mask == 1) {
/* final bit indicates read / write mode */
CSR_WRITE_4(sc, AML_RTC_0_REG,
(CSR_READ_4(sc, AML_RTC_0_REG) & ~AML_RTC_SEN));
}
aml8726_rtc_send_bit(sc, (addr & mask));
}
}
static inline void
aml8726_rtc_send_data(struct aml8726_rtc_softc *sc, uint32_t data)
{
unsigned mask;
for (mask = 1U << 31; mask; mask >>= 1)
aml8726_rtc_send_bit(sc, (data & mask));
}
static inline void
aml8726_rtc_recv_data(struct aml8726_rtc_softc *sc, uint32_t *dp)
{
uint32_t data;
unsigned i;
data = 0;
for (i = 0; i < 32; i++) {
aml8726_rtc_sclk_pulse(sc);
data <<= 1;
data |= (CSR_READ_4(sc, AML_RTC_1_REG) & AML_RTC_SDO) ? 1 : 0;
}
*dp = data;
}
static int
aml8726_rtc_sreg_read(struct aml8726_rtc_softc *sc, u_char sreg, uint32_t *val)
{
u_char addr;
int error;
/* read is indicated by lsb = 0 */
addr = (sreg << 1) | 0;
error = aml8726_rtc_start_transfer(sc);
if (error)
return (error);
aml8726_rtc_send_addr(sc, addr);
aml8726_rtc_recv_data(sc, val);
return (0);
}
static int
aml8726_rtc_sreg_write(struct aml8726_rtc_softc *sc, u_char sreg, uint32_t val)
{
u_char addr;
int error;
/* write is indicated by lsb = 1 */
addr = (sreg << 1) | 1;
error = aml8726_rtc_start_transfer(sc);
if (error)
return (error);
aml8726_rtc_send_data(sc, val);
aml8726_rtc_send_addr(sc, addr);
return (0);
}
static int
aml8726_rtc_initialize(struct aml8726_rtc_softc *sc)
{
int error;
unsigned i;
/* idle the serial interface */
CSR_WRITE_4(sc, AML_RTC_0_REG, (CSR_READ_4(sc, AML_RTC_0_REG) &
~(AML_RTC_SCLK | AML_RTC_SEN | AML_RTC_SDI)));
CSR_BARRIER(sc, AML_RTC_0_REG);
/* see if it is ready for a new cycle */
for (i = 40; i; i--) {
DELAY(5);
if ( (CSR_READ_4(sc, AML_RTC_1_REG) & AML_RTC_SRDY) )
break;
}
if (sc->init.always == TRUE || (CSR_READ_4(sc, AML_RTC_1_REG) &
AML_RTC_SRDY) == 0) {
/*
* The RTC has a 16 bit initialization register. The upper
* bits can be written directly. The lower bits are written
* through a shift register.
*/
CSR_WRITE_4(sc, AML_RTC_4_REG, ((sc->init.xo >> 8) & 0xff));
CSR_WRITE_4(sc, AML_RTC_0_REG,
((CSR_READ_4(sc, AML_RTC_0_REG) & 0xffffff) |
((uint32_t)(sc->init.xo & 0xff) << 24) | AML_RTC_AS |
AML_RTC_IRQ_DIS));
while ((CSR_READ_4(sc, AML_RTC_0_REG) & AML_RTC_ABSY) != 0)
cpu_spinwait();
DELAY(2);
error = aml8726_rtc_sreg_write(sc, AML_RTC_GPO_SREG,
sc->init.gpo);
if (error)
return (error);
}
return (0);
}
static int
aml8726_rtc_check_xo(struct aml8726_rtc_softc *sc)
{
uint32_t now, previous;
int i;
/*
* The RTC is driven by a 32.768khz clock meaning it's period
* is roughly 30.5 us. Check that it's working (implying the
* RTC could contain a valid value) by enabling count always
* and seeing if the value changes after 200 us (per RTC User
* Guide ... presumably the extra time is to cover XO startup).
*/
CSR_WRITE_4(sc, AML_RTC_3_REG, (CSR_READ_4(sc, AML_RTC_3_REG) |
AML_RTC_MSR_CA));
previous = CSR_READ_4(sc, AML_RTC_2_REG);
for (i = 0; i < 4; i++) {
DELAY(50);
now = CSR_READ_4(sc, AML_RTC_2_REG);
if (now != previous)
break;
}
CSR_WRITE_4(sc, AML_RTC_3_REG, (CSR_READ_4(sc, AML_RTC_3_REG) &
~AML_RTC_MSR_CA));
if (now == previous)
return (EINVAL);
return (0);
}
static int
aml8726_rtc_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-rtc"))
return (ENXIO);
device_set_desc(dev, "Amlogic aml8726 RTC");
return (BUS_PROBE_DEFAULT);
}
static int
aml8726_rtc_attach(device_t dev)
{
struct aml8726_rtc_softc *sc = device_get_softc(dev);
sc->dev = dev;
switch (aml8726_soc_hw_rev) {
case AML_SOC_HW_REV_M3:
sc->init.always = true;
sc->init.xo = 0x3c0a;
sc->init.gpo = 0x100000;
break;
case AML_SOC_HW_REV_M6:
case AML_SOC_HW_REV_M8:
case AML_SOC_HW_REV_M8B:
sc->init.always = false;
sc->init.xo = 0x180a;
sc->init.gpo = 0x500000;
break;
default:
device_printf(dev, "unsupported SoC\n");
return (ENXIO);
/* NOTREACHED */
}
if (bus_alloc_resources(dev, aml8726_rtc_spec, sc->res)) {
device_printf(dev, "can not allocate resources for device\n");
return (ENXIO);
}
aml8726_rtc_initialize(sc);
if (aml8726_rtc_check_xo(sc) != 0) {
device_printf(dev, "crystal oscillator check failed\n");
bus_release_resources(dev, aml8726_rtc_spec, sc->res);
return (ENXIO);
}
AML_RTC_LOCK_INIT(sc);
clock_register(dev, 1000000);
return (0);
}
static int
aml8726_rtc_detach(device_t dev)
{
return (EBUSY);
}
static int
aml8726_rtc_gettime(device_t dev, struct timespec *ts)
{
struct aml8726_rtc_softc *sc = device_get_softc(dev);
uint32_t sec;
int error;
AML_RTC_LOCK(sc);
error = aml8726_rtc_sreg_read(sc, AML_RTC_TIME_SREG, &sec);
AML_RTC_UNLOCK(sc);
ts->tv_sec = sec;
ts->tv_nsec = 0;
return (error);
}
static int
aml8726_rtc_settime(device_t dev, struct timespec *ts)
{
struct aml8726_rtc_softc *sc = device_get_softc(dev);
uint32_t sec;
int error;
sec = ts->tv_sec;
/* Accuracy is only one second. */
if (ts->tv_nsec >= 500000000)
sec++;
AML_RTC_LOCK(sc);
error = aml8726_rtc_sreg_write(sc, AML_RTC_TIME_SREG, sec);
AML_RTC_UNLOCK(sc);
return (error);
}
static device_method_t aml8726_rtc_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aml8726_rtc_probe),
DEVMETHOD(device_attach, aml8726_rtc_attach),
DEVMETHOD(device_detach, aml8726_rtc_detach),
/* Clock interface */
DEVMETHOD(clock_gettime, aml8726_rtc_gettime),
DEVMETHOD(clock_settime, aml8726_rtc_settime),
DEVMETHOD_END
};
static driver_t aml8726_rtc_driver = {
"rtc",
aml8726_rtc_methods,
sizeof(struct aml8726_rtc_softc),
};
static devclass_t aml8726_rtc_devclass;
DRIVER_MODULE(rtc, simplebus, aml8726_rtc_driver, aml8726_rtc_devclass, 0, 0);

File diff suppressed because it is too large Load Diff

View File

@ -1,223 +0,0 @@
/*-
* Copyright 2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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 _ARM_AMLOGIC_AML8726_SDXC_M8_H
#define _ARM_AMLOGIC_AML8726_SDXC_M8_H
#define AML_SDXC_ALIGN_DMA 4
#define AML_SDXC_MAX_DMA 4096
/*
* Timeouts are in milliseconds
*
* Read and write are per section 4.6.2 of the:
*
* SD Specifications Part 1
* Physical Layer Simplified Specification
* Version 4.10
*/
#define AML_SDXC_CMD_TIMEOUT 50
#define AML_SDXC_READ_TIMEOUT 100
#define AML_SDXC_WRITE_TIMEOUT 500
#define AML_SDXC_MAX_TIMEOUT 5000
#define AML_SDXC_BUSY_POLL_INTVL 1
#define AML_SDXC_BUSY_TIMEOUT 1000
/*
* There's some disagreements between the S805 documentation
* and the Amlogic Linux platform code regarding the exact
* layout of various registers ... when in doubt we follow
* the platform code.
*/
#define AML_SDXC_CMD_ARGUMENT_REG 0
#define AML_SDXC_SEND_REG 4
#define AML_SDXC_SEND_REP_PKG_CNT_MASK (0xffffU << 16)
#define AML_SDXC_SEND_REP_PKG_CNT_SHIFT 16
#define AML_SDXC_SEND_DATA_STOP (1 << 11)
#define AML_SDXC_SEND_DATA_WRITE (1 << 10)
#define AML_SDXC_SEND_RESP_NO_CRC7_CHECK (1 << 9)
#define AML_SDXC_SEND_RESP_136 (1 << 8)
#define AML_SDXC_SEND_CMD_HAS_DATA (1 << 7)
#define AML_SDXC_SEND_CMD_HAS_RESP (1 << 6)
#define AML_SDXC_SEND_INDEX_MASK 0x3f
#define AML_SDXC_SEND_INDEX_SHIFT 0
#define AML_SDXC_CNTRL_REG 8
#define AML_SDXC_CNTRL_TX_ENDIAN_MASK (7 << 29)
#define AML_SDXC_CNTRL_TX_ENDIAN_SHIFT 29
#define AML_SDXC_CNTRL_RX_ENDIAN_MASK (7 << 24)
#define AML_SDXC_CNTRL_RX_ENDIAN_SHIFT 24
#define AML_SDXC_CNTRL_RX_PERIOD_SHIFT 20
#define AML_SDXC_CNTRL_RX_TIMEOUT_SHIFT 13
#define AML_SDXC_CNTRL_PKG_LEN_MASK (0x1ff << 4)
#define AML_SDXC_CNTRL_PKG_LEN_SHIFT 4
#define AML_SDXC_CNTRL_BUS_WIDTH_MASK (3 << 0)
#define AML_SDXC_CNTRL_BUS_WIDTH_1 (0 << 0)
#define AML_SDXC_CNTRL_BUS_WIDTH_4 (1 << 0)
#define AML_SDXC_CNTRL_BUS_WIDTH_8 (2 << 0)
#define AML_SDXC_STATUS_REG 12
#define AML_SDXC_STATUS_TX_CNT_MASK (0x7f << 13)
#define AML_SDXC_STATUS_TX_CNT_SHIFT 13
#define AML_SDXC_STATUS_RX_CNT_MASK (0x7f << 6)
#define AML_SDXC_STATUS_RX_CNT_SHIFT 6
#define AML_SDXC_STATUS_CMD (1 << 5)
#define AML_SDXC_STATUS_DAT3 (1 << 4)
#define AML_SDXC_STATUS_DAT2 (1 << 3)
#define AML_SDXC_STATUS_DAT1 (1 << 2)
#define AML_SDXC_STATUS_DAT0 (1 << 1)
#define AML_SDXC_STATUS_BUSY (1 << 0)
#define AML_SDXC_CLK_CNTRL_REG 16
#define AML_SDXC_CLK_CNTRL_MEM_PWR_MASK (3 << 25)
#define AML_SDXC_CLK_CNTRL_MEM_PWR_OFF (3 << 25)
#define AML_SDXC_CLK_CNTRL_MEM_PWR_ON (0 << 25)
#define AML_SDXC_CLK_CNTRL_CLK_SEL_MASK (3 << 16)
#define AML_SDXC_CLK_CNTRL_CLK_SEL_SHIFT 16
#define AML_SDXC_CLK_CNTRL_CLK_MODULE_EN (1 << 15)
#define AML_SDXC_CLK_CNTRL_SD_CLK_EN (1 << 14)
#define AML_SDXC_CLK_CNTRL_RX_CLK_EN (1 << 13)
#define AML_SDXC_CLK_CNTRL_TX_CLK_EN (1 << 12)
#define AML_SDXC_CLK_CNTRL_CLK_DIV_MASK 0x0fff
#define AML_SDXC_CLK_CNTRL_CLK_DIV_SHIFT 0
#define AML_SDXC_DMA_ADDR_REG 20
#define AML_SDXC_PDMA_REG 24
#define AML_SDXC_PDMA_TX_FILL (1U << 31)
#define AML_SDXC_PDMA_RX_FLUSH_NOW (1 << 30)
#define AML_SDXC_PDMA_RX_FLUSH_MODE_SW (1 << 29)
#define AML_SDXC_PDMA_TX_THOLD_MASK (0x3f << 22)
#define AML_SDXC_PDMA_TX_THOLD_SHIFT 22
#define AML_SDXC_PDMA_RX_THOLD_MASK (0x3f << 15)
#define AML_SDXC_PDMA_RX_THOLD_SHIFT 15
#define AML_SDXC_PDMA_RD_BURST_MASK (0x1f << 10)
#define AML_SDXC_PDMA_RD_BURST_SHIFT 10
#define AML_SDXC_PDMA_WR_BURST_MASK (0x1f << 5)
#define AML_SDXC_PDMA_WR_BURST_SHIFT 5
#define AML_SDXC_PDMA_DMA_URGENT (1 << 4)
#define AML_SDXC_PDMA_RESP_INDEX_MASK (7 << 1)
#define AML_SDXC_PDMA_RESP_INDEX_SHIFT 1
#define AML_SDXC_PDMA_DMA_EN (1 << 0)
#define AML_SDXC_MISC_REG 28
#define AML_SDXC_MISC_TXSTART_THOLD_MASK (7U << 29)
#define AML_SDXC_MISC_TXSTART_THOLD_SHIFT 29
#define AML_SDXC_MISC_MANUAL_STOP_MODE (1 << 28)
#define AML_SDXC_MISC_WCRC_OK_PAT_MASK (7 << 7)
#define AML_SDXC_MISC_WCRC_OK_PAT_SHIFT 7
#define AML_SDXC_MISC_WCRC_ERR_PAT_MASK (7 << 4)
#define AML_SDXC_MISC_WCRC_ERR_PAT_SHIFT 4
#define AML_SDXC_DATA_REG 32
#define AML_SDXC_IRQ_ENABLE_REG 36
#define AML_SDXC_IRQ_ENABLE_TX_FIFO_EMPTY (1 << 13)
#define AML_SDXC_IRQ_ENABLE_RX_FIFO_FULL (1 << 12)
#define AML_SDXC_IRQ_ENABLE_DMA_DONE (1 << 11)
#define AML_SDXC_IRQ_ENABLE_TRANSFER_DONE_OK (1 << 7)
#define AML_SDXC_IRQ_ENABLE_A_PKG_CRC_ERR (1 << 6)
#define AML_SDXC_IRQ_ENABLE_A_PKG_TIMEOUT_ERR (1 << 5)
#define AML_SDXC_IRQ_ENABLE_A_PKG_DONE_OK (1 << 4)
#define AML_SDXC_IRQ_ENABLE_RESP_CRC_ERR (1 << 2)
#define AML_SDXC_IRQ_ENABLE_RESP_TIMEOUT_ERR (1 << 1)
#define AML_SDXC_IRQ_ENABLE_RESP_OK (1 << 0)
#define AML_SDXC_IRQ_ENABLE_STANDARD \
(AML_SDXC_IRQ_ENABLE_TX_FIFO_EMPTY | \
AML_SDXC_IRQ_ENABLE_RX_FIFO_FULL | \
AML_SDXC_IRQ_ENABLE_A_PKG_CRC_ERR | \
AML_SDXC_IRQ_ENABLE_A_PKG_TIMEOUT_ERR | \
AML_SDXC_IRQ_ENABLE_RESP_CRC_ERR | \
AML_SDXC_IRQ_ENABLE_RESP_TIMEOUT_ERR | \
AML_SDXC_IRQ_ENABLE_RESP_OK)
#define AML_SDXC_IRQ_STATUS_REG 40
#define AML_SDXC_IRQ_STATUS_TX_FIFO_EMPTY (1 << 13)
#define AML_SDXC_IRQ_STATUS_RX_FIFO_FULL (1 << 12)
#define AML_SDXC_IRQ_STATUS_DMA_DONE (1 << 11)
#define AML_SDXC_IRQ_STATUS_TRANSFER_DONE_OK (1 << 7)
#define AML_SDXC_IRQ_STATUS_A_PKG_CRC_ERR (1 << 6)
#define AML_SDXC_IRQ_STATUS_A_PKG_TIMEOUT_ERR (1 << 5)
#define AML_SDXC_IRQ_STATUS_A_PKG_DONE_OK (1 << 4)
#define AML_SDXC_IRQ_STATUS_RESP_CRC_ERR (1 << 2)
#define AML_SDXC_IRQ_STATUS_RESP_TIMEOUT_ERR (1 << 1)
#define AML_SDXC_IRQ_STATUS_RESP_OK (1 << 0)
#define AML_SDXC_IRQ_STATUS_CLEAR \
(AML_SDXC_IRQ_STATUS_TX_FIFO_EMPTY | \
AML_SDXC_IRQ_STATUS_RX_FIFO_FULL | \
AML_SDXC_IRQ_STATUS_DMA_DONE | \
AML_SDXC_IRQ_STATUS_TRANSFER_DONE_OK | \
AML_SDXC_IRQ_STATUS_A_PKG_CRC_ERR | \
AML_SDXC_IRQ_STATUS_A_PKG_TIMEOUT_ERR | \
AML_SDXC_IRQ_STATUS_RESP_CRC_ERR | \
AML_SDXC_IRQ_STATUS_RESP_TIMEOUT_ERR | \
AML_SDXC_IRQ_STATUS_RESP_OK)
#define AML_SDXC_SOFT_RESET_REG 44
#define AML_SDXC_SOFT_RESET_DMA (1 << 5)
#define AML_SDXC_SOFT_RESET_TX_PHY (1 << 4)
#define AML_SDXC_SOFT_RESET_RX_PHY (1 << 3)
#define AML_SDXC_SOFT_RESET_TX_FIFO (1 << 2)
#define AML_SDXC_SOFT_RESET_RX_FIFO (1 << 1)
#define AML_SDXC_SOFT_RESET_MAIN (1 << 0)
#define AML_SDXC_SOFT_RESET \
(AML_SDXC_SOFT_RESET_DMA | \
AML_SDXC_SOFT_RESET_TX_FIFO | \
AML_SDXC_SOFT_RESET_RX_FIFO | \
AML_SDXC_SOFT_RESET_MAIN)
#define AML_SDXC_ENH_CNTRL_REG 52
#define AML_SDXC_ENH_CNTRL_TX_EMPTY_THOLD_MASK (0x7f << 25)
#define AML_SDXC_ENH_CNTRL_TX_EMPTY_THOLD_SHIFT 25
#define AML_SDXC_ENH_CNTRL_RX_FULL_THOLD_MASK (0x7f << 18)
#define AML_SDXC_ENH_CNTRL_RX_FULL_THOLD_SHIFT 18
#define AML_SDXC_ENH_CNTRL_SDIO_IRQ_PERIOD_MASK (0xff << 8)
#define AML_SDXC_ENH_CNTRL_SDIO_IRQ_PERIOD_SHIFT 8
#define AML_SDXC_ENH_CNTRL_DMA_NO_WR_RESP_CHECK_M8 (1 << 17)
#define AML_SDXC_ENH_CNTRL_DMA_NO_RD_RESP_CHECK_M8 (1 << 16)
#define AML_SDXC_ENH_CNTRL_RX_TIMEOUT_MASK_M8 (0xff << 0)
#define AML_SDXC_ENH_CNTRL_RX_TIMEOUT_SHIFT_M8 0
#define AML_SDXC_ENH_CNTRL_NO_DMA_CHECK_M8M2 (1 << 2)
#define AML_SDXC_ENH_CNTRL_NO_WR_RESP_CHECK_M8M2 (1 << 1)
#define AML_SDXC_ENH_CNTRL_WR_RESP_MODE_SKIP_M8M2 (1 << 0)
#define AML_SDXC_CLK2_REG 56
#define AML_SDXC_CLK2_SD_PHASE_MASK (0x3ff << 12)
#define AML_SDXC_CLK2_SD_PHASE_SHIFT 12
#define AML_SDXC_CLK2_RX_PHASE_MASK (0x3ff << 0)
#define AML_SDXC_CLK2_RX_PHASE_SHIFT 0
#endif /* _ARM_AMLOGIC_AML8726_SDXC_M8_H */

View File

@ -1,58 +0,0 @@
/*-
* Copyright 2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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 _ARM_AMLOGIC_AML8726_SOC_H
#define _ARM_AMLOGIC_AML8726_SOC_H
#define AML_SOC_AOBUS_BASE_ADDR 0xc8100000
#define AML_SOC_CBUS_BASE_ADDR 0xc1100000
void aml8726_identify_soc(void);
/* cbus */
#define AML_SOC_HW_REV_REG 0x7d4c
#define AML_SOC_HW_REV_UNKNOWN 0xffffffff
#define AML_SOC_HW_REV_M3 0x15
#define AML_SOC_HW_REV_M6 0x16
#define AML_SOC_HW_REV_M6TV 0x17
#define AML_SOC_HW_REV_M6TVL 0x18
#define AML_SOC_HW_REV_M8 0x19
#define AML_SOC_HW_REV_M8B 0x1b
#define AML_SOC_METAL_REV_REG 0x81a8
#define AML_SOC_METAL_REV_UNKNOWN 0xffffffff
#define AML_SOC_M8_METAL_REV_A 0x11111111
#define AML_SOC_M8_METAL_REV_M2_A 0x11111112
#define AML_SOC_M8_METAL_REV_B 0x11111113
#define AML_SOC_M8_METAL_REV_C 0x11111133
#define AML_SOC_M8B_METAL_REV_A 0x11111111
extern uint32_t aml8726_soc_hw_rev;
extern uint32_t aml8726_soc_metal_rev;
#endif /* _ARM_AMLOGIC_AML8726_SOC_H */

View File

@ -1,396 +0,0 @@
/*-
* Copyright 2013-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*
*/
/*
* Amlogic aml8726 timer driver.
*
* 16 bit Timer A is used for the event timer / hard clock.
* 32 bit Timer E is used for the timecounter / DELAY.
*
* The current implementation doesn't use Timers B-D. Another approach is
* to split the timers between the cores implementing per cpu event timers.
*
* The timers all share the MUX register which requires a mutex to serialize
* access. The mutex is also used to avoid potential problems between the
* interrupt handler and timer_start / timer_stop.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/rman.h>
#include <sys/timetc.h>
#include <sys/timeet.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
struct aml8726_timer_softc {
device_t dev;
struct resource * res[2];
struct mtx mtx;
void * ih_cookie;
struct eventtimer et;
uint32_t first_ticks;
uint32_t period_ticks;
struct timecounter tc;
};
static struct resource_spec aml8726_timer_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ SYS_RES_IRQ, 0, RF_ACTIVE }, /* INT_TIMER_A */
{ -1, 0 }
};
/*
* devclass_get_device / device_get_softc could be used
* to dynamically locate this, however the timers are a
* required device which can't be unloaded so there's
* no need for the overhead.
*/
static struct aml8726_timer_softc *aml8726_timer_sc = NULL;
#define AML_TIMER_LOCK(sc) mtx_lock_spin(&(sc)->mtx)
#define AML_TIMER_UNLOCK(sc) mtx_unlock_spin(&(sc)->mtx)
#define AML_TIMER_LOCK_INIT(sc) \
mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
"timer", MTX_SPIN)
#define AML_TIMER_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx);
#define AML_TIMER_MUX_REG 0
#define AML_TIMER_INPUT_1us 0
#define AML_TIMER_INPUT_10us 1
#define AML_TIMER_INPUT_100us 2
#define AML_TIMER_INPUT_1ms 3
#define AML_TIMER_INPUT_MASK 3
#define AML_TIMER_A_INPUT_MASK 3
#define AML_TIMER_A_INPUT_SHIFT 0
#define AML_TIMER_B_INPUT_MASK (3 << 2)
#define AML_TIMER_B_INPUT_SHIFT 2
#define AML_TIMER_C_INPUT_MASK (3 << 4)
#define AML_TIMER_C_INPUT_SHIFT 4
#define AML_TIMER_D_INPUT_MASK (3 << 6)
#define AML_TIMER_D_INPUT_SHIFT 6
#define AML_TIMER_E_INPUT_SYS 0
#define AML_TIMER_E_INPUT_1us 1
#define AML_TIMER_E_INPUT_10us 2
#define AML_TIMER_E_INPUT_100us 3
#define AML_TIMER_E_INPUT_1ms 4
#define AML_TIMER_E_INPUT_MASK (7 << 8)
#define AML_TIMER_E_INPUT_SHIFT 8
#define AML_TIMER_A_PERIODIC (1 << 12)
#define AML_TIMER_B_PERIODIC (1 << 13)
#define AML_TIMER_C_PERIODIC (1 << 14)
#define AML_TIMER_D_PERIODIC (1 << 15)
#define AML_TIMER_A_EN (1 << 16)
#define AML_TIMER_B_EN (1 << 17)
#define AML_TIMER_C_EN (1 << 18)
#define AML_TIMER_D_EN (1 << 19)
#define AML_TIMER_E_EN (1 << 20)
#define AML_TIMER_A_REG 4
#define AML_TIMER_B_REG 8
#define AML_TIMER_C_REG 12
#define AML_TIMER_D_REG 16
#define AML_TIMER_E_REG 20
#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val))
#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
static unsigned
aml8726_get_timecount(struct timecounter *tc)
{
struct aml8726_timer_softc *sc =
(struct aml8726_timer_softc *)tc->tc_priv;
return CSR_READ_4(sc, AML_TIMER_E_REG);
}
static int
aml8726_hardclock(void *arg)
{
struct aml8726_timer_softc *sc = (struct aml8726_timer_softc *)arg;
AML_TIMER_LOCK(sc);
if (sc->first_ticks != 0 && sc->period_ticks != 0) {
sc->first_ticks = 0;
CSR_WRITE_4(sc, AML_TIMER_A_REG, sc->period_ticks);
CSR_WRITE_4(sc, AML_TIMER_MUX_REG,
(CSR_READ_4(sc, AML_TIMER_MUX_REG) |
AML_TIMER_A_PERIODIC | AML_TIMER_A_EN));
}
AML_TIMER_UNLOCK(sc);
if (sc->et.et_active)
sc->et.et_event_cb(&sc->et, sc->et.et_arg);
return (FILTER_HANDLED);
}
static int
aml8726_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
{
struct aml8726_timer_softc *sc =
(struct aml8726_timer_softc *)et->et_priv;
uint32_t first_ticks;
uint32_t period_ticks;
uint32_t periodic;
uint32_t ticks;
first_ticks = (first * et->et_frequency) / SBT_1S;
period_ticks = (period * et->et_frequency) / SBT_1S;
if (first_ticks != 0) {
ticks = first_ticks;
periodic = 0;
} else {
ticks = period_ticks;
periodic = AML_TIMER_A_PERIODIC;
}
if (ticks == 0)
return (EINVAL);
AML_TIMER_LOCK(sc);
sc->first_ticks = first_ticks;
sc->period_ticks = period_ticks;
CSR_WRITE_4(sc, AML_TIMER_A_REG, ticks);
CSR_WRITE_4(sc, AML_TIMER_MUX_REG,
((CSR_READ_4(sc, AML_TIMER_MUX_REG) & ~AML_TIMER_A_PERIODIC) |
AML_TIMER_A_EN | periodic));
AML_TIMER_UNLOCK(sc);
return (0);
}
static int
aml8726_timer_stop(struct eventtimer *et)
{
struct aml8726_timer_softc *sc =
(struct aml8726_timer_softc *)et->et_priv;
AML_TIMER_LOCK(sc);
CSR_WRITE_4(sc, AML_TIMER_MUX_REG,
(CSR_READ_4(sc, AML_TIMER_MUX_REG) & ~AML_TIMER_A_EN));
AML_TIMER_UNLOCK(sc);
return (0);
}
static int
aml8726_timer_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "amlogic,meson6-timer"))
return (ENXIO);
device_set_desc(dev, "Amlogic aml8726 timer");
return (BUS_PROBE_DEFAULT);
}
static int
aml8726_timer_attach(device_t dev)
{
struct aml8726_timer_softc *sc = device_get_softc(dev);
/* There should be exactly one instance. */
if (aml8726_timer_sc != NULL)
return (ENXIO);
sc->dev = dev;
if (bus_alloc_resources(dev, aml8726_timer_spec, sc->res)) {
device_printf(dev, "can not allocate resources for device\n");
return (ENXIO);
}
/*
* Disable the timers, select the input for each timer,
* clear timer E, and then enable timer E.
*/
CSR_WRITE_4(sc, AML_TIMER_MUX_REG,
((CSR_READ_4(sc, AML_TIMER_MUX_REG) &
~(AML_TIMER_A_EN | AML_TIMER_A_INPUT_MASK |
AML_TIMER_E_EN | AML_TIMER_E_INPUT_MASK)) |
(AML_TIMER_INPUT_1us << AML_TIMER_A_INPUT_SHIFT) |
(AML_TIMER_E_INPUT_1us << AML_TIMER_E_INPUT_SHIFT)));
CSR_WRITE_4(sc, AML_TIMER_E_REG, 0);
CSR_WRITE_4(sc, AML_TIMER_MUX_REG,
(CSR_READ_4(sc, AML_TIMER_MUX_REG) | AML_TIMER_E_EN));
/*
* Initialize the mutex prior to installing the interrupt handler
* in case of a spurious interrupt.
*/
AML_TIMER_LOCK_INIT(sc);
if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_CLK,
aml8726_hardclock, NULL, sc, &sc->ih_cookie)) {
device_printf(dev, "could not setup interrupt handler\n");
bus_release_resources(dev, aml8726_timer_spec, sc->res);
AML_TIMER_LOCK_DESTROY(sc);
return (ENXIO);
}
aml8726_timer_sc = sc;
sc->et.et_name = "aml8726 timer A";
sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT;
sc->et.et_frequency = 1000000;
sc->et.et_quality = 1000;
sc->et.et_min_period = (0x00000002LLU * SBT_1S) / sc->et.et_frequency;
sc->et.et_max_period = (0x0000fffeLLU * SBT_1S) / sc->et.et_frequency;
sc->et.et_start = aml8726_timer_start;
sc->et.et_stop = aml8726_timer_stop;
sc->et.et_priv = sc;
et_register(&sc->et);
sc->tc.tc_get_timecount = aml8726_get_timecount;
sc->tc.tc_name = "aml8726 timer E";
sc->tc.tc_frequency = 1000000;
sc->tc.tc_counter_mask = ~0u;
sc->tc.tc_quality = 1000;
sc->tc.tc_priv = sc;
tc_init(&sc->tc);
return (0);
}
static int
aml8726_timer_detach(device_t dev)
{
return (EBUSY);
}
static device_method_t aml8726_timer_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aml8726_timer_probe),
DEVMETHOD(device_attach, aml8726_timer_attach),
DEVMETHOD(device_detach, aml8726_timer_detach),
DEVMETHOD_END
};
static driver_t aml8726_timer_driver = {
"timer",
aml8726_timer_methods,
sizeof(struct aml8726_timer_softc),
};
static devclass_t aml8726_timer_devclass;
EARLY_DRIVER_MODULE(timer, simplebus, aml8726_timer_driver,
aml8726_timer_devclass, 0, 0, BUS_PASS_TIMER);
void
DELAY(int usec)
{
uint32_t counter;
uint32_t delta, now, previous, remaining;
/* Timer has not yet been initialized */
if (aml8726_timer_sc == NULL) {
for (; usec > 0; usec--)
for (counter = 200; counter > 0; counter--) {
/* Prevent gcc from optimizing out the loop */
cpufunc_nullop();
}
return;
}
TSENTER();
/*
* Some of the other timers in the source tree do this calculation as:
*
* usec * ((sc->tc.tc_frequency / 1000000) + 1)
*
* which gives a fairly pessimistic result when tc_frequency is an exact
* multiple of 1000000. Given the data type and typical values for
* tc_frequency adding 999999 shouldn't overflow.
*/
remaining = usec * ((aml8726_timer_sc->tc.tc_frequency + 999999) /
1000000);
/*
* We add one since the first iteration may catch the counter just
* as it is changing.
*/
remaining += 1;
previous = aml8726_get_timecount(&aml8726_timer_sc->tc);
for ( ; ; ) {
now = aml8726_get_timecount(&aml8726_timer_sc->tc);
/*
* If the timer has rolled over, then we have the case:
*
* if (previous > now) {
* delta = (0 - previous) + now
* }
*
* which is really no different then the normal case.
* Both cases are simply:
*
* delta = now - previous.
*/
delta = now - previous;
if (delta >= remaining)
break;
previous = now;
remaining -= delta;
}
TSEXIT();
}

View File

@ -1,107 +0,0 @@
/*-
* Copyright 2013-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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 _ARM_AMLOGIC_AML8726_UART_H
#define _ARM_AMLOGIC_AML8726_UART_H
#define AML_UART_WFIFO_REG 0
#define AML_UART_RFIFO_REG 4
#define AML_UART_CONTROL_REG 8
#define AML_UART_CONTROL_TX_INT_EN (1 << 28)
#define AML_UART_CONTROL_RX_INT_EN (1 << 27)
#define AML_UART_CONTROL_CLR_ERR (1 << 24)
#define AML_UART_CONTROL_RX_RST (1 << 23)
#define AML_UART_CONTROL_TX_RST (1 << 22)
#define AML_UART_CONTROL_DB_MASK (3 << 20)
#define AML_UART_CONTROL_8_DB (0 << 20)
#define AML_UART_CONTROL_7_DB (1 << 20)
#define AML_UART_CONTROL_6_DB (2 << 20)
#define AML_UART_CONTROL_5_DB (3 << 20)
#define AML_UART_CONTROL_P_MASK (3 << 18)
#define AML_UART_CONTROL_P_EN (1 << 19)
#define AML_UART_CONTROL_P_EVEN (0 << 18)
#define AML_UART_CONTROL_P_ODD (1 << 18)
#define AML_UART_CONTROL_SB_MASK (3 << 16)
#define AML_UART_CONTROL_1_SB (0 << 16)
#define AML_UART_CONTROL_2_SB (1 << 16)
#define AML_UART_CONTROL_TWO_WIRE_EN (1 << 15)
#define AML_UART_CONTROL_RX_EN (1 << 13)
#define AML_UART_CONTROL_TX_EN (1 << 12)
#define AML_UART_CONTROL_BAUD_MASK 0xfff
#define AML_UART_CONTROL_BAUD_WIDTH 12
#define AML_UART_STATUS_REG 12
#define AML_UART_STATUS_RECV_BUSY (1 << 26)
#define AML_UART_STATUS_XMIT_BUSY (1 << 25)
#define AML_UART_STATUS_RX_FIFO_OVERFLOW (1 << 24)
#define AML_UART_STATUS_TX_FIFO_EMPTY (1 << 22)
#define AML_UART_STATUS_TX_FIFO_FULL (1 << 21)
#define AML_UART_STATUS_RX_FIFO_EMPTY (1 << 20)
#define AML_UART_STATUS_RX_FIFO_FULL (1 << 19)
#define AML_UART_STATUS_TX_FIFO_WRITE_ERR (1 << 18)
#define AML_UART_STATUS_FRAME_ERR (1 << 17)
#define AML_UART_STATUS_PARITY_ERR (1 << 16)
#define AML_UART_STATUS_TX_FIFO_CNT_MASK (0x7f << 8)
#define AML_UART_STATUS_TX_FIFO_CNT_SHIFT 8
#define AML_UART_STATUS_RX_FIFO_CNT_MASK (0x7f << 0)
#define AML_UART_STATUS_RX_FIFO_CNT_SHIFT 0
#define AML_UART_MISC_REG 16
#define AML_UART_MISC_OLD_RX_BAUD (1 << 30)
#define AML_UART_MISC_BAUD_EXT_MASK (0xf << 20)
#define AML_UART_MISC_BAUD_EXT_SHIFT 20
/*
* The documentation appears to be incorrect as the
* IRQ is actually generated when TX FIFO count is
* * equal to * or less than the selected threshold.
*/
#define AML_UART_MISC_XMIT_IRQ_CNT_MASK (0xff << 8)
#define AML_UART_MISC_XMIT_IRQ_CNT_SHIFT 8
/*
* The documentation appears to be incorrect as the
* IRQ is actually generated when RX FIFO count is
* * equal to * or greater than the selected threshold.
*/
#define AML_UART_MISC_RECV_IRQ_CNT_MASK 0xff
#define AML_UART_MISC_RECV_IRQ_CNT_SHIFT 0
/*
* The new baud rate register is available on the
* aml8726-m6 and later.
*/
#define AML_UART_NEW_BAUD_REG 20
#define AML_UART_NEW_BAUD_USE_XTAL_CLK (1 << 24)
#define AML_UART_NEW_BAUD_RATE_EN (1 << 23)
#define AML_UART_NEW_BAUD_RATE_MASK (0x7fffff << 0)
#define AML_UART_NEW_BAUD_RATE_SHIFT 0
#endif /* _ARM_AMLOGIC_AML8726_UART_H */

View File

@ -1,180 +0,0 @@
/*-
* Copyright 2013-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*/
/*
* Amlogic aml8726 UART console driver.
*
* This is only necessary to use when debugging early boot code.
* The standard uart driver is available for use later in the boot.
*
* It's assumed the SoC uart is mapped into AML_UART_KVM_BASE meaning
* when using EARLY_PRINTF you'll need to define SOCDEV_VA to be
* 0xd8100000 and SOCDEV_PA to be 0xc8100000 in your config file.
*/
#include "opt_global.h"
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/cons.h>
#include <sys/consio.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <arm/amlogic/aml8726/aml8726_machdep.h>
#include <arm/amlogic/aml8726/aml8726_uart.h>
#define AML_UART_KVM_BASE (aml8726_aobus_kva_base + 0x130 * 4)
static uint32_t
ub_getreg(uint32_t off)
{
return *((volatile uint32_t *)(AML_UART_KVM_BASE + off));
}
static void
ub_setreg(uint32_t off, uint32_t val)
{
*((volatile uint32_t *)(AML_UART_KVM_BASE + off)) = val;
}
static void
uart_cnprobe(struct consdev *cp)
{
sprintf(cp->cn_name, "uart");
cp->cn_pri = CN_REMOTE;
}
static void
uart_cngrab(struct consdev *cp)
{
}
static void
uart_cnungrab(struct consdev *cp)
{
}
static void
uart_cninit(struct consdev *cp)
{
uint32_t cr;
uint32_t mr;
#ifdef EARLY_PRINTF
if (early_putc != NULL) {
printf("Early printf yielding control to the real console.\n");
early_putc = NULL;
}
/*
* Give pending characters a chance to drain.
*/
DELAY(4000);
#endif
cr = ub_getreg(AML_UART_CONTROL_REG);
/* Disable all interrupt sources. */
cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN);
/* Reset the transmitter and receiver. */
cr |= (AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
/* Use two wire mode. */
cr |= AML_UART_CONTROL_TWO_WIRE_EN;
/* Enable the transmitter and receiver. */
cr |= (AML_UART_CONTROL_TX_EN | AML_UART_CONTROL_RX_EN);
ub_setreg(AML_UART_CONTROL_REG, cr);
/* Clear RX FIFO level for generating interrupts. */
mr = ub_getreg(AML_UART_MISC_REG);
mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK;
ub_setreg(AML_UART_MISC_REG, mr);
/* Ensure the reset bits are clear. */
cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
ub_setreg(AML_UART_CONTROL_REG, cr);
}
static void
uart_cnterm(struct consdev * cp)
{
}
static void
uart_cnputc(struct consdev *cp, int c)
{
while ((ub_getreg(AML_UART_STATUS_REG) &
AML_UART_STATUS_TX_FIFO_FULL) != 0)
cpu_spinwait();
ub_setreg(AML_UART_WFIFO_REG, c);
}
static int
uart_cngetc(struct consdev * cp)
{
int c;
if ((ub_getreg(AML_UART_STATUS_REG) &
AML_UART_STATUS_RX_FIFO_EMPTY) != 0)
return (-1);
c = ub_getreg(AML_UART_RFIFO_REG) & 0xff;
return (c);
}
CONSOLE_DRIVER(uart);
#ifdef EARLY_PRINTF
#if !(defined(SOCDEV_PA) && defined(SOCDEV_VA))
#error SOCDEV_PA and SOCDEV_VA must be defined.
#endif
static void
eputc(int c)
{
if (c == '\n')
eputc('\r');
uart_cnputc(NULL, c);
}
early_putc_t *early_putc = eputc;
#endif

View File

@ -1,428 +0,0 @@
/*-
* Copyright 2014-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*/
/*
* Amlogic aml8726-m3 USB physical layer driver.
*
* Both USB physical interfaces share the same configuration register.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <sys/gpio.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "gpio_if.h"
struct aml8726_usb_phy_gpio {
device_t dev;
uint32_t pin;
uint32_t pol;
};
struct aml8726_usb_phy_softc {
device_t dev;
struct resource *res[1];
uint32_t npwr_en;
struct aml8726_usb_phy_gpio *pwr_en;
};
static struct resource_spec aml8726_usb_phy_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ -1, 0 }
};
#define AML_USB_PHY_CFG_REG 0
#define AML_USB_PHY_CFG_A_CLK_DETECTED (1U << 31)
#define AML_USB_PHY_CFG_CLK_DIV_MASK (0x7f << 24)
#define AML_USB_PHY_CFG_CLK_DIV_SHIFT 24
#define AML_USB_PHY_CFG_B_CLK_DETECTED (1 << 22)
#define AML_USB_PHY_CFG_A_PLL_RST (1 << 19)
#define AML_USB_PHY_CFG_A_PHYS_RST (1 << 18)
#define AML_USB_PHY_CFG_A_RST (1 << 17)
#define AML_USB_PHY_CFG_B_PLL_RST (1 << 13)
#define AML_USB_PHY_CFG_B_PHYS_RST (1 << 12)
#define AML_USB_PHY_CFG_B_RST (1 << 11)
#define AML_USB_PHY_CFG_CLK_EN (1 << 8)
#define AML_USB_PHY_CFG_CLK_SEL_MASK (7 << 5)
#define AML_USB_PHY_CFG_CLK_SEL_XTAL (0 << 5)
#define AML_USB_PHY_CFG_CLK_SEL_XTAL_DIV2 (1 << 5)
#define AML_USB_PHY_CFG_B_POR (1 << 1)
#define AML_USB_PHY_CFG_A_POR (1 << 0)
#define AML_USB_PHY_CFG_CLK_DETECTED \
(AML_USB_PHY_CFG_A_CLK_DETECTED | AML_USB_PHY_CFG_B_CLK_DETECTED)
#define AML_USB_PHY_MISC_A_REG 12
#define AML_USB_PHY_MISC_B_REG 16
#define AML_USB_PHY_MISC_ID_OVERIDE_EN (1 << 23)
#define AML_USB_PHY_MISC_ID_OVERIDE_DEVICE (1 << 22)
#define AML_USB_PHY_MISC_ID_OVERIDE_HOST (0 << 22)
#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val))
#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
#define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \
(BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
#define PIN_ON_FLAG(pol) ((pol) == 0 ? \
GPIO_PIN_LOW : GPIO_PIN_HIGH)
#define PIN_OFF_FLAG(pol) ((pol) == 0 ? \
GPIO_PIN_HIGH : GPIO_PIN_LOW)
static int
aml8726_usb_phy_mode(const char *dwcotg_path, uint32_t *mode)
{
char *usb_mode;
phandle_t node;
ssize_t len;
if ((node = OF_finddevice(dwcotg_path)) == -1)
return (ENXIO);
if (fdt_is_compatible_strict(node, "synopsys,designware-hs-otg2") == 0)
return (ENXIO);
*mode = 0;
len = OF_getprop_alloc(node, "dr_mode",
(void **)&usb_mode);
if (len <= 0)
return (0);
if (strcasecmp(usb_mode, "host") == 0) {
*mode = AML_USB_PHY_MISC_ID_OVERIDE_EN |
AML_USB_PHY_MISC_ID_OVERIDE_HOST;
} else if (strcasecmp(usb_mode, "peripheral") == 0) {
*mode = AML_USB_PHY_MISC_ID_OVERIDE_EN |
AML_USB_PHY_MISC_ID_OVERIDE_DEVICE;
}
OF_prop_free(usb_mode);
return (0);
}
static int
aml8726_usb_phy_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-m3-usb-phy"))
return (ENXIO);
device_set_desc(dev, "Amlogic aml8726-m3 USB PHY");
return (BUS_PROBE_DEFAULT);
}
static int
aml8726_usb_phy_attach(device_t dev)
{
struct aml8726_usb_phy_softc *sc = device_get_softc(dev);
int err;
int npwr_en;
pcell_t *prop;
phandle_t node;
ssize_t len;
uint32_t div;
uint32_t i;
uint32_t mode_a;
uint32_t mode_b;
uint32_t value;
sc->dev = dev;
if (aml8726_usb_phy_mode("/soc/usb@c9040000", &mode_a) != 0) {
device_printf(dev, "missing usb@c9040000 node in FDT\n");
return (ENXIO);
}
if (aml8726_usb_phy_mode("/soc/usb@c90c0000", &mode_b) != 0) {
device_printf(dev, "missing usb@c90c0000 node in FDT\n");
return (ENXIO);
}
if (bus_alloc_resources(dev, aml8726_usb_phy_spec, sc->res)) {
device_printf(dev, "can not allocate resources for device\n");
return (ENXIO);
}
node = ofw_bus_get_node(dev);
err = 0;
len = OF_getencprop_alloc_multi(node, "usb-pwr-en",
3 * sizeof(pcell_t), (void **)&prop);
npwr_en = (len > 0) ? len : 0;
sc->npwr_en = 0;
sc->pwr_en = (struct aml8726_usb_phy_gpio *)
malloc(npwr_en * sizeof (*sc->pwr_en), M_DEVBUF, M_WAITOK);
for (i = 0; i < npwr_en; i++) {
sc->pwr_en[i].dev = OF_device_from_xref(prop[i * 3]);
sc->pwr_en[i].pin = prop[i * 3 + 1];
sc->pwr_en[i].pol = prop[i * 3 + 2];
if (sc->pwr_en[i].dev == NULL) {
err = 1;
break;
}
}
OF_prop_free(prop);
if (err) {
device_printf(dev, "unable to parse gpio\n");
goto fail;
}
/* Turn on power by setting pin and then enabling output driver. */
for (i = 0; i < npwr_en; i++) {
if (GPIO_PIN_SET(sc->pwr_en[i].dev, sc->pwr_en[i].pin,
PIN_ON_FLAG(sc->pwr_en[i].pol)) != 0 ||
GPIO_PIN_SETFLAGS(sc->pwr_en[i].dev, sc->pwr_en[i].pin,
GPIO_PIN_OUTPUT) != 0) {
device_printf(dev,
"could not use gpio to control power\n");
goto fail;
}
sc->npwr_en++;
}
/*
* Configure the clock source and divider.
*/
div = 2;
value = CSR_READ_4(sc, AML_USB_PHY_CFG_REG);
value &= ~(AML_USB_PHY_CFG_CLK_DIV_MASK | AML_USB_PHY_CFG_CLK_SEL_MASK);
value &= ~(AML_USB_PHY_CFG_A_RST | AML_USB_PHY_CFG_B_RST);
value &= ~(AML_USB_PHY_CFG_A_PLL_RST | AML_USB_PHY_CFG_B_PLL_RST);
value &= ~(AML_USB_PHY_CFG_A_PHYS_RST | AML_USB_PHY_CFG_B_PHYS_RST);
value &= ~(AML_USB_PHY_CFG_A_POR | AML_USB_PHY_CFG_B_POR);
value |= AML_USB_PHY_CFG_CLK_SEL_XTAL;
value |= ((div - 1) << AML_USB_PHY_CFG_CLK_DIV_SHIFT) &
AML_USB_PHY_CFG_CLK_DIV_MASK;
value |= AML_USB_PHY_CFG_CLK_EN;
CSR_WRITE_4(sc, AML_USB_PHY_CFG_REG, value);
CSR_BARRIER(sc, AML_USB_PHY_CFG_REG);
/*
* Issue the reset sequence.
*/
value |= (AML_USB_PHY_CFG_A_RST | AML_USB_PHY_CFG_B_RST);
CSR_WRITE_4(sc, AML_USB_PHY_CFG_REG, value);
CSR_BARRIER(sc, AML_USB_PHY_CFG_REG);
DELAY(200);
value &= ~(AML_USB_PHY_CFG_A_RST | AML_USB_PHY_CFG_B_RST);
CSR_WRITE_4(sc, AML_USB_PHY_CFG_REG, value);
CSR_BARRIER(sc, AML_USB_PHY_CFG_REG);
DELAY(200);
value |= (AML_USB_PHY_CFG_A_PLL_RST | AML_USB_PHY_CFG_B_PLL_RST);
CSR_WRITE_4(sc, AML_USB_PHY_CFG_REG, value);
CSR_BARRIER(sc, AML_USB_PHY_CFG_REG);
DELAY(200);
value &= ~(AML_USB_PHY_CFG_A_PLL_RST | AML_USB_PHY_CFG_B_PLL_RST);
CSR_WRITE_4(sc, AML_USB_PHY_CFG_REG, value);
CSR_BARRIER(sc, AML_USB_PHY_CFG_REG);
DELAY(200);
value |= (AML_USB_PHY_CFG_A_PHYS_RST | AML_USB_PHY_CFG_B_PHYS_RST);
CSR_WRITE_4(sc, AML_USB_PHY_CFG_REG, value);
CSR_BARRIER(sc, AML_USB_PHY_CFG_REG);
DELAY(200);
value &= ~(AML_USB_PHY_CFG_A_PHYS_RST | AML_USB_PHY_CFG_B_PHYS_RST);
CSR_WRITE_4(sc, AML_USB_PHY_CFG_REG, value);
CSR_BARRIER(sc, AML_USB_PHY_CFG_REG);
DELAY(200);
value |= (AML_USB_PHY_CFG_A_POR | AML_USB_PHY_CFG_B_POR);
CSR_WRITE_4(sc, AML_USB_PHY_CFG_REG, value);
CSR_BARRIER(sc, AML_USB_PHY_CFG_REG);
DELAY(200);
/*
* Enable by clearing the power on reset.
*/
value &= ~(AML_USB_PHY_CFG_A_POR | AML_USB_PHY_CFG_B_POR);
CSR_WRITE_4(sc, AML_USB_PHY_CFG_REG, value);
CSR_BARRIER(sc, AML_USB_PHY_CFG_REG);
DELAY(200);
/*
* Check if the clock was detected.
*/
value = CSR_READ_4(sc, AML_USB_PHY_CFG_REG);
if ((value & AML_USB_PHY_CFG_CLK_DETECTED) !=
AML_USB_PHY_CFG_CLK_DETECTED)
device_printf(dev, "PHY Clock not detected\n");
/*
* Configure the mode for each port.
*/
value = CSR_READ_4(sc, AML_USB_PHY_MISC_A_REG);
value &= ~(AML_USB_PHY_MISC_ID_OVERIDE_EN |
AML_USB_PHY_MISC_ID_OVERIDE_DEVICE |
AML_USB_PHY_MISC_ID_OVERIDE_HOST);
value |= mode_a;
CSR_WRITE_4(sc, AML_USB_PHY_MISC_A_REG, value);
value = CSR_READ_4(sc, AML_USB_PHY_MISC_B_REG);
value &= ~(AML_USB_PHY_MISC_ID_OVERIDE_EN |
AML_USB_PHY_MISC_ID_OVERIDE_DEVICE |
AML_USB_PHY_MISC_ID_OVERIDE_HOST);
value |= mode_b;
CSR_WRITE_4(sc, AML_USB_PHY_MISC_B_REG, value);
CSR_BARRIER(sc, AML_USB_PHY_MISC_B_REG);
return (0);
fail:
/* In the event of problems attempt to turn things back off. */
i = sc->npwr_en;
while (i-- != 0) {
GPIO_PIN_SET(sc->pwr_en[i].dev, sc->pwr_en[i].pin,
PIN_OFF_FLAG(sc->pwr_en[i].pol));
}
free (sc->pwr_en, M_DEVBUF);
sc->pwr_en = NULL;
bus_release_resources(dev, aml8726_usb_phy_spec, sc->res);
return (ENXIO);
}
static int
aml8726_usb_phy_detach(device_t dev)
{
struct aml8726_usb_phy_softc *sc = device_get_softc(dev);
uint32_t i;
uint32_t value;
/*
* Disable by issuing a power on reset.
*/
value = CSR_READ_4(sc, AML_USB_PHY_CFG_REG);
value |= (AML_USB_PHY_CFG_A_POR | AML_USB_PHY_CFG_B_POR);
CSR_WRITE_4(sc, AML_USB_PHY_CFG_REG, value);
CSR_BARRIER(sc, AML_USB_PHY_CFG_REG);
/* Turn off power */
i = sc->npwr_en;
while (i-- != 0) {
(void)GPIO_PIN_SET(sc->pwr_en[i].dev, sc->pwr_en[i].pin,
PIN_OFF_FLAG(sc->pwr_en[i].pol));
}
free (sc->pwr_en, M_DEVBUF);
sc->pwr_en = NULL;
bus_release_resources(dev, aml8726_usb_phy_spec, sc->res);
return (0);
}
static device_method_t aml8726_usb_phy_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aml8726_usb_phy_probe),
DEVMETHOD(device_attach, aml8726_usb_phy_attach),
DEVMETHOD(device_detach, aml8726_usb_phy_detach),
DEVMETHOD_END
};
static driver_t aml8726_usb_phy_driver = {
"usbphy",
aml8726_usb_phy_methods,
sizeof(struct aml8726_usb_phy_softc),
};
static devclass_t aml8726_usb_phy_devclass;
DRIVER_MODULE(aml8726_m3usbphy, simplebus, aml8726_usb_phy_driver,
aml8726_usb_phy_devclass, 0, 0);
MODULE_DEPEND(aml8726_m3usbphy, aml8726_gpio, 1, 1, 1);

View File

@ -1,418 +0,0 @@
/*-
* Copyright 2014-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*/
/*
* Amlogic aml8726-m6 (and later) USB physical layer driver.
*
* Each USB physical interface has a dedicated register block.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <sys/gpio.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/amlogic/aml8726/aml8726_soc.h>
#include "gpio_if.h"
struct aml8726_usb_phy_gpio {
device_t dev;
uint32_t pin;
uint32_t pol;
};
struct aml8726_usb_phy_softc {
device_t dev;
struct resource *res[1];
uint32_t npwr_en;
struct aml8726_usb_phy_gpio *pwr_en;
boolean_t force_aca;
struct aml8726_usb_phy_gpio hub_rst;
};
static struct resource_spec aml8726_usb_phy_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ -1, 0 }
};
#define AML_USB_PHY_CFG_REG 0
#define AML_USB_PHY_CFG_CLK_SEL_32K_ALT (1 << 15)
#define AML_USB_PHY_CFG_CLK_DIV_MASK (0x7f << 4)
#define AML_USB_PHY_CFG_CLK_DIV_SHIFT 4
#define AML_USB_PHY_CFG_CLK_SEL_MASK (7 << 1)
#define AML_USB_PHY_CFG_CLK_SEL_XTAL (0 << 1)
#define AML_USB_PHY_CFG_CLK_SEL_XTAL_DIV2 (1 << 1)
#define AML_USB_PHY_CFG_CLK_EN (1 << 0)
#define AML_USB_PHY_CTRL_REG 4
#define AML_USB_PHY_CTRL_FSEL_MASK (7 << 22)
#define AML_USB_PHY_CTRL_FSEL_12M (2 << 22)
#define AML_USB_PHY_CTRL_FSEL_24M (5 << 22)
#define AML_USB_PHY_CTRL_POR (1 << 15)
#define AML_USB_PHY_CTRL_CLK_DETECTED (1 << 8)
#define AML_USB_PHY_ADP_BC_REG 12
#define AML_USB_PHY_ADP_BC_ACA_FLOATING (1 << 26)
#define AML_USB_PHY_ADP_BC_ACA_EN (1 << 16)
#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val))
#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
#define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \
(BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
#define PIN_ON_FLAG(pol) ((pol) == 0 ? \
GPIO_PIN_LOW : GPIO_PIN_HIGH)
#define PIN_OFF_FLAG(pol) ((pol) == 0 ? \
GPIO_PIN_HIGH : GPIO_PIN_LOW)
static int
aml8726_usb_phy_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-m6-usb-phy") &&
!ofw_bus_is_compatible(dev, "amlogic,aml8726-m8-usb-phy"))
return (ENXIO);
switch (aml8726_soc_hw_rev) {
case AML_SOC_HW_REV_M8:
case AML_SOC_HW_REV_M8B:
device_set_desc(dev, "Amlogic aml8726-m8 USB PHY");
break;
default:
device_set_desc(dev, "Amlogic aml8726-m6 USB PHY");
break;
}
return (BUS_PROBE_DEFAULT);
}
static int
aml8726_usb_phy_attach(device_t dev)
{
struct aml8726_usb_phy_softc *sc = device_get_softc(dev);
char *force_aca;
int err;
int npwr_en;
pcell_t *prop;
phandle_t node;
ssize_t len;
uint32_t div;
uint32_t i;
uint32_t value;
sc->dev = dev;
if (bus_alloc_resources(dev, aml8726_usb_phy_spec, sc->res)) {
device_printf(dev, "can not allocate resources for device\n");
return (ENXIO);
}
node = ofw_bus_get_node(dev);
len = OF_getprop_alloc(node, "force-aca",
(void **)&force_aca);
sc->force_aca = FALSE;
if (len > 0) {
if (strncmp(force_aca, "true", len) == 0)
sc->force_aca = TRUE;
}
OF_prop_free(force_aca);
err = 0;
len = OF_getencprop_alloc_multi(node, "usb-pwr-en",
3 * sizeof(pcell_t), (void **)&prop);
npwr_en = (len > 0) ? len : 0;
sc->npwr_en = 0;
sc->pwr_en = (struct aml8726_usb_phy_gpio *)
malloc(npwr_en * sizeof (*sc->pwr_en), M_DEVBUF, M_WAITOK);
for (i = 0; i < npwr_en; i++) {
sc->pwr_en[i].dev = OF_device_from_xref(prop[i * 3]);
sc->pwr_en[i].pin = prop[i * 3 + 1];
sc->pwr_en[i].pol = prop[i * 3 + 2];
if (sc->pwr_en[i].dev == NULL) {
err = 1;
break;
}
}
OF_prop_free(prop);
len = OF_getencprop_alloc_multi(node, "usb-hub-rst",
3 * sizeof(pcell_t), (void **)&prop);
if (len > 0) {
sc->hub_rst.dev = OF_device_from_xref(prop[0]);
sc->hub_rst.pin = prop[1];
sc->hub_rst.pol = prop[2];
if (len > 1 || sc->hub_rst.dev == NULL)
err = 1;
}
OF_prop_free(prop);
if (err) {
device_printf(dev, "unable to parse gpio\n");
goto fail;
}
/* Turn on power by setting pin and then enabling output driver. */
for (i = 0; i < npwr_en; i++) {
if (GPIO_PIN_SET(sc->pwr_en[i].dev, sc->pwr_en[i].pin,
PIN_ON_FLAG(sc->pwr_en[i].pol)) != 0 ||
GPIO_PIN_SETFLAGS(sc->pwr_en[i].dev, sc->pwr_en[i].pin,
GPIO_PIN_OUTPUT) != 0) {
device_printf(dev,
"could not use gpio to control power\n");
goto fail;
}
sc->npwr_en++;
}
/*
* Configure the clock source and divider.
*/
value = CSR_READ_4(sc, AML_USB_PHY_CFG_REG);
value &= ~(AML_USB_PHY_CFG_CLK_SEL_32K_ALT |
AML_USB_PHY_CFG_CLK_DIV_MASK |
AML_USB_PHY_CFG_CLK_SEL_MASK |
AML_USB_PHY_CFG_CLK_EN);
switch (aml8726_soc_hw_rev) {
case AML_SOC_HW_REV_M8:
case AML_SOC_HW_REV_M8B:
value |= AML_USB_PHY_CFG_CLK_SEL_32K_ALT;
break;
default:
div = 2;
value |= AML_USB_PHY_CFG_CLK_SEL_XTAL;
value |= ((div - 1) << AML_USB_PHY_CFG_CLK_DIV_SHIFT) &
AML_USB_PHY_CFG_CLK_DIV_MASK;
value |= AML_USB_PHY_CFG_CLK_EN;
break;
}
CSR_WRITE_4(sc, AML_USB_PHY_CFG_REG, value);
CSR_BARRIER(sc, AML_USB_PHY_CFG_REG);
/*
* Configure the clock frequency and issue a power on reset.
*/
value = CSR_READ_4(sc, AML_USB_PHY_CTRL_REG);
value &= ~AML_USB_PHY_CTRL_FSEL_MASK;
switch (aml8726_soc_hw_rev) {
case AML_SOC_HW_REV_M8:
case AML_SOC_HW_REV_M8B:
value |= AML_USB_PHY_CTRL_FSEL_24M;
break;
default:
value |= AML_USB_PHY_CTRL_FSEL_12M;
break;
}
value |= AML_USB_PHY_CTRL_POR;
CSR_WRITE_4(sc, AML_USB_PHY_CTRL_REG, value);
CSR_BARRIER(sc, AML_USB_PHY_CTRL_REG);
DELAY(500);
/*
* Enable by clearing the power on reset.
*/
value &= ~AML_USB_PHY_CTRL_POR;
CSR_WRITE_4(sc, AML_USB_PHY_CTRL_REG, value);
CSR_BARRIER(sc, AML_USB_PHY_CTRL_REG);
DELAY(1000);
/*
* Check if the clock was detected.
*/
value = CSR_READ_4(sc, AML_USB_PHY_CTRL_REG);
if ((value & AML_USB_PHY_CTRL_CLK_DETECTED) == 0)
device_printf(dev, "PHY Clock not detected\n");
/*
* If necessary enabled Accessory Charger Adaptor detection
* so that the port knows what mode to operate in.
*/
if (sc->force_aca) {
value = CSR_READ_4(sc, AML_USB_PHY_ADP_BC_REG);
value |= AML_USB_PHY_ADP_BC_ACA_EN;
CSR_WRITE_4(sc, AML_USB_PHY_ADP_BC_REG, value);
CSR_BARRIER(sc, AML_USB_PHY_ADP_BC_REG);
DELAY(50);
value = CSR_READ_4(sc, AML_USB_PHY_ADP_BC_REG);
if ((value & AML_USB_PHY_ADP_BC_ACA_FLOATING) != 0) {
device_printf(dev,
"force-aca requires newer silicon\n");
goto fail;
}
}
/*
* Reset the hub.
*/
if (sc->hub_rst.dev != NULL) {
err = 0;
if (GPIO_PIN_SET(sc->hub_rst.dev, sc->hub_rst.pin,
PIN_ON_FLAG(sc->hub_rst.pol)) != 0 ||
GPIO_PIN_SETFLAGS(sc->hub_rst.dev, sc->hub_rst.pin,
GPIO_PIN_OUTPUT) != 0)
err = 1;
DELAY(30);
if (GPIO_PIN_SET(sc->hub_rst.dev, sc->hub_rst.pin,
PIN_OFF_FLAG(sc->hub_rst.pol)) != 0)
err = 1;
DELAY(60000);
if (err) {
device_printf(dev,
"could not use gpio to reset hub\n");
goto fail;
}
}
return (0);
fail:
/* In the event of problems attempt to turn things back off. */
i = sc->npwr_en;
while (i-- != 0) {
GPIO_PIN_SET(sc->pwr_en[i].dev, sc->pwr_en[i].pin,
PIN_OFF_FLAG(sc->pwr_en[i].pol));
}
free (sc->pwr_en, M_DEVBUF);
sc->pwr_en = NULL;
bus_release_resources(dev, aml8726_usb_phy_spec, sc->res);
return (ENXIO);
}
static int
aml8726_usb_phy_detach(device_t dev)
{
struct aml8726_usb_phy_softc *sc = device_get_softc(dev);
uint32_t i;
uint32_t value;
/*
* Disable by issuing a power on reset.
*/
value = CSR_READ_4(sc, AML_USB_PHY_CTRL_REG);
value |= AML_USB_PHY_CTRL_POR;
CSR_WRITE_4(sc, AML_USB_PHY_CTRL_REG, value);
CSR_BARRIER(sc, AML_USB_PHY_CTRL_REG);
/* Turn off power */
i = sc->npwr_en;
while (i-- != 0) {
GPIO_PIN_SET(sc->pwr_en[i].dev, sc->pwr_en[i].pin,
PIN_OFF_FLAG(sc->pwr_en[i].pol));
}
free (sc->pwr_en, M_DEVBUF);
sc->pwr_en = NULL;
bus_release_resources(dev, aml8726_usb_phy_spec, sc->res);
return (0);
}
static device_method_t aml8726_usb_phy_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aml8726_usb_phy_probe),
DEVMETHOD(device_attach, aml8726_usb_phy_attach),
DEVMETHOD(device_detach, aml8726_usb_phy_detach),
DEVMETHOD_END
};
static driver_t aml8726_usb_phy_driver = {
"usbphy",
aml8726_usb_phy_methods,
sizeof(struct aml8726_usb_phy_softc),
};
static devclass_t aml8726_usb_phy_devclass;
DRIVER_MODULE(aml8726_m6usbphy, simplebus, aml8726_usb_phy_driver,
aml8726_usb_phy_devclass, 0, 0);
MODULE_DEPEND(aml8726_m6usbphy, aml8726_gpio, 1, 1, 1);

View File

@ -1,305 +0,0 @@
/*-
* Copyright 2013-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*
*/
/*
* Amlogic aml8726 watchdog driver.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/eventhandler.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/rman.h>
#include <sys/watchdog.h>
#include <machine/bus.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/amlogic/aml8726/aml8726_soc.h>
struct aml8726_wdt_softc {
device_t dev;
struct resource * res[2];
struct mtx mtx;
void * ih_cookie;
};
static struct resource_spec aml8726_wdt_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ SYS_RES_IRQ, 0, RF_ACTIVE },
{ -1, 0 }
};
static struct {
uint32_t ctrl_cpu_mask;
uint32_t ctrl_en;
uint32_t term_cnt_mask;
uint32_t reset_cnt_mask;
} aml8726_wdt_soc_params;
/*
* devclass_get_device / device_get_softc could be used
* to dynamically locate this, however the wdt is a
* required device which can't be unloaded so there's
* no need for the overhead.
*/
static struct aml8726_wdt_softc *aml8726_wdt_sc = NULL;
#define AML_WDT_LOCK(sc) mtx_lock_spin(&(sc)->mtx)
#define AML_WDT_UNLOCK(sc) mtx_unlock_spin(&(sc)->mtx)
#define AML_WDT_LOCK_INIT(sc) \
mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
"wdt", MTX_SPIN)
#define AML_WDT_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx);
#define AML_WDT_CTRL_REG 0
#define AML_WDT_CTRL_CPU_WDRESET_MASK aml8726_wdt_soc_params.ctrl_cpu_mask
#define AML_WDT_CTRL_CPU_WDRESET_SHIFT 24
#define AML_WDT_CTRL_IRQ_EN (1 << 23)
#define AML_WDT_CTRL_EN aml8726_wdt_soc_params.ctrl_en
#define AML_WDT_CTRL_TERMINAL_CNT_MASK aml8726_wdt_soc_params.term_cnt_mask
#define AML_WDT_CTRL_TERMINAL_CNT_SHIFT 0
#define AML_WDT_RESET_REG 4
#define AML_WDT_RESET_CNT_MASK aml8726_wdt_soc_params.reset_cnt_mask
#define AML_WDT_RESET_CNT_SHIFT 0
#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val))
#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
#define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \
(BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
static void
aml8726_wdt_watchdog(void *private, u_int cmd, int *error)
{
struct aml8726_wdt_softc *sc = (struct aml8726_wdt_softc *)private;
uint32_t wcr;
uint64_t tens_of_usec;
AML_WDT_LOCK(sc);
tens_of_usec = (((uint64_t)1 << (cmd & WD_INTERVAL)) + 9999) / 10000;
if (cmd != 0 && tens_of_usec <= (AML_WDT_CTRL_TERMINAL_CNT_MASK >>
AML_WDT_CTRL_TERMINAL_CNT_SHIFT)) {
wcr = AML_WDT_CTRL_CPU_WDRESET_MASK |
AML_WDT_CTRL_EN | ((uint32_t)tens_of_usec <<
AML_WDT_CTRL_TERMINAL_CNT_SHIFT);
CSR_WRITE_4(sc, AML_WDT_RESET_REG, 0);
CSR_WRITE_4(sc, AML_WDT_CTRL_REG, wcr);
*error = 0;
} else
CSR_WRITE_4(sc, AML_WDT_CTRL_REG,
(CSR_READ_4(sc, AML_WDT_CTRL_REG) &
~(AML_WDT_CTRL_IRQ_EN | AML_WDT_CTRL_EN)));
AML_WDT_UNLOCK(sc);
}
static int
aml8726_wdt_intr(void *arg)
{
struct aml8726_wdt_softc *sc = (struct aml8726_wdt_softc *)arg;
/*
* Normally a timeout causes a hardware reset, however
* the watchdog timer can be configured to cause an
* interrupt instead by setting AML_WDT_CTRL_IRQ_EN
* and clearing AML_WDT_CTRL_CPU_WDRESET_MASK.
*/
AML_WDT_LOCK(sc);
CSR_WRITE_4(sc, AML_WDT_CTRL_REG,
(CSR_READ_4(sc, AML_WDT_CTRL_REG) & ~(AML_WDT_CTRL_IRQ_EN |
AML_WDT_CTRL_EN)));
CSR_BARRIER(sc, AML_WDT_CTRL_REG);
AML_WDT_UNLOCK(sc);
device_printf(sc->dev, "timeout expired\n");
return (FILTER_HANDLED);
}
static int
aml8726_wdt_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "amlogic,meson6-wdt"))
return (ENXIO);
device_set_desc(dev, "Amlogic aml8726 WDT");
return (BUS_PROBE_DEFAULT);
}
static int
aml8726_wdt_attach(device_t dev)
{
struct aml8726_wdt_softc *sc = device_get_softc(dev);
/* There should be exactly one instance. */
if (aml8726_wdt_sc != NULL)
return (ENXIO);
sc->dev = dev;
if (bus_alloc_resources(dev, aml8726_wdt_spec, sc->res)) {
device_printf(dev, "can not allocate resources for device\n");
return (ENXIO);
}
/*
* Certain bitfields are dependent on the hardware revision.
*/
switch (aml8726_soc_hw_rev) {
case AML_SOC_HW_REV_M8:
aml8726_wdt_soc_params.ctrl_cpu_mask = 0xf <<
AML_WDT_CTRL_CPU_WDRESET_SHIFT;
switch (aml8726_soc_metal_rev) {
case AML_SOC_M8_METAL_REV_M2_A:
aml8726_wdt_soc_params.ctrl_en = 1 << 19;
aml8726_wdt_soc_params.term_cnt_mask = 0x07ffff <<
AML_WDT_CTRL_TERMINAL_CNT_SHIFT;
aml8726_wdt_soc_params.reset_cnt_mask = 0x07ffff <<
AML_WDT_RESET_CNT_SHIFT;
break;
default:
aml8726_wdt_soc_params.ctrl_en = 1 << 22;
aml8726_wdt_soc_params.term_cnt_mask = 0x3fffff <<
AML_WDT_CTRL_TERMINAL_CNT_SHIFT;
aml8726_wdt_soc_params.reset_cnt_mask = 0x3fffff <<
AML_WDT_RESET_CNT_SHIFT;
break;
}
break;
case AML_SOC_HW_REV_M8B:
aml8726_wdt_soc_params.ctrl_cpu_mask = 0xf <<
AML_WDT_CTRL_CPU_WDRESET_SHIFT;
aml8726_wdt_soc_params.ctrl_en = 1 << 19;
aml8726_wdt_soc_params.term_cnt_mask = 0x07ffff <<
AML_WDT_CTRL_TERMINAL_CNT_SHIFT;
aml8726_wdt_soc_params.reset_cnt_mask = 0x07ffff <<
AML_WDT_RESET_CNT_SHIFT;
break;
default:
aml8726_wdt_soc_params.ctrl_cpu_mask = 3 <<
AML_WDT_CTRL_CPU_WDRESET_SHIFT;
aml8726_wdt_soc_params.ctrl_en = 1 << 22;
aml8726_wdt_soc_params.term_cnt_mask = 0x3fffff <<
AML_WDT_CTRL_TERMINAL_CNT_SHIFT;
aml8726_wdt_soc_params.reset_cnt_mask = 0x3fffff <<
AML_WDT_RESET_CNT_SHIFT;
break;
}
/*
* Disable the watchdog.
*/
CSR_WRITE_4(sc, AML_WDT_CTRL_REG,
(CSR_READ_4(sc, AML_WDT_CTRL_REG) & ~(AML_WDT_CTRL_IRQ_EN |
AML_WDT_CTRL_EN)));
/*
* Initialize the mutex prior to installing the interrupt handler
* in case of a spurious interrupt.
*/
AML_WDT_LOCK_INIT(sc);
if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
aml8726_wdt_intr, NULL, sc, &sc->ih_cookie)) {
device_printf(dev, "could not setup interrupt handler\n");
bus_release_resources(dev, aml8726_wdt_spec, sc->res);
AML_WDT_LOCK_DESTROY(sc);
return (ENXIO);
}
aml8726_wdt_sc = sc;
EVENTHANDLER_REGISTER(watchdog_list, aml8726_wdt_watchdog, sc, 0);
return (0);
}
static int
aml8726_wdt_detach(device_t dev)
{
return (EBUSY);
}
static device_method_t aml8726_wdt_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aml8726_wdt_probe),
DEVMETHOD(device_attach, aml8726_wdt_attach),
DEVMETHOD(device_detach, aml8726_wdt_detach),
DEVMETHOD_END
};
static driver_t aml8726_wdt_driver = {
"wdt",
aml8726_wdt_methods,
sizeof(struct aml8726_wdt_softc),
};
static devclass_t aml8726_wdt_devclass;
EARLY_DRIVER_MODULE(wdt, simplebus, aml8726_wdt_driver, aml8726_wdt_devclass,
0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
void
cpu_reset(void)
{
/* Watchdog has not yet been initialized */
if (aml8726_wdt_sc == NULL)
printf("Reset hardware has not yet been initialized.\n");
else {
CSR_WRITE_4(aml8726_wdt_sc, AML_WDT_RESET_REG, 0);
CSR_WRITE_4(aml8726_wdt_sc, AML_WDT_CTRL_REG,
(AML_WDT_CTRL_CPU_WDRESET_MASK | AML_WDT_CTRL_EN |
(10 << AML_WDT_CTRL_TERMINAL_CNT_SHIFT)));
}
while (1);
}

View File

@ -1,29 +0,0 @@
#$FreeBSD$
arm/amlogic/aml8726/aml8726_l2cache.c standard
arm/amlogic/aml8726/aml8726_machdep.c standard
arm/amlogic/aml8726/aml8726_mp.c optional smp
arm/amlogic/aml8726/aml8726_identsoc.c standard
arm/amlogic/aml8726/aml8726_ccm.c standard
arm/amlogic/aml8726/aml8726_clkmsr.c standard
arm/amlogic/aml8726/aml8726_pic.c optional aml_pic
arm/amlogic/aml8726/aml8726_rtc.c standard
arm/amlogic/aml8726/aml8726_timer.c standard
arm/amlogic/aml8726/aml8726_wdt.c standard
# serial console for debugging early boot code
# also define SOCDEV_PA and SOCDEV_VA in std.aml8726
#arm/amlogic/aml8726/aml8726_uart_console.c standard
arm/amlogic/aml8726/aml8726_fb.c optional vt
arm/amlogic/aml8726/aml8726_gpio.c optional gpio
arm/amlogic/aml8726/aml8726_i2c.c optional iicbus
arm/amlogic/aml8726/aml8726_mmc.c optional mmc gpio
arm/amlogic/aml8726/aml8726_sdxc-m8.c optional mmc gpio
arm/amlogic/aml8726/aml8726_pinctrl.c optional fdt_pinctrl
#arm/amlogic/aml8726/aml8726_rng.c optional random
arm/amlogic/aml8726/uart_dev_aml8726.c optional uart
arm/amlogic/aml8726/aml8726_usb_phy-m3.c optional dwcotg usb gpio
arm/amlogic/aml8726/aml8726_usb_phy-m6.c optional dwcotg usb gpio
arm/amlogic/aml8726/aml8726_if_dwc.c optional dwc

View File

@ -1,25 +0,0 @@
# $FreeBSD$
cpu CPU_CORTEXA
machine arm armv7
makeoptions CONF_CFLAGS="-march=armv7a"
# Physical memory starts at 0x80000000. We assume the kernel is loaded
# at 0x80100000 by u-boot (which doesn't support ubldr since it's missing
# CONFIG_API). The kernel must be supplied as a binary since u-boot is
# also missing CONFIG_CMD_ELF.
#
#
options KERNVIRTADDR=0xc0100000 # Used in ldscript.arm
makeoptions KERNVIRTADDR=0xc0100000
device fdt_pinctrl
files "../amlogic/aml8726/files.aml8726"
# Set all global interrupts to be edge triggered, active high.
options GIC_DEFAULT_ICFGR_INIT=0xffffffff
#options EARLY_PRINTF
#options SOCDEV_PA=0xc8100000
#options SOCDEV_VA=0xd8100000

View File

@ -1,771 +0,0 @@
/*-
* Copyright 2013-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* 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.
*/
/*
* Amlogic aml8726 UART driver.
*
* The current implementation only targets features common to all
* uarts. For example ... though UART A as a 128 byte FIFO, the
* others only have a 64 byte FIFO.
*
* Also, it's assumed that the USE_XTAL_CLK feature (available on
* the aml8726-m6 and later) has not been activated.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/uart/uart.h>
#include <dev/uart/uart_cpu.h>
#include <dev/uart/uart_cpu_fdt.h>
#include <dev/uart/uart_bus.h>
#include <arm/amlogic/aml8726/aml8726_soc.h>
#include <arm/amlogic/aml8726/aml8726_uart.h>
#include "uart_if.h"
#undef uart_getreg
#undef uart_setreg
#define uart_getreg(bas, reg) \
bus_space_read_4((bas)->bst, (bas)->bsh, reg)
#define uart_setreg(bas, reg, value) \
bus_space_write_4((bas)->bst, (bas)->bsh, reg, value)
#define SIGCHG(c, i, s, d) \
do { \
if (c) { \
i |= (i & s) ? s : s | d; \
} else { \
i = (i & s) ? (i & ~s) | d : i; \
} \
} while (0)
static int
aml8726_uart_divisor(int rclk, int baudrate)
{
int actual_baud, divisor;
int error;
if (baudrate == 0)
return (0);
/* integer version of (rclk / baudrate + .5) */
divisor = ((rclk << 1) + baudrate) / (baudrate << 1);
if (divisor == 0)
return (0);
actual_baud = rclk / divisor;
/* 10 times error in percent: */
error = (((actual_baud - baudrate) * 2000) / baudrate + 1) >> 1;
/* 3.0% maximum error tolerance: */
if (error < -30 || error > 30)
return (0);
return (divisor);
}
static int
aml8726_uart_param(struct uart_bas *bas, int baudrate, int databits, int stopbits,
int parity)
{
uint32_t cr;
uint32_t mr;
uint32_t nbr;
int divisor;
cr = uart_getreg(bas, AML_UART_CONTROL_REG);
cr &= ~(AML_UART_CONTROL_DB_MASK | AML_UART_CONTROL_SB_MASK |
AML_UART_CONTROL_P_MASK);
switch (databits) {
case 5: cr |= AML_UART_CONTROL_5_DB; break;
case 6: cr |= AML_UART_CONTROL_6_DB; break;
case 7: cr |= AML_UART_CONTROL_7_DB; break;
case 8: cr |= AML_UART_CONTROL_8_DB; break;
default: return (EINVAL);
}
switch (stopbits) {
case 1: cr |= AML_UART_CONTROL_1_SB; break;
case 2: cr |= AML_UART_CONTROL_2_SB; break;
default: return (EINVAL);
}
switch (parity) {
case UART_PARITY_EVEN: cr |= AML_UART_CONTROL_P_EVEN;
cr |= AML_UART_CONTROL_P_EN;
break;
case UART_PARITY_ODD: cr |= AML_UART_CONTROL_P_ODD;
cr |= AML_UART_CONTROL_P_EN;
break;
case UART_PARITY_NONE: break;
default: return (EINVAL);
}
/* Set baudrate. */
if (baudrate > 0 && bas->rclk != 0) {
divisor = aml8726_uart_divisor(bas->rclk / 4, baudrate) - 1;
switch (aml8726_soc_hw_rev) {
case AML_SOC_HW_REV_M6:
case AML_SOC_HW_REV_M8:
case AML_SOC_HW_REV_M8B:
if (divisor > (AML_UART_NEW_BAUD_RATE_MASK >>
AML_UART_NEW_BAUD_RATE_SHIFT))
return (EINVAL);
nbr = uart_getreg(bas, AML_UART_NEW_BAUD_REG);
nbr &= ~(AML_UART_NEW_BAUD_USE_XTAL_CLK |
AML_UART_NEW_BAUD_RATE_MASK);
nbr |= AML_UART_NEW_BAUD_RATE_EN |
(divisor << AML_UART_NEW_BAUD_RATE_SHIFT);
uart_setreg(bas, AML_UART_NEW_BAUD_REG, nbr);
divisor = 0;
break;
default:
if (divisor > 0xffff)
return (EINVAL);
break;
}
cr &= ~AML_UART_CONTROL_BAUD_MASK;
cr |= (divisor & AML_UART_CONTROL_BAUD_MASK);
divisor >>= AML_UART_CONTROL_BAUD_WIDTH;
mr = uart_getreg(bas, AML_UART_MISC_REG);
mr &= ~(AML_UART_MISC_OLD_RX_BAUD |
AML_UART_MISC_BAUD_EXT_MASK);
mr |= ((divisor << AML_UART_MISC_BAUD_EXT_SHIFT) &
AML_UART_MISC_BAUD_EXT_MASK);
uart_setreg(bas, AML_UART_MISC_REG, mr);
}
uart_setreg(bas, AML_UART_CONTROL_REG, cr);
uart_barrier(bas);
return (0);
}
/*
* Low-level UART interface.
*/
static int
aml8726_uart_probe(struct uart_bas *bas)
{
return (0);
}
static void
aml8726_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
int parity)
{
uint32_t cr;
uint32_t mr;
(void)aml8726_uart_param(bas, baudrate, databits, stopbits, parity);
cr = uart_getreg(bas, AML_UART_CONTROL_REG);
/* Disable all interrupt sources. */
cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN);
/* Reset the transmitter and receiver. */
cr |= (AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
/* Enable the transmitter and receiver. */
cr |= (AML_UART_CONTROL_TX_EN | AML_UART_CONTROL_RX_EN);
uart_setreg(bas, AML_UART_CONTROL_REG, cr);
uart_barrier(bas);
/* Clear RX FIFO level for generating interrupts. */
mr = uart_getreg(bas, AML_UART_MISC_REG);
mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK;
uart_setreg(bas, AML_UART_MISC_REG, mr);
uart_barrier(bas);
/* Ensure the reset bits are clear. */
cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
uart_setreg(bas, AML_UART_CONTROL_REG, cr);
uart_barrier(bas);
}
static void
aml8726_uart_term(struct uart_bas *bas)
{
}
static void
aml8726_uart_putc(struct uart_bas *bas, int c)
{
while ((uart_getreg(bas, AML_UART_STATUS_REG) &
AML_UART_STATUS_TX_FIFO_FULL) != 0)
cpu_spinwait();
uart_setreg(bas, AML_UART_WFIFO_REG, c);
uart_barrier(bas);
}
static int
aml8726_uart_rxready(struct uart_bas *bas)
{
return ((uart_getreg(bas, AML_UART_STATUS_REG) &
AML_UART_STATUS_RX_FIFO_EMPTY) == 0 ? 1 : 0);
}
static int
aml8726_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
{
int c;
uart_lock(hwmtx);
while ((uart_getreg(bas, AML_UART_STATUS_REG) &
AML_UART_STATUS_RX_FIFO_EMPTY) != 0) {
uart_unlock(hwmtx);
DELAY(4);
uart_lock(hwmtx);
}
c = uart_getreg(bas, AML_UART_RFIFO_REG) & 0xff;
uart_unlock(hwmtx);
return (c);
}
struct uart_ops aml8726_uart_ops = {
.probe = aml8726_uart_probe,
.init = aml8726_uart_init,
.term = aml8726_uart_term,
.putc = aml8726_uart_putc,
.rxready = aml8726_uart_rxready,
.getc = aml8726_uart_getc,
};
static unsigned int
aml8726_uart_bus_clk(phandle_t node)
{
pcell_t prop;
ssize_t len;
phandle_t clk_node;
len = OF_getencprop(node, "clocks", &prop, sizeof(prop));
if ((len / sizeof(prop)) != 1 || prop == 0 ||
(clk_node = OF_node_from_xref(prop)) == 0)
return (0);
len = OF_getencprop(clk_node, "clock-frequency", &prop, sizeof(prop));
if ((len / sizeof(prop)) != 1 || prop == 0)
return (0);
return ((unsigned int)prop);
}
static int
aml8726_uart_bus_probe(struct uart_softc *sc)
{
int error;
error = aml8726_uart_probe(&sc->sc_bas);
if (error)
return (error);
sc->sc_rxfifosz = 64;
sc->sc_txfifosz = 64;
sc->sc_hwiflow = 1;
sc->sc_hwoflow = 1;
device_set_desc(sc->sc_dev, "Amlogic aml8726 UART");
return (0);
}
static int
aml8726_uart_bus_getsig(struct uart_softc *sc)
{
uint32_t new, old, sig;
/*
* Treat DSR, DCD, and CTS as always on.
*/
do {
old = sc->sc_hwsig;
sig = old;
SIGCHG(1, sig, SER_DSR, SER_DDSR);
SIGCHG(1, sig, SER_DCD, SER_DDCD);
SIGCHG(1, sig, SER_CTS, SER_DCTS);
new = sig & ~SER_MASK_DELTA;
} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
return (sig);
}
static int
aml8726_uart_bus_setsig(struct uart_softc *sc, int sig)
{
uint32_t new, old;
do {
old = sc->sc_hwsig;
new = old;
if (sig & SER_DDTR) {
SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
}
if (sig & SER_DRTS) {
SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
}
} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
return (0);
}
static int
aml8726_uart_bus_attach(struct uart_softc *sc)
{
struct uart_bas *bas;
uint32_t cr;
uint32_t mr;
bas = &sc->sc_bas;
bas->rclk = aml8726_uart_bus_clk(ofw_bus_get_node(sc->sc_dev));
if (bas->rclk == 0) {
device_printf(sc->sc_dev, "missing clocks attribute in FDT\n");
return (ENXIO);
}
cr = uart_getreg(bas, AML_UART_CONTROL_REG);
/* Disable all interrupt sources. */
cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN);
/* Ensure the reset bits are clear. */
cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
/*
* Reset the transmitter and receiver only if not acting as a
* console, otherwise it means that:
*
* 1) aml8726_uart_init was already called which did the reset
*
* 2) there may be console bytes sitting in the transmit fifo
*/
if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE)
;
else
cr |= (AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
/* Default to two wire mode. */
cr |= AML_UART_CONTROL_TWO_WIRE_EN;
/* Enable the transmitter and receiver. */
cr |= (AML_UART_CONTROL_TX_EN | AML_UART_CONTROL_RX_EN);
/* Reset error bits. */
cr |= AML_UART_CONTROL_CLR_ERR;
uart_setreg(bas, AML_UART_CONTROL_REG, cr);
uart_barrier(bas);
/* Set FIFO levels for generating interrupts. */
mr = uart_getreg(bas, AML_UART_MISC_REG);
mr &= ~AML_UART_MISC_XMIT_IRQ_CNT_MASK;
mr |= (0 << AML_UART_MISC_XMIT_IRQ_CNT_SHIFT);
mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK;
mr |= (1 << AML_UART_MISC_RECV_IRQ_CNT_SHIFT);
uart_setreg(bas, AML_UART_MISC_REG, mr);
uart_barrier(bas);
aml8726_uart_bus_getsig(sc);
/* Ensure the reset bits are clear. */
cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
cr &= ~AML_UART_CONTROL_CLR_ERR;
/* Enable the receive interrupt. */
cr |= AML_UART_CONTROL_RX_INT_EN;
uart_setreg(bas, AML_UART_CONTROL_REG, cr);
uart_barrier(bas);
return (0);
}
static int
aml8726_uart_bus_detach(struct uart_softc *sc)
{
struct uart_bas *bas;
uint32_t cr;
uint32_t mr;
bas = &sc->sc_bas;
/* Disable all interrupt sources. */
cr = uart_getreg(bas, AML_UART_CONTROL_REG);
cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN);
uart_setreg(bas, AML_UART_CONTROL_REG, cr);
uart_barrier(bas);
/* Clear RX FIFO level for generating interrupts. */
mr = uart_getreg(bas, AML_UART_MISC_REG);
mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK;
uart_setreg(bas, AML_UART_MISC_REG, mr);
uart_barrier(bas);
return (0);
}
static int
aml8726_uart_bus_flush(struct uart_softc *sc, int what)
{
struct uart_bas *bas;
uint32_t cr;
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
cr = uart_getreg(bas, AML_UART_CONTROL_REG);
if (what & UART_FLUSH_TRANSMITTER)
cr |= AML_UART_CONTROL_TX_RST;
if (what & UART_FLUSH_RECEIVER)
cr |= AML_UART_CONTROL_RX_RST;
uart_setreg(bas, AML_UART_CONTROL_REG, cr);
uart_barrier(bas);
/* Ensure the reset bits are clear. */
cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
uart_setreg(bas, AML_UART_CONTROL_REG, cr);
uart_barrier(bas);
uart_unlock(sc->sc_hwmtx);
return (0);
}
static int
aml8726_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
{
struct uart_bas *bas;
int baudrate, divisor, error;
uint32_t cr, mr, nbr;
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
error = 0;
switch (request) {
case UART_IOCTL_BAUD:
cr = uart_getreg(bas, AML_UART_CONTROL_REG);
cr &= AML_UART_CONTROL_BAUD_MASK;
mr = uart_getreg(bas, AML_UART_MISC_REG);
mr &= AML_UART_MISC_BAUD_EXT_MASK;
divisor = ((mr >> AML_UART_MISC_BAUD_EXT_SHIFT) <<
AML_UART_CONTROL_BAUD_WIDTH) | cr;
switch (aml8726_soc_hw_rev) {
case AML_SOC_HW_REV_M6:
case AML_SOC_HW_REV_M8:
case AML_SOC_HW_REV_M8B:
nbr = uart_getreg(bas, AML_UART_NEW_BAUD_REG);
if ((nbr & AML_UART_NEW_BAUD_RATE_EN) != 0) {
divisor = (nbr & AML_UART_NEW_BAUD_RATE_MASK) >>
AML_UART_NEW_BAUD_RATE_SHIFT;
}
break;
default:
break;
}
baudrate = bas->rclk / 4 / (divisor + 1);
if (baudrate > 0)
*(int*)data = baudrate;
else
error = ENXIO;
break;
case UART_IOCTL_IFLOW:
case UART_IOCTL_OFLOW:
cr = uart_getreg(bas, AML_UART_CONTROL_REG);
if (data)
cr &= ~AML_UART_CONTROL_TWO_WIRE_EN;
else
cr |= AML_UART_CONTROL_TWO_WIRE_EN;
uart_setreg(bas, AML_UART_CONTROL_REG, cr);
break;
default:
error = EINVAL;
break;
}
uart_unlock(sc->sc_hwmtx);
return (error);
}
static int
aml8726_uart_bus_ipend(struct uart_softc *sc)
{
struct uart_bas *bas;
int ipend;
uint32_t sr;
uint32_t cr;
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
ipend = 0;
sr = uart_getreg(bas, AML_UART_STATUS_REG);
cr = uart_getreg(bas, AML_UART_CONTROL_REG);
if ((sr & AML_UART_STATUS_RX_FIFO_OVERFLOW) != 0)
ipend |= SER_INT_OVERRUN;
if ((sr & AML_UART_STATUS_TX_FIFO_EMPTY) != 0 &&
(cr & AML_UART_CONTROL_TX_INT_EN) != 0) {
ipend |= SER_INT_TXIDLE;
cr &= ~AML_UART_CONTROL_TX_INT_EN;
uart_setreg(bas, AML_UART_CONTROL_REG, cr);
uart_barrier(bas);
}
if ((sr & AML_UART_STATUS_RX_FIFO_EMPTY) == 0)
ipend |= SER_INT_RXREADY;
uart_unlock(sc->sc_hwmtx);
return (ipend);
}
static int
aml8726_uart_bus_param(struct uart_softc *sc, int baudrate, int databits,
int stopbits, int parity)
{
struct uart_bas *bas;
int error;
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
error = aml8726_uart_param(bas, baudrate, databits, stopbits, parity);
uart_unlock(sc->sc_hwmtx);
return (error);
}
static int
aml8726_uart_bus_receive(struct uart_softc *sc)
{
struct uart_bas *bas;
int xc;
uint32_t sr;
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
sr = uart_getreg(bas, AML_UART_STATUS_REG);
while ((sr & AML_UART_STATUS_RX_FIFO_EMPTY) == 0) {
if (uart_rx_full(sc)) {
sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
break;
}
xc = uart_getreg(bas, AML_UART_RFIFO_REG) & 0xff;
if (sr & AML_UART_STATUS_FRAME_ERR)
xc |= UART_STAT_FRAMERR;
if (sr & AML_UART_STATUS_PARITY_ERR)
xc |= UART_STAT_PARERR;
uart_rx_put(sc, xc);
sr = uart_getreg(bas, AML_UART_STATUS_REG);
}
/* Discard everything left in the RX FIFO. */
while ((sr & AML_UART_STATUS_RX_FIFO_EMPTY) == 0) {
(void)uart_getreg(bas, AML_UART_RFIFO_REG);
sr = uart_getreg(bas, AML_UART_STATUS_REG);
}
/* Reset error bits */
if ((sr & (AML_UART_STATUS_FRAME_ERR | AML_UART_STATUS_PARITY_ERR)) != 0) {
uart_setreg(bas, AML_UART_CONTROL_REG,
(uart_getreg(bas, AML_UART_CONTROL_REG) |
AML_UART_CONTROL_CLR_ERR));
uart_barrier(bas);
uart_setreg(bas, AML_UART_CONTROL_REG,
(uart_getreg(bas, AML_UART_CONTROL_REG) &
~AML_UART_CONTROL_CLR_ERR));
uart_barrier(bas);
}
uart_unlock(sc->sc_hwmtx);
return (0);
}
static int
aml8726_uart_bus_transmit(struct uart_softc *sc)
{
struct uart_bas *bas;
int i;
uint32_t cr;
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
/*
* Wait for sufficient space since aml8726_uart_putc
* may have been called after SER_INT_TXIDLE occurred.
*/
while ((uart_getreg(bas, AML_UART_STATUS_REG) &
AML_UART_STATUS_TX_FIFO_EMPTY) == 0)
cpu_spinwait();
for (i = 0; i < sc->sc_txdatasz; i++) {
uart_setreg(bas, AML_UART_WFIFO_REG, sc->sc_txbuf[i]);
uart_barrier(bas);
}
sc->sc_txbusy = 1;
cr = uart_getreg(bas, AML_UART_CONTROL_REG);
cr |= AML_UART_CONTROL_TX_INT_EN;
uart_setreg(bas, AML_UART_CONTROL_REG, cr);
uart_barrier(bas);
uart_unlock(sc->sc_hwmtx);
return (0);
}
static void
aml8726_uart_bus_grab(struct uart_softc *sc)
{
struct uart_bas *bas;
uint32_t cr;
/*
* Disable the receive interrupt to avoid a race between
* aml8726_uart_getc and aml8726_uart_bus_receive which
* can trigger:
*
* panic: bad stray interrupt
*
* due to the RX FIFO receiving a character causing an
* interrupt which gets serviced after aml8726_uart_getc
* has been called (meaning the RX FIFO is now empty).
*/
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
cr = uart_getreg(bas, AML_UART_CONTROL_REG);
cr &= ~AML_UART_CONTROL_RX_INT_EN;
uart_setreg(bas, AML_UART_CONTROL_REG, cr);
uart_barrier(bas);
uart_unlock(sc->sc_hwmtx);
}
static void
aml8726_uart_bus_ungrab(struct uart_softc *sc)
{
struct uart_bas *bas;
uint32_t cr;
uint32_t mr;
/*
* The RX FIFO level being set indicates that the device
* is currently attached meaning the receive interrupt
* should be enabled.
*/
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
mr = uart_getreg(bas, AML_UART_MISC_REG);
mr &= AML_UART_MISC_RECV_IRQ_CNT_MASK;
if (mr != 0) {
cr = uart_getreg(bas, AML_UART_CONTROL_REG);
cr |= AML_UART_CONTROL_RX_INT_EN;
uart_setreg(bas, AML_UART_CONTROL_REG, cr);
uart_barrier(bas);
}
uart_unlock(sc->sc_hwmtx);
}
static kobj_method_t aml8726_uart_methods[] = {
KOBJMETHOD(uart_probe, aml8726_uart_bus_probe),
KOBJMETHOD(uart_attach, aml8726_uart_bus_attach),
KOBJMETHOD(uart_detach, aml8726_uart_bus_detach),
KOBJMETHOD(uart_flush, aml8726_uart_bus_flush),
KOBJMETHOD(uart_getsig, aml8726_uart_bus_getsig),
KOBJMETHOD(uart_setsig, aml8726_uart_bus_setsig),
KOBJMETHOD(uart_ioctl, aml8726_uart_bus_ioctl),
KOBJMETHOD(uart_ipend, aml8726_uart_bus_ipend),
KOBJMETHOD(uart_param, aml8726_uart_bus_param),
KOBJMETHOD(uart_receive, aml8726_uart_bus_receive),
KOBJMETHOD(uart_transmit, aml8726_uart_bus_transmit),
KOBJMETHOD(uart_grab, aml8726_uart_bus_grab),
KOBJMETHOD(uart_ungrab, aml8726_uart_bus_ungrab),
{ 0, 0 }
};
struct uart_class uart_aml8726_class = {
"uart",
aml8726_uart_methods,
sizeof(struct uart_softc),
.uc_ops = &aml8726_uart_ops,
.uc_range = 24,
.uc_rclk = 0,
.uc_rshift = 0
};
static struct ofw_compat_data compat_data[] = {
{ "amlogic,meson-uart", (uintptr_t)&uart_aml8726_class },
{ NULL, (uintptr_t)NULL }
};
UART_FDT_CLASS_AND_DEVICE(compat_data);