1*6e54367aSthorpej /* $NetBSD: fixedclock.c,v 1.6 2021/01/27 03:10:21 thorpej Exp $ */
2d374aef8Sjmcneill
3d374aef8Sjmcneill /*-
4d374aef8Sjmcneill * Copyright (c) 2017 Jared D. McNeill <jmcneill@invisible.ca>
5d374aef8Sjmcneill * All rights reserved.
6d374aef8Sjmcneill *
7d374aef8Sjmcneill * Redistribution and use in source and binary forms, with or without
8d374aef8Sjmcneill * modification, are permitted provided that the following conditions
9d374aef8Sjmcneill * are met:
10d374aef8Sjmcneill * 1. Redistributions of source code must retain the above copyright
11d374aef8Sjmcneill * notice, this list of conditions and the following disclaimer.
12d374aef8Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
13d374aef8Sjmcneill * notice, this list of conditions and the following disclaimer in the
14d374aef8Sjmcneill * documentation and/or other materials provided with the distribution.
15d374aef8Sjmcneill *
16d374aef8Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17d374aef8Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18d374aef8Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19d374aef8Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20d374aef8Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21d374aef8Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22d374aef8Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23d374aef8Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24d374aef8Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25d374aef8Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26d374aef8Sjmcneill * SUCH DAMAGE.
27d374aef8Sjmcneill */
28d374aef8Sjmcneill
29d374aef8Sjmcneill #include <sys/cdefs.h>
30*6e54367aSthorpej __KERNEL_RCSID(0, "$NetBSD: fixedclock.c,v 1.6 2021/01/27 03:10:21 thorpej Exp $");
31d374aef8Sjmcneill
32d374aef8Sjmcneill #include <sys/param.h>
33d374aef8Sjmcneill #include <sys/systm.h>
34d374aef8Sjmcneill #include <sys/device.h>
35d374aef8Sjmcneill #include <sys/kmem.h>
36d374aef8Sjmcneill #include <sys/bus.h>
37d374aef8Sjmcneill
38d374aef8Sjmcneill #include <dev/clk/clk_backend.h>
39d374aef8Sjmcneill
40d374aef8Sjmcneill #include <dev/fdt/fdtvar.h>
41d374aef8Sjmcneill
42d374aef8Sjmcneill static int fixedclock_match(device_t, cfdata_t, void *);
43d374aef8Sjmcneill static void fixedclock_attach(device_t, device_t, void *);
44d374aef8Sjmcneill
4551b425efSaymeric static struct clk *fixedclock_decode(device_t, int, const void *, size_t);
46d374aef8Sjmcneill
47d374aef8Sjmcneill static const struct fdtbus_clock_controller_func fixedclock_fdt_funcs = {
48d374aef8Sjmcneill .decode = fixedclock_decode
49d374aef8Sjmcneill };
50d374aef8Sjmcneill
51d374aef8Sjmcneill static struct clk *fixedclock_get(void *, const char *);
52d374aef8Sjmcneill static void fixedclock_put(void *, struct clk *);
53d374aef8Sjmcneill static u_int fixedclock_get_rate(void *, struct clk *);
54d374aef8Sjmcneill
55d374aef8Sjmcneill static const struct clk_funcs fixedclock_clk_funcs = {
56d374aef8Sjmcneill .get = fixedclock_get,
57d374aef8Sjmcneill .put = fixedclock_put,
58d374aef8Sjmcneill .get_rate = fixedclock_get_rate,
59d374aef8Sjmcneill };
60d374aef8Sjmcneill
61d374aef8Sjmcneill struct fixedclock_clk {
62d374aef8Sjmcneill struct clk base;
63d374aef8Sjmcneill u_int rate;
64d374aef8Sjmcneill };
65d374aef8Sjmcneill
66d374aef8Sjmcneill struct fixedclock_softc {
67d374aef8Sjmcneill device_t sc_dev;
68d374aef8Sjmcneill int sc_phandle;
69d374aef8Sjmcneill
70d374aef8Sjmcneill struct clk_domain sc_clkdom;
71d374aef8Sjmcneill struct fixedclock_clk sc_clk;
72d374aef8Sjmcneill };
73d374aef8Sjmcneill
74d374aef8Sjmcneill CFATTACH_DECL_NEW(fclock, sizeof(struct fixedclock_softc),
75d374aef8Sjmcneill fixedclock_match, fixedclock_attach, NULL, NULL);
76d374aef8Sjmcneill
77*6e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
78*6e54367aSthorpej { .compat = "fixed-clock" },
79*6e54367aSthorpej DEVICE_COMPAT_EOL
80*6e54367aSthorpej };
81*6e54367aSthorpej
82d374aef8Sjmcneill static int
fixedclock_match(device_t parent,cfdata_t cf,void * aux)83d374aef8Sjmcneill fixedclock_match(device_t parent, cfdata_t cf, void *aux)
84d374aef8Sjmcneill {
85d374aef8Sjmcneill const struct fdt_attach_args *faa = aux;
86d374aef8Sjmcneill
87*6e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
88d374aef8Sjmcneill }
89d374aef8Sjmcneill
90d374aef8Sjmcneill static void
fixedclock_attach(device_t parent,device_t self,void * aux)91d374aef8Sjmcneill fixedclock_attach(device_t parent, device_t self, void *aux)
92d374aef8Sjmcneill {
93d374aef8Sjmcneill struct fixedclock_softc * const sc = device_private(self);
94d374aef8Sjmcneill const struct fdt_attach_args *faa = aux;
95d374aef8Sjmcneill const int phandle = faa->faa_phandle;
963b4803fcSjmcneill const char *clkname;
973b4803fcSjmcneill
983b4803fcSjmcneill clkname = fdtbus_get_string(phandle, "clock-output-names");
993b4803fcSjmcneill if (!clkname)
1003b4803fcSjmcneill clkname = faa->faa_name;
101d374aef8Sjmcneill
102d374aef8Sjmcneill sc->sc_dev = self;
103d374aef8Sjmcneill sc->sc_phandle = phandle;
104a756758dSjmcneill sc->sc_clkdom.name = device_xname(self);
105d374aef8Sjmcneill sc->sc_clkdom.funcs = &fixedclock_clk_funcs;
106d374aef8Sjmcneill sc->sc_clkdom.priv = sc;
107d374aef8Sjmcneill if (of_getprop_uint32(phandle, "clock-frequency",
108d374aef8Sjmcneill &sc->sc_clk.rate) != 0) {
109d374aef8Sjmcneill aprint_error(": couldn't determine frequency\n");
110d374aef8Sjmcneill return;
111d374aef8Sjmcneill }
112d374aef8Sjmcneill sc->sc_clk.base.domain = &sc->sc_clkdom;
1133b4803fcSjmcneill sc->sc_clk.base.name = kmem_asprintf("%s", clkname);
114a756758dSjmcneill clk_attach(&sc->sc_clk.base);
115d374aef8Sjmcneill
11610ea0ce9Sjmcneill aprint_naive("\n");
1173b4803fcSjmcneill aprint_normal(": %u Hz fixed clock (%s)\n", sc->sc_clk.rate, clkname);
118d374aef8Sjmcneill
119d374aef8Sjmcneill fdtbus_register_clock_controller(self, phandle, &fixedclock_fdt_funcs);
120d374aef8Sjmcneill }
121d374aef8Sjmcneill
122d374aef8Sjmcneill static struct clk *
fixedclock_decode(device_t dev,int cc_phandle,const void * data,size_t len)12351b425efSaymeric fixedclock_decode(device_t dev, int cc_phandle, const void *data, size_t len)
124d374aef8Sjmcneill {
125d374aef8Sjmcneill struct fixedclock_softc * const sc = device_private(dev);
126d374aef8Sjmcneill
127d374aef8Sjmcneill /* #clock-cells for a fixed clock is always 0 */
128d374aef8Sjmcneill if (len != 0)
129d374aef8Sjmcneill return NULL;
130d374aef8Sjmcneill
131d374aef8Sjmcneill return &sc->sc_clk.base;
132d374aef8Sjmcneill }
133d374aef8Sjmcneill
134d374aef8Sjmcneill static struct clk *
fixedclock_get(void * priv,const char * name)135d374aef8Sjmcneill fixedclock_get(void *priv, const char *name)
136d374aef8Sjmcneill {
137d374aef8Sjmcneill struct fixedclock_softc * const sc = priv;
138d374aef8Sjmcneill
139d374aef8Sjmcneill if (strcmp(name, sc->sc_clk.base.name) != 0)
140d374aef8Sjmcneill return NULL;
141d374aef8Sjmcneill
142d374aef8Sjmcneill return &sc->sc_clk.base;
143d374aef8Sjmcneill }
144d374aef8Sjmcneill
145d374aef8Sjmcneill static void
fixedclock_put(void * priv,struct clk * clk)146d374aef8Sjmcneill fixedclock_put(void *priv, struct clk *clk)
147d374aef8Sjmcneill {
148d374aef8Sjmcneill }
149d374aef8Sjmcneill
150d374aef8Sjmcneill static u_int
fixedclock_get_rate(void * priv,struct clk * clk)151d374aef8Sjmcneill fixedclock_get_rate(void *priv, struct clk *clk)
152d374aef8Sjmcneill {
153d374aef8Sjmcneill struct fixedclock_clk *fclk = (struct fixedclock_clk *)clk;
154d374aef8Sjmcneill
155d374aef8Sjmcneill return fclk->rate;
156d374aef8Sjmcneill }
157