Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F151843220
D56071.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
5 KB
Referenced Files
None
Subscribers
None
D56071.diff
View Options
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, ¤t_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, ¤t_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
Details
Attached
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)
Attached To
Mode
D56071: dev/clk/broadcom: bcm_clk_periph: add mux switching and parent selection
Attached
Detach File
Event Timeline
Log In to Comment