/* $NetBSD: kirkwood.c,v 1.10 2017/01/07 16:19:28 kiyohara Exp $ */ /* * Copyright (c) 2010 KIYOHARA Takashi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __KERNEL_RCSID(0, "$NetBSD: kirkwood.c,v 1.10 2017/01/07 16:19:28 kiyohara Exp $"); #define _INTR_PRIVATE #include "mvsocgpp.h" #include #include #include #include #include #include #include #include #include static void kirkwood_intr_init(void); static void kirkwood_pic_unblock_irqs(struct pic_softc *, size_t, uint32_t); static void kirkwood_pic_block_irqs(struct pic_softc *, size_t, uint32_t); static void kirkwood_pic_establish_irq(struct pic_softc *, struct intrsource *); static void kirkwood_pic_source_name(struct pic_softc *, int, char *, size_t); static int kirkwood_find_pending_irqs(void); static void kirkwood_getclks(vaddr_t); static int kirkwood_clkgating(struct marvell_attach_args *); static const char * const sources[64] = { "MainHighSum(0)", "Bridge(1)", "Host2CPU DB(2)", "CPU2Host DB(3)", "Reserved_4(4)", "Xor0Chan0(5)", "Xor0Chan1(6)", "Xor1Chan0(7)", "Xor1Chan1(8)", "PEX0INT(9)", "Reserved(10)", "GbE0Sum(11)", "GbE0Rx(12)", "GbE0Tx(13)", "GbE0Misc(14)", "GbE1Sum(15)", "GbE1Rx(16)", "GbE1Tx(17)", "GbE1Misc(18)", "USB0Cnt(19)", "Reserved(20)", "Sata(21)", "SecurityInt(22)", "SPIInt(23)", "AudioINT(24)", "Reserved(25)", "TS0Int(26)", "Reserved(27)", "SDIOInt(28)", "TWSI(29)", "AVBInt(30)", "TDMInt(31)", "Reserved(32)", "Uart0Int(33)", "Uart1Int(34)", "GPIOLo7_0(35)", "GPIOLo8_15(36)", "GPIOLo16_23(37)", "GPIOLo24_31(38)", "GPIOHi7_0(39)", "GPIOHi8_15(40)", "GPIOHi16_23(41)", "XOR0Err(42)", "XOR1Err(43)", "PEX0Err(44)", "Reserved(45)", "GbE0Err(46)", "GbE1Err(47)", "USBErr(48)", "SecurityErr(49)", "AudioErr(50)", "Reserved(51)", "Reserved(52)", "RTCInt(53)", "Reserved(54)", "Reserved(55)", "Reserved(56)", "Reserved(57)", "Reserved(58)", "Reserved(59)", "Reserved(60)", "Reserved(61)", "Reserved(62)", "Reserved(63)" }; static struct pic_ops kirkwood_picops = { .pic_unblock_irqs = kirkwood_pic_unblock_irqs, .pic_block_irqs = kirkwood_pic_block_irqs, .pic_establish_irq = kirkwood_pic_establish_irq, .pic_source_name = kirkwood_pic_source_name, }; static struct pic_softc kirkwood_pic = { .pic_ops = &kirkwood_picops, .pic_maxsources = 64, .pic_name = "kirkwood", }; static struct { bus_size_t offset; uint32_t bits; } clkgatings[]= { { KIRKWOOD_GBE0_BASE, (1 << 0) }, { MVSOC_PEX_BASE, (1 << 2) }, { KIRKWOOD_USB_BASE, (1 << 3) }, { KIRKWOOD_SDIO_BASE, (1 << 4) }, { KIRKWOOD_MTS_BASE, (1 << 5) }, #if 0 { Dunit, (1 << 6) }, /* SDRAM Unit Clock */ { Runit, (1 << 7) }, /* Runit Clock */ #endif { KIRKWOOD_IDMAC_BASE, (1 << 8) | (1 << 16) }, { KIRKWOOD_AUDIO_BASE, (1 << 9) }, { KIRKWOOD_SATAHC_BASE, (1 << 14) | (1 << 15) }, { KIRKWOOD_CESA_BASE, (1 << 17) }, { KIRKWOOD_GBE1_BASE, (1 << 19) }, { KIRKWOOD_TDM_BASE, (1 << 20) }, }; /* * kirkwood_bootstrap: * * Initialize the rest of the Kirkwood dependencies, making it * ready to handle interrupts from devices. */ void kirkwood_bootstrap(vaddr_t iobase) { /* disable all interrupts */ write_mlmbreg(KIRKWOOD_MLMB_MIRQIMLR, 0); write_mlmbreg(KIRKWOOD_MLMB_MIRQIMHR, 0); /* disable all bridge interrupts */ write_mlmbreg(MVSOC_MLMB_MLMBIMR, 0); mvsoc_intr_init = kirkwood_intr_init; #if NMVSOCGPP > 0 switch (mvsoc_model()) { case MARVELL_KIRKWOOD_88F6180: gpp_npins = 30; break; case MARVELL_KIRKWOOD_88F6192: gpp_npins = 36; break; case MARVELL_KIRKWOOD_88F6281: gpp_npins = 50; break; case MARVELL_KIRKWOOD_88F6282: gpp_npins = 50; break; } gpp_irqbase = 96; /* Main Low(32) + High(32) + Bridge(32) */ #endif kirkwood_getclks(iobase); mvsoc_clkgating = kirkwood_clkgating; } static void kirkwood_intr_init(void) { extern struct pic_softc mvsoc_bridge_pic; void *ih __diagused; pic_add(&kirkwood_pic, 0); pic_add(&mvsoc_bridge_pic, 64); ih = intr_establish(KIRKWOOD_IRQ_BRIDGE, IPL_HIGH, IST_LEVEL_HIGH, pic_handle_intr, &mvsoc_bridge_pic); KASSERT(ih != NULL); find_pending_irqs = kirkwood_find_pending_irqs; } /* ARGSUSED */ static void kirkwood_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase, uint32_t irq_mask) { const size_t reg = KIRKWOOD_MLMB_MIRQIMLR + irqbase * (KIRKWOOD_MLMB_MIRQIMHR - KIRKWOOD_MLMB_MIRQIMLR) / 32; KASSERT(irqbase < 64); write_mlmbreg(reg, read_mlmbreg(reg) | irq_mask); } /* ARGSUSED */ static void kirkwood_pic_block_irqs(struct pic_softc *pic, size_t irqbase, uint32_t irq_mask) { const size_t reg = KIRKWOOD_MLMB_MIRQIMLR + irqbase * (KIRKWOOD_MLMB_MIRQIMHR - KIRKWOOD_MLMB_MIRQIMLR) / 32; KASSERT(irqbase < 64); write_mlmbreg(reg, read_mlmbreg(reg) & ~irq_mask); } /* ARGSUSED */ static void kirkwood_pic_establish_irq(struct pic_softc *pic, struct intrsource *is) { /* Nothing */ } static void kirkwood_pic_source_name(struct pic_softc *pic, int irq, char *buf, size_t len) { strlcpy(buf, sources[pic->pic_irqbase + irq], len); } /* * Called with interrupts disabled */ static int kirkwood_find_pending_irqs(void) { int ipl = 0; uint32_t causelow = read_mlmbreg(KIRKWOOD_MLMB_MICLR); uint32_t pendinglow = read_mlmbreg(KIRKWOOD_MLMB_MIRQIMLR); pendinglow &= causelow; if (pendinglow != 0) ipl |= pic_mark_pending_sources(&kirkwood_pic, 0, pendinglow); if ((causelow & (1 << KIRKWOOD_IRQ_HIGH)) == (1 << KIRKWOOD_IRQ_HIGH)) { uint32_t causehigh = read_mlmbreg(KIRKWOOD_MLMB_MICHR); uint32_t pendinghigh = read_mlmbreg(KIRKWOOD_MLMB_MIRQIMHR); pendinghigh &= causehigh; ipl |= pic_mark_pending_sources(&kirkwood_pic, 32, pendinghigh); } return ipl; } /* * Clock functions */ static void kirkwood_getclks(vaddr_t iobase) { uint32_t reg; uint16_t model; #define MHz * 1000 * 1000 model = mvsoc_model(); if (model == MARVELL_KIRKWOOD_88F6281 || model == MARVELL_KIRKWOOD_88F6282) mvTclk = 200 MHz; else /* 166MHz */ mvTclk = 166666667; reg = *(volatile uint32_t *)(iobase + KIRKWOOD_MPP_BASE + KIRKWOOD_MPP_SAMPLE_AT_RESET); if (model == MARVELL_KIRKWOOD_88F6180) { switch (reg & 0x0000001c) { case 0x00000014: mvPclk = 600 MHz; break; case 0x00000018: mvPclk = 800 MHz; break; default: panic("unknown mvPclk\n"); } mvSysclk = 200 MHz; } else { switch (reg & 0x0040001a) { case 0x00000002: mvPclk = 400 MHz; break; case 0x00000008: mvPclk = 600 MHz; break; case 0x00400008: mvPclk = 800 MHz; break; case 0x0040000a: mvPclk = 1000 MHz; break; case 0x00000012: mvPclk = 1200 MHz; break; case 0x00000018: mvPclk = 1500 MHz; break; case 0x0000001a: mvPclk = 1600 MHz; break; case 0x00400018: mvPclk = 1800 MHz; break; case 0x0040001a: mvPclk = 2000 MHz; break; default: panic("unknown mvPclk\n"); } switch (reg & 0x000001e0) { case 0x00000000: mvSysclk = mvPclk * 1 / 1; break; case 0x00000040: mvSysclk = mvPclk * 1 / 2; break; case 0x00000060: mvSysclk = mvPclk * 2 / 5; break; case 0x00000080: mvSysclk = mvPclk * 1 / 3; break; case 0x000000c0: mvSysclk = mvPclk * 1 / 4; break; case 0x000000e0: mvSysclk = mvPclk * 2 / 9; break; case 0x00000100: mvSysclk = mvPclk * 1 / 5; break; case 0x00000120: mvSysclk = mvPclk * 1 / 6; break; default: panic("unknown mvSysclk\n"); } } #undef MHz } static int kirkwood_clkgating(struct marvell_attach_args *mva) { uint32_t val; int i; for (i = 0; i < __arraycount(clkgatings); i++) { if (clkgatings[i].offset == mva->mva_offset) { val = read_mlmbreg(MVSOC_MLMB_CLKGATING); if ((val & clkgatings[i].bits) == clkgatings[i].bits) /* Clock enabled */ return 0; return 1; } } /* Clock Gating not support */ return 0; }