Add necessary changes to support various Amlogic SoC devices

specially aml8726-m6 and aml8726-m8b SoC based devices.
aml8726-m6 SoC exist in devices such as Visson ATV-102.
Hardkernel ODROID-C1 board has aml8726-m8b SoC.

The following support is included:
  Basic machdep code
  SMP
  Interrupt controller
  Clock control driver (aka gate)
  Pinctrl
  Timer
  Real time clock
  UART
  GPIO
  I2C
  SD controller
  SDXC controller
  USB
  Watchdog
  Random number generator
  PLL / Clock frequency measurement
  Frame buffer

Submitted by:   John Wehle
Approved by:    stas (mentor)
This commit is contained in:
Ganbold Tsagaankhuu 2015-03-31 11:50:46 +00:00
parent 18832f1fd1
commit 562246dff8
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=280905
37 changed files with 10779 additions and 1 deletions

View File

@ -0,0 +1,231 @@
/*-
* 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/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_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", sizeof(char),
(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);
}
free(functions, M_OFWPROP);
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 */
break;
}
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

@ -0,0 +1,325 @@
/*-
* 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

@ -0,0 +1,271 @@
/*-
* 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_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 (sizeof(aml8726_clkmsr_clks) \
/ sizeof(aml8726_clkmsr_clks[0]))
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 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);
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;
DRIVER_MODULE(clkmsr, simplebus, aml8726_clkmsr_driver,
aml8726_clkmsr_devclass, 0, 0);
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;
/*
* Try to access the clkmsr node directly i.e. through /aliases/.
*/
if ((node = OF_finddevice("clkmsr")) != 0)
if (fdt_is_compatible_strict(node, "amlogic,aml8726-clkmsr"))
goto moveon;
/*
* Find the node the long way.
*/
if ((node = OF_finddevice("/soc")) == 0)
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

@ -0,0 +1,34 @@
/*-
* 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

@ -0,0 +1,468 @@
/*-
* 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 <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 <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

@ -0,0 +1,163 @@
/*-
* 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

@ -0,0 +1,372 @@
/*-
* 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/fdt/fdt_common.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

@ -0,0 +1,284 @@
/*-
* 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/fdt/fdt_common.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,aml8726-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

@ -0,0 +1,137 @@
/*-
* 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 = 0xffffffff;
uint32_t aml8726_soc_metal_rev = 0xffffffff;
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 }
};
static void
aml8726_identify_soc(void *dummy)
{
int err;
int i;
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);
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_soc, SI_SUB_CPU, SI_ORDER_SECOND,
aml8726_identify_soc, NULL);

View File

@ -0,0 +1,92 @@
/*-
* 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

@ -0,0 +1,211 @@
/*-
* 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$");
#define _ARM32_BUS_DMA_PRIVATE
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <machine/bus.h>
#include <machine/devmap.h>
#include <machine/machdep.h>
#include <machine/platform.h>
#include <dev/fdt/fdt_common.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()
{
phandle_t node, child;
pcell_t freq, prop;
ssize_t len;
/*
* Set the bus-frequency for any top level SoC simple-bus which
* 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));
for (child = OF_child(node); child != 0; child = OF_peer(child)) {
if (fdt_is_compatible_strict(child, "simple-bus")) {
len = OF_getencprop(child, "bus-frequency",
&prop, sizeof(prop));
if ((len / sizeof(prop)) == 1 && prop == 0)
OF_setprop(child, "bus-frequency",
(void *)&freq, sizeof(freq));
}
}
}
vm_offset_t
platform_lastaddr(void)
{
return (arm_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)arm_devmap_ptov(0xc8100000, 0x100000);
/*
* 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)
{
arm_devmap_add_entry(0xc1100000, 0x200000); /* cbus */
arm_devmap_add_entry(0xc4200000, 0x100000); /* pl310 */
arm_devmap_add_entry(0xc4300000, 0x100000); /* periph */
arm_devmap_add_entry(0xc8000000, 0x100000); /* apbbus */
arm_devmap_add_entry(0xc8100000, 0x100000); /* aobus */
arm_devmap_add_entry(0xc9000000, 0x800000); /* ahbbus */
arm_devmap_add_entry(0xd9000000, 0x100000); /* ahb */
arm_devmap_add_entry(0xda000000, 0x100000); /* secbus */
return (0);
}
struct arm32_dma_range *
bus_dma_get_range(void)
{
return (NULL);
}
int
bus_dma_get_range_nb(void)
{
return (0);
}
struct fdt_fixup_entry fdt_fixup_table[] = {
{ NULL, NULL }
};
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. However the
* multi core chips also have a GIC.
*/
#ifdef SMP
if (!fdt_is_compatible_strict(node, "arm,gic"))
#else
if (!fdt_is_compatible_strict(node, "amlogic,aml8726-pic"))
#endif
return (ENXIO);
*interrupt = fdt32_to_cpu(intr[0]);
*trig = INTR_TRIGGER_EDGE;
*pol = INTR_POLARITY_HIGH;
switch (*interrupt) {
case 30: /* INT_USB_A */
case 31: /* INT_USB_B */
*trig = INTR_TRIGGER_LEVEL;
break;
default:
break;
}
#ifdef SMP
*interrupt += 32;
#endif
return (0);
}
fdt_pic_decode_t fdt_pic_table[] = {
&fdt_pic_decode_ic,
NULL
};

View File

@ -0,0 +1,41 @@
/*-
* 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

@ -0,0 +1,137 @@
/*-
* 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
* Physicaly 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
/*
* Internally the timeout is implemented by counting clock cycles.
*
* Since the hardware implements timeouts by counting cycles
* the minimum read / write timeout (assuming the minimum
* conversion factor of 1 cycle per usec) is:
*
* (8 bits * 512 bytes per block + 16 bits CRC) = 4112 usec
*/
#if ((AML_MMC_READ_TIMEOUT * 1000) < 4112 || \
(AML_MMC_WRITE_TIMEOUT * 1000) < 4112)
#error "Single block timeout is smaller than supported"
#endif
#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

@ -0,0 +1,657 @@
/*-
* 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 <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)) != 0)
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")) == 0)
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_init_secondary(void)
{
/*
* Consider modifying the timer driver to support
* per-cpu timers and then enabling the timer for
* each AP.
*/
arm_init_secondary_ic();
}
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;
}
int
platform_mp_probe(void)
{
if (mp_ncpus == 0)
platform_mp_setmaxid();
return (mp_ncpus > 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);
cpu_idcache_wbinv_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 */
armv7_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));
}
void
platform_ipi_send(cpuset_t cpus, u_int ipi)
{
pic_ipi_send(cpus, ipi);
}
/*
* 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

@ -0,0 +1,272 @@
/*-
* 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/fdt/fdt_common.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;
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 SMP
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 SMP
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;
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);
}
#endif

View File

@ -0,0 +1,434 @@
/*-
* 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/fdt/fdt_common.h>
#include <dev/fdt/fdt_pinctrl.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_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 */
break;
}
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",
sizeof(char), (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);
free(function_name, M_OFWPROP);
return (ENXIO);
}
free(function_name, M_OFWPROP);
len = OF_getprop_alloc(node, "amlogic,pull",
sizeof(char), (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);
free(pull, M_OFWPROP);
return (ENXIO);
}
}
free(pull, M_OFWPROP);
/*
* 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",
sizeof(char), (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);
}
free(pins, M_OFWPROP);
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

@ -0,0 +1,155 @@
/*-
* 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/fdt/fdt_common.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), sizeof(rn) * NBBY / 2,
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, CALLOUT_MPSAFE);
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

@ -0,0 +1,505 @@
/*-
* 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/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.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);
boolean_t init_always_valid;
char *init_always;
pcell_t prop;
phandle_t node;
ssize_t len;
sc->dev = dev;
node = ofw_bus_get_node(dev);
len = OF_getprop_alloc(node, "init-always",
sizeof(char), (void **)&init_always);
sc->init.always = FALSE;
init_always_valid = FALSE;
if (len > 0) {
if (strncmp(init_always, "true", len) == 0) {
sc->init.always = TRUE;
init_always_valid = TRUE;
} else if (strncmp(init_always, "false", len) == 0)
init_always_valid = TRUE;
free(init_always, M_OFWPROP);
}
if (init_always_valid == FALSE) {
device_printf(dev, "missing init-always attribute in FDT\n");
return (ENXIO);
}
if (OF_getencprop(node, "xo-init", &prop, sizeof(prop)) <= 0) {
device_printf(dev, "missing xo-init attribute in FDT\n");
return (ENXIO);
}
sc->init.xo = prop;
if (OF_getencprop(node, "gpo-init", &prop, sizeof(prop)) <= 0) {
device_printf(dev, "missing gpo-init attribute in FDT\n");
return (ENXIO);
}
sc->init.gpo = prop;
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

@ -0,0 +1,223 @@
/*-
* 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
* Physicaly 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

@ -0,0 +1,54 @@
/*-
* 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
/* cbus */
#define AML_SOC_HW_REV_REG 0x7d4c
#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_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

@ -0,0 +1,395 @@
/*-
* 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/fdt/fdt_common.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,aml8726-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;
}
/*
* 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;
}
}

View File

@ -0,0 +1,97 @@
/*-
* 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
#endif /* _ARM_AMLOGIC_AML8726_UART_H */

View File

@ -0,0 +1,180 @@
/*-
* 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

@ -0,0 +1,417 @@
/*-
* 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/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 "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",
sizeof(char), (void **)&force_aca);
sc->force_aca = FALSE;
if (len > 0) {
if (strncmp(force_aca, "true", len) == 0)
sc->force_aca = TRUE;
}
free(force_aca, M_OFWPROP);
err = 0;
len = OF_getencprop_alloc(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;
}
free(prop, M_OFWPROP);
len = OF_getencprop_alloc(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;
}
free(prop, M_OFWPROP);
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

@ -0,0 +1,308 @@
/*-
* 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/kernel.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/rman.h>
#include <sys/watchdog.h>
#include <machine/bus.h>
#include <machine/cpufunc.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>
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,aml8726-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()
{
/* 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

@ -0,0 +1,35 @@
#$FreeBSD$
kern/kern_clocksource.c standard
arm/arm/bus_space_base.c standard
arm/arm/bus_space_generic.c standard
arm/arm/bus_space_asm_generic.S standard
arm/arm/pl310.c standard
arm/amlogic/aml8726/aml8726_l2cache.c standard
arm/amlogic/aml8726/aml8726_machdep.c standard
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 standard
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-m6.c optional dwcotg usb gpio
dev/dwc/if_dwc.c optional dwc

View File

@ -0,0 +1,4 @@
#$FreeBSD$
arm/arm/gic.c standard
arm/amlogic/aml8726/aml8726_mp.c standard

View File

@ -0,0 +1,21 @@
# $FreeBSD$
cpu CPU_CORTEXA
machine arm armv6
makeoptions CONF_CFLAGS="-march=armv7a"
device fdt_pinctrl
files "../amlogic/aml8726/files.aml8726"
options ARM_L2_PIPT
# Set all global interrupts to be edge triggered, active high.
options GIC_DEFAULT_ICFGR_INIT=0xffffffff
options IPI_IRQ_START=0
options IPI_IRQ_END=15
#options EARLY_PRINTF
#options SOCDEV_PA=0xc8100000
#options SOCDEV_VA=0xd8100000

View File

@ -0,0 +1,17 @@
# $FreeBSD$
include "../amlogic/aml8726/std.aml8726"
makeoptions FDT_DTS_FILE=odroidc1.dts
options SMP # Enable multiple cores
files "../amlogic/aml8726/files.smp"
# Physical memory starts at 0x00000000. We assume the kernel is loaded
# at 0x00100000 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

View File

@ -0,0 +1,17 @@
# $FreeBSD$
include "../amlogic/aml8726/std.aml8726"
makeoptions FDT_DTS_FILE=vsatv102-m6.dts
options SMP # Enable multiple cores
files "../amlogic/aml8726/files.smp"
# 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

View File

@ -0,0 +1,709 @@
/*-
* 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 register 5 (the new baud rate register
* present on the aml8726-m6) 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/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_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 || divisor >= 65536)
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;
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;
if (divisor > 0xffff)
return (EINVAL);
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;
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 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;
if (bas->rclk == 0) {
device_printf(sc->sc_dev, "missing clock 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;
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;
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
};
static struct ofw_compat_data compat_data[] = {
{ "amlogic,aml8726-uart", (uintptr_t)&uart_aml8726_class },
{ NULL, (uintptr_t)NULL }
};
UART_FDT_CLASS_AND_DEVICE(compat_data);

View File

@ -98,6 +98,10 @@ __FBSDID("$FreeBSD$");
#define GICD_ICFGR_TRIG_EDGE (1 << 1)
#define GICD_ICFGR_TRIG_MASK 0x2
#ifndef GIC_DEFAULT_ICFGR_INIT
#define GIC_DEFAULT_ICFGR_INIT 0x00000000
#endif
struct arm_gic_softc {
device_t gic_dev;
struct resource * gic_res[3];
@ -280,7 +284,7 @@ arm_gic_attach(device_t dev)
/* Set all global interrupts to be level triggered, active low. */
for (i = 32; i < sc->nirqs; i += 16) {
gic_d_write_4(sc, GICD_ICFGR(i >> 4), 0x00000000);
gic_d_write_4(sc, GICD_ICFGR(i >> 4), GIC_DEFAULT_ICFGR_INIT);
}
/* Disable all interrupts. */

View File

@ -22,6 +22,7 @@ CPU_XSCALE_IXP425 opt_global.h
CPU_XSCALE_IXP435 opt_global.h
CPU_XSCALE_PXA2X0 opt_global.h
FLASHADDR opt_global.h
GIC_DEFAULT_ICFGR_INIT opt_global.h
IPI_IRQ_START opt_smp.h
IPI_IRQ_END opt_smp.h
FREEBSD_BOOT_LOADER opt_global.h

View File

@ -1797,6 +1797,8 @@ static driver_t mmc_driver = {
};
static devclass_t mmc_devclass;
DRIVER_MODULE(mmc, aml8726_mmc, mmc_driver, mmc_devclass, NULL, NULL);
DRIVER_MODULE(mmc, aml8726_sdxc, mmc_driver, mmc_devclass, NULL, NULL);
DRIVER_MODULE(mmc, at91_mci, mmc_driver, mmc_devclass, NULL, NULL);
DRIVER_MODULE(mmc, sdhci_bcm, mmc_driver, mmc_devclass, NULL, NULL);
DRIVER_MODULE(mmc, sdhci_fdt, mmc_driver, mmc_devclass, NULL, NULL);