1/* $NetBSD: kirkwood.c,v 1.11 2021/08/30 00:04:30 rin Exp $ */ 2/* 3 * Copyright (c) 2010 KIYOHARA Takashi 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__KERNEL_RCSID(0, "$NetBSD: kirkwood.c,v 1.11 2021/08/30 00:04:30 rin Exp $"); 30 31#define _INTR_PRIVATE 32 33#include "mvsocgpp.h" 34 35#include <sys/param.h> 36#include <sys/bus.h> 37 38#include <machine/intr.h> 39 40#include <arm/pic/picvar.h> 41#include <arm/pic/picvar.h> 42 43#include <arm/marvell/mvsocreg.h> 44#include <arm/marvell/mvsocvar.h> 45#include <arm/marvell/kirkwoodreg.h> 46 47#include <dev/marvell/marvellreg.h> 48 49 50static void kirkwood_intr_init(void); 51 52static void kirkwood_pic_unblock_irqs(struct pic_softc *, size_t, uint32_t); 53static void kirkwood_pic_block_irqs(struct pic_softc *, size_t, uint32_t); 54static void kirkwood_pic_establish_irq(struct pic_softc *, struct intrsource *); 55static void kirkwood_pic_source_name(struct pic_softc *, int, char *, size_t); 56 57static int kirkwood_find_pending_irqs(void); 58 59static void kirkwood_getclks(vaddr_t); 60static int kirkwood_clkgating(struct marvell_attach_args *); 61 62static const char * const sources[64] = { 63 "MainHighSum(0)", "Bridge(1)", "Host2CPU DB(2)", "CPU2Host DB(3)", 64 "Reserved_4(4)", "Xor0Chan0(5)", "Xor0Chan1(6)", "Xor1Chan0(7)", 65 "Xor1Chan1(8)", "PEX0INT(9)", "Reserved(10)", "GbE0Sum(11)", 66 "GbE0Rx(12)", "GbE0Tx(13)", "GbE0Misc(14)", "GbE1Sum(15)", 67 "GbE1Rx(16)", "GbE1Tx(17)", "GbE1Misc(18)", "USB0Cnt(19)", 68 "Reserved(20)", "Sata(21)", "SecurityInt(22)", "SPIInt(23)", 69 "AudioINT(24)", "Reserved(25)", "TS0Int(26)", "Reserved(27)", 70 "SDIOInt(28)", "TWSI(29)", "AVBInt(30)", "TDMInt(31)", 71 72 "Reserved(32)", "Uart0Int(33)", "Uart1Int(34)", "GPIOLo7_0(35)", 73 "GPIOLo8_15(36)", "GPIOLo16_23(37)", "GPIOLo24_31(38)", "GPIOHi7_0(39)", 74 "GPIOHi8_15(40)", "GPIOHi16_23(41)", "XOR0Err(42)", "XOR1Err(43)", 75 "PEX0Err(44)", "Reserved(45)", "GbE0Err(46)", "GbE1Err(47)", 76 "USBErr(48)", "SecurityErr(49)", "AudioErr(50)", "Reserved(51)", 77 "Reserved(52)", "RTCInt(53)", "Reserved(54)", "Reserved(55)", 78 "Reserved(56)", "Reserved(57)", "Reserved(58)", "Reserved(59)", 79 "Reserved(60)", "Reserved(61)", "Reserved(62)", "Reserved(63)" 80}; 81 82static struct pic_ops kirkwood_picops = { 83 .pic_unblock_irqs = kirkwood_pic_unblock_irqs, 84 .pic_block_irqs = kirkwood_pic_block_irqs, 85 .pic_establish_irq = kirkwood_pic_establish_irq, 86 .pic_source_name = kirkwood_pic_source_name, 87}; 88static struct pic_softc kirkwood_pic = { 89 .pic_ops = &kirkwood_picops, 90 .pic_maxsources = 64, 91 .pic_name = "kirkwood", 92}; 93 94static struct { 95 bus_size_t offset; 96 uint32_t bits; 97} clkgatings[]= { 98 { KIRKWOOD_GBE0_BASE, (1 << 0) }, 99 { MVSOC_PEX_BASE, (1 << 2) }, 100 { KIRKWOOD_USB_BASE, (1 << 3) }, 101 { KIRKWOOD_SDIO_BASE, (1 << 4) }, 102 { KIRKWOOD_MTS_BASE, (1 << 5) }, 103#if 0 104 { Dunit, (1 << 6) }, /* SDRAM Unit Clock */ 105 { Runit, (1 << 7) }, /* Runit Clock */ 106#endif 107 { KIRKWOOD_IDMAC_BASE, (1 << 8) | (1 << 16) }, 108 { KIRKWOOD_AUDIO_BASE, (1 << 9) }, 109 { KIRKWOOD_SATAHC_BASE, (1 << 14) | (1 << 15) }, 110 { KIRKWOOD_CESA_BASE, (1 << 17) }, 111 { KIRKWOOD_GBE1_BASE, (1 << 19) }, 112 { KIRKWOOD_TDM_BASE, (1 << 20) }, 113}; 114 115 116/* 117 * kirkwood_bootstrap: 118 * 119 * Initialize the rest of the Kirkwood dependencies, making it 120 * ready to handle interrupts from devices. 121 */ 122void 123kirkwood_bootstrap(vaddr_t iobase) 124{ 125 126 /* disable all interrupts */ 127 write_mlmbreg(KIRKWOOD_MLMB_MIRQIMLR, 0); 128 write_mlmbreg(KIRKWOOD_MLMB_MIRQIMHR, 0); 129 130 /* disable all bridge interrupts */ 131 write_mlmbreg(MVSOC_MLMB_MLMBIMR, 0); 132 133 mvsoc_intr_init = kirkwood_intr_init; 134 135#if NMVSOCGPP > 0 136 switch (mvsoc_model()) { 137 case MARVELL_KIRKWOOD_88F6180: gpp_npins = 30; break; 138 case MARVELL_KIRKWOOD_88F6192: gpp_npins = 36; break; 139 case MARVELL_KIRKWOOD_88F6281: gpp_npins = 50; break; 140 case MARVELL_KIRKWOOD_88F6282: gpp_npins = 50; break; 141 } 142 gpp_irqbase = 96; /* Main Low(32) + High(32) + Bridge(32) */ 143#endif 144 145 kirkwood_getclks(iobase); 146 mvsoc_clkgating = kirkwood_clkgating; 147} 148 149static void 150kirkwood_intr_init(void) 151{ 152 extern struct pic_softc mvsoc_bridge_pic; 153 void *ih __diagused; 154 155 pic_add(&kirkwood_pic, 0); 156 157 pic_add(&mvsoc_bridge_pic, 64); 158 ih = intr_establish(KIRKWOOD_IRQ_BRIDGE, IPL_HIGH, IST_LEVEL_HIGH, 159 pic_handle_intr, &mvsoc_bridge_pic); 160 KASSERT(ih != NULL); 161 162 find_pending_irqs = kirkwood_find_pending_irqs; 163} 164 165/* ARGSUSED */ 166static void 167kirkwood_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase, 168 uint32_t irq_mask) 169{ 170 const size_t reg = KIRKWOOD_MLMB_MIRQIMLR 171 + irqbase * (KIRKWOOD_MLMB_MIRQIMHR - KIRKWOOD_MLMB_MIRQIMLR) / 32; 172 173 KASSERT(irqbase < 64); 174 write_mlmbreg(reg, read_mlmbreg(reg) | irq_mask); 175} 176 177/* ARGSUSED */ 178static void 179kirkwood_pic_block_irqs(struct pic_softc *pic, size_t irqbase, 180 uint32_t irq_mask) 181{ 182 const size_t reg = KIRKWOOD_MLMB_MIRQIMLR 183 + irqbase * (KIRKWOOD_MLMB_MIRQIMHR - KIRKWOOD_MLMB_MIRQIMLR) / 32; 184 185 KASSERT(irqbase < 64); 186 write_mlmbreg(reg, read_mlmbreg(reg) & ~irq_mask); 187} 188 189/* ARGSUSED */ 190static void 191kirkwood_pic_establish_irq(struct pic_softc *pic, struct intrsource *is) 192{ 193 /* Nothing */ 194} 195 196static void 197kirkwood_pic_source_name(struct pic_softc *pic, int irq, char *buf, size_t len) 198{ 199 200 strlcpy(buf, sources[pic->pic_irqbase + irq], len); 201} 202 203/* 204 * Called with interrupts disabled 205 */ 206static int 207kirkwood_find_pending_irqs(void) 208{ 209 int ipl = 0; 210 211 uint32_t causelow = read_mlmbreg(KIRKWOOD_MLMB_MICLR); 212 uint32_t pendinglow = read_mlmbreg(KIRKWOOD_MLMB_MIRQIMLR); 213 214 pendinglow &= causelow; 215 if (pendinglow != 0) 216 ipl |= pic_mark_pending_sources(&kirkwood_pic, 0, pendinglow); 217 218 if ((causelow & (1 << KIRKWOOD_IRQ_HIGH)) == (1 << KIRKWOOD_IRQ_HIGH)) { 219 uint32_t causehigh = read_mlmbreg(KIRKWOOD_MLMB_MICHR); 220 uint32_t pendinghigh = read_mlmbreg(KIRKWOOD_MLMB_MIRQIMHR); 221 pendinghigh &= causehigh; 222 ipl |= pic_mark_pending_sources(&kirkwood_pic, 32, pendinghigh); 223 } 224 225 return ipl; 226} 227 228/* 229 * Clock functions 230 */ 231 232static void 233kirkwood_getclks(vaddr_t iobase) 234{ 235 uint32_t reg; 236 uint16_t model; 237 238#define MHz * 1000 * 1000 239 240 model = mvsoc_model(); 241 if (model == MARVELL_KIRKWOOD_88F6281 || 242 model == MARVELL_KIRKWOOD_88F6282) 243 mvTclk = 200 MHz; 244 else /* 166MHz */ 245 mvTclk = 166666667; 246 247 reg = le32toh(*(volatile uint32_t *)(iobase + KIRKWOOD_MPP_BASE + 248 KIRKWOOD_MPP_SAMPLE_AT_RESET)); 249 if (model == MARVELL_KIRKWOOD_88F6180) { 250 switch (reg & 0x0000001c) { 251 case 0x00000014: mvPclk = 600 MHz; break; 252 case 0x00000018: mvPclk = 800 MHz; break; 253 default: 254 panic("unknown mvPclk\n"); 255 } 256 mvSysclk = 200 MHz; 257 } else { 258 switch (reg & 0x0040001a) { 259 case 0x00000002: mvPclk = 400 MHz; break; 260 case 0x00000008: mvPclk = 600 MHz; break; 261 case 0x00400008: mvPclk = 800 MHz; break; 262 case 0x0040000a: mvPclk = 1000 MHz; break; 263 case 0x00000012: mvPclk = 1200 MHz; break; 264 case 0x00000018: mvPclk = 1500 MHz; break; 265 case 0x0000001a: mvPclk = 1600 MHz; break; 266 case 0x00400018: mvPclk = 1800 MHz; break; 267 case 0x0040001a: mvPclk = 2000 MHz; break; 268 default: 269 panic("unknown mvPclk\n"); 270 } 271 272 switch (reg & 0x000001e0) { 273 case 0x00000000: mvSysclk = mvPclk * 1 / 1; break; 274 case 0x00000040: mvSysclk = mvPclk * 1 / 2; break; 275 case 0x00000060: mvSysclk = mvPclk * 2 / 5; break; 276 case 0x00000080: mvSysclk = mvPclk * 1 / 3; break; 277 case 0x000000c0: mvSysclk = mvPclk * 1 / 4; break; 278 case 0x000000e0: mvSysclk = mvPclk * 2 / 9; break; 279 case 0x00000100: mvSysclk = mvPclk * 1 / 5; break; 280 case 0x00000120: mvSysclk = mvPclk * 1 / 6; break; 281 default: 282 panic("unknown mvSysclk\n"); 283 } 284 } 285 286#undef MHz 287 288} 289 290static int 291kirkwood_clkgating(struct marvell_attach_args *mva) 292{ 293 uint32_t val; 294 int i; 295 296 for (i = 0; i < __arraycount(clkgatings); i++) { 297 if (clkgatings[i].offset == mva->mva_offset) { 298 val = read_mlmbreg(MVSOC_MLMB_CLKGATING); 299 if ((val & clkgatings[i].bits) == clkgatings[i].bits) 300 /* Clock enabled */ 301 return 0; 302 return 1; 303 } 304 } 305 /* Clock Gating not support */ 306 return 0; 307} 308