1*6e54367aSthorpej /* $NetBSD: fixedfactorclock.c,v 1.4 2021/01/27 03:10:21 thorpej Exp $ */
20a9d84f0Sjmcneill
30a9d84f0Sjmcneill /*-
40a9d84f0Sjmcneill * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
50a9d84f0Sjmcneill * All rights reserved.
60a9d84f0Sjmcneill *
70a9d84f0Sjmcneill * Redistribution and use in source and binary forms, with or without
80a9d84f0Sjmcneill * modification, are permitted provided that the following conditions
90a9d84f0Sjmcneill * are met:
100a9d84f0Sjmcneill * 1. Redistributions of source code must retain the above copyright
110a9d84f0Sjmcneill * notice, this list of conditions and the following disclaimer.
120a9d84f0Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
130a9d84f0Sjmcneill * notice, this list of conditions and the following disclaimer in the
140a9d84f0Sjmcneill * documentation and/or other materials provided with the distribution.
150a9d84f0Sjmcneill *
160a9d84f0Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
170a9d84f0Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
180a9d84f0Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
190a9d84f0Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
200a9d84f0Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
210a9d84f0Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
220a9d84f0Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
230a9d84f0Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
240a9d84f0Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
250a9d84f0Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
260a9d84f0Sjmcneill * SUCH DAMAGE.
270a9d84f0Sjmcneill */
280a9d84f0Sjmcneill
290a9d84f0Sjmcneill #include <sys/cdefs.h>
30*6e54367aSthorpej __KERNEL_RCSID(0, "$NetBSD: fixedfactorclock.c,v 1.4 2021/01/27 03:10:21 thorpej Exp $");
310a9d84f0Sjmcneill
320a9d84f0Sjmcneill #include <sys/param.h>
330a9d84f0Sjmcneill #include <sys/systm.h>
340a9d84f0Sjmcneill #include <sys/device.h>
350a9d84f0Sjmcneill #include <sys/kmem.h>
360a9d84f0Sjmcneill #include <sys/bus.h>
370a9d84f0Sjmcneill
380a9d84f0Sjmcneill #include <dev/clk/clk_backend.h>
390a9d84f0Sjmcneill
400a9d84f0Sjmcneill #include <dev/fdt/fdtvar.h>
410a9d84f0Sjmcneill
420a9d84f0Sjmcneill static int fixedfactorclock_match(device_t, cfdata_t, void *);
430a9d84f0Sjmcneill static void fixedfactorclock_attach(device_t, device_t, void *);
440a9d84f0Sjmcneill
4551b425efSaymeric static struct clk *fixedfactorclock_decode(device_t, int, const void *, size_t);
460a9d84f0Sjmcneill
470a9d84f0Sjmcneill static const struct fdtbus_clock_controller_func fixedfactorclock_fdt_funcs = {
480a9d84f0Sjmcneill .decode = fixedfactorclock_decode
490a9d84f0Sjmcneill };
500a9d84f0Sjmcneill
510a9d84f0Sjmcneill static struct clk *fixedfactorclock_get(void *, const char *);
520a9d84f0Sjmcneill static void fixedfactorclock_put(void *, struct clk *);
530a9d84f0Sjmcneill static u_int fixedfactorclock_get_rate(void *, struct clk *);
540a9d84f0Sjmcneill
550a9d84f0Sjmcneill static const struct clk_funcs fixedfactorclock_clk_funcs = {
560a9d84f0Sjmcneill .get = fixedfactorclock_get,
570a9d84f0Sjmcneill .put = fixedfactorclock_put,
580a9d84f0Sjmcneill .get_rate = fixedfactorclock_get_rate,
590a9d84f0Sjmcneill };
600a9d84f0Sjmcneill
610a9d84f0Sjmcneill struct fixedfactorclock_clk {
620a9d84f0Sjmcneill struct clk base;
630a9d84f0Sjmcneill
640a9d84f0Sjmcneill u_int div;
650a9d84f0Sjmcneill u_int mult;
660a9d84f0Sjmcneill };
670a9d84f0Sjmcneill
680a9d84f0Sjmcneill struct fixedfactorclock_softc {
690a9d84f0Sjmcneill device_t sc_dev;
700a9d84f0Sjmcneill int sc_phandle;
710a9d84f0Sjmcneill
720a9d84f0Sjmcneill struct clk_domain sc_clkdom;
730a9d84f0Sjmcneill struct fixedfactorclock_clk sc_clk;
740a9d84f0Sjmcneill };
750a9d84f0Sjmcneill
760a9d84f0Sjmcneill CFATTACH_DECL_NEW(ffclock, sizeof(struct fixedfactorclock_softc),
770a9d84f0Sjmcneill fixedfactorclock_match, fixedfactorclock_attach, NULL, NULL);
780a9d84f0Sjmcneill
79*6e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
80*6e54367aSthorpej { .compat = "fixed-factor-clock" },
81*6e54367aSthorpej DEVICE_COMPAT_EOL
82*6e54367aSthorpej };
83*6e54367aSthorpej
840a9d84f0Sjmcneill static int
fixedfactorclock_match(device_t parent,cfdata_t cf,void * aux)850a9d84f0Sjmcneill fixedfactorclock_match(device_t parent, cfdata_t cf, void *aux)
860a9d84f0Sjmcneill {
870a9d84f0Sjmcneill const struct fdt_attach_args *faa = aux;
880a9d84f0Sjmcneill
89*6e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
900a9d84f0Sjmcneill }
910a9d84f0Sjmcneill
920a9d84f0Sjmcneill static void
fixedfactorclock_attach(device_t parent,device_t self,void * aux)930a9d84f0Sjmcneill fixedfactorclock_attach(device_t parent, device_t self, void *aux)
940a9d84f0Sjmcneill {
950a9d84f0Sjmcneill struct fixedfactorclock_softc * const sc = device_private(self);
960a9d84f0Sjmcneill const struct fdt_attach_args *faa = aux;
970a9d84f0Sjmcneill const int phandle = faa->faa_phandle;
980a9d84f0Sjmcneill const char *name;
990a9d84f0Sjmcneill
1000a9d84f0Sjmcneill sc->sc_dev = self;
1010a9d84f0Sjmcneill sc->sc_phandle = phandle;
102a756758dSjmcneill sc->sc_clkdom.name = device_xname(self);
1030a9d84f0Sjmcneill sc->sc_clkdom.funcs = &fixedfactorclock_clk_funcs;
1040a9d84f0Sjmcneill sc->sc_clkdom.priv = sc;
1050a9d84f0Sjmcneill
1060a9d84f0Sjmcneill of_getprop_uint32(phandle, "clock-div", &sc->sc_clk.div);
1070a9d84f0Sjmcneill of_getprop_uint32(phandle, "clock-mult", &sc->sc_clk.mult);
1080a9d84f0Sjmcneill
1090a9d84f0Sjmcneill if (sc->sc_clk.div == 0 || sc->sc_clk.mult == 0) {
1100a9d84f0Sjmcneill aprint_error(": invalid properties\n");
1110a9d84f0Sjmcneill return;
1120a9d84f0Sjmcneill }
1130a9d84f0Sjmcneill
1140a9d84f0Sjmcneill name = fdtbus_get_string(phandle, "clock-output-names");
1150a9d84f0Sjmcneill if (name == NULL)
1160a9d84f0Sjmcneill name = faa->faa_name;
1170a9d84f0Sjmcneill
1180a9d84f0Sjmcneill sc->sc_clk.base.domain = &sc->sc_clkdom;
1190a9d84f0Sjmcneill sc->sc_clk.base.name = name;
120a756758dSjmcneill clk_attach(&sc->sc_clk.base);
1210a9d84f0Sjmcneill
1220a9d84f0Sjmcneill aprint_naive("\n");
1230a9d84f0Sjmcneill aprint_normal(": x%u /%u fixed-factor clock\n",
1240a9d84f0Sjmcneill sc->sc_clk.mult, sc->sc_clk.div);
1250a9d84f0Sjmcneill
1260a9d84f0Sjmcneill fdtbus_register_clock_controller(self, phandle,
1270a9d84f0Sjmcneill &fixedfactorclock_fdt_funcs);
1280a9d84f0Sjmcneill }
1290a9d84f0Sjmcneill
1300a9d84f0Sjmcneill static struct clk *
fixedfactorclock_decode(device_t dev,int cc_phandle,const void * data,size_t len)13151b425efSaymeric fixedfactorclock_decode(device_t dev, int cc_phandle, const void *data,
13251b425efSaymeric size_t len)
1330a9d84f0Sjmcneill {
1340a9d84f0Sjmcneill struct fixedfactorclock_softc * const sc = device_private(dev);
1350a9d84f0Sjmcneill
1360a9d84f0Sjmcneill /* #clock-cells for a fixed factor clock is always 0 */
1370a9d84f0Sjmcneill if (len != 0)
1380a9d84f0Sjmcneill return NULL;
1390a9d84f0Sjmcneill
1400a9d84f0Sjmcneill return &sc->sc_clk.base;
1410a9d84f0Sjmcneill }
1420a9d84f0Sjmcneill
1430a9d84f0Sjmcneill static struct clk *
fixedfactorclock_get(void * priv,const char * name)1440a9d84f0Sjmcneill fixedfactorclock_get(void *priv, const char *name)
1450a9d84f0Sjmcneill {
1460a9d84f0Sjmcneill struct fixedfactorclock_softc * const sc = priv;
1470a9d84f0Sjmcneill
1480a9d84f0Sjmcneill if (strcmp(name, sc->sc_clk.base.name) != 0)
1490a9d84f0Sjmcneill return NULL;
1500a9d84f0Sjmcneill
1510a9d84f0Sjmcneill return &sc->sc_clk.base;
1520a9d84f0Sjmcneill }
1530a9d84f0Sjmcneill
1540a9d84f0Sjmcneill static void
fixedfactorclock_put(void * priv,struct clk * clk)1550a9d84f0Sjmcneill fixedfactorclock_put(void *priv, struct clk *clk)
1560a9d84f0Sjmcneill {
1570a9d84f0Sjmcneill }
1580a9d84f0Sjmcneill
1590a9d84f0Sjmcneill static u_int
fixedfactorclock_get_rate(void * priv,struct clk * clk)1600a9d84f0Sjmcneill fixedfactorclock_get_rate(void *priv, struct clk *clk)
1610a9d84f0Sjmcneill {
1620a9d84f0Sjmcneill struct fixedfactorclock_softc * const sc = priv;
1630a9d84f0Sjmcneill struct fixedfactorclock_clk *fclk = (struct fixedfactorclock_clk *)clk;
1640a9d84f0Sjmcneill struct clk *clkp_parent;
1650a9d84f0Sjmcneill
1660a9d84f0Sjmcneill clkp_parent = fdtbus_clock_get_index(sc->sc_phandle, 0);
1670a9d84f0Sjmcneill if (clkp_parent == NULL)
1680a9d84f0Sjmcneill return 0;
1690a9d84f0Sjmcneill
1700a9d84f0Sjmcneill return (clk_get_rate(clkp_parent) * fclk->mult) / fclk->div;
1710a9d84f0Sjmcneill }
172