arm64: rockchip, implement the two rk805/808 clocks

While the xin32k clk was implemented in rk3399_cru as a fixed rate
clock, migrate it to rk805 as we will also need the 2nd clock
'rtc_clko_wifi' for SDIO and BT.
Both clocks remain fixed rate, and while the 1st one is always on
(though that is not expressed in the clk framework), the 2nd one
we can toggle on/off.

Reviewed-by:	manu
Tested-by:	manu
MFC-after:	2 weeks
Differential Revision:	https://reviews.freebsd.org/D26870
This commit is contained in:
Bjoern A. Zeeb 2021-05-06 14:25:52 +00:00
parent 7de0aa0135
commit f0a5e81af4
2 changed files with 120 additions and 4 deletions

View File

@ -775,7 +775,7 @@ PLIST(uart3_p)= {"clk_uart3_div", "clk_uart3_frac", "xin24m"};
static struct rk_clk rk3399_clks[] = {
/* External clocks */
LINK("xin24m"),
FRATE(0, "xin32k", 32768),
LINK("xin32k"),
FFACT(0, "xin12m", "xin24m", 1, 2),
FRATE(0, "clkin_i2s", 0),
FRATE(0, "pclkin_cif", 0),

View File

@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/extres/clk/clk.h>
#include <dev/extres/regulator/regulator.h>
#include <arm64/rockchip/rk805reg.h>
@ -584,6 +585,118 @@ rk805_reg_attach(device_t dev, phandle_t node,
return (reg_sc);
}
/* -------------------------------------------------------------------------- */
/* Clock class and method */
struct rk805_clk_sc {
device_t base_dev;
};
#define CLK32OUT_REG 0x20
#define CLK32OUT_CLKOUT2_EN 1
static int
rk805_clk_set_gate_1(struct clknode *clk, bool enable)
{
struct rk805_clk_sc *sc;
uint8_t val;
sc = clknode_get_softc(clk);
rk805_read(sc->base_dev, CLK32OUT_REG, &val, sizeof(val));
if (enable)
val |= CLK32OUT_CLKOUT2_EN;
else
val &= ~CLK32OUT_CLKOUT2_EN;
rk805_write(sc->base_dev, CLK32OUT_REG, &val, 1);
return (0);
}
static int
rk805_clk_recalc(struct clknode *clk, uint64_t *freq)
{
*freq = 32768;
return (0);
}
static clknode_method_t rk805_clk_clknode_methods_0[] = {
CLKNODEMETHOD(clknode_recalc_freq, rk805_clk_recalc),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(rk805_clk_clknode_0, rk805_clk_clknode_class_0,
rk805_clk_clknode_methods_0, sizeof(struct rk805_clk_sc),
clknode_class);
static clknode_method_t rk805_clk_clknode_methods_1[] = {
CLKNODEMETHOD(clknode_set_gate, rk805_clk_set_gate_1),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(rk805_clk_clknode_1, rk805_clk_clknode_class_1,
rk805_clk_clknode_methods_1, sizeof(struct rk805_clk_sc),
rk805_clk_clknode_class_0);
static int
rk805_export_clocks(device_t dev)
{
struct clkdom *clkdom;
struct clknode_init_def clkidef;
struct clknode *clk;
struct rk805_clk_sc *clksc;
const char **clknames;
phandle_t node;
int nclks, rv;
node = ofw_bus_get_node(dev);
/* clock-output-names are optional. Could use them for clkidef.name. */
nclks = ofw_bus_string_list_to_array(node, "clock-output-names",
&clknames);
clkdom = clkdom_create(dev);
memset(&clkidef, 0, sizeof(clkidef));
clkidef.id = 0;
clkidef.name = (nclks = 2) ? clknames[0] : "clk32kout1";
clk = clknode_create(clkdom, &rk805_clk_clknode_class_0, &clkidef);
if (clk == NULL) {
device_printf(dev, "Cannot create '%s'.\n", clkidef.name);
return (ENXIO);
}
clksc = clknode_get_softc(clk);
clksc->base_dev = dev;
clknode_register(clkdom, clk);
memset(&clkidef, 0, sizeof(clkidef));
clkidef.id = 1;
clkidef.name = (nclks = 2) ? clknames[1] : "clk32kout2";
clk = clknode_create(clkdom, &rk805_clk_clknode_class_1, &clkidef);
if (clk == NULL) {
device_printf(dev, "Cannot create '%s'.\n", clkidef.name);
return (ENXIO);
}
clksc = clknode_get_softc(clk);
clksc->base_dev = dev;
clknode_register(clkdom, clk);
rv = clkdom_finit(clkdom);
if (rv != 0) {
device_printf(dev, "Cannot finalize clkdom initialization: "
"%d\n", rv);
return (ENXIO);
}
if (bootverbose)
clkdom_dump(clkdom);
return (0);
}
/* -------------------------------------------------------------------------- */
static int
rk805_probe(device_t dev)
{
@ -745,17 +858,20 @@ rk805_attach(device_t dev)
struct rk805_regdef *regdefs;
struct reg_list *regp;
phandle_t rnode, child;
int i;
int error, i;
sc = device_get_softc(dev);
sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
error = rk805_export_clocks(dev);
if (error != 0)
return (error);
sc->intr_hook.ich_func = rk805_start;
sc->intr_hook.ich_arg = dev;
if (config_intrhook_establish(&sc->intr_hook) != 0)
return (ENOMEM);
sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
switch (sc->type) {
case RK805:
regdefs = rk805_regdefs;