Home | History | Annotate | Line # | Download | only in ti
      1  1.4  jmcneill /* $NetBSD: ti_prcm.c,v 1.4 2020/06/03 14:56:09 jmcneill Exp $ */
      2  1.1  jmcneill 
      3  1.1  jmcneill /*-
      4  1.1  jmcneill  * Copyright (c) 2017 Jared McNeill <jmcneill (at) invisible.ca>
      5  1.1  jmcneill  * All rights reserved.
      6  1.1  jmcneill  *
      7  1.1  jmcneill  * Redistribution and use in source and binary forms, with or without
      8  1.1  jmcneill  * modification, are permitted provided that the following conditions
      9  1.1  jmcneill  * are met:
     10  1.1  jmcneill  * 1. Redistributions of source code must retain the above copyright
     11  1.1  jmcneill  *    notice, this list of conditions and the following disclaimer.
     12  1.1  jmcneill  * 2. Redistributions in binary form must reproduce the above copyright
     13  1.1  jmcneill  *    notice, this list of conditions and the following disclaimer in the
     14  1.1  jmcneill  *    documentation and/or other materials provided with the distribution.
     15  1.1  jmcneill  *
     16  1.1  jmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  1.1  jmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  1.1  jmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  1.1  jmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  1.1  jmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  1.1  jmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22  1.1  jmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  1.1  jmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  1.1  jmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  1.1  jmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  1.1  jmcneill  * SUCH DAMAGE.
     27  1.1  jmcneill  */
     28  1.1  jmcneill 
     29  1.1  jmcneill #include <sys/cdefs.h>
     30  1.4  jmcneill __KERNEL_RCSID(0, "$NetBSD: ti_prcm.c,v 1.4 2020/06/03 14:56:09 jmcneill Exp $");
     31  1.1  jmcneill 
     32  1.1  jmcneill #include <sys/param.h>
     33  1.1  jmcneill #include <sys/bus.h>
     34  1.1  jmcneill #include <sys/cpu.h>
     35  1.1  jmcneill #include <sys/device.h>
     36  1.1  jmcneill 
     37  1.1  jmcneill #include <dev/fdt/fdtvar.h>
     38  1.1  jmcneill 
     39  1.1  jmcneill #include <dev/clk/clk_backend.h>
     40  1.1  jmcneill 
     41  1.1  jmcneill #define	TI_PRCM_PRIVATE
     42  1.1  jmcneill #include <arm/ti/ti_prcm.h>
     43  1.1  jmcneill 
     44  1.1  jmcneill static struct ti_prcm_softc *prcm_softc = NULL;
     45  1.1  jmcneill 
     46  1.1  jmcneill static struct clk *
     47  1.1  jmcneill ti_prcm_clock_get(void *priv, const char *name)
     48  1.1  jmcneill {
     49  1.1  jmcneill 	struct ti_prcm_softc * const sc = priv;
     50  1.1  jmcneill 	struct ti_prcm_clk *clk;
     51  1.1  jmcneill 
     52  1.1  jmcneill 	clk = ti_prcm_clock_find(sc, name);
     53  1.1  jmcneill 	if (clk == NULL)
     54  1.1  jmcneill 		return NULL;
     55  1.1  jmcneill 
     56  1.1  jmcneill 	return &clk->base;
     57  1.1  jmcneill }
     58  1.1  jmcneill 
     59  1.1  jmcneill static void
     60  1.1  jmcneill ti_prcm_clock_put(void *priv, struct clk *clk)
     61  1.1  jmcneill {
     62  1.1  jmcneill }
     63  1.1  jmcneill 
     64  1.1  jmcneill static u_int
     65  1.1  jmcneill ti_prcm_clock_get_rate(void *priv, struct clk *clkp)
     66  1.1  jmcneill {
     67  1.1  jmcneill 	struct ti_prcm_softc * const sc = priv;
     68  1.1  jmcneill 	struct ti_prcm_clk *clk = (struct ti_prcm_clk *)clkp;
     69  1.1  jmcneill 	struct clk *clkp_parent;
     70  1.1  jmcneill 
     71  1.1  jmcneill 	if (clk->get_rate)
     72  1.1  jmcneill 		return clk->get_rate(sc, clk);
     73  1.1  jmcneill 
     74  1.1  jmcneill 	clkp_parent = clk_get_parent(clkp);
     75  1.1  jmcneill 	if (clkp_parent == NULL) {
     76  1.1  jmcneill 		aprint_error("%s: no parent for %s\n", __func__, clk->base.name);
     77  1.1  jmcneill 		return 0;
     78  1.1  jmcneill 	}
     79  1.1  jmcneill 
     80  1.1  jmcneill 	return clk_get_rate(clkp_parent);
     81  1.1  jmcneill }
     82  1.1  jmcneill 
     83  1.1  jmcneill static int
     84  1.1  jmcneill ti_prcm_clock_set_rate(void *priv, struct clk *clkp, u_int rate)
     85  1.1  jmcneill {
     86  1.1  jmcneill 	struct ti_prcm_softc * const sc = priv;
     87  1.1  jmcneill 	struct ti_prcm_clk *clk = (struct ti_prcm_clk *)clkp;
     88  1.1  jmcneill 	struct clk *clkp_parent;
     89  1.1  jmcneill 
     90  1.1  jmcneill 	if (clkp->flags & CLK_SET_RATE_PARENT) {
     91  1.1  jmcneill 		clkp_parent = clk_get_parent(clkp);
     92  1.1  jmcneill 		if (clkp_parent == NULL) {
     93  1.1  jmcneill 			aprint_error("%s: no parent for %s\n", __func__, clk->base.name);
     94  1.1  jmcneill 			return ENXIO;
     95  1.1  jmcneill 		}
     96  1.1  jmcneill 		return clk_set_rate(clkp_parent, rate);
     97  1.1  jmcneill 	}
     98  1.1  jmcneill 
     99  1.1  jmcneill 	if (clk->set_rate)
    100  1.1  jmcneill 		return clk->set_rate(sc, clk, rate);
    101  1.1  jmcneill 
    102  1.1  jmcneill 	return ENXIO;
    103  1.1  jmcneill }
    104  1.1  jmcneill 
    105  1.1  jmcneill static int
    106  1.1  jmcneill ti_prcm_clock_enable(void *priv, struct clk *clkp)
    107  1.1  jmcneill {
    108  1.1  jmcneill 	struct ti_prcm_softc * const sc = priv;
    109  1.1  jmcneill 	struct ti_prcm_clk *clk = (struct ti_prcm_clk *)clkp;
    110  1.1  jmcneill 	struct clk *clkp_parent;
    111  1.1  jmcneill 	int error = 0;
    112  1.1  jmcneill 
    113  1.1  jmcneill 	clkp_parent = clk_get_parent(clkp);
    114  1.1  jmcneill 	if (clkp_parent != NULL) {
    115  1.1  jmcneill 		error = clk_enable(clkp_parent);
    116  1.1  jmcneill 		if (error != 0)
    117  1.1  jmcneill 			return error;
    118  1.1  jmcneill 	}
    119  1.1  jmcneill 
    120  1.1  jmcneill 	if (clk->enable)
    121  1.1  jmcneill 		error = clk->enable(sc, clk, 1);
    122  1.1  jmcneill 
    123  1.1  jmcneill 	return error;
    124  1.1  jmcneill }
    125  1.1  jmcneill 
    126  1.1  jmcneill static int
    127  1.1  jmcneill ti_prcm_clock_disable(void *priv, struct clk *clkp)
    128  1.1  jmcneill {
    129  1.1  jmcneill 	struct ti_prcm_softc * const sc = priv;
    130  1.1  jmcneill 	struct ti_prcm_clk *clk = (struct ti_prcm_clk *)clkp;
    131  1.1  jmcneill 	int error = EINVAL;
    132  1.1  jmcneill 
    133  1.1  jmcneill 	if (clk->enable)
    134  1.1  jmcneill 		error = clk->enable(sc, clk, 0);
    135  1.1  jmcneill 
    136  1.1  jmcneill 	return error;
    137  1.1  jmcneill }
    138  1.1  jmcneill 
    139  1.1  jmcneill static int
    140  1.1  jmcneill ti_prcm_clock_set_parent(void *priv, struct clk *clkp,
    141  1.1  jmcneill     struct clk *clkp_parent)
    142  1.1  jmcneill {
    143  1.1  jmcneill 	struct ti_prcm_softc * const sc = priv;
    144  1.1  jmcneill 	struct ti_prcm_clk *clk = (struct ti_prcm_clk *)clkp;
    145  1.1  jmcneill 
    146  1.1  jmcneill 	if (clk->set_parent == NULL)
    147  1.1  jmcneill 		return EINVAL;
    148  1.1  jmcneill 
    149  1.1  jmcneill 	return clk->set_parent(sc, clk, clkp_parent->name);
    150  1.1  jmcneill }
    151  1.1  jmcneill 
    152  1.1  jmcneill static struct clk *
    153  1.1  jmcneill ti_prcm_clock_get_parent(void *priv, struct clk *clkp)
    154  1.1  jmcneill {
    155  1.1  jmcneill 	struct ti_prcm_softc * const sc = priv;
    156  1.1  jmcneill 	struct ti_prcm_clk *clk = (struct ti_prcm_clk *)clkp;
    157  1.1  jmcneill 	struct ti_prcm_clk *clk_parent;
    158  1.1  jmcneill 	const char *parent;
    159  1.1  jmcneill 
    160  1.1  jmcneill 	if (clk->get_parent == NULL)
    161  1.1  jmcneill 		return NULL;
    162  1.1  jmcneill 
    163  1.1  jmcneill 	parent = clk->get_parent(sc, clk);
    164  1.1  jmcneill 	if (parent == NULL)
    165  1.1  jmcneill 		return NULL;
    166  1.1  jmcneill 
    167  1.1  jmcneill 	clk_parent = ti_prcm_clock_find(sc, parent);
    168  1.1  jmcneill 	if (clk_parent != NULL)
    169  1.1  jmcneill 		return &clk_parent->base;
    170  1.1  jmcneill 
    171  1.1  jmcneill 	/* No parent in this domain, try FDT */
    172  1.1  jmcneill 	return fdtbus_clock_get(sc->sc_phandle, parent);
    173  1.1  jmcneill }
    174  1.1  jmcneill 
    175  1.1  jmcneill static const struct clk_funcs ti_prcm_clock_funcs = {
    176  1.1  jmcneill 	.get = ti_prcm_clock_get,
    177  1.1  jmcneill 	.put = ti_prcm_clock_put,
    178  1.1  jmcneill 	.get_rate = ti_prcm_clock_get_rate,
    179  1.1  jmcneill 	.set_rate = ti_prcm_clock_set_rate,
    180  1.1  jmcneill 	.enable = ti_prcm_clock_enable,
    181  1.1  jmcneill 	.disable = ti_prcm_clock_disable,
    182  1.1  jmcneill 	.set_parent = ti_prcm_clock_set_parent,
    183  1.1  jmcneill 	.get_parent = ti_prcm_clock_get_parent,
    184  1.1  jmcneill };
    185  1.1  jmcneill 
    186  1.1  jmcneill struct ti_prcm_clk *
    187  1.1  jmcneill ti_prcm_clock_find(struct ti_prcm_softc *sc, const char *name)
    188  1.1  jmcneill {
    189  1.1  jmcneill 	for (int i = 0; i < sc->sc_nclks; i++) {
    190  1.1  jmcneill 		if (sc->sc_clks[i].base.name == NULL)
    191  1.1  jmcneill 			continue;
    192  1.1  jmcneill 		if (strcmp(sc->sc_clks[i].base.name, name) == 0)
    193  1.1  jmcneill 			return &sc->sc_clks[i];
    194  1.1  jmcneill 	}
    195  1.1  jmcneill 
    196  1.1  jmcneill 	return NULL;
    197  1.1  jmcneill }
    198  1.1  jmcneill 
    199  1.1  jmcneill int
    200  1.1  jmcneill ti_prcm_attach(struct ti_prcm_softc *sc)
    201  1.1  jmcneill {
    202  1.1  jmcneill 	bus_addr_t addr;
    203  1.1  jmcneill 	bus_size_t size;
    204  1.1  jmcneill 	int i;
    205  1.1  jmcneill 
    206  1.1  jmcneill 	if (fdtbus_get_reg(sc->sc_phandle, 0, &addr, &size) != 0) {
    207  1.1  jmcneill 		aprint_error(": couldn't get registers\n");
    208  1.1  jmcneill 		return ENXIO;
    209  1.1  jmcneill 	}
    210  1.1  jmcneill 	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
    211  1.1  jmcneill 		aprint_error(": couldn't map registers\n");
    212  1.1  jmcneill 		return ENXIO;
    213  1.1  jmcneill 	}
    214  1.1  jmcneill 
    215  1.1  jmcneill 	sc->sc_clkdom.funcs = &ti_prcm_clock_funcs;
    216  1.1  jmcneill 	sc->sc_clkdom.priv = sc;
    217  1.1  jmcneill 	for (i = 0; i < sc->sc_nclks; i++)
    218  1.1  jmcneill 		sc->sc_clks[i].base.domain = &sc->sc_clkdom;
    219  1.1  jmcneill 
    220  1.1  jmcneill 	KASSERT(prcm_softc == NULL);
    221  1.1  jmcneill 	prcm_softc = sc;
    222  1.1  jmcneill 
    223  1.1  jmcneill 	return 0;
    224  1.1  jmcneill }
    225  1.1  jmcneill 
    226  1.1  jmcneill struct clk *
    227  1.1  jmcneill ti_prcm_get_hwmod(const int phandle, u_int index)
    228  1.1  jmcneill {
    229  1.1  jmcneill 	struct ti_prcm_clk *tc;
    230  1.1  jmcneill 	const char *hwmods, *p;
    231  1.3  jmcneill 	int len, resid, hwmod_phandle;
    232  1.1  jmcneill 	u_int n;
    233  1.1  jmcneill 
    234  1.1  jmcneill 	KASSERTMSG(prcm_softc != NULL, "prcm driver not attached");
    235  1.1  jmcneill 
    236  1.3  jmcneill 	/* If this node does not have a ti,hwmods property, try the parent */
    237  1.3  jmcneill 	hwmod_phandle = of_hasprop(phandle, "ti,hwmods") ? phandle : OF_parent(phandle);
    238  1.3  jmcneill 
    239  1.4  jmcneill 	/* Lookup clock by "ti,hwmods" name (old method) */
    240  1.3  jmcneill 	hwmods = fdtbus_get_prop(hwmod_phandle, "ti,hwmods", &len);
    241  1.4  jmcneill 	if (len > 0) {
    242  1.4  jmcneill 		p = hwmods;
    243  1.4  jmcneill 		for (n = 0, resid = len; resid > 0; n++) {
    244  1.4  jmcneill 			if (n == index) {
    245  1.4  jmcneill 				tc = ti_prcm_clock_find(prcm_softc, p);
    246  1.4  jmcneill 				if (tc == NULL) {
    247  1.4  jmcneill 					aprint_error_dev(prcm_softc->sc_dev,
    248  1.4  jmcneill 					    "no hwmod with name '%s'\n", p);
    249  1.4  jmcneill 					return NULL;
    250  1.4  jmcneill 				}
    251  1.4  jmcneill 				KASSERT(tc->type == TI_PRCM_HWMOD);
    252  1.4  jmcneill 				return &tc->base;
    253  1.1  jmcneill 			}
    254  1.4  jmcneill 			resid -= strlen(p);
    255  1.4  jmcneill 			p += strlen(p) + 1;
    256  1.1  jmcneill 		}
    257  1.1  jmcneill 	}
    258  1.1  jmcneill 
    259  1.4  jmcneill 	/* Find clock resource */
    260  1.4  jmcneill 	return fdtbus_clock_get_index(hwmod_phandle, index);
    261  1.1  jmcneill }
    262  1.2  jmcneill 
    263  1.2  jmcneill int
    264  1.2  jmcneill ti_prcm_enable_hwmod(const int phandle, u_int index)
    265  1.2  jmcneill {
    266  1.2  jmcneill 	struct clk *clk;
    267  1.2  jmcneill 
    268  1.2  jmcneill 	clk = ti_prcm_get_hwmod(phandle, index);
    269  1.2  jmcneill 	if (clk == NULL)
    270  1.2  jmcneill 		return ENOENT;
    271  1.2  jmcneill 
    272  1.2  jmcneill 	return clk_enable(clk);
    273  1.2  jmcneill }
    274