Add AM335x DVFS support.
This commit is contained in:
parent
a5c41a8d26
commit
0517767c44
@ -1,7 +1,8 @@
|
||||
# $NetBSD: files.ti,v 1.11 2019/10/27 19:11:07 jmcneill Exp $
|
||||
# $NetBSD: files.ti,v 1.12 2019/10/28 21:16:47 jmcneill Exp $
|
||||
#
|
||||
|
||||
file arch/arm/ti/ti_platform.c soc_ti
|
||||
file arch/arm/ti/ti_cpufreq.c soc_ti
|
||||
|
||||
# Interrupt controller
|
||||
device omapintc: pic, pic_splfuncs
|
||||
@ -18,10 +19,23 @@ define ti_prcm
|
||||
file arch/arm/ti/ti_prcm.c ti_prcm
|
||||
|
||||
# PRCM (AM3xxx)
|
||||
device am3prcm: ti_prcm
|
||||
device am3prcm { } : fdt, ti_prcm
|
||||
attach am3prcm at fdt with am3_prcm
|
||||
file arch/arm/ti/am3_prcm.c am3_prcm
|
||||
|
||||
# Clocks
|
||||
device timuxclk
|
||||
attach timuxclk at fdt with ti_mux_clock
|
||||
file arch/arm/ti/ti_mux_clock.c ti_mux_clock
|
||||
|
||||
device tidivclk
|
||||
attach tidivclk at fdt with ti_div_clock
|
||||
file arch/arm/ti/ti_div_clock.c ti_div_clock
|
||||
|
||||
device tidpllclk
|
||||
attach tidpllclk at fdt with ti_dpll_clock
|
||||
file arch/arm/ti/ti_dpll_clock.c ti_dpll_clock
|
||||
|
||||
# UART
|
||||
attach com at fdt with ti_com: ti_prcm
|
||||
file arch/arm/ti/ti_com.c ti_com needs-flag
|
||||
|
113
sys/arch/arm/ti/ti_cpufreq.c
Normal file
113
sys/arch/arm/ti/ti_cpufreq.c
Normal file
@ -0,0 +1,113 @@
|
||||
/* $NetBSD: ti_cpufreq.c,v 1.1 2019/10/28 21:16:47 jmcneill Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2019 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.
|
||||
*/
|
||||
|
||||
#include "opt_soc.h"
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: ti_cpufreq.c,v 1.1 2019/10/28 21:16:47 jmcneill Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/bus.h>
|
||||
|
||||
#include <dev/fdt/fdtvar.h>
|
||||
#include <dev/fdt/syscon.h>
|
||||
|
||||
static bool ti_opp_probed = false;
|
||||
static bool (*ti_opp_supportedfn)(const int, const int);
|
||||
static struct syscon *ti_opp_syscon;
|
||||
|
||||
#ifdef SOC_TI_AM335X
|
||||
|
||||
#define AM33XX_REV_OFFSET 0x0600
|
||||
#define AM33XX_REV_MASK 0xf0000000
|
||||
#define AM33XX_EFUSE_OFFSET 0x07fc
|
||||
#define AM33XX_EFUSE_MASK 0x00001fff
|
||||
#define AM33XX_EFUSE_DEFAULT 0x1e2f
|
||||
|
||||
static const char * const am33xx_compatible[] = { "ti,am33xx", NULL };
|
||||
|
||||
static bool
|
||||
am33xx_opp_supported(const int opp_table, const int opp_node)
|
||||
{
|
||||
const u_int *supported_hw;
|
||||
uint32_t efuse, rev;
|
||||
int len;
|
||||
|
||||
syscon_lock(ti_opp_syscon);
|
||||
rev = __SHIFTOUT(syscon_read_4(ti_opp_syscon, AM33XX_REV_OFFSET), AM33XX_REV_MASK);
|
||||
efuse = __SHIFTOUT(syscon_read_4(ti_opp_syscon, AM33XX_EFUSE_OFFSET), AM33XX_EFUSE_MASK);
|
||||
syscon_unlock(ti_opp_syscon);
|
||||
|
||||
if (efuse == 0)
|
||||
efuse = AM33XX_EFUSE_DEFAULT;
|
||||
efuse = ~efuse;
|
||||
|
||||
supported_hw = fdtbus_get_prop(opp_node, "opp-supported-hw", &len);
|
||||
if (len != 8)
|
||||
return false;
|
||||
|
||||
if ((rev & be32toh(supported_hw[0])) == 0)
|
||||
return false;
|
||||
|
||||
if ((efuse & be32toh(supported_hw[1])) == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
ti_opp_probe(const int opp_table)
|
||||
{
|
||||
if (ti_opp_probed)
|
||||
return;
|
||||
ti_opp_probed = true;
|
||||
|
||||
ti_opp_syscon = fdtbus_syscon_acquire(opp_table, "syscon");
|
||||
|
||||
#ifdef SOC_TI_AM335X
|
||||
if (ti_opp_syscon && of_match_compatible(OF_finddevice("/"), am33xx_compatible))
|
||||
ti_opp_supportedfn = am33xx_opp_supported;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
ti_opp_supported(const int opp_table, const int opp_node)
|
||||
{
|
||||
ti_opp_probe(opp_table);
|
||||
|
||||
if (!of_hasprop(opp_node, "opp-supported-hw"))
|
||||
return true;
|
||||
|
||||
return ti_opp_supportedfn ? ti_opp_supportedfn(opp_table, opp_node) : false;
|
||||
}
|
||||
|
||||
FDT_OPP(ti, "operating-points-v2-ti-cpu", ti_opp_supported);
|
202
sys/arch/arm/ti/ti_div_clock.c
Normal file
202
sys/arch/arm/ti/ti_div_clock.c
Normal file
@ -0,0 +1,202 @@
|
||||
/* $NetBSD: ti_div_clock.c,v 1.1 2019/10/28 21:16:47 jmcneill Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2019 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.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: ti_div_clock.c,v 1.1 2019/10/28 21:16:47 jmcneill Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/bus.h>
|
||||
|
||||
#include <dev/clk/clk_backend.h>
|
||||
|
||||
#include <dev/fdt/fdtvar.h>
|
||||
|
||||
static const char * const compatible[] = {
|
||||
"ti,divider-clock",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int ti_div_clock_match(device_t, cfdata_t, void *);
|
||||
static void ti_div_clock_attach(device_t, device_t, void *);
|
||||
|
||||
static struct clk *ti_div_clock_decode(device_t, int, const void *, size_t);
|
||||
|
||||
static const struct fdtbus_clock_controller_func ti_div_clock_fdt_funcs = {
|
||||
.decode = ti_div_clock_decode
|
||||
};
|
||||
|
||||
static struct clk *ti_div_clock_get(void *, const char *);
|
||||
static void ti_div_clock_put(void *, struct clk *);
|
||||
static u_int ti_div_clock_get_rate(void *, struct clk *);
|
||||
static struct clk *ti_div_clock_get_parent(void *, struct clk *);
|
||||
|
||||
static const struct clk_funcs ti_div_clock_clk_funcs = {
|
||||
.get = ti_div_clock_get,
|
||||
.put = ti_div_clock_put,
|
||||
.get_rate = ti_div_clock_get_rate,
|
||||
.get_parent = ti_div_clock_get_parent,
|
||||
};
|
||||
|
||||
struct ti_div_clock_softc {
|
||||
device_t sc_dev;
|
||||
int sc_phandle;
|
||||
bus_space_tag_t sc_bst;
|
||||
bus_space_handle_t sc_bsh;
|
||||
|
||||
struct clk_domain sc_clkdom;
|
||||
struct clk sc_clk;
|
||||
};
|
||||
|
||||
#define RD4(sc) \
|
||||
bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, 0)
|
||||
#define WR4(sc, val) \
|
||||
bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, 0, (val))
|
||||
|
||||
CFATTACH_DECL_NEW(ti_div_clock, sizeof(struct ti_div_clock_softc),
|
||||
ti_div_clock_match, ti_div_clock_attach, NULL, NULL);
|
||||
|
||||
static int
|
||||
ti_div_clock_match(device_t parent, cfdata_t cf, void *aux)
|
||||
{
|
||||
const struct fdt_attach_args *faa = aux;
|
||||
|
||||
return of_match_compatible(faa->faa_phandle, compatible);
|
||||
}
|
||||
|
||||
static void
|
||||
ti_div_clock_attach(device_t parent, device_t self, void *aux)
|
||||
{
|
||||
struct ti_div_clock_softc * const sc = device_private(self);
|
||||
const struct fdt_attach_args *faa = aux;
|
||||
const int phandle = faa->faa_phandle;
|
||||
bus_addr_t addr, base_addr;
|
||||
|
||||
const int prcm_phandle = OF_parent(OF_parent(phandle));
|
||||
if (fdtbus_get_reg(phandle, 0, &addr, NULL) != 0 ||
|
||||
fdtbus_get_reg(prcm_phandle, 0, &base_addr, NULL) != 0) {
|
||||
aprint_error(": couldn't get registers\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sc->sc_dev = self;
|
||||
sc->sc_phandle = phandle;
|
||||
sc->sc_bst = faa->faa_bst;
|
||||
if (bus_space_map(sc->sc_bst, base_addr + addr, 4, 0, &sc->sc_bsh) != 0) {
|
||||
aprint_error(": couldn't map registers\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sc->sc_clkdom.name = device_xname(self);
|
||||
sc->sc_clkdom.funcs = &ti_div_clock_clk_funcs;
|
||||
sc->sc_clkdom.priv = sc;
|
||||
|
||||
sc->sc_clk.domain = &sc->sc_clkdom;
|
||||
sc->sc_clk.name = kmem_asprintf("%s", faa->faa_name);
|
||||
clk_attach(&sc->sc_clk);
|
||||
|
||||
aprint_naive("\n");
|
||||
aprint_normal(": TI divider clock (%s)\n", sc->sc_clk.name);
|
||||
|
||||
fdtbus_register_clock_controller(self, phandle, &ti_div_clock_fdt_funcs);
|
||||
}
|
||||
|
||||
static struct clk *
|
||||
ti_div_clock_decode(device_t dev, int cc_phandle, const void *data,
|
||||
size_t len)
|
||||
{
|
||||
struct ti_div_clock_softc * const sc = device_private(dev);
|
||||
|
||||
return &sc->sc_clk;
|
||||
}
|
||||
|
||||
static struct clk *
|
||||
ti_div_clock_get(void *priv, const char *name)
|
||||
{
|
||||
struct ti_div_clock_softc * const sc = priv;
|
||||
|
||||
return &sc->sc_clk;
|
||||
}
|
||||
|
||||
static void
|
||||
ti_div_clock_put(void *priv, struct clk *clk)
|
||||
{
|
||||
}
|
||||
|
||||
static u_int
|
||||
ti_div_clock_get_rate(void *priv, struct clk *clk)
|
||||
{
|
||||
struct ti_div_clock_softc * const sc = priv;
|
||||
struct clk *clk_parent = clk_get_parent(clk);
|
||||
uint64_t parent_rate;
|
||||
uint32_t val, max_val, mask;
|
||||
u_int div, max_div, bit_shift;
|
||||
|
||||
if (clk_parent == NULL)
|
||||
return 0;
|
||||
|
||||
if (of_hasprop(sc->sc_phandle, "ti,index-power-of-two"))
|
||||
return EINVAL; /* not supported yet */
|
||||
|
||||
const int start_index = of_hasprop(sc->sc_phandle, "ti,index-starts-at-one") ? 1 : 0;
|
||||
|
||||
if (of_getprop_uint32(sc->sc_phandle, "ti,bit-shift", &bit_shift) != 0)
|
||||
bit_shift = 0;
|
||||
|
||||
if (of_getprop_uint32(sc->sc_phandle, "ti,max-div", &max_div) == 0) {
|
||||
max_val = start_index + max_div;
|
||||
while (!powerof2(max_val))
|
||||
max_val++;
|
||||
mask = (max_val - 1) << bit_shift;
|
||||
} else {
|
||||
/*
|
||||
* The bindings allow for this, but it is not yet supported
|
||||
* by this driver.
|
||||
*/
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
val = RD4(sc);
|
||||
div = __SHIFTOUT(val, mask) + (start_index ? 0 : 1);
|
||||
if (div == 0)
|
||||
return EINVAL;
|
||||
|
||||
parent_rate = clk_get_rate(clk_parent);
|
||||
|
||||
return (u_int)(parent_rate / div);
|
||||
}
|
||||
|
||||
static struct clk *
|
||||
ti_div_clock_get_parent(void *priv, struct clk *clk)
|
||||
{
|
||||
struct ti_div_clock_softc * const sc = priv;
|
||||
|
||||
return fdtbus_clock_get_index(sc->sc_phandle, 0);
|
||||
}
|
252
sys/arch/arm/ti/ti_dpll_clock.c
Normal file
252
sys/arch/arm/ti/ti_dpll_clock.c
Normal file
@ -0,0 +1,252 @@
|
||||
/* $NetBSD: ti_dpll_clock.c,v 1.1 2019/10/28 21:16:47 jmcneill Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2019 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.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: ti_dpll_clock.c,v 1.1 2019/10/28 21:16:47 jmcneill Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/bus.h>
|
||||
|
||||
#include <dev/clk/clk_backend.h>
|
||||
|
||||
#include <dev/fdt/fdtvar.h>
|
||||
|
||||
/* CM_IDLEST_DPLL_MPU */
|
||||
#define ST_MN_BYPASS __BIT(8)
|
||||
#define ST_DPLL_CLK __BIT(0)
|
||||
|
||||
/* CM_CLKSEL_DPLL_MPU */
|
||||
#define DPLL_BYP_CLKSEL __BIT(23)
|
||||
#define DPLL_MULT __BITS(18,8)
|
||||
#define DPLL_DIV __BITS(6,0)
|
||||
|
||||
/* CM_CLKMODE_DPLL_MPU */
|
||||
#define DPLL_EN __BITS(2,0)
|
||||
#define DPLL_EN_NM_BYPASS 4
|
||||
#define DPLL_EN_LOCK 7
|
||||
|
||||
static const char * const compatible[] = {
|
||||
"ti,am3-dpll-clock",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int ti_dpll_clock_match(device_t, cfdata_t, void *);
|
||||
static void ti_dpll_clock_attach(device_t, device_t, void *);
|
||||
|
||||
static struct clk *ti_dpll_clock_decode(device_t, int, const void *, size_t);
|
||||
|
||||
static const struct fdtbus_clock_controller_func ti_dpll_clock_fdt_funcs = {
|
||||
.decode = ti_dpll_clock_decode
|
||||
};
|
||||
|
||||
static struct clk *ti_dpll_clock_get(void *, const char *);
|
||||
static void ti_dpll_clock_put(void *, struct clk *);
|
||||
static int ti_dpll_clock_set_rate(void *, struct clk *, u_int);
|
||||
static u_int ti_dpll_clock_get_rate(void *, struct clk *);
|
||||
static struct clk *ti_dpll_clock_get_parent(void *, struct clk *);
|
||||
|
||||
static const struct clk_funcs ti_dpll_clock_clk_funcs = {
|
||||
.get = ti_dpll_clock_get,
|
||||
.put = ti_dpll_clock_put,
|
||||
.set_rate = ti_dpll_clock_set_rate,
|
||||
.get_rate = ti_dpll_clock_get_rate,
|
||||
.get_parent = ti_dpll_clock_get_parent,
|
||||
};
|
||||
|
||||
enum {
|
||||
REG_CONTROL,
|
||||
REG_IDLEST,
|
||||
REG_MULT_DIV1,
|
||||
NREG
|
||||
};
|
||||
|
||||
struct ti_dpll_clock_softc {
|
||||
device_t sc_dev;
|
||||
int sc_phandle;
|
||||
bus_space_tag_t sc_bst;
|
||||
bus_space_handle_t sc_bsh[NREG];
|
||||
|
||||
struct clk_domain sc_clkdom;
|
||||
struct clk sc_clk;
|
||||
};
|
||||
|
||||
#define RD4(sc, space) \
|
||||
bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh[(space)], 0)
|
||||
#define WR4(sc, space, val) \
|
||||
bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh[(space)], 0, (val))
|
||||
|
||||
CFATTACH_DECL_NEW(ti_dpll_clock, sizeof(struct ti_dpll_clock_softc),
|
||||
ti_dpll_clock_match, ti_dpll_clock_attach, NULL, NULL);
|
||||
|
||||
static int
|
||||
ti_dpll_clock_match(device_t parent, cfdata_t cf, void *aux)
|
||||
{
|
||||
const struct fdt_attach_args *faa = aux;
|
||||
|
||||
return of_match_compatible(faa->faa_phandle, compatible);
|
||||
}
|
||||
|
||||
static void
|
||||
ti_dpll_clock_attach(device_t parent, device_t self, void *aux)
|
||||
{
|
||||
struct ti_dpll_clock_softc * const sc = device_private(self);
|
||||
const struct fdt_attach_args *faa = aux;
|
||||
const int phandle = faa->faa_phandle;
|
||||
bus_addr_t addr[NREG], base_addr;
|
||||
u_int n;
|
||||
|
||||
const int prcm_phandle = OF_parent(OF_parent(phandle));
|
||||
if (fdtbus_get_reg(prcm_phandle, 0, &base_addr, NULL) != 0) {
|
||||
aprint_error(": couldn't get prcm registers\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (n = 0; n < NREG; n++) {
|
||||
if (fdtbus_get_reg(phandle, n, &addr[n], NULL) != 0) {
|
||||
aprint_error(": couldn't get registers\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sc->sc_dev = self;
|
||||
sc->sc_phandle = phandle;
|
||||
sc->sc_bst = faa->faa_bst;
|
||||
for (n = 0; n < NREG; n++) {
|
||||
if (bus_space_map(sc->sc_bst, base_addr + addr[n], 4, 0, &sc->sc_bsh[n]) != 0) {
|
||||
aprint_error(": couldn't map registers\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sc->sc_clkdom.name = device_xname(self);
|
||||
sc->sc_clkdom.funcs = &ti_dpll_clock_clk_funcs;
|
||||
sc->sc_clkdom.priv = sc;
|
||||
|
||||
sc->sc_clk.domain = &sc->sc_clkdom;
|
||||
sc->sc_clk.name = kmem_asprintf("%s", faa->faa_name);
|
||||
clk_attach(&sc->sc_clk);
|
||||
|
||||
aprint_naive("\n");
|
||||
aprint_normal(": TI DPLL clock (%s)\n", sc->sc_clk.name);
|
||||
|
||||
fdtbus_register_clock_controller(self, phandle, &ti_dpll_clock_fdt_funcs);
|
||||
}
|
||||
|
||||
static struct clk *
|
||||
ti_dpll_clock_decode(device_t dev, int cc_phandle, const void *data,
|
||||
size_t len)
|
||||
{
|
||||
struct ti_dpll_clock_softc * const sc = device_private(dev);
|
||||
|
||||
return &sc->sc_clk;
|
||||
}
|
||||
|
||||
static struct clk *
|
||||
ti_dpll_clock_get(void *priv, const char *name)
|
||||
{
|
||||
struct ti_dpll_clock_softc * const sc = priv;
|
||||
|
||||
return &sc->sc_clk;
|
||||
}
|
||||
|
||||
static void
|
||||
ti_dpll_clock_put(void *priv, struct clk *clk)
|
||||
{
|
||||
}
|
||||
|
||||
static u_int
|
||||
ti_dpll_clock_get_rate(void *priv, struct clk *clk)
|
||||
{
|
||||
struct ti_dpll_clock_softc * const sc = priv;
|
||||
struct clk *clk_parent = clk_get_parent(clk);
|
||||
uint32_t val;
|
||||
uint64_t parent_rate;
|
||||
|
||||
if (clk_parent == NULL)
|
||||
return 0;
|
||||
|
||||
val = RD4(sc, REG_MULT_DIV1);
|
||||
const u_int mult = __SHIFTOUT(val, DPLL_MULT);
|
||||
const u_int div = __SHIFTOUT(val, DPLL_DIV) + 1;
|
||||
|
||||
parent_rate = clk_get_rate(clk_parent);
|
||||
|
||||
return (u_int)((mult * parent_rate) / div);
|
||||
}
|
||||
|
||||
static int
|
||||
ti_dpll_clock_set_rate(void *priv, struct clk *clk, u_int rate)
|
||||
{
|
||||
struct ti_dpll_clock_softc * const sc = priv;
|
||||
struct clk *clk_parent = clk_get_parent(clk);
|
||||
uint64_t parent_rate;
|
||||
uint32_t control, mult_div1;
|
||||
|
||||
if (clk_parent == NULL)
|
||||
return ENXIO;
|
||||
|
||||
parent_rate = clk_get_rate(clk_parent);
|
||||
|
||||
const u_int div = (parent_rate / 1000000) - 1;
|
||||
const u_int mult = rate / (parent_rate / (div + 1));
|
||||
if (mult < 2 || mult > 2047)
|
||||
return EINVAL;
|
||||
|
||||
control = RD4(sc, REG_CONTROL);
|
||||
control &= ~DPLL_EN;
|
||||
control |= __SHIFTIN(DPLL_EN_LOCK, DPLL_EN);
|
||||
WR4(sc, REG_CONTROL, control);
|
||||
|
||||
while ((RD4(sc, REG_IDLEST) & DPLL_EN_NM_BYPASS) != 0)
|
||||
;
|
||||
|
||||
mult_div1 = __SHIFTIN(mult, DPLL_MULT);
|
||||
mult_div1 |= __SHIFTIN(div, DPLL_DIV);
|
||||
WR4(sc, REG_MULT_DIV1, mult_div1);
|
||||
|
||||
control &= ~DPLL_EN;
|
||||
control |= __SHIFTIN(DPLL_EN_LOCK, DPLL_EN);
|
||||
WR4(sc, REG_CONTROL, control);
|
||||
|
||||
while ((RD4(sc, REG_IDLEST) & ST_DPLL_CLK) != 0)
|
||||
;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct clk *
|
||||
ti_dpll_clock_get_parent(void *priv, struct clk *clk)
|
||||
{
|
||||
struct ti_dpll_clock_softc * const sc = priv;
|
||||
|
||||
/* XXX assume ref clk */
|
||||
return fdtbus_clock_get_index(sc->sc_phandle, 0);
|
||||
}
|
211
sys/arch/arm/ti/ti_mux_clock.c
Normal file
211
sys/arch/arm/ti/ti_mux_clock.c
Normal file
@ -0,0 +1,211 @@
|
||||
/* $NetBSD: ti_mux_clock.c,v 1.1 2019/10/28 21:16:47 jmcneill Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2019 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.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: ti_mux_clock.c,v 1.1 2019/10/28 21:16:47 jmcneill Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/bus.h>
|
||||
|
||||
#include <dev/clk/clk_backend.h>
|
||||
|
||||
#include <dev/fdt/fdtvar.h>
|
||||
|
||||
static int ti_mux_clock_match(device_t, cfdata_t, void *);
|
||||
static void ti_mux_clock_attach(device_t, device_t, void *);
|
||||
|
||||
static struct clk *ti_mux_clock_decode(device_t, int, const void *, size_t);
|
||||
|
||||
static const struct fdtbus_clock_controller_func ti_mux_clock_fdt_funcs = {
|
||||
.decode = ti_mux_clock_decode
|
||||
};
|
||||
|
||||
static struct clk *ti_mux_clock_get(void *, const char *);
|
||||
static void ti_mux_clock_put(void *, struct clk *);
|
||||
static u_int ti_mux_clock_get_rate(void *, struct clk *);
|
||||
static struct clk *ti_mux_clock_get_parent(void *, struct clk *);
|
||||
static int ti_mux_clock_set_parent(void *, struct clk *, struct clk *);
|
||||
|
||||
static const struct clk_funcs ti_mux_clock_clk_funcs = {
|
||||
.get = ti_mux_clock_get,
|
||||
.put = ti_mux_clock_put,
|
||||
.get_rate = ti_mux_clock_get_rate,
|
||||
.get_parent = ti_mux_clock_get_parent,
|
||||
.set_parent = ti_mux_clock_set_parent,
|
||||
};
|
||||
|
||||
struct ti_mux_clock_softc {
|
||||
device_t sc_dev;
|
||||
int sc_phandle;
|
||||
bus_space_tag_t sc_bst;
|
||||
bus_space_handle_t sc_bsh;
|
||||
|
||||
struct clk_domain sc_clkdom;
|
||||
struct clk sc_clk;
|
||||
u_int sc_nparent;
|
||||
|
||||
uint32_t sc_mask;
|
||||
u_int sc_start_index;
|
||||
};
|
||||
|
||||
#define RD4(sc, reg) \
|
||||
bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
|
||||
#define WR4(sc, reg, val) \
|
||||
bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
|
||||
|
||||
CFATTACH_DECL_NEW(ti_mux_clock, sizeof(struct ti_mux_clock_softc),
|
||||
ti_mux_clock_match, ti_mux_clock_attach, NULL, NULL);
|
||||
|
||||
static int
|
||||
ti_mux_clock_match(device_t parent, cfdata_t cf, void *aux)
|
||||
{
|
||||
const char * const compatible[] = { "ti,mux-clock", NULL };
|
||||
const struct fdt_attach_args *faa = aux;
|
||||
|
||||
return of_match_compatible(faa->faa_phandle, compatible);
|
||||
}
|
||||
|
||||
static void
|
||||
ti_mux_clock_attach(device_t parent, device_t self, void *aux)
|
||||
{
|
||||
struct ti_mux_clock_softc * const sc = device_private(self);
|
||||
const struct fdt_attach_args *faa = aux;
|
||||
const int phandle = faa->faa_phandle;
|
||||
bus_addr_t addr, base_addr;
|
||||
u_int shift;
|
||||
|
||||
const int prcm_phandle = OF_parent(OF_parent(phandle));
|
||||
if (fdtbus_get_reg(phandle, 0, &addr, NULL) != 0 ||
|
||||
fdtbus_get_reg(prcm_phandle, 0, &base_addr, NULL) != 0) {
|
||||
aprint_error(": couldn't get registers\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sc->sc_dev = self;
|
||||
sc->sc_phandle = phandle;
|
||||
sc->sc_bst = faa->faa_bst;
|
||||
if (bus_space_map(sc->sc_bst, base_addr + addr, 4, 0, &sc->sc_bsh) != 0) {
|
||||
aprint_error(": couldn't map registers\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sc->sc_nparent = fdtbus_clock_count(phandle, "clocks");
|
||||
sc->sc_start_index = of_hasprop(phandle, "ti,index-starts-at-one") ? 1 : 0;
|
||||
sc->sc_nparent += sc->sc_start_index;
|
||||
while (!powerof2(sc->sc_nparent))
|
||||
sc->sc_nparent++;
|
||||
|
||||
if (of_getprop_uint32(phandle, "ti,bit-shift", &shift) != 0)
|
||||
shift = 0;
|
||||
|
||||
sc->sc_mask = (sc->sc_nparent - 1) << shift;
|
||||
|
||||
sc->sc_clkdom.name = device_xname(self);
|
||||
sc->sc_clkdom.funcs = &ti_mux_clock_clk_funcs;
|
||||
sc->sc_clkdom.priv = sc;
|
||||
|
||||
sc->sc_clk.domain = &sc->sc_clkdom;
|
||||
sc->sc_clk.name = kmem_asprintf("%s", faa->faa_name);
|
||||
clk_attach(&sc->sc_clk);
|
||||
|
||||
aprint_naive("\n");
|
||||
aprint_normal(": TI mux clock (%s)\n", sc->sc_clk.name);
|
||||
|
||||
fdtbus_register_clock_controller(self, phandle, &ti_mux_clock_fdt_funcs);
|
||||
}
|
||||
|
||||
static struct clk *
|
||||
ti_mux_clock_decode(device_t dev, int cc_phandle, const void *data,
|
||||
size_t len)
|
||||
{
|
||||
struct ti_mux_clock_softc * const sc = device_private(dev);
|
||||
|
||||
return &sc->sc_clk;
|
||||
}
|
||||
|
||||
static struct clk *
|
||||
ti_mux_clock_get(void *priv, const char *name)
|
||||
{
|
||||
struct ti_mux_clock_softc * const sc = priv;
|
||||
|
||||
return &sc->sc_clk;
|
||||
}
|
||||
|
||||
static void
|
||||
ti_mux_clock_put(void *priv, struct clk *clk)
|
||||
{
|
||||
}
|
||||
|
||||
static u_int
|
||||
ti_mux_clock_get_rate(void *priv, struct clk *clk)
|
||||
{
|
||||
struct clk *clk_parent = clk_get_parent(clk);
|
||||
|
||||
if (clk_parent == NULL)
|
||||
return 0;
|
||||
|
||||
return clk_get_rate(clk_parent);
|
||||
}
|
||||
|
||||
static struct clk *
|
||||
ti_mux_clock_get_parent(void *priv, struct clk *clk)
|
||||
{
|
||||
struct ti_mux_clock_softc * const sc = priv;
|
||||
uint32_t val;
|
||||
u_int sel;
|
||||
|
||||
val = RD4(sc, 0);
|
||||
sel = __SHIFTOUT(val, sc->sc_mask);
|
||||
|
||||
return fdtbus_clock_get_index(sc->sc_phandle, sel - sc->sc_start_index);
|
||||
}
|
||||
|
||||
static int
|
||||
ti_mux_clock_set_parent(void *priv, struct clk *clk, struct clk *parent_clk)
|
||||
{
|
||||
struct ti_mux_clock_softc * const sc = priv;
|
||||
uint32_t val;
|
||||
u_int sel;
|
||||
|
||||
for (sel = sc->sc_start_index; sel < sc->sc_nparent; sel++) {
|
||||
if (fdtbus_clock_get_index(sc->sc_phandle, sel - sc->sc_start_index) == parent_clk)
|
||||
break;
|
||||
}
|
||||
if (sel == sc->sc_nparent)
|
||||
return EINVAL;
|
||||
|
||||
val = RD4(sc, 0);
|
||||
val &= ~sc->sc_mask;
|
||||
val |= __SHIFTIN(sel, sc->sc_mask);
|
||||
WR4(sc, 0, val);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user