1 /* $NetBSD: cdm.c,v 1.1 2026/06/27 13:28:34 rkujawa Exp $ */ 2 3 /*- 4 * Copyright (c) 2008, 2026 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Radoslaw Kujawa and Robert Swindells. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Driver for the MPC5200B Clock Distribution Module (CDM). 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: cdm.c,v 1.1 2026/06/27 13:28:34 rkujawa Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/device.h> 42 #include <sys/bus.h> 43 44 #include <dev/ofw/openfirm.h> 45 46 #include <machine/autoconf.h> 47 48 #include <powerpc/mpc5200/obiovar.h> 49 #include <powerpc/mpc5200/mpc5200reg.h> 50 #include <powerpc/mpc5200/cdmvar.h> 51 52 /* 53 * External reference oscillator on the SYS_XTAL_IN pin, in Hz. 54 */ 55 #define CDM_SYS_XTAL_IN 33333333 /* XXX: should be configurable? */ 56 57 /* CDM register offsets from the block base (MBAR + 0x0200). */ 58 #define CDM_JTAG_ID 0x00 /* JTAG identification (ro) */ 59 #define CDM_PORCFG 0x04 /* power-on reset config (ro) */ 60 #define CDM_BREADCRUMB 0x08 /* firmware scratch (never reset) */ 61 #define CDM_CFG 0x0c /* clock configuration */ 62 #define CDM_FRAC_DIV 0x10 /* 48MHz fractional divider */ 63 #define CDM_CLK_ENABLE 0x14 /* per-module clock enables */ 64 65 #define CDM_REG_SIZE 0x38 /* register window if OF omits a size */ 66 67 /* 68 * Register fields use the manual's big-endian bit numbering (MSB = bit 0); 69 * CDM_BIT() converts a manual bit number to NetBSD __BIT() (LSB = bit 0). 70 */ 71 #define CDM_BIT(n) __BIT(31 - (n)) 72 73 /* CDM Power-On Reset Configuration Register (CDM_PORCFG). */ 74 #define PORCFG_SYS_PLL_BYPASS CDM_BIT(15) /* PLL bypassed, xtal direct */ 75 #define PORCFG_SYS_PLL_CFG_1 CDM_BIT(24) /* doubles VCO only (no clk effect) */ 76 #define PORCFG_SYS_PLL_CFG_0 CDM_BIT(25) /* 0: fsys=16x, 1: fsys=12x xtal */ 77 #define PORCFG_XLB_CLK_SEL CDM_BIT(26) /* 0: XLB=fsys/4, 1: XLB=fsys/8 */ 78 #define PORCFG_PPC_PLL_CFG __BITS(0, 4) /* e300 core APLL config [0:4] */ 79 80 /* CDM Configuration Register (CDM_CFG). */ 81 #define CFG_XLB_CLK_SEL CDM_BIT(15) /* read-only mirror of PORCFG */ 82 #define CFG_IPB_CLK_SEL CDM_BIT(23) /* 0: IPB=XLB, 1: IPB=XLB/2 */ 83 #define CFG_PCI_CLK_SEL __BITS(0, 1) /* PCI clock select [1:0] */ 84 85 #define PCI_CLK_SEL_IPB 0 /* PCI = IPB */ 86 #define PCI_CLK_SEL_IPB_DIV2 1 /* PCI = IPB / 2 */ 87 /* values 2 and 3 both select PCI = XLB / 4 */ 88 89 /* 90 * Computed clock tree, shared with the on-demand accessors. 91 */ 92 static struct cdm_clocks { 93 bool cc_valid; 94 uint32_t cc_core; 95 uint32_t cc_xlb; 96 uint32_t cc_ipb; 97 uint32_t cc_pci; 98 } cdm_clocks; 99 100 static int cdm_match(device_t, cfdata_t, void *); 101 static void cdm_attach(device_t, device_t, void *); 102 103 CFATTACH_DECL_NEW(cdm, sizeof(struct cdm_softc), 104 cdm_match, cdm_attach, NULL, NULL); 105 106 static const uint8_t cdm_core_ratio_x2[32] = { 107 [0x03] = 2, /* 1x - xlb clocks core directly */ 108 [0x04] = 4, /* 2x */ [0x05] = 4, /* 2x */ 109 [0x06] = 5, /* 2.5x */ [0x07] = 9, /* 4.5x */ 110 [0x08] = 6, /* 3x */ [0x09] = 11, /* 5.5x */ 111 [0x0a] = 8, /* 4x */ [0x0b] = 10, /* 5x */ 112 [0x0d] = 12, /* 6x */ [0x0e] = 7, /* 3.5x */ 113 [0x10] = 6, /* 3x */ [0x11] = 5, /* 2.5x */ 114 [0x12] = 13, /* 6.5x */ [0x13] = 2, /* 1x direct */ 115 [0x14] = 14, /* 7x */ [0x16] = 15, /* 7.5x */ 116 [0x1c] = 16, /* 8x */ 117 }; 118 119 /* 120 * Reconstruct the clock tree from the two strap registers and cache it. 121 */ 122 static void 123 cdm_compute(uint32_t porcfg, uint32_t cfg) 124 { 125 uint32_t fsystem, xlb, ipb, pci, core; 126 uint32_t ppc_cfg, ratio_x2; 127 128 if (porcfg & PORCFG_SYS_PLL_BYPASS) 129 fsystem = CDM_SYS_XTAL_IN; /* PLL bypassed, no multiply */ 130 else 131 fsystem = CDM_SYS_XTAL_IN * 132 ((porcfg & PORCFG_SYS_PLL_CFG_0) ? 12 : 16); 133 134 xlb = fsystem / ((porcfg & PORCFG_XLB_CLK_SEL) ? 8 : 4); 135 ipb = xlb / ((cfg & CFG_IPB_CLK_SEL) ? 2 : 1); 136 137 switch (__SHIFTOUT(cfg, CFG_PCI_CLK_SEL)) { 138 case PCI_CLK_SEL_IPB: 139 pci = ipb; 140 break; 141 case PCI_CLK_SEL_IPB_DIV2: 142 pci = ipb / 2; 143 break; 144 default: /* 2, 3: PCI = XLB / 4 */ 145 pci = xlb / 4; 146 break; 147 } 148 149 ppc_cfg = __SHIFTOUT(porcfg, PORCFG_PPC_PLL_CFG); 150 ratio_x2 = cdm_core_ratio_x2[ppc_cfg]; 151 if (ratio_x2 != 0) 152 core = (uint32_t)(((uint64_t)xlb * ratio_x2) / 2); 153 else 154 core = 0; /* reserved/unclocked strap */ 155 156 cdm_clocks.cc_core = core; 157 cdm_clocks.cc_xlb = xlb; 158 cdm_clocks.cc_ipb = ipb; 159 cdm_clocks.cc_pci = pci; 160 cdm_clocks.cc_valid = true; 161 } 162 163 /* 164 * Ensure the clock tree is populated. 165 */ 166 static void 167 cdm_ensure_valid(void) 168 { 169 volatile uint32_t *cdm; 170 uint32_t porcfg, cfg; 171 172 if (cdm_clocks.cc_valid) 173 return; 174 175 cdm = (volatile uint32_t *)(uintptr_t) 176 (MPC5200_MBAR_DEFAULT + MPC5200_REG_CDM); 177 porcfg = cdm[CDM_PORCFG / sizeof(*cdm)]; 178 cfg = cdm[CDM_CFG / sizeof(*cdm)]; 179 180 cdm_compute(porcfg, cfg); 181 182 /* 183 * Guard against an implausible read 184 */ 185 if (cdm_clocks.cc_ipb < 16000000 || cdm_clocks.cc_ipb > 200000000) { 186 cdm_clocks.cc_core = MPC5200_CORE_FREQ_DEFAULT; 187 cdm_clocks.cc_xlb = MPC5200_XLB_FREQ_DEFAULT; 188 cdm_clocks.cc_ipb = MPC5200_IPB_FREQ_DEFAULT; 189 cdm_clocks.cc_pci = MPC5200_PCI_FREQ_DEFAULT; 190 } 191 } 192 193 uint32_t 194 mpc5200_cdm_get_core_freq(void) 195 { 196 cdm_ensure_valid(); 197 return cdm_clocks.cc_core != 0 ? 198 cdm_clocks.cc_core : MPC5200_CORE_FREQ_DEFAULT; 199 } 200 201 uint32_t 202 mpc5200_cdm_get_xlb_freq(void) 203 { 204 cdm_ensure_valid(); 205 return cdm_clocks.cc_xlb; 206 } 207 208 uint32_t 209 mpc5200_cdm_get_ipb_freq(void) 210 { 211 cdm_ensure_valid(); 212 return cdm_clocks.cc_ipb; 213 } 214 215 uint32_t 216 mpc5200_cdm_get_pci_freq(void) 217 { 218 cdm_ensure_valid(); 219 return cdm_clocks.cc_pci; 220 } 221 222 static int 223 cdm_match(device_t parent, cfdata_t cf, void *aux) 224 { 225 struct obio_attach_args *oba = aux; 226 char compat[32]; 227 int len; 228 229 if (strcmp(oba->obio_name, "cdm") == 0) 230 return 1; 231 232 len = OF_getprop(oba->obio_node, "compatible", compat, sizeof(compat)); 233 if (len > 0 && 234 (strcmp(compat, "mpc5200-cdm") == 0 || 235 strcmp(compat, "mpc5200b-cdm") == 0)) 236 return 1; 237 238 return 0; 239 } 240 241 static void 242 cdm_attach(device_t parent, device_t self, void *aux) 243 { 244 struct cdm_softc *sc = device_private(self); 245 struct obio_attach_args *oba = aux; 246 bus_size_t size; 247 uint32_t porcfg, cfg; 248 249 sc->sc_dev = self; 250 sc->sc_iot = oba->obio_bst; 251 252 size = oba->obio_size != 0 ? oba->obio_size : CDM_REG_SIZE; 253 if (bus_space_map(sc->sc_iot, oba->obio_addr, size, 0, 254 &sc->sc_ioh) != 0) { 255 aprint_error(": can't map registers\n"); 256 return; 257 } 258 259 porcfg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CDM_PORCFG); 260 cfg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CDM_CFG); 261 cdm_compute(porcfg, cfg); 262 263 aprint_normal(": core %u.%u MHz, XLB %u.%u MHz, IPB %u.%u MHz, " 264 "PCI %u.%u MHz\n", 265 cdm_clocks.cc_core / 1000000, (cdm_clocks.cc_core / 100000) % 10, 266 cdm_clocks.cc_xlb / 1000000, (cdm_clocks.cc_xlb / 100000) % 10, 267 cdm_clocks.cc_ipb / 1000000, (cdm_clocks.cc_ipb / 100000) % 10, 268 cdm_clocks.cc_pci / 1000000, (cdm_clocks.cc_pci / 100000) % 10); 269 } 270