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