Home | History | Annotate | Line # | Download | only in mpc5200
      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