arm64: allwinner: a64: Add PLL_MIPI

PLL_MIPI is the last important PLL that we missed.
Add support for it.
Since it's one of the possible parent for TCON0 also add this clock
now that we can.
While here add some info about what video related clocks should be
enabled at boot and with what frequency.
This commit is contained in:
Emmanuel Vadot 2019-09-28 22:14:33 +00:00
parent 0432618600
commit f99055394d
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=352848
7 changed files with 418 additions and 5 deletions

View File

@ -311,6 +311,8 @@ aw_ccung_attach(device_t dev)
case AW_CLK_FRAC:
aw_clk_frac_register(sc->clkdom, sc->clks[i].clk.frac);
break;
case AW_CLK_MIPI:
aw_clk_mipi_register(sc->clkdom, sc->clks[i].clk.mipi);
}
}

View File

@ -32,6 +32,7 @@
#include <arm/allwinner/clkng/aw_clk.h>
#include <arm/allwinner/clkng/aw_clk_m.h>
#include <arm/allwinner/clkng/aw_clk_mipi.h>
#include <arm/allwinner/clkng/aw_clk_nkmp.h>
#include <arm/allwinner/clkng/aw_clk_nm.h>
#include <arm/allwinner/clkng/aw_clk_prediv_mux.h>
@ -50,6 +51,7 @@ enum aw_ccung_clk_type {
AW_CLK_PREDIV_MUX,
AW_CLK_FRAC,
AW_CLK_M,
AW_CLK_MIPI,
};
struct aw_ccung_clk {
@ -63,6 +65,7 @@ struct aw_ccung_clk {
struct aw_clk_prediv_mux_def *prediv_mux;
struct aw_clk_frac_def *frac;
struct aw_clk_m_def *m;
struct aw_clk_mipi_def *mipi;
} clk;
};

View File

@ -73,6 +73,8 @@ struct aw_clk_init {
#define AW_CLK_FACTOR_HAS_COND 0x0004
#define AW_CLK_FACTOR_FIXED 0x0008
#define AW_CLK_FACTOR_ZERO_IS_ONE 0x0010
#define AW_CLK_FACTOR_MIN_VALUE 0x0020
#define AW_CLK_FACTOR_MAX_VALUE 0x0040
struct aw_clk_factor {
uint32_t shift; /* Shift bits for the factor */
@ -85,6 +87,9 @@ struct aw_clk_factor {
uint32_t cond_width;
uint32_t cond_value;
uint32_t min_value;
uint32_t max_value;
uint32_t flags; /* Flags */
};
@ -147,6 +152,8 @@ aw_clk_factor_get_min(struct aw_clk_factor *factor)
min = factor->value;
else if (factor->flags & AW_CLK_FACTOR_ZERO_BASED)
min = 0;
else if (factor->flags & AW_CLK_FACTOR_MIN_VALUE)
min = factor->min_value;
else
min = 1;
@ -166,7 +173,9 @@ aw_clk_factor_get_value(struct aw_clk_factor *factor, uint32_t raw)
else if (factor->flags & AW_CLK_FACTOR_POWER_OF_TWO) {
for (val = 0; raw != 1; val++)
raw >>= 1;
} else
} else if (factor->flags & AW_CLK_FACTOR_MAX_VALUE)
val = factor->max_value;
else
val = raw - 1;
return (val);
@ -457,6 +466,32 @@ aw_clk_factor_get_value(struct aw_clk_factor *factor, uint32_t raw)
.prediv.cond_value = _prediv_cond_value, \
}
#define MIPI_CLK(_clkname, _id, _name, _pnames, \
_offset, \
_kshift, _kwidth, _kflags, _kmin, \
_mshift, _mwidth, \
_nshift, _nwidth, \
_gate_shift, _lock_shift) \
static struct aw_clk_mipi_def _clkname = { \
.clkdef = { \
.id = _id, \
.name = _name, \
.parent_names = _pnames, \
.parent_cnt = nitems(_pnames) \
}, \
.offset = _offset, \
.k.shift = _kshift, \
.k.width = _kwidth, \
.k.flags = _kflags, \
.k.min_value = _kmin, \
.m.shift = _mshift, \
.m.width = _mwidth, \
.n.shift = _nshift, \
.n.width = _nwidth, \
.gate_shift = _gate_shift, \
.lock_shift = _lock_shift, \
}
#define MUX_CLK(_clkname, _id, _name, _pnames, \
_offset, _shift, _width) \
static struct clk_mux_def _clkname = { \

View File

@ -0,0 +1,300 @@
/*-
* Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
*
* 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$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <dev/extres/clk/clk.h>
#include <arm/allwinner/clkng/aw_clk.h>
#include <arm/allwinner/clkng/aw_clk_mipi.h>
#include "clkdev_if.h"
/* #define dprintf(format, arg...) printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) */
#define dprintf(format, arg...)
/*
* clknode for PLL_MIPI :
*
* clk = (pll_video0 * n * k) / m when vfb_sel=0
* clk depend on sint_frac, sdiv2, s6p25_7p5, pll_feedback_div when vfb_sel=1
*
*/
struct aw_clk_mipi_sc {
uint32_t offset;
struct aw_clk_factor k;
struct aw_clk_factor m;
struct aw_clk_factor n;
uint64_t min_freq;
uint64_t max_freq;
uint32_t gate_shift;
uint32_t lock_shift;
uint32_t lock_retries;
uint32_t flags;
};
#define WRITE4(_clk, off, val) \
CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
#define READ4(_clk, off, val) \
CLKDEV_READ_4(clknode_get_device(_clk), off, val)
#define DEVICE_LOCK(_clk) \
CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
#define DEVICE_UNLOCK(_clk) \
CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
#define LDO1_EN_SHIFT 23
#define LDO2_EN_SHIFT 22
#define VFB_SEL_SHIFT 16
static int
aw_clk_mipi_init(struct clknode *clk, device_t dev)
{
struct aw_clk_mipi_sc *sc;
sc = clknode_get_softc(clk);
clknode_init_parent_idx(clk, 0);
return (0);
}
static int
aw_clk_mipi_set_gate(struct clknode *clk, bool enable)
{
struct aw_clk_mipi_sc *sc;
uint32_t val;
sc = clknode_get_softc(clk);
dprintf("%sabling gate\n", enable ? "En" : "Dis");
DEVICE_LOCK(clk);
READ4(clk, sc->offset, &val);
if (enable) {
val |= (1 << sc->gate_shift);
val |= (1 << LDO1_EN_SHIFT);
val |= (1 << LDO2_EN_SHIFT);
} else {
val &= ~(1 << sc->gate_shift);
val &= ~(1 << LDO1_EN_SHIFT);
val &= ~(1 << LDO2_EN_SHIFT);
}
WRITE4(clk, sc->offset, val);
DEVICE_UNLOCK(clk);
return (0);
}
static uint64_t
aw_clk_mipi_find_best(struct aw_clk_mipi_sc *sc, uint64_t fparent, uint64_t *fout,
uint32_t *factor_k, uint32_t *factor_m, uint32_t *factor_n)
{
uint64_t cur, best;
uint32_t n, k, m;
best = 0;
*factor_n = 0;
*factor_k = 0;
*factor_m = 0;
for (n = aw_clk_factor_get_min(&sc->n); n <= aw_clk_factor_get_max(&sc->n); ) {
for (k = aw_clk_factor_get_min(&sc->k); k <= aw_clk_factor_get_max(&sc->k); ) {
for (m = aw_clk_factor_get_min(&sc->m); m <= aw_clk_factor_get_max(&sc->m); ) {
cur = (fparent * n * k) / m;
if ((*fout - cur) < (*fout - best)) {
best = cur;
*factor_n = n;
*factor_k = k;
*factor_m = m;
}
if (best == *fout)
return (best);
m++;
}
k++;
}
n++;
}
return best;
}
static int
aw_clk_mipi_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
int flags, int *stop)
{
struct aw_clk_mipi_sc *sc;
uint64_t best = 0;
uint32_t best_k, best_m, best_n;
uint32_t k, m, n;
uint32_t val;
uint32_t retry;
sc = clknode_get_softc(clk);
best = aw_clk_mipi_find_best(sc, fparent, fout, &best_k, &best_m, &best_n);
if (best < sc->min_freq ||
best > sc->max_freq) {
printf("%s: Cannot set %ju for %s (min=%ju max=%ju)\n",
__func__, best, clknode_get_name(clk),
sc->min_freq, sc->max_freq);
return (ERANGE);
}
if ((flags & CLK_SET_DRYRUN) != 0) {
*fout = best;
*stop = 1;
return (0);
}
DEVICE_LOCK(clk);
READ4(clk, sc->offset, &val);
/* Disable clock during freq changes */
val &= ~(1 << sc->gate_shift);
WRITE4(clk, sc->offset, val);
k = aw_clk_factor_get_value(&sc->k, best_k);
n = aw_clk_factor_get_value(&sc->n, best_n);
m = aw_clk_factor_get_value(&sc->m, best_m);
val &= ~sc->k.mask;
val &= ~sc->m.mask;
val &= ~sc->n.mask;
val |= k << sc->k.shift;
val |= m << sc->m.shift;
val |= n << sc->n.shift;
/* Write the clock changes */
WRITE4(clk, sc->offset, val);
/* Enable clock now that we've change it */
val |= 1 << sc->gate_shift;
WRITE4(clk, sc->offset, val);
DEVICE_UNLOCK(clk);
for (retry = 0; retry < sc->lock_retries; retry++) {
READ4(clk, sc->offset, &val);
if ((val & (1 << sc->lock_shift)) != 0)
break;
DELAY(1000);
}
*fout = best;
*stop = 1;
return (0);
}
static int
aw_clk_mipi_recalc(struct clknode *clk, uint64_t *freq)
{
struct aw_clk_mipi_sc *sc;
uint32_t val, m, n, k;
sc = clknode_get_softc(clk);
DEVICE_LOCK(clk);
READ4(clk, sc->offset, &val);
DEVICE_UNLOCK(clk);
k = aw_clk_get_factor(val, &sc->k);
m = aw_clk_get_factor(val, &sc->m);
n = aw_clk_get_factor(val, &sc->n);
*freq = (*freq * n * k) / m;
return (0);
}
static clknode_method_t aw_mipi_clknode_methods[] = {
/* Device interface */
CLKNODEMETHOD(clknode_init, aw_clk_mipi_init),
CLKNODEMETHOD(clknode_set_gate, aw_clk_mipi_set_gate),
CLKNODEMETHOD(clknode_recalc_freq, aw_clk_mipi_recalc),
CLKNODEMETHOD(clknode_set_freq, aw_clk_mipi_set_freq),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(aw_mipi_clknode, aw_mipi_clknode_class, aw_mipi_clknode_methods,
sizeof(struct aw_clk_mipi_sc), clknode_class);
int
aw_clk_mipi_register(struct clkdom *clkdom, struct aw_clk_mipi_def *clkdef)
{
struct clknode *clk;
struct aw_clk_mipi_sc *sc;
clk = clknode_create(clkdom, &aw_mipi_clknode_class, &clkdef->clkdef);
if (clk == NULL)
return (1);
sc = clknode_get_softc(clk);
sc->offset = clkdef->offset;
sc->k.shift = clkdef->k.shift;
sc->k.width = clkdef->k.width;
sc->k.mask = ((1 << sc->k.width) - 1) << sc->k.shift;
sc->k.value = clkdef->k.value;
sc->k.flags = clkdef->k.flags;
sc->k.min_value = clkdef->k.min_value;
sc->m.shift = clkdef->m.shift;
sc->m.width = clkdef->m.width;
sc->m.mask = ((1 << sc->m.width) - 1) << sc->m.shift;
sc->m.value = clkdef->m.value;
sc->m.flags = clkdef->m.flags;
sc->m.min_value = clkdef->m.min_value;
sc->n.shift = clkdef->n.shift;
sc->n.width = clkdef->n.width;
sc->n.mask = ((1 << sc->n.width) - 1) << sc->n.shift;
sc->n.value = clkdef->n.value;
sc->n.flags = clkdef->n.flags;
sc->n.min_value = clkdef->n.min_value;
sc->min_freq = clkdef->min_freq;
sc->max_freq = clkdef->max_freq;
sc->gate_shift = clkdef->gate_shift;
sc->lock_shift = clkdef->lock_shift;
sc->lock_retries = clkdef->lock_retries;
sc->flags = clkdef->flags;
clknode_register(clkdom, clk);
return (0);
}

View File

@ -0,0 +1,53 @@
/*-
* Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
*
* 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$
*/
#ifndef __AW_CLK_MIPI_H__
#define __AW_CLK_MIPI_H__
#include <dev/extres/clk/clk.h>
struct aw_clk_mipi_def {
struct clknode_init_def clkdef;
uint32_t offset;
struct aw_clk_factor k;
struct aw_clk_factor m;
struct aw_clk_factor n;
uint64_t min_freq;
uint64_t max_freq;
uint32_t gate_shift;
uint32_t lock_shift;
uint32_t lock_retries;
uint32_t flags;
};
int aw_clk_mipi_register(struct clkdom *clkdom, struct aw_clk_mipi_def *clkdef);
#endif /* __AW_CLK_MIPI_H__ */

View File

@ -70,6 +70,7 @@ __FBSDID("$FreeBSD$");
#define CLK_PLL_PERIPH1_2X 14
#define CLK_PLL_VIDEO1 15
#define CLK_PLL_GPU 16
#define CLK_PLL_MIPI 17
#define CLK_PLL_HSIC 18
#define CLK_PLL_DE 19
#define CLK_PLL_DDR1 20
@ -397,7 +398,15 @@ FRAC_CLK(pll_gpu_clk,
24, 25, /* mode sel, freq sel */
192000000, 600000000); /* min freq, max freq */
/* PLL MIPI is missing */
static const char *pll_mipi_parents[] = {"pll_video0"};
MIPI_CLK(pll_mipi_clk,
CLK_PLL_MIPI,
"pll_mipi", pll_mipi_parents,
0x40,
4, 2, AW_CLK_FACTOR_MIN_VALUE, 2,
0, 3,
8, 4,
31, 28);
static const char *pll_hsic_parents[] = {"osc24M"};
FRAC_CLK(pll_hsic_clk,
@ -642,8 +651,13 @@ M_CLK(de_clk,
31, /* gate */
AW_CLK_HAS_MUX | AW_CLK_HAS_GATE); /* flags */
/* TCON0/1 Needs mux table */
static const char *tcon1_parents[] = {"pll_video0", "pll_video0", "pll_video1"};
static const char *tcon0_parents[] = {"pll_mipi", NULL, "pll_video0-2x"};
MUX_CLK(tcon0_clk,
CLK_TCON0, /* id */
"tcon0", tcon0_parents, /* name, parents */
0x118, 24, 2); /* offset, shift, width */
static const char *tcon1_parents[] = {"pll_video0", NULL, "pll_video1"};
M_CLK(tcon1_clk,
CLK_TCON1, "tcon1", tcon1_parents, /* id, name, parents */
0x11C, /* offset */
@ -726,7 +740,7 @@ static struct aw_ccung_clk a64_ccu_clks[] = {
{ .type = AW_CLK_NKMP, .clk.nkmp = &pll_periph1_2x_clk},
{ .type = AW_CLK_FRAC, .clk.frac = &pll_video1_clk},
{ .type = AW_CLK_FRAC, .clk.frac = &pll_gpu_clk},
/* PLL_MIPI */
{ .type = AW_CLK_MIPI, .clk.mipi = &pll_mipi_clk},
{ .type = AW_CLK_FRAC, .clk.frac = &pll_hsic_clk},
{ .type = AW_CLK_FRAC, .clk.frac = &pll_de_clk},
{ .type = AW_CLK_NKMP, .clk.nkmp = &pll_ddr1_clk},
@ -757,6 +771,7 @@ static struct aw_ccung_clk a64_ccu_clks[] = {
{ .type = AW_CLK_MUX, .clk.mux = &i2s0mux_clk},
{ .type = AW_CLK_MUX, .clk.mux = &i2s1mux_clk},
{ .type = AW_CLK_MUX, .clk.mux = &i2s2mux_clk},
{ .type = AW_CLK_MUX, .clk.mux = &tcon0_clk},
{ .type = AW_CLK_DIV, .clk.div = &axi_clk},
{ .type = AW_CLK_DIV, .clk.div = &apb1_clk},
{ .type = AW_CLK_DIV, .clk.div = &apb_clk},
@ -774,6 +789,10 @@ static struct aw_clk_init a64_init_clks[] = {
{"ahb1", "pll_periph0", 0, false},
{"ahb2", "pll_periph0", 0, false},
{"dram", "pll_ddr0", 0, false},
{"pll_de", NULL, 432000000, true},
{"de", "pll_de", 0, true},
{"tcon0", "pll_video0-2x", 0, false},
{"tcon1", "pll_video1", 0, false},
};
static int

View File

@ -51,6 +51,7 @@ arm/allwinner/if_awg.c optional awg ext_resources syscon aw_sid nvmem fdt
arm/allwinner/clkng/aw_ccung.c optional aw_ccu fdt
arm/allwinner/clkng/aw_clk_frac.c optional aw_ccu fdt
arm/allwinner/clkng/aw_clk_m.c optional aw_ccu fdt
arm/allwinner/clkng/aw_clk_mipi.c optional aw_ccu fdt
arm/allwinner/clkng/aw_clk_nkmp.c optional aw_ccu fdt
arm/allwinner/clkng/aw_clk_nm.c optional aw_ccu fdt
arm/allwinner/clkng/aw_clk_prediv_mux.c optional aw_ccu fdt