mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-22 11:17:19 +00:00
Convert Allwinner port to extres clk/hwreset/regulator APIs.
Reviewed by: andrew, gonzo, Emmanuel Vadot <manu@bidouilliste.com> Approved by: gonzo (mentor) Differential Revision: https://reviews.freebsd.org/D5752
This commit is contained in:
parent
15719ec499
commit
6a05f063ed
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=297627
@ -47,7 +47,7 @@ __FBSDID("$FreeBSD$");
|
||||
#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 @@ __FBSDID("$FreeBSD$");
|
||||
#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 @@ ahci_a10_attach(device_t dev)
|
||||
{
|
||||
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 @@ ahci_a10_attach(device_t dev)
|
||||
&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 @@ ahci_a10_attach(device_t dev)
|
||||
* 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
|
||||
|
@ -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);
|
||||
}
|
@ -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_ */
|
@ -50,7 +50,7 @@ __FBSDID("$FreeBSD$");
|
||||
#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 @@ a10codec_attach(device_t dev)
|
||||
{
|
||||
struct a10codec_info *sc;
|
||||
char status[SND_STATUSLEN];
|
||||
clk_t clk_apb, clk_codec;
|
||||
uint32_t val;
|
||||
int error;
|
||||
|
||||
@ -778,6 +779,24 @@ a10codec_attach(device_t dev)
|
||||
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 @@ a10codec_attach(device_t dev)
|
||||
* 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);
|
||||
|
@ -46,7 +46,7 @@ __FBSDID("$FreeBSD$");
|
||||
#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 @@ a10dmac_attach(device_t dev)
|
||||
{
|
||||
struct a10dmac_softc *sc;
|
||||
unsigned int index;
|
||||
clk_t clk;
|
||||
int error;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
@ -123,7 +124,16 @@ a10dmac_attach(device_t dev)
|
||||
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);
|
||||
|
@ -59,8 +59,8 @@ __FBSDID("$FreeBSD$");
|
||||
#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 @@ static device_detach_t a10_ehci_detach;
|
||||
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 @@ a10_ehci_probe(device_t self)
|
||||
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 @@ a10_ehci_attach(device_t self)
|
||||
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 @@ a10_ehci_attach(device_t self)
|
||||
|
||||
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 @@ a10_ehci_attach(device_t self)
|
||||
return (0);
|
||||
|
||||
error:
|
||||
if (aw_sc->clk)
|
||||
clk_release(aw_sc->clk);
|
||||
a10_ehci_detach(self);
|
||||
return (ENXIO);
|
||||
}
|
||||
@ -247,7 +258,8 @@ a10_ehci_attach(device_t self)
|
||||
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_ehci_detach(device_t self)
|
||||
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);
|
||||
}
|
||||
|
@ -54,7 +54,8 @@ __FBSDID("$FreeBSD$");
|
||||
#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 @@ __FBSDID("$FreeBSD$");
|
||||
#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 @@ a10fb_freefb(struct a10fb_softc *sc)
|
||||
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 @@ a10fb_setup_debe(struct a10fb_softc *sc, const struct videomode *mode)
|
||||
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 @@ a10fb_setup_tcon(struct a10fb_softc *sc, const struct videomode *mode)
|
||||
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 @@ a10fb_setup_tcon(struct a10fb_softc *sc, const struct videomode *mode)
|
||||
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 @@ a10fb_configure(struct a10fb_softc *sc, const struct videomode *mode)
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
@ -49,7 +49,7 @@ __FBSDID("$FreeBSD$");
|
||||
#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 @@ __FBSDID("$FreeBSD$");
|
||||
#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 @@ struct a10hdmi_softc {
|
||||
|
||||
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 @@ a10hdmi_attach(device_t dev)
|
||||
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);
|
||||
|
||||
@ -526,6 +557,38 @@ a10hdmi_set_audiomode(device_t dev, const struct videomode *mode)
|
||||
HDMI_WRITE(sc, HDMI_AUD_CTRL, AUD_CTRL_EN);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@ -543,9 +606,11 @@ a10hdmi_set_videomode(device_t dev, const struct videomode *mode)
|
||||
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));
|
||||
|
@ -49,9 +49,9 @@ __FBSDID("$FreeBSD$");
|
||||
#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 @@ __FBSDID("$FreeBSD$");
|
||||
#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 @@ struct a10_mmc_softc {
|
||||
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 @@ a10_mmc_attach(device_t dev)
|
||||
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 @@ a10_mmc_attach(device_t dev)
|
||||
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 @@ a10_mmc_attach(device_t dev)
|
||||
break;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
/* 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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
/* Activate the module clock. */
|
||||
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;
|
||||
}
|
||||
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 @@ a10_mmc_attach(device_t dev)
|
||||
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 @@ a10_mmc_update_ios(device_t bus, device_t child)
|
||||
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;
|
||||
|
@ -41,8 +41,7 @@ __FBSDID("$FreeBSD$");
|
||||
#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 @@ a20_if_dwc_probe(device_t dev)
|
||||
static int
|
||||
a20_if_dwc_init(device_t dev)
|
||||
{
|
||||
int clk;
|
||||
const char *tx_parent_name;
|
||||
char *phy_type;
|
||||
clk_t clk_tx, clk_tx_parent;
|
||||
phandle_t node;
|
||||
int error;
|
||||
|
||||
/* 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;
|
||||
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);
|
||||
}
|
||||
if (clk != 0) {
|
||||
device_printf(dev, "could not activate gmac module\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
@ -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);
|
||||
}
|
@ -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_ */
|
224
sys/arm/allwinner/aw_ccu.c
Normal file
224
sys/arm/allwinner/aw_ccu.c
Normal file
@ -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);
|
163
sys/arm/allwinner/aw_reset.c
Normal file
163
sys/arm/allwinner/aw_reset.c
Normal file
@ -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);
|
@ -44,10 +44,11 @@ __FBSDID("$FreeBSD$");
|
||||
#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 },
|
||||
@ -57,91 +58,47 @@ static struct ofw_compat_data compat_data[] = {
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
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;
|
||||
regulator_t reg;
|
||||
hwreset_t rst;
|
||||
clk_t clk;
|
||||
|
||||
node = ofw_bus_get_node(dev);
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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_id_det-gpio", off);
|
||||
error = awusbphy_gpio_set(dev, node, pname);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
snprintf(pname, sizeof(pname), "usb%d_vbus_det-gpio", off);
|
||||
error = awusbphy_gpio_set(dev, node, pname);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
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);
|
||||
}
|
||||
|
308
sys/arm/allwinner/clk/aw_ahbclk.c
Normal file
308
sys/arm/allwinner/clk/aw_ahbclk.c
Normal file
@ -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);
|
283
sys/arm/allwinner/clk/aw_apbclk.c
Normal file
283
sys/arm/allwinner/clk/aw_apbclk.c
Normal file
@ -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);
|
201
sys/arm/allwinner/clk/aw_axiclk.c
Normal file
201
sys/arm/allwinner/clk/aw_axiclk.c
Normal file
@ -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);
|
164
sys/arm/allwinner/clk/aw_codecclk.c
Normal file
164
sys/arm/allwinner/clk/aw_codecclk.c
Normal file
@ -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);
|
161
sys/arm/allwinner/clk/aw_cpuclk.c
Normal file
161
sys/arm/allwinner/clk/aw_cpuclk.c
Normal file
@ -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);
|
351
sys/arm/allwinner/clk/aw_debeclk.c
Normal file
351
sys/arm/allwinner/clk/aw_debeclk.c
Normal file
@ -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);
|
198
sys/arm/allwinner/clk/aw_gate.c
Normal file
198
sys/arm/allwinner/clk/aw_gate.c
Normal file
@ -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);
|
259
sys/arm/allwinner/clk/aw_gmacclk.c
Normal file
259
sys/arm/allwinner/clk/aw_gmacclk.c
Normal file
@ -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);
|
315
sys/arm/allwinner/clk/aw_hdmiclk.c
Normal file
315
sys/arm/allwinner/clk/aw_hdmiclk.c
Normal file
@ -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);
|
560
sys/arm/allwinner/clk/aw_lcdclk.c
Normal file
560
sys/arm/allwinner/clk/aw_lcdclk.c
Normal file
@ -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);
|
351
sys/arm/allwinner/clk/aw_mmcclk.c
Normal file
351
sys/arm/allwinner/clk/aw_mmcclk.c
Normal file
@ -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);
|
318
sys/arm/allwinner/clk/aw_modclk.c
Normal file
318
sys/arm/allwinner/clk/aw_modclk.c
Normal file
@ -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);
|
132
sys/arm/allwinner/clk/aw_oscclk.c
Normal file
132
sys/arm/allwinner/clk/aw_oscclk.c
Normal file
@ -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);
|
757
sys/arm/allwinner/clk/aw_pll.c
Normal file
757
sys/arm/allwinner/clk/aw_pll.c
Normal file
@ -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);
|
246
sys/arm/allwinner/clk/aw_usbclk.c
Normal file
246
sys/arm/allwinner/clk/aw_usbclk.c
Normal file
@ -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);
|
@ -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_fb.c optional vt
|
||||
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
|
||||
|
@ -78,11 +78,12 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#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 emac_softc {
|
||||
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_shutdown(device_t);
|
||||
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 @@ static int sysctl_hw_emac_proc_limit(SYSCTL_HANDLER_ARGS);
|
||||
#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 @@ emac_detach(device_t dev)
|
||||
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 @@ emac_attach(device_t dev)
|
||||
}
|
||||
}
|
||||
/* 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);
|
||||
|
@ -51,6 +51,13 @@ options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed
|
||||
#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
|
||||
|
@ -55,6 +55,13 @@ options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed
|
||||
#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
|
||||
|
||||
|
@ -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 {
|
||||
soc@01c00000 {
|
||||
hdmi@01c16000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usb2: usb@01c1c000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
UART0: serial@01c28000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
mmc0: mmc@01c0f000 {
|
||||
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";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
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>;
|
||||
};
|
||||
};
|
||||
|
||||
chosen {
|
||||
bootargs = "-v";
|
||||
stdin = "UART0";
|
||||
stdout = "UART0";
|
||||
};
|
||||
};
|
||||
|
||||
&mmc0_pins_a {
|
||||
allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
|
||||
};
|
||||
|
@ -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"
|
||||
|
@ -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";
|
||||
};
|
||||
|
@ -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";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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 >;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -70,6 +70,11 @@ __FBSDID("$FreeBSD$");
|
||||
#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 @@ dwc_reset(device_t dev)
|
||||
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 @@ dwc_attach(device_t dev)
|
||||
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);
|
||||
|
@ -48,8 +48,8 @@ __FBSDID("$FreeBSD$");
|
||||
#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 @@ __FBSDID("$FreeBSD$");
|
||||
#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 @@ static int
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user