1 /* $OpenBSD: mvclock.c,v 1.2 2018/07/24 21:52:38 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 22 #include <machine/intr.h> 23 #include <machine/bus.h> 24 #include <machine/fdt.h> 25 26 #include <dev/ofw/openfirm.h> 27 #include <dev/ofw/ofw_clock.h> 28 #include <dev/ofw/ofw_misc.h> 29 #include <dev/ofw/fdt.h> 30 31 struct mvclock_softc { 32 struct device sc_dev; 33 34 struct clock_device sc_cd; 35 }; 36 37 int mvclock_match(struct device *, void *, void *); 38 void mvclock_attach(struct device *, struct device *, void *); 39 40 struct cfattach mvclock_ca = { 41 sizeof (struct mvclock_softc), mvclock_match, mvclock_attach 42 }; 43 44 struct cfdriver mvclock_cd = { 45 NULL, "mvclock", DV_DULL 46 }; 47 48 uint32_t ap806_get_frequency(void *, uint32_t *); 49 uint32_t cp110_get_frequency(void *, uint32_t *); 50 void cp110_enable(void *, uint32_t *, int); 51 52 int 53 mvclock_match(struct device *parent, void *match, void *aux) 54 { 55 struct fdt_attach_args *faa = aux; 56 57 return (OF_is_compatible(faa->fa_node, "marvell,ap806-clock") || 58 OF_is_compatible(faa->fa_node, "marvell,cp110-clock")); 59 } 60 61 void 62 mvclock_attach(struct device *parent, struct device *self, void *aux) 63 { 64 struct mvclock_softc *sc = (struct mvclock_softc *)self; 65 struct fdt_attach_args *faa = aux; 66 67 printf("\n"); 68 69 sc->sc_cd.cd_node = faa->fa_node; 70 sc->sc_cd.cd_cookie = sc; 71 if (OF_is_compatible(faa->fa_node, "marvell,ap806-clock")) { 72 sc->sc_cd.cd_get_frequency = ap806_get_frequency; 73 } else { 74 sc->sc_cd.cd_get_frequency = cp110_get_frequency; 75 sc->sc_cd.cd_enable = cp110_enable; 76 } 77 clock_register(&sc->sc_cd); 78 } 79 80 /* AP806 block */ 81 82 #define AP806_CORE_FIXED 2 83 #define AP806_CORE_MSS 3 84 #define AP806_CORE_SDIO 4 85 86 uint32_t 87 ap806_get_frequency(void *cookie, uint32_t *cells) 88 { 89 uint32_t idx = cells[0]; 90 91 switch (idx) { 92 case AP806_CORE_FIXED: 93 /* fixed PLL at 1200MHz */ 94 return 1200000000; 95 case AP806_CORE_MSS: 96 /* MSS clock is fixed clock divided by 6 */ 97 return 200000000; 98 case AP806_CORE_SDIO: 99 /* SDIO/eMMC clock is fixed clock divided by 3 */ 100 return 400000000; 101 default: 102 break; 103 } 104 105 printf("%s: 0x%08x\n", __func__, idx); 106 return 0; 107 } 108 109 /* CP110 block */ 110 111 #define CP110_PM_CLOCK_GATING_CTRL 0x220 112 113 #define CP110_CORE_APLL 0 114 #define CP110_CORE_PPV2 1 115 #define CP110_CORE_X2CORE 2 116 #define CP110_CORE_CORE 3 117 #define CP110_CORE_SDIO 5 118 119 #define CP110_GATE_SDIO 4 120 #define CP110_GATE_SLOW_IO 21 121 122 uint32_t 123 cp110_get_frequency(void *cookie, uint32_t *cells) 124 { 125 struct mvclock_softc *sc = cookie; 126 uint32_t mod = cells[0]; 127 uint32_t idx = cells[1]; 128 uint32_t parent[2] = { 0, 0 }; 129 130 /* Core clocks */ 131 if (mod == 0) { 132 switch (idx) { 133 case CP110_CORE_APLL: 134 /* fixed PLL at 1GHz */ 135 return 1000000000; 136 case CP110_CORE_PPV2: 137 /* PPv2 clock is APLL/3 */ 138 return 333333333; 139 case CP110_CORE_X2CORE: 140 /* X2CORE clock is APLL/2 */ 141 return 500000000; 142 case CP110_CORE_CORE: 143 /* Core clock is X2CORE/2 */ 144 return 250000000; 145 case CP110_CORE_SDIO: 146 /* SDIO clock is APLL/2.5 */ 147 return 400000000; 148 default: 149 break; 150 } 151 } 152 153 /* Gatable clocks */ 154 if (mod == 1) { 155 switch (idx) { 156 case CP110_GATE_SDIO: 157 parent[1] = CP110_CORE_SDIO; 158 break; 159 case CP110_GATE_SLOW_IO: 160 parent[1] = CP110_CORE_X2CORE; 161 break; 162 default: 163 break; 164 } 165 166 if (parent[1] != 0) 167 return cp110_get_frequency(sc, parent); 168 } 169 170 printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx); 171 return 0; 172 } 173 174 void 175 cp110_enable(void *cookie, uint32_t *cells, int on) 176 { 177 struct mvclock_softc *sc = cookie; 178 uint32_t mod = cells[0]; 179 uint32_t idx = cells[1]; 180 181 /* Gatable clocks */ 182 if (mod == 1 && idx < 32) { 183 struct regmap *rm; 184 uint32_t reg; 185 186 rm = regmap_bynode(OF_parent(sc->sc_cd.cd_node)); 187 if (rm == NULL) { 188 printf("%s: can't enable clock 0x%08x 0x%08x\n", 189 sc->sc_dev.dv_xname, mod, idx); 190 return; 191 } 192 reg = regmap_read_4(rm, CP110_PM_CLOCK_GATING_CTRL); 193 if (on) 194 reg |= (1U << idx); 195 else 196 reg &= ~(1U << idx); 197 regmap_write_4(rm, CP110_PM_CLOCK_GATING_CTRL, reg); 198 return; 199 } 200 201 printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx); 202 } 203