1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-17 10:26:15 +00:00

CLK: Add enumerator for 'clocks' OFW node. Add bus device bindings

for clk_fixed class.
This commit is contained in:
Michal Meloun 2016-03-15 15:27:15 +00:00
parent 3ad8c0e5a1
commit 58de845996
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=296904
5 changed files with 341 additions and 7 deletions

View File

@ -1413,6 +1413,7 @@ dev/exca/exca.c optional cbb
dev/extres/clk/clk.c optional ext_resources clk
dev/extres/clk/clkdev_if.m optional ext_resources clk
dev/extres/clk/clknode_if.m optional ext_resources clk
dev/extres/clk/clk_bus.c optional ext_resources clk fdt
dev/extres/clk/clk_div.c optional ext_resources clk
dev/extres/clk/clk_fixed.c optional ext_resources clk
dev/extres/clk/clk_gate.c optional ext_resources clk

View File

@ -1258,4 +1258,75 @@ clk_get_by_ofw_name(device_t dev, const char *name, clk_t *clk)
return (rv);
return (clk_get_by_ofw_index(dev, idx, clk));
}
/* --------------------------------------------------------------------------
*
* Support functions for parsing various clock related OFW things.
*/
/*
* Get "clock-output-names" and (optional) "clock-indices" lists.
* Both lists are alocated using M_OFWPROP specifier.
*
* Returns number of items or 0.
*/
int
clk_parse_ofw_out_names(device_t dev, phandle_t node, const char ***out_names,
uint32_t *indices)
{
int name_items, rv;
*out_names = NULL;
indices = NULL;
if (!OF_hasprop(node, "clock-output-names"))
return (0);
rv = ofw_bus_string_list_to_array(node, "clock-output-names",
out_names);
if (rv <= 0)
return (0);
name_items = rv;
if (!OF_hasprop(node, "clock-indices"))
return (name_items);
rv = OF_getencprop_alloc(node, "clock-indices", sizeof (uint32_t),
(void **)indices);
if (rv != name_items) {
device_printf(dev, " Size of 'clock-output-names' and "
"'clock-indices' differs\n");
free(*out_names, M_OFWPROP);
free(indices, M_OFWPROP);
return (0);
}
return (name_items);
}
/*
* Get output clock name for single output clock node.
*/
int
clk_parse_ofw_clk_name(device_t dev, phandle_t node, const char **name)
{
const char **out_names;
const char *tmp_name;
int rv;
*name = NULL;
if (!OF_hasprop(node, "clock-output-names")) {
tmp_name = ofw_bus_get_name(dev);
if (tmp_name == NULL)
return (ENXIO);
*name = strdup(tmp_name, M_OFWPROP);
return (0);
}
rv = ofw_bus_string_list_to_array(node, "clock-output-names",
&out_names);
if (rv != 1) {
free(out_names, M_OFWPROP);
device_printf(dev, "Malformed 'clock-output-names' property\n");
return (ENXIO);
}
*name = strdup(out_names[0], M_OFWPROP);
free(out_names, M_OFWPROP);
return (0);
}
#endif

View File

@ -131,6 +131,9 @@ const char *clk_get_name(clk_t clk);
#ifdef FDT
int clk_get_by_ofw_index(device_t dev, int idx, clk_t *clk);
int clk_get_by_ofw_name(device_t dev, const char *name, clk_t *clk);
int clk_parse_ofw_out_names(device_t dev, phandle_t node,
const char ***out_names, uint32_t *indices);
int clk_parse_ofw_clk_name(device_t dev, phandle_t node, const char **name);
#endif
#endif /* _DEV_EXTRES_CLK_H_ */

View File

@ -0,0 +1,93 @@
/*-
* 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/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <dev/fdt/simplebus.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus_subr.h>
struct ofw_clkbus_softc {
struct simplebus_softc simplebus_sc;
};
static int
ofw_clkbus_probe(device_t dev)
{
const char *name;
name = ofw_bus_get_name(dev);
if (name == NULL || strcmp(name, "clocks") != 0)
return (ENXIO);
device_set_desc(dev, "OFW clocks bus");
return (0);
}
static int
ofw_clkbus_attach(device_t dev)
{
struct ofw_clkbus_softc *sc;
phandle_t node, child;
device_t cdev;
sc = device_get_softc(dev);
node = ofw_bus_get_node(dev);
simplebus_init(dev, node);
for (child = OF_child(node); child > 0; child = OF_peer(child)) {
cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL);
if (cdev != NULL)
device_probe_and_attach(cdev);
}
return (bus_generic_attach(dev));
}
static device_method_t ofw_clkbus_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ofw_clkbus_probe),
DEVMETHOD(device_attach, ofw_clkbus_attach),
DEVMETHOD_END
};
DEFINE_CLASS_1(ofw_clkbus, ofw_clkbus_driver, ofw_clkbus_methods,
sizeof(struct ofw_clkbus_softc), simplebus_driver);
static devclass_t ofw_clkbus_devclass;
EARLY_DRIVER_MODULE(ofw_clkbus, simplebus, ofw_clkbus_driver,
ofw_clkbus_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(ofw_clkbus, 1);

View File

@ -27,7 +27,6 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/bus.h>
@ -35,15 +34,24 @@ __FBSDID("$FreeBSD$");
#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_fixed.h>
#define CLK_TYPE_FIXED 1
#define CLK_TYPE_FIXED_FACTOR 2
static int clknode_fixed_init(struct clknode *clk, device_t dev);
static int clknode_fixed_recalc(struct clknode *clk, uint64_t *freq);
static int clknode_fixed_set_freq(struct clknode *clk, uint64_t fin,
uint64_t *fout, int flags, int *stop);
struct clknode_fixed_sc {
int fixed_flags;
uint64_t freq;
@ -55,6 +63,7 @@ static clknode_method_t clknode_fixed_methods[] = {
/* Device interface */
CLKNODEMETHOD(clknode_init, clknode_fixed_init),
CLKNODEMETHOD(clknode_recalc_freq, clknode_fixed_recalc),
CLKNODEMETHOD(clknode_set_freq, clknode_fixed_set_freq),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(clknode_fixed, clknode_fixed_class, clknode_fixed_methods,
@ -77,12 +86,31 @@ clknode_fixed_recalc(struct clknode *clk, uint64_t *freq)
struct clknode_fixed_sc *sc;
sc = clknode_get_softc(clk);
if (sc->freq != 0)
*freq = sc->freq;
else if ((sc->mult != 0) && (sc->div != 0))
if ((sc->mult != 0) && (sc->div != 0))
*freq = (*freq / sc->div) * sc->mult;
else
*freq = 0;
*freq = sc->freq;
return (0);
}
static int
clknode_fixed_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
int flags, int *stop)
{
struct clknode_fixed_sc *sc;
sc = clknode_get_softc(clk);
if (sc->mult == 0 || sc->div == 0) {
/* Fixed frequency clock. */
*stop = 1;
if (*fout != sc->freq)
return (ERANGE);
return (0);
}
/* Fixed factor clock. */
*stop = 0;
*fout = (*fout / sc->mult) * sc->div;
return (0);
}
@ -92,8 +120,6 @@ clknode_fixed_register(struct clkdom *clkdom, struct clk_fixed_def *clkdef)
struct clknode *clk;
struct clknode_fixed_sc *sc;
if ((clkdef->freq == 0) && (clkdef->clkdef.parent_cnt == 0))
panic("fixed clk: Frequency is not defined for clock source");
clk = clknode_create(clkdom, &clknode_fixed_class, &clkdef->clkdef);
if (clk == NULL)
return (1);
@ -107,3 +133,143 @@ clknode_fixed_register(struct clkdom *clkdom, struct clk_fixed_def *clkdef)
clknode_register(clkdom, clk);
return (0);
}
#ifdef FDT
static struct ofw_compat_data compat_data[] = {
{"fixed-clock", CLK_TYPE_FIXED},
{"fixed-factor-clock", CLK_TYPE_FIXED_FACTOR},
{NULL, 0},
};
struct clk_fixed_softc {
device_t dev;
struct clkdom *clkdom;
};
static int
clk_fixed_probe(device_t dev)
{
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
device_set_desc(dev, "Fixed clock");
return (BUS_PROBE_DEFAULT);
}
return (ENXIO);
}
static int
clk_fixed_init_fixed(struct clk_fixed_softc *sc, phandle_t node,
struct clk_fixed_def *def)
{
uint32_t freq;
int rv;
def->clkdef.id = 1;
rv = OF_getencprop(node, "clock-frequency", &freq, sizeof(freq));
if (rv <= 0)
return (ENXIO);
def->freq = freq;
return (0);
}
static int
clk_fixed_init_fixed_factor(struct clk_fixed_softc *sc, phandle_t node,
struct clk_fixed_def *def)
{
int rv;
clk_t parent;
def->clkdef.id = 1;
rv = OF_getencprop(node, "clock-mult", &def->mult, sizeof(def->mult));
if (rv <= 0)
return (ENXIO);
rv = OF_getencprop(node, "clock-div", &def->mult, sizeof(def->div));
if (rv <= 0)
return (ENXIO);
/* Get name of parent clock */
rv = clk_get_by_ofw_name(sc->dev, "clocks", &parent);
if (rv != 0)
return (ENXIO);
def->clkdef.parent_names = malloc(sizeof(char *), M_OFWPROP, M_WAITOK);
def->clkdef.parent_names[0] = clk_get_name(parent);
def->clkdef.parent_cnt = 1;
clk_release(parent);
return (0);
}
static int
clk_fixed_attach(device_t dev)
{
struct clk_fixed_softc *sc;
intptr_t clk_type;
phandle_t node;
struct clk_fixed_def def;
int rv;
sc = device_get_softc(dev);
sc->dev = dev;
node = ofw_bus_get_node(dev);
clk_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
bzero(&def, sizeof(def));
if (clk_type == CLK_TYPE_FIXED)
rv = clk_fixed_init_fixed(sc, node, &def);
else if (clk_type == CLK_TYPE_FIXED_FACTOR)
rv = clk_fixed_init_fixed_factor(sc, node, &def);
else
rv = ENXIO;
if (rv != 0) {
device_printf(sc->dev, "Cannot FDT parameters.\n");
goto fail;
}
rv = clk_parse_ofw_clk_name(dev, node, &def.clkdef.name);
if (rv != 0) {
device_printf(sc->dev, "Cannot parse clock name.\n");
goto fail;
}
sc->clkdom = clkdom_create(dev);
KASSERT(sc->clkdom != NULL, ("Clock domain is NULL"));
rv = clknode_fixed_register(sc->clkdom, &def);
if (rv != 0) {
device_printf(sc->dev, "Cannot register fixed clock.\n");
rv = ENXIO;
goto fail;
}
rv = clkdom_finit(sc->clkdom);
if (rv != 0) {
device_printf(sc->dev, "Clk domain finit fails.\n");
rv = ENXIO;
goto fail;
}
#ifdef CLK_DEBUG
clkdom_dump(sc->clkdom);
#endif
free(__DECONST(char *, def.clkdef.name), M_OFWPROP);
free(def.clkdef.parent_names, M_OFWPROP);
return (bus_generic_attach(dev));
fail:
free(__DECONST(char *, def.clkdef.name), M_OFWPROP);
free(def.clkdef.parent_names, M_OFWPROP);
return (rv);
}
static device_method_t clk_fixed_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, clk_fixed_probe),
DEVMETHOD(device_attach, clk_fixed_attach),
DEVMETHOD_END
};
DEFINE_CLASS_0(clk_fixed, clk_fixed_driver, clk_fixed_methods,
sizeof(struct clk_fixed_softc));
static devclass_t clk_fixed_devclass;
EARLY_DRIVER_MODULE(clk_fixed, simplebus, clk_fixed_driver,
clk_fixed_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(clk_fixed, 1);
#endif