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
This commit is contained in:
parent
09bc401f9d
commit
124a91ac18
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=354554
@ -1681,6 +1681,7 @@ dev/extres/clk/clk_bus.c optional ext_resources clk fdt
|
||||
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
|
||||
|
@ -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 clknode_class,
|
||||
{
|
||||
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
|
||||
CLK_TOPO_SLOCK();
|
||||
if (clknode_find_by_name(def->name) != NULL)
|
||||
panic("Duplicated clock registration: %s\n", def->name);
|
||||
CLK_TOPO_UNLOCK();
|
||||
#endif
|
||||
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();
|
||||
clknode = clknode_find_by_name(def->name);
|
||||
CLK_TOPO_UNLOCK();
|
||||
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,7 +622,9 @@ clknode_create(struct clkdom * clkdom, clknode_class_t clknode_class,
|
||||
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,
|
||||
@ -617,6 +673,10 @@ clknode_register(struct clkdom * clkdom, struct clknode *clknode)
|
||||
{
|
||||
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 clknode *clknode)
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
|
@ -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))
|
||||
|
122
sys/dev/extres/clk/clk_link.c
Normal file
122
sys/dev/extres/clk/clk_link.c
Normal file
@ -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);
|
||||
}
|
47
sys/dev/extres/clk/clk_link.h
Normal file
47
sys/dev/extres/clk/clk_link.h
Normal file
@ -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_*/
|
Loading…
Reference in New Issue
Block a user