1 /* $OpenBSD: mvclock.c,v 1.1 2018/03/17 18:50:23 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_EIP 2 116 #define CP110_CORE_CORE 3 117 #define CP110_CORE_SDIO 5 118 119 #define CP110_GATE_SDIO 4 120 121 uint32_t 122 cp110_get_frequency(void *cookie, uint32_t *cells) 123 { 124 struct mvclock_softc *sc = cookie; 125 uint32_t mod = cells[0]; 126 uint32_t idx = cells[1]; 127 uint32_t parent[2] = { 0, 0 }; 128 129 /* Core clocks */ 130 if (mod == 0) { 131 switch (idx) { 132 case CP110_CORE_APLL: 133 /* fixed PLL at 1GHz */ 134 return 1000000000; 135 case CP110_CORE_PPV2: 136 /* PPv2 clock is APLL/3 */ 137 return 333333333; 138 case CP110_CORE_EIP: 139 /* EIP clock is APLL/2 */ 140 return 500000000; 141 case CP110_CORE_CORE: 142 /* Core clock is EIP/2 */ 143 return 250000000; 144 case CP110_CORE_SDIO: 145 /* SDIO clock is APLL/2.5 */ 146 return 400000000; 147 default: 148 break; 149 } 150 } 151 152 /* Gatable clocks */ 153 if (mod == 1) { 154 switch (idx) { 155 case CP110_GATE_SDIO: 156 parent[1] = CP110_CORE_SDIO; 157 break; 158 default: 159 break; 160 } 161 162 if (parent[1] != 0) 163 return cp110_get_frequency(sc, parent); 164 } 165 166 printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx); 167 return 0; 168 } 169 170 void 171 cp110_enable(void *cookie, uint32_t *cells, int on) 172 { 173 struct mvclock_softc *sc = cookie; 174 uint32_t mod = cells[0]; 175 uint32_t idx = cells[1]; 176 177 /* Gatable clocks */ 178 if (mod == 1 && idx < 32) { 179 struct regmap *rm; 180 uint32_t reg; 181 182 rm = regmap_bynode(OF_parent(sc->sc_cd.cd_node)); 183 if (rm == NULL) { 184 printf("%s: can't enable clock 0x%08x 0x%08x\n", 185 sc->sc_dev.dv_xname, mod, idx); 186 return; 187 } 188 reg = regmap_read_4(rm, CP110_PM_CLOCK_GATING_CTRL); 189 if (on) 190 reg |= (1U << idx); 191 else 192 reg &= ~(1U << idx); 193 regmap_write_4(rm, CP110_PM_CLOCK_GATING_CTRL, reg); 194 return; 195 } 196 197 printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx); 198 } 199