Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F110454533
D5752.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
226 KB
Referenced Files
None
Subscribers
None
D5752.diff
View Options
Index: head/sys/arm/allwinner/a10_ahci.c
===================================================================
--- head/sys/arm/allwinner/a10_ahci.c
+++ head/sys/arm/allwinner/a10_ahci.c
@@ -47,7 +47,7 @@
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/ahci/ahci.h>
-#include <arm/allwinner/a10_clk.h>
+#include <dev/extres/clk/clk.h>
/*
* Allwinner a1x/a2x/a8x SATA attachment. This is just the AHCI register
@@ -117,6 +117,8 @@
#define AHCI_P0PHYCR 0x0078
#define AHCI_P0PHYSR 0x007C
+#define PLL_FREQ 100000000
+
static void inline
ahci_set(struct resource *m, bus_size_t off, uint32_t set)
{
@@ -295,8 +297,11 @@
{
int error;
struct ahci_controller *ctlr;
+ clk_t clk_pll, clk_gate;
ctlr = device_get_softc(dev);
+ clk_pll = clk_gate = NULL;
+
ctlr->quirks = AHCI_Q_NOPMP;
ctlr->vendorid = 0;
ctlr->deviceid = 0;
@@ -307,15 +312,36 @@
&ctlr->r_rid, RF_ACTIVE)))
return (ENXIO);
- /* Turn on the PLL for SATA */
- a10_clk_ahci_activate();
-
+ /* Enable clocks */
+ error = clk_get_by_ofw_index(dev, 0, &clk_pll);
+ if (error != 0) {
+ device_printf(dev, "Cannot get PLL clock\n");
+ goto fail;
+ }
+ error = clk_get_by_ofw_index(dev, 1, &clk_gate);
+ if (error != 0) {
+ device_printf(dev, "Cannot get gate clock\n");
+ goto fail;
+ }
+ error = clk_set_freq(clk_pll, PLL_FREQ, CLK_SET_ROUND_DOWN);
+ if (error != 0) {
+ device_printf(dev, "Cannot set PLL frequency\n");
+ goto fail;
+ }
+ error = clk_enable(clk_pll);
+ if (error != 0) {
+ device_printf(dev, "Cannot enable PLL\n");
+ goto fail;
+ }
+ error = clk_enable(clk_gate);
+ if (error != 0) {
+ device_printf(dev, "Cannot enable clk gate\n");
+ goto fail;
+ }
+
/* 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);
- };
+ if ((error = ahci_a10_ctlr_reset(dev)) != 0)
+ goto fail;
/*
* No MSI registers on this platform.
@@ -330,6 +356,14 @@
* Note: ahci_attach will release ctlr->r_mem on errors automatically
*/
return (ahci_attach(dev));
+
+fail:
+ if (clk_gate != NULL)
+ clk_release(clk_gate);
+ if (clk_pll != NULL)
+ clk_release(clk_pll);
+ bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem);
+ return (error);
}
static int
Index: head/sys/arm/allwinner/a10_clk.h
===================================================================
--- head/sys/arm/allwinner/a10_clk.h
+++ head/sys/arm/allwinner/a10_clk.h
@@ -1,247 +0,0 @@
-/*-
- * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $FreeBSD$
- */
-
-#ifndef _A10_CLK_H_
-#define _A10_CLK_H_
-
-#define CCM_PLL1_CFG 0x0000
-#define CCM_PLL1_TUN 0x0004
-#define CCM_PLL2_CFG 0x0008
-#define CCM_PLL2_TUN 0x000c
-#define CCM_PLL3_CFG 0x0010
-#define CCM_PLL3_TUN 0x0014
-#define CCM_PLL4_CFG 0x0018
-#define CCM_PLL4_TUN 0x001c
-#define CCM_PLL5_CFG 0x0020
-#define CCM_PLL5_TUN 0x0024
-#define CCM_PLL6_CFG 0x0028
-#define CCM_PLL6_TUN 0x002c
-#define CCM_PLL7_CFG 0x0030
-#define CCM_PLL7_TUN 0x0034
-#define CCM_PLL1_TUN2 0x0038
-#define CCM_PLL5_TUN2 0x003c
-#define CCM_PLL_LOCK_DBG 0x004c
-#define CCM_OSC24M_CFG 0x0050
-#define CCM_CPU_AHB_APB0_CFG 0x0054
-#define CCM_APB1_CLK_DIV 0x0058
-#define CCM_AXI_GATING 0x005c
-#define CCM_AHB_GATING0 0x0060
-#define CCM_AHB_GATING1 0x0064
-#define CCM_APB0_GATING 0x0068
-#define CCM_APB1_GATING 0x006c
-#define CCM_NAND_SCLK_CFG 0x0080
-#define CCM_MS_SCLK_CFG 0x0084
-#define CCM_MMC0_SCLK_CFG 0x0088
-#define CCM_MMC1_SCLK_CFG 0x008c
-#define CCM_MMC2_SCLK_CFG 0x0090
-#define CCM_MMC3_SCLK_CFG 0x0094
-#define CCM_TS_CLK 0x0098
-#define CCM_SS_CLK 0x009c
-#define CCM_SPI0_CLK 0x00a0
-#define CCM_SPI1_CLK 0x00a4
-#define CCM_SPI2_CLK 0x00a8
-#define CCM_PATA_CLK 0x00ac
-#define CCM_IR0_CLK 0x00b0
-#define CCM_IR1_CLK 0x00b4
-#define CCM_IIS_CLK 0x00b8
-#define CCM_AC97_CLK 0x00bc
-#define CCM_SPDIF_CLK 0x00c0
-#define CCM_KEYPAD_CLK 0x00c4
-#define CCM_SATA_CLK 0x00c8
-#define CCM_USB_CLK 0x00cc
-#define CCM_GPS_CLK 0x00d0
-#define CCM_SPI3_CLK 0x00d4
-#define CCM_DRAM_CLK 0x0100
-#define CCM_BE0_SCLK 0x0104
-#define CCM_BE1_SCLK 0x0108
-#define CCM_FE0_CLK 0x010c
-#define CCM_FE1_CLK 0x0110
-#define CCM_MP_CLK 0x0114
-#define CCM_LCD0_CH0_CLK 0x0118
-#define CCM_LCD1_CH0_CLK 0x011c
-#define CCM_CSI_ISP_CLK 0x0120
-#define CCM_TVD_CLK 0x0128
-#define CCM_LCD0_CH1_CLK 0x012c
-#define CCM_LCD1_CH1_CLK 0x0130
-#define CCM_CS0_CLK 0x0134
-#define CCM_CS1_CLK 0x0138
-#define CCM_VE_CLK 0x013c
-#define CCM_AUDIO_CODEC_CLK 0x0140
-#define CCM_AVS_CLK 0x0144
-#define CCM_ACE_CLK 0x0148
-#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
-
-/* APB0_GATING */
-#define CCM_APB0_GATING_ADDA (1 << 0)
-
-/* AHB_GATING_REG0 */
-#define CCM_AHB_GATING_USB0 (1 << 0)
-#define CCM_AHB_GATING_EHCI0 (1 << 1)
-#define CCM_AHB_GATING_OHCI0 (1 << 2)
-#define CCM_AHB_GATING_EHCI1 (1 << 3)
-#define CCM_AHB_GATING_OHCI1 (1 << 4)
-#define CCM_AHB_GATING_DMA (1 << 6)
-#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_AHB_GATING_DE_BE1 (1 << 13)
-#define CCM_AHB_GATING_DE_BE0 (1 << 12)
-#define CCM_AHB_GATING_HDMI (1 << 11)
-#define CCM_AHB_GATING_LCD1 (1 << 5)
-#define CCM_AHB_GATING_LCD0 (1 << 4)
-
-/* APB1_GATING_REG */
-#define CCM_APB1_GATING_TWI (1 << 0)
-
-/* USB */
-#define CCM_USB_PHY (1 << 8)
-#define CCM_SCLK_GATING_OHCI1 (1 << 7)
-#define CCM_SCLK_GATING_OHCI0 (1 << 6)
-#define CCM_USBPHY2_RESET (1 << 2)
-#define CCM_USBPHY1_RESET (1 << 1)
-#define CCM_USBPHY0_RESET (1 << 0)
-
-#define CCM_PLL_CFG_ENABLE (1U << 31)
-#define CCM_PLL_CFG_BYPASS (1U << 30)
-#define CCM_PLL_CFG_PLL5 (1U << 25)
-#define CCM_PLL_CFG_PLL6 (1U << 24)
-#define CCM_PLL_CFG_FACTOR_N 0x1f00
-#define CCM_PLL_CFG_FACTOR_N_SHIFT 8
-#define CCM_PLL_CFG_FACTOR_K 0x30
-#define CCM_PLL_CFG_FACTOR_K_SHIFT 4
-#define CCM_PLL_CFG_FACTOR_M 0x3
-
-#define CCM_PLL2_CFG_POSTDIV 0x3c000000
-#define CCM_PLL2_CFG_POSTDIV_SHIFT 26
-#define CCM_PLL2_CFG_PREDIV 0x1f
-#define CCM_PLL2_CFG_PREDIV_SHIFT 0
-
-#define CCM_PLL3_CFG_MODE_SEL_SHIFT 15
-#define CCM_PLL3_CFG_MODE_SEL_FRACT (0 << CCM_PLL3_CFG_MODE_SEL_SHIFT)
-#define CCM_PLL3_CFG_MODE_SEL_INT (1 << CCM_PLL3_CFG_MODE_SEL_SHIFT)
-#define CCM_PLL3_CFG_FUNC_SET_SHIFT 14
-#define CCM_PLL3_CFG_FUNC_SET_270MHZ (0 << CCM_PLL3_CFG_FUNC_SET_SHIFT)
-#define CCM_PLL3_CFG_FUNC_SET_297MHZ (1 << CCM_PLL3_CFG_FUNC_SET_SHIFT)
-#define CCM_PLL3_CFG_FACTOR_M 0x7f
-
-#define CCM_PLL5_CFG_OUT_EXT_DIV_P 0x30000
-#define CCM_PLL5_CFG_OUT_EXT_DIV_P_SHIFT 16
-
-#define CCM_PLL6_CFG_SATA_CLKEN (1U << 14)
-
-#define CCM_SD_CLK_SRC_SEL 0x3000000
-#define CCM_SD_CLK_SRC_SEL_SHIFT 24
-#define CCM_SD_CLK_SRC_SEL_OSC24M 0
-#define CCM_SD_CLK_SRC_SEL_PLL6 1
-#define CCM_SD_CLK_PHASE_CTR 0x700000
-#define CCM_SD_CLK_PHASE_CTR_SHIFT 20
-#define CCM_SD_CLK_DIV_RATIO_N 0x30000
-#define CCM_SD_CLK_DIV_RATIO_N_SHIFT 16
-#define CCM_SD_CLK_OPHASE_CTR 0x700
-#define CCM_SD_CLK_OPHASE_CTR_SHIFT 8
-#define CCM_SD_CLK_DIV_RATIO_M 0xf
-
-#define CCM_AUDIO_CODEC_ENABLE (1U << 31)
-
-#define CCM_LCD_CH0_SCLK_GATING (1U << 31)
-#define CCM_LCD_CH0_RESET (1U << 30)
-#define CCM_LCD_CH0_SRC_SEL 0x03000000
-#define CCM_LCD_CH0_SRC_SEL_SHIFT 24
-#define CCM_LCD_CH0_SRC_SEL_PLL3 0
-#define CCM_LCD_CH0_SRC_SEL_PLL7 1
-#define CCM_LCD_CH0_SRC_SEL_PLL3_2X 2
-#define CCM_LCD_CH0_SRC_SEL_PLL6_2X 3
-
-#define CCM_LCD_CH1_SCLK2_GATING (1U << 31)
-#define CCM_LCD_CH1_SRC_SEL 0x03000000
-#define CCM_LCD_CH1_SRC_SEL_SHIFT 24
-#define CCM_LCD_CH1_SRC_SEL_PLL3 0
-#define CCM_LCD_CH1_SRC_SEL_PLL7 1
-#define CCM_LCD_CH1_SRC_SEL_PLL3_2X 2
-#define CCM_LCD_CH1_SRC_SEL_PLL7_2X 3
-#define CCM_LCD_CH1_SCLK1_GATING (1U << 15)
-#define CCM_LCD_CH1_SCLK1_SRC_SEL_SHIFT 11
-#define CCM_LCD_CH1_SCLK1_SRC_SEL_SCLK2 0
-#define CCM_LCD_CH1_SCLK1_SRC_SEL_SCLK2_DIV2 1
-#define CCM_LCD_CH1_CLK_DIV_RATIO_M 0xf
-
-#define CCM_DRAM_CLK_BE1_CLK_ENABLE (1U << 27)
-#define CCM_DRAM_CLK_BE0_CLK_ENABLE (1U << 26)
-
-#define CCM_BE_CLK_SCLK_GATING (1U << 31)
-#define CCM_BE_CLK_RESET (1U << 30)
-#define CCM_BE_CLK_SRC_SEL 0x03000000
-#define CCM_BE_CLK_SRC_SEL_SHIFT 24
-#define CCM_BE_CLK_SRC_SEL_PLL3 0
-#define CCM_BE_CLK_SRC_SEL_PLL7 1
-#define CCM_BE_CLK_SRC_SEL_PLL5 2
-#define CCM_BE_CLK_DIV_RATIO_M 0xf
-
-#define CCM_HDMI_CLK_SCLK_GATING (1U << 31)
-#define CCM_HDMI_CLK_SRC_SEL 0x03000000
-#define CCM_HDMI_CLK_SRC_SEL_SHIFT 24
-#define CCM_HDMI_CLK_SRC_SEL_PLL3 0
-#define CCM_HDMI_CLK_SRC_SEL_PLL7 1
-#define CCM_HDMI_CLK_SRC_SEL_PLL3_2X 2
-#define CCM_HDMI_CLK_SRC_SEL_PLL7_2X 3
-#define CCM_HDMI_CLK_DIV_RATIO_M 0xf
-
-#define CCM_CLK_REF_FREQ 24000000U
-
-int a10_clk_ehci_activate(void);
-int a10_clk_ehci_deactivate(void);
-int a10_clk_ohci_activate(void);
-int a10_clk_ohci_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);
-int a10_clk_i2c_activate(int);
-int a10_clk_dmac_activate(void);
-int a10_clk_codec_activate(unsigned int);
-int a10_clk_debe_activate(void);
-int a10_clk_lcd_activate(void);
-int a10_clk_tcon_activate(unsigned int);
-int a10_clk_tcon_get_config(int *, int *);
-int a10_clk_hdmi_activate(void);
-
-#endif /* _A10_CLK_H_ */
Index: head/sys/arm/allwinner/a10_clk.c
===================================================================
--- head/sys/arm/allwinner/a10_clk.c
+++ head/sys/arm/allwinner/a10_clk.c
@@ -1,862 +0,0 @@
-/*-
- * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/* Simple clock driver for Allwinner A10 */
-
-#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 <machine/bus.h>
-
-#include <dev/ofw/openfirm.h>
-#include <dev/ofw/ofw_bus_subr.h>
-
-#include "a10_clk.h"
-
-#define TCON_PLL_WORST 1000000
-#define TCON_PLL_N_MIN 1
-#define TCON_PLL_N_MAX 15
-#define TCON_PLL_M_MIN 9
-#define TCON_PLL_M_MAX 127
-#define TCON_PLLREF_SINGLE 3000 /* kHz */
-#define TCON_PLLREF_DOUBLE 6000 /* kHz */
-#define TCON_RATE_KHZ(rate_hz) ((rate_hz) / 1000)
-#define TCON_RATE_HZ(rate_khz) ((rate_khz) * 1000)
-#define HDMI_DEFAULT_RATE 297000000
-#define DEBE_DEFAULT_RATE 300000000
-
-struct a10_ccm_softc {
- struct resource *res;
- bus_space_tag_t bst;
- bus_space_handle_t bsh;
- struct mtx mtx;
- int pll6_enabled;
- int ehci_cnt;
- int ohci_cnt;
- int usbphy_cnt;
- int usb_cnt;
-};
-
-static struct a10_ccm_softc *a10_ccm_sc = NULL;
-
-static int a10_clk_usbphy_activate(struct a10_ccm_softc *sc);
-static int a10_clk_usbphy_deactivate(struct a10_ccm_softc *sc);
-static int a10_clk_usb_activate(struct a10_ccm_softc *sc);
-static int a10_clk_usb_deactivate(struct a10_ccm_softc *sc);
-
-#define CCM_LOCK(sc) mtx_lock(&(sc)->mtx);
-#define CCM_UNLOCK(sc) mtx_unlock(&(sc)->mtx);
-#define CCM_LOCK_ASSERT(sc) mtx_assert(&(sc)->mtx, MA_OWNED)
-#define ccm_read_4(sc, reg) \
- bus_space_read_4((sc)->bst, (sc)->bsh, (reg))
-#define ccm_write_4(sc, reg, val) \
- bus_space_write_4((sc)->bst, (sc)->bsh, (reg), (val))
-
-static int
-a10_ccm_probe(device_t dev)
-{
-
- if (!ofw_bus_status_okay(dev))
- return (ENXIO);
-
- if (ofw_bus_is_compatible(dev, "allwinner,sun4i-ccm")) {
- device_set_desc(dev, "Allwinner Clock Control Module");
- return(BUS_PROBE_DEFAULT);
- }
-
- return (ENXIO);
-}
-
-static int
-a10_ccm_attach(device_t dev)
-{
- struct a10_ccm_softc *sc = device_get_softc(dev);
- int rid = 0;
-
- if (a10_ccm_sc)
- return (ENXIO);
-
- sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
- if (!sc->res) {
- device_printf(dev, "could not allocate resource\n");
- return (ENXIO);
- }
-
- sc->bst = rman_get_bustag(sc->res);
- sc->bsh = rman_get_bushandle(sc->res);
-
- mtx_init(&sc->mtx, "a10_ccm", NULL, MTX_DEF);
-
- a10_ccm_sc = sc;
-
- return (0);
-}
-
-static device_method_t a10_ccm_methods[] = {
- DEVMETHOD(device_probe, a10_ccm_probe),
- DEVMETHOD(device_attach, a10_ccm_attach),
- { 0, 0 }
-};
-
-static driver_t a10_ccm_driver = {
- "a10_ccm",
- a10_ccm_methods,
- sizeof(struct a10_ccm_softc),
-};
-
-static devclass_t a10_ccm_devclass;
-
-EARLY_DRIVER_MODULE(a10_ccm, simplebus, a10_ccm_driver, a10_ccm_devclass, 0, 0,
- BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
-
-int
-a10_clk_ehci_activate(void)
-{
- struct a10_ccm_softc *sc = a10_ccm_sc;
- uint32_t reg_value;
-
- if (sc == NULL)
- return (ENXIO);
-
- CCM_LOCK(sc);
-
- if (++sc->ehci_cnt == 1) {
- /* Gating AHB clock for USB */
- reg_value = ccm_read_4(sc, CCM_AHB_GATING0);
- reg_value |= CCM_AHB_GATING_EHCI0; /* AHB clock gate ehci0 */
- reg_value |= CCM_AHB_GATING_EHCI1; /* AHB clock gate ehci1 */
- ccm_write_4(sc, CCM_AHB_GATING0, reg_value);
- }
-
- a10_clk_usb_activate(sc);
- a10_clk_usbphy_activate(sc);
-
- CCM_UNLOCK(sc);
-
- return (0);
-}
-
-int
-a10_clk_ehci_deactivate(void)
-{
- struct a10_ccm_softc *sc = a10_ccm_sc;
- uint32_t reg_value;
-
- if (sc == NULL)
- return (ENXIO);
-
- CCM_LOCK(sc);
-
- if (--sc->ehci_cnt == 0) {
- /* Disable gating AHB clock for USB */
- reg_value = ccm_read_4(sc, CCM_AHB_GATING0);
- reg_value &= ~CCM_AHB_GATING_EHCI0; /* disable AHB clock gate ehci0 */
- reg_value &= ~CCM_AHB_GATING_EHCI1; /* disable AHB clock gate ehci1 */
- ccm_write_4(sc, CCM_AHB_GATING0, reg_value);
- }
-
- a10_clk_usb_deactivate(sc);
- a10_clk_usbphy_deactivate(sc);
-
- CCM_UNLOCK(sc);
-
- return (0);
-}
-
-int
-a10_clk_ohci_activate(void)
-{
- struct a10_ccm_softc *sc = a10_ccm_sc;
- uint32_t reg_value;
-
- if (sc == NULL)
- return (ENXIO);
-
- CCM_LOCK(sc);
-
- if (++sc->ohci_cnt == 1) {
- /* Gating AHB clock for USB */
- reg_value = ccm_read_4(sc, CCM_AHB_GATING0);
- reg_value |= CCM_AHB_GATING_OHCI0; /* AHB clock gate ohci0 */
- reg_value |= CCM_AHB_GATING_OHCI1; /* AHB clock gate ohci1 */
- ccm_write_4(sc, CCM_AHB_GATING0, reg_value);
-
- /* Enable clock for USB */
- reg_value = ccm_read_4(sc, CCM_USB_CLK);
- reg_value |= CCM_SCLK_GATING_OHCI0;
- reg_value |= CCM_SCLK_GATING_OHCI1;
- ccm_write_4(sc, CCM_USB_CLK, reg_value);
- }
-
- a10_clk_usb_activate(sc);
- a10_clk_usbphy_activate(sc);
-
- CCM_UNLOCK(sc);
-
- return (0);
-}
-
-int
-a10_clk_ohci_deactivate(void)
-{
- struct a10_ccm_softc *sc = a10_ccm_sc;
- uint32_t reg_value;
-
- if (sc == NULL)
- return (ENXIO);
-
- CCM_LOCK(sc);
-
- if (--sc->ohci_cnt == 0) {
- /* Disable clock for USB */
- reg_value = ccm_read_4(sc, CCM_USB_CLK);
- reg_value &= ~CCM_SCLK_GATING_OHCI0;
- reg_value &= ~CCM_SCLK_GATING_OHCI1;
- ccm_write_4(sc, CCM_USB_CLK, reg_value);
-
- /* Disable gating AHB clock for USB */
- reg_value = ccm_read_4(sc, CCM_AHB_GATING0);
- reg_value &= ~CCM_AHB_GATING_OHCI0; /* disable AHB clock gate ohci0 */
- reg_value &= ~CCM_AHB_GATING_OHCI1; /* disable AHB clock gate ohci1 */
- ccm_write_4(sc, CCM_AHB_GATING0, reg_value);
- }
-
- a10_clk_usb_deactivate(sc);
- a10_clk_usbphy_deactivate(sc);
-
- CCM_UNLOCK(sc);
-
- return (0);
-}
-
-static int
-a10_clk_usb_activate(struct a10_ccm_softc *sc)
-{
- uint32_t reg_value;
-
- CCM_LOCK_ASSERT(sc);
-
- if (++sc->usb_cnt == 1) {
- /* Gating AHB clock for USB */
- reg_value = ccm_read_4(sc, CCM_AHB_GATING0);
- reg_value |= CCM_AHB_GATING_USB0; /* AHB clock gate usb0 */
- ccm_write_4(sc, CCM_AHB_GATING0, reg_value);
- }
-
- return (0);
-}
-
-static int
-a10_clk_usb_deactivate(struct a10_ccm_softc *sc)
-{
- uint32_t reg_value;
-
- CCM_LOCK_ASSERT(sc);
-
- if (--sc->usb_cnt == 0) {
- /* Disable gating AHB clock for USB */
- reg_value = ccm_read_4(sc, CCM_AHB_GATING0);
- reg_value &= ~CCM_AHB_GATING_USB0; /* disable AHB clock gate usb0 */
- ccm_write_4(sc, CCM_AHB_GATING0, reg_value);
- }
-
- return (0);
-}
-
-static int
-a10_clk_usbphy_activate(struct a10_ccm_softc *sc)
-{
- uint32_t reg_value;
-
- CCM_LOCK_ASSERT(sc);
-
- if (++sc->usbphy_cnt == 1) {
- /* Enable clock for USB */
- reg_value = ccm_read_4(sc, CCM_USB_CLK);
- reg_value |= CCM_USB_PHY; /* USBPHY */
- reg_value |= CCM_USBPHY0_RESET; /* disable reset for USBPHY0 */
- reg_value |= CCM_USBPHY1_RESET; /* disable reset for USBPHY1 */
- reg_value |= CCM_USBPHY2_RESET; /* disable reset for USBPHY2 */
- ccm_write_4(sc, CCM_USB_CLK, reg_value);
- }
-
- return (0);
-}
-
-static int
-a10_clk_usbphy_deactivate(struct a10_ccm_softc *sc)
-{
- uint32_t reg_value;
-
- CCM_LOCK_ASSERT(sc);
-
- if (--sc->usbphy_cnt == 0) {
- /* Disable clock for USB */
- reg_value = ccm_read_4(sc, CCM_USB_CLK);
- reg_value &= ~CCM_USB_PHY; /* USBPHY */
- reg_value &= ~CCM_USBPHY0_RESET; /* reset for USBPHY0 */
- reg_value &= ~CCM_USBPHY1_RESET; /* reset for USBPHY1 */
- reg_value &= ~CCM_USBPHY2_RESET; /* reset for USBPHY2 */
- ccm_write_4(sc, CCM_USB_CLK, reg_value);
- }
-
- return (0);
-}
-
-int
-a10_clk_emac_activate(void)
-{
- struct a10_ccm_softc *sc = a10_ccm_sc;
- uint32_t reg_value;
-
- if (sc == NULL)
- return (ENXIO);
-
- /* Gating AHB clock for EMAC */
- reg_value = ccm_read_4(sc, CCM_AHB_GATING0);
- reg_value |= CCM_AHB_GATING_EMAC;
- ccm_write_4(sc, CCM_AHB_GATING0, reg_value);
-
- 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-mode", 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)
-{
- struct a10_ccm_softc *sc;
- uint32_t reg_value;
-
- /*
- * SATA needs PLL6 to be a 100MHz clock.
- * The SATA output frequency is 24MHz * n * k / m / 6.
- * To get to 100MHz, k & m must be equal and n must be 25.
- * 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 |
- CCM_PLL_CFG_FACTOR_N);
- reg_value |= (25 << CCM_PLL_CFG_FACTOR_N_SHIFT);
- 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
-a10_clk_pll6_get_rate(void)
-{
- struct a10_ccm_softc *sc;
- uint32_t k, n, reg_value;
-
- sc = a10_ccm_sc;
- reg_value = ccm_read_4(sc, CCM_PLL6_CFG);
- n = ((reg_value & CCM_PLL_CFG_FACTOR_N) >> CCM_PLL_CFG_FACTOR_N_SHIFT);
- k = ((reg_value & CCM_PLL_CFG_FACTOR_K) >> CCM_PLL_CFG_FACTOR_K_SHIFT) +
- 1;
-
- return ((CCM_CLK_REF_FREQ * n * k) / 2);
-}
-
-static int
-a10_clk_pll2_set_rate(unsigned int freq)
-{
- struct a10_ccm_softc *sc;
- uint32_t reg_value;
- unsigned int prediv, postdiv, n;
-
- sc = a10_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- reg_value = ccm_read_4(sc, CCM_PLL2_CFG);
- reg_value &= ~(CCM_PLL2_CFG_PREDIV | CCM_PLL2_CFG_POSTDIV |
- CCM_PLL_CFG_FACTOR_N);
-
- /*
- * Audio Codec needs PLL2 to be either 24576000 Hz or 22579200 Hz
- *
- * PLL2 output frequency is 24MHz * n / prediv / postdiv.
- * To get as close as possible to the desired rate, we use a
- * pre-divider of 21 and a post-divider of 4. With these values,
- * a multiplier of 86 or 79 gets us close to the target rates.
- */
- prediv = 21;
- postdiv = 4;
-
- switch (freq) {
- case 24576000:
- n = 86;
- reg_value |= CCM_PLL_CFG_ENABLE;
- break;
- case 22579200:
- n = 79;
- reg_value |= CCM_PLL_CFG_ENABLE;
- break;
- case 0:
- n = 1;
- reg_value &= ~CCM_PLL_CFG_ENABLE;
- break;
- default:
- return (EINVAL);
- }
-
- reg_value |= (prediv << CCM_PLL2_CFG_PREDIV_SHIFT);
- reg_value |= (postdiv << CCM_PLL2_CFG_POSTDIV_SHIFT);
- reg_value |= (n << CCM_PLL_CFG_FACTOR_N_SHIFT);
- ccm_write_4(sc, CCM_PLL2_CFG, reg_value);
-
- return (0);
-}
-
-static int
-a10_clk_pll3_set_rate(unsigned int freq)
-{
- struct a10_ccm_softc *sc;
- uint32_t reg_value;
- int m;
-
- sc = a10_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- if (freq == 0) {
- /* Disable PLL3 */
- ccm_write_4(sc, CCM_PLL3_CFG, 0);
- return (0);
- }
-
- m = freq / TCON_RATE_HZ(TCON_PLLREF_SINGLE);
-
- reg_value = CCM_PLL_CFG_ENABLE | CCM_PLL3_CFG_MODE_SEL_INT | m;
- ccm_write_4(sc, CCM_PLL3_CFG, reg_value);
-
- return (0);
-}
-
-static unsigned int
-a10_clk_pll5x_get_rate(void)
-{
- struct a10_ccm_softc *sc;
- uint32_t k, n, p, reg_value;
-
- sc = a10_ccm_sc;
- reg_value = ccm_read_4(sc, CCM_PLL5_CFG);
- n = ((reg_value & CCM_PLL_CFG_FACTOR_N) >> CCM_PLL_CFG_FACTOR_N_SHIFT);
- k = ((reg_value & CCM_PLL_CFG_FACTOR_K) >> CCM_PLL_CFG_FACTOR_K_SHIFT) +
- 1;
- p = ((reg_value & CCM_PLL5_CFG_OUT_EXT_DIV_P) >> CCM_PLL5_CFG_OUT_EXT_DIV_P_SHIFT);
-
- return ((CCM_CLK_REF_FREQ * n * k) >> p);
-}
-
-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;
- uint32_t reg_value;
-
- sc = a10_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- a10_clk_pll6_enable();
-
- /* Gating AHB clock for SD/MMC */
- reg_value = ccm_read_4(sc, CCM_AHB_GATING0);
- reg_value |= CCM_AHB_GATING_SDMMC0 << devid;
- ccm_write_4(sc, CCM_AHB_GATING0, reg_value);
-
- return (0);
-}
-
-int
-a10_clk_mmc_cfg(int devid, int freq)
-{
- struct a10_ccm_softc *sc;
- uint32_t clksrc, m, n, ophase, phase, reg_value;
- unsigned int pll_freq;
-
- sc = a10_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- freq /= 1000;
- if (freq <= 400) {
- pll_freq = CCM_CLK_REF_FREQ / 1000;
- clksrc = CCM_SD_CLK_SRC_SEL_OSC24M;
- ophase = 0;
- phase = 0;
- n = 2;
- } else if (freq <= 25000) {
- pll_freq = a10_clk_pll6_get_rate() / 1000;
- clksrc = CCM_SD_CLK_SRC_SEL_PLL6;
- ophase = 0;
- phase = 5;
- n = 2;
- } else if (freq <= 50000) {
- pll_freq = a10_clk_pll6_get_rate() / 1000;
- clksrc = CCM_SD_CLK_SRC_SEL_PLL6;
- ophase = 3;
- phase = 5;
- n = 0;
- } else
- return (EINVAL);
- m = ((pll_freq / (1 << n)) / (freq)) - 1;
- reg_value = ccm_read_4(sc, CCM_MMC0_SCLK_CFG + (devid * 4));
- reg_value &= ~CCM_SD_CLK_SRC_SEL;
- reg_value |= (clksrc << CCM_SD_CLK_SRC_SEL_SHIFT);
- reg_value &= ~CCM_SD_CLK_PHASE_CTR;
- reg_value |= (phase << CCM_SD_CLK_PHASE_CTR_SHIFT);
- reg_value &= ~CCM_SD_CLK_DIV_RATIO_N;
- reg_value |= (n << CCM_SD_CLK_DIV_RATIO_N_SHIFT);
- reg_value &= ~CCM_SD_CLK_OPHASE_CTR;
- reg_value |= (ophase << CCM_SD_CLK_OPHASE_CTR_SHIFT);
- reg_value &= ~CCM_SD_CLK_DIV_RATIO_M;
- reg_value |= m;
- reg_value |= CCM_PLL_CFG_ENABLE;
- ccm_write_4(sc, CCM_MMC0_SCLK_CFG + (devid * 4), reg_value);
-
- return (0);
-}
-
-int
-a10_clk_i2c_activate(int devid)
-{
- struct a10_ccm_softc *sc;
- uint32_t reg_value;
-
- sc = a10_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- a10_clk_pll6_enable();
-
- /* Gating APB clock for I2C/TWI */
- reg_value = ccm_read_4(sc, CCM_APB1_GATING);
- if (devid == 4)
- reg_value |= CCM_APB1_GATING_TWI << 15;
- else
- reg_value |= CCM_APB1_GATING_TWI << devid;
- ccm_write_4(sc, CCM_APB1_GATING, reg_value);
-
- return (0);
-}
-
-int
-a10_clk_dmac_activate(void)
-{
- struct a10_ccm_softc *sc;
- uint32_t reg_value;
-
- sc = a10_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- /* Gating AHB clock for DMA controller */
- reg_value = ccm_read_4(sc, CCM_AHB_GATING0);
- reg_value |= CCM_AHB_GATING_DMA;
- ccm_write_4(sc, CCM_AHB_GATING0, reg_value);
-
- return (0);
-}
-
-int
-a10_clk_codec_activate(unsigned int freq)
-{
- struct a10_ccm_softc *sc;
- uint32_t reg_value;
-
- sc = a10_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- a10_clk_pll2_set_rate(freq);
-
- /* Gating APB clock for ADDA */
- reg_value = ccm_read_4(sc, CCM_APB0_GATING);
- reg_value |= CCM_APB0_GATING_ADDA;
- ccm_write_4(sc, CCM_APB0_GATING, reg_value);
-
- /* Enable audio codec clock */
- reg_value = ccm_read_4(sc, CCM_AUDIO_CODEC_CLK);
- reg_value |= CCM_AUDIO_CODEC_ENABLE;
- ccm_write_4(sc, CCM_AUDIO_CODEC_CLK, reg_value);
-
- return (0);
-}
-
-static void
-calc_tcon_pll(int f_ref, int f_out, int *pm, int *pn)
-{
- int best, m, n, f_cur, diff;
-
- best = TCON_PLL_WORST;
- for (n = TCON_PLL_N_MIN; n <= TCON_PLL_N_MAX; n++) {
- for (m = TCON_PLL_M_MIN; m <= TCON_PLL_M_MAX; m++) {
- f_cur = (m * f_ref) / n;
- diff = f_out - f_cur;
- if (diff > 0 && diff < best) {
- best = diff;
- *pm = m;
- *pn = n;
- }
- }
- }
-}
-
-int
-a10_clk_debe_activate(void)
-{
- struct a10_ccm_softc *sc;
- int pll_rate, clk_div;
- uint32_t reg_value;
-
- sc = a10_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- /* Leave reset */
- reg_value = ccm_read_4(sc, CCM_BE0_SCLK);
- reg_value |= CCM_BE_CLK_RESET;
- ccm_write_4(sc, CCM_BE0_SCLK, reg_value);
-
- pll_rate = a10_clk_pll5x_get_rate();
-
- clk_div = howmany(pll_rate, DEBE_DEFAULT_RATE);
-
- /* Set BE0 source to PLL5 (DDR external peripheral clock) */
- reg_value = CCM_BE_CLK_RESET;
- reg_value |= (CCM_BE_CLK_SRC_SEL_PLL5 << CCM_BE_CLK_SRC_SEL_SHIFT);
- reg_value |= (clk_div - 1);
- ccm_write_4(sc, CCM_BE0_SCLK, reg_value);
-
- /* Gating AHB clock for BE0 */
- reg_value = ccm_read_4(sc, CCM_AHB_GATING1);
- reg_value |= CCM_AHB_GATING_DE_BE0;
- ccm_write_4(sc, CCM_AHB_GATING1, reg_value);
-
- /* Enable DRAM clock to BE0 */
- reg_value = ccm_read_4(sc, CCM_DRAM_CLK);
- reg_value |= CCM_DRAM_CLK_BE0_CLK_ENABLE;
- ccm_write_4(sc, CCM_DRAM_CLK, reg_value);
-
- /* Enable BE0 clock */
- reg_value = ccm_read_4(sc, CCM_BE0_SCLK);
- reg_value |= CCM_BE_CLK_SCLK_GATING;
- ccm_write_4(sc, CCM_BE0_SCLK, reg_value);
-
- return (0);
-}
-
-int
-a10_clk_lcd_activate(void)
-{
- struct a10_ccm_softc *sc;
- uint32_t reg_value;
-
- sc = a10_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- /* Clear LCD0 reset */
- reg_value = ccm_read_4(sc, CCM_LCD0_CH0_CLK);
- reg_value |= CCM_LCD_CH0_RESET;
- ccm_write_4(sc, CCM_LCD0_CH0_CLK, reg_value);
-
- /* Gating AHB clock for LCD0 */
- reg_value = ccm_read_4(sc, CCM_AHB_GATING1);
- reg_value |= CCM_AHB_GATING_LCD0;
- ccm_write_4(sc, CCM_AHB_GATING1, reg_value);
-
- return (0);
-}
-
-int
-a10_clk_tcon_activate(unsigned int freq)
-{
- struct a10_ccm_softc *sc;
- int m, n, m2, n2, f_single, f_double, dbl, src_sel;
-
- sc = a10_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- m = n = m2 = n2 = 0;
- dbl = 0;
-
- calc_tcon_pll(TCON_PLLREF_SINGLE, TCON_RATE_KHZ(freq), &m, &n);
- calc_tcon_pll(TCON_PLLREF_DOUBLE, TCON_RATE_KHZ(freq), &m2, &n2);
-
- f_single = n ? (m * TCON_PLLREF_SINGLE) / n : 0;
- f_double = n2 ? (m2 * TCON_PLLREF_DOUBLE) / n2 : 0;
-
- if (f_double > f_single) {
- dbl = 1;
- m = m2;
- n = n2;
- }
- src_sel = dbl ? CCM_LCD_CH1_SRC_SEL_PLL3_2X : CCM_LCD_CH1_SRC_SEL_PLL3;
-
- if (n == 0 || m == 0)
- return (EINVAL);
-
- /* Set PLL3 to the closest possible rate */
- a10_clk_pll3_set_rate(TCON_RATE_HZ(m * TCON_PLLREF_SINGLE));
-
- /* Enable LCD0 CH1 clock */
- ccm_write_4(sc, CCM_LCD0_CH1_CLK,
- CCM_LCD_CH1_SCLK2_GATING | CCM_LCD_CH1_SCLK1_GATING |
- (src_sel << CCM_LCD_CH1_SRC_SEL_SHIFT) | (n - 1));
-
- return (0);
-}
-
-int
-a10_clk_tcon_get_config(int *pdiv, int *pdbl)
-{
- struct a10_ccm_softc *sc;
- uint32_t reg_value;
- int src;
-
- sc = a10_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- reg_value = ccm_read_4(sc, CCM_LCD0_CH1_CLK);
-
- *pdiv = (reg_value & CCM_LCD_CH1_CLK_DIV_RATIO_M) + 1;
-
- src = (reg_value & CCM_LCD_CH1_SRC_SEL) >> CCM_LCD_CH1_SRC_SEL_SHIFT;
- switch (src) {
- case CCM_LCD_CH1_SRC_SEL_PLL3:
- case CCM_LCD_CH1_SRC_SEL_PLL7:
- *pdbl = 0;
- break;
- case CCM_LCD_CH1_SRC_SEL_PLL3_2X:
- case CCM_LCD_CH1_SRC_SEL_PLL7_2X:
- *pdbl = 1;
- break;
- }
-
- return (0);
-}
-
-int
-a10_clk_hdmi_activate(void)
-{
- struct a10_ccm_softc *sc;
- uint32_t reg_value;
- int error;
-
- sc = a10_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- /* Set PLL3 to 297MHz */
- error = a10_clk_pll3_set_rate(HDMI_DEFAULT_RATE);
- if (error != 0)
- return (error);
-
- /* Enable HDMI clock, source PLL3 */
- reg_value = ccm_read_4(sc, CCM_HDMI_CLK);
- reg_value |= CCM_HDMI_CLK_SCLK_GATING;
- reg_value &= ~CCM_HDMI_CLK_SRC_SEL;
- reg_value |= (CCM_HDMI_CLK_SRC_SEL_PLL3 << CCM_HDMI_CLK_SRC_SEL_SHIFT);
- ccm_write_4(sc, CCM_HDMI_CLK, reg_value);
-
- /* Gating AHB clock for HDMI */
- reg_value = ccm_read_4(sc, CCM_AHB_GATING1);
- reg_value |= CCM_AHB_GATING_HDMI;
- ccm_write_4(sc, CCM_AHB_GATING1, reg_value);
-
- return (0);
-}
Index: head/sys/arm/allwinner/a10_codec.c
===================================================================
--- head/sys/arm/allwinner/a10_codec.c
+++ head/sys/arm/allwinner/a10_codec.c
@@ -50,7 +50,7 @@
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
-#include <arm/allwinner/a10_clk.h>
+#include <dev/extres/clk/clk.h>
#include "sunxi_dma_if.h"
#include "mixer_if.h"
@@ -738,6 +738,7 @@
{
struct a10codec_info *sc;
char status[SND_STATUSLEN];
+ clk_t clk_apb, clk_codec;
uint32_t val;
int error;
@@ -778,6 +779,24 @@
goto fail;
}
+ /* Get clocks */
+ error = clk_get_by_ofw_name(dev, "apb", &clk_apb);
+ if (error != 0) {
+ device_printf(dev, "cannot find apb clock\n");
+ goto fail;
+ }
+ error = clk_get_by_ofw_name(dev, "codec", &clk_codec);
+ if (error != 0) {
+ device_printf(dev, "cannot find codec clock\n");
+ goto fail;
+ }
+
+ /* Gating APB clock for codec */
+ error = clk_enable(clk_apb);
+ if (error != 0) {
+ device_printf(dev, "cannot enable apb clock\n");
+ goto fail;
+ }
/* Activate audio codec clock. According to the A10 and A20 user
* manuals, Audio_pll can be either 24.576MHz or 22.5792MHz. Most
* audio sampling rates require an 24.576MHz input clock with the
@@ -787,7 +806,17 @@
* 24.576MHz clock source and don't advertise native support for
* the three sampling rates that require a 22.5792MHz input.
*/
- a10_clk_codec_activate(24576000);
+ error = clk_set_freq(clk_codec, 24576000, CLK_SET_ROUND_DOWN);
+ if (error != 0) {
+ device_printf(dev, "cannot set codec clock frequency\n");
+ goto fail;
+ }
+ /* Enable audio codec clock */
+ error = clk_enable(clk_codec);
+ if (error != 0) {
+ device_printf(dev, "cannot enable codec clock\n");
+ goto fail;
+ }
/* Enable DAC */
val = CODEC_READ(sc, AC_DAC_DPC);
Index: head/sys/arm/allwinner/a10_dmac.c
===================================================================
--- head/sys/arm/allwinner/a10_dmac.c
+++ head/sys/arm/allwinner/a10_dmac.c
@@ -46,7 +46,7 @@
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/allwinner/a10_dmac.h>
-#include <arm/allwinner/a10_clk.h>
+#include <dev/extres/clk/clk.h>
#include "sunxi_dma_if.h"
@@ -111,6 +111,7 @@
{
struct a10dmac_softc *sc;
unsigned int index;
+ clk_t clk;
int error;
sc = device_get_softc(dev);
@@ -123,7 +124,16 @@
mtx_init(&sc->sc_mtx, "a10 dmac", NULL, MTX_SPIN);
/* Activate DMA controller clock */
- a10_clk_dmac_activate();
+ error = clk_get_by_ofw_index(dev, 0, &clk);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock\n");
+ return (error);
+ }
+ error = clk_enable(clk);
+ if (error != 0) {
+ device_printf(dev, "cannot enable clock\n");
+ return (error);
+ }
/* Disable all interrupts and clear pending status */
DMA_WRITE(sc, AWIN_DMA_IRQ_EN_REG, 0);
Index: head/sys/arm/allwinner/a10_ehci.c
===================================================================
--- head/sys/arm/allwinner/a10_ehci.c
+++ head/sys/arm/allwinner/a10_ehci.c
@@ -59,8 +59,8 @@
#include <dev/usb/controller/ehcireg.h>
#include <arm/allwinner/allwinner_machdep.h>
-#include <arm/allwinner/a10_clk.h>
-#include <arm/allwinner/a31/a31_clk.h>
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
#define EHCI_HC_DEVSTR "Allwinner Integrated USB 2.0 controller"
@@ -90,25 +90,22 @@
bs_r_1_proto(reversed);
bs_w_1_proto(reversed);
+struct aw_ehci_softc {
+ ehci_softc_t sc;
+ clk_t clk;
+ hwreset_t rst;
+};
+
struct aw_ehci_conf {
- int (*clk_activate)(void);
- int (*clk_deactivate)(void);
bool sdram_init;
};
static const struct aw_ehci_conf a10_ehci_conf = {
-#if defined(SOC_ALLWINNER_A10) || defined(SOC_ALLWINNER_A20)
- .clk_activate = a10_clk_ehci_activate,
- .clk_deactivate = a10_clk_ehci_deactivate,
-#endif
.sdram_init = true,
};
static const struct aw_ehci_conf a31_ehci_conf = {
-#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S)
- .clk_activate = a31_clk_ehci_activate,
- .clk_deactivate = a31_clk_ehci_deactivate,
-#endif
+ .sdram_init = false,
};
static struct ofw_compat_data compat_data[] = {
@@ -136,7 +133,8 @@
static int
a10_ehci_attach(device_t self)
{
- ehci_softc_t *sc = device_get_softc(self);
+ struct aw_ehci_softc *aw_sc = device_get_softc(self);
+ ehci_softc_t *sc = &aw_sc->sc;
const struct aw_ehci_conf *conf;
bus_space_handle_t bsh;
int err;
@@ -144,10 +142,6 @@
uint32_t reg_value = 0;
conf = USB_CONF(self);
- if (conf->clk_activate == NULL) {
- device_printf(self, "clock not supported\n");
- return (ENXIO);
- }
/* initialise some bus fields */
sc->sc_bus.parent = self;
@@ -208,9 +202,24 @@
sc->sc_flags |= EHCI_SCFLG_DONTRESET;
+ /* De-assert reset */
+ if (hwreset_get_by_ofw_idx(self, 0, &aw_sc->rst) == 0) {
+ err = hwreset_deassert(aw_sc->rst);
+ if (err != 0) {
+ device_printf(self, "Could not de-assert reset\n");
+ goto error;
+ }
+ }
+
/* Enable clock for USB */
- if (conf->clk_activate() != 0) {
- device_printf(self, "Could not activate clock\n");
+ err = clk_get_by_ofw_index(self, 0, &aw_sc->clk);
+ if (err != 0) {
+ device_printf(self, "Could not get clock\n");
+ goto error;
+ }
+ err = clk_enable(aw_sc->clk);
+ if (err != 0) {
+ device_printf(self, "Could not enable clock\n");
goto error;
}
@@ -240,6 +249,8 @@
return (0);
error:
+ if (aw_sc->clk)
+ clk_release(aw_sc->clk);
a10_ehci_detach(self);
return (ENXIO);
}
@@ -247,7 +258,8 @@
static int
a10_ehci_detach(device_t self)
{
- ehci_softc_t *sc = device_get_softc(self);
+ struct aw_ehci_softc *aw_sc = device_get_softc(self);
+ ehci_softc_t *sc = &aw_sc->sc;
const struct aw_ehci_conf *conf;
device_t bdev;
int err;
@@ -305,7 +317,14 @@
A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value);
/* Disable clock for USB */
- conf->clk_deactivate();
+ clk_disable(aw_sc->clk);
+ clk_release(aw_sc->clk);
+
+ /* Assert reset */
+ if (aw_sc->rst != NULL) {
+ hwreset_assert(aw_sc->rst);
+ hwreset_release(aw_sc->rst);
+ }
return (0);
}
Index: head/sys/arm/allwinner/a10_fb.c
===================================================================
--- head/sys/arm/allwinner/a10_fb.c
+++ head/sys/arm/allwinner/a10_fb.c
@@ -54,7 +54,8 @@
#include <dev/videomode/videomode.h>
#include <dev/videomode/edidvar.h>
-#include <arm/allwinner/a10_clk.h>
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
#include "fb_if.h"
#include "hdmi_if.h"
@@ -66,6 +67,7 @@
#define FB_ALIGN 0x1000
#define HDMI_ENABLE_DELAY 20000
+#define DEBE_FREQ 300000000
#define DOT_CLOCK_TO_HZ(c) ((c) * 1000)
@@ -193,18 +195,68 @@
kmem_free(kernel_arena, sc->vaddr, sc->fbsize);
}
-static void
+static int
a10fb_setup_debe(struct a10fb_softc *sc, const struct videomode *mode)
{
int width, height, interlace, reg;
+ clk_t clk_ahb, clk_dram, clk_debe;
+ hwreset_t rst;
uint32_t val;
+ int error;
interlace = !!(mode->flags & VID_INTERLACE);
width = mode->hdisplay;
height = mode->vdisplay << interlace;
- /* Enable DEBE clocks */
- a10_clk_debe_activate();
+ /* Leave reset */
+ error = hwreset_get_by_ofw_name(sc->dev, "de_be", &rst);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot find reset 'de_be'\n");
+ return (error);
+ }
+ error = hwreset_deassert(rst);
+ if (error != 0) {
+ device_printf(sc->dev, "couldn't de-assert reset 'de_be'\n");
+ return (error);
+ }
+ /* Gating AHB clock for BE */
+ error = clk_get_by_ofw_name(sc->dev, "ahb_de_be", &clk_ahb);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot find clk 'ahb_de_be'\n");
+ return (error);
+ }
+ error = clk_enable(clk_ahb);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot enable clk 'ahb_de_be'\n");
+ return (error);
+ }
+ /* Enable DRAM clock to BE */
+ error = clk_get_by_ofw_name(sc->dev, "dram_de_be", &clk_dram);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot find clk 'dram_de_be'\n");
+ return (error);
+ }
+ error = clk_enable(clk_dram);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot enable clk 'dram_de_be'\n");
+ return (error);
+ }
+ /* Set BE clock to 300MHz and enable */
+ error = clk_get_by_ofw_name(sc->dev, "de_be", &clk_debe);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot find clk 'de_be'\n");
+ return (error);
+ }
+ error = clk_set_freq(clk_debe, DEBE_FREQ, CLK_SET_ROUND_DOWN);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot set 'de_be' frequency\n");
+ return (error);
+ }
+ error = clk_enable(clk_debe);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot enable clk 'de_be'\n");
+ return (error);
+ }
/* Initialize all registers to 0 */
for (reg = DEBE_REG_START; reg < DEBE_REG_END; reg += DEBE_REG_WIDTH)
@@ -247,14 +299,55 @@
val = DEBE_READ(sc, DEBE_MODCTL);
val |= MODCTL_START_CTL;
DEBE_WRITE(sc, DEBE_MODCTL, val);
+
+ return (0);
}
-static void
+static int
+a10fb_setup_pll(struct a10fb_softc *sc, uint64_t freq)
+{
+ clk_t clk_sclk1, clk_sclk2;
+ int error;
+
+ error = clk_get_by_ofw_name(sc->dev, "lcd_ch1_sclk1", &clk_sclk1);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk1'\n");
+ return (error);
+ }
+ error = clk_get_by_ofw_name(sc->dev, "lcd_ch1_sclk2", &clk_sclk2);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk2'\n");
+ return (error);
+ }
+
+ error = clk_set_freq(clk_sclk2, freq, 0);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot set lcd ch1 frequency\n");
+ return (error);
+ }
+ error = clk_enable(clk_sclk2);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot enable lcd ch1 sclk2\n");
+ return (error);
+ }
+ error = clk_enable(clk_sclk1);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot enable lcd ch1 sclk1\n");
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
a10fb_setup_tcon(struct a10fb_softc *sc, const struct videomode *mode)
{
u_int interlace, hspw, hbp, vspw, vbp, vbl, width, height, start_delay;
u_int vtotal, framerate, clk;
+ clk_t clk_ahb;
+ hwreset_t rst;
uint32_t val;
+ int error;
interlace = !!(mode->flags & VID_INTERLACE);
width = mode->hdisplay;
@@ -266,8 +359,28 @@
vbl = VBLANK_LEN(mode->vtotal, mode->vdisplay, interlace);
start_delay = START_DELAY(vbl);
- /* Enable LCD clocks */
- a10_clk_lcd_activate();
+ /* Leave reset */
+ error = hwreset_get_by_ofw_name(sc->dev, "lcd", &rst);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot find reset 'lcd'\n");
+ return (error);
+ }
+ error = hwreset_deassert(rst);
+ if (error != 0) {
+ device_printf(sc->dev, "couldn't de-assert reset 'lcd'\n");
+ return (error);
+ }
+ /* Gating AHB clock for LCD */
+ error = clk_get_by_ofw_name(sc->dev, "ahb_lcd", &clk_ahb);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot find clk 'ahb_lcd'\n");
+ return (error);
+ }
+ error = clk_enable(clk_ahb);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot enable clk 'ahb_lcd'\n");
+ return (error);
+ }
/* Disable TCON and TCON1 */
TCON_WRITE(sc, TCON_GCTL, 0);
@@ -322,7 +435,7 @@
TCON_WRITE(sc, TCON1_CTL, val);
/* Setup PLL */
- a10_clk_tcon_activate(DOT_CLOCK_TO_HZ(mode->dot_clock));
+ return (a10fb_setup_pll(sc, DOT_CLOCK_TO_HZ(mode->dot_clock)));
}
static void
@@ -378,10 +491,14 @@
}
/* Setup display backend */
- a10fb_setup_debe(sc, mode);
+ error = a10fb_setup_debe(sc, mode);
+ if (error != 0)
+ return (error);
/* Setup display timing controller */
- a10fb_setup_tcon(sc, mode);
+ error = a10fb_setup_tcon(sc, mode);
+ if (error != 0)
+ return (error);
/* Attach framebuffer device */
sc->info.fb_name = device_get_nameunit(sc->dev);
Index: head/sys/arm/allwinner/a10_hdmi.c
===================================================================
--- head/sys/arm/allwinner/a10_hdmi.c
+++ head/sys/arm/allwinner/a10_hdmi.c
@@ -49,7 +49,7 @@
#include <dev/videomode/videomode.h>
#include <dev/videomode/edidvar.h>
-#include <arm/allwinner/a10_clk.h>
+#include <dev/extres/clk/clk.h>
#include "hdmi_if.h"
@@ -204,6 +204,7 @@
#define HDMI_VSDB_MINLEN 5
#define HDMI_OUI "\x03\x0c\x00"
#define HDMI_OUI_LEN 3
+#define HDMI_DEFAULT_FREQ 297000000
struct a10hdmi_softc {
struct resource *res;
@@ -214,6 +215,10 @@
int has_hdmi;
int has_audio;
+
+ clk_t clk_ahb;
+ clk_t clk_hdmi;
+ clk_t clk_lcd;
};
static struct resource_spec a10hdmi_spec[] = {
@@ -287,7 +292,33 @@
return (ENXIO);
}
- a10_clk_hdmi_activate();
+ /* Setup clocks */
+ error = clk_get_by_ofw_name(dev, "ahb", &sc->clk_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot find ahb clock\n");
+ return (error);
+ }
+ error = clk_get_by_ofw_name(dev, "hdmi", &sc->clk_hdmi);
+ if (error != 0) {
+ device_printf(dev, "cannot find hdmi clock\n");
+ return (error);
+ }
+ error = clk_get_by_ofw_name(dev, "lcd", &sc->clk_lcd);
+ if (error != 0) {
+ device_printf(dev, "cannot find lcd clock\n");
+ }
+ /* Enable HDMI clock */
+ error = clk_enable(sc->clk_hdmi);
+ if (error != 0) {
+ device_printf(dev, "cannot enable hdmi clock\n");
+ return (error);
+ }
+ /* Gating AHB clock for HDMI */
+ error = clk_enable(sc->clk_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot enable ahb gate\n");
+ return (error);
+ }
a10hdmi_init(sc);
@@ -527,6 +558,38 @@
}
static int
+a10hdmi_get_tcon_config(struct a10hdmi_softc *sc, int *div, int *dbl)
+{
+ uint64_t lcd_fin, lcd_fout;
+ clk_t clk_lcd_parent;
+ const char *pname;
+ int error;
+
+ error = clk_get_parent(sc->clk_lcd, &clk_lcd_parent);
+ if (error != 0)
+ return (error);
+
+ /* Get the LCD CH1 special clock 2 divider */
+ error = clk_get_freq(sc->clk_lcd, &lcd_fout);
+ if (error != 0)
+ return (error);
+ error = clk_get_freq(clk_lcd_parent, &lcd_fin);
+ if (error != 0)
+ return (error);
+ *div = lcd_fin / lcd_fout;
+
+ /* Detect LCD CH1 special clock using a 1X or 2X source */
+ /* XXX */
+ pname = clk_get_name(clk_lcd_parent);
+ if (strcmp(pname, "pll3-1x") == 0 || strcmp(pname, "pll7-1x") == 0)
+ *dbl = 0;
+ else
+ *dbl = 1;
+
+ return (0);
+}
+
+static int
a10hdmi_set_videomode(device_t dev, const struct videomode *mode)
{
struct a10hdmi_softc *sc;
@@ -543,9 +606,11 @@
vspw = mode->vsync_end - mode->vsync_start;
vbp = mode->vtotal - mode->vsync_start;
- error = a10_clk_tcon_get_config(&clk_div, &clk_dbl);
- if (error != 0)
+ error = a10hdmi_get_tcon_config(sc, &clk_div, &clk_dbl);
+ if (error != 0) {
+ device_printf(dev, "couldn't get tcon config: %d\n", error);
return (error);
+ }
/* Clear interrupt status */
HDMI_WRITE(sc, HDMI_INT_STATUS, HDMI_READ(sc, HDMI_INT_STATUS));
Index: head/sys/arm/allwinner/a10_mmc.c
===================================================================
--- head/sys/arm/allwinner/a10_mmc.c
+++ head/sys/arm/allwinner/a10_mmc.c
@@ -49,9 +49,9 @@
#include <dev/mmc/mmcbrvar.h>
#include <arm/allwinner/allwinner_machdep.h>
-#include <arm/allwinner/a10_clk.h>
#include <arm/allwinner/a10_mmc.h>
-#include <arm/allwinner/a31/a31_clk.h>
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
#define A10_MMC_MEMRES 0
#define A10_MMC_IRQRES 1
@@ -60,6 +60,8 @@
#define A10_MMC_DMA_MAX_SIZE 0x2000
#define A10_MMC_DMA_FTRGLEVEL 0x20070008
+#define CARD_ID_FREQUENCY 400000
+
static int a10_mmc_pio_mode = 0;
TUNABLE_INT("hw.a10.mmc.pio_mode", &a10_mmc_pio_mode);
@@ -74,6 +76,9 @@
bus_space_handle_t a10_bsh;
bus_space_tag_t a10_bst;
device_t a10_dev;
+ clk_t a10_clk_ahb;
+ clk_t a10_clk_mmc;
+ hwreset_t a10_rst_ahb;
int a10_bus_busy;
int a10_id;
int a10_resid;
@@ -147,7 +152,7 @@
struct a10_mmc_softc *sc;
struct sysctl_ctx_list *ctx;
struct sysctl_oid_list *tree;
- int clk;
+ int error;
sc = device_get_softc(dev);
sc->a10_dev = dev;
@@ -170,6 +175,9 @@
device_printf(dev, "cannot setup interrupt handler\n");
return (ENXIO);
}
+ mtx_init(&sc->a10_mtx, device_get_nameunit(sc->a10_dev), "a10_mmc",
+ MTX_DEF);
+ callout_init_mtx(&sc->a10_timeoutc, &sc->a10_mtx, 0);
/*
* Later chips use a different FIFO offset. Unfortunately the FDT
@@ -186,30 +194,41 @@
break;
}
+ /* De-assert reset */
+ if (hwreset_get_by_ofw_name(dev, "ahb", &sc->a10_rst_ahb) == 0) {
+ error = hwreset_deassert(sc->a10_rst_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot de-assert reset\n");
+ return (error);
+ }
+ }
+
/* Activate the module clock. */
- switch (allwinner_soc_type()) {
-#if defined(SOC_ALLWINNER_A10) || defined(SOC_ALLWINNER_A20)
- case ALLWINNERSOC_A10:
- case ALLWINNERSOC_A10S:
- case ALLWINNERSOC_A20:
- clk = a10_clk_mmc_activate(sc->a10_id);
- break;
-#endif
-#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S)
- case ALLWINNERSOC_A31:
- case ALLWINNERSOC_A31S:
- clk = a31_clk_mmc_activate(sc->a10_id);
- break;
-#endif
- default:
- clk = -1;
+ error = clk_get_by_ofw_name(dev, "ahb", &sc->a10_clk_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot get ahb clock\n");
+ goto fail;
}
- if (clk != 0) {
- bus_teardown_intr(dev, sc->a10_res[A10_MMC_IRQRES],
- sc->a10_intrhand);
- bus_release_resources(dev, a10_mmc_res_spec, sc->a10_res);
- device_printf(dev, "cannot activate mmc clock\n");
- return (ENXIO);
+ error = clk_enable(sc->a10_clk_ahb);
+ if (error != 0) {
+ device_printf(dev, "cannot enable ahb clock\n");
+ goto fail;
+ }
+ error = clk_get_by_ofw_name(dev, "mmc", &sc->a10_clk_mmc);
+ if (error != 0) {
+ device_printf(dev, "cannot get mmc clock\n");
+ goto fail;
+ }
+ error = clk_set_freq(sc->a10_clk_mmc, CARD_ID_FREQUENCY,
+ CLK_SET_ROUND_DOWN);
+ if (error != 0) {
+ device_printf(dev, "cannot init mmc clock\n");
+ goto fail;
+ }
+ error = clk_enable(sc->a10_clk_mmc);
+ if (error != 0) {
+ device_printf(dev, "cannot enable mmc clock\n");
+ goto fail;
}
sc->a10_timeout = 10;
@@ -217,9 +236,6 @@
tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "req_timeout", CTLFLAG_RW,
&sc->a10_timeout, 0, "Request timeout in seconds");
- mtx_init(&sc->a10_mtx, device_get_nameunit(sc->a10_dev), "a10_mmc",
- MTX_DEF);
- callout_init_mtx(&sc->a10_timeoutc, &sc->a10_mtx, 0);
/* Reset controller. */
if (a10_mmc_reset(sc) != 0) {
@@ -826,25 +842,14 @@
return (error);
/* Set the MMC clock. */
- switch (allwinner_soc_type()) {
-#if defined(SOC_ALLWINNER_A10) || defined(SOC_ALLWINNER_A20)
- case ALLWINNERSOC_A10:
- case ALLWINNERSOC_A10S:
- case ALLWINNERSOC_A20:
- error = a10_clk_mmc_cfg(sc->a10_id, ios->clock);
- break;
-#endif
-#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S)
- case ALLWINNERSOC_A31:
- case ALLWINNERSOC_A31S:
- error = a31_clk_mmc_cfg(sc->a10_id, ios->clock);
- break;
-#endif
- default:
- error = ENXIO;
- }
- if (error != 0)
+ error = clk_set_freq(sc->a10_clk_mmc, ios->clock,
+ CLK_SET_ROUND_DOWN);
+ if (error != 0) {
+ device_printf(sc->a10_dev,
+ "failed to set frequency to %u Hz: %d\n",
+ ios->clock, error);
return (error);
+ }
/* Enable clock. */
clkcr |= A10_MMC_CARD_CLK_ON;
Index: head/sys/arm/allwinner/a20/a20_if_dwc.c
===================================================================
--- head/sys/arm/allwinner/a20/a20_if_dwc.c
+++ head/sys/arm/allwinner/a20/a20_if_dwc.c
@@ -41,8 +41,7 @@
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/allwinner/allwinner_machdep.h>
-#include <arm/allwinner/a10_clk.h>
-#include <arm/allwinner/a31/a31_clk.h>
+#include <dev/extres/clk/clk.h>
#include "if_dwc_if.h"
@@ -62,29 +61,39 @@
static int
a20_if_dwc_init(device_t dev)
{
- int clk;
-
- /* Activate GMAC clock and set the pin mux to rgmii. */
- switch (allwinner_soc_type()) {
-#if defined(SOC_ALLWINNER_A10) || defined(SOC_ALLWINNER_A20)
- case ALLWINNERSOC_A10:
- case ALLWINNERSOC_A10S:
- case ALLWINNERSOC_A20:
- clk = a10_clk_gmac_activate(ofw_bus_get_node(dev));
- break;
-#endif
-#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S)
- case ALLWINNERSOC_A31:
- case ALLWINNERSOC_A31S:
- clk = a31_clk_gmac_activate(ofw_bus_get_node(dev));
- break;
-#endif
- default:
- clk = -1;
- }
- if (clk != 0) {
- device_printf(dev, "could not activate gmac module\n");
- return (ENXIO);
+ const char *tx_parent_name;
+ char *phy_type;
+ clk_t clk_tx, clk_tx_parent;
+ phandle_t node;
+ int error;
+
+ node = ofw_bus_get_node(dev);
+
+ /* Configure PHY for MII or RGMII mode */
+ if (OF_getprop_alloc(node, "phy-mode", 1, (void **)&phy_type)) {
+ error = clk_get_by_ofw_name(dev, "allwinner_gmac_tx", &clk_tx);
+ if (error != 0) {
+ device_printf(dev, "could not get tx clk\n");
+ return (error);
+ }
+
+ if (strcmp(phy_type, "rgmii") == 0)
+ tx_parent_name = "gmac_int_tx";
+ else
+ tx_parent_name = "mii_phy_tx";
+
+ error = clk_get_by_name(dev, tx_parent_name, &clk_tx_parent);
+ if (error != 0) {
+ device_printf(dev, "could not get clock '%s'\n",
+ tx_parent_name);
+ return (error);
+ }
+
+ error = clk_set_parent_by_clk(clk_tx, clk_tx_parent);
+ if (error != 0) {
+ device_printf(dev, "could not set tx clk parent\n");
+ return (error);
+ }
}
return (0);
Index: head/sys/arm/allwinner/a31/a31_clk.h
===================================================================
--- head/sys/arm/allwinner/a31/a31_clk.h
+++ head/sys/arm/allwinner/a31/a31_clk.h
@@ -1,213 +0,0 @@
-/*-
- * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org>
- * Copyright (c) 2016 Emmanuel Vadot <manu@bidouilliste.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 _A31_CLK_H_
-#define _A31_CLK_H_
-
-#define A31_CCM_PLL1_CFG 0x0000
-#define A31_CCM_PLL2_CFG 0x0008
-#define A31_CCM_PLL3_CFG 0x0010
-#define A31_CCM_PLL4_CFG 0x0018
-#define A31_CCM_PLL5_CFG 0x0020
-#define A31_CCM_PLL6_CFG 0x0028
-#define A31_CCM_PLL7_CFG 0x0030
-#define A31_CCM_PLL8_CFG 0x0038
-#define A31_CCM_MIPI_PLL_CFG 0x0040
-#define A31_CCM_PLL9_CFG 0x0044
-#define A31_CCM_PLL10_CFG 0x0048
-#define A31_CCM_AXI_CFG_REG 0x0050
-#define A31_CCM_AHB1_APB1_CFG 0x0054
-#define A31_CCM_APB2_CLK_DIV 0x0058
-#define A31_CCM_AHB_GATING0 0x0060
-#define A31_CCM_AHB_GATING1 0x0064
-#define A31_CCM_APB1_GATING 0x0068
-#define A31_CCM_APB2_GATING 0x006c
-#define A31_CCM_NAND0_SCLK_CFG 0x0080
-#define A31_CCM_NAND1_SCLK_CFG 0x0084
-#define A31_CCM_MMC0_SCLK_CFG 0x0088
-#define A31_CCM_MMC1_SCLK_CFG 0x008c
-#define A31_CCM_MMC2_SCLK_CFG 0x0090
-#define A31_CCM_MMC3_SCLK_CFG 0x0094
-#define A31_CCM_TS_CLK 0x0098
-#define A31_CCM_SS_CLK 0x009c
-#define A31_CCM_SPI0_CLK 0x00a0
-#define A31_CCM_SPI1_CLK 0x00a4
-#define A31_CCM_SPI2_CLK 0x00a8
-#define A31_CCM_SPI3_CLK 0x00ac
-#define A31_CCM_DAUDIO0_CLK 0x00b0
-#define A31_CCM_DAUDIO1_CLK 0x00b4
-#define A31_CCM_USBPHY_CLK 0x00cc
-#define A31_CCM_GMAC_CLK 0x00d0
-#define A31_CCM_MDFS_CLK 0x00f0
-#define A31_CCM_DRAM_CLK 0x00f4
-#define A31_CCM_DRAM_GATING 0x0100
-#define A31_CCM_BE0_SCLK 0x0104
-#define A31_CCM_BE1_SCLK 0x0108
-#define A31_CCM_FE0_CLK 0x010c
-#define A31_CCM_FE1_CLK 0x0110
-#define A31_CCM_MP_CLK 0x0114
-#define A31_CCM_LCD0_CH0_CLK 0x0118
-#define A31_CCM_LCD1_CH0_CLK 0x011c
-#define A31_CCM_LCD0_CH1_CLK 0x012c
-#define A31_CCM_LCD1_CH1_CLK 0x0130
-#define A31_CCM_CSI0_CLK 0x0134
-#define A31_CCM_CSI1_CLK 0x0138
-#define A31_CCM_VE_CLK 0x013c
-#define A31_CCM_AUDIO_CODEC_CLK 0x0140
-#define A31_CCM_AVS_CLK 0x0144
-#define A31_CCM_DIGITAL_MIC_CLK 0x0148
-#define A31_CCM_HDMI_CLK 0x0150
-#define A31_CCM_PS_CLK 0x0154
-#define A31_CCM_MBUS_SCLK_CFG0 0x015c
-#define A31_CCM_MBUS_SCLK_CFG1 0x0160
-#define A31_CCM_MIPI_DSI_CLK 0x0168
-#define A31_CCM_MIPI_CSI0_CLK 0x016c
-#define A31_CCM_DRC0_SCLK_CFG 0x0180
-#define A31_CCM_DRC1_SCLK_CFG 0x0184
-#define A31_CCM_DEU0_SCLK_CFG 0x0188
-#define A31_CCM_DEU1_SCLK_CFG 0x018c
-#define A31_CCM_GPU_CORE_CLK 0x01a0
-#define A31_CCM_GPU_MEM_CLK 0x01a4
-#define A31_CCM_GPU_HYD_CLK 0x01a8
-#define A31_CCM_ATS_CLK 0x01b0
-#define A31_CCM_TRACE_CLK 0x01b4
-#define A31_CCM_PLL_LOCK_CFG 0x0200
-#define A31_CCM_PLL1_LOCK_CFG 0x0204
-#define A31_CCM_PLL1_BIAS 0x0220
-#define A31_CCM_PLL2_BIAS 0x0224
-#define A31_CCM_PLL3_BIAS 0x0228
-#define A31_CCM_PLL4_BIAS 0x022c
-#define A31_CCM_PLL5_BIAS 0x0230
-#define A31_CCM_PLL6_BIAS 0x0234
-#define A31_CCM_PLL7_BIAS 0x0238
-#define A31_CCM_PLL8_BIAS 0x023c
-#define A31_CCM_PLL9_BIAS 0x0240
-#define A31_CCM_MIPI_PLL_BIAS 0x0244
-#define A31_CCM_PLL10_BIAS 0x0248
-#define A31_CCM_PLL1_PAT_CFG 0x0280
-#define A31_CCM_PLL2_PAT_CFG 0x0284
-#define A31_CCM_PLL3_PAT_CFG 0x0288
-#define A31_CCM_PLL4_PAT_CFG 0x028c
-#define A31_CCM_PLL5_PAT_CFG 0x0290
-#define A31_CCM_PLL6_PAT_CFG 0x0294
-#define A31_CCM_PLL7_PAT_CFG 0x0298
-#define A31_CCM_PLL8_PAT_CFG 0x029c
-#define A31_CCM_MIPI_PLL_PAT_CFG 0x02a0
-#define A31_CCM_PLL9_PAT_CFG 0x02a4
-#define A31_CCM_PLL10_PAT_CFG 0x02a8
-#define A31_CCM_AHB1_RST_REG0 0x02c0
-#define A31_CCM_AHB1_RST_REG1 0x02c4
-#define A31_CCM_AHB1_RST_REG2 0x02c8
-#define A31_CCM_APB1_RST 0x02d0
-#define A31_CCM_APB2_RST 0x02d8
-#define A31_CCM_CLK_OUTA 0x0300
-#define A31_CCM_CLK_OUTB 0x0304
-#define A31_CCM_CLK_OUTC 0x0308
-
-/* PLL6_CFG_REG */
-#define A31_CCM_PLL6_CFG_REG_LOCK (1 << 28)
-
-/* AHB_GATING_REG0 */
-#define A31_CCM_AHB_GATING_OHCI2 (1 << 31)
-#define A31_CCM_AHB_GATING_OHCI1 (1 << 30)
-#define A31_CCM_AHB_GATING_OHCI0 (1 << 29)
-#define A31_CCM_AHB_GATING_EHCI1 (1 << 27)
-#define A31_CCM_AHB_GATING_EHCI0 (1 << 26)
-#define A31_CCM_AHB_GATING_USBDRD (1 << 24)
-#define A31_CCM_AHB_GATING_GMAC (1 << 17)
-#define A31_CCM_AHB_GATING_SDMMC0 (1 << 8)
-
-#define A31_CCM_PLL_CFG_ENABLE (1U << 31)
-#define A31_CCM_PLL_CFG_BYPASS (1U << 30)
-#define A31_CCM_PLL_CFG_PLL5 (1U << 25)
-#define A31_CCM_PLL_CFG_PLL6 (1U << 24)
-#define A31_CCM_PLL_CFG_FACTOR_N 0x1f00
-#define A31_CCM_PLL_CFG_FACTOR_N_SHIFT 8
-#define A31_CCM_PLL_CFG_FACTOR_K 0x30
-#define A31_CCM_PLL_CFG_FACTOR_K_SHIFT 4
-#define A31_CCM_PLL_CFG_FACTOR_M 0x3
-
-/* APB2_GATING */
-#define A31_CCM_APB2_GATING_TWI (1 << 0)
-
-/* AHB1_RST_REG0 */
-#define A31_CCM_AHB1_RST_REG0_OHCI2 (1 << 31)
-#define A31_CCM_AHB1_RST_REG0_OHCI1 (1 << 30)
-#define A31_CCM_AHB1_RST_REG0_OHCI0 (1 << 29)
-#define A31_CCM_AHB1_RST_REG0_EHCI1 (1 << 27)
-#define A31_CCM_AHB1_RST_REG0_EHCI0 (1 << 26)
-#define A31_CCM_AHB1_RST_REG0_GMAC (1 << 17)
-#define A31_CCM_AHB1_RST_REG0_SDMMC (1 << 8)
-
-/* APB2_RST_REG */
-#define A31_CCM_APB2_RST_TWI (1 << 0)
-
-
-/* GMAC */
-#define A31_CCM_GMAC_CLK_DELAY_SHIFT 10
-#define A31_CCM_GMAC_CLK_MODE_MASK 0x7
-#define A31_CCM_GMAC_MODE_RGMII (1 << 2)
-#define A31_CCM_GMAC_CLK_MII 0x0
-#define A31_CCM_GMAC_CLK_EXT_RGMII 0x1
-#define A31_CCM_GMAC_CLK_RGMII 0x2
-
-/* SD/MMC */
-#define A31_CCM_SD_CLK_SRC_SEL 0x3000000
-#define A31_CCM_SD_CLK_SRC_SEL_SHIFT 24
-#define A31_CCM_SD_CLK_SRC_SEL_OSC24M 0
-#define A31_CCM_SD_CLK_SRC_SEL_PLL6 1
-#define A31_CCM_SD_CLK_PHASE_CTR 0x700000
-#define A31_CCM_SD_CLK_PHASE_CTR_SHIFT 20
-#define A31_CCM_SD_CLK_DIV_RATIO_N 0x30000
-#define A31_CCM_SD_CLK_DIV_RATIO_N_SHIFT 16
-#define A31_CCM_SD_CLK_OPHASE_CTR 0x700
-#define A31_CCM_SD_CLK_OPHASE_CTR_SHIFT 8
-#define A31_CCM_SD_CLK_DIV_RATIO_M 0xf
-
-/* USB */
-#define A31_CCM_USBPHY_CLK_GATING_OHCI2 (1 << 18)
-#define A31_CCM_USBPHY_CLK_GATING_OHCI1 (1 << 17)
-#define A31_CCM_USBPHY_CLK_GATING_OHCI0 (1 << 16)
-#define A31_CCM_USBPHY_CLK_GATING_USBPHY2 (1 << 10)
-#define A31_CCM_USBPHY_CLK_GATING_USBPHY1 (1 << 9)
-#define A31_CCM_USBPHY_CLK_GATING_USBPHY0 (1 << 8)
-#define A31_CCM_USBPHY_CLK_USBPHY2_RST (1 << 2)
-#define A31_CCM_USBPHY_CLK_USBPHY1_RST (1 << 1)
-#define A31_CCM_USBPHY_CLK_USBPHY0_RST (1 << 0)
-
-#define A31_CCM_CLK_REF_FREQ 24000000U
-
-int a31_clk_gmac_activate(phandle_t);
-int a31_clk_mmc_activate(int);
-int a31_clk_mmc_cfg(int, int);
-int a31_clk_i2c_activate(int);
-int a31_clk_ehci_activate(void);
-int a31_clk_ehci_deactivate(void);
-
-#endif /* _A31_CLK_H_ */
Index: head/sys/arm/allwinner/a31/a31_clk.c
===================================================================
--- head/sys/arm/allwinner/a31/a31_clk.c
+++ head/sys/arm/allwinner/a31/a31_clk.c
@@ -1,378 +0,0 @@
-/*-
- * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org>
- * Copyright (c) 2016 Emmanuel Vadot <manu@bidouilliste.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.
- */
-
-/*
- * Simple clock driver for Allwinner A31
- * Adapted from a10_clk.c
-*/
-
-#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 <machine/bus.h>
-
-#include <dev/ofw/openfirm.h>
-#include <dev/ofw/ofw_bus_subr.h>
-
-#include <arm/allwinner/a31/a31_clk.h>
-
-struct a31_ccm_softc {
- struct resource *res;
- struct mtx mtx;
- int pll6_enabled;
- int ehci_refcnt;
-};
-
-static struct a31_ccm_softc *a31_ccm_sc = NULL;
-
-#define ccm_read_4(sc, reg) \
- bus_read_4((sc)->res, (reg))
-#define ccm_write_4(sc, reg, val) \
- bus_write_4((sc)->res, (reg), (val))
-
-#define CCM_LOCK(sc) mtx_lock(&(sc)->mtx)
-#define CCM_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
-
-#define PLL6_TIMEOUT 10
-
-static int
-a31_ccm_probe(device_t dev)
-{
-
- if (!ofw_bus_status_okay(dev))
- return (ENXIO);
-
- if (ofw_bus_is_compatible(dev, "allwinner,sun6i-a31-ccm")) {
- device_set_desc(dev, "Allwinner Clock Control Module");
- return(BUS_PROBE_DEFAULT);
- }
-
- return (ENXIO);
-}
-
-static int
-a31_ccm_attach(device_t dev)
-{
- struct a31_ccm_softc *sc = device_get_softc(dev);
- int rid = 0;
-
- if (a31_ccm_sc)
- return (ENXIO);
-
- sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
- if (!sc->res) {
- device_printf(dev, "could not allocate resource\n");
- return (ENXIO);
- }
-
- mtx_init(&sc->mtx, "a31 ccm", NULL, MTX_DEF);
-
- a31_ccm_sc = sc;
-
- return (0);
-}
-
-static device_method_t a31_ccm_methods[] = {
- DEVMETHOD(device_probe, a31_ccm_probe),
- DEVMETHOD(device_attach, a31_ccm_attach),
- { 0, 0 }
-};
-
-static driver_t a31_ccm_driver = {
- "a31_ccm",
- a31_ccm_methods,
- sizeof(struct a31_ccm_softc),
-};
-
-static devclass_t a31_ccm_devclass;
-
-EARLY_DRIVER_MODULE(a31_ccm, simplebus, a31_ccm_driver, a31_ccm_devclass, 0, 0,
- BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
-
-static int
-a31_clk_pll6_enable(void)
-{
- struct a31_ccm_softc *sc;
- uint32_t reg_value;
- int i;
-
- /* Datasheet recommand to use the default 600Mhz value */
- sc = a31_ccm_sc;
- if (sc->pll6_enabled)
- return (0);
- reg_value = ccm_read_4(sc, A31_CCM_PLL6_CFG);
- reg_value |= A31_CCM_PLL_CFG_ENABLE;
- ccm_write_4(sc, A31_CCM_PLL6_CFG, reg_value);
-
- /* Wait for PLL to be stable */
- for (i = 0; i < PLL6_TIMEOUT; i++)
- if ((ccm_read_4(sc, A31_CCM_PLL6_CFG) &
- A31_CCM_PLL6_CFG_REG_LOCK) == A31_CCM_PLL6_CFG_REG_LOCK)
- break;
- if (i == PLL6_TIMEOUT)
- return (ENXIO);
- sc->pll6_enabled = 1;
-
- return (0);
-}
-
-static unsigned int
-a31_clk_pll6_get_rate(void)
-{
- struct a31_ccm_softc *sc;
- uint32_t k, n, reg_value;
-
- sc = a31_ccm_sc;
- reg_value = ccm_read_4(sc, A31_CCM_PLL6_CFG);
- n = ((reg_value & A31_CCM_PLL_CFG_FACTOR_N) >>
- A31_CCM_PLL_CFG_FACTOR_N_SHIFT);
- k = ((reg_value & A31_CCM_PLL_CFG_FACTOR_K) >>
- A31_CCM_PLL_CFG_FACTOR_K_SHIFT) + 1;
-
- return ((A31_CCM_CLK_REF_FREQ * n * k) / 2);
-}
-
-int
-a31_clk_gmac_activate(phandle_t node)
-{
- char *phy_type;
- struct a31_ccm_softc *sc;
- uint32_t reg_value;
-
- sc = a31_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- if (a31_clk_pll6_enable())
- return (ENXIO);
-
- /* Gating AHB clock for GMAC */
- reg_value = ccm_read_4(sc, A31_CCM_AHB_GATING0);
- reg_value |= A31_CCM_AHB_GATING_GMAC;
- ccm_write_4(sc, A31_CCM_AHB_GATING0, reg_value);
-
- /* Set GMAC mode. */
- reg_value = A31_CCM_GMAC_CLK_MII;
- if (OF_getprop_alloc(node, "phy-mode", 1, (void **)&phy_type) > 0) {
- if (strcasecmp(phy_type, "rgmii") == 0)
- reg_value = A31_CCM_GMAC_CLK_RGMII |
- A31_CCM_GMAC_MODE_RGMII;
- free(phy_type, M_OFWPROP);
- }
- ccm_write_4(sc, A31_CCM_GMAC_CLK, reg_value);
-
- /* Reset gmac */
- reg_value = ccm_read_4(sc, A31_CCM_AHB1_RST_REG0);
- reg_value |= A31_CCM_AHB1_RST_REG0_GMAC;
- ccm_write_4(sc, A31_CCM_AHB1_RST_REG0, reg_value);
-
- return (0);
-}
-
-int
-a31_clk_mmc_activate(int devid)
-{
- struct a31_ccm_softc *sc;
- uint32_t reg_value;
-
- sc = a31_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- if (a31_clk_pll6_enable())
- return (ENXIO);
-
- /* Gating AHB clock for SD/MMC */
- reg_value = ccm_read_4(sc, A31_CCM_AHB_GATING0);
- reg_value |= A31_CCM_AHB_GATING_SDMMC0 << devid;
- ccm_write_4(sc, A31_CCM_AHB_GATING0, reg_value);
-
- /* Soft reset */
- reg_value = ccm_read_4(sc, A31_CCM_AHB1_RST_REG0);
- reg_value |= A31_CCM_AHB1_RST_REG0_SDMMC << devid;
- ccm_write_4(sc, A31_CCM_AHB1_RST_REG0, reg_value);
-
- return (0);
-}
-
-int
-a31_clk_mmc_cfg(int devid, int freq)
-{
- struct a31_ccm_softc *sc;
- uint32_t clksrc, m, n, ophase, phase, reg_value;
- unsigned int pll_freq;
-
- sc = a31_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- freq /= 1000;
- if (freq <= 400) {
- pll_freq = A31_CCM_CLK_REF_FREQ / 1000;
- clksrc = A31_CCM_SD_CLK_SRC_SEL_OSC24M;
- ophase = 0;
- phase = 0;
- n = 2;
- } else if (freq <= 25000) {
- pll_freq = a31_clk_pll6_get_rate() / 1000;
- clksrc = A31_CCM_SD_CLK_SRC_SEL_PLL6;
- ophase = 0;
- phase = 5;
- n = 2;
- } else if (freq <= 50000) {
- pll_freq = a31_clk_pll6_get_rate() / 1000;
- clksrc = A31_CCM_SD_CLK_SRC_SEL_PLL6;
- ophase = 3;
- phase = 5;
- n = 0;
- } else
- return (EINVAL);
- m = ((pll_freq / (1 << n)) / (freq)) - 1;
- reg_value = ccm_read_4(sc, A31_CCM_MMC0_SCLK_CFG + (devid * 4));
- reg_value &= ~A31_CCM_SD_CLK_SRC_SEL;
- reg_value |= (clksrc << A31_CCM_SD_CLK_SRC_SEL_SHIFT);
- reg_value &= ~A31_CCM_SD_CLK_PHASE_CTR;
- reg_value |= (phase << A31_CCM_SD_CLK_PHASE_CTR_SHIFT);
- reg_value &= ~A31_CCM_SD_CLK_DIV_RATIO_N;
- reg_value |= (n << A31_CCM_SD_CLK_DIV_RATIO_N_SHIFT);
- reg_value &= ~A31_CCM_SD_CLK_OPHASE_CTR;
- reg_value |= (ophase << A31_CCM_SD_CLK_OPHASE_CTR_SHIFT);
- reg_value &= ~A31_CCM_SD_CLK_DIV_RATIO_M;
- reg_value |= m;
- reg_value |= A31_CCM_PLL_CFG_ENABLE;
- ccm_write_4(sc, A31_CCM_MMC0_SCLK_CFG + (devid * 4), reg_value);
-
- return (0);
-}
-
-int
-a31_clk_i2c_activate(int devid)
-{
- struct a31_ccm_softc *sc;
- uint32_t reg_value;
-
- sc = a31_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- if (a31_clk_pll6_enable())
- return (ENXIO);
-
- /* Gating APB clock for I2C/TWI */
- reg_value = ccm_read_4(sc, A31_CCM_APB2_GATING);
- reg_value |= A31_CCM_APB2_GATING_TWI << devid;
- ccm_write_4(sc, A31_CCM_APB2_GATING, reg_value);
-
- /* Soft reset */
- reg_value = ccm_read_4(sc, A31_CCM_APB2_RST);
- reg_value |= A31_CCM_APB2_RST_TWI << devid;
- ccm_write_4(sc, A31_CCM_APB2_RST, reg_value);
-
- return (0);
-}
-
-int
-a31_clk_ehci_activate(void)
-{
- struct a31_ccm_softc *sc;
- uint32_t reg_value;
-
- sc = a31_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- CCM_LOCK(sc);
- if (++sc->ehci_refcnt == 1) {
- /* Enable USB PHY */
- reg_value = ccm_read_4(sc, A31_CCM_USBPHY_CLK);
- reg_value |= A31_CCM_USBPHY_CLK_GATING_USBPHY0;
- reg_value |= A31_CCM_USBPHY_CLK_GATING_USBPHY1;
- reg_value |= A31_CCM_USBPHY_CLK_GATING_USBPHY2;
- reg_value |= A31_CCM_USBPHY_CLK_USBPHY1_RST;
- reg_value |= A31_CCM_USBPHY_CLK_USBPHY2_RST;
- ccm_write_4(sc, A31_CCM_USBPHY_CLK, reg_value);
-
- /* Gating AHB clock for EHCI */
- reg_value = ccm_read_4(sc, A31_CCM_AHB_GATING0);
- reg_value |= A31_CCM_AHB_GATING_EHCI0;
- reg_value |= A31_CCM_AHB_GATING_EHCI1;
- ccm_write_4(sc, A31_CCM_AHB_GATING0, reg_value);
-
- /* De-assert reset */
- reg_value = ccm_read_4(sc, A31_CCM_AHB1_RST_REG0);
- reg_value |= A31_CCM_AHB1_RST_REG0_EHCI0;
- reg_value |= A31_CCM_AHB1_RST_REG0_EHCI1;
- ccm_write_4(sc, A31_CCM_AHB1_RST_REG0, reg_value);
- }
- CCM_UNLOCK(sc);
-
- return (0);
-}
-
-int
-a31_clk_ehci_deactivate(void)
-{
- struct a31_ccm_softc *sc;
- uint32_t reg_value;
-
- sc = a31_ccm_sc;
- if (sc == NULL)
- return (ENXIO);
-
- CCM_LOCK(sc);
- if (--sc->ehci_refcnt == 0) {
- /* Disable USB PHY */
- reg_value = ccm_read_4(sc, A31_CCM_USBPHY_CLK);
- reg_value &= ~A31_CCM_USBPHY_CLK_GATING_USBPHY0;
- reg_value &= ~A31_CCM_USBPHY_CLK_GATING_USBPHY1;
- reg_value &= ~A31_CCM_USBPHY_CLK_GATING_USBPHY2;
- reg_value &= ~A31_CCM_USBPHY_CLK_USBPHY1_RST;
- reg_value &= ~A31_CCM_USBPHY_CLK_USBPHY2_RST;
- ccm_write_4(sc, A31_CCM_USBPHY_CLK, reg_value);
-
- /* Gating AHB clock for EHCI */
- reg_value = ccm_read_4(sc, A31_CCM_AHB_GATING0);
- reg_value &= ~A31_CCM_AHB_GATING_EHCI0;
- reg_value &= ~A31_CCM_AHB_GATING_EHCI1;
- ccm_write_4(sc, A31_CCM_AHB_GATING0, reg_value);
-
- /* Assert reset */
- reg_value = ccm_read_4(sc, A31_CCM_AHB1_RST_REG0);
- reg_value &= ~A31_CCM_AHB1_RST_REG0_EHCI0;
- reg_value &= ~A31_CCM_AHB1_RST_REG0_EHCI1;
- ccm_write_4(sc, A31_CCM_AHB1_RST_REG0, reg_value);
- }
- CCM_UNLOCK(sc);
-
- return (0);
-}
Index: head/sys/arm/allwinner/aw_ccu.c
===================================================================
--- head/sys/arm/allwinner/aw_ccu.c
+++ head/sys/arm/allwinner/aw_ccu.c
@@ -0,0 +1,224 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner oscillator clock
+ */
+
+#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 <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include "clkdev_if.h"
+
+#define CCU_BASE 0x01c20000
+#define CCU_SIZE 0x400
+
+struct aw_ccu_softc {
+ struct simplebus_softc sc;
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ struct mtx mtx;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10", 1 },
+ { "allwinner,sun7i-a20", 1 },
+ { "allwinner,sun6i-a31", 1 },
+ { "allwinner,sun6i-a31s", 1 },
+ { NULL, 0 }
+};
+
+static int
+aw_ccu_check_addr(bus_addr_t addr)
+{
+ if (addr < CCU_BASE || addr >= (CCU_BASE + CCU_SIZE))
+ return (EINVAL);
+ return (0);
+}
+
+static int
+aw_ccu_write_4(device_t dev, bus_addr_t addr, uint32_t val)
+{
+ struct aw_ccu_softc *sc;
+
+ if (aw_ccu_check_addr(addr) != 0)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ mtx_assert(&sc->mtx, MA_OWNED);
+ bus_space_write_4(sc->bst, sc->bsh, addr - CCU_BASE, val);
+
+ return (0);
+}
+
+static int
+aw_ccu_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
+{
+ struct aw_ccu_softc *sc;
+
+ if (aw_ccu_check_addr(addr) != 0)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ mtx_assert(&sc->mtx, MA_OWNED);
+ *val = bus_space_read_4(sc->bst, sc->bsh, addr - CCU_BASE);
+
+ return (0);
+}
+
+static int
+aw_ccu_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
+{
+ struct aw_ccu_softc *sc;
+ uint32_t val;
+
+ if (aw_ccu_check_addr(addr) != 0)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ mtx_assert(&sc->mtx, MA_OWNED);
+ val = bus_space_read_4(sc->bst, sc->bsh, addr - CCU_BASE);
+ val &= ~clr;
+ val |= set;
+ bus_space_write_4(sc->bst, sc->bsh, addr - CCU_BASE, val);
+
+ return (0);
+}
+
+static void
+aw_ccu_device_lock(device_t dev)
+{
+ struct aw_ccu_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->mtx);
+}
+
+static void
+aw_ccu_device_unlock(device_t dev)
+{
+ struct aw_ccu_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_unlock(&sc->mtx);
+}
+
+static int
+aw_ccu_probe(device_t dev)
+{
+ const char *name;
+ device_t pdev;
+
+ name = ofw_bus_get_name(dev);
+
+ if (name == NULL || strcmp(name, "clocks") != 0)
+ return (ENXIO);
+
+ pdev = device_get_parent(dev);
+ if (ofw_bus_search_compatible(pdev, compat_data)->ocd_data == 0)
+ return (0);
+
+ device_set_desc(dev, "Allwinner Clock Control Unit");
+ return (BUS_PROBE_SPECIFIC);
+}
+
+static int
+aw_ccu_attach(device_t dev)
+{
+ struct aw_ccu_softc *sc;
+ phandle_t node, child;
+ device_t cdev;
+ int error;
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(dev);
+
+ simplebus_init(dev, node);
+
+ /*
+ * Map CCU registers. The DT doesn't have a "reg" property for the
+ * /clocks node and child nodes have conflicting "reg" properties.
+ */
+ sc->bst = bus_get_bus_tag(dev);
+ error = bus_space_map(sc->bst, CCU_BASE, CCU_SIZE, 0, &sc->bsh);
+ if (error != 0) {
+ device_printf(dev, "couldn't map CCU: %d\n", error);
+ return (error);
+ }
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ /* Attach child devices */
+ for (child = OF_child(node); child > 0; child = OF_peer(child)) {
+ cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL);
+ if (cdev != NULL)
+ device_probe_and_attach(cdev);
+ }
+
+ return (bus_generic_attach(dev));
+}
+
+static device_method_t aw_ccu_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_ccu_probe),
+ DEVMETHOD(device_attach, aw_ccu_attach),
+
+ /* clkdev interface */
+ DEVMETHOD(clkdev_write_4, aw_ccu_write_4),
+ DEVMETHOD(clkdev_read_4, aw_ccu_read_4),
+ DEVMETHOD(clkdev_modify_4, aw_ccu_modify_4),
+ DEVMETHOD(clkdev_device_lock, aw_ccu_device_lock),
+ DEVMETHOD(clkdev_device_unlock, aw_ccu_device_unlock),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(aw_ccu, aw_ccu_driver, aw_ccu_methods,
+ sizeof(struct aw_ccu_softc), simplebus_driver);
+
+static devclass_t aw_ccu_devclass;
+
+EARLY_DRIVER_MODULE(aw_ccu, simplebus, aw_ccu_driver, aw_ccu_devclass,
+ 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+
+MODULE_VERSION(aw_ccu, 1);
Index: head/sys/arm/allwinner/aw_reset.c
===================================================================
--- head/sys/arm/allwinner/aw_reset.c
+++ head/sys/arm/allwinner/aw_reset.c
@@ -0,0 +1,163 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner module software reset registers
+ */
+
+#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 <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/hwreset/hwreset.h>
+
+#include "hwreset_if.h"
+
+#define RESET_OFFSET(index) ((index / 32) * 4)
+#define RESET_SHIFT(index) (index % 32)
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun6i-a31-ahb1-reset", 1 },
+ { "allwinner,sun6i-a31-clock-reset", 1 },
+ { NULL, 0 }
+};
+
+struct aw_reset_softc {
+ struct resource *res;
+ struct mtx mtx;
+};
+
+static struct resource_spec aw_reset_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+#define RESET_READ(sc, reg) bus_read_4((sc)->res, (reg))
+#define RESET_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
+
+static int
+aw_reset_assert(device_t dev, intptr_t id, bool reset)
+{
+ struct aw_reset_softc *sc;
+ uint32_t reg_value;
+
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->mtx);
+ reg_value = RESET_READ(sc, RESET_OFFSET(id));
+ if (reset)
+ reg_value &= ~(1 << RESET_SHIFT(id));
+ else
+ reg_value |= (1 << RESET_SHIFT(id));
+ RESET_WRITE(sc, RESET_OFFSET(id), reg_value);
+ mtx_unlock(&sc->mtx);
+
+ return (0);
+}
+
+static int
+aw_reset_is_asserted(device_t dev, intptr_t id, bool *reset)
+{
+ struct aw_reset_softc *sc;
+ uint32_t reg_value;
+
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->mtx);
+ reg_value = RESET_READ(sc, RESET_OFFSET(id));
+ mtx_unlock(&sc->mtx);
+
+ *reset = (reg_value & (1 << RESET_SHIFT(id))) != 0 ? false : true;
+
+ return (0);
+}
+
+static int
+aw_reset_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner Module Resets");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_reset_attach(device_t dev)
+{
+ struct aw_reset_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, aw_reset_spec, &sc->res) != 0) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ hwreset_register_ofw_provider(dev);
+
+ return (0);
+}
+
+static device_method_t aw_reset_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_reset_probe),
+ DEVMETHOD(device_attach, aw_reset_attach),
+
+ /* Reset interface */
+ DEVMETHOD(hwreset_assert, aw_reset_assert),
+ DEVMETHOD(hwreset_is_asserted, aw_reset_is_asserted),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_reset_driver = {
+ "aw_reset",
+ aw_reset_methods,
+ sizeof(struct aw_reset_softc),
+};
+
+static devclass_t aw_reset_devclass;
+
+DRIVER_MODULE(aw_reset, simplebus, aw_reset_driver, aw_reset_devclass, 0, 0);
+MODULE_VERSION(aw_reset, 1);
Index: head/sys/arm/allwinner/aw_usbphy.c
===================================================================
--- head/sys/arm/allwinner/aw_usbphy.c
+++ head/sys/arm/allwinner/aw_usbphy.c
@@ -44,10 +44,11 @@
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
-#include "gpio_if.h"
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/regulator/regulator.h>
#define USBPHY_NUMOFF 3
-#define GPIO_POLARITY(flags) (((flags) & 1) ? GPIO_PIN_LOW : GPIO_PIN_HIGH)
static struct ofw_compat_data compat_data[] = {
{ "allwinner,sun4i-a10-usb-phy", 1 },
@@ -58,89 +59,45 @@
};
static int
-awusbphy_gpio_set(device_t dev, phandle_t node, const char *pname)
-{
- pcell_t gpio_prop[4];
- phandle_t gpio_node;
- device_t gpio_dev;
- uint32_t pin, flags;
- ssize_t len;
- int val;
-
- len = OF_getencprop(node, pname, gpio_prop, sizeof(gpio_prop));
- if (len == -1)
- return (0);
-
- if (len != sizeof(gpio_prop)) {
- device_printf(dev, "property %s length was %d, expected %d\n",
- pname, len, sizeof(gpio_prop));
- return (ENXIO);
- }
-
- gpio_node = OF_node_from_xref(gpio_prop[0]);
- gpio_dev = OF_device_from_xref(gpio_prop[0]);
- if (gpio_dev == NULL) {
- device_printf(dev, "failed to get the GPIO device for %s\n",
- pname);
- return (ENOENT);
- }
-
- if (GPIO_MAP_GPIOS(gpio_dev, node, gpio_node,
- sizeof(gpio_prop) / sizeof(gpio_prop[0]) - 1, gpio_prop + 1,
- &pin, &flags) != 0) {
- device_printf(dev, "failed to map the GPIO pin for %s\n",
- pname);
- return (ENXIO);
- }
-
- val = GPIO_POLARITY(flags);
-
- GPIO_PIN_SETFLAGS(gpio_dev, pin, GPIO_PIN_OUTPUT);
- GPIO_PIN_SET(gpio_dev, pin, val);
-
- return (0);
-}
-
-static int
-awusbphy_supply_set(device_t dev, const char *pname)
-{
- phandle_t node, reg_node;
- pcell_t reg_xref;
-
- node = ofw_bus_get_node(dev);
-
- if (OF_getencprop(node, pname, ®_xref, sizeof(reg_xref)) == -1)
- return (0);
-
- reg_node = OF_node_from_xref(reg_xref);
-
- return (awusbphy_gpio_set(dev, reg_node, "gpio"));
-}
-
-static int
awusbphy_init(device_t dev)
{
char pname[20];
- phandle_t node;
int error, off;
-
- node = ofw_bus_get_node(dev);
-
- for (off = 0; off < USBPHY_NUMOFF; off++) {
- snprintf(pname, sizeof(pname), "usb%d_id_det-gpio", off);
- error = awusbphy_gpio_set(dev, node, pname);
- if (error)
+ regulator_t reg;
+ hwreset_t rst;
+ clk_t clk;
+
+ /* Enable clocks */
+ for (off = 0; clk_get_by_ofw_index(dev, off, &clk) == 0; off++) {
+ error = clk_enable(clk);
+ if (error != 0) {
+ device_printf(dev, "couldn't enable clock %s\n",
+ clk_get_name(clk));
return (error);
+ }
+ }
- snprintf(pname, sizeof(pname), "usb%d_vbus_det-gpio", off);
- error = awusbphy_gpio_set(dev, node, pname);
- if (error)
+ /* De-assert resets */
+ for (off = 0; hwreset_get_by_ofw_idx(dev, off, &rst) == 0; off++) {
+ error = hwreset_deassert(rst);
+ if (error != 0) {
+ device_printf(dev, "couldn't de-assert reset %d\n",
+ off);
return (error);
+ }
+ }
+ /* Enable regulator(s) */
+ for (off = 0; off < USBPHY_NUMOFF; off++) {
snprintf(pname, sizeof(pname), "usb%d_vbus-supply", off);
- error = awusbphy_supply_set(dev, pname);
- if (error)
+ if (regulator_get_by_ofw_property(dev, pname, ®) != 0)
+ continue;
+ error = regulator_enable(reg);
+ if (error != 0) {
+ device_printf(dev, "couldn't enable regulator %s\n",
+ pname);
return (error);
+ }
}
return (0);
Index: head/sys/arm/allwinner/clk/aw_ahbclk.c
===================================================================
--- head/sys/arm/allwinner/clk/aw_ahbclk.c
+++ head/sys/arm/allwinner/clk/aw_ahbclk.c
@@ -0,0 +1,308 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner AHB clock
+ */
+
+#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 <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include "clkdev_if.h"
+
+#define A10_AHB_CLK_DIV_RATIO (0x3 << 4)
+#define A10_AHB_CLK_DIV_RATIO_SHIFT 4
+
+#define A13_AHB_CLK_SRC_SEL (0x3 << 6)
+#define A13_AHB_CLK_SRC_SEL_MAX 3
+#define A13_AHB_CLK_SRC_SEL_SHIFT 6
+
+#define A31_AHB1_PRE_DIV (0x3 << 6)
+#define A31_AHB1_PRE_DIV_SHIFT 6
+#define A31_AHB1_CLK_SRC_SEL (0x3 << 12)
+#define A31_AHB1_CLK_SRC_SEL_PLL6 3
+#define A31_AHB1_CLK_SRC_SEL_MAX 3
+#define A31_AHB1_CLK_SRC_SEL_SHIFT 12
+
+enum aw_ahbclk_type {
+ AW_A10_AHB = 1,
+ AW_A13_AHB,
+ AW_A31_AHB1,
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-ahb-clk", AW_A10_AHB },
+ { "allwinner,sun5i-a13-ahb-clk", AW_A13_AHB },
+ { "allwinner,sun6i-a31-ahb1-clk", AW_A31_AHB1 },
+ { NULL, 0 }
+};
+
+struct aw_ahbclk_sc {
+ device_t clkdev;
+ bus_addr_t reg;
+ enum aw_ahbclk_type type;
+};
+
+#define AHBCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
+#define AHBCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
+#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
+#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
+
+static int
+aw_ahbclk_init(struct clknode *clk, device_t dev)
+{
+ struct aw_ahbclk_sc *sc;
+ uint32_t val, index;
+
+ sc = clknode_get_softc(clk);
+
+ switch (sc->type) {
+ case AW_A10_AHB:
+ index = 0;
+ break;
+ case AW_A13_AHB:
+ DEVICE_LOCK(sc);
+ AHBCLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+ index = (val & A13_AHB_CLK_SRC_SEL) >>
+ A13_AHB_CLK_SRC_SEL_SHIFT;
+ break;
+ case AW_A31_AHB1:
+ DEVICE_LOCK(sc);
+ AHBCLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+ index = (val & A31_AHB1_CLK_SRC_SEL) >>
+ A31_AHB1_CLK_SRC_SEL_SHIFT;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ clknode_init_parent_idx(clk, index);
+ return (0);
+}
+
+static int
+aw_ahbclk_recalc_freq(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_ahbclk_sc *sc;
+ uint32_t val, src_sel, div, pre_div;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ AHBCLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >>
+ A10_AHB_CLK_DIV_RATIO_SHIFT);
+
+ switch (sc->type) {
+ case AW_A31_AHB1:
+ src_sel = (val & A31_AHB1_CLK_SRC_SEL) >>
+ A31_AHB1_CLK_SRC_SEL_SHIFT;
+ if (src_sel == A31_AHB1_CLK_SRC_SEL_PLL6)
+ pre_div = ((val & A31_AHB1_PRE_DIV) >>
+ A31_AHB1_PRE_DIV_SHIFT) + 1;
+ else
+ pre_div = 1;
+ break;
+ default:
+ pre_div = 1;
+ break;
+ }
+
+ *freq = *freq / pre_div / div;
+
+ return (0);
+}
+
+static int
+aw_ahbclk_set_mux(struct clknode *clk, int index)
+{
+ struct aw_ahbclk_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ switch (sc->type) {
+ case AW_A10_AHB:
+ if (index != 0)
+ return (ERANGE);
+ break;
+ case AW_A13_AHB:
+ if (index < 0 || index > A13_AHB_CLK_SRC_SEL_MAX)
+ return (ERANGE);
+ DEVICE_LOCK(sc);
+ AHBCLK_READ(sc, &val);
+ val &= ~A13_AHB_CLK_SRC_SEL;
+ val |= (index << A13_AHB_CLK_SRC_SEL_SHIFT);
+ AHBCLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static clknode_method_t aw_ahbclk_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_ahbclk_init),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_ahbclk_recalc_freq),
+ CLKNODEMETHOD(clknode_set_mux, aw_ahbclk_set_mux),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(aw_ahbclk_clknode, aw_ahbclk_clknode_class,
+ aw_ahbclk_clknode_methods, sizeof(struct aw_ahbclk_sc), clknode_class);
+
+static int
+aw_ahbclk_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner AHB Clock");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_ahbclk_attach(device_t dev)
+{
+ struct clknode_init_def def;
+ struct aw_ahbclk_sc *sc;
+ struct clkdom *clkdom;
+ struct clknode *clk;
+ clk_t clk_parent;
+ bus_addr_t paddr;
+ bus_size_t psize;
+ phandle_t node;
+ int error, ncells, i;
+
+ node = ofw_bus_get_node(dev);
+
+ if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
+ device_printf(dev, "cannot parse 'reg' property\n");
+ return (ENXIO);
+ }
+
+ error = ofw_bus_parse_xref_list_get_length(node, "clocks",
+ "#clock-cells", &ncells);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock count\n");
+ return (error);
+ }
+
+ clkdom = clkdom_create(dev);
+
+ memset(&def, 0, sizeof(def));
+ def.id = 1;
+ def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP,
+ M_WAITOK);
+ for (i = 0; i < ncells; i++) {
+ error = clk_get_by_ofw_index(dev, i, &clk_parent);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock %d\n", i);
+ goto fail;
+ }
+ def.parent_names[i] = clk_get_name(clk_parent);
+ clk_release(clk_parent);
+ }
+ def.parent_cnt = ncells;
+
+ error = clk_parse_ofw_clk_name(dev, node, &def.name);
+ if (error != 0) {
+ device_printf(dev, "cannot parse clock name\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ clk = clknode_create(clkdom, &aw_ahbclk_clknode_class, &def);
+ if (clk == NULL) {
+ device_printf(dev, "cannot create clknode\n");
+ error = ENXIO;
+ goto fail;
+ }
+ sc = clknode_get_softc(clk);
+ sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ sc->reg = paddr;
+ sc->clkdev = device_get_parent(dev);
+
+ clknode_register(clkdom, clk);
+
+ if (clkdom_finit(clkdom) != 0) {
+ device_printf(dev, "cannot finalize clkdom initialization\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ return (0);
+
+fail:
+ return (error);
+}
+
+static device_method_t aw_ahbclk_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_ahbclk_probe),
+ DEVMETHOD(device_attach, aw_ahbclk_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_ahbclk_driver = {
+ "aw_ahbclk",
+ aw_ahbclk_methods,
+ 0
+};
+
+static devclass_t aw_ahbclk_devclass;
+
+EARLY_DRIVER_MODULE(aw_ahbclk, simplebus, aw_ahbclk_driver,
+ aw_ahbclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
Index: head/sys/arm/allwinner/clk/aw_apbclk.c
===================================================================
--- head/sys/arm/allwinner/clk/aw_apbclk.c
+++ head/sys/arm/allwinner/clk/aw_apbclk.c
@@ -0,0 +1,283 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner APB clock
+ */
+
+#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 <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include "clkdev_if.h"
+
+#define APB0_CLK_RATIO (0x3 << 8)
+#define APB0_CLK_RATIO_SHIFT 8
+#define APB1_CLK_SRC_SEL (0x3 << 24)
+#define APB1_CLK_SRC_SEL_SHIFT 24
+#define APB1_CLK_SRC_SEL_MAX 0x3
+#define APB1_CLK_RAT_N (0x3 << 16)
+#define APB1_CLK_RAT_N_SHIFT 16
+#define APB1_CLK_RAT_M (0x1f << 0)
+#define APB1_CLK_RAT_M_SHIFT 0
+
+enum aw_apbclk_type {
+ AW_A10_APB0 = 1,
+ AW_A10_APB1,
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-apb0-clk", AW_A10_APB0 },
+ { "allwinner,sun4i-a10-apb1-clk", AW_A10_APB1 },
+ { NULL, 0 }
+};
+
+struct aw_apbclk_sc {
+ device_t clkdev;
+ bus_addr_t reg;
+ enum aw_apbclk_type type;
+};
+
+#define APBCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
+#define APBCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
+#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
+#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
+
+static int
+aw_apbclk_init(struct clknode *clk, device_t dev)
+{
+ struct aw_apbclk_sc *sc;
+ uint32_t val, index;
+
+ sc = clknode_get_softc(clk);
+
+ switch (sc->type) {
+ case AW_A10_APB0:
+ index = 0;
+ break;
+ case AW_A10_APB1:
+ DEVICE_LOCK(sc);
+ APBCLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+ index = (val & APB1_CLK_SRC_SEL) >> APB1_CLK_SRC_SEL_SHIFT;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ clknode_init_parent_idx(clk, index);
+ return (0);
+}
+
+static int
+aw_apbclk_recalc_freq(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_apbclk_sc *sc;
+ uint32_t val, div, m, n;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ APBCLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ switch (sc->type) {
+ case AW_A10_APB0:
+ div = 1 << ((val & APB0_CLK_RATIO) >> APB0_CLK_RATIO_SHIFT);
+ if (div == 1)
+ div = 2;
+ *freq = *freq / div;
+ break;
+ case AW_A10_APB1:
+ n = 1 << ((val & APB1_CLK_RAT_N) >> APB1_CLK_RAT_N_SHIFT);
+ m = ((val & APB1_CLK_RAT_N) >> APB1_CLK_RAT_M_SHIFT) + 1;
+ *freq = *freq / n / m;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+aw_apbclk_set_mux(struct clknode *clk, int index)
+{
+ struct aw_apbclk_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ if (sc->type != AW_A10_APB1)
+ return (ENXIO);
+
+ if (index < 0 || index > APB1_CLK_SRC_SEL_MAX)
+ return (ERANGE);
+
+ DEVICE_LOCK(sc);
+ APBCLK_READ(sc, &val);
+ val &= ~APB1_CLK_SRC_SEL;
+ val |= (index << APB1_CLK_SRC_SEL_SHIFT);
+ APBCLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ return (0);
+}
+
+static clknode_method_t aw_apbclk_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_apbclk_init),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_apbclk_recalc_freq),
+ CLKNODEMETHOD(clknode_set_mux, aw_apbclk_set_mux),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(aw_apbclk_clknode, aw_apbclk_clknode_class,
+ aw_apbclk_clknode_methods, sizeof(struct aw_apbclk_sc), clknode_class);
+
+static int
+aw_apbclk_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner APB Clock");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_apbclk_attach(device_t dev)
+{
+ struct clknode_init_def def;
+ struct aw_apbclk_sc *sc;
+ struct clkdom *clkdom;
+ struct clknode *clk;
+ clk_t clk_parent;
+ bus_addr_t paddr;
+ bus_size_t psize;
+ phandle_t node;
+ int error, ncells, i;
+
+ node = ofw_bus_get_node(dev);
+
+ if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
+ device_printf(dev, "cannot parse 'reg' property\n");
+ return (ENXIO);
+ }
+
+ error = ofw_bus_parse_xref_list_get_length(node, "clocks",
+ "#clock-cells", &ncells);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock count\n");
+ return (error);
+ }
+
+ clkdom = clkdom_create(dev);
+
+ memset(&def, 0, sizeof(def));
+ error = clk_parse_ofw_clk_name(dev, node, &def.name);
+ if (error != 0) {
+ device_printf(dev, "cannot parse clock name\n");
+ error = ENXIO;
+ goto fail;
+ }
+ def.id = 1;
+ def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK);
+ for (i = 0; i < ncells; i++) {
+ error = clk_get_by_ofw_index(dev, i, &clk_parent);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock %d\n", i);
+ goto fail;
+ }
+ def.parent_names[i] = clk_get_name(clk_parent);
+ clk_release(clk_parent);
+ }
+ def.parent_cnt = ncells;
+
+ clk = clknode_create(clkdom, &aw_apbclk_clknode_class, &def);
+ if (clk == NULL) {
+ device_printf(dev, "cannot create clknode\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ sc = clknode_get_softc(clk);
+ sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ sc->reg = paddr;
+ sc->clkdev = device_get_parent(dev);
+
+ clknode_register(clkdom, clk);
+
+ if (clkdom_finit(clkdom) != 0) {
+ device_printf(dev, "cannot finalize clkdom initialization\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ return (0);
+
+fail:
+ return (error);
+}
+
+static device_method_t aw_apbclk_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_apbclk_probe),
+ DEVMETHOD(device_attach, aw_apbclk_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_apbclk_driver = {
+ "aw_apbclk",
+ aw_apbclk_methods,
+ 0
+};
+
+static devclass_t aw_apbclk_devclass;
+
+EARLY_DRIVER_MODULE(aw_apbclk, simplebus, aw_apbclk_driver,
+ aw_apbclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
Index: head/sys/arm/allwinner/clk/aw_axiclk.c
===================================================================
--- head/sys/arm/allwinner/clk/aw_axiclk.c
+++ head/sys/arm/allwinner/clk/aw_axiclk.c
@@ -0,0 +1,201 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner AXI clock
+ */
+
+#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 <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
+
+#include <dev/extres/clk/clk_mux.h>
+#include <dev/extres/clk/clk_gate.h>
+
+#include "clkdev_if.h"
+
+#define AXI_CLK_DIV_RATIO (0x3 << 0)
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-axi-clk", 1 },
+ { NULL, 0 }
+};
+
+struct aw_axiclk_sc {
+ device_t clkdev;
+ bus_addr_t reg;
+};
+
+#define AXICLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
+#define AXICLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
+#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
+#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
+
+static int
+aw_axiclk_init(struct clknode *clk, device_t dev)
+{
+ clknode_init_parent_idx(clk, 0);
+ return (0);
+}
+
+static int
+aw_axiclk_recalc_freq(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_axiclk_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ AXICLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ *freq = *freq / ((val & AXI_CLK_DIV_RATIO) + 1);
+
+ return (0);
+}
+
+static clknode_method_t aw_axiclk_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_axiclk_init),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_axiclk_recalc_freq),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(aw_axiclk_clknode, aw_axiclk_clknode_class,
+ aw_axiclk_clknode_methods, sizeof(struct aw_axiclk_sc), clknode_class);
+
+static int
+aw_axiclk_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner AXI Clock");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_axiclk_attach(device_t dev)
+{
+ struct clknode_init_def def;
+ struct aw_axiclk_sc *sc;
+ struct clkdom *clkdom;
+ struct clknode *clk;
+ clk_t clk_parent;
+ bus_addr_t paddr;
+ bus_size_t psize;
+ phandle_t node;
+ int error;
+
+ node = ofw_bus_get_node(dev);
+
+ if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
+ device_printf(dev, "cannot parse 'reg' property\n");
+ return (ENXIO);
+ }
+
+ clkdom = clkdom_create(dev);
+
+ error = clk_get_by_ofw_index(dev, 0, &clk_parent);
+ if (error != 0) {
+ device_printf(dev, "cannot parse clock parent\n");
+ return (ENXIO);
+ }
+
+ memset(&def, 0, sizeof(def));
+ error = clk_parse_ofw_clk_name(dev, node, &def.name);
+ if (error != 0) {
+ device_printf(dev, "cannot parse clock name\n");
+ error = ENXIO;
+ goto fail;
+ }
+ def.id = 1;
+ def.parent_names = malloc(sizeof(char *), M_OFWPROP, M_WAITOK);
+ def.parent_names[0] = clk_get_name(clk_parent);
+ def.parent_cnt = 1;
+
+ clk = clknode_create(clkdom, &aw_axiclk_clknode_class, &def);
+ if (clk == NULL) {
+ device_printf(dev, "cannot create clknode\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ sc = clknode_get_softc(clk);
+ sc->reg = paddr;
+ sc->clkdev = device_get_parent(dev);
+
+ clknode_register(clkdom, clk);
+
+ if (clkdom_finit(clkdom) != 0) {
+ device_printf(dev, "cannot finalize clkdom initialization\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ return (0);
+
+fail:
+ return (error);
+}
+
+static device_method_t aw_axiclk_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_axiclk_probe),
+ DEVMETHOD(device_attach, aw_axiclk_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_axiclk_driver = {
+ "aw_axiclk",
+ aw_axiclk_methods,
+ 0
+};
+
+static devclass_t aw_axiclk_devclass;
+
+EARLY_DRIVER_MODULE(aw_axiclk, simplebus, aw_axiclk_driver,
+ aw_axiclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
Index: head/sys/arm/allwinner/clk/aw_codecclk.c
===================================================================
--- head/sys/arm/allwinner/clk/aw_codecclk.c
+++ head/sys/arm/allwinner/clk/aw_codecclk.c
@@ -0,0 +1,164 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner CODEC clock
+ */
+
+#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 <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
+
+#include <dev/extres/clk/clk_gate.h>
+#include <dev/extres/hwreset/hwreset.h>
+
+#include "clkdev_if.h"
+
+#define SCLK_GATING_SHIFT 31
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-codec-clk", 1 },
+ { NULL, 0 }
+};
+
+static int
+aw_codecclk_create(device_t dev, bus_addr_t paddr, struct clkdom *clkdom,
+ const char *pclkname, const char *clkname, int index)
+{
+ const char *parent_names[1] = { pclkname };
+ struct clk_gate_def def;
+
+ memset(&def, 0, sizeof(def));
+ def.clkdef.id = index;
+ def.clkdef.name = clkname;
+ def.clkdef.parent_names = parent_names;
+ def.clkdef.parent_cnt = 1;
+ def.offset = paddr;
+ def.shift = SCLK_GATING_SHIFT;
+ def.mask = 1;
+ def.on_value = 1;
+ def.off_value = 0;
+
+ return (clknode_gate_register(clkdom, &def));
+}
+
+static int
+aw_codecclk_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner CODEC Clock");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_codecclk_attach(device_t dev)
+{
+ struct clkdom *clkdom;
+ const char **names;
+ int nout, error;
+ uint32_t *indices;
+ clk_t clk_parent;
+ bus_addr_t paddr;
+ bus_size_t psize;
+ phandle_t node;
+
+ node = ofw_bus_get_node(dev);
+ indices = NULL;
+
+ if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
+ device_printf(dev, "cannot parse 'reg' property\n");
+ return (ENXIO);
+ }
+
+ clkdom = clkdom_create(dev);
+
+ nout = clk_parse_ofw_out_names(dev, node, &names, &indices);
+ if (nout != 1) {
+ device_printf(dev, "must have exactly one output clock\n");
+ error = ENOENT;
+ goto fail;
+ }
+
+ error = clk_get_by_ofw_index(dev, 0, &clk_parent);
+ if (error != 0) {
+ device_printf(dev, "cannot parse clock parent\n");
+ return (ENXIO);
+ }
+
+ error = aw_codecclk_create(dev, paddr, clkdom,
+ clk_get_name(clk_parent), names[0], 1);
+
+ if (clkdom_finit(clkdom) != 0) {
+ device_printf(dev, "cannot finalize clkdom initialization\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ return (0);
+
+fail:
+ return (error);
+}
+
+static device_method_t aw_codecclk_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_codecclk_probe),
+ DEVMETHOD(device_attach, aw_codecclk_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_codecclk_driver = {
+ "aw_codecclk",
+ aw_codecclk_methods,
+ 0
+};
+
+static devclass_t aw_codecclk_devclass;
+
+EARLY_DRIVER_MODULE(aw_codecclk, simplebus, aw_codecclk_driver,
+ aw_codecclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
Index: head/sys/arm/allwinner/clk/aw_cpuclk.c
===================================================================
--- head/sys/arm/allwinner/clk/aw_cpuclk.c
+++ head/sys/arm/allwinner/clk/aw_cpuclk.c
@@ -0,0 +1,161 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner CPU clock
+ */
+
+#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 <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
+
+#include <dev/extres/clk/clk_mux.h>
+
+#define CPU_CLK_SRC_SEL_WIDTH 2
+#define CPU_CLK_SRC_SEL_SHIFT 16
+
+static int
+aw_cpuclk_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-cpu-clk"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner CPU Clock");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_cpuclk_attach(device_t dev)
+{
+ struct clk_mux_def def;
+ struct clkdom *clkdom;
+ bus_addr_t paddr;
+ bus_size_t psize;
+ phandle_t node;
+ int error, ncells, i;
+ clk_t clk;
+
+ node = ofw_bus_get_node(dev);
+
+ if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
+ device_printf(dev, "cannot parse 'reg' property\n");
+ return (ENXIO);
+ }
+
+ error = ofw_bus_parse_xref_list_get_length(node, "clocks",
+ "#clock-cells", &ncells);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock count\n");
+ return (error);
+ }
+
+ clkdom = clkdom_create(dev);
+
+ memset(&def, 0, sizeof(def));
+ def.clkdef.id = 1;
+ def.clkdef.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP,
+ M_WAITOK);
+ for (i = 0; i < ncells; i++) {
+ error = clk_get_by_ofw_index(dev, i, &clk);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock %d\n", i);
+ goto fail;
+ }
+ def.clkdef.parent_names[i] = clk_get_name(clk);
+ clk_release(clk);
+ }
+ def.clkdef.parent_cnt = ncells;
+ def.offset = paddr;
+ def.shift = CPU_CLK_SRC_SEL_SHIFT;
+ def.width = CPU_CLK_SRC_SEL_WIDTH;
+
+ error = clk_parse_ofw_clk_name(dev, node, &def.clkdef.name);
+ if (error != 0) {
+ device_printf(dev, "cannot parse clock name\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ error = clknode_mux_register(clkdom, &def);
+ if (error != 0) {
+ device_printf(dev, "cannot register mux clock\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (clkdom_finit(clkdom) != 0) {
+ device_printf(dev, "cannot finalize clkdom initialization\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ free(__DECONST(char *, def.clkdef.parent_names), M_OFWPROP);
+ free(__DECONST(char *, def.clkdef.name), M_OFWPROP);
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ return (0);
+
+fail:
+ free(__DECONST(char *, def.clkdef.name), M_OFWPROP);
+ return (error);
+}
+
+static device_method_t aw_cpuclk_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_cpuclk_probe),
+ DEVMETHOD(device_attach, aw_cpuclk_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_cpuclk_driver = {
+ "aw_cpuclk",
+ aw_cpuclk_methods,
+ 0
+};
+
+static devclass_t aw_cpuclk_devclass;
+
+EARLY_DRIVER_MODULE(aw_cpuclk, simplebus, aw_cpuclk_driver,
+ aw_cpuclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
Index: head/sys/arm/allwinner/clk/aw_debeclk.c
===================================================================
--- head/sys/arm/allwinner/clk/aw_debeclk.c
+++ head/sys/arm/allwinner/clk/aw_debeclk.c
@@ -0,0 +1,351 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner display backend clocks
+ */
+
+#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 <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+
+#include "clkdev_if.h"
+#include "hwreset_if.h"
+
+#define SCLK_GATING (1 << 31)
+#define BE_RST (1 << 30)
+#define CLK_SRC_SEL (0x3 << 24)
+#define CLK_SRC_SEL_SHIFT 24
+#define CLK_SRC_SEL_MAX 2
+#define CLK_SRC_SEL_PLL3 0
+#define CLK_SRC_SEL_PLL7 1
+#define CLK_SRC_SEL_PLL5 2
+#define CLK_RATIO_M (0xf << 0)
+#define CLK_RATIO_M_SHIFT 0
+#define CLK_RATIO_M_MAX 0xf
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-de-be-clk", 1 },
+ { NULL, 0 }
+};
+
+struct aw_debeclk_softc {
+ device_t clkdev;
+ bus_addr_t reg;
+};
+
+#define DEBECLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
+#define DEBECLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
+#define DEBECLK_MODIFY(sc, clr, set) \
+ CLKDEV_MODIFY_4((sc)->clkdev, (sc)->reg, (clr), (set))
+#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
+#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
+
+static int
+aw_debeclk_hwreset_assert(device_t dev, intptr_t id, bool value)
+{
+ struct aw_debeclk_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ DEVICE_LOCK(sc);
+ error = DEBECLK_MODIFY(sc, BE_RST, value ? 0 : BE_RST);
+ DEVICE_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+aw_debeclk_hwreset_is_asserted(device_t dev, intptr_t id, bool *value)
+{
+ struct aw_debeclk_softc *sc;
+ uint32_t val;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ DEVICE_LOCK(sc);
+ error = DEBECLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ if (error)
+ return (error);
+
+ *value = (val & BE_RST) != 0 ? false : true;
+
+ return (0);
+}
+
+static int
+aw_debeclk_init(struct clknode *clk, device_t dev)
+{
+ struct aw_debeclk_softc *sc;
+ uint32_t val, index;
+
+ sc = clknode_get_softc(clk);
+
+ /* Set BE source to PLL5 (DDR external peripheral clock) */
+ index = CLK_SRC_SEL_PLL5;
+
+ DEVICE_LOCK(sc);
+ DEBECLK_READ(sc, &val);
+ val &= ~CLK_SRC_SEL;
+ val |= (index << CLK_SRC_SEL_SHIFT);
+ DEBECLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ clknode_init_parent_idx(clk, index);
+ return (0);
+}
+
+static int
+aw_debeclk_set_mux(struct clknode *clk, int index)
+{
+ struct aw_debeclk_softc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ if (index < 0 || index > CLK_SRC_SEL_MAX)
+ return (ERANGE);
+
+ DEVICE_LOCK(sc);
+ DEBECLK_READ(sc, &val);
+ val &= ~CLK_SRC_SEL;
+ val |= (index << CLK_SRC_SEL_SHIFT);
+ DEBECLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+aw_debeclk_set_gate(struct clknode *clk, bool enable)
+{
+ struct aw_debeclk_softc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ DEBECLK_READ(sc, &val);
+ if (enable)
+ val |= SCLK_GATING;
+ else
+ val &= ~SCLK_GATING;
+ DEBECLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+aw_debeclk_recalc_freq(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_debeclk_softc *sc;
+ uint32_t val, m;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ DEBECLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ m = ((val & CLK_RATIO_M) >> CLK_RATIO_M_SHIFT) + 1;
+
+ *freq = *freq / m;
+
+ return (0);
+}
+
+static int
+aw_debeclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct aw_debeclk_softc *sc;
+ uint32_t val, m;
+
+ sc = clknode_get_softc(clk);
+
+ m = howmany(fin, *fout) - 1;
+
+ DEVICE_LOCK(sc);
+ DEBECLK_READ(sc, &val);
+ val &= ~CLK_RATIO_M;
+ val |= (m << CLK_RATIO_M_SHIFT);
+ DEBECLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ *fout = fin / (m + 1);
+ *stop = 1;
+
+ return (0);
+}
+
+static clknode_method_t aw_debeclk_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_debeclk_init),
+ CLKNODEMETHOD(clknode_set_gate, aw_debeclk_set_gate),
+ CLKNODEMETHOD(clknode_set_mux, aw_debeclk_set_mux),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_debeclk_recalc_freq),
+ CLKNODEMETHOD(clknode_set_freq, aw_debeclk_set_freq),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(aw_debeclk_clknode, aw_debeclk_clknode_class,
+ aw_debeclk_clknode_methods, sizeof(struct aw_debeclk_softc), clknode_class);
+
+static int
+aw_debeclk_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner Display Engine Backend Clock");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_debeclk_attach(device_t dev)
+{
+ struct clknode_init_def def;
+ struct aw_debeclk_softc *sc, *clk_sc;
+ struct clkdom *clkdom;
+ struct clknode *clk;
+ clk_t clk_parent;
+ bus_size_t psize;
+ phandle_t node;
+ int error, ncells, i;
+
+ sc = device_get_softc(dev);
+ sc->clkdev = device_get_parent(dev);
+ node = ofw_bus_get_node(dev);
+
+ if (ofw_reg_to_paddr(node, 0, &sc->reg, &psize, NULL) != 0) {
+ device_printf(dev, "cannot parse 'reg' property\n");
+ return (ENXIO);
+ }
+
+ error = ofw_bus_parse_xref_list_get_length(node, "clocks",
+ "#clock-cells", &ncells);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock count\n");
+ return (error);
+ }
+
+ clkdom = clkdom_create(dev);
+
+ memset(&def, 0, sizeof(def));
+ error = clk_parse_ofw_clk_name(dev, node, &def.name);
+ if (error != 0) {
+ device_printf(dev, "cannot parse clock name\n");
+ error = ENXIO;
+ goto fail;
+ }
+ def.id = 1;
+ def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK);
+ for (i = 0; i < ncells; i++) {
+ error = clk_get_by_ofw_index(dev, i, &clk_parent);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock %d\n", i);
+ goto fail;
+ }
+ def.parent_names[i] = clk_get_name(clk_parent);
+ clk_release(clk_parent);
+ }
+ def.parent_cnt = ncells;
+
+ clk = clknode_create(clkdom, &aw_debeclk_clknode_class, &def);
+ if (clk == NULL) {
+ device_printf(dev, "cannot create clknode\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ clk_sc = clknode_get_softc(clk);
+ clk_sc->reg = sc->reg;
+ clk_sc->clkdev = device_get_parent(dev);
+
+ clknode_register(clkdom, clk);
+
+ if (clkdom_finit(clkdom) != 0) {
+ device_printf(dev, "cannot finalize clkdom initialization\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ hwreset_register_ofw_provider(dev);
+
+ return (0);
+
+fail:
+ return (error);
+}
+
+static device_method_t aw_debeclk_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_debeclk_probe),
+ DEVMETHOD(device_attach, aw_debeclk_attach),
+
+ /* Reset interface */
+ DEVMETHOD(hwreset_assert, aw_debeclk_hwreset_assert),
+ DEVMETHOD(hwreset_is_asserted, aw_debeclk_hwreset_is_asserted),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_debeclk_driver = {
+ "aw_debeclk",
+ aw_debeclk_methods,
+ sizeof(struct aw_debeclk_softc)
+};
+
+static devclass_t aw_debeclk_devclass;
+
+EARLY_DRIVER_MODULE(aw_debeclk, simplebus, aw_debeclk_driver,
+ aw_debeclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
Index: head/sys/arm/allwinner/clk/aw_gate.c
===================================================================
--- head/sys/arm/allwinner/clk/aw_gate.c
+++ head/sys/arm/allwinner/clk/aw_gate.c
@@ -0,0 +1,198 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner clock gates
+ */
+
+#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 <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
+
+#include <dev/extres/clk/clk_gate.h>
+
+#define GATE_OFFSET(index) ((index / 32) * 4)
+#define GATE_SHIFT(index) (index % 32)
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-dram-gates-clk",
+ (uintptr_t)"Allwinner DRAM Clock Gates" },
+ { "allwinner,sun4i-a10-ahb-gates-clk",
+ (uintptr_t)"Allwinner AHB Clock Gates" },
+ { "allwinner,sun4i-a10-apb0-gates-clk",
+ (uintptr_t)"Allwinner APB0 Clock Gates" },
+ { "allwinner,sun4i-a10-apb1-gates-clk",
+ (uintptr_t)"Allwinner APB1 Clock Gates" },
+
+ { "allwinner,sun7i-a20-ahb-gates-clk",
+ (uintptr_t)"Allwinner AHB Clock Gates" },
+ { "allwinner,sun7i-a20-apb0-gates-clk",
+ (uintptr_t)"Allwinner APB0 Clock Gates" },
+ { "allwinner,sun7i-a20-apb1-gates-clk",
+ (uintptr_t)"Allwinner APB1 Clock Gates" },
+
+ { "allwinner,sun6i-a31-ahb1-gates-clk",
+ (uintptr_t)"Allwinner AHB1 Clock Gates" },
+ { "allwinner,sun6i-a31-apb0-gates-clk",
+ (uintptr_t)"Allwinner APB0 Clock Gates" },
+ { "allwinner,sun6i-a31-apb1-gates-clk",
+ (uintptr_t)"Allwinner APB1 Clock Gates" },
+ { "allwinner,sun6i-a31-apb2-gates-clk",
+ (uintptr_t)"Allwinner APB2 Clock Gates" },
+
+ { NULL, 0 }
+};
+
+static int
+aw_gate_create(device_t dev, bus_addr_t paddr, struct clkdom *clkdom,
+ const char *pclkname, const char *clkname, int index)
+{
+ const char *parent_names[1] = { pclkname };
+ struct clk_gate_def def;
+
+ memset(&def, 0, sizeof(def));
+ def.clkdef.id = index;
+ def.clkdef.name = clkname;
+ def.clkdef.parent_names = parent_names;
+ def.clkdef.parent_cnt = 1;
+ def.offset = paddr + GATE_OFFSET(index);
+ def.shift = GATE_SHIFT(index);
+ def.mask = 1;
+ def.on_value = 1;
+ def.off_value = 0;
+
+ return (clknode_gate_register(clkdom, &def));
+}
+
+static int
+aw_gate_probe(device_t dev)
+{
+ const char *d;
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ d = (const char *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ if (d == NULL)
+ return (ENXIO);
+
+ device_set_desc(dev, d);
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_gate_attach(device_t dev)
+{
+ struct clkdom *clkdom;
+ const char **names;
+ int index, nout, error;
+ uint32_t *indices;
+ clk_t clk_parent;
+ bus_addr_t paddr;
+ bus_size_t psize;
+ phandle_t node;
+
+ node = ofw_bus_get_node(dev);
+ indices = NULL;
+
+ if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
+ device_printf(dev, "cannot parse 'reg' property\n");
+ return (ENXIO);
+ }
+
+ clkdom = clkdom_create(dev);
+
+ nout = clk_parse_ofw_out_names(dev, node, &names, &indices);
+ if (nout == 0) {
+ device_printf(dev, "no clock outputs found\n");
+ error = ENOENT;
+ goto fail;
+ }
+ if (indices == NULL) {
+ device_printf(dev, "no clock-indices property\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ error = clk_get_by_ofw_index(dev, 0, &clk_parent);
+ if (error != 0) {
+ device_printf(dev, "cannot parse clock parent\n");
+ return (ENXIO);
+ }
+
+ for (index = 0; index < nout; index++) {
+ error = aw_gate_create(dev, paddr, clkdom,
+ clk_get_name(clk_parent), names[index], indices[index]);
+ if (error)
+ goto fail;
+ }
+
+ if (clkdom_finit(clkdom) != 0) {
+ device_printf(dev, "cannot finalize clkdom initialization\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ return (0);
+
+fail:
+ return (error);
+}
+
+static device_method_t aw_gate_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_gate_probe),
+ DEVMETHOD(device_attach, aw_gate_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_gate_driver = {
+ "aw_gate",
+ aw_gate_methods,
+ 0
+};
+
+static devclass_t aw_gate_devclass;
+
+EARLY_DRIVER_MODULE(aw_gate, simplebus, aw_gate_driver,
+ aw_gate_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
Index: head/sys/arm/allwinner/clk/aw_gmacclk.c
===================================================================
--- head/sys/arm/allwinner/clk/aw_gmacclk.c
+++ head/sys/arm/allwinner/clk/aw_gmacclk.c
@@ -0,0 +1,259 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner GMAC clock
+ */
+
+#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 <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
+
+#include <dev/extres/clk/clk_mux.h>
+#include <dev/extres/clk/clk_gate.h>
+
+#include "clkdev_if.h"
+
+#define GMAC_CLK_PIT (0x1 << 2)
+#define GMAC_CLK_PIT_SHIFT 2
+#define GMAC_CLK_PIT_MII 0
+#define GMAC_CLK_PIT_RGMII 1
+#define GMAC_CLK_SRC (0x3 << 0)
+#define GMAC_CLK_SRC_SHIFT 0
+#define GMAC_CLK_SRC_MII 0
+#define GMAC_CLK_SRC_EXT_RGMII 1
+#define GMAC_CLK_SRC_RGMII 2
+
+#define CLK_IDX_MII 0
+#define CLK_IDX_RGMII 1
+#define CLK_IDX_COUNT 2
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun7i-a20-gmac-clk", 1 },
+ { NULL, 0 }
+};
+
+struct aw_gmacclk_sc {
+ device_t clkdev;
+ bus_addr_t reg;
+};
+
+#define GMACCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
+#define GMACCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
+#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
+#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
+
+static int
+aw_gmacclk_init(struct clknode *clk, device_t dev)
+{
+ struct aw_gmacclk_sc *sc;
+ uint32_t val, index;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ GMACCLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ switch ((val & GMAC_CLK_SRC) >> GMAC_CLK_SRC_SHIFT) {
+ case GMAC_CLK_SRC_MII:
+ index = CLK_IDX_MII;
+ break;
+ case GMAC_CLK_SRC_RGMII:
+ index = CLK_IDX_RGMII;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ clknode_init_parent_idx(clk, index);
+ return (0);
+}
+
+static int
+aw_gmacclk_set_mux(struct clknode *clk, int index)
+{
+ struct aw_gmacclk_sc *sc;
+ uint32_t val, clk_src, pit;
+ int error;
+
+ sc = clknode_get_softc(clk);
+ error = 0;
+
+ switch (index) {
+ case CLK_IDX_MII:
+ clk_src = GMAC_CLK_SRC_MII;
+ pit = GMAC_CLK_PIT_MII;
+ break;
+ case CLK_IDX_RGMII:
+ clk_src = GMAC_CLK_SRC_RGMII;
+ pit = GMAC_CLK_PIT_RGMII;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ DEVICE_LOCK(sc);
+ GMACCLK_READ(sc, &val);
+ val &= ~(GMAC_CLK_SRC | GMAC_CLK_PIT);
+ val |= (clk_src << GMAC_CLK_SRC_SHIFT);
+ val |= (pit << GMAC_CLK_PIT_SHIFT);
+ GMACCLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ return (0);
+}
+
+static clknode_method_t aw_gmacclk_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_gmacclk_init),
+ CLKNODEMETHOD(clknode_set_mux, aw_gmacclk_set_mux),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(aw_gmacclk_clknode, aw_gmacclk_clknode_class,
+ aw_gmacclk_clknode_methods, sizeof(struct aw_gmacclk_sc), clknode_class);
+
+static int
+aw_gmacclk_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner Module Clock");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_gmacclk_attach(device_t dev)
+{
+ struct clknode_init_def def;
+ struct aw_gmacclk_sc *sc;
+ struct clkdom *clkdom;
+ struct clknode *clk;
+ clk_t clk_parent;
+ bus_addr_t paddr;
+ bus_size_t psize;
+ phandle_t node;
+ int error, ncells, i;
+
+ node = ofw_bus_get_node(dev);
+
+ if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
+ device_printf(dev, "cannot parse 'reg' property\n");
+ return (ENXIO);
+ }
+
+ error = ofw_bus_parse_xref_list_get_length(node, "clocks",
+ "#clock-cells", &ncells);
+ if (error != 0 || ncells != CLK_IDX_COUNT) {
+ device_printf(dev, "couldn't find parent clocks\n");
+ return (ENXIO);
+ }
+
+ clkdom = clkdom_create(dev);
+
+ memset(&def, 0, sizeof(def));
+ error = clk_parse_ofw_clk_name(dev, node, &def.name);
+ if (error != 0) {
+ device_printf(dev, "cannot parse clock name\n");
+ error = ENXIO;
+ goto fail;
+ }
+ def.id = 1;
+ def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK);
+ for (i = 0; i < ncells; i++) {
+ error = clk_get_by_ofw_index(dev, i, &clk_parent);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock %d\n", error);
+ goto fail;
+ }
+ def.parent_names[i] = clk_get_name(clk_parent);
+ clk_release(clk_parent);
+ }
+ def.parent_cnt = ncells;
+
+ clk = clknode_create(clkdom, &aw_gmacclk_clknode_class, &def);
+ if (clk == NULL) {
+ device_printf(dev, "cannot create clknode\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ sc = clknode_get_softc(clk);
+ sc->reg = paddr;
+ sc->clkdev = device_get_parent(dev);
+
+ clknode_register(clkdom, clk);
+
+ if (clkdom_finit(clkdom) != 0) {
+ device_printf(dev, "cannot finalize clkdom initialization\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ return (0);
+
+fail:
+ return (error);
+}
+
+static device_method_t aw_gmacclk_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_gmacclk_probe),
+ DEVMETHOD(device_attach, aw_gmacclk_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_gmacclk_driver = {
+ "aw_gmacclk",
+ aw_gmacclk_methods,
+ 0
+};
+
+static devclass_t aw_gmacclk_devclass;
+
+EARLY_DRIVER_MODULE(aw_gmacclk, simplebus, aw_gmacclk_driver,
+ aw_gmacclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
Index: head/sys/arm/allwinner/clk/aw_hdmiclk.c
===================================================================
--- head/sys/arm/allwinner/clk/aw_hdmiclk.c
+++ head/sys/arm/allwinner/clk/aw_hdmiclk.c
@@ -0,0 +1,315 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner HDMI clock
+ */
+
+#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 <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
+
+#include <dev/extres/clk/clk_mux.h>
+#include <dev/extres/clk/clk_gate.h>
+
+#include "clkdev_if.h"
+
+#define SCLK_GATING (1 << 31)
+#define CLK_SRC_SEL (0x3 << 24)
+#define CLK_SRC_SEL_SHIFT 24
+#define CLK_SRC_SEL_MAX 0x3
+#define CLK_RATIO_N (0x3 << 16)
+#define CLK_RATIO_N_SHIFT 16
+#define CLK_RATIO_N_MAX 0x3
+#define CLK_RATIO_M (0x1f << 0)
+#define CLK_RATIO_M_SHIFT 0
+#define CLK_RATIO_M_MAX 0x1f
+
+#define CLK_IDX_PLL3_1X 0
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-hdmi-clk", 1 },
+ { NULL, 0 }
+};
+
+struct aw_hdmiclk_sc {
+ device_t clkdev;
+ bus_addr_t reg;
+};
+
+#define HDMICLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
+#define HDMICLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
+#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
+#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
+
+static int
+aw_hdmiclk_init(struct clknode *clk, device_t dev)
+{
+ struct aw_hdmiclk_sc *sc;
+ uint32_t val, index;
+
+ sc = clknode_get_softc(clk);
+
+ /* Select PLL3(1X) clock source */
+ index = CLK_IDX_PLL3_1X;
+
+ DEVICE_LOCK(sc);
+ HDMICLK_READ(sc, &val);
+ val &= ~CLK_SRC_SEL;
+ val |= (index << CLK_SRC_SEL_SHIFT);
+ HDMICLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ clknode_init_parent_idx(clk, index);
+ return (0);
+}
+
+static int
+aw_hdmiclk_set_mux(struct clknode *clk, int index)
+{
+ struct aw_hdmiclk_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ if (index < 0 || index > CLK_SRC_SEL_MAX)
+ return (ERANGE);
+
+ DEVICE_LOCK(sc);
+ HDMICLK_READ(sc, &val);
+ val &= ~CLK_SRC_SEL;
+ val |= (index << CLK_SRC_SEL_SHIFT);
+ HDMICLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+aw_hdmiclk_set_gate(struct clknode *clk, bool enable)
+{
+ struct aw_hdmiclk_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ HDMICLK_READ(sc, &val);
+ if (enable)
+ val |= SCLK_GATING;
+ else
+ val &= ~SCLK_GATING;
+ HDMICLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+aw_hdmiclk_recalc_freq(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_hdmiclk_sc *sc;
+ uint32_t val, m, n;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ HDMICLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ n = 1 << ((val & CLK_RATIO_N) >> CLK_RATIO_N_SHIFT);
+ m = ((val & CLK_RATIO_M) >> CLK_RATIO_M_SHIFT) + 1;
+
+ *freq = *freq / n / m;
+
+ return (0);
+}
+
+static int
+aw_hdmiclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct aw_hdmiclk_sc *sc;
+ uint32_t val, m, n, best_m, best_n;
+ uint64_t cur_freq;
+ int64_t best_diff, cur_diff;
+
+ sc = clknode_get_softc(clk);
+ best_n = best_m = 0;
+ best_diff = (int64_t)*fout;
+
+ for (n = 0; n <= CLK_RATIO_N_MAX; n++)
+ for (m = 0; m <= CLK_RATIO_M_MAX; m++) {
+ cur_freq = fin / (1 << n) / (m + 1);
+ cur_diff = (int64_t)*fout - cur_freq;
+ if (cur_diff >= 0 && cur_diff < best_diff) {
+ best_diff = cur_diff;
+ best_m = m;
+ best_n = n;
+ }
+ }
+
+ if (best_diff == (int64_t)*fout)
+ return (ERANGE);
+
+ DEVICE_LOCK(sc);
+ HDMICLK_READ(sc, &val);
+ val &= ~(CLK_RATIO_N | CLK_RATIO_M);
+ val |= (best_n << CLK_RATIO_N_SHIFT);
+ val |= (best_m << CLK_RATIO_M_SHIFT);
+ HDMICLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ *fout = fin / (1 << best_n) / (best_m + 1);
+ *stop = 1;
+
+ return (0);
+}
+
+static clknode_method_t aw_hdmiclk_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_hdmiclk_init),
+ CLKNODEMETHOD(clknode_set_gate, aw_hdmiclk_set_gate),
+ CLKNODEMETHOD(clknode_set_mux, aw_hdmiclk_set_mux),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_hdmiclk_recalc_freq),
+ CLKNODEMETHOD(clknode_set_freq, aw_hdmiclk_set_freq),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(aw_hdmiclk_clknode, aw_hdmiclk_clknode_class,
+ aw_hdmiclk_clknode_methods, sizeof(struct aw_hdmiclk_sc), clknode_class);
+
+static int
+aw_hdmiclk_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner HDMI Clock");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_hdmiclk_attach(device_t dev)
+{
+ struct clknode_init_def def;
+ struct aw_hdmiclk_sc *sc;
+ struct clkdom *clkdom;
+ struct clknode *clk;
+ clk_t clk_parent;
+ bus_addr_t paddr;
+ bus_size_t psize;
+ phandle_t node;
+ int error;
+
+ node = ofw_bus_get_node(dev);
+
+ if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
+ device_printf(dev, "cannot parse 'reg' property\n");
+ return (ENXIO);
+ }
+
+ clkdom = clkdom_create(dev);
+
+ error = clk_get_by_ofw_index(dev, 0, &clk_parent);
+ if (error != 0) {
+ device_printf(dev, "cannot parse clock parent\n");
+ return (ENXIO);
+ }
+
+ memset(&def, 0, sizeof(def));
+ error = clk_parse_ofw_clk_name(dev, node, &def.name);
+ if (error != 0) {
+ device_printf(dev, "cannot parse clock name\n");
+ error = ENXIO;
+ goto fail;
+ }
+ def.id = 1;
+ def.parent_names = malloc(sizeof(char *), M_OFWPROP, M_WAITOK);
+ def.parent_names[0] = clk_get_name(clk_parent);
+ def.parent_cnt = 1;
+
+ clk = clknode_create(clkdom, &aw_hdmiclk_clknode_class, &def);
+ if (clk == NULL) {
+ device_printf(dev, "cannot create clknode\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ sc = clknode_get_softc(clk);
+ sc->reg = paddr;
+ sc->clkdev = device_get_parent(dev);
+
+ clknode_register(clkdom, clk);
+
+ if (clkdom_finit(clkdom) != 0) {
+ device_printf(dev, "cannot finalize clkdom initialization\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ return (0);
+
+fail:
+ return (error);
+}
+
+static device_method_t aw_hdmiclk_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_hdmiclk_probe),
+ DEVMETHOD(device_attach, aw_hdmiclk_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_hdmiclk_driver = {
+ "aw_hdmiclk",
+ aw_hdmiclk_methods,
+ 0
+};
+
+static devclass_t aw_hdmiclk_devclass;
+
+EARLY_DRIVER_MODULE(aw_hdmiclk, simplebus, aw_hdmiclk_driver,
+ aw_hdmiclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
Index: head/sys/arm/allwinner/clk/aw_lcdclk.c
===================================================================
--- head/sys/arm/allwinner/clk/aw_lcdclk.c
+++ head/sys/arm/allwinner/clk/aw_lcdclk.c
@@ -0,0 +1,560 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner LCD clocks
+ */
+
+#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 <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+
+#include "clkdev_if.h"
+#include "hwreset_if.h"
+
+/* CH0 */
+#define CH0_SCLK_GATING (1 << 31)
+#define CH0_LCD_RST (1 << 30)
+#define CH0_CLK_SRC_SEL (0x3 << 24)
+#define CH0_CLK_SRC_SEL_SHIFT 24
+#define CH0_CLK_SRC_SEL_PLL3_1X 0
+#define CH0_CLK_SRC_SEL_PLL7_1X 1
+#define CH0_CLK_SRC_SEL_PLL3_2X 2
+#define CH0_CLK_SRC_SEL_PLL6 3
+
+/* CH1 */
+#define CH1_SCLK2_GATING (1 << 31)
+#define CH1_SCLK2_SEL (0x3 << 24)
+#define CH1_SCLK2_SEL_SHIFT 24
+#define CH1_SCLK2_SEL_PLL3_1X 0
+#define CH1_SCLK2_SEL_PLL7_1X 1
+#define CH1_SCLK2_SEL_PLL3_2X 2
+#define CH1_SCLK2_SEL_PLL7_2X 3
+#define CH1_SCLK1_GATING (1 << 15)
+#define CH1_SCLK1_SEL (0x1 << 11)
+#define CH1_SCLK1_SEL_SHIFT 11
+#define CH1_SCLK1_SEL_SCLK2 0
+#define CH1_SCLK1_SEL_SCLK2_DIV2 1
+#define CH1_CLK_DIV_RATIO_M (0x1f << 0)
+#define CH1_CLK_DIV_RATIO_M_SHIFT 0
+
+#define TCON_PLLREF 3000000ULL
+#define TCON_PLL_M_MIN 1
+#define TCON_PLL_M_MAX 15
+#define TCON_PLL_N_MIN 9
+#define TCON_PLL_N_MAX 127
+
+#define CLK_IDX_CH1_SCLK1 0
+#define CLK_IDX_CH1_SCLK2 1
+
+#define CLK_IDX_
+
+enum aw_lcdclk_type {
+ AW_LCD_CH0 = 1,
+ AW_LCD_CH1,
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-lcd-ch0-clk", AW_LCD_CH0 },
+ { "allwinner,sun4i-a10-lcd-ch1-clk", AW_LCD_CH1 },
+ { NULL, 0 }
+};
+
+struct aw_lcdclk_softc {
+ enum aw_lcdclk_type type;
+ device_t clkdev;
+ bus_addr_t reg;
+ int id;
+};
+
+#define LCDCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
+#define LCDCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
+#define LCDCLK_MODIFY(sc, clr, set) \
+ CLKDEV_MODIFY_4((sc)->clkdev, (sc)->reg, (clr), (set))
+#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
+#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
+
+static int
+aw_lcdclk_hwreset_assert(device_t dev, intptr_t id, bool value)
+{
+ struct aw_lcdclk_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if (sc->type != AW_LCD_CH0)
+ return (ENXIO);
+
+ DEVICE_LOCK(sc);
+ error = LCDCLK_MODIFY(sc, CH0_LCD_RST, value ? 0 : CH0_LCD_RST);
+ DEVICE_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+aw_lcdclk_hwreset_is_asserted(device_t dev, intptr_t id, bool *value)
+{
+ struct aw_lcdclk_softc *sc;
+ uint32_t val;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if (sc->type != AW_LCD_CH0)
+ return (ENXIO);
+
+ DEVICE_LOCK(sc);
+ error = LCDCLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ if (error)
+ return (error);
+
+ *value = (val & CH0_LCD_RST) != 0 ? false : true;
+
+ return (0);
+}
+
+static int
+aw_lcdclk_init(struct clknode *clk, device_t dev)
+{
+ struct aw_lcdclk_softc *sc;
+ uint32_t val, index;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ LCDCLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ switch (sc->type) {
+ case AW_LCD_CH0:
+ index = (val & CH0_CLK_SRC_SEL) >> CH0_CLK_SRC_SEL_SHIFT;
+ break;
+ case AW_LCD_CH1:
+ switch (sc->id) {
+ case CLK_IDX_CH1_SCLK1:
+ index = 0;
+ break;
+ case CLK_IDX_CH1_SCLK2:
+ index = (val & CH1_SCLK2_SEL_SHIFT) >>
+ CH1_SCLK2_SEL_SHIFT;
+ break;
+ default:
+ return (ENXIO);
+ }
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ clknode_init_parent_idx(clk, index);
+ return (0);
+}
+
+static int
+aw_lcdclk_set_mux(struct clknode *clk, int index)
+{
+ struct aw_lcdclk_softc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ switch (sc->type) {
+ case AW_LCD_CH0:
+ DEVICE_LOCK(sc);
+ LCDCLK_READ(sc, &val);
+ val &= ~CH0_CLK_SRC_SEL;
+ val |= (index << CH0_CLK_SRC_SEL_SHIFT);
+ LCDCLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+ break;
+ case AW_LCD_CH1:
+ switch (sc->id) {
+ case CLK_IDX_CH1_SCLK2:
+ DEVICE_LOCK(sc);
+ LCDCLK_READ(sc, &val);
+ val &= ~CH1_SCLK2_SEL;
+ val |= (index << CH1_SCLK2_SEL_SHIFT);
+ LCDCLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+ break;
+ default:
+ return (ENXIO);
+ }
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+aw_lcdclk_set_gate(struct clknode *clk, bool enable)
+{
+ struct aw_lcdclk_softc *sc;
+ uint32_t val, mask;
+
+ sc = clknode_get_softc(clk);
+
+ switch (sc->type) {
+ case AW_LCD_CH0:
+ mask = CH0_SCLK_GATING;
+ break;
+ case AW_LCD_CH1:
+ mask = (sc->id == CLK_IDX_CH1_SCLK1) ? CH1_SCLK1_GATING :
+ CH1_SCLK2_GATING;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ DEVICE_LOCK(sc);
+ LCDCLK_READ(sc, &val);
+ if (enable)
+ val |= mask;
+ else
+ val &= ~mask;
+ LCDCLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+aw_lcdclk_recalc_freq(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_lcdclk_softc *sc;
+ uint32_t val, m;
+
+ sc = clknode_get_softc(clk);
+
+ if (sc->type != AW_LCD_CH1)
+ return (0);
+
+ DEVICE_LOCK(sc);
+ LCDCLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ m = ((val & CH1_CLK_DIV_RATIO_M) >> CH1_CLK_DIV_RATIO_M_SHIFT) + 1;
+ *freq = *freq / m;
+
+ if (sc->id == CLK_IDX_CH1_SCLK1) {
+ if ((val & CH1_SCLK1_SEL) == CH1_SCLK1_SEL_SCLK2_DIV2)
+ *freq /= 2;
+ }
+
+ return (0);
+}
+
+static void
+calc_tcon_pll(uint64_t fin, uint64_t fout, uint32_t *pm, uint32_t *pn)
+{
+ int64_t diff, fcur, best;
+ int m, n;
+
+ best = fout;
+ for (m = TCON_PLL_M_MIN; m <= TCON_PLL_M_MAX; m++) {
+ for (n = TCON_PLL_N_MIN; n <= TCON_PLL_N_MAX; n++) {
+ fcur = (n * fin) / m;
+ diff = (int64_t)fout - fcur;
+ if (diff > 0 && diff < best) {
+ best = diff;
+ *pm = m;
+ *pn = n;
+ }
+ }
+ }
+}
+
+static int
+aw_lcdclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct aw_lcdclk_softc *sc;
+ uint32_t val, m, m2, n, n2, src_sel;
+ uint64_t fsingle, fdouble;
+ int error;
+ bool dbl;
+
+ sc = clknode_get_softc(clk);
+
+ switch (sc->type) {
+ case AW_LCD_CH0:
+ *stop = 0;
+ break;
+ case AW_LCD_CH1:
+ if (sc->id != CLK_IDX_CH1_SCLK2)
+ return (ENXIO);
+
+ m = n = m2 = n2 = 0;
+ dbl = false;
+
+ /* Find the frequency closes to the target dot clock, using
+ * both 1X and 2X PLL inputs as possible candidates.
+ */
+ calc_tcon_pll(TCON_PLLREF, *fout, &m, &n);
+ calc_tcon_pll(TCON_PLLREF * 2, *fout, &m2, &n2);
+
+ fsingle = m ? (n * TCON_PLLREF) / m : 0;
+ fdouble = m2 ? (n2 * TCON_PLLREF * 2) / m2 : 0;
+
+ if (fdouble > fsingle) {
+ dbl = true;
+ m = m2;
+ n = n2;
+ }
+
+ src_sel = dbl ? CH0_CLK_SRC_SEL_PLL3_2X :
+ CH0_CLK_SRC_SEL_PLL3_1X;
+
+ /* Switch parent clock if necessary */
+ if (src_sel != clknode_get_parent_idx(clk)) {
+ error = clknode_set_parent_by_idx(clk, src_sel);
+ if (error != 0)
+ return (error);
+ }
+
+ /* Set desired parent frequency */
+ fin = n * TCON_PLLREF;
+
+ error = clknode_set_freq(clknode_get_parent(clk), fin, 0, 0);
+ if (error != 0)
+ return (error);
+
+ error = clknode_enable(clknode_get_parent(clk));
+ if (error != 0)
+ return (error);
+
+ /* Fetch new input frequency */
+ error = clknode_get_freq(clknode_get_parent(clk), &fin);
+ if (error != 0)
+ return (error);
+
+ /* Set LCD divisor */
+ DEVICE_LOCK(sc);
+ LCDCLK_READ(sc, &val);
+ val &= ~CH1_CLK_DIV_RATIO_M;
+ val |= ((m - 1) << CH1_CLK_DIV_RATIO_M_SHIFT);
+ LCDCLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ *fout = fin / m;
+ *stop = 1;
+
+ break;
+ }
+
+ return (0);
+}
+
+static clknode_method_t aw_lcdclk_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_lcdclk_init),
+ CLKNODEMETHOD(clknode_set_gate, aw_lcdclk_set_gate),
+ CLKNODEMETHOD(clknode_set_mux, aw_lcdclk_set_mux),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_lcdclk_recalc_freq),
+ CLKNODEMETHOD(clknode_set_freq, aw_lcdclk_set_freq),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(aw_lcdclk_clknode, aw_lcdclk_clknode_class,
+ aw_lcdclk_clknode_methods, sizeof(struct aw_lcdclk_softc), clknode_class);
+
+static int
+aw_lcdclk_create(device_t dev, struct clkdom *clkdom,
+ const char **parent_names, int parent_cnt, const char *name, int index)
+{
+ struct aw_lcdclk_softc *sc, *clk_sc;
+ struct clknode_init_def def;
+ struct clknode *clk;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(dev);
+
+ memset(&def, 0, sizeof(def));
+ def.id = index;
+ def.name = name;
+ def.parent_names = parent_names;
+ def.parent_cnt = parent_cnt;
+
+ clk = clknode_create(clkdom, &aw_lcdclk_clknode_class, &def);
+ if (clk == NULL) {
+ device_printf(dev, "cannot create clknode\n");
+ return (ENXIO);
+ }
+
+ clk_sc = clknode_get_softc(clk);
+ clk_sc->type = sc->type;
+ clk_sc->reg = sc->reg;
+ clk_sc->clkdev = sc->clkdev;
+ clk_sc->id = index;
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
+
+static int
+aw_lcdclk_probe(device_t dev)
+{
+ enum aw_lcdclk_type type;
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ switch (type) {
+ case AW_LCD_CH0:
+ device_set_desc(dev, "Allwinner LCD CH0 Clock");
+ break;
+ case AW_LCD_CH1:
+ device_set_desc(dev, "Allwinner LCD CH1 Clock");
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_lcdclk_attach(device_t dev)
+{
+ struct aw_lcdclk_softc *sc;
+ struct clkdom *clkdom;
+ clk_t clk_parent;
+ bus_size_t psize;
+ phandle_t node;
+ uint32_t *indices;
+ const char **parent_names;
+ const char **names;
+ int error, ncells, nout, i;
+
+ sc = device_get_softc(dev);
+ sc->clkdev = device_get_parent(dev);
+ sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ node = ofw_bus_get_node(dev);
+
+ if (ofw_reg_to_paddr(node, 0, &sc->reg, &psize, NULL) != 0) {
+ device_printf(dev, "cannot parse 'reg' property\n");
+ return (ENXIO);
+ }
+
+ error = ofw_bus_parse_xref_list_get_length(node, "clocks",
+ "#clock-cells", &ncells);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock count\n");
+ return (error);
+ }
+
+ parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK);
+ for (i = 0; i < ncells; i++) {
+ error = clk_get_by_ofw_index(dev, i, &clk_parent);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock %d\n", i);
+ goto fail;
+ }
+ parent_names[i] = clk_get_name(clk_parent);
+ clk_release(clk_parent);
+ }
+
+ nout = clk_parse_ofw_out_names(dev, node, &names, &indices);
+ if (nout == 0) {
+ device_printf(dev, "no clock outputs found\n");
+ return (error);
+ }
+
+ clkdom = clkdom_create(dev);
+
+ for (i = 0; i < nout; i++) {
+ error = aw_lcdclk_create(dev, clkdom, parent_names, ncells,
+ names[i], nout == 1 ? 1 : i);
+ if (error)
+ goto fail;
+ }
+
+ if (clkdom_finit(clkdom) != 0) {
+ device_printf(dev, "cannot finalize clkdom initialization\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ if (sc->type == AW_LCD_CH0)
+ hwreset_register_ofw_provider(dev);
+
+ free(parent_names, M_OFWPROP);
+ return (0);
+
+fail:
+ free(parent_names, M_OFWPROP);
+ return (error);
+}
+
+static device_method_t aw_lcdclk_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_lcdclk_probe),
+ DEVMETHOD(device_attach, aw_lcdclk_attach),
+
+ /* Reset interface */
+ DEVMETHOD(hwreset_assert, aw_lcdclk_hwreset_assert),
+ DEVMETHOD(hwreset_is_asserted, aw_lcdclk_hwreset_is_asserted),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_lcdclk_driver = {
+ "aw_lcdclk",
+ aw_lcdclk_methods,
+ sizeof(struct aw_lcdclk_softc)
+};
+
+static devclass_t aw_lcdclk_devclass;
+
+EARLY_DRIVER_MODULE(aw_lcdclk, simplebus, aw_lcdclk_driver,
+ aw_lcdclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
Index: head/sys/arm/allwinner/clk/aw_mmcclk.c
===================================================================
--- head/sys/arm/allwinner/clk/aw_mmcclk.c
+++ head/sys/arm/allwinner/clk/aw_mmcclk.c
@@ -0,0 +1,351 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner MMC clocks
+ */
+
+#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 <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
+
+#include <dev/extres/clk/clk_mux.h>
+#include <dev/extres/clk/clk_gate.h>
+
+#include "clkdev_if.h"
+
+#define SCLK_GATING (1 << 31)
+#define CLK_SRC_SEL (0x3 << 24)
+#define CLK_SRC_SEL_SHIFT 24
+#define CLK_SRC_SEL_MAX 0x3
+#define CLK_SRC_SEL_OSC24M 0
+#define CLK_SRC_SEL_PLL6 1
+#define CLK_PHASE_CTR (0x7 << 20)
+#define CLK_PHASE_CTR_SHIFT 20
+#define CLK_RATIO_N (0x3 << 16)
+#define CLK_RATIO_N_SHIFT 16
+#define CLK_RATIO_N_MAX 0x3
+#define OUTPUT_CLK_PHASE_CTR (0x7 << 8)
+#define OUTPUT_CLK_PHASE_CTR_SHIFT 8
+#define CLK_RATIO_M (0xf << 0)
+#define CLK_RATIO_M_SHIFT 0
+#define CLK_RATIO_M_MAX 0xf
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-mmc-clk", 1 },
+ { NULL, 0 }
+};
+
+struct aw_mmcclk_sc {
+ device_t clkdev;
+ bus_addr_t reg;
+};
+
+#define MODCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
+#define MODCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
+#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
+#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
+
+static int
+aw_mmcclk_init(struct clknode *clk, device_t dev)
+{
+ struct aw_mmcclk_sc *sc;
+ uint32_t val, index;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ MODCLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ index = (val & CLK_SRC_SEL) >> CLK_SRC_SEL_SHIFT;
+
+ clknode_init_parent_idx(clk, index);
+ return (0);
+}
+
+static int
+aw_mmcclk_set_mux(struct clknode *clk, int index)
+{
+ struct aw_mmcclk_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ if (index < 0 || index > CLK_SRC_SEL_MAX)
+ return (ERANGE);
+
+ DEVICE_LOCK(sc);
+ MODCLK_READ(sc, &val);
+ val &= ~CLK_SRC_SEL;
+ val |= (index << CLK_SRC_SEL_SHIFT);
+ MODCLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+aw_mmcclk_set_gate(struct clknode *clk, bool enable)
+{
+ struct aw_mmcclk_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ MODCLK_READ(sc, &val);
+ if (enable)
+ val |= SCLK_GATING;
+ else
+ val &= ~SCLK_GATING;
+ MODCLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+aw_mmcclk_recalc_freq(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_mmcclk_sc *sc;
+ uint32_t val, m, n;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ MODCLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ n = 1 << ((val & CLK_RATIO_N) >> CLK_RATIO_N_SHIFT);
+ m = ((val & CLK_RATIO_M) >> CLK_RATIO_M_SHIFT) + 1;
+
+ *freq = *freq / n / m;
+
+ return (0);
+}
+
+static int
+aw_mmcclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct aw_mmcclk_sc *sc;
+ uint32_t val, m, n, phase, ophase;
+ int parent_idx, error;
+
+ sc = clknode_get_softc(clk);
+
+ /* XXX
+ * The ophase/phase values should be set by the MMC driver, but
+ * there is currently no way to do this with the clk API
+ */
+ if (*fout <= 400000) {
+ parent_idx = CLK_SRC_SEL_OSC24M;
+ ophase = 0;
+ phase = 0;
+ n = 2;
+ } else if (*fout <= 25000000) {
+ parent_idx = CLK_SRC_SEL_PLL6;
+ ophase = 0;
+ phase = 5;
+ n = 2;
+ } else if (*fout <= 50000000) {
+ parent_idx = CLK_SRC_SEL_PLL6;
+ ophase = 3;
+ phase = 5;
+ n = 0;
+ } else
+ return (ERANGE);
+
+ /* Switch parent clock, if necessary */
+ if (parent_idx != clknode_get_parent_idx(clk)) {
+ error = clknode_set_parent_by_idx(clk, parent_idx);
+ if (error != 0)
+ return (error);
+
+ /* Fetch new input frequency */
+ error = clknode_get_freq(clknode_get_parent(clk), &fin);
+ if (error != 0)
+ return (error);
+ }
+
+ m = ((fin / (1 << n)) / *fout) - 1;
+
+ DEVICE_LOCK(sc);
+ MODCLK_READ(sc, &val);
+ val &= ~(CLK_RATIO_N | CLK_RATIO_M | CLK_PHASE_CTR |
+ OUTPUT_CLK_PHASE_CTR);
+ val |= (n << CLK_RATIO_N_SHIFT);
+ val |= (m << CLK_RATIO_M_SHIFT);
+ val |= (phase << CLK_PHASE_CTR_SHIFT);
+ val |= (ophase << OUTPUT_CLK_PHASE_CTR_SHIFT);
+ MODCLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ *fout = fin / (1 << n) / (m + 1);
+ *stop = 1;
+
+ return (0);
+}
+
+static clknode_method_t aw_mmcclk_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_mmcclk_init),
+ CLKNODEMETHOD(clknode_set_gate, aw_mmcclk_set_gate),
+ CLKNODEMETHOD(clknode_set_mux, aw_mmcclk_set_mux),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_mmcclk_recalc_freq),
+ CLKNODEMETHOD(clknode_set_freq, aw_mmcclk_set_freq),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(aw_mmcclk_clknode, aw_mmcclk_clknode_class,
+ aw_mmcclk_clknode_methods, sizeof(struct aw_mmcclk_sc), clknode_class);
+
+static int
+aw_mmcclk_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner MMC Clock");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_mmcclk_attach(device_t dev)
+{
+ struct clknode_init_def def;
+ struct aw_mmcclk_sc *sc;
+ struct clkdom *clkdom;
+ struct clknode *clk;
+ const char **names;
+ uint32_t *indices;
+ clk_t clk_parent;
+ bus_addr_t paddr;
+ bus_size_t psize;
+ phandle_t node;
+ int error, nout, ncells, i;
+
+ node = ofw_bus_get_node(dev);
+
+ if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
+ device_printf(dev, "cannot parse 'reg' property\n");
+ return (ENXIO);
+ }
+
+ error = ofw_bus_parse_xref_list_get_length(node, "clocks",
+ "#clock-cells", &ncells);
+ if (error != 0 || ncells == 0) {
+ device_printf(dev, "couldn't find parent clocks\n");
+ return (ENXIO);
+ }
+
+ clkdom = clkdom_create(dev);
+
+ nout = clk_parse_ofw_out_names(dev, node, &names, &indices);
+ if (nout == 0) {
+ device_printf(dev, "no output clocks found\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ memset(&def, 0, sizeof(def));
+ def.name = names[0];
+ def.id = 0;
+ def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK);
+ for (i = 0; i < ncells; i++) {
+ error = clk_get_by_ofw_index(dev, i, &clk_parent);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock %d\n", i);
+ goto fail;
+ }
+ def.parent_names[i] = clk_get_name(clk_parent);
+ clk_release(clk_parent);
+ }
+ def.parent_cnt = ncells;
+ def.flags = CLK_NODE_GLITCH_FREE;
+
+ clk = clknode_create(clkdom, &aw_mmcclk_clknode_class, &def);
+ if (clk == NULL) {
+ device_printf(dev, "cannot create clknode\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ sc = clknode_get_softc(clk);
+ sc->reg = paddr;
+ sc->clkdev = device_get_parent(dev);
+
+ clknode_register(clkdom, clk);
+
+ if (clkdom_finit(clkdom) != 0) {
+ device_printf(dev, "cannot finalize clkdom initialization\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ return (0);
+
+fail:
+ return (error);
+}
+
+static device_method_t aw_mmcclk_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_mmcclk_probe),
+ DEVMETHOD(device_attach, aw_mmcclk_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_mmcclk_driver = {
+ "aw_mmcclk",
+ aw_mmcclk_methods,
+ 0
+};
+
+static devclass_t aw_mmcclk_devclass;
+
+EARLY_DRIVER_MODULE(aw_mmcclk, simplebus, aw_mmcclk_driver,
+ aw_mmcclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
Index: head/sys/arm/allwinner/clk/aw_modclk.c
===================================================================
--- head/sys/arm/allwinner/clk/aw_modclk.c
+++ head/sys/arm/allwinner/clk/aw_modclk.c
@@ -0,0 +1,318 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner module clocks
+ */
+
+#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 <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
+
+#include <dev/extres/clk/clk_mux.h>
+#include <dev/extres/clk/clk_gate.h>
+
+#include "clkdev_if.h"
+
+#define SCLK_GATING (1 << 31)
+#define CLK_SRC_SEL (0x3 << 24)
+#define CLK_SRC_SEL_SHIFT 24
+#define CLK_SRC_SEL_MAX 0x3
+#define CLK_RATIO_N (0x3 << 16)
+#define CLK_RATIO_N_SHIFT 16
+#define CLK_RATIO_N_MAX 0x3
+#define CLK_RATIO_M (0x1f << 0)
+#define CLK_RATIO_M_SHIFT 0
+#define CLK_RATIO_M_MAX 0x1f
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-mod0-clk", 1 },
+ { NULL, 0 }
+};
+
+struct aw_modclk_sc {
+ device_t clkdev;
+ bus_addr_t reg;
+};
+
+#define MODCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
+#define MODCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
+#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
+#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
+
+static int
+aw_modclk_init(struct clknode *clk, device_t dev)
+{
+ struct aw_modclk_sc *sc;
+ uint32_t val, index;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ MODCLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ index = (val & CLK_SRC_SEL) >> CLK_SRC_SEL_SHIFT;
+
+ clknode_init_parent_idx(clk, index);
+ return (0);
+}
+
+static int
+aw_modclk_set_mux(struct clknode *clk, int index)
+{
+ struct aw_modclk_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ if (index < 0 || index > CLK_SRC_SEL_MAX)
+ return (ERANGE);
+
+ DEVICE_LOCK(sc);
+ MODCLK_READ(sc, &val);
+ val &= ~CLK_SRC_SEL;
+ val |= (index << CLK_SRC_SEL_SHIFT);
+ MODCLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+aw_modclk_set_gate(struct clknode *clk, bool enable)
+{
+ struct aw_modclk_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ MODCLK_READ(sc, &val);
+ if (enable)
+ val |= SCLK_GATING;
+ else
+ val &= ~SCLK_GATING;
+ MODCLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+aw_modclk_recalc_freq(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_modclk_sc *sc;
+ uint32_t val, m, n;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ MODCLK_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ n = 1 << ((val & CLK_RATIO_N) >> CLK_RATIO_N_SHIFT);
+ m = ((val & CLK_RATIO_M) >> CLK_RATIO_M_SHIFT) + 1;
+
+ *freq = *freq / n / m;
+
+ return (0);
+}
+
+static int
+aw_modclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct aw_modclk_sc *sc;
+ uint32_t val, m, n, best_m, best_n;
+ uint64_t cur_freq;
+ int64_t best_diff, cur_diff;
+
+ sc = clknode_get_softc(clk);
+ best_n = best_m = 0;
+ best_diff = (int64_t)*fout;
+
+ for (n = 0; n <= CLK_RATIO_N_MAX; n++)
+ for (m = 0; m <= CLK_RATIO_M_MAX; m++) {
+ cur_freq = fin / (1 << n) / (m + 1);
+ cur_diff = (int64_t)*fout - cur_freq;
+ if (cur_diff >= 0 && cur_diff < best_diff) {
+ best_diff = cur_diff;
+ best_m = m;
+ best_n = n;
+ }
+ }
+
+ if (best_diff == (int64_t)*fout)
+ return (ERANGE);
+
+ DEVICE_LOCK(sc);
+ MODCLK_READ(sc, &val);
+ val &= ~(CLK_RATIO_N | CLK_RATIO_M);
+ val |= (best_n << CLK_RATIO_N_SHIFT);
+ val |= (best_m << CLK_RATIO_M_SHIFT);
+ MODCLK_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ *fout = fin / (1 << best_n) / (best_m + 1);
+ *stop = 1;
+
+ return (0);
+}
+
+static clknode_method_t aw_modclk_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_modclk_init),
+ CLKNODEMETHOD(clknode_set_gate, aw_modclk_set_gate),
+ CLKNODEMETHOD(clknode_set_mux, aw_modclk_set_mux),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_modclk_recalc_freq),
+ CLKNODEMETHOD(clknode_set_freq, aw_modclk_set_freq),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(aw_modclk_clknode, aw_modclk_clknode_class,
+ aw_modclk_clknode_methods, sizeof(struct aw_modclk_sc), clknode_class);
+
+static int
+aw_modclk_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner Module Clock");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_modclk_attach(device_t dev)
+{
+ struct clknode_init_def def;
+ struct aw_modclk_sc *sc;
+ struct clkdom *clkdom;
+ struct clknode *clk;
+ clk_t clk_parent;
+ bus_addr_t paddr;
+ bus_size_t psize;
+ phandle_t node;
+ int error, ncells, i;
+
+ node = ofw_bus_get_node(dev);
+
+ if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
+ device_printf(dev, "cannot parse 'reg' property\n");
+ return (ENXIO);
+ }
+
+ error = ofw_bus_parse_xref_list_get_length(node, "clocks",
+ "#clock-cells", &ncells);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock count\n");
+ return (error);
+ }
+
+ clkdom = clkdom_create(dev);
+
+ memset(&def, 0, sizeof(def));
+ error = clk_parse_ofw_clk_name(dev, node, &def.name);
+ if (error != 0) {
+ device_printf(dev, "cannot parse clock name\n");
+ error = ENXIO;
+ goto fail;
+ }
+ def.id = 1;
+ def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK);
+ for (i = 0; i < ncells; i++) {
+ error = clk_get_by_ofw_index(dev, i, &clk_parent);
+ if (error != 0) {
+ device_printf(dev, "cannot get clock %d\n", i);
+ goto fail;
+ }
+ def.parent_names[i] = clk_get_name(clk_parent);
+ clk_release(clk_parent);
+ }
+ def.parent_cnt = ncells;
+
+ clk = clknode_create(clkdom, &aw_modclk_clknode_class, &def);
+ if (clk == NULL) {
+ device_printf(dev, "cannot create clknode\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ sc = clknode_get_softc(clk);
+ sc->reg = paddr;
+ sc->clkdev = device_get_parent(dev);
+
+ clknode_register(clkdom, clk);
+
+ if (clkdom_finit(clkdom) != 0) {
+ device_printf(dev, "cannot finalize clkdom initialization\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ return (0);
+
+fail:
+ return (error);
+}
+
+static device_method_t aw_modclk_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_modclk_probe),
+ DEVMETHOD(device_attach, aw_modclk_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_modclk_driver = {
+ "aw_modclk",
+ aw_modclk_methods,
+ 0
+};
+
+static devclass_t aw_modclk_devclass;
+
+EARLY_DRIVER_MODULE(aw_modclk, simplebus, aw_modclk_driver,
+ aw_modclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
Index: head/sys/arm/allwinner/clk/aw_oscclk.c
===================================================================
--- head/sys/arm/allwinner/clk/aw_oscclk.c
+++ head/sys/arm/allwinner/clk/aw_oscclk.c
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner oscillator clock
+ */
+
+#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 <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk_fixed.h>
+
+static int
+aw_oscclk_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-osc-clk"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner Oscillator Clock");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_oscclk_attach(device_t dev)
+{
+ struct clk_fixed_def def;
+ struct clkdom *clkdom;
+ phandle_t node;
+ uint32_t freq;
+ int error;
+
+ node = ofw_bus_get_node(dev);
+
+ if (OF_getencprop(node, "clock-frequency", &freq, sizeof(freq)) <= 0) {
+ device_printf(dev, "missing clock-frequency property\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ clkdom = clkdom_create(dev);
+
+ memset(&def, 0, sizeof(def));
+ def.clkdef.id = 1;
+ def.freq = freq;
+ error = clk_parse_ofw_clk_name(dev, node, &def.clkdef.name);
+ if (error != 0) {
+ device_printf(dev, "cannot parse clock name\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ error = clknode_fixed_register(clkdom, &def);
+ if (error != 0) {
+ device_printf(dev, "cannot register fixed clock\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (clkdom_finit(clkdom) != 0) {
+ device_printf(dev, "cannot finalize clkdom initialization\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ free(__DECONST(char *, def.clkdef.name), M_OFWPROP);
+
+ return (0);
+
+fail:
+ free(__DECONST(char *, def.clkdef.name), M_OFWPROP);
+ return (error);
+}
+
+static device_method_t aw_oscclk_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_oscclk_probe),
+ DEVMETHOD(device_attach, aw_oscclk_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_oscclk_driver = {
+ "aw_oscclk",
+ aw_oscclk_methods,
+ 0,
+};
+
+static devclass_t aw_oscclk_devclass;
+
+EARLY_DRIVER_MODULE(aw_oscclk, simplebus, aw_oscclk_driver,
+ aw_oscclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
Index: head/sys/arm/allwinner/clk/aw_pll.c
===================================================================
--- head/sys/arm/allwinner/clk/aw_pll.c
+++ head/sys/arm/allwinner/clk/aw_pll.c
@@ -0,0 +1,757 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner PLL clock
+ */
+
+#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 <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <dt-bindings/clock/sun4i-a10-pll2.h>
+
+#include "clkdev_if.h"
+
+#define AW_PLL_ENABLE (1 << 31)
+
+#define A10_PLL1_OUT_EXT_DIVP (0x3 << 16)
+#define A10_PLL1_OUT_EXT_DIVP_SHIFT 16
+#define A10_PLL1_FACTOR_N (0x1f << 8)
+#define A10_PLL1_FACTOR_N_SHIFT 8
+#define A10_PLL1_FACTOR_K (0x3 << 4)
+#define A10_PLL1_FACTOR_K_SHIFT 4
+#define A10_PLL1_FACTOR_M (0x3 << 0)
+#define A10_PLL1_FACTOR_M_SHIFT 0
+
+#define A10_PLL2_POST_DIV (0xf << 26)
+#define A10_PLL2_POST_DIV_SHIFT 26
+#define A10_PLL2_FACTOR_N (0x7f << 8)
+#define A10_PLL2_FACTOR_N_SHIFT 8
+#define A10_PLL2_PRE_DIV (0x1f << 0)
+#define A10_PLL2_PRE_DIV_SHIFT 0
+
+#define A10_PLL3_MODE_SEL (0x1 << 15)
+#define A10_PLL3_MODE_SEL_FRACT (0 << 15)
+#define A10_PLL3_MODE_SEL_INT (1 << 15)
+#define A10_PLL3_FUNC_SET (0x1 << 14)
+#define A10_PLL3_FUNC_SET_270MHZ (0 << 14)
+#define A10_PLL3_FUNC_SET_297MHZ (1 << 14)
+#define A10_PLL3_FACTOR_M (0x7f << 0)
+#define A10_PLL3_FACTOR_M_SHIFT 0
+#define A10_PLL3_REF_FREQ 3000000
+
+#define A10_PLL5_OUT_EXT_DIVP (0x3 << 16)
+#define A10_PLL5_OUT_EXT_DIVP_SHIFT 16
+#define A10_PLL5_FACTOR_N (0x1f << 8)
+#define A10_PLL5_FACTOR_N_SHIFT 8
+#define A10_PLL5_FACTOR_K (0x3 << 4)
+#define A10_PLL5_FACTOR_K_SHIFT 4
+#define A10_PLL5_FACTOR_M1 (0x3 << 2)
+#define A10_PLL5_FACTOR_M1_SHIFT 2
+#define A10_PLL5_FACTOR_M (0x3 << 0)
+#define A10_PLL5_FACTOR_M_SHIFT 0
+
+#define A10_PLL6_BYPASS_EN (1 << 30)
+#define A10_PLL6_SATA_CLK_EN (1 << 14)
+#define A10_PLL6_FACTOR_N (0x1f << 8)
+#define A10_PLL6_FACTOR_N_SHIFT 8
+#define A10_PLL6_FACTOR_K (0x3 << 4)
+#define A10_PLL6_FACTOR_K_SHIFT 4
+#define A10_PLL6_FACTOR_M (0x3 << 0)
+#define A10_PLL6_FACTOR_M_SHIFT 0
+
+#define A10_PLL2_POST_DIV (0xf << 26)
+
+#define A31_PLL1_LOCK (1 << 28)
+#define A31_PLL1_CPU_SIGMA_DELTA_EN (1 << 24)
+#define A31_PLL1_FACTOR_N (0x1f << 8)
+#define A31_PLL1_FACTOR_N_SHIFT 8
+#define A31_PLL1_FACTOR_K (0x3 << 4)
+#define A31_PLL1_FACTOR_K_SHIFT 4
+#define A31_PLL1_FACTOR_M (0x3 << 0)
+#define A31_PLL1_FACTOR_M_SHIFT 0
+
+#define A31_PLL6_LOCK (1 << 28)
+#define A31_PLL6_BYPASS_EN (1 << 25)
+#define A31_PLL6_CLK_OUT_EN (1 << 24)
+#define A31_PLL6_24M_OUT_EN (1 << 18)
+#define A31_PLL6_24M_POST_DIV (0x3 << 16)
+#define A31_PLL6_24M_POST_DIV_SHIFT 16
+#define A31_PLL6_FACTOR_N (0x1f << 8)
+#define A31_PLL6_FACTOR_N_SHIFT 8
+#define A31_PLL6_FACTOR_K (0x3 << 4)
+#define A31_PLL6_FACTOR_K_SHIFT 4
+#define A31_PLL6_DEFAULT_N 0x18
+#define A31_PLL6_DEFAULT_K 0x1
+#define A31_PLL6_TIMEOUT 10
+
+#define CLKID_A10_PLL3_1X 0
+#define CLKID_A10_PLL3_2X 1
+
+#define CLKID_A10_PLL5_DDR 0
+#define CLKID_A10_PLL5_OTHER 1
+
+#define CLKID_A10_PLL6_SATA 0
+#define CLKID_A10_PLL6_OTHER 1
+#define CLKID_A10_PLL6 2
+#define CLKID_A10_PLL6_DIV_4 3
+
+#define CLKID_A31_PLL6 0
+#define CLKID_A31_PLL6_X2 1
+
+enum aw_pll_type {
+ AWPLL_A10_PLL1 = 1,
+ AWPLL_A10_PLL2,
+ AWPLL_A10_PLL3,
+ AWPLL_A10_PLL5,
+ AWPLL_A10_PLL6,
+ AWPLL_A31_PLL1,
+ AWPLL_A31_PLL6,
+};
+
+struct aw_pll_sc {
+ enum aw_pll_type type;
+ device_t clkdev;
+ bus_addr_t reg;
+ int id;
+};
+
+struct aw_pll_funcs {
+ int (*recalc)(struct aw_pll_sc *, uint64_t *);
+ int (*set_freq)(struct aw_pll_sc *, uint64_t, uint64_t *, int);
+ int (*init)(device_t, bus_addr_t, struct clknode_init_def *);
+};
+
+#define PLL_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
+#define PLL_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
+#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
+#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
+
+static int
+a10_pll1_recalc(struct aw_pll_sc *sc, uint64_t *freq)
+{
+ uint32_t val, m, n, k, p;
+
+ DEVICE_LOCK(sc);
+ PLL_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ p = 1 << ((val & A10_PLL1_OUT_EXT_DIVP) >> A10_PLL1_OUT_EXT_DIVP_SHIFT);
+ m = ((val & A10_PLL1_FACTOR_M) >> A10_PLL1_FACTOR_M_SHIFT) + 1;
+ k = ((val & A10_PLL1_FACTOR_K) >> A10_PLL1_FACTOR_K_SHIFT) + 1;
+ n = (val & A10_PLL1_FACTOR_N) >> A10_PLL1_FACTOR_N_SHIFT;
+ if (n == 0)
+ n = 1;
+
+ *freq = (*freq * n * k) / (m * p);
+
+ return (0);
+}
+
+static int
+a10_pll2_recalc(struct aw_pll_sc *sc, uint64_t *freq)
+{
+ uint32_t val, post_div, n, pre_div;
+
+ DEVICE_LOCK(sc);
+ PLL_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ post_div = (val & A10_PLL2_POST_DIV) >> A10_PLL2_POST_DIV_SHIFT;
+ if (post_div == 0)
+ post_div = 1;
+ n = (val & A10_PLL2_FACTOR_N) >> A10_PLL2_FACTOR_N_SHIFT;
+ if (n == 0)
+ n = 1;
+ pre_div = (val & A10_PLL2_PRE_DIV) >> A10_PLL2_PRE_DIV_SHIFT;
+ if (pre_div == 0)
+ pre_div = 1;
+
+ switch (sc->id) {
+ case SUN4I_A10_PLL2_1X:
+ *freq = (*freq * 2 * n) / pre_div / post_div / 2;
+ break;
+ case SUN4I_A10_PLL2_2X:
+ *freq = (*freq * 2 * n) / pre_div / 4;
+ break;
+ case SUN4I_A10_PLL2_4X:
+ *freq = (*freq * 2 * n) / pre_div / 2;
+ break;
+ case SUN4I_A10_PLL2_8X:
+ *freq = (*freq * 2 * n) / pre_div;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static int
+a10_pll2_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout,
+ int flags)
+{
+ uint32_t val, post_div, n, pre_div;
+
+ if (sc->id != SUN4I_A10_PLL2_1X)
+ return (ENXIO);
+
+ /*
+ * Audio Codec needs PLL2-1X to be either 24576000 or 22579200.
+ *
+ * PLL2-1X output frequency is (48MHz * n) / pre_div / post_div / 2.
+ * To get as close as possible to the desired rate, we use a
+ * pre-divider of 21 and a post-divider of 4. With these values,
+ * a multiplier of 86 or 79 gets us close to the target rates.
+ */
+ if (*fout != 24576000 && *fout != 22579200)
+ return (EINVAL);
+
+ pre_div = 21;
+ post_div = 4;
+ n = (*fout * pre_div * post_div * 2) / (2 * fin);
+
+ DEVICE_LOCK(sc);
+ PLL_READ(sc, &val);
+ val &= ~(A10_PLL2_POST_DIV | A10_PLL2_FACTOR_N | A10_PLL2_PRE_DIV);
+ val |= (post_div << A10_PLL2_POST_DIV_SHIFT);
+ val |= (n << A10_PLL2_FACTOR_N_SHIFT);
+ val |= (pre_div << A10_PLL2_PRE_DIV_SHIFT);
+ PLL_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+a10_pll3_recalc(struct aw_pll_sc *sc, uint64_t *freq)
+{
+ uint32_t val, m;
+
+ DEVICE_LOCK(sc);
+ PLL_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ if ((val & A10_PLL3_MODE_SEL) == A10_PLL3_MODE_SEL_INT) {
+ /* In integer mode, output is 3MHz * m */
+ m = (val & A10_PLL3_FACTOR_M) >> A10_PLL3_FACTOR_M_SHIFT;
+ *freq = A10_PLL3_REF_FREQ * m;
+ } else {
+ /* In fractional mode, output is either 270MHz or 297MHz */
+ if ((val & A10_PLL3_FUNC_SET) == A10_PLL3_FUNC_SET_270MHZ)
+ *freq = 270000000;
+ else
+ *freq = 297000000;
+ }
+
+ if (sc->id == CLKID_A10_PLL3_2X)
+ *freq *= 2;
+
+ return (0);
+}
+
+static int
+a10_pll3_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout,
+ int flags)
+{
+ uint32_t val, m, mode, func;
+
+ m = *fout / A10_PLL3_REF_FREQ;
+ if (sc->id == CLKID_A10_PLL3_2X)
+ m /= 2;
+
+ mode = A10_PLL3_MODE_SEL_INT;
+ func = 0;
+ *fout = m * A10_PLL3_REF_FREQ;
+ if (sc->id == CLKID_A10_PLL3_2X)
+ *fout *= 2;
+
+ DEVICE_LOCK(sc);
+ PLL_READ(sc, &val);
+ val &= ~(A10_PLL3_MODE_SEL | A10_PLL3_FUNC_SET | A10_PLL3_FACTOR_M);
+ val |= mode;
+ val |= func;
+ val |= (m << A10_PLL3_FACTOR_M_SHIFT);
+ PLL_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+a10_pll3_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def)
+{
+ uint32_t val;
+
+ /* Allow changing PLL frequency while enabled */
+ def->flags = CLK_NODE_GLITCH_FREE;
+
+ /* Set PLL to 297MHz */
+ CLKDEV_DEVICE_LOCK(dev);
+ CLKDEV_READ_4(dev, reg, &val);
+ val &= ~(A10_PLL3_MODE_SEL | A10_PLL3_FUNC_SET | A10_PLL3_FACTOR_M);
+ val |= A10_PLL3_MODE_SEL_FRACT;
+ val |= A10_PLL3_FUNC_SET_297MHZ;
+ CLKDEV_WRITE_4(dev, reg, val);
+ CLKDEV_DEVICE_UNLOCK(dev);
+
+ return (0);
+}
+
+static int
+a10_pll5_recalc(struct aw_pll_sc *sc, uint64_t *freq)
+{
+ uint32_t val, m, n, k, p;
+
+ DEVICE_LOCK(sc);
+ PLL_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ p = 1 << ((val & A10_PLL5_OUT_EXT_DIVP) >> A10_PLL5_OUT_EXT_DIVP_SHIFT);
+ m = ((val & A10_PLL5_FACTOR_M) >> A10_PLL5_FACTOR_M_SHIFT) + 1;
+ k = ((val & A10_PLL5_FACTOR_K) >> A10_PLL5_FACTOR_K_SHIFT) + 1;
+ n = (val & A10_PLL5_FACTOR_N) >> A10_PLL5_FACTOR_N_SHIFT;
+ if (n == 0)
+ return (ENXIO);
+
+ switch (sc->id) {
+ case CLKID_A10_PLL5_DDR:
+ *freq = (*freq * n * k) / m;
+ break;
+ case CLKID_A10_PLL5_OTHER:
+ *freq = (*freq * n * k) / p;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+a10_pll6_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def)
+{
+ uint32_t val, m, n, k;
+
+ /*
+ * SATA needs PLL6 to be a 100MHz clock.
+ *
+ * The SATA output frequency is (24MHz * n * k) / m / 6.
+ * To get to 100MHz, k & m must be equal and n must be 25.
+ */
+ m = k = 0;
+ n = 25;
+
+ CLKDEV_DEVICE_LOCK(dev);
+ CLKDEV_READ_4(dev, reg, &val);
+ val &= ~(A10_PLL6_FACTOR_N | A10_PLL6_FACTOR_K | A10_PLL6_FACTOR_M);
+ val &= ~A10_PLL6_BYPASS_EN;
+ val |= A10_PLL6_SATA_CLK_EN;
+ val |= (n << A10_PLL6_FACTOR_N_SHIFT);
+ val |= (k << A10_PLL6_FACTOR_K_SHIFT);
+ val |= (m << A10_PLL6_FACTOR_M_SHIFT);
+ CLKDEV_WRITE_4(dev, reg, val);
+ CLKDEV_DEVICE_UNLOCK(dev);
+
+ return (0);
+}
+
+static int
+a10_pll6_recalc(struct aw_pll_sc *sc, uint64_t *freq)
+{
+ uint32_t val, m, k, n;
+
+ DEVICE_LOCK(sc);
+ PLL_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ m = ((val & A10_PLL6_FACTOR_M) >> A10_PLL6_FACTOR_M_SHIFT) + 1;
+ k = ((val & A10_PLL6_FACTOR_K) >> A10_PLL6_FACTOR_K_SHIFT) + 1;
+ n = (val & A10_PLL6_FACTOR_N) >> A10_PLL6_FACTOR_N_SHIFT;
+ if (n == 0)
+ return (ENXIO);
+
+ switch (sc->id) {
+ case CLKID_A10_PLL6_SATA:
+ *freq = (*freq * n * k) / m / 6;
+ break;
+ case CLKID_A10_PLL6_OTHER:
+ *freq = (*freq * n * k) / 2;
+ break;
+ case CLKID_A10_PLL6:
+ *freq = (*freq * n * k);
+ break;
+ case CLKID_A10_PLL6_DIV_4:
+ *freq = (*freq * n * k) / 4;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+a10_pll6_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout,
+ int flags)
+{
+ if (sc->id != CLKID_A10_PLL6_SATA)
+ return (ENXIO);
+
+ /* PLL6 SATA output has been set to 100MHz in a10_pll6_init */
+ if (*fout != 100000000)
+ return (ERANGE);
+
+ return (0);
+}
+
+static int
+a31_pll1_recalc(struct aw_pll_sc *sc, uint64_t *freq)
+{
+ uint32_t val, m, n, k;
+
+ DEVICE_LOCK(sc);
+ PLL_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ m = ((val & A31_PLL1_FACTOR_M) >> A31_PLL1_FACTOR_M_SHIFT) + 1;
+ k = ((val & A31_PLL1_FACTOR_K) >> A31_PLL1_FACTOR_K_SHIFT) + 1;
+ n = ((val & A31_PLL1_FACTOR_N) >> A31_PLL1_FACTOR_N_SHIFT) + 1;
+
+ *freq = (*freq * n * k) / m;
+
+ return (0);
+}
+
+static int
+a31_pll6_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def)
+{
+ uint32_t val;
+ int retry;
+
+ if (def->id != CLKID_A31_PLL6)
+ return (0);
+
+ /*
+ * The datasheet recommends that PLL6 output should be fixed to
+ * 600MHz.
+ */
+ CLKDEV_DEVICE_LOCK(dev);
+ CLKDEV_READ_4(dev, reg, &val);
+ val &= ~(A31_PLL6_FACTOR_N | A31_PLL6_FACTOR_K | A31_PLL6_BYPASS_EN);
+ val |= (A31_PLL6_DEFAULT_N << A31_PLL6_FACTOR_N_SHIFT);
+ val |= (A31_PLL6_DEFAULT_K << A31_PLL6_FACTOR_K_SHIFT);
+ CLKDEV_WRITE_4(dev, reg, val);
+
+ /* Wait for PLL to become stable */
+ for (retry = A31_PLL6_TIMEOUT; retry > 0; retry--) {
+ CLKDEV_READ_4(dev, reg, &val);
+ if ((val & A31_PLL6_LOCK) == A31_PLL6_LOCK)
+ break;
+ DELAY(1);
+ }
+
+ CLKDEV_DEVICE_UNLOCK(dev);
+
+ if (retry == 0)
+ return (ETIMEDOUT);
+
+ return (0);
+}
+
+static int
+a31_pll6_recalc(struct aw_pll_sc *sc, uint64_t *freq)
+{
+ uint32_t val, k, n;
+
+ DEVICE_LOCK(sc);
+ PLL_READ(sc, &val);
+ DEVICE_UNLOCK(sc);
+
+ k = ((val & A10_PLL6_FACTOR_K) >> A10_PLL6_FACTOR_K_SHIFT) + 1;
+ n = ((val & A10_PLL6_FACTOR_N) >> A10_PLL6_FACTOR_N_SHIFT) + 1;
+
+ switch (sc->id) {
+ case CLKID_A31_PLL6:
+ *freq = (*freq * n * k) / 2;
+ break;
+ case CLKID_A31_PLL6_X2:
+ *freq = *freq * n * k;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+#define PLL(_type, _recalc, _set_freq, _init) \
+ [(_type)] = { \
+ .recalc = (_recalc), \
+ .set_freq = (_set_freq), \
+ .init = (_init) \
+ }
+
+static struct aw_pll_funcs aw_pll_func[] = {
+ PLL(AWPLL_A10_PLL1, a10_pll1_recalc, NULL, NULL),
+ PLL(AWPLL_A10_PLL2, a10_pll2_recalc, a10_pll2_set_freq, NULL),
+ PLL(AWPLL_A10_PLL3, a10_pll3_recalc, a10_pll3_set_freq, a10_pll3_init),
+ PLL(AWPLL_A10_PLL5, a10_pll5_recalc, NULL, NULL),
+ PLL(AWPLL_A10_PLL6, a10_pll6_recalc, a10_pll6_set_freq, a10_pll6_init),
+ PLL(AWPLL_A31_PLL1, a31_pll1_recalc, NULL, NULL),
+ PLL(AWPLL_A31_PLL6, a31_pll6_recalc, NULL, a31_pll6_init),
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-pll1-clk", AWPLL_A10_PLL1 },
+ { "allwinner,sun4i-a10-pll2-clk", AWPLL_A10_PLL2 },
+ { "allwinner,sun4i-a10-pll3-clk", AWPLL_A10_PLL3 },
+ { "allwinner,sun4i-a10-pll5-clk", AWPLL_A10_PLL5 },
+ { "allwinner,sun4i-a10-pll6-clk", AWPLL_A10_PLL6 },
+ { "allwinner,sun6i-a31-pll1-clk", AWPLL_A31_PLL1 },
+ { "allwinner,sun6i-a31-pll6-clk", AWPLL_A31_PLL6 },
+ { NULL, 0 }
+};
+
+static int
+aw_pll_init(struct clknode *clk, device_t dev)
+{
+ clknode_init_parent_idx(clk, 0);
+ return (0);
+}
+
+static int
+aw_pll_set_gate(struct clknode *clk, bool enable)
+{
+ struct aw_pll_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(sc);
+ PLL_READ(sc, &val);
+ if (enable)
+ val |= AW_PLL_ENABLE;
+ else
+ val &= ~AW_PLL_ENABLE;
+ PLL_WRITE(sc, val);
+ DEVICE_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+aw_pll_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct aw_pll_sc *sc;
+
+ sc = clknode_get_softc(clk);
+
+ if (aw_pll_func[sc->type].recalc == NULL)
+ return (ENXIO);
+
+ return (aw_pll_func[sc->type].recalc(sc, freq));
+}
+
+static int
+aw_pll_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct aw_pll_sc *sc;
+
+ sc = clknode_get_softc(clk);
+
+ *stop = 1;
+
+ if (aw_pll_func[sc->type].set_freq == NULL)
+ return (ENXIO);
+
+ return (aw_pll_func[sc->type].set_freq(sc, fin, fout, flags));
+}
+
+static clknode_method_t aw_pll_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, aw_pll_init),
+ CLKNODEMETHOD(clknode_set_gate, aw_pll_set_gate),
+ CLKNODEMETHOD(clknode_recalc_freq, aw_pll_recalc),
+ CLKNODEMETHOD(clknode_set_freq, aw_pll_set_freq),
+ CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(aw_pll_clknode, aw_pll_clknode_class, aw_pll_clknode_methods,
+ sizeof(struct aw_pll_sc), clknode_class);
+
+static int
+aw_pll_create(device_t dev, bus_addr_t paddr, struct clkdom *clkdom,
+ const char *pclkname, const char *clkname, int index)
+{
+ enum aw_pll_type type;
+ struct clknode_init_def clkdef;
+ struct aw_pll_sc *sc;
+ struct clknode *clk;
+ int error;
+
+ type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ memset(&clkdef, 0, sizeof(clkdef));
+ clkdef.id = index;
+ clkdef.name = clkname;
+ if (pclkname != NULL) {
+ clkdef.parent_names = malloc(sizeof(char *), M_OFWPROP,
+ M_WAITOK);
+ clkdef.parent_names[0] = pclkname;
+ clkdef.parent_cnt = 1;
+ } else
+ clkdef.parent_cnt = 0;
+
+ if (aw_pll_func[type].init != NULL) {
+ error = aw_pll_func[type].init(device_get_parent(dev),
+ paddr, &clkdef);
+ if (error != 0) {
+ device_printf(dev, "clock %s init failed\n", clkname);
+ return (error);
+ }
+ }
+
+ clk = clknode_create(clkdom, &aw_pll_clknode_class, &clkdef);
+ if (clk == NULL) {
+ device_printf(dev, "cannot create clock node\n");
+ return (ENXIO);
+ }
+ sc = clknode_get_softc(clk);
+ sc->clkdev = device_get_parent(dev);
+ sc->reg = paddr;
+ sc->type = type;
+ sc->id = clkdef.id;
+
+ clknode_register(clkdom, clk);
+
+ free(__DECONST(char *, clkdef.parent_names), M_OFWPROP);
+
+ return (0);
+}
+
+static int
+aw_pll_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner PLL Clock");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_pll_attach(device_t dev)
+{
+ struct clkdom *clkdom;
+ const char **names;
+ int index, nout, error;
+ clk_t clk_parent;
+ uint32_t *indices;
+ bus_addr_t paddr;
+ bus_size_t psize;
+ phandle_t node;
+
+ node = ofw_bus_get_node(dev);
+
+ if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
+ device_printf(dev, "couldn't parse 'reg' property\n");
+ return (ENXIO);
+ }
+
+ clkdom = clkdom_create(dev);
+
+ nout = clk_parse_ofw_out_names(dev, node, &names, &indices);
+ if (nout == 0) {
+ device_printf(dev, "no clock outputs found\n");
+ error = ENOENT;
+ goto fail;
+ }
+
+ if (clk_get_by_ofw_index(dev, 0, &clk_parent) != 0)
+ clk_parent = NULL;
+
+ for (index = 0; index < nout; index++) {
+ error = aw_pll_create(dev, paddr, clkdom,
+ clk_parent ? clk_get_name(clk_parent) : NULL,
+ names[index], nout == 1 ? 1 : index);
+ if (error)
+ goto fail;
+ }
+
+ if (clkdom_finit(clkdom) != 0) {
+ device_printf(dev, "cannot finalize clkdom initialization\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ return (0);
+
+fail:
+ return (error);
+}
+
+static device_method_t aw_pll_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_pll_probe),
+ DEVMETHOD(device_attach, aw_pll_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_pll_driver = {
+ "aw_pll",
+ aw_pll_methods,
+ 0,
+};
+
+static devclass_t aw_pll_devclass;
+
+EARLY_DRIVER_MODULE(aw_pll, simplebus, aw_pll_driver,
+ aw_pll_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
Index: head/sys/arm/allwinner/clk/aw_usbclk.c
===================================================================
--- head/sys/arm/allwinner/clk/aw_usbclk.c
+++ head/sys/arm/allwinner/clk/aw_usbclk.c
@@ -0,0 +1,246 @@
+/*-
+ * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
+ * 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 ``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 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$
+ */
+
+/*
+ * Allwinner USB clocks
+ */
+
+#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 <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
+
+#include <dev/extres/clk/clk_gate.h>
+#include <dev/extres/hwreset/hwreset.h>
+
+#include "clkdev_if.h"
+#include "hwreset_if.h"
+
+#define A10_SCLK_GATING_USBPHY (1 << 8)
+#define A10_SCLK_GATING_OHCI1 (1 << 7)
+#define A10_SCLK_GATING_OHCI0 (1 << 6)
+
+#define USBPHY2_RST (1 << 2)
+#define USBPHY1_RST (1 << 1)
+#define USBPHY0_RST (1 << 0)
+
+enum aw_usbclk_type {
+ AW_A10_USBCLK = 1,
+ AW_A31_USBCLK,
+};
+
+static struct ofw_compat_data compat_data[] = {
+ { "allwinner,sun4i-a10-usb-clk", AW_A10_USBCLK },
+ { "allwinner,sun6i-a31-usb-clk", AW_A31_USBCLK },
+ { NULL, 0 }
+};
+
+/* Clock indices for A10, as there is no clock-indices property in the DT */
+static uint32_t aw_usbclk_indices_a10[] = { 6, 7, 8 };
+
+struct aw_usbclk_softc {
+ bus_addr_t reg;
+};
+
+static int
+aw_usbclk_hwreset_assert(device_t dev, intptr_t id, bool value)
+{
+ struct aw_usbclk_softc *sc;
+ uint32_t mask;
+ device_t pdev;
+ int error;
+
+ sc = device_get_softc(dev);
+ pdev = device_get_parent(dev);
+
+ mask = USBPHY0_RST << id;
+
+ CLKDEV_DEVICE_LOCK(pdev);
+ error = CLKDEV_MODIFY_4(pdev, sc->reg, mask, value ? 0 : mask);
+ CLKDEV_DEVICE_UNLOCK(pdev);
+
+ return (error);
+}
+
+static int
+aw_usbclk_hwreset_is_asserted(device_t dev, intptr_t id, bool *value)
+{
+ struct aw_usbclk_softc *sc;
+ uint32_t mask, val;
+ device_t pdev;
+ int error;
+
+ sc = device_get_softc(dev);
+ pdev = device_get_parent(dev);
+
+ mask = USBPHY0_RST << id;
+
+ CLKDEV_DEVICE_LOCK(pdev);
+ error = CLKDEV_READ_4(pdev, sc->reg, &val);
+ CLKDEV_DEVICE_UNLOCK(pdev);
+
+ if (error)
+ return (error);
+
+ *value = (val & mask) != 0 ? false : true;
+
+ return (0);
+}
+
+static int
+aw_usbclk_create(device_t dev, bus_addr_t paddr, struct clkdom *clkdom,
+ const char *pclkname, const char *clkname, int index)
+{
+ const char *parent_names[1] = { pclkname };
+ struct clk_gate_def def;
+
+ memset(&def, 0, sizeof(def));
+ def.clkdef.id = index;
+ def.clkdef.name = clkname;
+ def.clkdef.parent_names = parent_names;
+ def.clkdef.parent_cnt = 1;
+ def.offset = paddr;
+ def.shift = index;
+ def.mask = 1;
+ def.on_value = 1;
+ def.off_value = 0;
+
+ return (clknode_gate_register(clkdom, &def));
+}
+
+static int
+aw_usbclk_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner USB Clocks");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_usbclk_attach(device_t dev)
+{
+ struct aw_usbclk_softc *sc;
+ struct clkdom *clkdom;
+ const char **names;
+ int index, nout, error;
+ enum aw_usbclk_type type;
+ uint32_t *indices;
+ clk_t clk_parent;
+ bus_size_t psize;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(dev);
+ indices = NULL;
+ type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ if (ofw_reg_to_paddr(node, 0, &sc->reg, &psize, NULL) != 0) {
+ device_printf(dev, "cannot parse 'reg' property\n");
+ return (ENXIO);
+ }
+
+ clkdom = clkdom_create(dev);
+
+ nout = clk_parse_ofw_out_names(dev, node, &names, &indices);
+ if (nout == 0) {
+ device_printf(dev, "no clock outputs found\n");
+ error = ENOENT;
+ goto fail;
+ }
+
+ if (indices == NULL && type == AW_A10_USBCLK)
+ indices = aw_usbclk_indices_a10;
+
+ error = clk_get_by_ofw_index(dev, 0, &clk_parent);
+ if (error != 0) {
+ device_printf(dev, "cannot parse clock parent\n");
+ return (ENXIO);
+ }
+
+ for (index = 0; index < nout; index++) {
+ error = aw_usbclk_create(dev, sc->reg, clkdom,
+ clk_get_name(clk_parent), names[index],
+ indices != NULL ? indices[index] : index);
+ if (error)
+ goto fail;
+ }
+
+ if (clkdom_finit(clkdom) != 0) {
+ device_printf(dev, "cannot finalize clkdom initialization\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ if (bootverbose)
+ clkdom_dump(clkdom);
+
+ hwreset_register_ofw_provider(dev);
+
+ return (0);
+
+fail:
+ return (error);
+}
+
+static device_method_t aw_usbclk_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aw_usbclk_probe),
+ DEVMETHOD(device_attach, aw_usbclk_attach),
+
+ /* Reset interface */
+ DEVMETHOD(hwreset_assert, aw_usbclk_hwreset_assert),
+ DEVMETHOD(hwreset_is_asserted, aw_usbclk_hwreset_is_asserted),
+
+ DEVMETHOD_END
+};
+
+static driver_t aw_usbclk_driver = {
+ "aw_usbclk",
+ aw_usbclk_methods,
+ sizeof(struct aw_usbclk_softc)
+};
+
+static devclass_t aw_usbclk_devclass;
+
+EARLY_DRIVER_MODULE(aw_usbclk, simplebus, aw_usbclk_driver,
+ aw_usbclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
Index: head/sys/arm/allwinner/files.allwinner
===================================================================
--- head/sys/arm/allwinner/files.allwinner
+++ head/sys/arm/allwinner/files.allwinner
@@ -2,7 +2,6 @@
kern/kern_clocksource.c standard
arm/allwinner/a10_ahci.c optional ahci
-arm/allwinner/a10_clk.c standard
arm/allwinner/a10_codec.c optional sound
arm/allwinner/a10_common.c standard
arm/allwinner/a10_dmac.c standard
@@ -25,3 +24,21 @@
arm/allwinner/a10_hdmi.c optional hdmi
arm/allwinner/a10_hdmiaudio.c optional hdmi sound
arm/arm/hdmi_if.m optional hdmi
+
+arm/allwinner/aw_reset.c standard
+arm/allwinner/aw_ccu.c standard
+arm/allwinner/clk/aw_ahbclk.c standard
+arm/allwinner/clk/aw_apbclk.c standard
+arm/allwinner/clk/aw_axiclk.c standard
+arm/allwinner/clk/aw_codecclk.c standard
+arm/allwinner/clk/aw_cpuclk.c standard
+arm/allwinner/clk/aw_debeclk.c standard
+arm/allwinner/clk/aw_gate.c standard
+arm/allwinner/clk/aw_gmacclk.c standard
+arm/allwinner/clk/aw_hdmiclk.c standard
+arm/allwinner/clk/aw_lcdclk.c standard
+arm/allwinner/clk/aw_modclk.c standard
+arm/allwinner/clk/aw_mmcclk.c standard
+arm/allwinner/clk/aw_oscclk.c standard
+arm/allwinner/clk/aw_pll.c standard
+arm/allwinner/clk/aw_usbclk.c standard
Index: head/sys/arm/allwinner/if_emac.c
===================================================================
--- head/sys/arm/allwinner/if_emac.c
+++ head/sys/arm/allwinner/if_emac.c
@@ -78,11 +78,12 @@
#include <arm/allwinner/if_emacreg.h>
+#include <dev/extres/clk/clk.h>
+
#include "miibus_if.h"
#include "gpio_if.h"
-#include "a10_clk.h"
#include "a10_sramc.h"
struct emac_softc {
@@ -94,6 +95,7 @@
struct resource *emac_res;
struct resource *emac_irq;
void *emac_intrhand;
+ clk_t emac_clk;
int emac_if_flags;
struct mtx emac_mtx;
struct callout emac_tick_ch;
@@ -110,7 +112,7 @@
static int emac_suspend(device_t);
static int emac_resume(device_t);
-static void emac_sys_setup(void);
+static int emac_sys_setup(struct emac_softc *);
static void emac_reset(struct emac_softc *);
static void emac_init_locked(struct emac_softc *);
@@ -138,14 +140,27 @@
#define EMAC_WRITE_REG(sc, reg, val) \
bus_space_write_4(sc->emac_tag, sc->emac_handle, reg, val)
-static void
-emac_sys_setup(void)
+static int
+emac_sys_setup(struct emac_softc *sc)
{
+ int error;
/* Activate EMAC clock. */
- a10_clk_emac_activate();
+ error = clk_get_by_ofw_index(sc->emac_dev, 0, &sc->emac_clk);
+ if (error != 0) {
+ device_printf(sc->emac_dev, "cannot get clock\n");
+ return (error);
+ }
+ error = clk_enable(sc->emac_clk);
+ if (error != 0) {
+ device_printf(sc->emac_dev, "cannot enable clock\n");
+ return (error);
+ }
+
/* Map sram. */
a10_map_to_emac();
+
+ return (0);
}
static void
@@ -784,6 +799,9 @@
bus_generic_detach(sc->emac_dev);
}
+ if (sc->emac_clk != NULL)
+ clk_disable(sc->emac_clk);
+
if (sc->emac_res != NULL)
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->emac_res);
@@ -897,7 +915,10 @@
}
}
/* Setup EMAC */
- emac_sys_setup();
+ error = emac_sys_setup(sc);
+ if (error != 0)
+ goto fail;
+
emac_reset(sc);
ifp = sc->emac_ifp = if_alloc(IFT_ETHER);
Index: head/sys/arm/conf/A10
===================================================================
--- head/sys/arm/conf/A10
+++ head/sys/arm/conf/A10
@@ -51,6 +51,13 @@
#options BOOTP_NFSV3
#options BOOTP_WIRED_TO=emac0
+# EXT_RESOURCES pseudo devices
+options EXT_RESOURCES
+device clk
+device phy
+device hwreset
+device regulator
+
# MMC/SD/SDIO Card slot support
device mmc # mmc/sd bus
device mmcsd # mmc/sd flash cards
Index: head/sys/arm/conf/A20
===================================================================
--- head/sys/arm/conf/A20
+++ head/sys/arm/conf/A20
@@ -55,6 +55,13 @@
#options BOOTP_NFSV3
#options BOOTP_WIRED_TO=dwc0
+# EXT_RESOURCES pseudo devices
+options EXT_RESOURCES
+device clk
+device phy
+device hwreset
+device regulator
+
# Interrupt controller
device gic
Index: head/sys/boot/fdt/dts/arm/bananapi.dts
===================================================================
--- head/sys/boot/fdt/dts/arm/bananapi.dts
+++ head/sys/boot/fdt/dts/arm/bananapi.dts
@@ -26,83 +26,21 @@
* $FreeBSD$
*/
-/dts-v1/;
-
-#include "sun7i-a20.dtsi"
-
-#include <dt-bindings/gpio/gpio.h>
+#include "sun7i-a20-bananapi.dts"
+#include "sun7i-a20-hdmi.dtsi"
/ {
- model = "LeMaker Banana Pi";
- compatible = "lemaker,bananapi", "allwinner,sun7i-a20";
-
- memory {
- device_type = "memory";
- reg = < 0x40000000 0x40000000 >; /* 1GB RAM */
- };
-
- aliases {
- soc = &SOC;
- UART0 = &UART0;
- };
-
- SOC: a20 {
-
- usb1: usb@01c14000 {
- status = "okay";
- };
-
- usb2: usb@01c1c000 {
- status = "okay";
- };
-
- UART0: serial@01c28000 {
- status = "okay";
- };
-
- mmc0: mmc@01c0f000 {
+ soc@01c00000 {
+ hdmi@01c16000 {
status = "okay";
};
- gmac@01c50000 {
- phy-mode = "rgmii-bpi";
- status = "okay";
- pinctrl-names = "default";
- pinctrl-0 = <&gmac_pins_rgmii>;
- };
-
- ahci: sata@01c18000 {
- status = "okay";
- };
-
- hdmi: hdmi@01c16000 {
- compatible = "allwinner,sun7i-a20-hdmi";
- reg = <0x01c16000 0x1000>;
- };
-
hdmiaudio {
- compatible = "allwinner,sun7i-a20-hdmiaudio";
- };
-
- fb: fb@01e60000 {
- compatible = "allwinner,sun7i-a20-fb";
- reg = <0x01e60000 0x10000>, /* DEBE0 */
- <0x01c0c000 0x1000>; /* LCD0 */
- };
- };
-
- leds {
- compatible = "gpio-leds";
-
- green {
- label = "bananapi:green:usr";
- gpios = <&pio 7 24 GPIO_ACTIVE_HIGH>;
+ status = "okay";
};
};
+};
- chosen {
- bootargs = "-v";
- stdin = "UART0";
- stdout = "UART0";
- };
+&mmc0_pins_a {
+ allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
};
Index: head/sys/boot/fdt/dts/arm/cubieboard.dts
===================================================================
--- head/sys/boot/fdt/dts/arm/cubieboard.dts
+++ head/sys/boot/fdt/dts/arm/cubieboard.dts
@@ -26,56 +26,4 @@
* $FreeBSD$
*/
-/dts-v1/;
-
-#include "sun4i-a10.dtsi"
-
-/ {
- model = "Cubietech Cubieboard";
-
- memory {
- device_type = "memory";
- reg = < 0x40000000 0x40000000 >; /* 1GB RAM */
- };
-
- aliases {
- soc = &SOC;
- UART0 = &UART0;
- };
-
- SOC: a10 {
-
- usb1: usb@01c14000 {
- status = "okay";
- };
-
- usb2: usb@01c1c000 {
- status = "okay";
- };
-
- UART0: serial@01c28000 {
- status = "okay";
- };
-
- mmc0: mmc@01c0f000 {
- status = "okay";
- };
-
- emac@01c0b000 {
- status = "okay";
- pinctrl-names = "default";
- pinctrl-0 = <&emac_pins>;
- };
-
- ahci: sata@01c18000 {
- status = "okay";
- };
- };
-
- chosen {
- bootargs = "-v";
- stdin = "UART0";
- stdout = "UART0";
- };
-};
-
+#include "sun4i-a10-cubieboard.dts"
Index: head/sys/boot/fdt/dts/arm/cubieboard2.dts
===================================================================
--- head/sys/boot/fdt/dts/arm/cubieboard2.dts
+++ head/sys/boot/fdt/dts/arm/cubieboard2.dts
@@ -32,13 +32,6 @@
/ {
soc@01c00000 {
- ccm@01c20000 {
- compatible = "allwinner,sun4i-ccm";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = < 0x01c20000 0x400 >;
- };
-
hdmi@01c16000 {
status = "okay";
};
Index: head/sys/boot/fdt/dts/arm/olimex-a20-som-evb.dts
===================================================================
--- head/sys/boot/fdt/dts/arm/olimex-a20-som-evb.dts
+++ head/sys/boot/fdt/dts/arm/olimex-a20-som-evb.dts
@@ -27,14 +27,16 @@
*/
#include "sun7i-a20-olimex-som-evb.dts"
+#include "sun7i-a20-hdmi.dtsi"
/ {
soc@01c00000 {
- ccm@01c20000 {
- compatible = "allwinner,sun4i-ccm";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = < 0x01c20000 0x400 >;
+ hdmi@01c16000 {
+ status = "okay";
+ };
+
+ hdmiaudio {
+ status = "okay";
};
};
};
Index: head/sys/boot/fdt/dts/arm/olinuxino-lime.dts
===================================================================
--- head/sys/boot/fdt/dts/arm/olinuxino-lime.dts
+++ head/sys/boot/fdt/dts/arm/olinuxino-lime.dts
@@ -27,14 +27,3 @@
*/
#include "sun4i-a10-olinuxino-lime.dts"
-
-/ {
- soc@01c00000 {
- ccm@01c20000 {
- compatible = "allwinner,sun4i-ccm";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = < 0x01c20000 0x400 >;
- };
- };
-};
Index: head/sys/boot/fdt/dts/arm/sun4i-a10.dtsi
===================================================================
--- head/sys/boot/fdt/dts/arm/sun4i-a10.dtsi
+++ head/sys/boot/fdt/dts/arm/sun4i-a10.dtsi
@@ -1,153 +0,0 @@
-/*-
- * Copyright (c) 2014 Ganbold Tsagaankhuu <ganbold@freebsd.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $FreeBSD$
- */
-
-#include <dt-bindings/pinctrl/sun4i-a10.h>
-
-/ {
- compatible = "allwinner,sun4i-a10";
- #address-cells = <1>;
- #size-cells = <1>;
-
- interrupt-parent = <&AINTC>;
-
- aliases {
- soc = &SOC;
- };
-
- SOC: a10 {
- #address-cells = <1>;
- #size-cells = <1>;
- compatible = "simple-bus";
- ranges;
- bus-frequency = <0>;
-
- AINTC: interrupt-controller@01c20400 {
- compatible = "allwinner,sun4i-a10-ic";
- interrupt-controller;
- #address-cells = <0>;
- #interrupt-cells = <1>;
- reg = < 0x01c20400 0x400 >;
- };
-
- sramc@01c00000 {
- compatible = "allwinner,sun4i-sramc";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = < 0x01c00000 0x1000 >;
- };
-
- ccm@01c20000 {
- compatible = "allwinner,sun4i-ccm";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = < 0x01c20000 0x400 >;
- };
-
- timer@01c20c00 {
- compatible = "allwinner,sun4i-a10-timer";
- reg = <0x01c20c00 0x90>;
- interrupts = < 22 >;
- interrupt-parent = <&AINTC>;
- clock-frequency = < 24000000 >;
- };
-
- watchdog@01c20c90 {
- compatible = "allwinner,sun4i-wdt";
- reg = <0x01c20c90 0x08>;
- };
-
-
- GPIO: gpio@01c20800 {
- #gpio-cells = <3>;
- compatible = "allwinner,sun4i-a10-pinctrl";
- gpio-controller;
- reg =< 0x01c20800 0x400 >;
- interrupts = < 28 >;
- interrupt-parent = <&AINTC>;
-
- emac_pins: emac@0 {
- allwinner,pins = "PA0", "PA1", "PA2",
- "PA3", "PA4", "PA5", "PA6",
- "PA7", "PA8", "PA9", "PA10",
- "PA11", "PA12", "PA13", "PA14",
- "PA15", "PA16";
- allwinner,function = "emac";
- allwinner,drive = <SUN4I_PINCTRL_10_MA>;
- allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
- };
- };
-
- usb1: usb@01c14000 {
- compatible = "allwinner,sun4i-a10-ehci", "generic-ehci";
- reg = <0x01c14000 0x1000>;
- interrupts = < 39 >;
- interrupt-parent = <&AINTC>;
- };
-
- usb2: usb@01c1c000 {
- compatible = "allwinner,sun4i-a10-ehci", "generic-ehci";
- reg = <0x01c1c000 0x1000>;
- interrupts = < 40 >;
- interrupt-parent = <&AINTC>;
- };
-
- mmc0: mmc@01c0f000 {
- compatible = "allwinner,sun4i-a10-mmc";
- reg = <0x01c0f000 0x1000>;
- interrupts = <32>;
- interrupt-parent = <&AINTC>;
- status = "disabled";
- };
-
- sata@01c18000 {
- compatible = "allwinner,sun4i-a10-ahci";
- reg = <0x01c18000 0x1000>;
- interrupts = <56>;
- interrupt-parent = <&AINTC>;
- status = "disabled";
- };
-
- UART0: serial@01c28000 {
- compatible = "snps,dw-apb-uart";
- reg = <0x01c28000 0x400>;
- reg-shift = <2>;
- interrupts = <1>;
- interrupt-parent = <&AINTC>;
- current-speed = <115200>;
- clock-frequency = < 24000000 >;
- };
-
- emac@01c0b000 {
- compatible = "allwinner,sun4i-a10-emac";
- reg = <0x01c0b000 0x1000>;
- interrupts = <55>;
- interrupt-parent = <&AINTC>;
- };
- };
-};
-
Index: head/sys/boot/fdt/dts/arm/sun7i-a20-hdmi.dtsi
===================================================================
--- head/sys/boot/fdt/dts/arm/sun7i-a20-hdmi.dtsi
+++ head/sys/boot/fdt/dts/arm/sun7i-a20-hdmi.dtsi
@@ -27,10 +27,65 @@
*/
/ {
+ clocks {
+ pll3: clk@01c20010 {
+ #clock-cells = <1>;
+ compatible = "allwinner,sun4i-a10-pll3-clk";
+ reg = <0x01c20010 0x4>;
+ clock-output-names = "pll3-1x", "pll3-2x";
+ };
+
+ pll7: clk@01c20030 {
+ #clock-cells = <1>;
+ compatible = "allwinner,sun4i-a10-pll3-clk";
+ reg = <0x01c20030 0x4>;
+ clock-output-names = "pll7-1x", "pll7-2x";
+ };
+
+ hdmi_clk: clk@01c20150 {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun4i-a10-hdmi-clk";
+ reg = <0x01c20150 0x4>;
+ clocks = <&pll3 0>, <&pll7 0>, <&pll3 1>, <&pll7 1>;
+ clock-output-names = "hdmi";
+ };
+
+ lcd0_ch0_clk: clk@01c20118 {
+ #clock-cells = <0>;
+ #reset-cells = <0>;
+ compatible = "allwinner,sun4i-a10-lcd-ch0-clk";
+ reg = <0x01c20118 0x4>;
+ clocks = <&pll3 0>, <&pll7 0>, <&pll3 1>, <&pll6 2>;
+ clock-output-names = "lcd0_ch0";
+ };
+
+ lcd0_ch1_clk: clk@01c2012c {
+ #clock-cells = <1>;
+ compatible = "allwinner,sun4i-a10-lcd-ch1-clk";
+ reg = <0x01c2012c 0x4>;
+ clocks = <&pll3 0>, <&pll7 0>, <&pll3 1>, <&pll7 1>;
+ clock-output-names = "lcd0_ch1_sclk1",
+ "lcd0_ch1_sclk2";
+ };
+
+ de_be0_clk: clk@01c20104 {
+ #clock-cells = <0>;
+ #reset-cells = <0>;
+ compatible = "allwinner,sun4i-a10-de-be-clk";
+ reg = <0x01c20104 0x4>;
+ clocks = <&pll3 0>, <&pll7 0>, <&pll5 1>;
+ clock-output-names = "de_be0";
+ };
+ };
+
soc@01c00000 {
hdmi: hdmi@01c16000 {
compatible = "allwinner,sun7i-a20-hdmi";
reg = <0x01c16000 0x1000>;
+ clocks = <&ahb_gates 43>, <&hdmi_clk>,
+ <&lcd0_ch1_clk 1>;
+ clock-names = "ahb", "hdmi",
+ "lcd";
status = "disabled";
};
@@ -43,6 +98,14 @@
compatible = "allwinner,sun7i-a20-fb";
reg = <0x01e60000 0x10000>, /* DEBE0 */
<0x01c0c000 0x1000>; /* LCD0 */
+ clocks = <&ahb_gates 44>, <&dram_gates 26>,
+ <&de_be0_clk>, <&ahb_gates 36>,
+ <&lcd0_ch1_clk 0>, <&lcd0_ch1_clk 1>;
+ clock-names = "ahb_de_be", "dram_de_be",
+ "de_be", "ahb_lcd",
+ "lcd_ch1_sclk1", "lcd_ch1_sclk2";
+ resets = <&de_be0_clk>, <&lcd0_ch0_clk>;
+ reset-names = "de_be", "lcd";
};
};
};
Index: head/sys/boot/fdt/dts/arm/sun7i-a20.dtsi
===================================================================
--- head/sys/boot/fdt/dts/arm/sun7i-a20.dtsi
+++ head/sys/boot/fdt/dts/arm/sun7i-a20.dtsi
@@ -1,217 +0,0 @@
-/*-
- * Copyright (c) 2014 Ganbold Tsagaankhuu <ganbold@freebsd.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $FreeBSD$
- */
-
-#include <dt-bindings/interrupt-controller/arm-gic.h>
-#include <dt-bindings/pinctrl/sun4i-a10.h>
-
-/ {
- compatible = "allwinner,sun7i-a20";
- #address-cells = <1>;
- #size-cells = <1>;
-
- interrupt-parent = <&GIC>;
-
- aliases {
- soc = &SOC;
- };
-
- timer {
- compatible = "arm,armv7-timer";
- interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
- <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
- <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
- <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
- };
-
- SOC: a20 {
- #address-cells = <1>;
- #size-cells = <1>;
- compatible = "simple-bus";
- ranges;
- bus-frequency = <0>;
-
- GIC: interrupt-controller@01c81000 {
- compatible = "arm,gic";
- reg = <0x01c81000 0x1000>, /* Distributor Registers */
- <0x01c82000 0x0100>, /* CPU Interface Registers */
- <0x01c84000 0x2000>,
- <0x01c86000 0x2000>;
- interrupt-controller;
- #interrupt-cells = <3>;
- interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
- };
-
- sramc@01c00000 {
- compatible = "allwinner,sun4i-sramc";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = < 0x01c00000 0x1000 >;
- };
-
- cpu-cfg@01c25c00 {
- compatible = "allwinner,sun7i-cpu-cfg";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = < 0x01c25c00 0x400 >;
- };
-
- ccm@01c20000 {
- compatible = "allwinner,sun4i-ccm";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = < 0x01c20000 0x400 >;
- };
-
- timer@01c20c00 {
- compatible = "allwinner,sun4i-a10-timer";
- reg = <0x01c20c00 0x90>;
- interrupts = <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-parent = <&GIC>;
- clock-frequency = < 24000000 >;
- };
-
- watchdog@01c20c90 {
- compatible = "allwinner,sun4i-a10-wdt";
- reg = <0x01c20c90 0x10>;
- };
-
- pio: gpio@01c20800 {
- #gpio-cells = <3>;
- compatible = "allwinner,sun7i-a20-pinctrl";
- gpio-controller;
- reg =< 0x01c20800 0x400 >;
- interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-controller;
- #interrupt-cells = <2>;
- interrupt-parent = <&GIC>;
-
- gmac_pins_mii: gmac_mii@0 {
- allwinner,pins = "PA0", "PA1", "PA2",
- "PA3", "PA4", "PA5", "PA6",
- "PA7", "PA8", "PA9", "PA10",
- "PA11", "PA12", "PA13", "PA14",
- "PA15", "PA16";
- allwinner,function = "gmac";
- allwinner,drive = <SUN4I_PINCTRL_10_MA>;
- allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
- };
-
- gmac_pins_rgmii: gmac_rgmii@0 {
- allwinner,pins = "PA0", "PA1", "PA2",
- "PA3", "PA4", "PA5", "PA6",
- "PA7", "PA8", "PA10",
- "PA11", "PA12", "PA13",
- "PA15", "PA16";
- allwinner,function = "gmac";
- allwinner,drive = <SUN4I_PINCTRL_40_MA>;
- allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
- };
-
- };
-
- usb1: usb@01c14000 {
- compatible = "allwinner,sun7i-a20-ehci", "generic-ehci";
- reg = <0x01c14000 0x1000>;
- interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-parent = <&GIC>;
- };
-
- usb2: usb@01c1c000 {
- compatible = "allwinner,sun7i-a20-ehci", "generic-ehci";
- reg = <0x01c1c000 0x1000>;
- interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-parent = <&GIC>;
- };
-
- mmc0: mmc@01c0f000 {
- compatible = "allwinner,sun5i-a13-mmc";
- reg = <0x01c0f000 0x1000>;
- interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
- status = "disabled";
- };
-
- sata@01c18000 {
- compatible = "allwinner,sun4i-a10-ahci";
- reg = <0x01c18000 0x1000>;
- interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-parent = <&GIC>;
- status = "disabled";
- };
-
- UART0: serial@01c28000 {
- compatible = "snps,dw-apb-uart";
- reg = <0x01c28000 0x400>;
- reg-shift = <2>;
- interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
- current-speed = <115200>;
- clock-frequency = < 24000000 >;
- };
-
- emac@01c0b000 {
- compatible = "allwinner,sun4i-a10-emac";
- reg = <0x01c0b000 0x1000>;
- interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-parent = <&GIC>;
- status = "disabled";
- };
-
- gmac@01c50000 {
- compatible = "allwinner,sun7i-a20-gmac";
- reg = <0x01c50000 0x10000>;
- interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-parent = <&GIC>;
- snps,pbl = <2>;
- snps,fixed-burst;
- snps,force_sf_dma_mode;
- status = "disabled";
- #address-cells = <1>;
- #size-cells = <0>;
- };
-
- dma: dma-controller@01c02000 {
- compatible = "allwinner,sun4i-a10-dma";
- reg = <0x01c02000 0x1000>;
- interrupts = <27>;
- interrupt-parent = <&GIC>;
- };
-
- codec: codec@01c22c00 {
- compatible = "allwinner,sun7i-a20-codec";
- reg = <0x01c22c00 0x40>;
- interrupts = <30>;
- interrupt-parent = <&GIC>;
- status = "disabled";
- };
- };
-};
-
Index: head/sys/dev/dwc/if_dwc.c
===================================================================
--- head/sys/dev/dwc/if_dwc.c
+++ head/sys/dev/dwc/if_dwc.c
@@ -70,6 +70,11 @@
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
+#ifdef EXT_RESOURCES
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#endif
+
#include "if_dwc_if.h"
#include "gpio_if.h"
#include "miibus_if.h"
@@ -1068,6 +1073,36 @@
return (0);
}
+#ifdef EXT_RESOURCES
+static int
+dwc_clock_init(device_t dev)
+{
+ hwreset_t rst;
+ clk_t clk;
+ int error;
+
+ /* Enable clock */
+ if (clk_get_by_ofw_name(dev, "stmmaceth", &clk) == 0) {
+ error = clk_enable(clk);
+ if (error != 0) {
+ device_printf(dev, "could not enable main clock\n");
+ return (error);
+ }
+ }
+
+ /* De-assert reset */
+ if (hwreset_get_by_ofw_name(dev, "stmmaceth", &rst) == 0) {
+ error = hwreset_deassert(rst);
+ if (error != 0) {
+ device_printf(dev, "could not de-assert reset\n");
+ return (error);
+ }
+ }
+
+ return (0);
+}
+#endif
+
static int
dwc_probe(device_t dev)
{
@@ -1101,6 +1136,11 @@
if (IF_DWC_INIT(dev) != 0)
return (ENXIO);
+#ifdef EXT_RESOURCES
+ if (dwc_clock_init(dev) != 0)
+ return (ENXIO);
+#endif
+
if (bus_alloc_resources(dev, dwc_spec, sc->res)) {
device_printf(dev, "could not allocate resources\n");
return (ENXIO);
Index: head/sys/dev/iicbus/twsi/a10_twsi.c
===================================================================
--- head/sys/dev/iicbus/twsi/a10_twsi.c
+++ head/sys/dev/iicbus/twsi/a10_twsi.c
@@ -48,8 +48,8 @@
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
-#include <arm/allwinner/a10_clk.h>
-#include <arm/allwinner/a31/a31_clk.h>
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
#include "iicbus_if.h"
@@ -63,21 +63,16 @@
#define TWI_EFR 0x1C
#define TWI_LCR 0x20
-#define A10_I2C 1
-#define A31_I2C 2
-
static struct ofw_compat_data compat_data[] = {
- {"allwinner,sun4i-a10-i2c", A10_I2C},
- {"allwinner,sun6i-a31-i2c", A31_I2C},
+ {"allwinner,sun4i-a10-i2c", 1},
+ {"allwinner,sun6i-a31-i2c", 1},
{NULL, 0},
};
static int
a10_twsi_probe(device_t dev)
{
- struct twsi_softc *sc;
- sc = device_get_softc(dev);
if (!ofw_bus_status_okay(dev))
return (ENXIO);
@@ -92,29 +87,31 @@
a10_twsi_attach(device_t dev)
{
struct twsi_softc *sc;
- int clk;
+ clk_t clk;
+ hwreset_t rst;
+ int error;
sc = device_get_softc(dev);
- /* Activate clock */
- switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
-#if defined(SOC_ALLWINNER_A10) || defined(SOC_ALLWINNER_A20)
- case A10_I2C:
- clk = a10_clk_i2c_activate(device_get_unit(dev));
- break;
-#endif
-#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S)
- case A31_I2C:
- clk = a31_clk_i2c_activate(device_get_unit(dev));
- break;
-#endif
- default:
- clk = -1;
+ /* De-assert reset */
+ if (hwreset_get_by_ofw_idx(dev, 0, &rst) == 0) {
+ error = hwreset_deassert(rst);
+ if (error != 0) {
+ device_printf(dev, "could not de-assert reset\n");
+ return (error);
+ }
}
- if (clk != 0) {
- device_printf(dev, "could not activate i2c clock\n");
- return (ENXIO);
+ /* Activate clock */
+ error = clk_get_by_ofw_index(dev, 0, &clk);
+ if (error != 0) {
+ device_printf(dev, "could not find clock\n");
+ return (error);
+ }
+ error = clk_enable(clk);
+ if (error != 0) {
+ device_printf(dev, "could not enable clock\n");
+ return (error);
}
sc->reg_data = TWI_DATA;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Feb 19, 4:37 PM (21 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16724064
Default Alt Text
D5752.diff (226 KB)
Attached To
Mode
D5752: allwinner: Convert to extres clk/hwreset APIs
Attached
Detach File
Event Timeline
Log In to Comment