mirror of
https://github.com/opnsense/src.git
synced 2026-06-04 14:26:03 -04:00
allwinner: clk: Garbage collect old clock implementation
The old clocks are disconneted from the build since r337344. Remove all those pseudo drivers. The only one remaining is for gmac (the ethernet controller) so move it to sys/arm/allwinner. While here remove a83t support from gmacclk as it is unneeded since r326114. MFC after: 1 month
This commit is contained in:
parent
ba57dad4b0
commit
b800eb9d17
19 changed files with 13 additions and 5976 deletions
|
|
@ -53,38 +53,18 @@ __FBSDID("$FreeBSD$");
|
|||
#define CCU_BASE 0x01c20000
|
||||
#define CCU_SIZE 0x400
|
||||
|
||||
#define PRCM_BASE 0x01f01400
|
||||
#define PRCM_SIZE 0x200
|
||||
|
||||
#define SYSCTRL_BASE 0x01c00000
|
||||
#define SYSCTRL_SIZE 0x34
|
||||
|
||||
struct aw_ccu_softc {
|
||||
struct simplebus_softc sc;
|
||||
bus_space_tag_t bst;
|
||||
bus_space_handle_t ccu_bsh;
|
||||
bus_space_handle_t prcm_bsh;
|
||||
bus_space_handle_t sysctrl_bsh;
|
||||
bus_space_handle_t bsh;
|
||||
struct mtx mtx;
|
||||
int flags;
|
||||
};
|
||||
|
||||
#define CLOCK_CCU (1 << 0)
|
||||
#define CLOCK_PRCM (1 << 1)
|
||||
#define CLOCK_SYSCTRL (1 << 2)
|
||||
|
||||
static struct ofw_compat_data compat_data[] = {
|
||||
{ "allwinner,sun4i-a10", CLOCK_CCU },
|
||||
{ "allwinner,sun5i-a13", CLOCK_CCU },
|
||||
{ "allwinner,sun7i-a20", CLOCK_CCU },
|
||||
{ "allwinner,sun6i-a31", CLOCK_CCU },
|
||||
{ "allwinner,sun6i-a31s", CLOCK_CCU },
|
||||
{ "allwinner,sun50i-a64", CLOCK_CCU },
|
||||
{ "allwinner,sun50i-h5", CLOCK_CCU },
|
||||
{ "allwinner,sun8i-a33", CLOCK_CCU },
|
||||
{ "allwinner,sun8i-a83t", CLOCK_CCU|CLOCK_PRCM|CLOCK_SYSCTRL },
|
||||
{ "allwinner,sun8i-h2-plus", CLOCK_CCU|CLOCK_PRCM },
|
||||
{ "allwinner,sun8i-h3", CLOCK_CCU|CLOCK_PRCM },
|
||||
{ "allwinner,sun7i-a20", 1 },
|
||||
{ "allwinner,sun6i-a31", 1 },
|
||||
{ "allwinner,sun6i-a31s", 1 },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
|
|
@ -92,22 +72,9 @@ static int
|
|||
aw_ccu_check_addr(struct aw_ccu_softc *sc, bus_addr_t addr,
|
||||
bus_space_handle_t *pbsh, bus_size_t *poff)
|
||||
{
|
||||
if (addr >= CCU_BASE && addr < (CCU_BASE + CCU_SIZE) &&
|
||||
(sc->flags & CLOCK_CCU) != 0) {
|
||||
if (addr >= CCU_BASE && addr < (CCU_BASE + CCU_SIZE)) {
|
||||
*poff = addr - CCU_BASE;
|
||||
*pbsh = sc->ccu_bsh;
|
||||
return (0);
|
||||
}
|
||||
if (addr >= PRCM_BASE && addr < (PRCM_BASE + PRCM_SIZE) &&
|
||||
(sc->flags & CLOCK_PRCM) != 0) {
|
||||
*poff = addr - PRCM_BASE;
|
||||
*pbsh = sc->prcm_bsh;
|
||||
return (0);
|
||||
}
|
||||
if (addr >= SYSCTRL_BASE && addr < (SYSCTRL_BASE + SYSCTRL_SIZE) &&
|
||||
(sc->flags & CLOCK_SYSCTRL) != 0) {
|
||||
*poff = addr - SYSCTRL_BASE;
|
||||
*pbsh = sc->sysctrl_bsh;
|
||||
*pbsh = sc->bsh;
|
||||
return (0);
|
||||
}
|
||||
return (EINVAL);
|
||||
|
|
@ -241,29 +208,11 @@ aw_ccu_attach(device_t dev)
|
|||
* properties.
|
||||
*/
|
||||
sc->bst = bus_get_bus_tag(dev);
|
||||
if (sc->flags & CLOCK_CCU) {
|
||||
error = bus_space_map(sc->bst, CCU_BASE, CCU_SIZE, 0,
|
||||
&sc->ccu_bsh);
|
||||
if (error != 0) {
|
||||
device_printf(dev, "couldn't map CCU: %d\n", error);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
if (sc->flags & CLOCK_PRCM) {
|
||||
error = bus_space_map(sc->bst, PRCM_BASE, PRCM_SIZE, 0,
|
||||
&sc->prcm_bsh);
|
||||
if (error != 0) {
|
||||
device_printf(dev, "couldn't map PRCM: %d\n", error);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
if (sc->flags & CLOCK_SYSCTRL) {
|
||||
error = bus_space_map(sc->bst, SYSCTRL_BASE, SYSCTRL_SIZE, 0,
|
||||
&sc->sysctrl_bsh);
|
||||
if (error != 0) {
|
||||
device_printf(dev, "couldn't map SYSCTRL: %d\n", error);
|
||||
return (error);
|
||||
}
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -77,21 +77,14 @@ __FBSDID("$FreeBSD$");
|
|||
#define CLK_IDX_RGMII 1
|
||||
#define CLK_IDX_COUNT 2
|
||||
|
||||
enum aw_gmacclk_type {
|
||||
GMACCLK_A20 = 1,
|
||||
GMACCLK_A83T,
|
||||
};
|
||||
|
||||
static struct ofw_compat_data compat_data[] = {
|
||||
{ "allwinner,sun7i-a20-gmac-clk", GMACCLK_A20 },
|
||||
{ "allwinner,sun8i-a83t-emac-clk", GMACCLK_A83T },
|
||||
{ "allwinner,sun7i-a20-gmac-clk", 1 },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
struct aw_gmacclk_sc {
|
||||
device_t clkdev;
|
||||
bus_addr_t reg;
|
||||
enum aw_gmacclk_type type;
|
||||
|
||||
int rx_delay;
|
||||
int tx_delay;
|
||||
|
|
@ -159,20 +152,6 @@ aw_gmacclk_set_mux(struct clknode *clk, int index)
|
|||
val &= ~(GMAC_CLK_SRC | GMAC_CLK_PIT);
|
||||
val |= (clk_src << GMAC_CLK_SRC_SHIFT);
|
||||
val |= (pit << GMAC_CLK_PIT_SHIFT);
|
||||
if (sc->type == GMACCLK_A83T) {
|
||||
val &= ~EMAC_TXC_DIV_CFG;
|
||||
val |= (txc_div << EMAC_TXC_DIV_CFG_SHIFT);
|
||||
val &= ~EMAC_PHY_SELECT;
|
||||
val |= (EMAC_PHY_SELECT_EXT << EMAC_PHY_SELECT_SHIFT);
|
||||
if (sc->tx_delay >= 0) {
|
||||
val &= ~EMAC_ETXDC;
|
||||
val |= (sc->tx_delay << EMAC_ETXDC_SHIFT);
|
||||
}
|
||||
if (sc->rx_delay >= 0) {
|
||||
val &= ~EMAC_ERXDC;
|
||||
val |= (sc->rx_delay << EMAC_ERXDC_SHIFT);
|
||||
}
|
||||
}
|
||||
GMACCLK_WRITE(sc, val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
|
||||
|
|
@ -260,7 +239,6 @@ aw_gmacclk_attach(device_t dev)
|
|||
sc = clknode_get_softc(clk);
|
||||
sc->reg = paddr;
|
||||
sc->clkdev = device_get_parent(dev);
|
||||
sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
|
||||
sc->tx_delay = sc->rx_delay = -1;
|
||||
OF_getencprop(node, "tx-delay", &sc->tx_delay, sizeof(sc->tx_delay));
|
||||
OF_getencprop(node, "rx-delay", &sc->rx_delay, sizeof(sc->rx_delay));
|
||||
|
|
@ -1,385 +0,0 @@
|
|||
/*-
|
||||
* 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
|
||||
|
||||
#define A83T_AHB1_CLK_SRC_SEL (0x3 << 12)
|
||||
#define A83T_AHB1_CLK_SRC_SEL_ISPLL(x) ((x) & 0x2)
|
||||
#define A83T_AHB1_CLK_SRC_SEL_MAX 3
|
||||
#define A83T_AHB1_CLK_SRC_SEL_SHIFT 12
|
||||
#define A83T_AHB1_PRE_DIV (0x3 << 6)
|
||||
#define A83T_AHB1_PRE_DIV_SHIFT 6
|
||||
#define A83T_AHB1_CLK_DIV_RATIO (0x3 << 4)
|
||||
#define A83T_AHB1_CLK_DIV_RATIO_SHIFT 4
|
||||
|
||||
#define H3_AHB2_CLK_CFG (0x3 << 0)
|
||||
#define H3_AHB2_CLK_CFG_SHIFT 0
|
||||
#define H3_AHB2_CLK_CFG_AHB1 0
|
||||
#define H3_AHB2_CLK_CFG_PLL_PERIPH_DIV2 1
|
||||
#define H3_AHB2_CLK_CFG_MAX 1
|
||||
|
||||
enum aw_ahbclk_type {
|
||||
AW_A10_AHB = 1,
|
||||
AW_A13_AHB,
|
||||
AW_A31_AHB1,
|
||||
AW_A83T_AHB1,
|
||||
AW_H3_AHB2,
|
||||
};
|
||||
|
||||
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 },
|
||||
{ "allwinner,sun8i-a83t-ahb1-clk", AW_A83T_AHB1 },
|
||||
{ "allwinner,sun8i-h3-ahb2-clk", AW_H3_AHB2 },
|
||||
{ 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;
|
||||
case AW_A83T_AHB1:
|
||||
DEVICE_LOCK(sc);
|
||||
AHBCLK_READ(sc, &val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
index = (val & A83T_AHB1_CLK_SRC_SEL) >>
|
||||
A83T_AHB1_CLK_SRC_SEL_SHIFT;
|
||||
break;
|
||||
case AW_H3_AHB2:
|
||||
/* Set source to PLL_PERIPH/2 */
|
||||
index = H3_AHB2_CLK_CFG_PLL_PERIPH_DIV2;
|
||||
DEVICE_LOCK(sc);
|
||||
AHBCLK_READ(sc, &val);
|
||||
val &= ~H3_AHB2_CLK_CFG;
|
||||
val |= (index << H3_AHB2_CLK_CFG_SHIFT);
|
||||
AHBCLK_WRITE(sc, val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
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);
|
||||
|
||||
switch (sc->type) {
|
||||
case AW_A31_AHB1:
|
||||
div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >>
|
||||
A10_AHB_CLK_DIV_RATIO_SHIFT);
|
||||
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;
|
||||
case AW_A83T_AHB1:
|
||||
div = 1 << ((val & A83T_AHB1_CLK_DIV_RATIO) >>
|
||||
A83T_AHB1_CLK_DIV_RATIO_SHIFT);
|
||||
src_sel = (val & A83T_AHB1_CLK_SRC_SEL) >>
|
||||
A83T_AHB1_CLK_SRC_SEL_SHIFT;
|
||||
if (A83T_AHB1_CLK_SRC_SEL_ISPLL(src_sel))
|
||||
pre_div = ((val & A83T_AHB1_PRE_DIV) >>
|
||||
A83T_AHB1_PRE_DIV_SHIFT) + 1;
|
||||
else
|
||||
pre_div = 1;
|
||||
break;
|
||||
case AW_H3_AHB2:
|
||||
div = pre_div = 1;
|
||||
break;
|
||||
default:
|
||||
div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >>
|
||||
A10_AHB_CLK_DIV_RATIO_SHIFT);
|
||||
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;
|
||||
case AW_A83T_AHB1:
|
||||
if (index < 0 || index > A83T_AHB1_CLK_SRC_SEL_MAX)
|
||||
return (ERANGE);
|
||||
DEVICE_LOCK(sc);
|
||||
AHBCLK_READ(sc, &val);
|
||||
val &= ~A83T_AHB1_CLK_SRC_SEL;
|
||||
val |= (index << A83T_AHB1_CLK_SRC_SEL_SHIFT);
|
||||
AHBCLK_WRITE(sc, val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
break;
|
||||
case AW_H3_AHB2:
|
||||
if (index < 0 || index > H3_AHB2_CLK_CFG)
|
||||
return (ERANGE);
|
||||
DEVICE_LOCK(sc);
|
||||
AHBCLK_READ(sc, &val);
|
||||
val &= ~H3_AHB2_CLK_CFG;
|
||||
val |= (index << H3_AHB2_CLK_CFG_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, 0, 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;
|
||||
}
|
||||
|
||||
error = clk_set_assigned(dev, node);
|
||||
if (error != 0 && error != ENOENT) {
|
||||
device_printf(dev, "cannot set assigned parents: %d\n", error);
|
||||
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);
|
||||
|
|
@ -1,307 +0,0 @@
|
|||
/*-
|
||||
* 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 A10_APB0_CLK_RATIO (0x3 << 8)
|
||||
#define A10_APB0_CLK_RATIO_SHIFT 8
|
||||
#define A10_APB1_CLK_SRC_SEL (0x3 << 24)
|
||||
#define A10_APB1_CLK_SRC_SEL_SHIFT 24
|
||||
#define A10_APB1_CLK_SRC_SEL_MAX 0x3
|
||||
#define A10_APB1_CLK_RAT_N (0x3 << 16)
|
||||
#define A10_APB1_CLK_RAT_N_SHIFT 16
|
||||
#define A10_APB1_CLK_RAT_M (0x1f << 0)
|
||||
#define A10_APB1_CLK_RAT_M_SHIFT 0
|
||||
#define A23_APB0_CLK_RATIO (0x3 << 0)
|
||||
#define A23_APB0_CLK_RATIO_SHIFT 0
|
||||
#define A83T_APB1_CLK_RATIO (0x3 << 8)
|
||||
#define A83T_APB1_CLK_RATIO_SHIFT 8
|
||||
|
||||
enum aw_apbclk_type {
|
||||
AW_A10_APB0 = 1,
|
||||
AW_A10_APB1,
|
||||
AW_A23_APB0,
|
||||
AW_A83T_APB1,
|
||||
};
|
||||
|
||||
static struct ofw_compat_data compat_data[] = {
|
||||
{ "allwinner,sun4i-a10-apb0-clk", AW_A10_APB0 },
|
||||
{ "allwinner,sun4i-a10-apb1-clk", AW_A10_APB1 },
|
||||
{ "allwinner,sun8i-a23-apb0-clk", AW_A23_APB0 },
|
||||
{ "allwinner,sun8i-a83t-apb1-clk", AW_A83T_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:
|
||||
case AW_A23_APB0:
|
||||
case AW_A83T_APB1:
|
||||
index = 0;
|
||||
break;
|
||||
case AW_A10_APB1:
|
||||
DEVICE_LOCK(sc);
|
||||
APBCLK_READ(sc, &val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
index = (val & A10_APB1_CLK_SRC_SEL) >>
|
||||
A10_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 & A10_APB0_CLK_RATIO) >>
|
||||
A10_APB0_CLK_RATIO_SHIFT);
|
||||
if (div == 1)
|
||||
div = 2;
|
||||
*freq = *freq / div;
|
||||
break;
|
||||
case AW_A10_APB1:
|
||||
n = 1 << ((val & A10_APB1_CLK_RAT_N) >>
|
||||
A10_APB1_CLK_RAT_N_SHIFT);
|
||||
m = ((val & A10_APB1_CLK_RAT_N) >>
|
||||
A10_APB1_CLK_RAT_M_SHIFT) + 1;
|
||||
*freq = *freq / n / m;
|
||||
break;
|
||||
case AW_A23_APB0:
|
||||
div = 1 << ((val & A23_APB0_CLK_RATIO) >>
|
||||
A23_APB0_CLK_RATIO_SHIFT);
|
||||
*freq = *freq / div;
|
||||
break;
|
||||
case AW_A83T_APB1:
|
||||
div = ((val & A83T_APB1_CLK_RATIO) >>
|
||||
A83T_APB1_CLK_RATIO_SHIFT) + 1;
|
||||
*freq = *freq / div;
|
||||
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 > A10_APB1_CLK_SRC_SEL_MAX)
|
||||
return (ERANGE);
|
||||
|
||||
DEVICE_LOCK(sc);
|
||||
APBCLK_READ(sc, &val);
|
||||
val &= ~A10_APB1_CLK_SRC_SEL;
|
||||
val |= (index << A10_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, 0, 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);
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
/*-
|
||||
* 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 },
|
||||
{ "allwinner,sun8i-a23-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, 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);
|
||||
|
|
@ -1,164 +0,0 @@
|
|||
/*-
|
||||
* 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, 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);
|
||||
|
|
@ -1,197 +0,0 @@
|
|||
/*-
|
||||
* 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 A10_CPU_CLK_SRC_SEL_WIDTH 2
|
||||
#define A10_CPU_CLK_SRC_SEL_SHIFT 16
|
||||
|
||||
#define A83T_Cx_CLK_SRC_SEL_WIDTH 1
|
||||
#define A83T_C0_CLK_SRC_SEL_SHIFT 12
|
||||
#define A83T_C1_CLK_SRC_SEL_SHIFT 28
|
||||
|
||||
struct aw_cpuclk_config {
|
||||
u_int width;
|
||||
u_int shift;
|
||||
};
|
||||
|
||||
static struct aw_cpuclk_config a10_config = {
|
||||
.width = A10_CPU_CLK_SRC_SEL_WIDTH,
|
||||
.shift = A10_CPU_CLK_SRC_SEL_SHIFT,
|
||||
};
|
||||
|
||||
static struct aw_cpuclk_config a83t_c0_config = {
|
||||
.width = A83T_Cx_CLK_SRC_SEL_WIDTH,
|
||||
.shift = A83T_C0_CLK_SRC_SEL_SHIFT,
|
||||
};
|
||||
|
||||
static struct aw_cpuclk_config a83t_c1_config = {
|
||||
.width = A83T_Cx_CLK_SRC_SEL_WIDTH,
|
||||
.shift = A83T_C1_CLK_SRC_SEL_SHIFT,
|
||||
};
|
||||
|
||||
static struct ofw_compat_data compat_data[] = {
|
||||
{ "allwinner,sun4i-a10-cpu-clk", (uintptr_t)&a10_config },
|
||||
{ "allwinner,sun8i-a83t-c0cpu-clk", (uintptr_t)&a83t_c0_config },
|
||||
{ "allwinner,sun8i-a83t-c1cpu-clk", (uintptr_t)&a83t_c1_config },
|
||||
{ NULL, (uintptr_t)NULL }
|
||||
};
|
||||
|
||||
#define CPUCLK_CONF(d) \
|
||||
(void *)ofw_bus_search_compatible((d), compat_data)->ocd_data
|
||||
|
||||
static int
|
||||
aw_cpuclk_probe(device_t dev)
|
||||
{
|
||||
if (!ofw_bus_status_okay(dev))
|
||||
return (ENXIO);
|
||||
|
||||
if (CPUCLK_CONF(dev) == NULL)
|
||||
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;
|
||||
struct aw_cpuclk_config *conf;
|
||||
bus_addr_t paddr;
|
||||
bus_size_t psize;
|
||||
phandle_t node;
|
||||
int error, ncells, i;
|
||||
clk_t clk;
|
||||
|
||||
node = ofw_bus_get_node(dev);
|
||||
conf = CPUCLK_CONF(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, 0, 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 = conf->shift;
|
||||
def.width = conf->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;
|
||||
}
|
||||
|
||||
OF_prop_free(__DECONST(char *, def.clkdef.parent_names));
|
||||
OF_prop_free(__DECONST(char *, def.clkdef.name));
|
||||
|
||||
if (bootverbose)
|
||||
clkdom_dump(clkdom);
|
||||
|
||||
return (0);
|
||||
|
||||
fail:
|
||||
OF_prop_free(__DECONST(char *, def.clkdef.name));
|
||||
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);
|
||||
|
|
@ -1,320 +0,0 @@
|
|||
/*-
|
||||
* 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 CPUS 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 A80_CPUS_CLK_SRC_SEL (0x3 << 16)
|
||||
#define A80_CPUS_CLK_SRC_SEL_SHIFT 16
|
||||
#define A80_CPUS_CLK_SRC_SEL_X32KI 0
|
||||
#define A80_CPUS_CLK_SRC_SEL_OSC24M 1
|
||||
#define A80_CPUS_CLK_SRC_SEL_PLL_PERIPH 2
|
||||
#define A80_CPUS_CLK_SRC_SEL_PLL_AUDIO 3
|
||||
#define A80_CPUS_POST_DIV (0x1f << 8)
|
||||
#define A80_CPUS_POST_DIV_SHIFT 8
|
||||
#define A80_CPUS_CLK_RATIO (0x3 << 4)
|
||||
#define A80_CPUS_CLK_RATIO_SHIFT 4
|
||||
|
||||
#define A83T_CPUS_CLK_SRC_SEL (0x3 << 16)
|
||||
#define A83T_CPUS_CLK_SRC_SEL_SHIFT 16
|
||||
#define A83T_CPUS_CLK_SRC_SEL_X32KI 0
|
||||
#define A83T_CPUS_CLK_SRC_SEL_OSC24M 1
|
||||
#define A83T_CPUS_CLK_SRC_SEL_PLL_PERIPH 2
|
||||
#define A83T_CPUS_CLK_SRC_SEL_INTERNAL_OSC 3
|
||||
#define A83T_CPUS_POST_DIV (0x1f << 8)
|
||||
#define A83T_CPUS_POST_DIV_SHIFT 8
|
||||
#define A83T_CPUS_CLK_RATIO (0x3 << 4)
|
||||
#define A83T_CPUS_CLK_RATIO_SHIFT 4
|
||||
|
||||
enum aw_cpusclk_type {
|
||||
AW_A80_CPUS = 1,
|
||||
AW_A83T_CPUS,
|
||||
};
|
||||
|
||||
static struct ofw_compat_data compat_data[] = {
|
||||
{ "allwinner,sun9i-a80-cpus-clk", AW_A80_CPUS },
|
||||
{ "allwinner,sun8i-a83t-cpus-clk", AW_A83T_CPUS },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
struct aw_cpusclk_sc {
|
||||
device_t clkdev;
|
||||
bus_addr_t reg;
|
||||
enum aw_cpusclk_type type;
|
||||
};
|
||||
|
||||
#define CPUSCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
|
||||
#define CPUSCLK_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_cpusclk_init(struct clknode *clk, device_t dev)
|
||||
{
|
||||
struct aw_cpusclk_sc *sc;
|
||||
uint32_t val, mask, shift, index;
|
||||
|
||||
sc = clknode_get_softc(clk);
|
||||
|
||||
switch (sc->type) {
|
||||
case AW_A80_CPUS:
|
||||
mask = A80_CPUS_CLK_SRC_SEL;
|
||||
shift = A80_CPUS_CLK_SRC_SEL_SHIFT;
|
||||
break;
|
||||
case AW_A83T_CPUS:
|
||||
mask = A83T_CPUS_CLK_SRC_SEL;
|
||||
shift = A83T_CPUS_CLK_SRC_SEL_SHIFT;
|
||||
break;
|
||||
default:
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
DEVICE_LOCK(sc);
|
||||
CPUSCLK_READ(sc, &val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
index = (val & mask) >> shift;
|
||||
|
||||
clknode_init_parent_idx(clk, index);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
aw_cpusclk_recalc_freq(struct clknode *clk, uint64_t *freq)
|
||||
{
|
||||
struct aw_cpusclk_sc *sc;
|
||||
uint32_t val, src_sel, post_div, clk_ratio;
|
||||
|
||||
sc = clknode_get_softc(clk);
|
||||
|
||||
DEVICE_LOCK(sc);
|
||||
CPUSCLK_READ(sc, &val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
|
||||
switch (sc->type) {
|
||||
case AW_A80_CPUS:
|
||||
src_sel = (val & A80_CPUS_CLK_SRC_SEL) >>
|
||||
A80_CPUS_CLK_SRC_SEL_SHIFT;
|
||||
post_div = ((val & A80_CPUS_POST_DIV) >>
|
||||
A80_CPUS_POST_DIV_SHIFT) + 1;
|
||||
clk_ratio = ((val & A80_CPUS_CLK_RATIO) >>
|
||||
A80_CPUS_CLK_RATIO_SHIFT) + 1;
|
||||
if (src_sel == A80_CPUS_CLK_SRC_SEL_PLL_PERIPH)
|
||||
*freq = *freq / post_div / clk_ratio;
|
||||
else
|
||||
*freq = *freq / clk_ratio;
|
||||
break;
|
||||
case AW_A83T_CPUS:
|
||||
src_sel = (val & A83T_CPUS_CLK_SRC_SEL) >>
|
||||
A83T_CPUS_CLK_SRC_SEL_SHIFT;
|
||||
post_div = ((val & A83T_CPUS_POST_DIV) >>
|
||||
A83T_CPUS_POST_DIV_SHIFT) + 1;
|
||||
clk_ratio = 1 << ((val & A83T_CPUS_CLK_RATIO) >>
|
||||
A83T_CPUS_CLK_RATIO_SHIFT);
|
||||
if (src_sel == A83T_CPUS_CLK_SRC_SEL_PLL_PERIPH)
|
||||
*freq = *freq / post_div / clk_ratio;
|
||||
else
|
||||
*freq = *freq / clk_ratio;
|
||||
break;
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
aw_cpusclk_set_mux(struct clknode *clk, int index)
|
||||
{
|
||||
struct aw_cpusclk_sc *sc;
|
||||
uint32_t mask, shift, val;
|
||||
|
||||
sc = clknode_get_softc(clk);
|
||||
|
||||
switch (sc->type) {
|
||||
case AW_A80_CPUS:
|
||||
mask = A80_CPUS_CLK_SRC_SEL;
|
||||
shift = A80_CPUS_CLK_SRC_SEL_SHIFT;
|
||||
break;
|
||||
case AW_A83T_CPUS:
|
||||
mask = A83T_CPUS_CLK_SRC_SEL;
|
||||
shift = A83T_CPUS_CLK_SRC_SEL_SHIFT;
|
||||
break;
|
||||
default:
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
DEVICE_LOCK(sc);
|
||||
CPUSCLK_READ(sc, &val);
|
||||
val &= ~mask;
|
||||
val |= (index << shift);
|
||||
CPUSCLK_WRITE(sc, val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static clknode_method_t aw_cpusclk_clknode_methods[] = {
|
||||
/* Device interface */
|
||||
CLKNODEMETHOD(clknode_init, aw_cpusclk_init),
|
||||
CLKNODEMETHOD(clknode_recalc_freq, aw_cpusclk_recalc_freq),
|
||||
CLKNODEMETHOD(clknode_set_mux, aw_cpusclk_set_mux),
|
||||
CLKNODEMETHOD_END
|
||||
};
|
||||
DEFINE_CLASS_1(aw_cpusclk_clknode, aw_cpusclk_clknode_class,
|
||||
aw_cpusclk_clknode_methods, sizeof(struct aw_cpusclk_sc), clknode_class);
|
||||
|
||||
static int
|
||||
aw_cpusclk_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 CPUS Clock");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
aw_cpusclk_attach(device_t dev)
|
||||
{
|
||||
struct clknode_init_def def;
|
||||
struct aw_cpusclk_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, 0, 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_cpusclk_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_cpusclk_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, aw_cpusclk_probe),
|
||||
DEVMETHOD(device_attach, aw_cpusclk_attach),
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
static driver_t aw_cpusclk_driver = {
|
||||
"aw_cpusclk",
|
||||
aw_cpusclk_methods,
|
||||
0
|
||||
};
|
||||
|
||||
static devclass_t aw_cpusclk_devclass;
|
||||
|
||||
EARLY_DRIVER_MODULE(aw_cpusclk, simplebus, aw_cpusclk_driver,
|
||||
aw_cpusclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
|
||||
|
|
@ -1,354 +0,0 @@
|
|||
/*-
|
||||
* 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;
|
||||
|
||||
*fout = fin / (m + 1);
|
||||
*stop = 1;
|
||||
|
||||
if ((flags & CLK_SET_DRYRUN) != 0)
|
||||
return (0);
|
||||
|
||||
DEVICE_LOCK(sc);
|
||||
DEBECLK_READ(sc, &val);
|
||||
val &= ~CLK_RATIO_M;
|
||||
val |= (m << CLK_RATIO_M_SHIFT);
|
||||
DEBECLK_WRITE(sc, val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
|
||||
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, 0, 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);
|
||||
|
|
@ -1,235 +0,0 @@
|
|||
/*-
|
||||
* 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,sun5i-a13-ahb-gates-clk",
|
||||
(uintptr_t)"Allwinner AHB Clock Gates" },
|
||||
{ "allwinner,sun5i-a13-apb0-gates-clk",
|
||||
(uintptr_t)"Allwinner APB0 Clock Gates" },
|
||||
{ "allwinner,sun5i-a13-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" },
|
||||
|
||||
{ "allwinner,sun8i-a23-apb1-gates-clk",
|
||||
(uintptr_t)"Allwinner APB1 Clock Gates" },
|
||||
{ "allwinner,sun8i-a23-apb2-gates-clk",
|
||||
(uintptr_t)"Allwinner APB2 Clock Gates" },
|
||||
|
||||
{ "allwinner,sun8i-a83t-bus-gates-clk",
|
||||
(uintptr_t)"Allwinner Bus Clock Gates" },
|
||||
{ "allwinner,sun8i-a83t-apb0-gates-clk",
|
||||
(uintptr_t)"Allwinner APB0 Clock Gates" },
|
||||
|
||||
{ "allwinner,sun8i-h3-bus-gates-clk",
|
||||
(uintptr_t)"Allwinner Bus Clock Gates" },
|
||||
{ "allwinner,sun8i-h3-apb0-gates-clk",
|
||||
(uintptr_t)"Allwinner APB0 Clock Gates" },
|
||||
|
||||
{ "allwinner,sun9i-a80-apbs-gates-clk",
|
||||
(uintptr_t)"Allwinner APBS Clock Gates" },
|
||||
|
||||
{ "allwinner,sunxi-multi-bus-gates-clk",
|
||||
(uintptr_t)"Allwinner Multi Bus 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_add(device_t dev, struct clkdom *clkdom, phandle_t node,
|
||||
bus_addr_t paddr)
|
||||
{
|
||||
const char **names;
|
||||
uint32_t *indices;
|
||||
clk_t clk_parent;
|
||||
int index, nout, error;
|
||||
|
||||
indices = NULL;
|
||||
|
||||
nout = clk_parse_ofw_out_names(dev, node, &names, &indices);
|
||||
if (nout == 0) {
|
||||
device_printf(dev, "no clock outputs found\n");
|
||||
return (ENOENT);
|
||||
}
|
||||
if (indices == NULL) {
|
||||
device_printf(dev, "no clock-indices property\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
error = clk_get_by_ofw_index(dev, node, 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)
|
||||
return (error);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
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;
|
||||
bus_addr_t paddr;
|
||||
bus_size_t psize;
|
||||
phandle_t node, child;
|
||||
|
||||
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);
|
||||
|
||||
if (ofw_bus_is_compatible(dev, "allwinner,sunxi-multi-bus-gates-clk")) {
|
||||
for (child = OF_child(node); child > 0; child = OF_peer(child))
|
||||
aw_gate_add(dev, clkdom, child, paddr);
|
||||
} else
|
||||
aw_gate_add(dev, clkdom, node, paddr);
|
||||
|
||||
if (clkdom_finit(clkdom) != 0) {
|
||||
device_printf(dev, "cannot finalize clkdom initialization\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
if (bootverbose)
|
||||
clkdom_dump(clkdom);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
@ -1,318 +0,0 @@
|
|||
/*-
|
||||
* 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);
|
||||
|
||||
*fout = fin / (1 << best_n) / (best_m + 1);
|
||||
*stop = 1;
|
||||
|
||||
if ((flags & CLK_SET_DRYRUN) != 0)
|
||||
return (0);
|
||||
|
||||
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);
|
||||
|
||||
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, 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);
|
||||
|
|
@ -1,627 +0,0 @@
|
|||
/*-
|
||||
* 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_PLLREF_FRAC1 297000000ULL
|
||||
#define TCON_PLLREF_FRAC2 270000000ULL
|
||||
#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, src_sel;
|
||||
|
||||
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) {
|
||||
src_sel = (val & CH1_SCLK1_SEL) >> CH1_SCLK1_SEL_SHIFT;
|
||||
if (src_sel == CH1_SCLK1_SEL_SCLK2_DIV2)
|
||||
*freq /= 2;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
calc_tcon_pll_integer(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
|
||||
calc_tcon_pll_fractional(uint64_t fin, uint64_t fout, int *clk_div)
|
||||
{
|
||||
int m;
|
||||
|
||||
/* Test for 1X match */
|
||||
for (m = TCON_PLL_M_MIN; m <= TCON_PLL_M_MAX; m++) {
|
||||
if (fout == (fin / m)) {
|
||||
*clk_div = m;
|
||||
return (CH0_CLK_SRC_SEL_PLL3_1X);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test for 2X match */
|
||||
for (m = TCON_PLL_M_MIN; m <= TCON_PLL_M_MAX; m++) {
|
||||
if (fout == ((fin * 2) / m)) {
|
||||
*clk_div = m;
|
||||
return (CH0_CLK_SRC_SEL_PLL3_2X);
|
||||
}
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static int
|
||||
calc_tcon_pll(uint64_t fin, uint64_t fout, uint64_t *pll_freq, int *tcon_pll_div)
|
||||
{
|
||||
uint32_t m, m2, n, n2;
|
||||
uint64_t fsingle, fdouble;
|
||||
int src_sel;
|
||||
bool dbl;
|
||||
|
||||
/* Test fractional freq first */
|
||||
src_sel = calc_tcon_pll_fractional(TCON_PLLREF_FRAC1, fout,
|
||||
tcon_pll_div);
|
||||
if (src_sel != -1) {
|
||||
*pll_freq = TCON_PLLREF_FRAC1;
|
||||
return src_sel;
|
||||
}
|
||||
src_sel = calc_tcon_pll_fractional(TCON_PLLREF_FRAC2, fout,
|
||||
tcon_pll_div);
|
||||
if (src_sel != -1) {
|
||||
*pll_freq = TCON_PLLREF_FRAC2;
|
||||
return src_sel;
|
||||
}
|
||||
|
||||
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_integer(TCON_PLLREF, fout, &m, &n);
|
||||
calc_tcon_pll_integer(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;
|
||||
}
|
||||
|
||||
/* Set desired parent frequency */
|
||||
*pll_freq = n * TCON_PLLREF;
|
||||
*tcon_pll_div = m;
|
||||
|
||||
/* Return the desired source clock */
|
||||
return (dbl ? CH0_CLK_SRC_SEL_PLL3_2X :
|
||||
CH0_CLK_SRC_SEL_PLL3_1X);
|
||||
}
|
||||
|
||||
static int
|
||||
aw_lcdclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
|
||||
int flags, int *stop)
|
||||
{
|
||||
struct aw_lcdclk_softc *sc;
|
||||
struct clknode *parent_clk;
|
||||
const char **parent_names;
|
||||
uint64_t pll_freq;
|
||||
uint32_t val, src_sel;
|
||||
int error, tcon_pll_div;
|
||||
|
||||
sc = clknode_get_softc(clk);
|
||||
|
||||
if (sc->type == AW_LCD_CH0) {
|
||||
*stop = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (sc->id != CLK_IDX_CH1_SCLK2)
|
||||
return (ENXIO);
|
||||
|
||||
src_sel = calc_tcon_pll(fin, *fout, &pll_freq, &tcon_pll_div);
|
||||
|
||||
parent_names = clknode_get_parent_names(clk);
|
||||
parent_clk = clknode_find_by_name(parent_names[src_sel]);
|
||||
|
||||
if (parent_clk == NULL)
|
||||
return (ERANGE);
|
||||
|
||||
/* Fetch input frequency */
|
||||
error = clknode_get_freq(parent_clk, &pll_freq);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
*fout = pll_freq / tcon_pll_div;
|
||||
*stop = 1;
|
||||
|
||||
if ((flags & CLK_SET_DRYRUN) != 0)
|
||||
return (0);
|
||||
|
||||
/* Switch parent clock if necessary */
|
||||
error = clknode_set_parent_by_idx(clk, src_sel);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
error = clknode_set_freq(parent_clk, pll_freq,
|
||||
0, 0);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
/* Fetch new input frequency */
|
||||
error = clknode_get_freq(parent_clk, &pll_freq);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
*fout = pll_freq / tcon_pll_div;
|
||||
|
||||
error = clknode_enable(parent_clk);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
/* Set LCD divisor */
|
||||
DEVICE_LOCK(sc);
|
||||
LCDCLK_READ(sc, &val);
|
||||
val &= ~CH1_CLK_DIV_RATIO_M;
|
||||
val |= ((tcon_pll_div - 1) << CH1_CLK_DIV_RATIO_M_SHIFT);
|
||||
LCDCLK_WRITE(sc, val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
|
||||
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, 0, 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);
|
||||
|
||||
OF_prop_free(parent_names);
|
||||
return (0);
|
||||
|
||||
fail:
|
||||
OF_prop_free(parent_names);
|
||||
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);
|
||||
|
|
@ -1,364 +0,0 @@
|
|||
/*-
|
||||
* 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;
|
||||
};
|
||||
|
||||
struct phase_clk {
|
||||
uint64_t freq;
|
||||
int parent_idx;
|
||||
uint32_t ophase;
|
||||
uint32_t phase;
|
||||
uint32_t n;
|
||||
};
|
||||
|
||||
static struct phase_clk aw_mmcclk_phase[] = {
|
||||
{400000, CLK_SRC_SEL_OSC24M, 0, 0, 2},
|
||||
{25000000, CLK_SRC_SEL_PLL6, 0, 5, 2},
|
||||
{52000000, CLK_SRC_SEL_PLL6, 3, 5, 0},
|
||||
};
|
||||
|
||||
#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;
|
||||
struct clknode *parent_clk;
|
||||
const char **parent_names;
|
||||
uint32_t val, m;
|
||||
int parent_idx, error, phase;
|
||||
|
||||
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
|
||||
*/
|
||||
for (phase = 0; phase < nitems(aw_mmcclk_phase); phase++) {
|
||||
if (*fout <= aw_mmcclk_phase[phase].freq)
|
||||
break;
|
||||
}
|
||||
|
||||
if (phase == nitems(aw_mmcclk_phase))
|
||||
return (ERANGE);
|
||||
|
||||
parent_names = clknode_get_parent_names(clk);
|
||||
parent_idx = aw_mmcclk_phase[phase].parent_idx;
|
||||
parent_clk = clknode_find_by_name(parent_names[parent_idx]);
|
||||
|
||||
if (parent_clk == NULL)
|
||||
return (ERANGE);
|
||||
|
||||
error = clknode_get_freq(parent_clk, &fin);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
m = ((fin / (1 << aw_mmcclk_phase[phase].n)) / *fout) - 1;
|
||||
|
||||
*fout = fin / (1 << aw_mmcclk_phase[phase].n) / (m + 1);
|
||||
*stop = 1;
|
||||
|
||||
if ((flags & CLK_SET_DRYRUN) != 0)
|
||||
return (0);
|
||||
|
||||
/* Switch to the correct parent if needed */
|
||||
error = clknode_set_parent_by_idx(clk, parent_idx);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
DEVICE_LOCK(sc);
|
||||
MODCLK_READ(sc, &val);
|
||||
val &= ~(CLK_RATIO_N | CLK_RATIO_M | CLK_PHASE_CTR |
|
||||
OUTPUT_CLK_PHASE_CTR);
|
||||
val |= (aw_mmcclk_phase[phase].n << CLK_RATIO_N_SHIFT);
|
||||
val |= (m << CLK_RATIO_M_SHIFT);
|
||||
val |= (aw_mmcclk_phase[phase].phase << CLK_PHASE_CTR_SHIFT);
|
||||
val |= (aw_mmcclk_phase[phase].ophase << OUTPUT_CLK_PHASE_CTR_SHIFT);
|
||||
MODCLK_WRITE(sc, val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
|
||||
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, 0, 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);
|
||||
|
|
@ -1,347 +0,0 @@
|
|||
/*-
|
||||
* 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_RATIO_N (0x3 << 16)
|
||||
#define CLK_RATIO_N_SHIFT 16
|
||||
#define CLK_RATIO_N_MAX 0x3
|
||||
#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-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;
|
||||
|
||||
if (index <= clknode_get_parents_num(clk))
|
||||
clknode_init_parent_idx(clk, index);
|
||||
else
|
||||
clknode_init_parent_idx(clk, 0);
|
||||
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 >= clknode_get_parents_num(clk))
|
||||
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;
|
||||
struct clknode *parent_clk, *best_parent;
|
||||
const char **parent_names;
|
||||
uint32_t val, m, n, src, best_m, best_n, best_src;
|
||||
uint64_t cur_freq;
|
||||
int64_t best_diff, cur_diff;
|
||||
int error;
|
||||
|
||||
sc = clknode_get_softc(clk);
|
||||
best_n = best_m = 0;
|
||||
best_diff = (int64_t)*fout;
|
||||
best_src = 0;
|
||||
|
||||
parent_names = clknode_get_parent_names(clk);
|
||||
for (src = 0; src < clknode_get_parents_num(clk); src++) {
|
||||
parent_clk = clknode_find_by_name(parent_names[src]);
|
||||
if (parent_clk == NULL)
|
||||
continue;
|
||||
error = clknode_get_freq(parent_clk, &fin);
|
||||
if (error != 0)
|
||||
continue;
|
||||
|
||||
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_src = src;
|
||||
best_parent = parent_clk;
|
||||
best_diff = cur_diff;
|
||||
best_m = m;
|
||||
best_n = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best_diff == (int64_t)*fout)
|
||||
return (ERANGE);
|
||||
|
||||
error = clknode_get_freq(best_parent, &fin);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
*fout = fin / (1 << best_n) / (best_m + 1);
|
||||
*stop = 1;
|
||||
|
||||
if ((flags & CLK_SET_DRYRUN) != 0)
|
||||
return (0);
|
||||
|
||||
error = clknode_set_parent_by_idx(clk, best_src);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
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);
|
||||
|
||||
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, 0, 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);
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
/*-
|
||||
* 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);
|
||||
|
||||
OF_prop_free(__DECONST(char *, def.clkdef.name));
|
||||
|
||||
return (0);
|
||||
|
||||
fail:
|
||||
OF_prop_free(__DECONST(char *, def.clkdef.name));
|
||||
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);
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,324 +0,0 @@
|
|||
/*-
|
||||
* 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 THS 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 1
|
||||
#define CLK_DIV_RATIO (0x3 << 0)
|
||||
#define CLK_DIV_RATIO_SHIFT 0
|
||||
#define CLK_DIV_RATIO_MAX 3
|
||||
|
||||
static struct ofw_compat_data compat_data[] = {
|
||||
{ "allwinner,sun8i-h3-ths-clk", 1 },
|
||||
{ "allwinner,sun50i-a64-ths-clk", 1 },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
struct aw_thsclk_sc {
|
||||
device_t clkdev;
|
||||
bus_addr_t reg;
|
||||
};
|
||||
|
||||
#define THSCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
|
||||
#define THSCLK_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_thsclk_init(struct clknode *clk, device_t dev)
|
||||
{
|
||||
struct aw_thsclk_sc *sc;
|
||||
uint32_t val, index;
|
||||
|
||||
sc = clknode_get_softc(clk);
|
||||
|
||||
DEVICE_LOCK(sc);
|
||||
THSCLK_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_thsclk_set_mux(struct clknode *clk, int index)
|
||||
{
|
||||
struct aw_thsclk_sc *sc;
|
||||
uint32_t val;
|
||||
|
||||
sc = clknode_get_softc(clk);
|
||||
|
||||
if (index < 0 || index >= CLK_SRC_SEL_MAX)
|
||||
return (ERANGE);
|
||||
|
||||
DEVICE_LOCK(sc);
|
||||
THSCLK_READ(sc, &val);
|
||||
val &= ~CLK_SRC_SEL;
|
||||
val |= (index << CLK_SRC_SEL_SHIFT);
|
||||
THSCLK_WRITE(sc, val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
aw_thsclk_set_gate(struct clknode *clk, bool enable)
|
||||
{
|
||||
struct aw_thsclk_sc *sc;
|
||||
uint32_t val;
|
||||
|
||||
sc = clknode_get_softc(clk);
|
||||
|
||||
DEVICE_LOCK(sc);
|
||||
THSCLK_READ(sc, &val);
|
||||
if (enable)
|
||||
val |= SCLK_GATING;
|
||||
else
|
||||
val &= ~SCLK_GATING;
|
||||
THSCLK_WRITE(sc, val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
aw_thsclk_recalc_freq(struct clknode *clk, uint64_t *freq)
|
||||
{
|
||||
struct aw_thsclk_sc *sc;
|
||||
uint32_t val, div;
|
||||
|
||||
sc = clknode_get_softc(clk);
|
||||
|
||||
DEVICE_LOCK(sc);
|
||||
THSCLK_READ(sc, &val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
|
||||
switch (val & CLK_DIV_RATIO) {
|
||||
case 3:
|
||||
div = 6;
|
||||
break;
|
||||
default:
|
||||
div = 1 << (val & CLK_DIV_RATIO);
|
||||
break;
|
||||
}
|
||||
|
||||
*freq = *freq / div;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
aw_thsclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
|
||||
int flags, int *stop)
|
||||
{
|
||||
struct aw_thsclk_sc *sc;
|
||||
uint32_t val, div, n, best_div, best_n;
|
||||
uint64_t cur_freq;
|
||||
int64_t best_diff, cur_diff;
|
||||
|
||||
sc = clknode_get_softc(clk);
|
||||
best_diff = (int64_t)*fout;
|
||||
best_n = 0;
|
||||
|
||||
for (div = 0; div <= CLK_DIV_RATIO_MAX; div++) {
|
||||
n = (div == 3) ? 6 : (1 << div);
|
||||
cur_freq = fin / n;
|
||||
cur_diff = (int64_t)*fout - cur_freq;
|
||||
if (cur_diff >= 0 && cur_diff < best_diff) {
|
||||
best_diff = cur_diff;
|
||||
best_div = div;
|
||||
best_n = n;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_diff == (int64_t)*fout || best_n == 0)
|
||||
return (ERANGE);
|
||||
|
||||
if ((flags & CLK_SET_DRYRUN) != 0)
|
||||
return (0);
|
||||
|
||||
DEVICE_LOCK(sc);
|
||||
THSCLK_READ(sc, &val);
|
||||
val &= ~CLK_DIV_RATIO;
|
||||
val |= (best_div << CLK_DIV_RATIO_SHIFT);
|
||||
THSCLK_WRITE(sc, val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
|
||||
*fout = fin / best_n;
|
||||
*stop = 1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static clknode_method_t aw_thsclk_clknode_methods[] = {
|
||||
/* Device interface */
|
||||
CLKNODEMETHOD(clknode_init, aw_thsclk_init),
|
||||
CLKNODEMETHOD(clknode_set_gate, aw_thsclk_set_gate),
|
||||
CLKNODEMETHOD(clknode_set_mux, aw_thsclk_set_mux),
|
||||
CLKNODEMETHOD(clknode_recalc_freq, aw_thsclk_recalc_freq),
|
||||
CLKNODEMETHOD(clknode_set_freq, aw_thsclk_set_freq),
|
||||
CLKNODEMETHOD_END
|
||||
};
|
||||
DEFINE_CLASS_1(aw_thsclk_clknode, aw_thsclk_clknode_class,
|
||||
aw_thsclk_clknode_methods, sizeof(struct aw_thsclk_sc), clknode_class);
|
||||
|
||||
static int
|
||||
aw_thsclk_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 THS Clock");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
aw_thsclk_attach(device_t dev)
|
||||
{
|
||||
struct clknode_init_def def;
|
||||
struct aw_thsclk_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, 0, 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_thsclk_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_thsclk_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, aw_thsclk_probe),
|
||||
DEVMETHOD(device_attach, aw_thsclk_attach),
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
static driver_t aw_thsclk_driver = {
|
||||
"aw_thsclk",
|
||||
aw_thsclk_methods,
|
||||
0
|
||||
};
|
||||
|
||||
static devclass_t aw_thsclk_devclass;
|
||||
|
||||
EARLY_DRIVER_MODULE(aw_thsclk, simplebus, aw_thsclk_driver,
|
||||
aw_thsclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
|
||||
|
|
@ -1,267 +0,0 @@
|
|||
/*-
|
||||
* 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_A13_USBCLK,
|
||||
AW_A31_USBCLK,
|
||||
AW_A83T_USBCLK,
|
||||
AW_H3_USBCLK,
|
||||
};
|
||||
|
||||
static struct ofw_compat_data compat_data[] = {
|
||||
{ "allwinner,sun4i-a10-usb-clk", AW_A10_USBCLK },
|
||||
{ "allwinner,sun5i-a13-usb-clk", AW_A13_USBCLK },
|
||||
{ "allwinner,sun6i-a31-usb-clk", AW_A31_USBCLK },
|
||||
{ "allwinner,sun8i-a83t-usb-clk", AW_A83T_USBCLK },
|
||||
{ "allwinner,sun8i-h3-usb-clk", AW_H3_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 };
|
||||
/* Clock indices for H3, as there is no clock-indices property in the DT */
|
||||
static uint32_t aw_usbclk_indices_h3[] = { 8, 9, 10, 11, 16, 17, 18, 19 };
|
||||
|
||||
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;
|
||||
const char *pname;
|
||||
int index, nout, error;
|
||||
enum aw_usbclk_type type;
|
||||
uint32_t *indices;
|
||||
clk_t clk_parent, clk_parent_pll;
|
||||
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;
|
||||
else if (indices == NULL && type == AW_H3_USBCLK)
|
||||
indices = aw_usbclk_indices_h3;
|
||||
|
||||
error = clk_get_by_ofw_index(dev, 0, 0, &clk_parent);
|
||||
if (error != 0) {
|
||||
device_printf(dev, "cannot parse clock parent\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
if (type == AW_A83T_USBCLK) {
|
||||
error = clk_get_by_ofw_index(dev, 0, 1, &clk_parent_pll);
|
||||
if (error != 0) {
|
||||
device_printf(dev, "cannot parse pll clock parent\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
}
|
||||
|
||||
for (index = 0; index < nout; index++) {
|
||||
if (strcmp(names[index], "usb_hsic_pll") == 0)
|
||||
pname = clk_get_name(clk_parent_pll);
|
||||
else
|
||||
pname = clk_get_name(clk_parent);
|
||||
error = aw_usbclk_create(dev, sc->reg, clkdom, pname,
|
||||
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);
|
||||
|
|
@ -34,7 +34,7 @@ arm/allwinner/aw_cir.c optional aw_cir evdev
|
|||
|
||||
arm/allwinner/aw_reset.c standard
|
||||
arm/allwinner/aw_ccu.c standard
|
||||
arm/allwinner/clk/aw_gmacclk.c standard
|
||||
arm/allwinner/aw_gmacclk.c standard
|
||||
|
||||
arm/allwinner/clkng/aw_ccung.c standard
|
||||
arm/allwinner/clkng/aw_clk_nkmp.c standard
|
||||
|
|
|
|||
Loading…
Reference in a new issue