summaryrefslogtreecommitdiffstats
path: root/sys/arm
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arm')
-rw-r--r--sys/arm/allwinner/a10_ahci.c382
-rw-r--r--sys/arm/allwinner/a10_clk.c68
-rw-r--r--sys/arm/allwinner/a10_clk.h17
-rw-r--r--sys/arm/allwinner/a10_gpio.c6
-rw-r--r--sys/arm/allwinner/a10_gpio.h5
-rw-r--r--sys/arm/allwinner/a10_mmc.c238
-rw-r--r--sys/arm/allwinner/a10_mmc.h21
-rw-r--r--sys/arm/allwinner/files.allwinner1
-rw-r--r--sys/arm/allwinner/if_emac.c13
-rw-r--r--sys/arm/arm/genassym.c2
-rw-r--r--sys/arm/conf/CUBIEBOARD4
-rw-r--r--sys/arm/conf/CUBIEBOARD24
12 files changed, 712 insertions, 49 deletions
diff --git a/sys/arm/allwinner/a10_ahci.c b/sys/arm/allwinner/a10_ahci.c
new file mode 100644
index 0000000..37856fb
--- /dev/null
+++ b/sys/arm/allwinner/a10_ahci.c
@@ -0,0 +1,382 @@
+/*-
+ * Copyright (c) 2014-2015 M. Warner Losh <imp@freebsd.org>
+ * Copyright (c) 2015 Luiz Otavio O Souza <loos@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The magic-bit-bang sequence used in this code may be based on a linux
+ * platform driver in the Allwinner SDK from Allwinner Technology Co., Ltd.
+ * www.allwinnertech.com, by Daniel Wang <danielwang@allwinnertech.com>
+ * though none of the original code was copied.
+ */
+
+#include "opt_bus.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/gpio.h>
+
+#include <machine/bus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/ahci/ahci.h>
+#include <arm/allwinner/a10_clk.h>
+#include "gpio_if.h"
+
+/*
+ * Allwinner a1x/a2x/a8x SATA attachment. This is just the AHCI register
+ * set with a few extra implementation-specific registers that need to
+ * be accounted for. There's only one PHY in the system, and it needs
+ * to be trained to bring the link up. In addition, there's some DMA
+ * specific things that need to be done as well. These things are also
+ * just about completely undocumented, except in ugly code in the Linux
+ * SDK Allwinner releases.
+ */
+
+/* BITx -- Unknown bit that needs to be set/cleared at position x */
+/* UFx -- Uknown multi-bit field frobbed during init */
+#define AHCI_BISTAFR 0x00A0
+#define AHCI_BISTCR 0x00A4
+#define AHCI_BISTFCTR 0x00A8
+#define AHCI_BISTSR 0x00AC
+#define AHCI_BISTDECR 0x00B0
+#define AHCI_DIAGNR 0x00B4
+#define AHCI_DIAGNR1 0x00B8
+#define AHCI_OOBR 0x00BC
+#define AHCI_PHYCS0R 0x00C0
+/* Bits 0..17 are a mystery */
+#define PHYCS0R_BIT18 (1 << 18)
+#define PHYCS0R_POWER_ENABLE (1 << 19)
+#define PHYCS0R_UF1_MASK (7 << 20) /* Unknown Field 1 */
+#define PHYCS0R_UF1_INIT (3 << 20)
+#define PHYCS0R_BIT23 (1 << 23)
+#define PHYCS0R_UF2_MASK (7 << 24) /* Uknown Field 2 */
+#define PHYCS0R_UF2_INIT (5 << 24)
+/* Bit 27 mystery */
+#define PHYCS0R_POWER_STATUS_MASK (7 << 28)
+#define PHYCS0R_PS_GOOD (2 << 28)
+/* Bit 31 mystery */
+#define AHCI_PHYCS1R 0x00C4
+/* Bits 0..5 are a mystery */
+#define PHYCS1R_UF1_MASK (3 << 6)
+#define PHYCS1R_UF1_INIT (2 << 6)
+#define PHYCS1R_UF2_MASK (0x1f << 8)
+#define PHYCS1R_UF2_INIT (6 << 8)
+/* Bits 13..14 are a mystery */
+#define PHYCS1R_BIT15 (1 << 15)
+#define PHYCS1R_UF3_MASK (3 << 16)
+#define PHYCS1R_UF3_INIT (2 << 16)
+/* Bit 18 mystery */
+#define PHYCS1R_HIGHZ (1 << 19)
+/* Bits 20..27 mystery */
+#define PHYCS1R_BIT28 (1 << 28)
+/* Bits 29..31 mystery */
+#define AHCI_PHYCS2R 0x00C8
+/* bits 0..4 mystery */
+#define PHYCS2R_UF1_MASK (0x1f << 5)
+#define PHYCS2R_UF1_INIT (0x19 << 5)
+/* Bits 10..23 mystery */
+#define PHYCS2R_CALIBRATE (1 << 24)
+/* Bits 25..31 mystery */
+#define AHCI_TIMER1MS 0x00E0
+#define AHCI_GPARAM1R 0x00E8
+#define AHCI_GPARAM2R 0x00EC
+#define AHCI_PPARAMR 0x00F0
+#define AHCI_TESTR 0x00F4
+#define AHCI_VERSIONR 0x00F8
+#define AHCI_IDR 0x00FC
+#define AHCI_RWCR 0x00FC
+
+#define AHCI_P0DMACR 0x0070
+#define AHCI_P0PHYCR 0x0078
+#define AHCI_P0PHYSR 0x007C
+
+/* Kludge for CUBIEBOARD (and Banana PI too) */
+#define GPIO_AHCI_PWR 40
+
+static void inline
+ahci_set(struct resource *m, bus_size_t off, uint32_t set)
+{
+ uint32_t val = ATA_INL(m, off);
+
+ val |= set;
+ ATA_OUTL(m, off, val);
+}
+
+static void inline
+ahci_clr(struct resource *m, bus_size_t off, uint32_t clr)
+{
+ uint32_t val = ATA_INL(m, off);
+
+ val &= ~clr;
+ ATA_OUTL(m, off, val);
+}
+
+static void inline
+ahci_mask_set(struct resource *m, bus_size_t off, uint32_t mask, uint32_t set)
+{
+ uint32_t val = ATA_INL(m, off);
+
+ val &= mask;
+ val |= set;
+ ATA_OUTL(m, off, val);
+}
+
+/*
+ * Should this be phy_reset or phy_init
+ */
+#define PHY_RESET_TIMEOUT 1000
+static void
+ahci_a10_phy_reset(device_t dev)
+{
+ uint32_t to, val;
+ struct ahci_controller *ctlr = device_get_softc(dev);
+
+ /*
+ * Here start the the magic -- most of the comments are based
+ * on guesswork, names of routines and printf error
+ * messages. The code works, but it will do that even if the
+ * comments are 100% BS.
+ */
+
+ /*
+ * Lock out other access while we initialize. Or at least that
+ * seems to be the case based on Linux SDK #defines. Maybe this
+ * put things into reset?
+ */
+ ATA_OUTL(ctlr->r_mem, AHCI_RWCR, 0);
+ DELAY(100);
+
+ /*
+ * Set bit 19 in PHYCS1R. Guessing this disables driving the PHY
+ * port for a bit while we reset things.
+ */
+ ahci_set(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_HIGHZ);
+
+ /*
+ * Frob PHYCS0R...
+ */
+ ahci_mask_set(ctlr->r_mem, AHCI_PHYCS0R,
+ ~PHYCS0R_UF2_MASK,
+ PHYCS0R_UF2_INIT | PHYCS0R_BIT23 | PHYCS0R_BIT18);
+
+ /*
+ * Set three fields in PHYCS1R
+ */
+ ahci_mask_set(ctlr->r_mem, AHCI_PHYCS1R,
+ ~(PHYCS1R_UF1_MASK | PHYCS1R_UF2_MASK | PHYCS1R_UF3_MASK),
+ PHYCS1R_UF1_INIT | PHYCS1R_UF2_INIT | PHYCS1R_UF3_INIT);
+
+ /*
+ * Two more mystery bits in PHYCS1R. -- can these be combined above?
+ */
+ ahci_set(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_BIT15 | PHYCS1R_BIT28);
+
+ /*
+ * Now clear that first mysery bit. Perhaps this starts
+ * driving the PHY again so we can power it up and start
+ * talking to the SATA drive, if any below.
+ */
+ ahci_clr(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_HIGHZ);
+
+ /*
+ * Frob PHYCS0R again...
+ */
+ ahci_mask_set(ctlr->r_mem, AHCI_PHYCS0R,
+ ~PHYCS0R_UF1_MASK, PHYCS0R_UF1_INIT);
+
+ /*
+ * Frob PHYCS2R, because 25 means something?
+ */
+ ahci_mask_set(ctlr->r_mem, AHCI_PHYCS2R, ~PHYCS2R_UF1_MASK,
+ PHYCS2R_UF1_INIT);
+
+ DELAY(100); /* WAG */
+
+ /*
+ * Turn on the power to the PHY and wait for it to report back
+ * good?
+ */
+ ahci_set(ctlr->r_mem, AHCI_PHYCS0R, PHYCS0R_POWER_ENABLE);
+ for (to = PHY_RESET_TIMEOUT; to > 0; to--) {
+ val = ATA_INL(ctlr->r_mem, AHCI_PHYCS0R);
+ if ((val & PHYCS0R_POWER_STATUS_MASK) == PHYCS0R_PS_GOOD)
+ break;
+ DELAY(10);
+ }
+ if (to == 0 && bootverbose)
+ device_printf(dev, "PHY Power Failed PHYCS0R = %#x\n", val);
+
+ /*
+ * Calibrate the clocks between the device and the host. This appears
+ * to be an automated process that clears the bit when it is done.
+ */
+ ahci_set(ctlr->r_mem, AHCI_PHYCS2R, PHYCS2R_CALIBRATE);
+ for (to = PHY_RESET_TIMEOUT; to > 0; to--) {
+ val = ATA_INL(ctlr->r_mem, AHCI_PHYCS2R);
+ if ((val & PHYCS2R_CALIBRATE) == 0)
+ break;
+ DELAY(10);
+ }
+ if (to == 0 && bootverbose)
+ device_printf(dev, "PHY Cal Failed PHYCS2R %#x\n", val);
+
+ /*
+ * OK, let things settle down a bit.
+ */
+ DELAY(1000);
+
+ /*
+ * Go back into normal mode now that we've calibrated the PHY.
+ */
+ ATA_OUTL(ctlr->r_mem, AHCI_RWCR, 7);
+}
+
+static void
+ahci_a10_ch_start(struct ahci_channel *ch)
+{
+ uint32_t reg;
+
+ /*
+ * Magical values from Allwinner SDK, setup the DMA before start
+ * operations on this channel.
+ */
+ reg = ATA_INL(ch->r_mem, AHCI_P0DMACR);
+ reg &= ~0xff00;
+ reg |= 0x4400;
+ ATA_OUTL(ch->r_mem, AHCI_P0DMACR, reg);
+}
+
+static int
+ahci_a10_ctlr_reset(device_t dev)
+{
+
+ ahci_a10_phy_reset(dev);
+
+ return (ahci_ctlr_reset(dev));
+}
+
+static int
+ahci_a10_probe(device_t dev)
+{
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-ahci"))
+ return (ENXIO);
+ device_set_desc(dev, "Allwinner Integrated AHCI controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ahci_a10_attach(device_t dev)
+{
+ device_t gpio;
+ int error;
+ struct ahci_controller *ctlr;
+
+ ctlr = device_get_softc(dev);
+ ctlr->quirks = AHCI_Q_NOPMP;
+ ctlr->vendorid = 0;
+ ctlr->deviceid = 0;
+ ctlr->subvendorid = 0;
+ ctlr->subdeviceid = 0;
+ ctlr->r_rid = 0;
+ if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &ctlr->r_rid, RF_ACTIVE)))
+ return (ENXIO);
+
+ /* Turn on the PLL for SATA */
+ a10_clk_ahci_activate();
+
+ /* Apply power to the drive, if any */
+ gpio = devclass_get_device(devclass_find("gpio"), 0);
+ if (gpio == NULL) {
+ device_printf(dev,
+ "GPIO device not yet present (SATA won't work).\n");
+ bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid,
+ ctlr->r_mem);
+ return (ENXIO);
+ }
+ GPIO_PIN_SETFLAGS(gpio, GPIO_AHCI_PWR, GPIO_PIN_OUTPUT);
+ GPIO_PIN_SET(gpio, GPIO_AHCI_PWR, GPIO_PIN_HIGH);
+ DELAY(10000);
+
+ /* Reset controller */
+ if ((error = ahci_a10_ctlr_reset(dev)) != 0) {
+ bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid,
+ ctlr->r_mem);
+ return (error);
+ };
+
+ /*
+ * No MSI registers on this platform.
+ */
+ ctlr->msi = 0;
+ ctlr->numirqs = 1;
+
+ /* Channel start callback(). */
+ ctlr->ch_start = ahci_a10_ch_start;
+
+ /*
+ * Note: ahci_attach will release ctlr->r_mem on errors automatically
+ */
+ return (ahci_attach(dev));
+}
+
+static int
+ahci_a10_detach(device_t dev)
+{
+
+ return (ahci_detach(dev));
+}
+
+devclass_t ahci_devclass;
+
+static device_method_t ahci_ata_methods[] = {
+ DEVMETHOD(device_probe, ahci_a10_probe),
+ DEVMETHOD(device_attach, ahci_a10_attach),
+ DEVMETHOD(device_detach, ahci_a10_detach),
+ DEVMETHOD(bus_print_child, ahci_print_child),
+ DEVMETHOD(bus_alloc_resource, ahci_alloc_resource),
+ DEVMETHOD(bus_release_resource, ahci_release_resource),
+ DEVMETHOD(bus_setup_intr, ahci_setup_intr),
+ DEVMETHOD(bus_teardown_intr,ahci_teardown_intr),
+ DEVMETHOD(bus_child_location_str, ahci_child_location_str),
+ DEVMETHOD_END
+};
+
+static driver_t ahci_ata_driver = {
+ "ahci",
+ ahci_ata_methods,
+ sizeof(struct ahci_controller)
+};
+
+DRIVER_MODULE(ahci, simplebus, ahci_ata_driver, ahci_devclass, 0, 0);
diff --git a/sys/arm/allwinner/a10_clk.c b/sys/arm/allwinner/a10_clk.c
index 0c3634f..2f41d6f 100644
--- a/sys/arm/allwinner/a10_clk.c
+++ b/sys/arm/allwinner/a10_clk.c
@@ -36,26 +36,18 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/rman.h>
-#include <sys/timeet.h>
-#include <sys/timetc.h>
-#include <sys/watchdog.h>
#include <machine/bus.h>
-#include <machine/cpu.h>
-#include <machine/intr.h>
-#include <dev/fdt/fdt_common.h>
#include <dev/ofw/openfirm.h>
-#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
-#include <machine/bus.h>
-
#include "a10_clk.h"
struct a10_ccm_softc {
struct resource *res;
bus_space_tag_t bst;
bus_space_handle_t bsh;
+ int pll6_enabled;
};
static struct a10_ccm_softc *a10_ccm_sc = NULL;
@@ -190,6 +182,38 @@ a10_clk_emac_activate(void)
return (0);
}
+int
+a10_clk_gmac_activate(phandle_t node)
+{
+ char *phy_type;
+ struct a10_ccm_softc *sc;
+ uint32_t reg_value;
+
+ sc = a10_ccm_sc;
+ if (sc == NULL)
+ return (ENXIO);
+
+ /* Gating AHB clock for GMAC */
+ reg_value = ccm_read_4(sc, CCM_AHB_GATING1);
+ reg_value |= CCM_AHB_GATING_GMAC;
+ ccm_write_4(sc, CCM_AHB_GATING1, reg_value);
+
+ /* Set GMAC mode. */
+ reg_value = CCM_GMAC_CLK_MII;
+ if (OF_getprop_alloc(node, "phy-type", 1, (void **)&phy_type) > 0) {
+ if (strcasecmp(phy_type, "rgmii") == 0)
+ reg_value = CCM_GMAC_CLK_RGMII | CCM_GMAC_MODE_RGMII;
+ else if (strcasecmp(phy_type, "rgmii-bpi") == 0) {
+ reg_value = CCM_GMAC_CLK_RGMII | CCM_GMAC_MODE_RGMII;
+ reg_value |= (3 << CCM_GMAC_CLK_DELAY_SHIFT);
+ }
+ free(phy_type, M_OFWPROP);
+ }
+ ccm_write_4(sc, CCM_GMAC_CLK, reg_value);
+
+ return (0);
+}
+
static void
a10_clk_pll6_enable(void)
{
@@ -203,6 +227,8 @@ a10_clk_pll6_enable(void)
* For other uses the output frequency is 24MHz * n * k / 2.
*/
sc = a10_ccm_sc;
+ if (sc->pll6_enabled)
+ return;
reg_value = ccm_read_4(sc, CCM_PLL6_CFG);
reg_value &= ~CCM_PLL_CFG_BYPASS;
reg_value &= ~(CCM_PLL_CFG_FACTOR_K | CCM_PLL_CFG_FACTOR_M |
@@ -211,6 +237,7 @@ a10_clk_pll6_enable(void)
reg_value |= CCM_PLL6_CFG_SATA_CLKEN;
reg_value |= CCM_PLL_CFG_ENABLE;
ccm_write_4(sc, CCM_PLL6_CFG, reg_value);
+ sc->pll6_enabled = 1;
}
static unsigned int
@@ -229,6 +256,29 @@ a10_clk_pll6_get_rate(void)
}
int
+a10_clk_ahci_activate(void)
+{
+ struct a10_ccm_softc *sc;
+ uint32_t reg_value;
+
+ sc = a10_ccm_sc;
+ if (sc == NULL)
+ return (ENXIO);
+
+ a10_clk_pll6_enable();
+
+ /* Gating AHB clock for SATA */
+ reg_value = ccm_read_4(sc, CCM_AHB_GATING0);
+ reg_value |= CCM_AHB_GATING_SATA;
+ ccm_write_4(sc, CCM_AHB_GATING0, reg_value);
+ DELAY(1000);
+
+ ccm_write_4(sc, CCM_SATA_CLK, CCM_PLL_CFG_ENABLE);
+
+ return (0);
+}
+
+int
a10_clk_mmc_activate(int devid)
{
struct a10_ccm_softc *sc;
diff --git a/sys/arm/allwinner/a10_clk.h b/sys/arm/allwinner/a10_clk.h
index f9b88c1..e5aa8c2 100644
--- a/sys/arm/allwinner/a10_clk.h
+++ b/sys/arm/allwinner/a10_clk.h
@@ -29,8 +29,6 @@
#ifndef _A10_CLK_H_
#define _A10_CLK_H_
-#define CCMU_BASE 0xe1c20000
-
#define CCM_PLL1_CFG 0x0000
#define CCM_PLL1_TUN 0x0004
#define CCM_PLL2_CFG 0x0008
@@ -99,12 +97,25 @@
#define CCM_LVDS_CLK 0x014c
#define CCM_HDMI_CLK 0x0150
#define CCM_MALI400_CLK 0x0154
+#define CCM_GMAC_CLK 0x0164
+
+#define CCM_GMAC_CLK_DELAY_SHIFT 10
+#define CCM_GMAC_CLK_MODE_MASK 0x7
+#define CCM_GMAC_MODE_RGMII (1 << 2)
+#define CCM_GMAC_CLK_MII 0x0
+#define CCM_GMAC_CLK_EXT_RGMII 0x1
+#define CCM_GMAC_CLK_RGMII 0x2
+/* AHB_GATING_REG0 */
#define CCM_AHB_GATING_USB0 (1 << 0)
#define CCM_AHB_GATING_EHCI0 (1 << 1)
#define CCM_AHB_GATING_EHCI1 (1 << 3)
#define CCM_AHB_GATING_SDMMC0 (1 << 8)
#define CCM_AHB_GATING_EMAC (1 << 17)
+#define CCM_AHB_GATING_SATA (1 << 25)
+
+/* AHB_GATING_REG1 */
+#define CCM_AHB_GATING_GMAC (1 << 17)
#define CCM_USB_PHY (1 << 8)
#define CCM_USB0_RESET (1 << 0)
@@ -140,6 +151,8 @@
int a10_clk_usb_activate(void);
int a10_clk_usb_deactivate(void);
int a10_clk_emac_activate(void);
+int a10_clk_gmac_activate(phandle_t);
+int a10_clk_ahci_activate(void);
int a10_clk_mmc_activate(int);
int a10_clk_mmc_cfg(int, int);
diff --git a/sys/arm/allwinner/a10_gpio.c b/sys/arm/allwinner/a10_gpio.c
index b2dca00..c461013 100644
--- a/sys/arm/allwinner/a10_gpio.c
+++ b/sys/arm/allwinner/a10_gpio.c
@@ -524,8 +524,9 @@ static driver_t a10_gpio_driver = {
DRIVER_MODULE(a10_gpio, simplebus, a10_gpio_driver, a10_gpio_devclass, 0, 0);
int
-a10_emac_gpio_config(uint32_t pin)
+a10_gpio_ethernet_activate(uint32_t func)
{
+ int i;
struct a10_gpio_softc *sc = a10_gpio_sc;
if (sc == NULL)
@@ -533,7 +534,8 @@ a10_emac_gpio_config(uint32_t pin)
/* Configure pin mux settings for MII. */
A10_GPIO_LOCK(sc);
- a10_gpio_set_function(sc, pin, A10_GPIO_PULLDOWN);
+ for (i = 0; i <= 17; i++)
+ a10_gpio_set_function(sc, i, func);
A10_GPIO_UNLOCK(sc);
return (0);
diff --git a/sys/arm/allwinner/a10_gpio.h b/sys/arm/allwinner/a10_gpio.h
index 0e1dc86..e78b18f 100644
--- a/sys/arm/allwinner/a10_gpio.h
+++ b/sys/arm/allwinner/a10_gpio.h
@@ -29,6 +29,9 @@
#ifndef _A10_GPIO_H_
#define _A10_GPIO_H_
-int a10_emac_gpio_config(uint32_t pin);
+#define A10_GPIO_FUNC_MII 2
+#define A10_GPIO_FUNC_RGMII 5
+
+int a10_gpio_ethernet_activate(uint32_t);
#endif
diff --git a/sys/arm/allwinner/a10_mmc.c b/sys/arm/allwinner/a10_mmc.c
index 85ce980..c728e3b 100644
--- a/sys/arm/allwinner/a10_mmc.c
+++ b/sys/arm/allwinner/a10_mmc.c
@@ -54,6 +54,13 @@ __FBSDID("$FreeBSD$");
#define A10_MMC_MEMRES 0
#define A10_MMC_IRQRES 1
#define A10_MMC_RESSZ 2
+#define A10_MMC_DMA_SEGS 16
+#define A10_MMC_DMA_MAX_SIZE 0x2000
+#define A10_MMC_DMA_FTRGLEVEL 0x20070008
+
+static int a10_mmc_pio_mode = 0;
+
+TUNABLE_INT("hw.a10.mmc.pio_mode", &a10_mmc_pio_mode);
struct a10_mmc_softc {
bus_space_handle_t a10_bsh;
@@ -71,6 +78,16 @@ struct a10_mmc_softc {
uint32_t a10_intr;
uint32_t a10_intr_wait;
void * a10_intrhand;
+
+ /* Fields required for DMA access. */
+ bus_addr_t a10_dma_desc_phys;
+ bus_dmamap_t a10_dma_map;
+ bus_dma_tag_t a10_dma_tag;
+ void * a10_dma_desc;
+ bus_dmamap_t a10_dma_buf_map;
+ bus_dma_tag_t a10_dma_buf_tag;
+ int a10_dma_inuse;
+ int a10_dma_map_err;
};
static struct resource_spec a10_mmc_res_spec[] = {
@@ -82,6 +99,7 @@ static struct resource_spec a10_mmc_res_spec[] = {
static int a10_mmc_probe(device_t);
static int a10_mmc_attach(device_t);
static int a10_mmc_detach(device_t);
+static int a10_mmc_setup_dma(struct a10_mmc_softc *);
static int a10_mmc_reset(struct a10_mmc_softc *);
static void a10_mmc_intr(void *);
static int a10_mmc_update_clock(struct a10_mmc_softc *);
@@ -166,6 +184,14 @@ a10_mmc_attach(device_t dev)
goto fail;
}
+ if (a10_mmc_pio_mode == 0 && a10_mmc_setup_dma(sc) != 0) {
+ device_printf(sc->a10_dev, "Couldn't setup DMA!\n");
+ a10_mmc_pio_mode = 1;
+ }
+ if (bootverbose)
+ device_printf(sc->a10_dev, "DMA status: %s\n",
+ a10_mmc_pio_mode ? "disabled" : "enabled");
+
sc->a10_host.f_min = 400000;
sc->a10_host.f_max = 52000000;
sc->a10_host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340;
@@ -201,6 +227,140 @@ a10_mmc_detach(device_t dev)
return (EBUSY);
}
+static void
+a10_dma_desc_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int err)
+{
+ struct a10_mmc_softc *sc;
+
+ sc = (struct a10_mmc_softc *)arg;
+ if (err) {
+ sc->a10_dma_map_err = err;
+ return;
+ }
+ sc->a10_dma_desc_phys = segs[0].ds_addr;
+}
+
+static int
+a10_mmc_setup_dma(struct a10_mmc_softc *sc)
+{
+ int dma_desc_size, error;
+
+ /* Allocate the DMA descriptor memory. */
+ dma_desc_size = sizeof(struct a10_mmc_dma_desc) * A10_MMC_DMA_SEGS;
+ error = bus_dma_tag_create(bus_get_dma_tag(sc->a10_dev), 1, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ dma_desc_size, 1, dma_desc_size, 0, NULL, NULL, &sc->a10_dma_tag);
+ if (error)
+ return (error);
+ error = bus_dmamem_alloc(sc->a10_dma_tag, &sc->a10_dma_desc,
+ BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->a10_dma_map);
+ if (error)
+ return (error);
+
+ error = bus_dmamap_load(sc->a10_dma_tag, sc->a10_dma_map,
+ sc->a10_dma_desc, dma_desc_size, a10_dma_desc_cb, sc, 0);
+ if (error)
+ return (error);
+ if (sc->a10_dma_map_err)
+ return (sc->a10_dma_map_err);
+
+ /* Create the DMA map for data transfers. */
+ error = bus_dma_tag_create(bus_get_dma_tag(sc->a10_dev), 1, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ A10_MMC_DMA_MAX_SIZE * A10_MMC_DMA_SEGS, A10_MMC_DMA_SEGS,
+ A10_MMC_DMA_MAX_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL,
+ &sc->a10_dma_buf_tag);
+ if (error)
+ return (error);
+ error = bus_dmamap_create(sc->a10_dma_buf_tag, 0,
+ &sc->a10_dma_buf_map);
+ if (error)
+ return (error);
+
+ return (0);
+}
+
+static void
+a10_dma_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int err)
+{
+ int i;
+ struct a10_mmc_dma_desc *dma_desc;
+ struct a10_mmc_softc *sc;
+
+ sc = (struct a10_mmc_softc *)arg;
+ sc->a10_dma_map_err = err;
+ dma_desc = sc->a10_dma_desc;
+ /* Note nsegs is guaranteed to be zero if err is non-zero. */
+ for (i = 0; i < nsegs; i++) {
+ dma_desc[i].buf_size = segs[i].ds_len;
+ dma_desc[i].buf_addr = segs[i].ds_addr;
+ dma_desc[i].config = A10_MMC_DMA_CONFIG_CH |
+ A10_MMC_DMA_CONFIG_OWN;
+ if (i == 0)
+ dma_desc[i].config |= A10_MMC_DMA_CONFIG_FD;
+ if (i < (nsegs - 1)) {
+ dma_desc[i].config |= A10_MMC_DMA_CONFIG_DIC;
+ dma_desc[i].next = sc->a10_dma_desc_phys +
+ ((i + 1) * sizeof(struct a10_mmc_dma_desc));
+ } else {
+ dma_desc[i].config |= A10_MMC_DMA_CONFIG_LD |
+ A10_MMC_DMA_CONFIG_ER;
+ dma_desc[i].next = 0;
+ }
+ }
+}
+
+static int
+a10_mmc_prepare_dma(struct a10_mmc_softc *sc)
+{
+ bus_dmasync_op_t sync_op;
+ int error;
+ struct mmc_command *cmd;
+ uint32_t val;
+
+ cmd = sc->a10_req->cmd;
+ if (cmd->data->len > A10_MMC_DMA_MAX_SIZE * A10_MMC_DMA_SEGS)
+ return (EFBIG);
+ error = bus_dmamap_load(sc->a10_dma_buf_tag, sc->a10_dma_buf_map,
+ cmd->data->data, cmd->data->len, a10_dma_cb, sc, BUS_DMA_NOWAIT);
+ if (error)
+ return (error);
+ if (sc->a10_dma_map_err)
+ return (sc->a10_dma_map_err);
+
+ sc->a10_dma_inuse = 1;
+ if (cmd->data->flags & MMC_DATA_WRITE)
+ sync_op = BUS_DMASYNC_PREWRITE;
+ else
+ sync_op = BUS_DMASYNC_PREREAD;
+ bus_dmamap_sync(sc->a10_dma_buf_tag, sc->a10_dma_buf_map, sync_op);
+ bus_dmamap_sync(sc->a10_dma_tag, sc->a10_dma_map, BUS_DMASYNC_PREWRITE);
+
+ val = A10_MMC_READ_4(sc, A10_MMC_IMASK);
+ val &= ~(A10_MMC_RX_DATA_REQ | A10_MMC_TX_DATA_REQ);
+ A10_MMC_WRITE_4(sc, A10_MMC_IMASK, val);
+ val = A10_MMC_READ_4(sc, A10_MMC_GCTRL);
+ val &= ~A10_MMC_ACCESS_BY_AHB;
+ val |= A10_MMC_DMA_ENABLE;
+ A10_MMC_WRITE_4(sc, A10_MMC_GCTRL, val);
+ val |= A10_MMC_DMA_RESET;
+ A10_MMC_WRITE_4(sc, A10_MMC_GCTRL, val);
+ A10_MMC_WRITE_4(sc, A10_MMC_DMAC, A10_MMC_IDMAC_SOFT_RST);
+ A10_MMC_WRITE_4(sc, A10_MMC_DMAC,
+ A10_MMC_IDMAC_IDMA_ON | A10_MMC_IDMAC_FIX_BURST);
+ val = A10_MMC_READ_4(sc, A10_MMC_IDIE);
+ val &= ~(A10_MMC_IDMAC_RECEIVE_INT | A10_MMC_IDMAC_TRANSMIT_INT);
+ if (cmd->data->flags & MMC_DATA_WRITE)
+ val |= A10_MMC_IDMAC_TRANSMIT_INT;
+ else
+ val |= A10_MMC_IDMAC_RECEIVE_INT;
+ A10_MMC_WRITE_4(sc, A10_MMC_IDIE, val);
+ A10_MMC_WRITE_4(sc, A10_MMC_DLBA, sc->a10_dma_desc_phys);
+ A10_MMC_WRITE_4(sc, A10_MMC_FTRGL, A10_MMC_DMA_FTRGLEVEL);
+
+ return (0);
+}
+
static int
a10_mmc_reset(struct a10_mmc_softc *sc)
{
@@ -222,15 +382,14 @@ a10_mmc_reset(struct a10_mmc_softc *sc)
/* Clear pending interrupts. */
A10_MMC_WRITE_4(sc, A10_MMC_RINTR, 0xffffffff);
+ A10_MMC_WRITE_4(sc, A10_MMC_IDST, 0xffffffff);
/* Unmask interrupts. */
A10_MMC_WRITE_4(sc, A10_MMC_IMASK,
A10_MMC_CMD_DONE | A10_MMC_INT_ERR_BIT |
- A10_MMC_DATA_OVER | A10_MMC_AUTOCMD_DONE |
- A10_MMC_RX_DATA_REQ | A10_MMC_TX_DATA_REQ);
+ A10_MMC_DATA_OVER | A10_MMC_AUTOCMD_DONE);
/* Enable interrupts and AHB access. */
A10_MMC_WRITE_4(sc, A10_MMC_GCTRL,
- A10_MMC_READ_4(sc, A10_MMC_GCTRL) |
- A10_MMC_INT_ENABLE | A10_MMC_ACCESS_BY_AHB);
+ A10_MMC_READ_4(sc, A10_MMC_GCTRL) | A10_MMC_INT_ENABLE);
return (0);
}
@@ -247,15 +406,19 @@ a10_mmc_req_done(struct a10_mmc_softc *sc)
a10_mmc_reset(sc);
a10_mmc_update_clock(sc);
}
- /* Reset the FIFO. */
- A10_MMC_WRITE_4(sc, A10_MMC_GCTRL,
- A10_MMC_READ_4(sc, A10_MMC_GCTRL) | A10_MMC_FIFO_RESET);
+ if (sc->a10_dma_inuse == 0) {
+ /* Reset the FIFO. */
+ A10_MMC_WRITE_4(sc, A10_MMC_GCTRL,
+ A10_MMC_READ_4(sc, A10_MMC_GCTRL) | A10_MMC_FIFO_RESET);
+ }
req = sc->a10_req;
callout_stop(&sc->a10_timeoutc);
sc->a10_req = NULL;
sc->a10_intr = 0;
sc->a10_resid = 0;
+ sc->a10_dma_inuse = 0;
+ sc->a10_dma_map_err = 0;
sc->a10_intr_wait = 0;
req->done(req);
}
@@ -295,7 +458,7 @@ a10_mmc_req_ok(struct a10_mmc_softc *sc)
a10_mmc_req_done(sc);
}
-static void
+static void
a10_mmc_timeout(void *arg)
{
struct a10_mmc_softc *sc;
@@ -335,28 +498,29 @@ a10_mmc_pio_transfer(struct a10_mmc_softc *sc, struct mmc_data *data)
static void
a10_mmc_intr(void *arg)
{
+ bus_dmasync_op_t sync_op;
struct a10_mmc_softc *sc;
struct mmc_data *data;
- uint32_t imask, rint;
+ uint32_t idst, imask, rint;
sc = (struct a10_mmc_softc *)arg;
A10_MMC_LOCK(sc);
rint = A10_MMC_READ_4(sc, A10_MMC_RINTR);
+ idst = A10_MMC_READ_4(sc, A10_MMC_IDST);
imask = A10_MMC_READ_4(sc, A10_MMC_IMASK);
- if (imask == 0 && rint == 0) {
+ if (idst == 0 && imask == 0 && rint == 0) {
A10_MMC_UNLOCK(sc);
return;
}
#ifdef DEBUG
- device_printf(sc->a10_dev, "imask: %#x, rint: %#x\n", imask, rint);
+ device_printf(sc->a10_dev, "idst: %#x, imask: %#x, rint: %#x\n",
+ idst, imask, rint);
#endif
if (sc->a10_req == NULL) {
device_printf(sc->a10_dev,
"Spurious interrupt - no active request, rint: 0x%08X\n",
rint);
- A10_MMC_WRITE_4(sc, A10_MMC_RINTR, rint);
- A10_MMC_UNLOCK(sc);
- return;
+ goto end;
}
if (rint & A10_MMC_INT_ERR_BIT) {
device_printf(sc->a10_dev, "error rint: 0x%08X\n", rint);
@@ -364,20 +528,39 @@ a10_mmc_intr(void *arg)
sc->a10_req->cmd->error = MMC_ERR_TIMEOUT;
else
sc->a10_req->cmd->error = MMC_ERR_FAILED;
- A10_MMC_WRITE_4(sc, A10_MMC_RINTR, rint);
a10_mmc_req_done(sc);
- A10_MMC_UNLOCK(sc);
- return;
+ goto end;
+ }
+ if (idst & A10_MMC_IDMAC_ERROR) {
+ device_printf(sc->a10_dev, "error idst: 0x%08x\n", idst);
+ sc->a10_req->cmd->error = MMC_ERR_FAILED;
+ a10_mmc_req_done(sc);
+ goto end;
}
sc->a10_intr |= rint;
data = sc->a10_req->cmd->data;
- if (data != NULL && (rint & (A10_MMC_DATA_OVER |
- A10_MMC_RX_DATA_REQ | A10_MMC_TX_DATA_REQ)) != 0)
+ if (data != NULL && sc->a10_dma_inuse == 1 &&
+ (idst & A10_MMC_IDMAC_COMPLETE)) {
+ if (data->flags & MMC_DATA_WRITE)
+ sync_op = BUS_DMASYNC_POSTWRITE;
+ else
+ sync_op = BUS_DMASYNC_POSTREAD;
+ bus_dmamap_sync(sc->a10_dma_buf_tag, sc->a10_dma_buf_map,
+ sync_op);
+ bus_dmamap_sync(sc->a10_dma_tag, sc->a10_dma_map,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->a10_dma_buf_tag, sc->a10_dma_buf_map);
+ sc->a10_resid = data->len >> 2;
+ } else if (data != NULL && sc->a10_dma_inuse == 0 &&
+ (rint & (A10_MMC_DATA_OVER | A10_MMC_RX_DATA_REQ |
+ A10_MMC_TX_DATA_REQ)) != 0)
a10_mmc_pio_transfer(sc, data);
if ((sc->a10_intr & sc->a10_intr_wait) == sc->a10_intr_wait)
a10_mmc_req_ok(sc);
+end:
+ A10_MMC_WRITE_4(sc, A10_MMC_IDST, idst);
A10_MMC_WRITE_4(sc, A10_MMC_RINTR, rint);
A10_MMC_UNLOCK(sc);
}
@@ -388,7 +571,7 @@ a10_mmc_request(device_t bus, device_t child, struct mmc_request *req)
int blksz;
struct a10_mmc_softc *sc;
struct mmc_command *cmd;
- uint32_t cmdreg;
+ uint32_t cmdreg, val;
sc = device_get_softc(bus);
A10_MMC_LOCK(sc);
@@ -424,6 +607,19 @@ a10_mmc_request(device_t bus, device_t child, struct mmc_request *req)
blksz = min(cmd->data->len, MMC_SECTOR_SIZE);
A10_MMC_WRITE_4(sc, A10_MMC_BLKSZ, blksz);
A10_MMC_WRITE_4(sc, A10_MMC_BCNTR, cmd->data->len);
+
+ if (a10_mmc_pio_mode == 0)
+ a10_mmc_prepare_dma(sc);
+ /* Enable PIO access if sc->a10_dma_inuse is not set. */
+ if (sc->a10_dma_inuse == 0) {
+ val = A10_MMC_READ_4(sc, A10_MMC_GCTRL);
+ val &= ~A10_MMC_DMA_ENABLE;
+ val |= A10_MMC_ACCESS_BY_AHB;
+ A10_MMC_WRITE_4(sc, A10_MMC_GCTRL, val);
+ val = A10_MMC_READ_4(sc, A10_MMC_IMASK);
+ val |= A10_MMC_RX_DATA_REQ | A10_MMC_TX_DATA_REQ;
+ A10_MMC_WRITE_4(sc, A10_MMC_IMASK, val);
+ }
}
A10_MMC_WRITE_4(sc, A10_MMC_CARG, cmd->arg);
@@ -436,7 +632,7 @@ a10_mmc_request(device_t bus, device_t child, struct mmc_request *req)
}
static int
-a10_mmc_read_ivar(device_t bus, device_t child, int which,
+a10_mmc_read_ivar(device_t bus, device_t child, int which,
uintptr_t *result)
{
struct a10_mmc_softc *sc;
diff --git a/sys/arm/allwinner/a10_mmc.h b/sys/arm/allwinner/a10_mmc.h
index 84c13a5..4b5d943 100644
--- a/sys/arm/allwinner/a10_mmc.h
+++ b/sys/arm/allwinner/a10_mmc.h
@@ -66,7 +66,6 @@
#define A10_MMC_DMA_ENABLE (1U << 5)
#define A10_MMC_DEBOUNCE_ENABLE (1U << 8)
#define A10_MMC_DDR_MODE (1U << 10)
-#define A10_MMC_ACCESS_BY_DMA (1U << 30)
#define A10_MMC_ACCESS_BY_AHB (1U << 31)
#define A10_MMC_RESET \
(A10_MMC_SOFT_RESET | A10_MMC_FIFO_RESET | A10_MMC_DMA_RESET)
@@ -175,5 +174,25 @@
#define A10_MMC_IDMAC_RD (6U << 13)
#define A10_MMC_IDMAC_WR (7U << 13)
#define A10_MMC_IDMAC_DESC_CLOSE (8U << 13)
+#define A10_MMC_IDMAC_ERROR \
+ (A10_MMC_IDMAC_FATAL_BUS_ERR | A10_MMC_IDMAC_CARD_ERR_SUM | \
+ A10_MMC_IDMAC_DES_INVALID | A10_MMC_IDMAC_ABNORMAL_INT_SUM)
+#define A10_MMC_IDMAC_COMPLETE \
+ (A10_MMC_IDMAC_TRANSMIT_INT | A10_MMC_IDMAC_RECEIVE_INT)
+
+/* The DMA descriptor table. */
+struct a10_mmc_dma_desc {
+ uint32_t config;
+#define A10_MMC_DMA_CONFIG_DIC (1U << 1)
+#define A10_MMC_DMA_CONFIG_LD (1U << 2)
+#define A10_MMC_DMA_CONFIG_FD (1U << 3)
+#define A10_MMC_DMA_CONFIG_CH (1U << 4)
+#define A10_MMC_DMA_CONFIG_ER (1U << 5)
+#define A10_MMC_DMA_CONFIG_CES (1U << 30)
+#define A10_MMC_DMA_CONFIG_OWN (1U << 31)
+ uint32_t buf_size;
+ uint32_t buf_addr;
+ uint32_t next;
+};
#endif /* _A10_MMC_H_ */
diff --git a/sys/arm/allwinner/files.allwinner b/sys/arm/allwinner/files.allwinner
index 8447939..08e688b 100644
--- a/sys/arm/allwinner/files.allwinner
+++ b/sys/arm/allwinner/files.allwinner
@@ -5,6 +5,7 @@ arm/arm/bus_space_base.c standard
arm/arm/bus_space_asm_generic.S standard
arm/arm/bus_space_generic.c standard
+arm/allwinner/a10_ahci.c optional ahci
arm/allwinner/a10_clk.c standard
arm/allwinner/a10_common.c standard
arm/allwinner/a10_ehci.c optional ehci
diff --git a/sys/arm/allwinner/if_emac.c b/sys/arm/allwinner/if_emac.c
index e311049..18aeb8f 100644
--- a/sys/arm/allwinner/if_emac.c
+++ b/sys/arm/allwinner/if_emac.c
@@ -142,17 +142,12 @@ static int sysctl_hw_emac_proc_limit(SYSCTL_HANDLER_ARGS);
static void
emac_sys_setup(void)
{
- int i;
+ /* Activate EMAC clock. */
a10_clk_emac_activate();
-
- /*
- * Configure pin mux settings for MII.
- * Pins PA0 from PA17.
- */
- for (i = 0; i <= 17; i++)
- a10_emac_gpio_config(i);
- /* Map sram */
+ /* Set the pin mux to EMAC (mii). */
+ a10_gpio_ethernet_activate(A10_GPIO_FUNC_MII);
+ /* Map sram. */
a10_map_to_emac();
}
diff --git a/sys/arm/arm/genassym.c b/sys/arm/arm/genassym.c
index 11bb90f..900fc41 100644
--- a/sys/arm/arm/genassym.c
+++ b/sys/arm/arm/genassym.c
@@ -134,7 +134,9 @@ ASSYM(ARM_RAS_END, ARM_RAS_END);
#ifdef VFP
ASSYM(PCB_VFPSTATE, offsetof(struct pcb, pcb_vfpstate));
+#endif
+#if __ARM_ARCH >= 6
ASSYM(PC_CURPMAP, offsetof(struct pcpu, pc_curpmap));
#endif
diff --git a/sys/arm/conf/CUBIEBOARD b/sys/arm/conf/CUBIEBOARD
index c1df03a..2de16f7 100644
--- a/sys/arm/conf/CUBIEBOARD
+++ b/sys/arm/conf/CUBIEBOARD
@@ -57,7 +57,7 @@ device mmc # mmc/sd bus
device mmcsd # mmc/sd flash cards
# ATA controllers
-#device ahci # AHCI-compatible SATA controllers
+device ahci # AHCI-compatible SATA controllers
#device ata # Legacy ATA/SATA controllers
#options ATA_STATIC_ID # Static device numbering
@@ -78,7 +78,7 @@ device gpio
device scbus # SCSI bus (required for ATA/SCSI)
device da # Direct Access (disks)
-device pass
+device pass # Passthrough device (direct ATA/SCSI access)
# USB support
options USB_HOST_ALIGN=64 # Align usb buffers to cache line size.
diff --git a/sys/arm/conf/CUBIEBOARD2 b/sys/arm/conf/CUBIEBOARD2
index c8fded0..5330725 100644
--- a/sys/arm/conf/CUBIEBOARD2
+++ b/sys/arm/conf/CUBIEBOARD2
@@ -61,7 +61,7 @@ device mmc # mmc/sd bus
device mmcsd # mmc/sd flash cards
# ATA controllers
-#device ahci # AHCI-compatible SATA controllers
+device ahci # AHCI-compatible SATA controllers
#device ata # Legacy ATA/SATA controllers
#options ATA_STATIC_ID # Static device numbering
@@ -82,7 +82,7 @@ device gpio
device scbus # SCSI bus (required for ATA/SCSI)
device da # Direct Access (disks)
-device pass
+device pass # Passthrough device (direct ATA/SCSI access)
# USB support
options USB_HOST_ALIGN=64 # Align usb buffers to cache line size.
OpenPOWER on IntegriCloud