Page MenuHomeFreeBSD

D56071.diff
No OneTemporary

D56071.diff

diff --git a/sys/dev/clk/broadcom/bcm_clk_periph.h b/sys/dev/clk/broadcom/bcm_clk_periph.h
--- a/sys/dev/clk/broadcom/bcm_clk_periph.h
+++ b/sys/dev/clk/broadcom/bcm_clk_periph.h
@@ -33,7 +33,8 @@
#include <dev/clk/clk.h>
/* Flags for bcm_clk_periph_def.flags */
-#define BCM_CLK_PERIPH_HAVE_MASH (1u << 0)
+#define BCM_CLK_PERIPH_HAVE_MASH (1u << 0)
+#define BCM_CLK_PERIPH_NEED_LOW_JITTER (1u << 1)
struct bcm_clk_periph_def {
struct clknode_init_def clkdef;
diff --git a/sys/dev/clk/broadcom/bcm_clk_periph.c b/sys/dev/clk/broadcom/bcm_clk_periph.c
--- a/sys/dev/clk/broadcom/bcm_clk_periph.c
+++ b/sys/dev/clk/broadcom/bcm_clk_periph.c
@@ -26,7 +26,6 @@
*/
#include <sys/cdefs.h>
-
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
@@ -60,10 +59,14 @@
CLKDEV_WRITE_4(clknode_get_device(_clk), (_off), (_val))
#define READ4(_clk, _off, _val) \
CLKDEV_READ_4(clknode_get_device(_clk), (_off), (_val))
+#define MODIFY4(_clk, _off, _clr, _set) \
+ CLKDEV_MODIFY_4(clknode_get_device(_clk), (_off), (_clr), (_set))
#define LOCK(_clk) CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
#define UNLOCK(_clk) CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+#define BCM_CLK_PERIPH_BUSY_TIMEOUT_US 100000
+
static int
bcm_clk_periph_init(struct clknode *clk, device_t dev)
{
@@ -113,7 +116,7 @@
return (ERANGE);
fin_scaled = fin << 12;
- min_div = (sc->flags | BCM_CLK_PERIPH_HAVE_MASH) ? (2u << 12) : (1u << 12);
+ min_div = (sc->flags & BCM_CLK_PERIPH_HAVE_MASH) ? (2u << 12) : (1u << 12);
max_div = sc->div_int_mask | sc->div_frac_mask;
switch (CLK_SET_ROUND(flags)) {
@@ -147,13 +150,118 @@
return (0);
}
+static int
+bcm_clk_periph_set_mux(struct clknode *clk, int idx)
+{
+ struct bcm_clk_periph_sc *sc;
+
+ sc = clknode_get_softc(clk);
+
+ if (idx < 0)
+ return (ERANGE);
+
+ LOCK(clk);
+ MODIFY4(clk, sc->ctl_offset, sc->src_mask,
+ ((uint32_t)idx << sc->src_shift) & sc->src_mask);
+ UNLOCK(clk);
+
+ return (0);
+}
+
+static int
+bcm_clk_periph_sel_parent(struct clknode *clk, uint64_t req, int flags,
+ uint64_t *fparentp, int *parent_idx)
+{
+ const char **pnames;
+ struct clknode *current_pclk;
+ struct bcm_clk_periph_sc *sc;
+ uint64_t current_pfreq, actual, low, high;
+ uint32_t div, low_div, high_div;
+ uint32_t i, pcnt, current_pidx, choosed_pidx;
+ uint64_t metric,
+ lowest_metric; /* Prevent potential subtration underflow. */
+
+ pnames = clknode_get_parent_names(clk);
+ pcnt = clknode_get_parents_num(clk);
+ current_pidx = clknode_get_parent_idx(clk);
+ choosed_pidx = current_pidx;
+
+ actual = 0;
+
+ lowest_metric = 0;
+
+ for (i = 0; i < pcnt; i++) {
+ /* Find parent clock that fits targeted freq. */
+ if (pnames[i] == NULL)
+ continue;
+
+ current_pclk = clknode_find_by_name(pnames[i]);
+ if (current_pclk == NULL)
+ continue;
+
+ if (clknode_get_freq(current_pclk, &current_pfreq) != 0 ||
+ current_pfreq == 0)
+ continue;
+
+ sc = clknode_get_softc(clk);
+ if (bcm_clk_periph_calc_div(sc, current_pfreq, req,
+ CLK_SET_ROUND_ANY, &div, &actual) != 0)
+ continue;
+
+ /* If we don't need low jitter, previous checks are enough. */
+ if ((sc->flags & BCM_CLK_PERIPH_NEED_LOW_JITTER) == 0) {
+ if ((flags & CLK_SET_DRYRUN) == 0 && i != current_pidx)
+ *parent_idx = i;
+ *fparentp = current_pfreq;
+ return (0);
+ }
+
+ if ((div & sc->div_frac_mask) == 0 && actual == req) {
+ if ((flags & CLK_SET_DRYRUN) == 0 && i != current_pidx)
+ *parent_idx = i;
+ *fparentp = current_pfreq;
+ return (0);
+ }
+
+ low_div = div & sc->div_int_mask;
+ high_div = low_div + (1u << 12);
+
+ if (low_div == 0)
+ continue;
+
+ low = (current_pfreq << 12) / low_div;
+ high = (current_pfreq << 12) / high_div;
+
+ metric = max(low - actual, actual - high);
+ if (metric < lowest_metric) {
+ lowest_metric = metric;
+ choosed_pidx = i;
+ }
+ }
+
+ if (actual == 0)
+ return (ERANGE);
+
+ if (actual == req) {
+ if ((flags & CLK_SET_DRYRUN) == 0 &&
+ choosed_pidx != current_pidx)
+ *parent_idx = choosed_pidx;
+ current_pclk = clknode_find_by_name(pnames[choosed_pidx]);
+ clknode_get_freq(current_pclk, &current_pfreq);
+ *fparentp = current_pfreq;
+ return (0);
+ }
+
+ return (ERANGE);
+}
+
static int
bcm_clk_wait_while_busy(struct clknode *clk, struct bcm_clk_periph_sc *sc)
{
uint32_t val;
int retry;
- for (retry = 0; retry < 50; retry++) {
+ for (retry = 0; retry < BCM_CLK_PERIPH_BUSY_TIMEOUT_US; retry++) {
READ4(clk, sc->ctl_offset, &val);
if ((val & (1u << sc->busy_shift)) == 0)
return (0);
@@ -171,9 +279,16 @@
uint32_t ctl, div;
bool was_enabled;
int rv;
+ int parent_idx;
sc = clknode_get_softc(clk);
req = *fout;
+ parent_idx = clknode_get_parent_idx(clk);
+
+ rv = bcm_clk_periph_sel_parent(clk, req, flags, &fparent, &parent_idx);
+ if (rv != 0)
+ return (rv);
+
rv = bcm_clk_periph_calc_div(sc, fparent, req, flags, &div, &actual);
if (rv != 0)
return (rv);
@@ -198,7 +313,11 @@
UNLOCK(clk);
return (rv);
}
+ UNLOCK(clk);
+ clknode_set_parent_by_idx(clk, parent_idx);
+
+ LOCK(clk);
READ4(clk, sc->ctl_offset, &ctl);
WRITE4(clk, sc->div_offset, div);
@@ -229,7 +348,8 @@
UNLOCK(clk);
/* Extract the full integer+fractional divider field (12.12
- * fixed-point). */
+ * fixed-point).
+ */
div &= (sc->div_int_mask | sc->div_frac_mask);
if (div == 0)
return (0);
@@ -244,6 +364,7 @@
CLKNODEMETHOD(clknode_set_gate, bcm_clk_ctl_periph_gate),
CLKNODEMETHOD(clknode_recalc_freq, bcm_clk_periph_recalc),
CLKNODEMETHOD(clknode_set_freq, bcm_clk_periph_set_freq),
+ CLKNODEMETHOD(clknode_set_mux, bcm_clk_periph_set_mux),
CLKNODEMETHOD_END
};

File Metadata

Mime Type
text/plain
Expires
Sun, Apr 12, 1:19 AM (20 h, 41 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30359457
Default Alt Text
D56071.diff (5 KB)

Event Timeline