Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 8 Nov 2019 18:57:41 +0000 (UTC)
From:      Michal Meloun <mmel@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r354554 - in head/sys: conf dev/extres/clk
Message-ID:  <201911081857.xA8Ivf2J092145@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mmel
Date: Fri Nov  8 18:57:41 2019
New Revision: 354554
URL: https://svnweb.freebsd.org/changeset/base/354554

Log:
  Implement support for (soft)linked clocks.
  This kind of clock nodes represent temporary placeholder for clocks
  defined later in boot process. Also, these are necessary to break
  circular dependencies occasionally occurring in complex clock graphs.
  
  MFC after: 3 weeks

Added:
  head/sys/dev/extres/clk/clk_link.c   (contents, props changed)
  head/sys/dev/extres/clk/clk_link.h   (contents, props changed)
Modified:
  head/sys/conf/files
  head/sys/dev/extres/clk/clk.c
  head/sys/dev/extres/clk/clk.h

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Fri Nov  8 18:56:02 2019	(r354553)
+++ head/sys/conf/files	Fri Nov  8 18:57:41 2019	(r354554)
@@ -1681,6 +1681,7 @@ dev/extres/clk/clk_bus.c	optional ext_resources clk fd
 dev/extres/clk/clk_div.c	optional ext_resources clk fdt
 dev/extres/clk/clk_fixed.c	optional ext_resources clk fdt
 dev/extres/clk/clk_gate.c	optional ext_resources clk fdt
+dev/extres/clk/clk_link.c	optional ext_resources clk fdt
 dev/extres/clk/clk_mux.c	optional ext_resources clk fdt
 dev/extres/phy/phy.c		optional ext_resources phy fdt
 dev/extres/phy/phydev_if.m	optional ext_resources phy fdt

Modified: head/sys/dev/extres/clk/clk.c
==============================================================================
--- head/sys/dev/extres/clk/clk.c	Fri Nov  8 18:56:02 2019	(r354553)
+++ head/sys/dev/extres/clk/clk.c	Fri Nov  8 18:57:41 2019	(r354554)
@@ -189,6 +189,9 @@ enum clknode_sysctl_type {
 static int clknode_sysctl(SYSCTL_HANDLER_ARGS);
 static int clkdom_sysctl(SYSCTL_HANDLER_ARGS);
 
+static void clknode_finish(void *dummy);
+SYSINIT(clknode_finish, SI_SUB_LAST, SI_ORDER_ANY, clknode_finish, NULL);
+
 /*
  * Default clock methods for base class.
  */
@@ -526,20 +529,71 @@ clknode_create(struct clkdom * clkdom, clknode_class_t
 {
 	struct clknode *clknode;
 	struct sysctl_oid *clknode_oid;
+	bool replaced;
 
 	KASSERT(def->name != NULL, ("clock name is NULL"));
 	KASSERT(def->name[0] != '\0', ("clock name is empty"));
-#ifdef   INVARIANTS
+	if (def->flags & CLK_NODE_LINKED) {
+		KASSERT(def->parent_cnt == 0,
+		 ("Linked clock must not have parents"));
+		KASSERT(clknode_class->size== 0,
+		 ("Linked clock cannot have own softc"));
+	}
+
+	/* Process duplicated clocks */
 	CLK_TOPO_SLOCK();
-	if (clknode_find_by_name(def->name) != NULL)
-		panic("Duplicated clock registration: %s\n", def->name);
+	clknode = clknode_find_by_name(def->name);
 	CLK_TOPO_UNLOCK();
-#endif
+		if (clknode !=  NULL) {
+		if (!(clknode->flags & CLK_NODE_LINKED) &&
+		    def->flags & CLK_NODE_LINKED) {
+			/*
+			 * New clock is linked and real already exists.
+			 * Do nothing and return real node. It is in right
+			 * domain, enqueued in right lists and fully initialized.
+			 */
+			return (clknode);
+		} else if (clknode->flags & CLK_NODE_LINKED &&
+		   !(def->flags & CLK_NODE_LINKED)) {
+			/*
+			 * New clock is real but linked already exists.
+			 * Remove old linked node from originating domain
+			 * (real clock must be owned by another) and from
+			 * global names link (it will be added back into it
+			 * again in following clknode_register()). Then reuse
+			 * original clknode structure and reinitialize it
+			 * with new dat. By this, all lists containing this
+			 * node remains valid, but the new node virtually
+			 * replace the linked one.
+			 */
+			KASSERT(clkdom != clknode->clkdom,
+			    ("linked clock must be from another "
+			    "domain that real one"));
+			TAILQ_REMOVE(&clkdom->clknode_list, clknode,
+			    clkdom_link);
+			TAILQ_REMOVE(&clknode_list, clknode, clklist_link);
+			replaced = true;
+		} else if (clknode->flags & CLK_NODE_LINKED &&
+		   def->flags & CLK_NODE_LINKED) {
+			/*
+			 * Both clocks are linked.
+			 * Return old one, so we hold only one copy od link.
+			 */
+			return (clknode);
+		} else {
+			/* Both clocks are real */
+			panic("Duplicated clock registration: %s\n", def->name);
+		}
+	} else {
+		/* Create clknode object and initialize it. */
+		clknode = malloc(sizeof(struct clknode), M_CLOCK,
+		    M_WAITOK | M_ZERO);
+		sx_init(&clknode->lock, "Clocknode lock");
+		TAILQ_INIT(&clknode->children);
+		replaced = false;
+	}
 
-	/* Create object and initialize it. */
-	clknode = malloc(sizeof(struct clknode), M_CLOCK, M_WAITOK | M_ZERO);
 	kobj_init((kobj_t)clknode, (kobj_class_t)clknode_class);
-	sx_init(&clknode->lock, "Clocknode lock");
 
 	/* Allocate softc if required. */
 	if (clknode_class->size > 0) {
@@ -568,8 +622,10 @@ clknode_create(struct clkdom * clkdom, clknode_class_t
 	clknode->parent_cnt = def->parent_cnt;
 	clknode->parent = NULL;
 	clknode->parent_idx = CLKNODE_IDX_NONE;
-	TAILQ_INIT(&clknode->children);
 
+	if (replaced)
+			return (clknode);
+
 	sysctl_ctx_init(&clknode->sysctl_ctx);
 	clknode_oid = SYSCTL_ADD_NODE(&clknode->sysctl_ctx,
 	    SYSCTL_STATIC_CHILDREN(_hw_clock),
@@ -617,6 +673,10 @@ clknode_register(struct clkdom * clkdom, struct clknod
 {
 	int rv;
 
+	/* Skip already registered linked node */
+	if (clknode->flags & CLK_NODE_REGISTERED)
+		return(clknode);
+
 	rv = CLKNODE_INIT(clknode, clknode_get_device(clknode));
 	if (rv != 0) {
 		printf(" CLKNODE_INIT failed: %d\n", rv);
@@ -624,10 +684,24 @@ clknode_register(struct clkdom * clkdom, struct clknod
 	}
 
 	TAILQ_INSERT_TAIL(&clkdom->clknode_list, clknode, clkdom_link);
-
+	clknode->flags |= CLK_NODE_REGISTERED;
 	return (clknode);
 }
 
+
+static void
+clknode_finish(void *dummy)
+{
+	struct clknode *clknode;
+
+	CLK_TOPO_SLOCK();
+	TAILQ_FOREACH(clknode, &clknode_list, clklist_link) {
+		if (clknode->flags & CLK_NODE_LINKED)
+			printf("Unresolved linked clock found: %s\n",
+			    clknode->name);
+	}
+	CLK_TOPO_UNLOCK();
+}
 /*
  * Clock providers interface.
  */

Modified: head/sys/dev/extres/clk/clk.h
==============================================================================
--- head/sys/dev/extres/clk/clk.h	Fri Nov  8 18:56:02 2019	(r354553)
+++ head/sys/dev/extres/clk/clk.h	Fri Nov  8 18:57:41 2019	(r354554)
@@ -41,7 +41,9 @@
 /* clknode flags. */
 #define	CLK_NODE_STATIC_STRINGS	0x00000001	/* Static name strings */
 #define	CLK_NODE_GLITCH_FREE	0x00000002	/* Freq can change w/o stop */
-#define	CLK_NODE_CANNOT_STOP	0x00000004	/* Clock cannot be disabled */
+#define	CLK_NODE_CANNOT_STOP	0x00000004	/* Cannot be disabled */
+#define	CLK_NODE_LINKED		0x00000008	/* Is linked clock */
+#define	CLK_NODE_REGISTERED	0x00000020	/* Is already registered */
 
 /* Flags passed to clk_set_freq() and clknode_set_freq(). */
 #define	CLK_SET_ROUND(x)	((x) & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN))

Added: head/sys/dev/extres/clk/clk_link.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/extres/clk/clk_link.c	Fri Nov  8 18:57:41 2019	(r354554)
@@ -0,0 +1,122 @@
+/*-
+ * Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/systm.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/extres/clk/clk_link.h>
+
+static int clknode_link_init(struct clknode *clk, device_t dev);
+static int clknode_link_recalc(struct clknode *clk, uint64_t *freq);
+static int clknode_link_set_freq(struct clknode *clk, uint64_t fin,
+    uint64_t *fout, int flags, int *stop);
+static int clknode_link_set_mux(struct clknode *clk, int idx);
+static int clknode_link_set_gate(struct clknode *clk, bool enable);
+
+static clknode_method_t clknode_link_methods[] = {
+	/* Device interface */
+	CLKNODEMETHOD(clknode_init,	   clknode_link_init),
+	CLKNODEMETHOD(clknode_recalc_freq, clknode_link_recalc),
+	CLKNODEMETHOD(clknode_set_freq,	   clknode_link_set_freq),
+	CLKNODEMETHOD(clknode_set_gate,	   clknode_link_set_gate),
+	CLKNODEMETHOD(clknode_set_mux,	   clknode_link_set_mux),
+	CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(clknode_link, clknode_link_class, clknode_link_methods,
+   0, clknode_class);
+
+static int
+clknode_link_init(struct clknode *clk, device_t dev)
+{
+	return(0);
+}
+
+static int
+clknode_link_recalc(struct clknode *clk, uint64_t *freq)
+{
+
+	printf("%s: Attempt to use unresolved linked clock: %s\n", __func__,
+	    clknode_get_name(clk));
+	return (EBADF);
+}
+
+static int
+clknode_link_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
+    int flags, int *stop)
+{
+
+	printf("%s: Attempt to use unresolved linked clock: %s\n", __func__,
+	    clknode_get_name(clk));
+	return (EBADF);
+}
+
+static int
+clknode_link_set_mux(struct clknode *clk, int idx)
+{
+
+	printf("%s: Attempt to use unresolved linked clock: %s\n", __func__,
+	    clknode_get_name(clk));
+	return (EBADF);
+}
+
+static int
+clknode_link_set_gate(struct clknode *clk, bool enable)
+{
+
+	printf("%s: Attempt to use unresolved linked clock: %s\n", __func__,
+	    clknode_get_name(clk));
+	return (EBADF);
+}
+
+int
+clknode_link_register(struct clkdom *clkdom, struct clk_link_def *clkdef)
+{
+	struct clknode *clk;
+	struct clknode_init_def tmp;
+
+	tmp = clkdef->clkdef;
+	tmp.flags |= CLK_NODE_LINKED;
+	clk = clknode_create(clkdom, &clknode_link_class, &tmp);
+	if (clk == NULL)
+		return (1);
+	clknode_register(clkdom, clk);
+	return (0);
+}

Added: head/sys/dev/extres/clk/clk_link.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/extres/clk/clk_link.h	Fri Nov  8 18:57:41 2019	(r354554)
@@ -0,0 +1,47 @@
+/*-
+ * Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 _DEV_EXTRES_CLK_LINK_H_
+#define _DEV_EXTRES_CLK_LINK_H_
+
+#include <dev/extres/clk/clk.h>
+
+/*
+ * A linked clock is used as placeholder for not yet available clock.
+ * It will be replaced by equally named clock from other domain, created
+ * in future stage of system initialization.
+*/
+
+struct clk_link_def {
+	struct clknode_init_def clkdef;
+
+};
+
+int clknode_link_register(struct clkdom *clkdom, struct clk_link_def *clkdef);
+
+#endif /*_DEV_EXTRES_CLK_LINK_H_*/



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201911081857.xA8Ivf2J092145>