Index: head/sys/arm/amlogic/aml8726/aml8726_clkmsr.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_clkmsr.c (revision 298351) +++ head/sys/arm/amlogic/aml8726/aml8726_clkmsr.c (revision 298352) @@ -1,301 +1,300 @@ /*- * Copyright 2014-2015 John Wehle * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. * */ /* * Amlogic aml8726 clock measurement driver. * * This allows various clock rates to be determine at runtime. * The measurements are done once and are not expected to change * (i.e. FDT fixup provides clk81 as bus-frequency to the MMC * and UART drivers which use the value when programming the * hardware). */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct aml8726_clkmsr_clk { const char * name; uint32_t mux; } aml8726_clkmsr_clks[] = { { "clk81", 7 }, }; #define AML_CLKMSR_CLK81 0 -#define AML_CLKMSR_NCLKS (sizeof(aml8726_clkmsr_clks) \ - / sizeof(aml8726_clkmsr_clks[0])) +#define AML_CLKMSR_NCLKS nitems(aml8726_clkmsr_clks) struct aml8726_clkmsr_softc { device_t dev; struct resource * res[1]; }; static struct resource_spec aml8726_clkmsr_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; /* * Duration can range from 1uS to 65535 uS and should be chosen * based on the expected frequency result so to maximize resolution * and avoid overflowing the 16 bit result counter. */ #define AML_CLKMSR_DURATION 32 #define AML_CLKMSR_DUTY_REG 0 #define AML_CLKMSR_0_REG 4 #define AML_CLKMSR_0_BUSY (1U << 31) #define AML_CLKMSR_0_MUX_MASK (0x3f << 20) #define AML_CLKMSR_0_MUX_SHIFT 20 #define AML_CLKMSR_0_MUX_EN (1 << 19) #define AML_CLKMSR_0_MEASURE (1 << 16) #define AML_CLKMSR_0_DURATION_MASK 0xffff #define AML_CLKMSR_0_DURATION_SHIFT 0 #define AML_CLKMSR_1_REG 8 #define AML_CLKMSR_2_REG 12 #define AML_CLKMSR_2_RESULT_MASK 0xffff #define AML_CLKMSR_2_RESULT_SHIFT 0 #define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) #define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) #define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \ (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) static int aml8726_clkmsr_clock_frequency(struct aml8726_clkmsr_softc *sc, unsigned clock) { uint32_t value; if (clock >= AML_CLKMSR_NCLKS) return (0); /* * Locking is not used as this is only expected to be called from * FDT fixup (which occurs prior to driver initialization) or attach. */ CSR_WRITE_4(sc, AML_CLKMSR_0_REG, 0); CSR_BARRIER(sc, AML_CLKMSR_0_REG); value = (aml8726_clkmsr_clks[clock].mux << AML_CLKMSR_0_MUX_SHIFT) | ((AML_CLKMSR_DURATION - 1) << AML_CLKMSR_0_DURATION_SHIFT) | AML_CLKMSR_0_MUX_EN | AML_CLKMSR_0_MEASURE; CSR_WRITE_4(sc, AML_CLKMSR_0_REG, value); CSR_BARRIER(sc, AML_CLKMSR_0_REG); while ((CSR_READ_4(sc, AML_CLKMSR_0_REG) & AML_CLKMSR_0_BUSY) != 0) cpu_spinwait(); value &= ~AML_CLKMSR_0_MEASURE; CSR_WRITE_4(sc, AML_CLKMSR_0_REG, value); CSR_BARRIER(sc, AML_CLKMSR_0_REG); value = (((CSR_READ_4(sc, AML_CLKMSR_2_REG) & AML_CLKMSR_2_RESULT_MASK) >> AML_CLKMSR_2_RESULT_SHIFT) + AML_CLKMSR_DURATION / 2) / AML_CLKMSR_DURATION; return value; } static void aml8726_clkmsr_fixup_clk81(struct aml8726_clkmsr_softc *sc, int freq) { pcell_t prop; ssize_t len; phandle_t clk_node; phandle_t node; node = ofw_bus_get_node(sc->dev); len = OF_getencprop(node, "clocks", &prop, sizeof(prop)); if ((len / sizeof(prop)) != 1 || prop == 0 || (clk_node = OF_node_from_xref(prop)) == 0) return; len = OF_getencprop(clk_node, "clock-frequency", &prop, sizeof(prop)); if ((len / sizeof(prop)) != 1 || prop != 0) return; freq = cpu_to_fdt32(freq); OF_setprop(clk_node, "clock-frequency", (void *)&freq, sizeof(freq)); } static int aml8726_clkmsr_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-clkmsr")) return (ENXIO); device_set_desc(dev, "Amlogic aml8726 clkmsr"); return (BUS_PROBE_DEFAULT); } static int aml8726_clkmsr_attach(device_t dev) { struct aml8726_clkmsr_softc *sc = device_get_softc(dev); int freq; sc->dev = dev; if (bus_alloc_resources(dev, aml8726_clkmsr_spec, sc->res)) { device_printf(dev, "can not allocate resources for device\n"); return (ENXIO); } freq = aml8726_clkmsr_clock_frequency(sc, AML_CLKMSR_CLK81); device_printf(sc->dev, "bus clock %u MHz\n", freq); aml8726_clkmsr_fixup_clk81(sc, freq * 1000000); return (0); } static int aml8726_clkmsr_detach(device_t dev) { struct aml8726_clkmsr_softc *sc = device_get_softc(dev); bus_release_resources(dev, aml8726_clkmsr_spec, sc->res); return (0); } static device_method_t aml8726_clkmsr_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aml8726_clkmsr_probe), DEVMETHOD(device_attach, aml8726_clkmsr_attach), DEVMETHOD(device_detach, aml8726_clkmsr_detach), DEVMETHOD_END }; static driver_t aml8726_clkmsr_driver = { "clkmsr", aml8726_clkmsr_methods, sizeof(struct aml8726_clkmsr_softc), }; static devclass_t aml8726_clkmsr_devclass; EARLY_DRIVER_MODULE(clkmsr, simplebus, aml8726_clkmsr_driver, aml8726_clkmsr_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_EARLY); int aml8726_clkmsr_bus_frequency() { struct resource mem; struct aml8726_clkmsr_softc sc; phandle_t node; u_long pbase, psize; u_long start, size; int freq; KASSERT(aml8726_soc_hw_rev != AML_SOC_HW_REV_UNKNOWN, ("aml8726_soc_hw_rev isn't initialized")); /* * Try to access the clkmsr node directly i.e. through /aliases/. */ if ((node = OF_finddevice("clkmsr")) != 0) if (fdt_is_compatible_strict(node, "amlogic,aml8726-clkmsr")) goto moveon; /* * Find the node the long way. */ if ((node = OF_finddevice("/soc")) == 0) return (0); if ((node = fdt_find_compatible(node, "amlogic,aml8726-clkmsr", 1)) == 0) return (0); moveon: if (fdt_get_range(OF_parent(node), 0, &pbase, &psize) != 0 || fdt_regsize(node, &start, &size) != 0) return (0); start += pbase; memset(&mem, 0, sizeof(mem)); mem.r_bustag = fdtbus_bs_tag; if (bus_space_map(mem.r_bustag, start, size, 0, &mem.r_bushandle) != 0) return (0); /* * Build an incomplete (however sufficient for the purpose * of calling aml8726_clkmsr_clock_frequency) softc. */ memset(&sc, 0, sizeof(sc)); sc.res[0] = &mem; freq = aml8726_clkmsr_clock_frequency(&sc, AML_CLKMSR_CLK81) * 1000000; bus_space_unmap(mem.r_bustag, mem.r_bushandle, size); return (freq); } Index: head/sys/arm/arm/db_interface.c =================================================================== --- head/sys/arm/arm/db_interface.c (revision 298351) +++ head/sys/arm/arm/db_interface.c (revision 298352) @@ -1,330 +1,330 @@ /* $NetBSD: db_interface.c,v 1.33 2003/08/25 04:51:10 mrg Exp $ */ /*- * Copyright (c) 1996 Scott K. Stevens * * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. * * From: db_interface.c,v 2.4 1991/02/05 17:11:13 mrt (CMU) */ /* * Interface to new debugger. */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include #include /* just for boothowto */ #include #ifdef KDB #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int nil = 0; int db_access_und_sp (struct db_variable *, db_expr_t *, int); int db_access_abt_sp (struct db_variable *, db_expr_t *, int); int db_access_irq_sp (struct db_variable *, db_expr_t *, int); static db_varfcn_t db_frame; #define DB_OFFSET(x) (db_expr_t *)offsetof(struct trapframe, x) struct db_variable db_regs[] = { { "spsr", DB_OFFSET(tf_spsr), db_frame }, { "r0", DB_OFFSET(tf_r0), db_frame }, { "r1", DB_OFFSET(tf_r1), db_frame }, { "r2", DB_OFFSET(tf_r2), db_frame }, { "r3", DB_OFFSET(tf_r3), db_frame }, { "r4", DB_OFFSET(tf_r4), db_frame }, { "r5", DB_OFFSET(tf_r5), db_frame }, { "r6", DB_OFFSET(tf_r6), db_frame }, { "r7", DB_OFFSET(tf_r7), db_frame }, { "r8", DB_OFFSET(tf_r8), db_frame }, { "r9", DB_OFFSET(tf_r9), db_frame }, { "r10", DB_OFFSET(tf_r10), db_frame }, { "r11", DB_OFFSET(tf_r11), db_frame }, { "r12", DB_OFFSET(tf_r12), db_frame }, { "usr_sp", DB_OFFSET(tf_usr_sp), db_frame }, { "usr_lr", DB_OFFSET(tf_usr_lr), db_frame }, { "svc_sp", DB_OFFSET(tf_svc_sp), db_frame }, { "svc_lr", DB_OFFSET(tf_svc_lr), db_frame }, { "pc", DB_OFFSET(tf_pc), db_frame }, { "und_sp", &nil, db_access_und_sp, }, { "abt_sp", &nil, db_access_abt_sp, }, { "irq_sp", &nil, db_access_irq_sp, }, }; -struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); +struct db_variable *db_eregs = db_regs + nitems(db_regs); int db_access_und_sp(struct db_variable *vp, db_expr_t *valp, int rw) { if (rw == DB_VAR_GET) { *valp = get_stackptr(PSR_UND32_MODE); return (1); } return (0); } int db_access_abt_sp(struct db_variable *vp, db_expr_t *valp, int rw) { if (rw == DB_VAR_GET) { *valp = get_stackptr(PSR_ABT32_MODE); return (1); } return (0); } int db_access_irq_sp(struct db_variable *vp, db_expr_t *valp, int rw) { if (rw == DB_VAR_GET) { *valp = get_stackptr(PSR_IRQ32_MODE); return (1); } return (0); } int db_frame(struct db_variable *vp, db_expr_t *valp, int rw) { int *reg; if (kdb_frame == NULL) return (0); reg = (int *)((uintptr_t)kdb_frame + (db_expr_t)vp->valuep); if (rw == DB_VAR_GET) *valp = *reg; else *reg = *valp; return (1); } void db_show_mdpcpu(struct pcpu *pc) { #if __ARM_ARCH >= 6 db_printf("curpmap = %p\n", pc->pc_curpmap); #endif } int db_validate_address(vm_offset_t addr) { struct proc *p = curproc; struct pmap *pmap; if (!p || !p->p_vmspace || !p->p_vmspace->vm_map.pmap || #ifndef ARM32_NEW_VM_LAYOUT addr >= VM_MAXUSER_ADDRESS #else addr >= VM_MIN_KERNEL_ADDRESS #endif ) pmap = kernel_pmap; else pmap = p->p_vmspace->vm_map.pmap; return (pmap_extract(pmap, addr) == FALSE); } /* * Read bytes from kernel address space for debugger. */ int db_read_bytes(addr, size, data) vm_offset_t addr; size_t size; char *data; { char *src = (char *)addr; if (db_validate_address((u_int)src)) { db_printf("address %p is invalid\n", src); return (-1); } if (size == 4 && (addr & 3) == 0 && ((uintptr_t)data & 3) == 0) { *((int*)data) = *((int*)src); return (0); } if (size == 2 && (addr & 1) == 0 && ((uintptr_t)data & 1) == 0) { *((short*)data) = *((short*)src); return (0); } while (size-- > 0) { if (db_validate_address((u_int)src)) { db_printf("address %p is invalid\n", src); return (-1); } *data++ = *src++; } return (0); } /* * Write bytes to kernel address space for debugger. */ int db_write_bytes(vm_offset_t addr, size_t size, char *data) { char *dst; size_t loop; dst = (char *)addr; if (db_validate_address((u_int)dst)) { db_printf("address %p is invalid\n", dst); return (0); } if (size == 4 && (addr & 3) == 0 && ((uintptr_t)data & 3) == 0) *((int*)dst) = *((int*)data); else if (size == 2 && (addr & 1) == 0 && ((uintptr_t)data & 1) == 0) *((short*)dst) = *((short*)data); else { loop = size; while (loop-- > 0) { if (db_validate_address((u_int)dst)) { db_printf("address %p is invalid\n", dst); return (-1); } *dst++ = *data++; } } /* make sure the caches and memory are in sync */ icache_sync(addr, size); /* In case the current page tables have been modified ... */ tlb_flush_all(); return (0); } static u_int db_fetch_reg(int reg) { switch (reg) { case 0: return (kdb_frame->tf_r0); case 1: return (kdb_frame->tf_r1); case 2: return (kdb_frame->tf_r2); case 3: return (kdb_frame->tf_r3); case 4: return (kdb_frame->tf_r4); case 5: return (kdb_frame->tf_r5); case 6: return (kdb_frame->tf_r6); case 7: return (kdb_frame->tf_r7); case 8: return (kdb_frame->tf_r8); case 9: return (kdb_frame->tf_r9); case 10: return (kdb_frame->tf_r10); case 11: return (kdb_frame->tf_r11); case 12: return (kdb_frame->tf_r12); case 13: return (kdb_frame->tf_svc_sp); case 14: return (kdb_frame->tf_svc_lr); case 15: return (kdb_frame->tf_pc); default: panic("db_fetch_reg: botch"); } } static u_int db_branch_taken_read_int(void *cookie __unused, vm_offset_t offset, u_int *val) { u_int ret; db_read_bytes(offset, 4, (char *)&ret); *val = ret; return (0); } static u_int db_branch_taken_fetch_reg(void *cookie __unused, int reg) { return (db_fetch_reg(reg)); } u_int branch_taken(u_int insn, db_addr_t pc) { register_t new_pc; int ret; ret = arm_predict_branch(NULL, insn, (register_t)pc, &new_pc, db_branch_taken_fetch_reg, db_branch_taken_read_int); if (ret != 0) kdb_reenter(); return (new_pc); } Index: head/sys/arm/at91/at91_pmc.c =================================================================== --- head/sys/arm/at91/at91_pmc.c (revision 298351) +++ head/sys/arm/at91/at91_pmc.c (revision 298352) @@ -1,720 +1,720 @@ /*- * Copyright (c) 2006 M. Warner Losh. All rights reserved. * Copyright (c) 2010 Greg Ansley. 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 AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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 "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef FDT #include #include #include #endif static struct at91_pmc_softc { bus_space_tag_t sc_st; bus_space_handle_t sc_sh; struct resource *mem_res; /* Memory resource */ device_t dev; } *pmc_softc; static uint32_t pllb_init; MALLOC_DECLARE(M_PMC); MALLOC_DEFINE(M_PMC, "at91_pmc_clocks", "AT91 PMC Clock descriptors"); #define AT91_PMC_BASE 0xffffc00 static void at91_pmc_set_pllb_mode(struct at91_pmc_clock *, int); static void at91_pmc_set_upll_mode(struct at91_pmc_clock *, int); static void at91_pmc_set_sys_mode(struct at91_pmc_clock *, int); static void at91_pmc_set_periph_mode(struct at91_pmc_clock *, int); static void at91_pmc_clock_alias(const char *name, const char *alias); static struct at91_pmc_clock slck = { .name = "slck", /* 32,768 Hz slow clock */ .hz = 32768, .refcnt = 1, .id = 0, .primary = 1, }; /* * NOTE: Clocks for "ordinary peripheral" devices e.g. spi0, udp0, uhp0 etc. * are now created automatically. Only "system" clocks need be defined here. */ static struct at91_pmc_clock main_ck = { .name = "main", /* Main clock */ .refcnt = 0, .id = 1, .primary = 1, .pmc_mask = PMC_IER_MOSCS, }; static struct at91_pmc_clock plla = { .name = "plla", /* PLLA Clock, used for CPU clocking */ .parent = &main_ck, .refcnt = 1, .id = 0, .primary = 1, .pll = 1, .pmc_mask = PMC_IER_LOCKA, }; static struct at91_pmc_clock pllb = { .name = "pllb", /* PLLB Clock, used for USB functions */ .parent = &main_ck, .refcnt = 0, .id = 0, .primary = 1, .pll = 1, .pmc_mask = PMC_IER_LOCKB, .set_mode = &at91_pmc_set_pllb_mode, }; /* Used by USB on at91sam9g45 */ static struct at91_pmc_clock upll = { .name = "upll", /* UTMI PLL, used for USB functions on 9G45 family */ .parent = &main_ck, .refcnt = 0, .id = 0, .primary = 1, .pll = 1, .pmc_mask = (1 << 6), .set_mode = &at91_pmc_set_upll_mode, }; static struct at91_pmc_clock udpck = { .name = "udpck", .parent = &pllb, .pmc_mask = PMC_SCER_UDP, .set_mode = at91_pmc_set_sys_mode }; static struct at91_pmc_clock uhpck = { .name = "uhpck", .parent = &pllb, .pmc_mask = PMC_SCER_UHP, .set_mode = at91_pmc_set_sys_mode }; static struct at91_pmc_clock mck = { .name = "mck", /* Master (Peripheral) Clock */ .pmc_mask = PMC_IER_MCKRDY, .refcnt = 0, }; static struct at91_pmc_clock cpu = { .name = "cpu", /* CPU Clock */ .parent = &plla, .pmc_mask = PMC_SCER_PCK, .refcnt = 0, }; /* "+32" or the automatic peripheral clocks */ static struct at91_pmc_clock *clock_list[16+32] = { &slck, &main_ck, &plla, &pllb, &upll, &udpck, &uhpck, &mck, &cpu }; static inline uint32_t RD4(struct at91_pmc_softc *sc, bus_size_t off) { if (sc == NULL) { uint32_t *p = (uint32_t *)(AT91_BASE + AT91_PMC_BASE + off); return *p; } return (bus_read_4(sc->mem_res, off)); } static inline void WR4(struct at91_pmc_softc *sc, bus_size_t off, uint32_t val) { if (sc == NULL) { uint32_t *p = (uint32_t *)(AT91_BASE + AT91_PMC_BASE + off); *p = val; } else bus_write_4(sc->mem_res, off, val); } /* * The following is unused currently since we don't ever set the PLLA * frequency of the device. If we did, we'd have to also pay attention * to the ICPLLA bit in the PMC_PLLICPR register for frequencies lower * than ~600MHz, which the PMC code doesn't do right now. */ uint32_t at91_pmc_800mhz_plla_outb(int freq) { uint32_t outa; /* * Set OUTA, per the data sheet. See Table 46-16 titled * PLLA Frequency Regarding ICPLLA and OUTA in the SAM9X25 doc, * Table 46-17 in the SAM9G20 doc, or Table 46-16 in the SAM9G45 doc. * Note: the frequencies overlap by 5MHz, so we add 3 here to * center shoot the transition. */ freq /= 1000000; /* MHz */ if (freq >= 800) freq = 800; freq += 3; /* Allow for overlap. */ outa = 3 - ((freq / 50) & 3); /* 750 / 50 = 7, see table */ return (1 << 29)| (outa << 14); } uint32_t at91_pmc_800mhz_pllb_outb(int freq) { return (0); } void at91_pmc_set_pllb_mode(struct at91_pmc_clock *clk, int on) { struct at91_pmc_softc *sc = pmc_softc; uint32_t value; value = on ? pllb_init : 0; /* * Only write to the register if the value is changing. Besides being * good common sense, this works around RM9200 Errata #26 (CKGR_PLL[AB]R * must not be written with the same value currently in the register). */ if (RD4(sc, CKGR_PLLBR) != value) { WR4(sc, CKGR_PLLBR, value); while (on && (RD4(sc, PMC_SR) & PMC_IER_LOCKB) != PMC_IER_LOCKB) continue; } } static void at91_pmc_set_upll_mode(struct at91_pmc_clock *clk, int on) { struct at91_pmc_softc *sc = pmc_softc; uint32_t value; if (on) { on = PMC_IER_LOCKU; value = CKGR_UCKR_UPLLEN | CKGR_UCKR_BIASEN; } else value = 0; WR4(sc, CKGR_UCKR, RD4(sc, CKGR_UCKR) | value); while ((RD4(sc, PMC_SR) & PMC_IER_LOCKU) != on) continue; WR4(sc, PMC_USB, PMC_USB_USBDIV(9) | PMC_USB_USBS); WR4(sc, PMC_SCER, PMC_SCER_UHP_SAM9); } static void at91_pmc_set_sys_mode(struct at91_pmc_clock *clk, int on) { struct at91_pmc_softc *sc = pmc_softc; WR4(sc, on ? PMC_SCER : PMC_SCDR, clk->pmc_mask); if (on) while ((RD4(sc, PMC_SCSR) & clk->pmc_mask) != clk->pmc_mask) continue; else while ((RD4(sc, PMC_SCSR) & clk->pmc_mask) == clk->pmc_mask) continue; } static void at91_pmc_set_periph_mode(struct at91_pmc_clock *clk, int on) { struct at91_pmc_softc *sc = pmc_softc; WR4(sc, on ? PMC_PCER : PMC_PCDR, clk->pmc_mask); if (on) while ((RD4(sc, PMC_PCSR) & clk->pmc_mask) != clk->pmc_mask) continue; else while ((RD4(sc, PMC_PCSR) & clk->pmc_mask) == clk->pmc_mask) continue; } struct at91_pmc_clock * at91_pmc_clock_add(const char *name, uint32_t irq, struct at91_pmc_clock *parent) { struct at91_pmc_clock *clk; int i, buflen; clk = malloc(sizeof(*clk), M_PMC, M_NOWAIT | M_ZERO); if (clk == NULL) goto err; buflen = strlen(name) + 1; clk->name = malloc(buflen, M_PMC, M_NOWAIT); if (clk->name == NULL) goto err; strlcpy(clk->name, name, buflen); clk->pmc_mask = 1 << irq; clk->set_mode = &at91_pmc_set_periph_mode; if (parent == NULL) clk->parent = &mck; else clk->parent = parent; - for (i = 0; i < sizeof(clock_list) / sizeof(clock_list[0]); i++) { + for (i = 0; i < nitems(clock_list); i++) { if (clock_list[i] == NULL) { clock_list[i] = clk; return (clk); } } err: if (clk != NULL) { if (clk->name != NULL) free(clk->name, M_PMC); free(clk, M_PMC); } panic("could not allocate pmc clock '%s'", name); return (NULL); } static void at91_pmc_clock_alias(const char *name, const char *alias) { struct at91_pmc_clock *clk, *alias_clk; clk = at91_pmc_clock_ref(name); if (clk) alias_clk = at91_pmc_clock_add(alias, 0, clk->parent); if (clk && alias_clk) { alias_clk->hz = clk->hz; alias_clk->pmc_mask = clk->pmc_mask; alias_clk->set_mode = clk->set_mode; } } struct at91_pmc_clock * at91_pmc_clock_ref(const char *name) { int i; - for (i = 0; i < sizeof(clock_list) / sizeof(clock_list[0]); i++) { + for (i = 0; i < nitems(clock_list); i++) { if (clock_list[i] == NULL) break; if (strcmp(name, clock_list[i]->name) == 0) return (clock_list[i]); } return (NULL); } void at91_pmc_clock_deref(struct at91_pmc_clock *clk) { if (clk == NULL) return; } void at91_pmc_clock_enable(struct at91_pmc_clock *clk) { if (clk == NULL) return; /* XXX LOCKING? XXX */ if (clk->parent) at91_pmc_clock_enable(clk->parent); if (clk->refcnt++ == 0 && clk->set_mode) clk->set_mode(clk, 1); } void at91_pmc_clock_disable(struct at91_pmc_clock *clk) { if (clk == NULL) return; /* XXX LOCKING? XXX */ if (--clk->refcnt == 0 && clk->set_mode) clk->set_mode(clk, 0); if (clk->parent) at91_pmc_clock_disable(clk->parent); } static int at91_pmc_pll_rate(struct at91_pmc_clock *clk, uint32_t reg) { uint32_t mul, div, freq; freq = clk->parent->hz; div = (reg >> clk->pll_div_shift) & clk->pll_div_mask; mul = (reg >> clk->pll_mul_shift) & clk->pll_mul_mask; #if 0 printf("pll = (%d / %d) * %d = %d\n", freq, div, mul + 1, (freq/div) * (mul+1)); #endif if (div != 0 && mul != 0) { freq /= div; freq *= mul + 1; } else freq = 0; clk->hz = freq; return (freq); } static uint32_t at91_pmc_pll_calc(struct at91_pmc_clock *clk, uint32_t out_freq) { uint32_t i, div = 0, mul = 0, diff = 1 << 30; unsigned ret = 0x3e00; if (out_freq > clk->pll_max_out) goto fail; for (i = 1; i < 256; i++) { int32_t diff1; uint32_t input, mul1; input = clk->parent->hz / i; if (input < clk->pll_min_in) break; if (input > clk->pll_max_in) continue; mul1 = out_freq / input; if (mul1 > (clk->pll_mul_mask + 1)) continue; if (mul1 == 0) break; diff1 = out_freq - input * mul1; if (diff1 < 0) diff1 = -diff1; if (diff > diff1) { diff = diff1; div = i; mul = mul1; if (diff == 0) break; } } if (diff > (out_freq >> PMC_PLL_SHIFT_TOL)) goto fail; if (clk->set_outb != NULL) ret |= clk->set_outb(out_freq); return (ret | ((mul - 1) << clk->pll_mul_shift) | (div << clk->pll_div_shift)); fail: return (0); } #if !defined(AT91C_MAIN_CLOCK) static const unsigned int at91_main_clock_tbl[] = { 3000000, 3276800, 3686400, 3840000, 4000000, 4433619, 4915200, 5000000, 5242880, 6000000, 6144000, 6400000, 6553600, 7159090, 7372800, 7864320, 8000000, 9830400, 10000000, 11059200, 12000000, 12288000, 13560000, 14318180, 14745600, 16000000, 17344700, 18432000, 20000000 }; -#define MAIN_CLOCK_TBL_LEN (sizeof(at91_main_clock_tbl) / sizeof(*at91_main_clock_tbl)) +#define MAIN_CLOCK_TBL_LEN nitems(at91_main_clock_tbl) #endif static unsigned int at91_pmc_sense_main_clock(void) { #if !defined(AT91C_MAIN_CLOCK) unsigned int ckgr_val; unsigned int diff, matchdiff, freq; int i; ckgr_val = (RD4(NULL, CKGR_MCFR) & CKGR_MCFR_MAINF_MASK) << 11; /* * Clocks up to 50MHz can be connected to some models. If * the frequency is >= 21MHz, assume that the slow clock can * measure it correctly, and that any error can be adequately * compensated for by roudning to the nearest 500Hz. Users * with fast, or odd-ball clocks will need to set * AT91C_MAIN_CLOCK in the kernel config file. */ if (ckgr_val >= 21000000) return ((ckgr_val + 250) / 500 * 500); /* * Try to find the standard frequency that match best. */ freq = at91_main_clock_tbl[0]; matchdiff = abs(ckgr_val - at91_main_clock_tbl[0]); for (i = 1; i < MAIN_CLOCK_TBL_LEN; i++) { diff = abs(ckgr_val - at91_main_clock_tbl[i]); if (diff < matchdiff) { freq = at91_main_clock_tbl[i]; matchdiff = diff; } } return (freq); #else return (AT91C_MAIN_CLOCK); #endif } void at91_pmc_init_clock(void) { struct at91_pmc_softc *sc = NULL; unsigned int main_clock; uint32_t mckr; uint32_t mdiv; soc_info.soc_data->soc_clock_init(); main_clock = at91_pmc_sense_main_clock(); if (at91_is_sam9() || at91_is_sam9xe()) { uhpck.pmc_mask = PMC_SCER_UHP_SAM9; udpck.pmc_mask = PMC_SCER_UDP_SAM9; } /* There is no pllb on AT91SAM9G45 */ if (at91_cpu_is(AT91_T_SAM9G45)) { uhpck.parent = &upll; uhpck.pmc_mask = PMC_SCER_UHP_SAM9; } mckr = RD4(sc, PMC_MCKR); main_ck.hz = main_clock; /* * Note: this means outa calc code for plla never used since * we never change it. If we did, we'd also have to mind * ICPLLA to get the charge pump current right. */ at91_pmc_pll_rate(&plla, RD4(sc, CKGR_PLLAR)); if (at91_cpu_is(AT91_T_SAM9G45) && (mckr & PMC_MCKR_PLLADIV2)) plla.hz /= 2; /* * Initialize the usb clock. This sets up pllb, but disables the * actual clock. XXX except for the if 0 :( */ if (!at91_cpu_is(AT91_T_SAM9G45)) { pllb_init = at91_pmc_pll_calc(&pllb, 48000000 * 2) | 0x10000000; at91_pmc_pll_rate(&pllb, pllb_init); #if 0 /* Turn off USB clocks */ at91_pmc_set_periph_mode(&ohci_clk, 0); at91_pmc_set_periph_mode(&udc_clk, 0); #endif } if (at91_is_rm92()) { WR4(sc, PMC_SCDR, PMC_SCER_UHP | PMC_SCER_UDP); WR4(sc, PMC_SCER, PMC_SCER_MCKUDP); } else WR4(sc, PMC_SCDR, PMC_SCER_UHP_SAM9 | PMC_SCER_UDP_SAM9); /* * MCK and PCU derive from one of the primary clocks. Initialize * this relationship. */ mck.parent = clock_list[mckr & 0x3]; mck.parent->refcnt++; cpu.hz = mck.hz = mck.parent->hz / (1 << ((mckr & PMC_MCKR_PRES_MASK) >> 2)); mdiv = (mckr & PMC_MCKR_MDIV_MASK) >> 8; if (at91_is_sam9() || at91_is_sam9xe()) { /* * On AT91SAM9G45 when mdiv == 3 we need to divide * MCK by 3 but not, for example, on 9g20. */ if (!at91_cpu_is(AT91_T_SAM9G45) || mdiv <= 2) mdiv *= 2; if (mdiv > 0) mck.hz /= mdiv; } else mck.hz /= (1 + mdiv); /* Only found on SAM9G20 */ if (at91_cpu_is(AT91_T_SAM9G20)) cpu.hz /= (mckr & PMC_MCKR_PDIV) ? 2 : 1; at91_master_clock = mck.hz; /* These clocks refrenced by "special" names */ at91_pmc_clock_alias("ohci0", "ohci_clk"); at91_pmc_clock_alias("udp0", "udp_clk"); /* Turn off "Progamable" clocks */ WR4(sc, PMC_SCDR, PMC_SCER_PCK0 | PMC_SCER_PCK1 | PMC_SCER_PCK2 | PMC_SCER_PCK3); /* XXX kludge, turn on all peripherals */ WR4(sc, PMC_PCER, 0xffffffff); /* Disable all interrupts for PMC */ WR4(sc, PMC_IDR, 0xffffffff); } static void at91_pmc_deactivate(device_t dev) { struct at91_pmc_softc *sc; sc = device_get_softc(dev); bus_generic_detach(sc->dev); if (sc->mem_res) bus_release_resource(dev, SYS_RES_IOPORT, rman_get_rid(sc->mem_res), sc->mem_res); sc->mem_res = NULL; } static int at91_pmc_activate(device_t dev) { struct at91_pmc_softc *sc; int rid; sc = device_get_softc(dev); rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) goto errout; return (0); errout: at91_pmc_deactivate(dev); return (ENOMEM); } static int at91_pmc_probe(device_t dev) { #ifdef FDT if (!ofw_bus_is_compatible(dev, "atmel,at91rm9200-pmc") && !ofw_bus_is_compatible(dev, "atmel,at91sam9260-pmc") && !ofw_bus_is_compatible(dev, "atmel,at91sam9g45-pmc") && !ofw_bus_is_compatible(dev, "atmel,at91sam9x5-pmc")) return (ENXIO); #endif device_set_desc(dev, "PMC"); return (0); } static int at91_pmc_attach(device_t dev) { int err; pmc_softc = device_get_softc(dev); pmc_softc->dev = dev; if ((err = at91_pmc_activate(dev)) != 0) return (err); /* * Configure main clock frequency. */ at91_pmc_init_clock(); /* * Display info about clocks previously computed */ device_printf(dev, "Primary: %d Hz PLLA: %d MHz CPU: %d MHz MCK: %d MHz\n", main_ck.hz, plla.hz / 1000000, cpu.hz / 1000000, mck.hz / 1000000); return (0); } static device_method_t at91_pmc_methods[] = { DEVMETHOD(device_probe, at91_pmc_probe), DEVMETHOD(device_attach, at91_pmc_attach), DEVMETHOD_END }; static driver_t at91_pmc_driver = { "at91_pmc", at91_pmc_methods, sizeof(struct at91_pmc_softc), }; static devclass_t at91_pmc_devclass; #ifdef FDT EARLY_DRIVER_MODULE(at91_pmc, simplebus, at91_pmc_driver, at91_pmc_devclass, NULL, NULL, BUS_PASS_CPU); #else EARLY_DRIVER_MODULE(at91_pmc, atmelarm, at91_pmc_driver, at91_pmc_devclass, NULL, NULL, BUS_PASS_CPU); #endif Index: head/sys/arm/mv/armadaxp/armadaxp.c =================================================================== --- head/sys/arm/mv/armadaxp/armadaxp.c (revision 298351) +++ head/sys/arm/mv/armadaxp/armadaxp.c (revision 298352) @@ -1,300 +1,300 @@ /*- * Copyright (c) 2011 Semihalf. * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. * * From: FreeBSD: src/sys/arm/mv/kirkwood/sheevaplug.c,v 1.2 2010/06/13 13:28:53 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #define CPU_FREQ_FIELD(sar) (((0x01 & (sar >> 52)) << 3) | \ (0x07 & (sar >> 21))) #define FAB_FREQ_FIELD(sar) (((0x01 & (sar >> 51)) << 4) | \ (0x0F & (sar >> 24))) static uint32_t count_l2clk(void); void armadaxp_l2_init(void); void armadaxp_init_coher_fabric(void); int platform_get_ncpus(void); #define ARMADAXP_L2_BASE (MV_BASE + 0x8000) #define ARMADAXP_L2_CTRL 0x100 #define L2_ENABLE (1 << 0) #define ARMADAXP_L2_AUX_CTRL 0x104 #define L2_WBWT_MODE_MASK (3 << 0) #define L2_WBWT_MODE_PAGE 0 #define L2_WBWT_MODE_WB 1 #define L2_WBWT_MODE_WT 2 #define L2_REP_STRAT_MASK (3 << 27) #define L2_REP_STRAT_LSFR (1 << 27) #define L2_REP_STRAT_SEMIPLRU (3 << 27) #define ARMADAXP_L2_CNTR_CTRL 0x200 #define ARMADAXP_L2_CNTR_CONF(x) (0x204 + (x) * 0xc) #define ARMADAXP_L2_CNTR2_VAL_LOW (0x208 + (x) * 0xc) #define ARMADAXP_L2_CNTR2_VAL_HI (0x20c + (x) * 0xc) #define ARMADAXP_L2_INT_CAUSE 0x220 #define ARMADAXP_L2_SYNC_BARRIER 0x700 #define ARMADAXP_L2_INV_WAY 0x778 #define ARMADAXP_L2_CLEAN_WAY 0x7BC #define ARMADAXP_L2_FLUSH_PHYS 0x7F0 #define ARMADAXP_L2_FLUSH_WAY 0x7FC #define MV_COHERENCY_FABRIC_BASE (MV_MBUS_BRIDGE_BASE + 0x200) #define COHER_FABRIC_CTRL 0x00 #define COHER_FABRIC_CONF 0x04 #define COHER_FABRIC_CFU 0x28 #define COHER_FABRIC_CIB_CTRL 0x80 struct vco_freq_ratio { uint8_t vco_cpu; /* VCO to CLK0(CPU) clock ratio */ uint8_t vco_l2c; /* VCO to NB(L2 cache) clock ratio */ uint8_t vco_hcl; /* VCO to HCLK(DDR controller) clock ratio */ uint8_t vco_ddr; /* VCO to DR(DDR memory) clock ratio */ }; static struct vco_freq_ratio freq_conf_table[] = { /*00*/ { 1, 1, 4, 2 }, /*01*/ { 1, 2, 2, 2 }, /*02*/ { 2, 2, 6, 3 }, /*03*/ { 2, 2, 3, 3 }, /*04*/ { 1, 2, 3, 3 }, /*05*/ { 1, 2, 4, 2 }, /*06*/ { 1, 1, 2, 2 }, /*07*/ { 2, 3, 6, 6 }, /*08*/ { 2, 3, 5, 5 }, /*09*/ { 1, 2, 6, 3 }, /*10*/ { 2, 4, 10, 5 }, /*11*/ { 1, 3, 6, 6 }, /*12*/ { 1, 2, 5, 5 }, /*13*/ { 1, 3, 6, 3 }, /*14*/ { 1, 2, 5, 5 }, /*15*/ { 2, 2, 5, 5 }, /*16*/ { 1, 1, 3, 3 }, /*17*/ { 2, 5, 10, 10 }, /*18*/ { 1, 3, 8, 4 }, /*19*/ { 1, 1, 2, 1 }, /*20*/ { 2, 3, 6, 3 }, /*21*/ { 1, 2, 8, 4 }, /*22*/ { 2, 5, 10, 5 } }; static uint16_t cpu_clock_table[] = { 1000, 1066, 1200, 1333, 1500, 1666, 1800, 2000, 600, 667, 800, 1600, 2133, 2200, 2400 }; uint32_t get_tclk(void) { uint32_t cputype; cputype = cpu_ident(); cputype &= CPU_ID_CPU_MASK; if (cputype == CPU_ID_MV88SV584X_V7) return (TCLK_250MHZ); else return (TCLK_200MHZ); } static uint32_t count_l2clk(void) { uint64_t sar_reg; uint32_t freq_vco, freq_l2clk; uint8_t sar_cpu_freq, sar_fab_freq, array_size; /* Get value of the SAR register and process it */ sar_reg = get_sar_value(); sar_cpu_freq = CPU_FREQ_FIELD(sar_reg); sar_fab_freq = FAB_FREQ_FIELD(sar_reg); /* Check if CPU frequency field has correct value */ - array_size = sizeof(cpu_clock_table) / sizeof(cpu_clock_table[0]); + array_size = nitems(cpu_clock_table); if (sar_cpu_freq >= array_size) panic("Reserved value in cpu frequency configuration field: " "%d", sar_cpu_freq); /* Check if fabric frequency field has correct value */ - array_size = sizeof(freq_conf_table) / sizeof(freq_conf_table[0]); + array_size = nitems(freq_conf_table); if (sar_fab_freq >= array_size) panic("Reserved value in fabric frequency configuration field: " "%d", sar_fab_freq); /* Get CPU clock frequency */ freq_vco = cpu_clock_table[sar_cpu_freq] * freq_conf_table[sar_fab_freq].vco_cpu; /* Get L2CLK clock frequency */ freq_l2clk = freq_vco / freq_conf_table[sar_fab_freq].vco_l2c; /* Round L2CLK value to integer MHz */ if (((freq_vco % freq_conf_table[sar_fab_freq].vco_l2c) * 10 / freq_conf_table[sar_fab_freq].vco_l2c) >= 5) freq_l2clk++; return (freq_l2clk * 1000000); } uint32_t get_l2clk(void) { static uint32_t l2clk_freq = 0; /* If get_l2clk is called first time get L2CLK value from register */ if (l2clk_freq == 0) l2clk_freq = count_l2clk(); return (l2clk_freq); } static uint32_t read_coher_fabric(uint32_t reg) { return (bus_space_read_4(fdtbus_bs_tag, MV_COHERENCY_FABRIC_BASE, reg)); } static void write_coher_fabric(uint32_t reg, uint32_t val) { bus_space_write_4(fdtbus_bs_tag, MV_COHERENCY_FABRIC_BASE, reg, val); } int platform_get_ncpus(void) { #if !defined(SMP) return (1); #else return ((read_coher_fabric(COHER_FABRIC_CONF) & 0xf) + 1); #endif } void armadaxp_init_coher_fabric(void) { uint32_t val, cpus, mask; cpus = platform_get_ncpus(); mask = (1 << cpus) - 1; val = read_coher_fabric(COHER_FABRIC_CTRL); val |= (mask << 24); write_coher_fabric(COHER_FABRIC_CTRL, val); val = read_coher_fabric(COHER_FABRIC_CONF); val |= (mask << 24); val |= (1 << 15); write_coher_fabric(COHER_FABRIC_CONF, val); } #define ALL_WAYS 0xffffffff /* L2 cache configuration registers */ static uint32_t read_l2_cache(uint32_t reg) { return (bus_space_read_4(fdtbus_bs_tag, ARMADAXP_L2_BASE, reg)); } static void write_l2_cache(uint32_t reg, uint32_t val) { bus_space_write_4(fdtbus_bs_tag, ARMADAXP_L2_BASE, reg, val); } static void armadaxp_l2_idcache_inv_all(void) { write_l2_cache(ARMADAXP_L2_INV_WAY, ALL_WAYS); } void armadaxp_l2_init(void) { u_int32_t reg; /* Set L2 policy */ reg = read_l2_cache(ARMADAXP_L2_AUX_CTRL); reg &= ~(L2_WBWT_MODE_MASK); reg &= ~(L2_REP_STRAT_MASK); reg |= L2_REP_STRAT_SEMIPLRU; reg |= L2_WBWT_MODE_WT; write_l2_cache(ARMADAXP_L2_AUX_CTRL, reg); /* Invalidate l2 cache */ armadaxp_l2_idcache_inv_all(); /* Clear pending L2 interrupts */ write_l2_cache(ARMADAXP_L2_INT_CAUSE, 0x1ff); /* Enable l2 cache */ reg = read_l2_cache(ARMADAXP_L2_CTRL); write_l2_cache(ARMADAXP_L2_CTRL, reg | L2_ENABLE); /* * For debug purposes * Configure and enable counter */ write_l2_cache(ARMADAXP_L2_CNTR_CONF(0), 0xf0000 | (4 << 2)); write_l2_cache(ARMADAXP_L2_CNTR_CONF(1), 0xf0000 | (2 << 2)); write_l2_cache(ARMADAXP_L2_CNTR_CTRL, 0x303); /* * Enable Cache maintenance operation propagation in coherency fabric * Change point of coherency and point of unification to DRAM. */ reg = read_coher_fabric(COHER_FABRIC_CFU); reg |= (1 << 17) | (1 << 18); write_coher_fabric(COHER_FABRIC_CFU, reg); /* Coherent IO Bridge initialization */ reg = read_coher_fabric(COHER_FABRIC_CIB_CTRL); reg &= ~(7 << 16); reg |= (7 << 16); write_coher_fabric(COHER_FABRIC_CIB_CTRL, reg); } Index: head/sys/arm/ti/cpsw/if_cpsw.c =================================================================== --- head/sys/arm/ti/cpsw/if_cpsw.c (revision 298351) +++ head/sys/arm/ti/cpsw/if_cpsw.c (revision 298352) @@ -1,2604 +1,2604 @@ /*- * Copyright (c) 2012 Damjan Marion * Copyright (c) 2016 Rubicon Communications, LLC (Netgate) * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. */ /* * TI Common Platform Ethernet Switch (CPSW) Driver * Found in TI8148 "DaVinci" and AM335x "Sitara" SoCs. * * This controller is documented in the AM335x Technical Reference * Manual, in the TMS320DM814x DaVinci Digital Video Processors TRM * and in the TMS320C6452 3 Port Switch Ethernet Subsystem TRM. * * It is basically a single Ethernet port (port 0) wired internally to * a 3-port store-and-forward switch connected to two independent * "sliver" controllers (port 1 and port 2). You can operate the * controller in a variety of different ways by suitably configuring * the slivers and the Address Lookup Engine (ALE) that routes packets * between the ports. * * This code was developed and tested on a BeagleBone with * an AM335x SoC. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "if_cpswreg.h" #include "if_cpswvar.h" #include #include "miibus_if.h" /* Device probe/attach/detach. */ static int cpsw_probe(device_t); static int cpsw_attach(device_t); static int cpsw_detach(device_t); static int cpswp_probe(device_t); static int cpswp_attach(device_t); static int cpswp_detach(device_t); static phandle_t cpsw_get_node(device_t, device_t); /* Device Init/shutdown. */ static int cpsw_shutdown(device_t); static void cpswp_init(void *); static void cpswp_init_locked(void *); static void cpswp_stop_locked(struct cpswp_softc *); /* Device Suspend/Resume. */ static int cpsw_suspend(device_t); static int cpsw_resume(device_t); /* Ioctl. */ static int cpswp_ioctl(struct ifnet *, u_long command, caddr_t data); static int cpswp_miibus_readreg(device_t, int phy, int reg); static int cpswp_miibus_writereg(device_t, int phy, int reg, int value); static void cpswp_miibus_statchg(device_t); /* Send/Receive packets. */ static void cpsw_intr_rx(void *arg); static struct mbuf *cpsw_rx_dequeue(struct cpsw_softc *); static void cpsw_rx_enqueue(struct cpsw_softc *); static void cpswp_start(struct ifnet *); static void cpswp_tx_enqueue(struct cpswp_softc *); static int cpsw_tx_dequeue(struct cpsw_softc *); /* Misc interrupts and watchdog. */ static void cpsw_intr_rx_thresh(void *); static void cpsw_intr_misc(void *); static void cpswp_tick(void *); static void cpswp_ifmedia_sts(struct ifnet *, struct ifmediareq *); static int cpswp_ifmedia_upd(struct ifnet *); static void cpsw_tx_watchdog(void *); /* ALE support */ static void cpsw_ale_read_entry(struct cpsw_softc *, uint16_t, uint32_t *); static void cpsw_ale_write_entry(struct cpsw_softc *, uint16_t, uint32_t *); static int cpsw_ale_mc_entry_set(struct cpsw_softc *, uint8_t, int, uint8_t *); static void cpsw_ale_dump_table(struct cpsw_softc *); static int cpsw_ale_update_vlan_table(struct cpsw_softc *, int, int, int, int, int); static int cpswp_ale_update_addresses(struct cpswp_softc *, int); /* Statistics and sysctls. */ static void cpsw_add_sysctls(struct cpsw_softc *); static void cpsw_stats_collect(struct cpsw_softc *); static int cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS); /* * Arbitrary limit on number of segments in an mbuf to be transmitted. * Packets with more segments than this will be defragmented before * they are queued. */ #define CPSW_TXFRAGS 16 /* Shared resources. */ static device_method_t cpsw_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cpsw_probe), DEVMETHOD(device_attach, cpsw_attach), DEVMETHOD(device_detach, cpsw_detach), DEVMETHOD(device_shutdown, cpsw_shutdown), DEVMETHOD(device_suspend, cpsw_suspend), DEVMETHOD(device_resume, cpsw_resume), /* OFW methods */ DEVMETHOD(ofw_bus_get_node, cpsw_get_node), DEVMETHOD_END }; static driver_t cpsw_driver = { "cpswss", cpsw_methods, sizeof(struct cpsw_softc), }; static devclass_t cpsw_devclass; DRIVER_MODULE(cpswss, simplebus, cpsw_driver, cpsw_devclass, 0, 0); /* Port/Slave resources. */ static device_method_t cpswp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cpswp_probe), DEVMETHOD(device_attach, cpswp_attach), DEVMETHOD(device_detach, cpswp_detach), /* MII interface */ DEVMETHOD(miibus_readreg, cpswp_miibus_readreg), DEVMETHOD(miibus_writereg, cpswp_miibus_writereg), DEVMETHOD(miibus_statchg, cpswp_miibus_statchg), DEVMETHOD_END }; static driver_t cpswp_driver = { "cpsw", cpswp_methods, sizeof(struct cpswp_softc), }; static devclass_t cpswp_devclass; DRIVER_MODULE(cpsw, cpswss, cpswp_driver, cpswp_devclass, 0, 0); DRIVER_MODULE(miibus, cpsw, miibus_driver, miibus_devclass, 0, 0); MODULE_DEPEND(cpsw, ether, 1, 1, 1); MODULE_DEPEND(cpsw, miibus, 1, 1, 1); static uint32_t slave_mdio_addr[] = { 0x4a100200, 0x4a100300 }; static struct resource_spec irq_res_spec[] = { { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { SYS_RES_IRQ, 1, RF_ACTIVE | RF_SHAREABLE }, { SYS_RES_IRQ, 2, RF_ACTIVE | RF_SHAREABLE }, { SYS_RES_IRQ, 3, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; /* Number of entries here must match size of stats * array in struct cpswp_softc. */ static struct cpsw_stat { int reg; char *oid; } cpsw_stat_sysctls[CPSW_SYSCTL_COUNT] = { {0x00, "GoodRxFrames"}, {0x04, "BroadcastRxFrames"}, {0x08, "MulticastRxFrames"}, {0x0C, "PauseRxFrames"}, {0x10, "RxCrcErrors"}, {0x14, "RxAlignErrors"}, {0x18, "OversizeRxFrames"}, {0x1c, "RxJabbers"}, {0x20, "ShortRxFrames"}, {0x24, "RxFragments"}, {0x30, "RxOctets"}, {0x34, "GoodTxFrames"}, {0x38, "BroadcastTxFrames"}, {0x3c, "MulticastTxFrames"}, {0x40, "PauseTxFrames"}, {0x44, "DeferredTxFrames"}, {0x48, "CollisionsTxFrames"}, {0x4c, "SingleCollisionTxFrames"}, {0x50, "MultipleCollisionTxFrames"}, {0x54, "ExcessiveCollisions"}, {0x58, "LateCollisions"}, {0x5c, "TxUnderrun"}, {0x60, "CarrierSenseErrors"}, {0x64, "TxOctets"}, {0x68, "RxTx64OctetFrames"}, {0x6c, "RxTx65to127OctetFrames"}, {0x70, "RxTx128to255OctetFrames"}, {0x74, "RxTx256to511OctetFrames"}, {0x78, "RxTx512to1024OctetFrames"}, {0x7c, "RxTx1024upOctetFrames"}, {0x80, "NetOctets"}, {0x84, "RxStartOfFrameOverruns"}, {0x88, "RxMiddleOfFrameOverruns"}, {0x8c, "RxDmaOverruns"} }; /* * Basic debug support. */ #define IF_DEBUG(_sc) if ((_sc)->if_flags & IFF_DEBUG) static void cpsw_debugf_head(const char *funcname) { int t = (int)(time_second % (24 * 60 * 60)); printf("%02d:%02d:%02d %s ", t / (60 * 60), (t / 60) % 60, t % 60, funcname); } #include static void cpsw_debugf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf("\n"); } #define CPSW_DEBUGF(_sc, a) do { \ if (sc->debug) { \ cpsw_debugf_head(__func__); \ cpsw_debugf a; \ } \ } while (0) #define CPSWP_DEBUGF(_sc, a) do { \ IF_DEBUG((_sc)) { \ cpsw_debugf_head(__func__); \ cpsw_debugf a; \ } \ } while (0) /* * Locking macros */ #define CPSW_TX_LOCK(sc) do { \ mtx_assert(&(sc)->rx.lock, MA_NOTOWNED); \ mtx_lock(&(sc)->tx.lock); \ } while (0) #define CPSW_TX_UNLOCK(sc) mtx_unlock(&(sc)->tx.lock) #define CPSW_TX_LOCK_ASSERT(sc) mtx_assert(&(sc)->tx.lock, MA_OWNED) #define CPSW_RX_LOCK(sc) do { \ mtx_assert(&(sc)->tx.lock, MA_NOTOWNED); \ mtx_lock(&(sc)->rx.lock); \ } while (0) #define CPSW_RX_UNLOCK(sc) mtx_unlock(&(sc)->rx.lock) #define CPSW_RX_LOCK_ASSERT(sc) mtx_assert(&(sc)->rx.lock, MA_OWNED) #define CPSW_GLOBAL_LOCK(sc) do { \ if ((mtx_owned(&(sc)->tx.lock) ? 1 : 0) != \ (mtx_owned(&(sc)->rx.lock) ? 1 : 0)) { \ panic("cpsw deadlock possibility detection!"); \ } \ mtx_lock(&(sc)->tx.lock); \ mtx_lock(&(sc)->rx.lock); \ } while (0) #define CPSW_GLOBAL_UNLOCK(sc) do { \ CPSW_RX_UNLOCK(sc); \ CPSW_TX_UNLOCK(sc); \ } while (0) #define CPSW_GLOBAL_LOCK_ASSERT(sc) do { \ CPSW_TX_LOCK_ASSERT(sc); \ CPSW_RX_LOCK_ASSERT(sc); \ } while (0) #define CPSW_PORT_LOCK(_sc) do { \ mtx_assert(&(_sc)->lock, MA_NOTOWNED); \ mtx_lock(&(_sc)->lock); \ } while (0) #define CPSW_PORT_UNLOCK(_sc) mtx_unlock(&(_sc)->lock) #define CPSW_PORT_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->lock, MA_OWNED) /* * Read/Write macros */ #define cpsw_read_4(_sc, _reg) bus_read_4((_sc)->mem_res, (_reg)) #define cpsw_write_4(_sc, _reg, _val) \ bus_write_4((_sc)->mem_res, (_reg), (_val)) #define cpsw_cpdma_bd_offset(i) (CPSW_CPPI_RAM_OFFSET + ((i)*16)) #define cpsw_cpdma_bd_paddr(sc, slot) \ BUS_SPACE_PHYSADDR(sc->mem_res, slot->bd_offset) #define cpsw_cpdma_read_bd(sc, slot, val) \ bus_read_region_4(sc->mem_res, slot->bd_offset, (uint32_t *) val, 4) #define cpsw_cpdma_write_bd(sc, slot, val) \ bus_write_region_4(sc->mem_res, slot->bd_offset, (uint32_t *) val, 4) #define cpsw_cpdma_write_bd_next(sc, slot, next_slot) \ cpsw_write_4(sc, slot->bd_offset, cpsw_cpdma_bd_paddr(sc, next_slot)) #define cpsw_cpdma_read_bd_flags(sc, slot) \ bus_read_2(sc->mem_res, slot->bd_offset + 14) #define cpsw_write_hdp_slot(sc, queue, slot) \ cpsw_write_4(sc, (queue)->hdp_offset, cpsw_cpdma_bd_paddr(sc, slot)) #define CP_OFFSET (CPSW_CPDMA_TX_CP(0) - CPSW_CPDMA_TX_HDP(0)) #define cpsw_read_cp(sc, queue) \ cpsw_read_4(sc, (queue)->hdp_offset + CP_OFFSET) #define cpsw_write_cp(sc, queue, val) \ cpsw_write_4(sc, (queue)->hdp_offset + CP_OFFSET, (val)) #define cpsw_write_cp_slot(sc, queue, slot) \ cpsw_write_cp(sc, queue, cpsw_cpdma_bd_paddr(sc, slot)) #if 0 /* XXX temporary function versions for debugging. */ static void cpsw_write_hdp_slotX(struct cpsw_softc *sc, struct cpsw_queue *queue, struct cpsw_slot *slot) { uint32_t reg = queue->hdp_offset; uint32_t v = cpsw_cpdma_bd_paddr(sc, slot); CPSW_DEBUGF(("HDP <=== 0x%08x (was 0x%08x)", v, cpsw_read_4(sc, reg))); cpsw_write_4(sc, reg, v); } static void cpsw_write_cp_slotX(struct cpsw_softc *sc, struct cpsw_queue *queue, struct cpsw_slot *slot) { uint32_t v = cpsw_cpdma_bd_paddr(sc, slot); CPSW_DEBUGF(("CP <=== 0x%08x (expecting 0x%08x)", v, cpsw_read_cp(sc, queue))); cpsw_write_cp(sc, queue, v); } #endif /* * Expanded dump routines for verbose debugging. */ static void cpsw_dump_slot(struct cpsw_softc *sc, struct cpsw_slot *slot) { static const char *flags[] = {"SOP", "EOP", "Owner", "EOQ", "TDownCmplt", "PassCRC", "Long", "Short", "MacCtl", "Overrun", "PktErr1", "PortEn/PktErr0", "RxVlanEncap", "Port2", "Port1", "Port0"}; struct cpsw_cpdma_bd bd; const char *sep; int i; cpsw_cpdma_read_bd(sc, slot, &bd); printf("BD Addr: 0x%08x Next: 0x%08x\n", cpsw_cpdma_bd_paddr(sc, slot), bd.next); printf(" BufPtr: 0x%08x BufLen: 0x%08x\n", bd.bufptr, bd.buflen); printf(" BufOff: 0x%08x PktLen: 0x%08x\n", bd.bufoff, bd.pktlen); printf(" Flags: "); sep = ""; for (i = 0; i < 16; ++i) { if (bd.flags & (1 << (15 - i))) { printf("%s%s", sep, flags[i]); sep = ","; } } printf("\n"); if (slot->mbuf) { printf(" Ether: %14D\n", (char *)(slot->mbuf->m_data), " "); printf(" Packet: %16D\n", (char *)(slot->mbuf->m_data) + 14, " "); } } #define CPSW_DUMP_SLOT(cs, slot) do { \ IF_DEBUG(sc) { \ cpsw_dump_slot(sc, slot); \ } \ } while (0) static void cpsw_dump_queue(struct cpsw_softc *sc, struct cpsw_slots *q) { struct cpsw_slot *slot; int i = 0; int others = 0; STAILQ_FOREACH(slot, q, next) { if (i > 4) ++others; else cpsw_dump_slot(sc, slot); ++i; } if (others) printf(" ... and %d more.\n", others); printf("\n"); } #define CPSW_DUMP_QUEUE(sc, q) do { \ IF_DEBUG(sc) { \ cpsw_dump_queue(sc, q); \ } \ } while (0) static void cpsw_init_slots(struct cpsw_softc *sc) { struct cpsw_slot *slot; int i; STAILQ_INIT(&sc->avail); /* Put the slot descriptors onto the global avail list. */ - for (i = 0; i < sizeof(sc->_slots) / sizeof(sc->_slots[0]); i++) { + for (i = 0; i < nitems(sc->_slots); i++) { slot = &sc->_slots[i]; slot->bd_offset = cpsw_cpdma_bd_offset(i); STAILQ_INSERT_TAIL(&sc->avail, slot, next); } } static int cpsw_add_slots(struct cpsw_softc *sc, struct cpsw_queue *queue, int requested) { - const int max_slots = sizeof(sc->_slots) / sizeof(sc->_slots[0]); + const int max_slots = nitems(sc->_slots); struct cpsw_slot *slot; int i; if (requested < 0) requested = max_slots; for (i = 0; i < requested; ++i) { slot = STAILQ_FIRST(&sc->avail); if (slot == NULL) return (0); if (bus_dmamap_create(sc->mbuf_dtag, 0, &slot->dmamap)) { device_printf(sc->dev, "failed to create dmamap\n"); return (ENOMEM); } STAILQ_REMOVE_HEAD(&sc->avail, next); STAILQ_INSERT_TAIL(&queue->avail, slot, next); ++queue->avail_queue_len; ++queue->queue_slots; } return (0); } static void cpsw_free_slot(struct cpsw_softc *sc, struct cpsw_slot *slot) { int error; if (slot->dmamap) { if (slot->mbuf) bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); error = bus_dmamap_destroy(sc->mbuf_dtag, slot->dmamap); KASSERT(error == 0, ("Mapping still active")); slot->dmamap = NULL; } if (slot->mbuf) { m_freem(slot->mbuf); slot->mbuf = NULL; } } static void cpsw_reset(struct cpsw_softc *sc) { int i; callout_stop(&sc->watchdog.callout); /* Reset RMII/RGMII wrapper. */ cpsw_write_4(sc, CPSW_WR_SOFT_RESET, 1); while (cpsw_read_4(sc, CPSW_WR_SOFT_RESET) & 1) ; /* Disable TX and RX interrupts for all cores. */ for (i = 0; i < 3; ++i) { cpsw_write_4(sc, CPSW_WR_C_RX_THRESH_EN(i), 0x00); cpsw_write_4(sc, CPSW_WR_C_TX_EN(i), 0x00); cpsw_write_4(sc, CPSW_WR_C_RX_EN(i), 0x00); cpsw_write_4(sc, CPSW_WR_C_MISC_EN(i), 0x00); } /* Reset CPSW subsystem. */ cpsw_write_4(sc, CPSW_SS_SOFT_RESET, 1); while (cpsw_read_4(sc, CPSW_SS_SOFT_RESET) & 1) ; /* Reset Sliver port 1 and 2 */ for (i = 0; i < 2; i++) { /* Reset */ cpsw_write_4(sc, CPSW_SL_SOFT_RESET(i), 1); while (cpsw_read_4(sc, CPSW_SL_SOFT_RESET(i)) & 1) ; } /* Reset DMA controller. */ cpsw_write_4(sc, CPSW_CPDMA_SOFT_RESET, 1); while (cpsw_read_4(sc, CPSW_CPDMA_SOFT_RESET) & 1) ; /* Disable TX & RX DMA */ cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 0); cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 0); /* Clear all queues. */ for (i = 0; i < 8; i++) { cpsw_write_4(sc, CPSW_CPDMA_TX_HDP(i), 0); cpsw_write_4(sc, CPSW_CPDMA_RX_HDP(i), 0); cpsw_write_4(sc, CPSW_CPDMA_TX_CP(i), 0); cpsw_write_4(sc, CPSW_CPDMA_RX_CP(i), 0); } /* Clear all interrupt Masks */ cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_CLEAR, 0xFFFFFFFF); cpsw_write_4(sc, CPSW_CPDMA_TX_INTMASK_CLEAR, 0xFFFFFFFF); } static void cpsw_init(struct cpsw_softc *sc) { struct cpsw_slot *slot; uint32_t reg; /* Clear ALE */ cpsw_write_4(sc, CPSW_ALE_CONTROL, CPSW_ALE_CTL_CLEAR_TBL); /* Enable ALE */ reg = CPSW_ALE_CTL_ENABLE; if (sc->dualemac) reg |= CPSW_ALE_CTL_VLAN_AWARE; cpsw_write_4(sc, CPSW_ALE_CONTROL, reg); /* Set Host Port Mapping. */ cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_TX_PRI_MAP, 0x76543210); cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_RX_CH_MAP, 0); /* Initialize ALE: set host port to forwarding(3). */ cpsw_write_4(sc, CPSW_ALE_PORTCTL(0), 3); cpsw_write_4(sc, CPSW_SS_PTYPE, 0); /* Enable statistics for ports 0, 1 and 2 */ cpsw_write_4(sc, CPSW_SS_STAT_PORT_EN, 7); /* Experiment: Turn off flow control */ /* This seems to fix the watchdog resets that have plagued earlier versions of this driver; I'm not yet sure if there are negative effects yet. */ cpsw_write_4(sc, CPSW_SS_FLOW_CONTROL, 0); /* Make IP hdr aligned with 4 */ cpsw_write_4(sc, CPSW_CPDMA_RX_BUFFER_OFFSET, 2); /* Initialize RX Buffer Descriptors */ cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), 0); /* Enable TX & RX DMA */ cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 1); cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 1); /* Enable Interrupts for core 0 */ cpsw_write_4(sc, CPSW_WR_C_RX_THRESH_EN(0), 0xFF); cpsw_write_4(sc, CPSW_WR_C_RX_EN(0), 0xFF); cpsw_write_4(sc, CPSW_WR_C_MISC_EN(0), 0x1F); /* Enable host Error Interrupt */ cpsw_write_4(sc, CPSW_CPDMA_DMA_INTMASK_SET, 3); /* Enable interrupts for RX Channel 0 */ cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_SET, 1); /* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */ /* TODO Calculate MDCLK=CLK/(CLKDIV+1) */ cpsw_write_4(sc, MDIOCONTROL, MDIOCTL_ENABLE | MDIOCTL_FAULTENB | 0xff); /* Select MII in GMII_SEL, Internal Delay mode */ //ti_scm_reg_write_4(0x650, 0); /* Initialize active queues. */ slot = STAILQ_FIRST(&sc->tx.active); if (slot != NULL) cpsw_write_hdp_slot(sc, &sc->tx, slot); slot = STAILQ_FIRST(&sc->rx.active); if (slot != NULL) cpsw_write_hdp_slot(sc, &sc->rx, slot); cpsw_rx_enqueue(sc); /* Activate network interface. */ sc->rx.running = 1; sc->tx.running = 1; sc->watchdog.timer = 0; callout_init(&sc->watchdog.callout, 0); callout_reset(&sc->watchdog.callout, hz, cpsw_tx_watchdog, sc); } /* * * Device Probe, Attach, Detach. * */ static int cpsw_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,cpsw")) return (ENXIO); device_set_desc(dev, "3-port Switch Ethernet Subsystem"); return (BUS_PROBE_DEFAULT); } static int cpsw_intr_attach(struct cpsw_softc *sc) { /* Note: We don't use sc->irq_res[2] (TX interrupt) */ if (bus_setup_intr(sc->dev, sc->irq_res[0], INTR_TYPE_NET | INTR_MPSAFE, NULL, cpsw_intr_rx_thresh, sc, &sc->ih_cookie[0]) != 0) { return (-1); } if (bus_setup_intr(sc->dev, sc->irq_res[1], INTR_TYPE_NET | INTR_MPSAFE, NULL, cpsw_intr_rx, sc, &sc->ih_cookie[1]) != 0) { return (-1); } if (bus_setup_intr(sc->dev, sc->irq_res[3], INTR_TYPE_NET | INTR_MPSAFE, NULL, cpsw_intr_misc, sc, &sc->ih_cookie[3]) != 0) { return (-1); } return (0); } static void cpsw_intr_detach(struct cpsw_softc *sc) { int i; for (i = 0; i < CPSW_INTR_COUNT; i++) { if (sc->ih_cookie[i]) { bus_teardown_intr(sc->dev, sc->irq_res[i], sc->ih_cookie[i]); } } } static int cpsw_get_fdt_data(struct cpsw_softc *sc, int port) { char *name; int len, phy, vlan; pcell_t phy_id[3], vlan_id; phandle_t child; unsigned long mdio_child_addr; /* Find any slave with phy_id */ phy = -1; vlan = -1; for (child = OF_child(sc->node); child != 0; child = OF_peer(child)) { if (OF_getprop_alloc(child, "name", 1, (void **)&name) < 0) continue; if (sscanf(name, "slave@%x", &mdio_child_addr) != 1) { free(name, M_OFWPROP); continue; } free(name, M_OFWPROP); if (mdio_child_addr != slave_mdio_addr[port]) continue; len = OF_getproplen(child, "phy_id"); if (len / sizeof(pcell_t) == 2) { /* Get phy address from fdt */ if (OF_getencprop(child, "phy_id", phy_id, len) > 0) phy = phy_id[1]; } len = OF_getproplen(child, "dual_emac_res_vlan"); if (len / sizeof(pcell_t) == 1) { /* Get phy address from fdt */ if (OF_getencprop(child, "dual_emac_res_vlan", &vlan_id, len) > 0) { vlan = vlan_id; } } break; } if (phy == -1) return (ENXIO); sc->port[port].phy = phy; sc->port[port].vlan = vlan; return (0); } static int cpsw_attach(device_t dev) { bus_dma_segment_t segs[1]; int error, i, nsegs; struct cpsw_softc *sc; uint32_t reg; sc = device_get_softc(dev); sc->dev = dev; sc->node = ofw_bus_get_node(dev); getbinuptime(&sc->attach_uptime); if (OF_getencprop(sc->node, "active_slave", &sc->active_slave, sizeof(sc->active_slave)) <= 0) { sc->active_slave = 0; } if (sc->active_slave > 1) sc->active_slave = 1; if (OF_hasprop(sc->node, "dual_emac")) sc->dualemac = 1; for (i = 0; i < CPSW_PORTS; i++) { if (!sc->dualemac && i != sc->active_slave) continue; if (cpsw_get_fdt_data(sc, i) != 0) { device_printf(dev, "failed to get PHY address from FDT\n"); return (ENXIO); } } /* Initialize mutexes */ mtx_init(&sc->tx.lock, device_get_nameunit(dev), "cpsw TX lock", MTX_DEF); mtx_init(&sc->rx.lock, device_get_nameunit(dev), "cpsw RX lock", MTX_DEF); /* Allocate IRQ resources */ error = bus_alloc_resources(dev, irq_res_spec, sc->irq_res); if (error) { device_printf(dev, "could not allocate IRQ resources\n"); cpsw_detach(dev); return (ENXIO); } sc->mem_rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(sc->dev, "failed to allocate memory resource\n"); cpsw_detach(dev); return (ENXIO); } reg = cpsw_read_4(sc, CPSW_SS_IDVER); device_printf(dev, "CPSW SS Version %d.%d (%d)\n", (reg >> 8 & 0x7), reg & 0xFF, (reg >> 11) & 0x1F); cpsw_add_sysctls(sc); /* Allocate a busdma tag and DMA safe memory for mbufs. */ error = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filtfunc, filtfuncarg */ MCLBYTES, CPSW_TXFRAGS, /* maxsize, nsegments */ MCLBYTES, 0, /* maxsegsz, flags */ NULL, NULL, /* lockfunc, lockfuncarg */ &sc->mbuf_dtag); /* dmatag */ if (error) { device_printf(dev, "bus_dma_tag_create failed\n"); cpsw_detach(dev); return (error); } /* Allocate the null mbuf and pre-sync it. */ sc->null_mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); memset(sc->null_mbuf->m_data, 0, sc->null_mbuf->m_ext.ext_size); bus_dmamap_create(sc->mbuf_dtag, 0, &sc->null_mbuf_dmamap); bus_dmamap_load_mbuf_sg(sc->mbuf_dtag, sc->null_mbuf_dmamap, sc->null_mbuf, segs, &nsegs, BUS_DMA_NOWAIT); bus_dmamap_sync(sc->mbuf_dtag, sc->null_mbuf_dmamap, BUS_DMASYNC_PREWRITE); sc->null_mbuf_paddr = segs[0].ds_addr; cpsw_init_slots(sc); /* Allocate slots to TX and RX queues. */ STAILQ_INIT(&sc->rx.avail); STAILQ_INIT(&sc->rx.active); STAILQ_INIT(&sc->tx.avail); STAILQ_INIT(&sc->tx.active); // For now: 128 slots to TX, rest to RX. // XXX TODO: start with 32/64 and grow dynamically based on demand. if (cpsw_add_slots(sc, &sc->tx, 128) || cpsw_add_slots(sc, &sc->rx, -1)) { device_printf(dev, "failed to allocate dmamaps\n"); cpsw_detach(dev); return (ENOMEM); } device_printf(dev, "Initial queue size TX=%d RX=%d\n", sc->tx.queue_slots, sc->rx.queue_slots); sc->tx.hdp_offset = CPSW_CPDMA_TX_HDP(0); sc->rx.hdp_offset = CPSW_CPDMA_RX_HDP(0); if (cpsw_intr_attach(sc) == -1) { device_printf(dev, "failed to setup interrupts\n"); cpsw_detach(dev); return (ENXIO); } /* Reset the controller. */ cpsw_reset(sc); cpsw_init(sc); for (i = 0; i < CPSW_PORTS; i++) { if (!sc->dualemac && i != sc->active_slave) continue; sc->port[i].dev = device_add_child(dev, "cpsw", i); if (sc->port[i].dev == NULL) { cpsw_detach(dev); return (ENXIO); } } bus_generic_attach(dev); return (0); } static int cpsw_detach(device_t dev) { struct cpsw_softc *sc; int error, i; bus_generic_detach(dev); sc = device_get_softc(dev); for (i = 0; i < CPSW_PORTS; i++) { if (sc->port[i].dev) device_delete_child(dev, sc->port[i].dev); } if (device_is_attached(dev)) { callout_stop(&sc->watchdog.callout); callout_drain(&sc->watchdog.callout); } /* Stop and release all interrupts */ cpsw_intr_detach(sc); /* Free dmamaps and mbufs */ - for (i = 0; i < sizeof(sc->_slots) / sizeof(sc->_slots[0]); ++i) + for (i = 0; i < nitems(sc->_slots); ++i) cpsw_free_slot(sc, &sc->_slots[i]); /* Free null mbuf. */ if (sc->null_mbuf_dmamap) { bus_dmamap_unload(sc->mbuf_dtag, sc->null_mbuf_dmamap); error = bus_dmamap_destroy(sc->mbuf_dtag, sc->null_mbuf_dmamap); KASSERT(error == 0, ("Mapping still active")); m_freem(sc->null_mbuf); } /* Free DMA tag */ if (sc->mbuf_dtag) { error = bus_dma_tag_destroy(sc->mbuf_dtag); KASSERT(error == 0, ("Unable to destroy DMA tag")); } /* Free IO memory handler */ if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); bus_release_resources(dev, irq_res_spec, sc->irq_res); /* Destroy mutexes */ mtx_destroy(&sc->rx.lock); mtx_destroy(&sc->tx.lock); return (0); } static phandle_t cpsw_get_node(device_t bus, device_t dev) { /* Share controller node with port device. */ return (ofw_bus_get_node(bus)); } static int cpswp_probe(device_t dev) { if (device_get_unit(dev) > 1) { device_printf(dev, "Only two ports are supported.\n"); return (ENXIO); } device_set_desc(dev, "Ethernet Switch Port"); return (BUS_PROBE_DEFAULT); } static int cpswp_attach(device_t dev) { int error; struct ifnet *ifp; struct cpswp_softc *sc; uint32_t reg; uint8_t mac_addr[ETHER_ADDR_LEN]; sc = device_get_softc(dev); sc->dev = dev; sc->pdev = device_get_parent(dev); sc->swsc = device_get_softc(sc->pdev); sc->unit = device_get_unit(dev); sc->phy = sc->swsc->port[sc->unit].phy; sc->vlan = sc->swsc->port[sc->unit].vlan; if (sc->swsc->dualemac && sc->vlan == -1) sc->vlan = sc->unit + 1; if (sc->unit == 0) { sc->physel = MDIOUSERPHYSEL0; sc->phyaccess = MDIOUSERACCESS0; } else { sc->physel = MDIOUSERPHYSEL1; sc->phyaccess = MDIOUSERACCESS1; } mtx_init(&sc->lock, device_get_nameunit(dev), "cpsw port lock", MTX_DEF); /* Allocate network interface */ ifp = sc->ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { cpswp_detach(dev); return (ENXIO); } if_initname(ifp, device_get_name(sc->dev), sc->unit); ifp->if_softc = sc; ifp->if_flags = IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST; ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_HWCSUM; //FIXME VLAN? ifp->if_capenable = ifp->if_capabilities; ifp->if_init = cpswp_init; ifp->if_start = cpswp_start; ifp->if_ioctl = cpswp_ioctl; ifp->if_snd.ifq_drv_maxlen = sc->swsc->tx.queue_slots; IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); IFQ_SET_READY(&ifp->if_snd); /* Get high part of MAC address from control module (mac_id[0|1]_hi) */ ti_scm_reg_read_4(0x634 + sc->unit * 8, ®); mac_addr[0] = reg & 0xFF; mac_addr[1] = (reg >> 8) & 0xFF; mac_addr[2] = (reg >> 16) & 0xFF; mac_addr[3] = (reg >> 24) & 0xFF; /* Get low part of MAC address from control module (mac_id[0|1]_lo) */ ti_scm_reg_read_4(0x630 + sc->unit * 8, ®); mac_addr[4] = reg & 0xFF; mac_addr[5] = (reg >> 8) & 0xFF; error = mii_attach(dev, &sc->miibus, ifp, cpswp_ifmedia_upd, cpswp_ifmedia_sts, BMSR_DEFCAPMASK, sc->phy, MII_OFFSET_ANY, 0); if (error) { device_printf(dev, "attaching PHYs failed\n"); cpswp_detach(dev); return (error); } sc->mii = device_get_softc(sc->miibus); /* Select PHY and enable interrupts */ cpsw_write_4(sc->swsc, sc->physel, MDIO_PHYSEL_LINKINTENB | (sc->phy & 0x1F)); ether_ifattach(sc->ifp, mac_addr); callout_init(&sc->mii_callout, 0); return (0); } static int cpswp_detach(device_t dev) { struct cpswp_softc *sc; sc = device_get_softc(dev); CPSWP_DEBUGF(sc, ("")); if (device_is_attached(dev)) { ether_ifdetach(sc->ifp); CPSW_PORT_LOCK(sc); cpswp_stop_locked(sc); CPSW_PORT_UNLOCK(sc); callout_drain(&sc->mii_callout); } bus_generic_detach(dev); if_free(sc->ifp); mtx_destroy(&sc->lock); return (0); } /* * * Init/Shutdown. * */ static int cpsw_ports_down(struct cpsw_softc *sc) { struct cpswp_softc *psc; struct ifnet *ifp1, *ifp2; if (!sc->dualemac) return (1); psc = device_get_softc(sc->port[0].dev); ifp1 = psc->ifp; psc = device_get_softc(sc->port[1].dev); ifp2 = psc->ifp; if ((ifp1->if_flags & IFF_UP) == 0 && (ifp2->if_flags & IFF_UP) == 0) return (1); return (0); } static void cpswp_init(void *arg) { struct cpswp_softc *sc = arg; CPSWP_DEBUGF(sc, ("")); CPSW_PORT_LOCK(sc); cpswp_init_locked(arg); CPSW_PORT_UNLOCK(sc); } static void cpswp_init_locked(void *arg) { struct cpswp_softc *sc = arg; struct ifnet *ifp; uint32_t reg; CPSWP_DEBUGF(sc, ("")); CPSW_PORT_LOCK_ASSERT(sc); ifp = sc->ifp; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) return; getbinuptime(&sc->init_uptime); if (!sc->swsc->rx.running && !sc->swsc->tx.running) { /* Reset the controller. */ cpsw_reset(sc->swsc); cpsw_init(sc->swsc); } /* Set Slave Mapping. */ cpsw_write_4(sc->swsc, CPSW_SL_RX_PRI_MAP(sc->unit), 0x76543210); cpsw_write_4(sc->swsc, CPSW_PORT_P_TX_PRI_MAP(sc->unit + 1), 0x33221100); cpsw_write_4(sc->swsc, CPSW_SL_RX_MAXLEN(sc->unit), 0x5f2); /* Enable MAC RX/TX modules. */ /* TODO: Docs claim that IFCTL_B and IFCTL_A do the same thing? */ /* Huh? Docs call bit 0 "Loopback" some places, "FullDuplex" others. */ reg = cpsw_read_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit)); reg |= CPSW_SL_MACTL_GMII_ENABLE; cpsw_write_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit), reg); /* Initialize ALE: set port to forwarding(3), initialize addrs */ cpsw_write_4(sc->swsc, CPSW_ALE_PORTCTL(sc->unit + 1), 3); cpswp_ale_update_addresses(sc, 1); if (sc->swsc->dualemac) { /* Set Port VID. */ cpsw_write_4(sc->swsc, CPSW_PORT_P_VLAN(sc->unit + 1), sc->vlan & 0xfff); cpsw_ale_update_vlan_table(sc->swsc, sc->vlan, (1 << (sc->unit + 1)) | (1 << 0), /* Member list */ (1 << (sc->unit + 1)) | (1 << 0), /* Untagged egress */ (1 << (sc->unit + 1)) | (1 << 0), 0); /* mcast reg flood */ } mii_mediachg(sc->mii); callout_reset(&sc->mii_callout, hz, cpswp_tick, sc); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } static int cpsw_shutdown(device_t dev) { struct cpsw_softc *sc; struct cpswp_softc *psc; int i; sc = device_get_softc(dev); CPSW_DEBUGF(sc, ("")); for (i = 0; i < CPSW_PORTS; i++) { if (!sc->dualemac && i != sc->active_slave) continue; psc = device_get_softc(sc->port[i].dev); CPSW_PORT_LOCK(psc); cpswp_stop_locked(psc); CPSW_PORT_UNLOCK(psc); } return (0); } static void cpsw_rx_teardown_locked(struct cpsw_softc *sc) { struct ifnet *ifp; struct mbuf *received, *next; int i = 0; CPSW_DEBUGF(sc, ("starting RX teardown")); cpsw_write_4(sc, CPSW_CPDMA_RX_TEARDOWN, 0); for (;;) { received = cpsw_rx_dequeue(sc); CPSW_GLOBAL_UNLOCK(sc); while (received != NULL) { next = received->m_nextpkt; received->m_nextpkt = NULL; ifp = received->m_pkthdr.rcvif; (*ifp->if_input)(ifp, received); if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); received = next; } CPSW_GLOBAL_LOCK(sc); if (!sc->rx.running) { CPSW_DEBUGF(sc, ("finished RX teardown (%d retries)", i)); return; } if (++i > 10) { device_printf(sc->dev, "Unable to cleanly shutdown receiver\n"); return; } DELAY(10); } } static void cpsw_tx_teardown_locked(struct cpsw_softc *sc) { int i = 0; CPSW_DEBUGF(sc, ("starting TX teardown")); cpsw_write_4(sc, CPSW_CPDMA_TX_TEARDOWN, 0); cpsw_tx_dequeue(sc); while (sc->tx.running && ++i < 10) { DELAY(10); cpsw_tx_dequeue(sc); } if (sc->tx.running) { device_printf(sc->dev, "Unable to cleanly shutdown transmitter\n"); } CPSW_DEBUGF(sc, ("finished TX teardown (%d retries, %d idle buffers)", i, sc->tx.active_queue_len)); } static void cpswp_stop_locked(struct cpswp_softc *sc) { struct ifnet *ifp; uint32_t reg; ifp = sc->ifp; CPSWP_DEBUGF(sc, ("")); CPSW_PORT_LOCK_ASSERT(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; /* Disable interface */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; ifp->if_drv_flags |= IFF_DRV_OACTIVE; /* Stop ticker */ callout_stop(&sc->mii_callout); /* Tear down the RX/TX queues. */ if (cpsw_ports_down(sc->swsc)) { CPSW_GLOBAL_LOCK(sc->swsc); cpsw_rx_teardown_locked(sc->swsc); cpsw_tx_teardown_locked(sc->swsc); CPSW_GLOBAL_UNLOCK(sc->swsc); } /* Stop MAC RX/TX modules. */ reg = cpsw_read_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit)); reg &= ~CPSW_SL_MACTL_GMII_ENABLE; cpsw_write_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit), reg); if (cpsw_ports_down(sc->swsc)) { /* Capture stats before we reset controller. */ cpsw_stats_collect(sc->swsc); cpsw_reset(sc->swsc); cpsw_init(sc->swsc); } } /* * Suspend/Resume. */ static int cpsw_suspend(device_t dev) { struct cpsw_softc *sc; struct cpswp_softc *psc; int i; sc = device_get_softc(dev); CPSW_DEBUGF(sc, ("")); for (i = 0; i < CPSW_PORTS; i++) { if (!sc->dualemac && i != sc->active_slave) continue; psc = device_get_softc(sc->port[i].dev); CPSW_PORT_LOCK(psc); cpswp_stop_locked(psc); CPSW_PORT_UNLOCK(psc); } return (0); } static int cpsw_resume(device_t dev) { struct cpsw_softc *sc; sc = device_get_softc(dev); CPSW_DEBUGF(sc, ("UNIMPLEMENTED")); return (0); } /* * * IOCTL * */ static void cpsw_set_promisc(struct cpswp_softc *sc, int set) { uint32_t reg; /* * Enabling promiscuous mode requires ALE_BYPASS to be enabled. * That disables the ALE forwarding logic and causes every * packet to be sent only to the host port. In bypass mode, * the ALE processes host port transmit packets the same as in * normal mode. */ reg = cpsw_read_4(sc->swsc, CPSW_ALE_CONTROL); reg &= ~CPSW_ALE_CTL_BYPASS; if (set) reg |= CPSW_ALE_CTL_BYPASS; cpsw_write_4(sc->swsc, CPSW_ALE_CONTROL, reg); } static void cpsw_set_allmulti(struct cpswp_softc *sc, int set) { if (set) { printf("All-multicast mode unimplemented\n"); } } static int cpswp_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct cpswp_softc *sc; struct ifreq *ifr; int error; uint32_t changed; error = 0; sc = ifp->if_softc; ifr = (struct ifreq *)data; switch (command) { case SIOCSIFFLAGS: CPSW_PORT_LOCK(sc); if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { changed = ifp->if_flags ^ sc->if_flags; CPSWP_DEBUGF(sc, ("SIOCSIFFLAGS: UP & RUNNING (changed=0x%x)", changed)); if (changed & IFF_PROMISC) cpsw_set_promisc(sc, ifp->if_flags & IFF_PROMISC); if (changed & IFF_ALLMULTI) cpsw_set_allmulti(sc, ifp->if_flags & IFF_ALLMULTI); } else { CPSWP_DEBUGF(sc, ("SIOCSIFFLAGS: UP but not RUNNING; starting up")); cpswp_init_locked(sc); } } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) { CPSWP_DEBUGF(sc, ("SIOCSIFFLAGS: not UP but RUNNING; shutting down")); cpswp_stop_locked(sc); } sc->if_flags = ifp->if_flags; CPSW_PORT_UNLOCK(sc); break; case SIOCADDMULTI: cpswp_ale_update_addresses(sc, 0); break; case SIOCDELMULTI: /* Ugh. DELMULTI doesn't provide the specific address being removed, so the best we can do is remove everything and rebuild it all. */ cpswp_ale_update_addresses(sc, 1); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->mii->mii_media, command); break; default: error = ether_ioctl(ifp, command, data); } return (error); } /* * * MIIBUS * */ static int cpswp_miibus_ready(struct cpsw_softc *sc, uint32_t reg) { uint32_t r, retries = CPSW_MIIBUS_RETRIES; while (--retries) { r = cpsw_read_4(sc, reg); if ((r & MDIO_PHYACCESS_GO) == 0) return (1); DELAY(CPSW_MIIBUS_DELAY); } return (0); } static int cpswp_miibus_readreg(device_t dev, int phy, int reg) { struct cpswp_softc *sc; uint32_t cmd, r; sc = device_get_softc(dev); if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) { device_printf(dev, "MDIO not ready to read\n"); return (0); } /* Set GO, reg, phy */ cmd = MDIO_PHYACCESS_GO | (reg & 0x1F) << 21 | (phy & 0x1F) << 16; cpsw_write_4(sc->swsc, sc->phyaccess, cmd); if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) { device_printf(dev, "MDIO timed out during read\n"); return (0); } r = cpsw_read_4(sc->swsc, sc->phyaccess); if ((r & MDIO_PHYACCESS_ACK) == 0) { device_printf(dev, "Failed to read from PHY.\n"); r = 0; } return (r & 0xFFFF); } static int cpswp_miibus_writereg(device_t dev, int phy, int reg, int value) { struct cpswp_softc *sc; uint32_t cmd; sc = device_get_softc(dev); if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) { device_printf(dev, "MDIO not ready to write\n"); return (0); } /* Set GO, WRITE, reg, phy, and value */ cmd = MDIO_PHYACCESS_GO | MDIO_PHYACCESS_WRITE | (reg & 0x1F) << 21 | (phy & 0x1F) << 16 | (value & 0xFFFF); cpsw_write_4(sc->swsc, sc->phyaccess, cmd); if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) { device_printf(dev, "MDIO timed out during write\n"); return (0); } if ((cpsw_read_4(sc->swsc, sc->phyaccess) & MDIO_PHYACCESS_ACK) == 0) device_printf(dev, "Failed to write to PHY.\n"); return (0); } static void cpswp_miibus_statchg(device_t dev) { struct cpswp_softc *sc; uint32_t mac_control, reg; sc = device_get_softc(dev); CPSWP_DEBUGF(sc, ("")); reg = CPSW_SL_MACCONTROL(sc->unit); mac_control = cpsw_read_4(sc->swsc, reg); mac_control &= ~(CPSW_SL_MACTL_GIG | CPSW_SL_MACTL_IFCTL_A | CPSW_SL_MACTL_IFCTL_B | CPSW_SL_MACTL_FULLDUPLEX); switch(IFM_SUBTYPE(sc->mii->mii_media_active)) { case IFM_1000_SX: case IFM_1000_LX: case IFM_1000_CX: case IFM_1000_T: mac_control |= CPSW_SL_MACTL_GIG; break; case IFM_100_TX: mac_control |= CPSW_SL_MACTL_IFCTL_A; break; } if (sc->mii->mii_media_active & IFM_FDX) mac_control |= CPSW_SL_MACTL_FULLDUPLEX; cpsw_write_4(sc->swsc, reg, mac_control); } /* * * Transmit/Receive Packets. * */ static void cpsw_intr_rx(void *arg) { struct cpsw_softc *sc = arg; struct ifnet *ifp; struct mbuf *received, *next; CPSW_RX_LOCK(sc); received = cpsw_rx_dequeue(sc); cpsw_rx_enqueue(sc); cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 1); CPSW_RX_UNLOCK(sc); while (received != NULL) { next = received->m_nextpkt; received->m_nextpkt = NULL; ifp = received->m_pkthdr.rcvif; (*ifp->if_input)(ifp, received); if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); received = next; } } static struct mbuf * cpsw_rx_dequeue(struct cpsw_softc *sc) { struct cpsw_cpdma_bd bd; struct cpsw_slot *slot; struct cpswp_softc *psc; struct mbuf *mb_head, *mb_tail; int port, removed = 0; mb_head = mb_tail = NULL; /* Pull completed packets off hardware RX queue. */ while ((slot = STAILQ_FIRST(&sc->rx.active)) != NULL) { cpsw_cpdma_read_bd(sc, slot, &bd); if (bd.flags & CPDMA_BD_OWNER) break; /* Still in use by hardware */ CPSW_DEBUGF(sc, ("Removing received packet from RX queue")); ++removed; STAILQ_REMOVE_HEAD(&sc->rx.active, next); STAILQ_INSERT_TAIL(&sc->rx.avail, slot, next); bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); if (bd.flags & CPDMA_BD_TDOWNCMPLT) { CPSW_DEBUGF(sc, ("RX teardown in progress")); m_freem(slot->mbuf); slot->mbuf = NULL; cpsw_write_cp(sc, &sc->rx, 0xfffffffc); sc->rx.running = 0; break; } cpsw_write_cp_slot(sc, &sc->rx, slot); port = (bd.flags & CPDMA_BD_PORT_MASK) - 1; KASSERT(port >= 0 && port <= 1, ("patcket received with invalid port: %d", port)); psc = device_get_softc(sc->port[port].dev); /* Set up mbuf */ /* TODO: track SOP/EOP bits to assemble a full mbuf out of received fragments. */ slot->mbuf->m_data += bd.bufoff; slot->mbuf->m_len = bd.pktlen - 4; slot->mbuf->m_pkthdr.len = bd.pktlen - 4; slot->mbuf->m_flags |= M_PKTHDR; slot->mbuf->m_pkthdr.rcvif = psc->ifp; slot->mbuf->m_nextpkt = NULL; if ((psc->ifp->if_capenable & IFCAP_RXCSUM) != 0) { /* check for valid CRC by looking into pkt_err[5:4] */ if ((bd.flags & CPDMA_BD_PKT_ERR_MASK) == 0) { slot->mbuf->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; slot->mbuf->m_pkthdr.csum_flags |= CSUM_IP_VALID; slot->mbuf->m_pkthdr.csum_data = 0xffff; } } /* Add mbuf to packet list to be returned. */ if (mb_tail) { mb_tail->m_nextpkt = slot->mbuf; } else { mb_head = slot->mbuf; } mb_tail = slot->mbuf; slot->mbuf = NULL; } if (removed != 0) { sc->rx.queue_removes += removed; sc->rx.active_queue_len -= removed; sc->rx.avail_queue_len += removed; if (sc->rx.avail_queue_len > sc->rx.max_avail_queue_len) sc->rx.max_avail_queue_len = sc->rx.avail_queue_len; } return (mb_head); } static void cpsw_rx_enqueue(struct cpsw_softc *sc) { bus_dma_segment_t seg[1]; struct cpsw_cpdma_bd bd; struct cpsw_slots tmpqueue = STAILQ_HEAD_INITIALIZER(tmpqueue); struct cpsw_slot *slot, *prev_slot = NULL; struct cpsw_slot *last_old_slot, *first_new_slot; int error, nsegs, added = 0; /* Register new mbufs with hardware. */ while ((slot = STAILQ_FIRST(&sc->rx.avail)) != NULL) { if (slot->mbuf == NULL) { slot->mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (slot->mbuf == NULL) { device_printf(sc->dev, "Unable to fill RX queue\n"); break; } slot->mbuf->m_len = slot->mbuf->m_pkthdr.len = slot->mbuf->m_ext.ext_size; } error = bus_dmamap_load_mbuf_sg(sc->mbuf_dtag, slot->dmamap, slot->mbuf, seg, &nsegs, BUS_DMA_NOWAIT); KASSERT(nsegs == 1, ("More than one segment (nsegs=%d)", nsegs)); KASSERT(error == 0, ("DMA error (error=%d)", error)); if (error != 0 || nsegs != 1) { device_printf(sc->dev, "%s: Can't prep RX buf for DMA (nsegs=%d, error=%d)\n", __func__, nsegs, error); bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); m_freem(slot->mbuf); slot->mbuf = NULL; break; } bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_PREREAD); /* Create and submit new rx descriptor*/ bd.next = 0; bd.bufptr = seg->ds_addr; bd.bufoff = 0; bd.buflen = MCLBYTES - 1; bd.pktlen = bd.buflen; bd.flags = CPDMA_BD_OWNER; cpsw_cpdma_write_bd(sc, slot, &bd); ++added; if (prev_slot != NULL) cpsw_cpdma_write_bd_next(sc, prev_slot, slot); prev_slot = slot; STAILQ_REMOVE_HEAD(&sc->rx.avail, next); sc->rx.avail_queue_len--; STAILQ_INSERT_TAIL(&tmpqueue, slot, next); } if (added == 0) return; CPSW_DEBUGF(sc, ("Adding %d buffers to RX queue", added)); /* Link new entries to hardware RX queue. */ last_old_slot = STAILQ_LAST(&sc->rx.active, cpsw_slot, next); first_new_slot = STAILQ_FIRST(&tmpqueue); STAILQ_CONCAT(&sc->rx.active, &tmpqueue); if (first_new_slot == NULL) { return; } else if (last_old_slot == NULL) { /* Start a fresh queue. */ cpsw_write_hdp_slot(sc, &sc->rx, first_new_slot); } else { /* Add buffers to end of current queue. */ cpsw_cpdma_write_bd_next(sc, last_old_slot, first_new_slot); /* If underrun, restart queue. */ if (cpsw_cpdma_read_bd_flags(sc, last_old_slot) & CPDMA_BD_EOQ) { cpsw_write_hdp_slot(sc, &sc->rx, first_new_slot); } } sc->rx.queue_adds += added; sc->rx.active_queue_len += added; if (sc->rx.active_queue_len > sc->rx.max_active_queue_len) { sc->rx.max_active_queue_len = sc->rx.active_queue_len; } } static void cpswp_start(struct ifnet *ifp) { struct cpswp_softc *sc = ifp->if_softc; CPSW_TX_LOCK(sc->swsc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && sc->swsc->tx.running) { cpswp_tx_enqueue(sc); cpsw_tx_dequeue(sc->swsc); } CPSW_TX_UNLOCK(sc->swsc); } static void cpswp_tx_enqueue(struct cpswp_softc *sc) { bus_dma_segment_t segs[CPSW_TXFRAGS]; struct cpsw_cpdma_bd bd; struct cpsw_slots tmpqueue = STAILQ_HEAD_INITIALIZER(tmpqueue); struct cpsw_slot *slot, *prev_slot = NULL; struct cpsw_slot *last_old_slot, *first_new_slot; struct mbuf *m0; int error, flags, nsegs, seg, added = 0, padlen; flags = 0; if (sc->swsc->dualemac) { flags = CPDMA_BD_TO_PORT | ((sc->unit + 1) & CPDMA_BD_PORT_MASK); } /* Pull pending packets from IF queue and prep them for DMA. */ while ((slot = STAILQ_FIRST(&sc->swsc->tx.avail)) != NULL) { IF_DEQUEUE(&sc->ifp->if_snd, m0); if (m0 == NULL) break; slot->mbuf = m0; padlen = ETHER_MIN_LEN - slot->mbuf->m_pkthdr.len; if (padlen < 0) padlen = 0; /* Create mapping in DMA memory */ error = bus_dmamap_load_mbuf_sg(sc->swsc->mbuf_dtag, slot->dmamap, slot->mbuf, segs, &nsegs, BUS_DMA_NOWAIT); /* If the packet is too fragmented, try to simplify. */ if (error == EFBIG || (error == 0 && nsegs + (padlen > 0 ? 1 : 0) > sc->swsc->tx.avail_queue_len)) { bus_dmamap_unload(sc->swsc->mbuf_dtag, slot->dmamap); if (padlen > 0) /* May as well add padding. */ m_append(slot->mbuf, padlen, sc->swsc->null_mbuf->m_data); m0 = m_defrag(slot->mbuf, M_NOWAIT); if (m0 == NULL) { device_printf(sc->dev, "Can't defragment packet; dropping\n"); m_freem(slot->mbuf); } else { CPSWP_DEBUGF(sc, ("Requeueing defragmented packet")); IF_PREPEND(&sc->ifp->if_snd, m0); } slot->mbuf = NULL; continue; } if (error != 0) { device_printf(sc->dev, "%s: Can't setup DMA (error=%d), dropping packet\n", __func__, error); bus_dmamap_unload(sc->swsc->mbuf_dtag, slot->dmamap); m_freem(slot->mbuf); slot->mbuf = NULL; break; } bus_dmamap_sync(sc->swsc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_PREWRITE); CPSWP_DEBUGF(sc, ("Queueing TX packet: %d segments + %d pad bytes", nsegs, padlen)); slot->ifp = sc->ifp; /* If there is only one segment, the for() loop * gets skipped and the single buffer gets set up * as both SOP and EOP. */ /* Start by setting up the first buffer */ bd.next = 0; bd.bufptr = segs[0].ds_addr; bd.bufoff = 0; bd.buflen = segs[0].ds_len; bd.pktlen = m_length(slot->mbuf, NULL) + padlen; bd.flags = CPDMA_BD_SOP | CPDMA_BD_OWNER | flags; for (seg = 1; seg < nsegs; ++seg) { /* Save the previous buffer (which isn't EOP) */ cpsw_cpdma_write_bd(sc->swsc, slot, &bd); if (prev_slot != NULL) { cpsw_cpdma_write_bd_next(sc->swsc, prev_slot, slot); } prev_slot = slot; STAILQ_REMOVE_HEAD(&sc->swsc->tx.avail, next); sc->swsc->tx.avail_queue_len--; STAILQ_INSERT_TAIL(&tmpqueue, slot, next); ++added; slot = STAILQ_FIRST(&sc->swsc->tx.avail); /* Setup next buffer (which isn't SOP) */ bd.next = 0; bd.bufptr = segs[seg].ds_addr; bd.bufoff = 0; bd.buflen = segs[seg].ds_len; bd.pktlen = 0; bd.flags = CPDMA_BD_OWNER | flags; } /* Save the final buffer. */ if (padlen <= 0) bd.flags |= CPDMA_BD_EOP; cpsw_cpdma_write_bd(sc->swsc, slot, &bd); if (prev_slot != NULL) cpsw_cpdma_write_bd_next(sc->swsc, prev_slot, slot); prev_slot = slot; STAILQ_REMOVE_HEAD(&sc->swsc->tx.avail, next); sc->swsc->tx.avail_queue_len--; STAILQ_INSERT_TAIL(&tmpqueue, slot, next); ++added; if (padlen > 0) { slot = STAILQ_FIRST(&sc->swsc->tx.avail); STAILQ_REMOVE_HEAD(&sc->swsc->tx.avail, next); sc->swsc->tx.avail_queue_len--; STAILQ_INSERT_TAIL(&tmpqueue, slot, next); ++added; /* Setup buffer of null pad bytes (definitely EOP) */ cpsw_cpdma_write_bd_next(sc->swsc, prev_slot, slot); prev_slot = slot; bd.next = 0; bd.bufptr = sc->swsc->null_mbuf_paddr; bd.bufoff = 0; bd.buflen = padlen; bd.pktlen = 0; bd.flags = CPDMA_BD_EOP | CPDMA_BD_OWNER | flags; cpsw_cpdma_write_bd(sc->swsc, slot, &bd); ++nsegs; } if (nsegs > sc->swsc->tx.longest_chain) sc->swsc->tx.longest_chain = nsegs; // TODO: Should we defer the BPF tap until // after all packets are queued? BPF_MTAP(sc->ifp, m0); } /* Attach the list of new buffers to the hardware TX queue. */ last_old_slot = STAILQ_LAST(&sc->swsc->tx.active, cpsw_slot, next); first_new_slot = STAILQ_FIRST(&tmpqueue); STAILQ_CONCAT(&sc->swsc->tx.active, &tmpqueue); if (first_new_slot == NULL) { return; } else if (last_old_slot == NULL) { /* Start a fresh queue. */ cpsw_write_hdp_slot(sc->swsc, &sc->swsc->tx, first_new_slot); } else { /* Add buffers to end of current queue. */ cpsw_cpdma_write_bd_next(sc->swsc, last_old_slot, first_new_slot); /* If underrun, restart queue. */ if (cpsw_cpdma_read_bd_flags(sc->swsc, last_old_slot) & CPDMA_BD_EOQ) { cpsw_write_hdp_slot(sc->swsc, &sc->swsc->tx, first_new_slot); } } sc->swsc->tx.queue_adds += added; sc->swsc->tx.active_queue_len += added; if (sc->swsc->tx.active_queue_len > sc->swsc->tx.max_active_queue_len) { sc->swsc->tx.max_active_queue_len = sc->swsc->tx.active_queue_len; } } static int cpsw_tx_dequeue(struct cpsw_softc *sc) { struct cpsw_slot *slot, *last_removed_slot = NULL; uint32_t flags, removed = 0; slot = STAILQ_FIRST(&sc->tx.active); if (slot == NULL && cpsw_read_cp(sc, &sc->tx) == 0xfffffffc) { CPSW_DEBUGF(sc, ("TX teardown of an empty queue")); cpsw_write_cp(sc, &sc->tx, 0xfffffffc); sc->tx.running = 0; return (0); } /* Pull completed buffers off the hardware TX queue. */ while (slot != NULL) { flags = cpsw_cpdma_read_bd_flags(sc, slot); if (flags & CPDMA_BD_OWNER) break; /* Hardware is still using this packet. */ CPSW_DEBUGF(sc, ("TX removing completed packet")); bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); m_freem(slot->mbuf); slot->mbuf = NULL; if (slot->ifp) if_inc_counter(slot->ifp, IFCOUNTER_OPACKETS, 1); /* Dequeue any additional buffers used by this packet. */ while (slot != NULL && slot->mbuf == NULL) { STAILQ_REMOVE_HEAD(&sc->tx.active, next); STAILQ_INSERT_TAIL(&sc->tx.avail, slot, next); ++removed; last_removed_slot = slot; slot = STAILQ_FIRST(&sc->tx.active); } /* TearDown complete is only marked on the SOP for the packet. */ if (flags & CPDMA_BD_TDOWNCMPLT) { CPSW_DEBUGF(sc, ("TX teardown in progress")); cpsw_write_cp(sc, &sc->tx, 0xfffffffc); // TODO: Increment a count of dropped TX packets sc->tx.running = 0; break; } } if (removed != 0) { cpsw_write_cp_slot(sc, &sc->tx, last_removed_slot); sc->tx.queue_removes += removed; sc->tx.active_queue_len -= removed; sc->tx.avail_queue_len += removed; if (sc->tx.avail_queue_len > sc->tx.max_avail_queue_len) sc->tx.max_avail_queue_len = sc->tx.avail_queue_len; } return (removed); } /* * * Miscellaneous interrupts. * */ static void cpsw_intr_rx_thresh(void *arg) { struct cpsw_softc *sc = arg; uint32_t stat = cpsw_read_4(sc, CPSW_WR_C_RX_THRESH_STAT(0)); CPSW_DEBUGF(sc, ("stat=%x", stat)); cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 0); } static void cpsw_intr_misc_host_error(struct cpsw_softc *sc) { uint32_t intstat; uint32_t dmastat; int txerr, rxerr, txchan, rxchan; printf("\n\n"); device_printf(sc->dev, "HOST ERROR: PROGRAMMING ERROR DETECTED BY HARDWARE\n"); printf("\n\n"); intstat = cpsw_read_4(sc, CPSW_CPDMA_DMA_INTSTAT_MASKED); device_printf(sc->dev, "CPSW_CPDMA_DMA_INTSTAT_MASKED=0x%x\n", intstat); dmastat = cpsw_read_4(sc, CPSW_CPDMA_DMASTATUS); device_printf(sc->dev, "CPSW_CPDMA_DMASTATUS=0x%x\n", dmastat); txerr = (dmastat >> 20) & 15; txchan = (dmastat >> 16) & 7; rxerr = (dmastat >> 12) & 15; rxchan = (dmastat >> 8) & 7; switch (txerr) { case 0: break; case 1: printf("SOP error on TX channel %d\n", txchan); break; case 2: printf("Ownership bit not set on SOP buffer on TX channel %d\n", txchan); break; case 3: printf("Zero Next Buffer but not EOP on TX channel %d\n", txchan); break; case 4: printf("Zero Buffer Pointer on TX channel %d\n", txchan); break; case 5: printf("Zero Buffer Length on TX channel %d\n", txchan); break; case 6: printf("Packet length error on TX channel %d\n", txchan); break; default: printf("Unknown error on TX channel %d\n", txchan); break; } if (txerr != 0) { printf("CPSW_CPDMA_TX%d_HDP=0x%x\n", txchan, cpsw_read_4(sc, CPSW_CPDMA_TX_HDP(txchan))); printf("CPSW_CPDMA_TX%d_CP=0x%x\n", txchan, cpsw_read_4(sc, CPSW_CPDMA_TX_CP(txchan))); cpsw_dump_queue(sc, &sc->tx.active); } switch (rxerr) { case 0: break; case 2: printf("Ownership bit not set on RX channel %d\n", rxchan); break; case 4: printf("Zero Buffer Pointer on RX channel %d\n", rxchan); break; case 5: printf("Zero Buffer Length on RX channel %d\n", rxchan); break; case 6: printf("Buffer offset too big on RX channel %d\n", rxchan); break; default: printf("Unknown RX error on RX channel %d\n", rxchan); break; } if (rxerr != 0) { printf("CPSW_CPDMA_RX%d_HDP=0x%x\n", rxchan, cpsw_read_4(sc,CPSW_CPDMA_RX_HDP(rxchan))); printf("CPSW_CPDMA_RX%d_CP=0x%x\n", rxchan, cpsw_read_4(sc, CPSW_CPDMA_RX_CP(rxchan))); cpsw_dump_queue(sc, &sc->rx.active); } printf("\nALE Table\n"); cpsw_ale_dump_table(sc); // XXX do something useful here?? panic("CPSW HOST ERROR INTERRUPT"); // Suppress this interrupt in the future. cpsw_write_4(sc, CPSW_CPDMA_DMA_INTMASK_CLEAR, intstat); printf("XXX HOST ERROR INTERRUPT SUPPRESSED\n"); // The watchdog will probably reset the controller // in a little while. It will probably fail again. } static void cpsw_intr_misc(void *arg) { struct cpsw_softc *sc = arg; uint32_t stat = cpsw_read_4(sc, CPSW_WR_C_MISC_STAT(0)); if (stat & CPSW_WR_C_MISC_EVNT_PEND) CPSW_DEBUGF(sc, ("Time sync event interrupt unimplemented")); if (stat & CPSW_WR_C_MISC_STAT_PEND) cpsw_stats_collect(sc); if (stat & CPSW_WR_C_MISC_HOST_PEND) cpsw_intr_misc_host_error(sc); if (stat & CPSW_WR_C_MISC_MDIOLINK) { cpsw_write_4(sc, MDIOLINKINTMASKED, cpsw_read_4(sc, MDIOLINKINTMASKED)); } if (stat & CPSW_WR_C_MISC_MDIOUSER) { CPSW_DEBUGF(sc, ("MDIO operation completed interrupt unimplemented")); } cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 3); } /* * * Periodic Checks and Watchdog. * */ static void cpswp_tick(void *msc) { struct cpswp_softc *sc = msc; /* Check for media type change */ mii_tick(sc->mii); if (sc->media_status != sc->mii->mii_media.ifm_media) { printf("%s: media type changed (ifm_media=%x)\n", __func__, sc->mii->mii_media.ifm_media); cpswp_ifmedia_upd(sc->ifp); } /* Schedule another timeout one second from now */ callout_reset(&sc->mii_callout, hz, cpswp_tick, sc); } static void cpswp_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct cpswp_softc *sc; struct mii_data *mii; sc = ifp->if_softc; CPSWP_DEBUGF(sc, ("")); CPSW_PORT_LOCK(sc); mii = sc->mii; mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; CPSW_PORT_UNLOCK(sc); } static int cpswp_ifmedia_upd(struct ifnet *ifp) { struct cpswp_softc *sc; sc = ifp->if_softc; CPSWP_DEBUGF(sc, ("")); CPSW_PORT_LOCK(sc); mii_mediachg(sc->mii); sc->media_status = sc->mii->mii_media.ifm_media; CPSW_PORT_UNLOCK(sc); return (0); } static void cpsw_tx_watchdog_full_reset(struct cpsw_softc *sc) { struct cpswp_softc *psc; int i; cpsw_debugf_head("CPSW watchdog"); device_printf(sc->dev, "watchdog timeout\n"); for (i = 0; i < CPSW_PORTS; i++) { if (!sc->dualemac && i != sc->active_slave) continue; psc = device_get_softc(sc->port[i].dev); CPSW_PORT_LOCK(psc); cpswp_stop_locked(psc); CPSW_PORT_UNLOCK(psc); } } static void cpsw_tx_watchdog(void *msc) { struct cpsw_softc *sc; sc = msc; CPSW_GLOBAL_LOCK(sc); if (sc->tx.active_queue_len == 0 || !sc->tx.running) { sc->watchdog.timer = 0; /* Nothing to do. */ } else if (sc->tx.queue_removes > sc->tx.queue_removes_at_last_tick) { sc->watchdog.timer = 0; /* Stuff done while we weren't looking. */ } else if (cpsw_tx_dequeue(sc) > 0) { sc->watchdog.timer = 0; /* We just did something. */ } else { /* There was something to do but it didn't get done. */ ++sc->watchdog.timer; if (sc->watchdog.timer > 5) { sc->watchdog.timer = 0; ++sc->watchdog.resets; cpsw_tx_watchdog_full_reset(sc); } } sc->tx.queue_removes_at_last_tick = sc->tx.queue_removes; CPSW_GLOBAL_UNLOCK(sc); /* Schedule another timeout one second from now */ callout_reset(&sc->watchdog.callout, hz, cpsw_tx_watchdog, sc); } /* * * ALE support routines. * */ static void cpsw_ale_read_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry) { cpsw_write_4(sc, CPSW_ALE_TBLCTL, idx & 1023); ale_entry[0] = cpsw_read_4(sc, CPSW_ALE_TBLW0); ale_entry[1] = cpsw_read_4(sc, CPSW_ALE_TBLW1); ale_entry[2] = cpsw_read_4(sc, CPSW_ALE_TBLW2); } static void cpsw_ale_write_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry) { cpsw_write_4(sc, CPSW_ALE_TBLW0, ale_entry[0]); cpsw_write_4(sc, CPSW_ALE_TBLW1, ale_entry[1]); cpsw_write_4(sc, CPSW_ALE_TBLW2, ale_entry[2]); cpsw_write_4(sc, CPSW_ALE_TBLCTL, 1 << 31 | (idx & 1023)); } static void cpsw_ale_remove_all_mc_entries(struct cpsw_softc *sc) { int i; uint32_t ale_entry[3]; /* First four entries are link address and broadcast. */ for (i = 10; i < CPSW_MAX_ALE_ENTRIES; i++) { cpsw_ale_read_entry(sc, i, ale_entry); if ((ALE_TYPE(ale_entry) == ALE_TYPE_ADDR || ALE_TYPE(ale_entry) == ALE_TYPE_VLAN_ADDR) && ALE_MCAST(ale_entry) == 1) { /* MCast link addr */ ale_entry[0] = ale_entry[1] = ale_entry[2] = 0; cpsw_ale_write_entry(sc, i, ale_entry); } } } static int cpsw_ale_mc_entry_set(struct cpsw_softc *sc, uint8_t portmap, int vlan, uint8_t *mac) { int free_index = -1, matching_index = -1, i; uint32_t ale_entry[3], ale_type; /* Find a matching entry or a free entry. */ for (i = 10; i < CPSW_MAX_ALE_ENTRIES; i++) { cpsw_ale_read_entry(sc, i, ale_entry); /* Entry Type[61:60] is 0 for free entry */ if (free_index < 0 && ALE_TYPE(ale_entry) == 0) free_index = i; if ((((ale_entry[1] >> 8) & 0xFF) == mac[0]) && (((ale_entry[1] >> 0) & 0xFF) == mac[1]) && (((ale_entry[0] >>24) & 0xFF) == mac[2]) && (((ale_entry[0] >>16) & 0xFF) == mac[3]) && (((ale_entry[0] >> 8) & 0xFF) == mac[4]) && (((ale_entry[0] >> 0) & 0xFF) == mac[5])) { matching_index = i; break; } } if (matching_index < 0) { if (free_index < 0) return (ENOMEM); i = free_index; } if (vlan != -1) ale_type = ALE_TYPE_VLAN_ADDR << 28 | vlan << 16; else ale_type = ALE_TYPE_ADDR << 28; /* Set MAC address */ ale_entry[0] = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; ale_entry[1] = mac[0] << 8 | mac[1]; /* Entry type[61:60] and Mcast fwd state[63:62] is fw(3). */ ale_entry[1] |= ALE_MCAST_FWD | ale_type; /* Set portmask [68:66] */ ale_entry[2] = (portmap & 7) << 2; cpsw_ale_write_entry(sc, i, ale_entry); return 0; } static void cpsw_ale_dump_table(struct cpsw_softc *sc) { int i; uint32_t ale_entry[3]; for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) { cpsw_ale_read_entry(sc, i, ale_entry); switch (ALE_TYPE(ale_entry)) { case ALE_TYPE_VLAN: printf("ALE[%4u] %08x %08x %08x ", i, ale_entry[2], ale_entry[1], ale_entry[0]); printf("type: %u ", ALE_TYPE(ale_entry)); printf("vlan: %u ", ALE_VLAN(ale_entry)); printf("untag: %u ", ALE_VLAN_UNTAG(ale_entry)); printf("reg flood: %u ", ALE_VLAN_REGFLOOD(ale_entry)); printf("unreg flood: %u ", ALE_VLAN_UNREGFLOOD(ale_entry)); printf("members: %u ", ALE_VLAN_MEMBERS(ale_entry)); printf("\n"); break; case ALE_TYPE_ADDR: case ALE_TYPE_VLAN_ADDR: printf("ALE[%4u] %08x %08x %08x ", i, ale_entry[2], ale_entry[1], ale_entry[0]); printf("type: %u ", ALE_TYPE(ale_entry)); printf("mac: %02x:%02x:%02x:%02x:%02x:%02x ", (ale_entry[1] >> 8) & 0xFF, (ale_entry[1] >> 0) & 0xFF, (ale_entry[0] >>24) & 0xFF, (ale_entry[0] >>16) & 0xFF, (ale_entry[0] >> 8) & 0xFF, (ale_entry[0] >> 0) & 0xFF); printf(ALE_MCAST(ale_entry) ? "mcast " : "ucast "); if (ALE_TYPE(ale_entry) == ALE_TYPE_VLAN_ADDR) printf("vlan: %u ", ALE_VLAN(ale_entry)); printf("port: %u ", ALE_PORTS(ale_entry)); printf("\n"); break; } } printf("\n"); } static int cpswp_ale_update_addresses(struct cpswp_softc *sc, int purge) { uint8_t *mac; uint32_t ale_entry[3], ale_type, portmask; struct ifmultiaddr *ifma; if (sc->swsc->dualemac) { ale_type = ALE_TYPE_VLAN_ADDR << 28 | sc->vlan << 16; portmask = 1 << (sc->unit + 1) | 1 << 0; } else { ale_type = ALE_TYPE_ADDR << 28; portmask = 7; } /* * Route incoming packets for our MAC address to Port 0 (host). * For simplicity, keep this entry at table index 0 for port 1 and * at index 2 for port 2 in the ALE. */ if_addr_rlock(sc->ifp); mac = LLADDR((struct sockaddr_dl *)sc->ifp->if_addr->ifa_addr); ale_entry[0] = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; ale_entry[1] = ale_type | mac[0] << 8 | mac[1]; /* addr entry + mac */ ale_entry[2] = 0; /* port = 0 */ cpsw_ale_write_entry(sc->swsc, 0 + 2 * sc->unit, ale_entry); /* Set outgoing MAC Address for slave port. */ cpsw_write_4(sc->swsc, CPSW_PORT_P_SA_HI(sc->unit + 1), mac[3] << 24 | mac[2] << 16 | mac[1] << 8 | mac[0]); cpsw_write_4(sc->swsc, CPSW_PORT_P_SA_LO(sc->unit + 1), mac[5] << 8 | mac[4]); if_addr_runlock(sc->ifp); /* Keep the broadcast address at table entry 1 (or 3). */ ale_entry[0] = 0xffffffff; /* Lower 32 bits of MAC */ /* ALE_MCAST_FWD, Addr type, upper 16 bits of Mac */ ale_entry[1] = ALE_MCAST_FWD | ale_type | 0xffff; ale_entry[2] = portmask << 2; cpsw_ale_write_entry(sc->swsc, 1 + 2 * sc->unit, ale_entry); /* SIOCDELMULTI doesn't specify the particular address being removed, so we have to remove all and rebuild. */ if (purge) cpsw_ale_remove_all_mc_entries(sc->swsc); /* Set other multicast addrs desired. */ if_maddr_rlock(sc->ifp); TAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; cpsw_ale_mc_entry_set(sc->swsc, portmask, sc->vlan, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); } if_maddr_runlock(sc->ifp); return (0); } static int cpsw_ale_update_vlan_table(struct cpsw_softc *sc, int vlan, int ports, int untag, int mcregflood, int mcunregflood) { int free_index, i, matching_index; uint32_t ale_entry[3]; free_index = matching_index = -1; /* Find a matching entry or a free entry. */ for (i = 5; i < CPSW_MAX_ALE_ENTRIES; i++) { cpsw_ale_read_entry(sc, i, ale_entry); /* Entry Type[61:60] is 0 for free entry */ if (free_index < 0 && ALE_TYPE(ale_entry) == 0) free_index = i; if (ALE_VLAN(ale_entry) == vlan) { matching_index = i; break; } } if (matching_index < 0) { if (free_index < 0) return (-1); i = free_index; } ale_entry[0] = (untag & 7) << 24 | (mcregflood & 7) << 16 | (mcunregflood & 7) << 8 | (ports & 7); ale_entry[1] = ALE_TYPE_VLAN << 28 | vlan << 16; ale_entry[2] = 0; cpsw_ale_write_entry(sc, i, ale_entry); return (0); } /* * * Statistics and Sysctls. * */ #if 0 static void cpsw_stats_dump(struct cpsw_softc *sc) { int i; uint32_t r; for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) { r = cpsw_read_4(sc, CPSW_STATS_OFFSET + cpsw_stat_sysctls[i].reg); CPSW_DEBUGF(sc, ("%s: %ju + %u = %ju", cpsw_stat_sysctls[i].oid, (intmax_t)sc->shadow_stats[i], r, (intmax_t)sc->shadow_stats[i] + r)); } } #endif static void cpsw_stats_collect(struct cpsw_softc *sc) { int i; uint32_t r; CPSW_DEBUGF(sc, ("Controller shadow statistics updated.")); for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) { r = cpsw_read_4(sc, CPSW_STATS_OFFSET + cpsw_stat_sysctls[i].reg); sc->shadow_stats[i] += r; cpsw_write_4(sc, CPSW_STATS_OFFSET + cpsw_stat_sysctls[i].reg, r); } } static int cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS) { struct cpsw_softc *sc; struct cpsw_stat *stat; uint64_t result; sc = (struct cpsw_softc *)arg1; stat = &cpsw_stat_sysctls[oidp->oid_number]; result = sc->shadow_stats[oidp->oid_number]; result += cpsw_read_4(sc, CPSW_STATS_OFFSET + stat->reg); return (sysctl_handle_64(oidp, &result, 0, req)); } static int cpsw_stat_attached(SYSCTL_HANDLER_ARGS) { struct cpsw_softc *sc; struct bintime t; unsigned result; sc = (struct cpsw_softc *)arg1; getbinuptime(&t); bintime_sub(&t, &sc->attach_uptime); result = t.sec; return (sysctl_handle_int(oidp, &result, 0, req)); } static int cpsw_stat_uptime(SYSCTL_HANDLER_ARGS) { struct cpsw_softc *swsc; struct cpswp_softc *sc; struct bintime t; unsigned result; swsc = arg1; sc = device_get_softc(swsc->port[arg2].dev); if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) { getbinuptime(&t); bintime_sub(&t, &sc->init_uptime); result = t.sec; } else result = 0; return (sysctl_handle_int(oidp, &result, 0, req)); } static void cpsw_add_queue_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node, struct cpsw_queue *queue) { struct sysctl_oid_list *parent; parent = SYSCTL_CHILDREN(node); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "totalBuffers", CTLFLAG_RD, &queue->queue_slots, 0, "Total buffers currently assigned to this queue"); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "activeBuffers", CTLFLAG_RD, &queue->active_queue_len, 0, "Buffers currently registered with hardware controller"); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "maxActiveBuffers", CTLFLAG_RD, &queue->max_active_queue_len, 0, "Max value of activeBuffers since last driver reset"); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "availBuffers", CTLFLAG_RD, &queue->avail_queue_len, 0, "Buffers allocated to this queue but not currently " "registered with hardware controller"); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "maxAvailBuffers", CTLFLAG_RD, &queue->max_avail_queue_len, 0, "Max value of availBuffers since last driver reset"); SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "totalEnqueued", CTLFLAG_RD, &queue->queue_adds, 0, "Total buffers added to queue"); SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "totalDequeued", CTLFLAG_RD, &queue->queue_removes, 0, "Total buffers removed from queue"); SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "longestChain", CTLFLAG_RD, &queue->longest_chain, 0, "Max buffers used for a single packet"); } static void cpsw_add_watchdog_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node, struct cpsw_softc *sc) { struct sysctl_oid_list *parent; parent = SYSCTL_CHILDREN(node); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "resets", CTLFLAG_RD, &sc->watchdog.resets, 0, "Total number of watchdog resets"); } static void cpsw_add_sysctls(struct cpsw_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid *stats_node, *queue_node, *node; struct sysctl_oid_list *parent, *stats_parent, *queue_parent; struct sysctl_oid_list *ports_parent, *port_parent; char port[16]; int i; ctx = device_get_sysctl_ctx(sc->dev); parent = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "debug", CTLFLAG_RW, &sc->debug, 0, "Enable switch debug messages"); SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "attachedSecs", CTLTYPE_UINT | CTLFLAG_RD, sc, 0, cpsw_stat_attached, "IU", "Time since driver attach"); node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "ports", CTLFLAG_RD, NULL, "CPSW Ports Statistics"); ports_parent = SYSCTL_CHILDREN(node); for (i = 0; i < CPSW_PORTS; i++) { if (!sc->dualemac && i != sc->active_slave) continue; port[0] = '0' + i; port[1] = '\0'; node = SYSCTL_ADD_NODE(ctx, ports_parent, OID_AUTO, port, CTLFLAG_RD, NULL, "CPSW Port Statistics"); port_parent = SYSCTL_CHILDREN(node); SYSCTL_ADD_PROC(ctx, port_parent, OID_AUTO, "uptime", CTLTYPE_UINT | CTLFLAG_RD, sc, i, cpsw_stat_uptime, "IU", "Seconds since driver init"); } stats_node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "stats", CTLFLAG_RD, NULL, "CPSW Statistics"); stats_parent = SYSCTL_CHILDREN(stats_node); for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) { SYSCTL_ADD_PROC(ctx, stats_parent, i, cpsw_stat_sysctls[i].oid, CTLTYPE_U64 | CTLFLAG_RD, sc, 0, cpsw_stats_sysctl, "IU", cpsw_stat_sysctls[i].oid); } queue_node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "queue", CTLFLAG_RD, NULL, "CPSW Queue Statistics"); queue_parent = SYSCTL_CHILDREN(queue_node); node = SYSCTL_ADD_NODE(ctx, queue_parent, OID_AUTO, "tx", CTLFLAG_RD, NULL, "TX Queue Statistics"); cpsw_add_queue_sysctls(ctx, node, &sc->tx); node = SYSCTL_ADD_NODE(ctx, queue_parent, OID_AUTO, "rx", CTLFLAG_RD, NULL, "RX Queue Statistics"); cpsw_add_queue_sysctls(ctx, node, &sc->rx); node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "watchdog", CTLFLAG_RD, NULL, "Watchdog Statistics"); cpsw_add_watchdog_sysctls(ctx, node, sc); } Index: head/sys/arm/xscale/ixp425/ixp425.c =================================================================== --- head/sys/arm/xscale/ixp425/ixp425.c (revision 298351) +++ head/sys/arm/xscale/ixp425/ixp425.c (revision 298352) @@ -1,696 +1,696 @@ /* $NetBSD: ixp425.c,v 1.10 2005/12/11 12:16:51 christos Exp $ */ /* * Copyright (c) 2003 * Ichiro FUKUHARA . * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Ichiro FUKUHARA. * 4. The name of the company nor the name of the author may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY ICHIRO FUKUHARA ``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 ICHIRO FUKUHARA OR THE VOICES IN HIS HEAD 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 __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #define _ARM32_BUS_DMA_PRIVATE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include volatile uint32_t intr_enabled; uint32_t intr_steer = 0; /* ixp43x et. al have +32 IRQ's */ volatile uint32_t intr_enabled2; uint32_t intr_steer2 = 0; struct ixp425_softc *ixp425_softc = NULL; struct mtx ixp425_gpio_mtx; static int ixp425_probe(device_t); static void ixp425_identify(driver_t *, device_t); static int ixp425_attach(device_t); /* * Return a mask of the "fuse" bits that identify * which h/w features are present. * NB: assumes the expansion bus is mapped. */ uint32_t ixp4xx_read_feature_bits(void) { uint32_t bits = ~IXPREG(IXP425_EXP_VBASE + EXP_FCTRL_OFFSET); bits &= ~EXP_FCTRL_RESVD; if (!cpu_is_ixp46x()) bits &= ~EXP_FCTRL_IXP46X_ONLY; return bits; } void ixp4xx_write_feature_bits(uint32_t v) { IXPREG(IXP425_EXP_VBASE + EXP_FCTRL_OFFSET) = ~v; } struct arm32_dma_range * bus_dma_get_range(void) { return (NULL); } int bus_dma_get_range_nb(void) { return (0); } static const uint8_t int2gpio[32] __attribute__ ((aligned(32))) = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* INT#0 -> INT#5 */ 0x00, 0x01, /* GPIO#0 -> GPIO#1 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* INT#8 -> INT#13 */ 0xff, 0xff, 0xff, 0xff, 0xff, /* INT#14 -> INT#18 */ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* GPIO#2 -> GPIO#7 */ 0x08, 0x09, 0x0a, 0x0b, 0x0c, /* GPIO#8 -> GPIO#12 */ 0xff, 0xff /* INT#30 -> INT#31 */ }; static __inline uint32_t ixp425_irq2gpio_bit(int irq) { return (1U << int2gpio[irq]); } #ifdef DDB #include DB_SHOW_COMMAND(gpio, db_show_gpio) { static const char *itype[8] = { [GPIO_TYPE_ACT_HIGH] = "act-high", [GPIO_TYPE_ACT_LOW] = "act-low", [GPIO_TYPE_EDG_RISING] = "edge-rising", [GPIO_TYPE_EDG_FALLING] = "edge-falling", [GPIO_TYPE_TRANSITIONAL]= "transitional", [5] = "type-5", [6] = "type-6", [7] = "type-7" }; uint32_t gpoutr = GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPOUTR); uint32_t gpoer = GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPOER); uint32_t gpinr = GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPINR); uint32_t gpit1r = GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPIT1R); uint32_t gpit2r = GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPIT2R); int i, j; db_printf("GPOUTR %08x GPINR %08x GPOER %08x GPISR %08x\n", gpoutr, gpinr, gpoer, GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPISR)); db_printf("GPIT1R %08x GPIT2R %08x GPCLKR %08x\n", gpit1r, gpit2r, GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPCLKR)); for (i = 0; i < 16; i++) { db_printf("[%2d] out %u in %u %-3s", i, (gpoutr>>i)&1, (gpinr>>i)&1, (gpoer>>i)&1 ? "in" : "out"); for (j = 0; j < 32; j++) if (int2gpio[j] == i) { db_printf(" irq %2u %s", j, itype[ (((i & 8) ? gpit2r : gpit1r) >> (3*(i&7))) & 7]); break; } db_printf("\n"); } } #endif void ixp425_set_gpio(struct ixp425_softc *sc, int pin, int type) { uint32_t gpiotr = GPIO_CONF_READ_4(sc, GPIO_TYPE_REG(pin)); IXP4XX_GPIO_LOCK(); /* clear interrupt type */ GPIO_CONF_WRITE_4(sc, GPIO_TYPE_REG(pin), gpiotr &~ GPIO_TYPE(pin, GPIO_TYPE_MASK)); /* clear any pending interrupt */ GPIO_CONF_WRITE_4(sc, IXP425_GPIO_GPISR, (1<> last; for (; mask != 0; mask >>= 1, last++) { if (mask & 1) return last; } last = 32; } if (cpu_is_ixp43x()) { mask = ixp435_irq_read() >> (32-last); for (; mask != 0; mask >>= 1, last++) { if (mask & 1) return last; } } return -1; } void cpu_reset(void) { bus_space_write_4(&ixp425_bs_tag, IXP425_TIMER_VBASE, IXP425_OST_WDOG_KEY, OST_WDOG_KEY_MAJICK); bus_space_write_4(&ixp425_bs_tag, IXP425_TIMER_VBASE, IXP425_OST_WDOG, 0); bus_space_write_4(&ixp425_bs_tag, IXP425_TIMER_VBASE, IXP425_OST_WDOG_ENAB, OST_WDOG_ENAB_RST_ENA | OST_WDOG_ENAB_CNT_ENA); printf("Reset failed!\n"); for(;;); } static void ixp425_identify(driver_t *driver, device_t parent) { BUS_ADD_CHILD(parent, 0, "ixp", 0); } static int ixp425_probe(device_t dev) { device_set_desc(dev, "Intel IXP4XX"); return (0); } static int ixp425_attach(device_t dev) { struct ixp425_softc *sc; device_printf(dev, "%b\n", ixp4xx_read_feature_bits(), EXP_FCTRL_BITS); sc = device_get_softc(dev); sc->sc_iot = &ixp425_bs_tag; KASSERT(ixp425_softc == NULL, ("%s called twice?", __func__)); ixp425_softc = sc; intr_enabled = 0; ixp425_set_intrmask(); ixp425_set_intrsteer(); if (cpu_is_ixp43x()) { intr_enabled2 = 0; ixp435_set_intrmask(); ixp435_set_intrsteer(); } arm_post_filter = ixp425_post_filter; mtx_init(&ixp425_gpio_mtx, "gpio", NULL, MTX_DEF); if (bus_space_map(sc->sc_iot, IXP425_GPIO_HWBASE, IXP425_GPIO_SIZE, 0, &sc->sc_gpio_ioh)) panic("%s: unable to map GPIO registers", __func__); if (bus_space_map(sc->sc_iot, IXP425_EXP_HWBASE, IXP425_EXP_SIZE, 0, &sc->sc_exp_ioh)) panic("%s: unable to map Expansion Bus registers", __func__); /* XXX belongs in platform init */ if (cpu_is_ixp43x()) cambria_exp_bus_init(sc); if (bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 0xffffffff, 0xff, 0xffffffff, 0, NULL, NULL, &sc->sc_dmat)) panic("%s: failed to create dma tag", __func__); sc->sc_irq_rman.rm_type = RMAN_ARRAY; sc->sc_irq_rman.rm_descr = "IXP4XX IRQs"; if (rman_init(&sc->sc_irq_rman) != 0 || rman_manage_region(&sc->sc_irq_rman, 0, cpu_is_ixp43x() ? 63 : 31) != 0) panic("%s: failed to set up IRQ rman", __func__); sc->sc_mem_rman.rm_type = RMAN_ARRAY; sc->sc_mem_rman.rm_descr = "IXP4XX Memory"; if (rman_init(&sc->sc_mem_rman) != 0 || rman_manage_region(&sc->sc_mem_rman, 0, ~0) != 0) panic("%s: failed to set up memory rman", __func__); BUS_ADD_CHILD(dev, 0, "pcib", 0); BUS_ADD_CHILD(dev, 0, "ixpclk", 0); BUS_ADD_CHILD(dev, 0, "ixpiic", 0); /* XXX move to hints? */ BUS_ADD_CHILD(dev, 0, "ixpwdog", 0); /* attach wired devices via hints */ bus_enumerate_hinted_children(dev); bus_generic_probe(dev); bus_generic_attach(dev); return (0); } static void ixp425_hinted_child(device_t bus, const char *dname, int dunit) { device_t child; struct ixp425_ivar *ivar; child = BUS_ADD_CHILD(bus, 0, dname, dunit); ivar = IXP425_IVAR(child); resource_int_value(dname, dunit, "addr", &ivar->addr); resource_int_value(dname, dunit, "irq", &ivar->irq); } static device_t ixp425_add_child(device_t dev, u_int order, const char *name, int unit) { device_t child; struct ixp425_ivar *ivar; child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return NULL; ivar = malloc(sizeof(struct ixp425_ivar), M_DEVBUF, M_NOWAIT); if (ivar == NULL) { device_delete_child(dev, child); return NULL; } ivar->addr = 0; ivar->irq = -1; device_set_ivars(child, ivar); return child; } static int ixp425_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) { struct ixp425_ivar *ivar = IXP425_IVAR(child); switch (which) { case IXP425_IVAR_ADDR: if (ivar->addr != 0) { *(uint32_t *)result = ivar->addr; return 0; } break; case IXP425_IVAR_IRQ: if (ivar->irq != -1) { *(int *)result = ivar->irq; return 0; } break; } return EINVAL; } /* * NB: This table handles P->V translations for regions setup with * static mappings in initarm. This is used solely for calls to * bus_alloc_resource_any; anything done with bus_space_map is * handled elsewhere and does not require an entry here. * * XXX this table is also used by uart_cpu_getdev via getvbase * (hence the public api) */ struct hwvtrans { uint32_t hwbase; uint32_t size; uint32_t vbase; int isa4x; /* XXX needs special bus space tag */ int isslow; /* XXX needs special bus space tag */ }; static const struct hwvtrans * gethwvtrans(uint32_t hwbase, uint32_t size) { static const struct hwvtrans hwvtrans[] = { /* NB: needed only for uart_cpu_getdev */ { .hwbase = IXP425_UART0_HWBASE, .size = IXP425_REG_SIZE, .vbase = IXP425_UART0_VBASE, .isa4x = 1 }, { .hwbase = IXP425_UART1_HWBASE, .size = IXP425_REG_SIZE, .vbase = IXP425_UART1_VBASE, .isa4x = 1 }, { .hwbase = IXP425_PCI_HWBASE, .size = IXP425_PCI_SIZE, .vbase = IXP425_PCI_VBASE }, { .hwbase = IXP425_PCI_MEM_HWBASE, .size = IXP425_PCI_MEM_SIZE, .vbase = IXP425_PCI_MEM_VBASE }, { .hwbase = IXP425_EXP_BUS_CS0_HWBASE, .size = IXP425_EXP_BUS_CS0_SIZE, .vbase = IXP425_EXP_BUS_CS0_VBASE }, /* NB: needed for ixp435 ehci controllers */ { .hwbase = IXP435_USB1_HWBASE, .size = IXP435_USB1_SIZE, .vbase = IXP435_USB1_VBASE }, { .hwbase = IXP435_USB2_HWBASE, .size = IXP435_USB2_SIZE, .vbase = IXP435_USB2_VBASE }, { .hwbase = CAMBRIA_GPS_HWBASE, .size = CAMBRIA_GPS_SIZE, .vbase = CAMBRIA_GPS_VBASE, .isslow = 1 }, { .hwbase = CAMBRIA_RS485_HWBASE, .size = CAMBRIA_RS485_SIZE, .vbase = CAMBRIA_RS485_VBASE, .isslow = 1 }, }; int i; - for (i = 0; i < sizeof hwvtrans / sizeof *hwvtrans; i++) { + for (i = 0; i < nitems(hwvtrans); i++) { if (hwbase >= hwvtrans[i].hwbase && hwbase + size <= hwvtrans[i].hwbase + hwvtrans[i].size) return &hwvtrans[i]; } return NULL; } /* XXX for uart_cpu_getdev */ int getvbase(uint32_t hwbase, uint32_t size, uint32_t *vbase) { const struct hwvtrans *hw; hw = gethwvtrans(hwbase, size); if (hw == NULL) return (ENOENT); *vbase = hwbase - hw->hwbase + hw->vbase; return (0); } static struct resource * ixp425_alloc_resource(device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct ixp425_softc *sc = device_get_softc(dev); const struct hwvtrans *vtrans; struct resource *rv; uint32_t addr; int needactivate = flags & RF_ACTIVE; int irq; flags &= ~RF_ACTIVE; switch (type) { case SYS_RES_IRQ: /* override per hints */ if (BUS_READ_IVAR(dev, child, IXP425_IVAR_IRQ, &irq) == 0) start = end = irq; rv = rman_reserve_resource(&sc->sc_irq_rman, start, end, count, flags, child); if (rv != NULL) rman_set_rid(rv, *rid); break; case SYS_RES_MEMORY: /* override per hints */ if (BUS_READ_IVAR(dev, child, IXP425_IVAR_ADDR, &addr) == 0) { start = addr; /* XXX use nominal window to check for mapping */ vtrans = gethwvtrans(start, 0x1000); if (vtrans != NULL) { /* * Assign the entire mapped region; this may * not be correct but without more info from * the caller we cannot tell. */ end = start + vtrans->size - (start - vtrans->hwbase); if (bootverbose) device_printf(child, "%s: assign 0x%jx:0x%jx%s\n", __func__, start, end - start, vtrans->isa4x ? " A4X" : vtrans->isslow ? " SLOW" : ""); } } else vtrans = gethwvtrans(start, end - start); if (vtrans == NULL) { /* likely means above table needs to be updated */ device_printf(child, "%s: no mapping for 0x%jx:0x%jx\n", __func__, start, end - start); return NULL; } rv = rman_reserve_resource(&sc->sc_mem_rman, start, end, end - start, flags, child); if (rv == NULL) { device_printf(child, "%s: cannot reserve 0x%jx:0x%jx\n", __func__, start, end - start); return NULL; } rman_set_rid(rv, *rid); break; default: rv = NULL; break; } if (rv != NULL && needactivate) { if (bus_activate_resource(child, type, *rid, rv)) { rman_release_resource(rv); return (NULL); } } return (rv); } static int ixp425_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { /* NB: no private resources, just release */ return rman_release_resource(r); } static int ixp425_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct ixp425_softc *sc = device_get_softc(dev); const struct hwvtrans *vtrans; if (type == SYS_RES_MEMORY) { vtrans = gethwvtrans(rman_get_start(r), rman_get_size(r)); if (vtrans == NULL) { /* NB: should not happen */ device_printf(child, "%s: no mapping for 0x%jx:0x%jx\n", __func__, rman_get_start(r), rman_get_size(r)); return (ENOENT); } if (vtrans->isa4x) rman_set_bustag(r, &ixp425_a4x_bs_tag); else if (vtrans->isslow) rman_set_bustag(r, &cambria_exp_bs_tag); else rman_set_bustag(r, sc->sc_iot); rman_set_bushandle(r, vtrans->vbase); } return (rman_activate_resource(r)); } static int ixp425_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { /* NB: no private resources, just deactive */ return (rman_deactivate_resource(r)); } static __inline void get_masks(struct resource *res, uint32_t *mask, uint32_t *mask2) { int i; *mask = 0; for (i = rman_get_start(res); i < 32 && i <= rman_get_end(res); i++) *mask |= 1 << i; *mask2 = 0; for (; i <= rman_get_end(res); i++) *mask2 |= 1 << (i - 32); } static __inline void update_masks(uint32_t mask, uint32_t mask2) { intr_enabled = mask; ixp425_set_intrmask(); if (cpu_is_ixp43x()) { intr_enabled2 = mask2; ixp435_set_intrmask(); } } static int ixp425_setup_intr(device_t dev, device_t child, struct resource *res, int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep) { uint32_t mask, mask2; int error; error = BUS_SETUP_INTR(device_get_parent(dev), child, res, flags, filt, intr, arg, cookiep); if (error) return (error); get_masks(res, &mask, &mask2); update_masks(intr_enabled | mask, intr_enabled2 | mask2); return (0); } static int ixp425_teardown_intr(device_t dev, device_t child, struct resource *res, void *cookie) { uint32_t mask, mask2; get_masks(res, &mask, &mask2); update_masks(intr_enabled &~ mask, intr_enabled2 &~ mask2); return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, res, cookie)); } static device_method_t ixp425_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ixp425_probe), DEVMETHOD(device_attach, ixp425_attach), DEVMETHOD(device_identify, ixp425_identify), /* Bus interface */ DEVMETHOD(bus_add_child, ixp425_add_child), DEVMETHOD(bus_hinted_child, ixp425_hinted_child), DEVMETHOD(bus_read_ivar, ixp425_read_ivar), DEVMETHOD(bus_alloc_resource, ixp425_alloc_resource), DEVMETHOD(bus_release_resource, ixp425_release_resource), DEVMETHOD(bus_activate_resource, ixp425_activate_resource), DEVMETHOD(bus_deactivate_resource, ixp425_deactivate_resource), DEVMETHOD(bus_setup_intr, ixp425_setup_intr), DEVMETHOD(bus_teardown_intr, ixp425_teardown_intr), {0, 0}, }; static driver_t ixp425_driver = { "ixp", ixp425_methods, sizeof(struct ixp425_softc), }; static devclass_t ixp425_devclass; DRIVER_MODULE(ixp, nexus, ixp425_driver, ixp425_devclass, 0, 0); Index: head/sys/mips/mips/db_interface.c =================================================================== --- head/sys/mips/mips/db_interface.c (revision 298351) +++ head/sys/mips/mips/db_interface.c (revision 298352) @@ -1,348 +1,348 @@ /* $OpenBSD: db_machdep.c,v 1.2 1998/09/15 10:50:13 pefo Exp $ */ /*- * Copyright (c) 1998 Per Fogelstrom, Opsycon AB * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed under OpenBSD by * Per Fogelstrom, Opsycon AB, Sweden. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * JNPR: db_interface.c,v 1.6.2.1 2007/08/29 12:24:49 girish */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static db_varfcn_t db_frame; #define DB_OFFSET(x) (db_expr_t *)offsetof(struct trapframe, x) struct db_variable db_regs[] = { { "at", DB_OFFSET(ast), db_frame }, { "v0", DB_OFFSET(v0), db_frame }, { "v1", DB_OFFSET(v1), db_frame }, { "a0", DB_OFFSET(a0), db_frame }, { "a1", DB_OFFSET(a1), db_frame }, { "a2", DB_OFFSET(a2), db_frame }, { "a3", DB_OFFSET(a3), db_frame }, #if defined(__mips_n32) || defined(__mips_n64) { "a4", DB_OFFSET(a4), db_frame }, { "a5", DB_OFFSET(a5), db_frame }, { "a6", DB_OFFSET(a6), db_frame }, { "a7", DB_OFFSET(a7), db_frame }, { "t0", DB_OFFSET(t0), db_frame }, { "t1", DB_OFFSET(t1), db_frame }, { "t2", DB_OFFSET(t2), db_frame }, { "t3", DB_OFFSET(t3), db_frame }, #else { "t0", DB_OFFSET(t0), db_frame }, { "t1", DB_OFFSET(t1), db_frame }, { "t2", DB_OFFSET(t2), db_frame }, { "t3", DB_OFFSET(t3), db_frame }, { "t4", DB_OFFSET(t4), db_frame }, { "t5", DB_OFFSET(t5), db_frame }, { "t6", DB_OFFSET(t6), db_frame }, { "t7", DB_OFFSET(t7), db_frame }, #endif { "s0", DB_OFFSET(s0), db_frame }, { "s1", DB_OFFSET(s1), db_frame }, { "s2", DB_OFFSET(s2), db_frame }, { "s3", DB_OFFSET(s3), db_frame }, { "s4", DB_OFFSET(s4), db_frame }, { "s5", DB_OFFSET(s5), db_frame }, { "s6", DB_OFFSET(s6), db_frame }, { "s7", DB_OFFSET(s7), db_frame }, { "t8", DB_OFFSET(t8), db_frame }, { "t9", DB_OFFSET(t9), db_frame }, { "k0", DB_OFFSET(k0), db_frame }, { "k1", DB_OFFSET(k1), db_frame }, { "gp", DB_OFFSET(gp), db_frame }, { "sp", DB_OFFSET(sp), db_frame }, { "s8", DB_OFFSET(s8), db_frame }, { "ra", DB_OFFSET(ra), db_frame }, { "sr", DB_OFFSET(sr), db_frame }, { "lo", DB_OFFSET(mullo), db_frame }, { "hi", DB_OFFSET(mulhi), db_frame }, { "bad", DB_OFFSET(badvaddr), db_frame }, { "cs", DB_OFFSET(cause), db_frame }, { "pc", DB_OFFSET(pc), db_frame }, }; -struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); +struct db_variable *db_eregs = db_regs + nitems(db_regs); int (*do_db_log_stack_trace_cmd)(char *); static int db_frame(struct db_variable *vp, db_expr_t *valuep, int op) { register_t *reg; if (kdb_frame == NULL) return (0); reg = (register_t *)((uintptr_t)kdb_frame + (size_t)(intptr_t)vp->valuep); if (op == DB_VAR_GET) *valuep = *reg; else *reg = *valuep; return (1); } int db_read_bytes(vm_offset_t addr, size_t size, char *data) { jmp_buf jb; void *prev_jb; int ret; prev_jb = kdb_jmpbuf(jb); ret = setjmp(jb); if (ret == 0) { /* * 'addr' could be a memory-mapped I/O address. Try to * do atomic load/store in unit of size requested. */ if ((size == 2 || size == 4 || size == 8) && ((addr & (size -1)) == 0) && (((vm_offset_t)data & (size -1)) == 0)) { switch (size) { case 2: *(uint16_t *)data = *(uint16_t *)addr; break; case 4: *(uint32_t *)data = *(uint32_t *)addr; break; case 8: atomic_load_64((volatile u_int64_t *)addr, (u_int64_t *)data); break; } } else { char *src; src = (char *)addr; while (size-- > 0) *data++ = *src++; } } (void)kdb_jmpbuf(prev_jb); return (ret); } int db_write_bytes(vm_offset_t addr, size_t size, char *data) { int ret; jmp_buf jb; void *prev_jb; prev_jb = kdb_jmpbuf(jb); ret = setjmp(jb); if (ret == 0) { /* * 'addr' could be a memory-mapped I/O address. Try to * do atomic load/store in unit of size requested. */ if ((size == 2 || size == 4 || size == 8) && ((addr & (size -1)) == 0) && (((vm_offset_t)data & (size -1)) == 0)) { switch (size) { case 2: *(uint16_t *)addr = *(uint16_t *)data; break; case 4: *(uint32_t *)addr = *(uint32_t *)data; break; case 8: atomic_store_64((volatile u_int64_t *)addr, (u_int64_t *)data); break; } } else { char *dst; size_t len = size; dst = (char *)addr; while (len-- > 0) *dst++ = *data++; } mips_icache_sync_range((db_addr_t) addr, size); mips_dcache_wbinv_range((db_addr_t) addr, size); } (void)kdb_jmpbuf(prev_jb); return (ret); } /* * To do a single step ddb needs to know the next address * that we will get to. It means that we need to find out * both the address for a branch taken and for not taken, NOT! :-) * MipsEmulateBranch will do the job to find out _exactly_ which * address we will end up at so the 'dual bp' method is not * requiered. */ db_addr_t next_instr_address(db_addr_t pc, boolean_t bd) { db_addr_t next; next = (db_addr_t)MipsEmulateBranch(kdb_frame, pc, 0, 0); return (next); } /* * Decode instruction and figure out type. */ int db_inst_type(int ins) { InstFmt inst; int ityp = 0; inst.word = ins; switch ((int)inst.JType.op) { case OP_SPECIAL: switch ((int)inst.RType.func) { case OP_JR: ityp = IT_BRANCH; break; case OP_JALR: case OP_SYSCALL: ityp = IT_CALL; break; } break; case OP_BCOND: switch ((int)inst.IType.rt) { case OP_BLTZ: case OP_BLTZL: case OP_BGEZ: case OP_BGEZL: ityp = IT_BRANCH; break; case OP_BLTZAL: case OP_BLTZALL: case OP_BGEZAL: case OP_BGEZALL: ityp = IT_CALL; break; } break; case OP_JAL: ityp = IT_CALL; break; case OP_J: case OP_BEQ: case OP_BEQL: case OP_BNE: case OP_BNEL: case OP_BLEZ: case OP_BLEZL: case OP_BGTZ: case OP_BGTZL: ityp = IT_BRANCH; break; case OP_COP1: switch (inst.RType.rs) { case OP_BCx: case OP_BCy: ityp = IT_BRANCH; break; } break; case OP_LB: case OP_LH: case OP_LW: case OP_LD: case OP_LBU: case OP_LHU: case OP_LWU: case OP_LWC1: ityp = IT_LOAD; break; case OP_SB: case OP_SH: case OP_SW: case OP_SD: case OP_SWC1: ityp = IT_STORE; break; } return (ityp); } /* * Return the next pc if the given branch is taken. * MachEmulateBranch() runs analysis for branch delay slot. */ db_addr_t branch_taken(int inst, db_addr_t pc) { db_addr_t ra; register_t fpucsr; /* TBD: when is fsr set */ fpucsr = (curthread) ? curthread->td_pcb->pcb_regs.fsr : 0; ra = (db_addr_t)MipsEmulateBranch(kdb_frame, pc, fpucsr, 0); return (ra); } Index: head/sys/pc98/cbus/gdc.c =================================================================== --- head/sys/pc98/cbus/gdc.c (revision 298351) +++ head/sys/pc98/cbus/gdc.c (revision 298352) @@ -1,1487 +1,1487 @@ /*- * Copyright (c) 1999 FreeBSD(98) port team. * 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 as * the first lines of this file unmodified. * 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * $FreeBSD$ */ #include "opt_gdc.h" #include "opt_fb.h" #include "opt_syscons.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef LINE30 #include #endif #include #include #define TEXT_GDC 0x60 #define GRAPHIC_GDC 0xa0 #define ROW 25 #define COL 80 #define DRIVER_NAME "gdc" /* cdev driver declaration */ #define GDC_UNIT(dev) dev2unit(dev) #define GDC_MKMINOR(unit) (unit) typedef struct gdc_softc { video_adapter_t *adp; struct resource *res_tgdc, *res_ggdc; struct resource *res_egc, *res_pegc, *res_grcg, *res_kcg; struct resource *res_tmem, *res_gmem1, *res_gmem2; #ifdef FB_INSTALL_CDEV genfb_softc_t gensc; #endif } gdc_softc_t; #define GDC_SOFTC(unit) \ ((gdc_softc_t *)devclass_get_softc(gdc_devclass, unit)) static bus_addr_t gdc_iat[] = {0, 2, 4, 6, 8, 10, 12, 14}; static devclass_t gdc_devclass; static int gdc_probe_unit(int unit, gdc_softc_t *sc, int flags); static int gdc_attach_unit(int unit, gdc_softc_t *sc, int flags); static int gdc_alloc_resource(device_t dev); static int gdc_release_resource(device_t dev); #ifdef FB_INSTALL_CDEV static d_open_t gdcopen; static d_close_t gdcclose; static d_read_t gdcread; static d_write_t gdcwrite; static d_ioctl_t gdcioctl; static d_mmap_t gdcmmap; static struct cdevsw gdc_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = gdcopen, .d_close = gdcclose, .d_read = gdcread, .d_write = gdcwrite, .d_ioctl = gdcioctl, .d_mmap = gdcmmap, .d_name = DRIVER_NAME, }; #endif /* FB_INSTALL_CDEV */ static void gdc_identify(driver_t *driver, device_t parent) { BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, DRIVER_NAME, 0); } static int gdcprobe(device_t dev) { int error; /* Check isapnp ids */ if (isa_get_vendorid(dev)) return (ENXIO); device_set_desc(dev, "Generic GDC"); error = gdc_alloc_resource(dev); if (error) return (error); error = gdc_probe_unit(device_get_unit(dev), device_get_softc(dev), device_get_flags(dev)); gdc_release_resource(dev); return (error); } static int gdc_attach(device_t dev) { gdc_softc_t *sc; int error; error = gdc_alloc_resource(dev); if (error) return (error); sc = device_get_softc(dev); error = gdc_attach_unit(device_get_unit(dev), sc, device_get_flags(dev)); if (error) { gdc_release_resource(dev); return error; } #ifdef FB_INSTALL_CDEV /* attach a virtual frame buffer device */ error = fb_attach(GDC_MKMINOR(device_get_unit(dev)), sc->adp, &gdc_cdevsw); if (error) { gdc_release_resource(dev); return error; } #endif /* FB_INSTALL_CDEV */ if (bootverbose) vidd_diag(sc->adp, bootverbose); return 0; } static int gdc_probe_unit(int unit, gdc_softc_t *sc, int flags) { video_switch_t *sw; sw = vid_get_switch(DRIVER_NAME); if (sw == NULL) return ENXIO; return (*sw->probe)(unit, &sc->adp, NULL, flags); } static int gdc_attach_unit(int unit, gdc_softc_t *sc, int flags) { video_switch_t *sw; sw = vid_get_switch(DRIVER_NAME); if (sw == NULL) return ENXIO; return (*sw->init)(unit, sc->adp, flags); } static int gdc_alloc_resource(device_t dev) { int rid; gdc_softc_t *sc; sc = device_get_softc(dev); /* TEXT GDC */ rid = 0; bus_set_resource(dev, SYS_RES_IOPORT, rid, TEXT_GDC, 1); sc->res_tgdc = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, gdc_iat, 8, RF_ACTIVE); if (sc->res_tgdc == NULL) { gdc_release_resource(dev); return (ENXIO); } isa_load_resourcev(sc->res_tgdc, gdc_iat, 8); /* GRAPHIC GDC */ rid = 8; bus_set_resource(dev, SYS_RES_IOPORT, rid, GRAPHIC_GDC, 1); sc->res_ggdc = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, gdc_iat, 8, RF_ACTIVE); if (sc->res_ggdc == NULL) { gdc_release_resource(dev); return (ENXIO); } isa_load_resourcev(sc->res_ggdc, gdc_iat, 8); /* EGC */ rid = 16; bus_set_resource(dev, SYS_RES_IOPORT, rid, 0x4a0, 1); sc->res_egc = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, gdc_iat, 8, RF_ACTIVE); if (sc->res_egc == NULL) { gdc_release_resource(dev); return (ENXIO); } isa_load_resourcev(sc->res_egc, gdc_iat, 8); /* PEGC */ rid = 24; bus_set_resource(dev, SYS_RES_IOPORT, rid, 0x9a0, 1); sc->res_pegc = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, gdc_iat, 8, RF_ACTIVE); if (sc->res_pegc == NULL) { gdc_release_resource(dev); return (ENXIO); } isa_load_resourcev(sc->res_pegc, gdc_iat, 8); /* CRTC/GRCG */ rid = 32; bus_set_resource(dev, SYS_RES_IOPORT, rid, 0x70, 1); sc->res_grcg = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, gdc_iat, 8, RF_ACTIVE); if (sc->res_grcg == NULL) { gdc_release_resource(dev); return (ENXIO); } isa_load_resourcev(sc->res_grcg, gdc_iat, 8); /* KCG */ rid = 40; bus_set_resource(dev, SYS_RES_IOPORT, rid, 0xa1, 1); sc->res_kcg = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, gdc_iat, 8, RF_ACTIVE); if (sc->res_kcg == NULL) { gdc_release_resource(dev); return (ENXIO); } isa_load_resourcev(sc->res_kcg, gdc_iat, 8); /* TEXT Memory */ rid = 0; sc->res_tmem = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0xa0000, 0xa4fff, 0x5000, RF_ACTIVE); if (sc->res_tmem == NULL) { gdc_release_resource(dev); return (ENXIO); } /* GRAPHIC Memory */ rid = 1; sc->res_gmem1 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0xa8000, 0xbffff, 0x18000, RF_ACTIVE); if (sc->res_gmem1 == NULL) { gdc_release_resource(dev); return (ENXIO); } rid = 2; sc->res_gmem2 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0xe0000, 0xe7fff, 0x8000, RF_ACTIVE); if (sc->res_gmem2 == NULL) { gdc_release_resource(dev); return (ENXIO); } return (0); } static int gdc_release_resource(device_t dev) { gdc_softc_t *sc; sc = device_get_softc(dev); if (sc->res_tgdc) bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->res_tgdc); if (sc->res_ggdc) bus_release_resource(dev, SYS_RES_IOPORT, 8, sc->res_ggdc); if (sc->res_egc) bus_release_resource(dev, SYS_RES_IOPORT, 16, sc->res_egc); if (sc->res_pegc) bus_release_resource(dev, SYS_RES_IOPORT, 24, sc->res_pegc); if (sc->res_grcg) bus_release_resource(dev, SYS_RES_IOPORT, 32, sc->res_grcg); if (sc->res_kcg) bus_release_resource(dev, SYS_RES_IOPORT, 40, sc->res_kcg); if (sc->res_tmem) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->res_tmem); if (sc->res_gmem1) bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->res_gmem1); if (sc->res_gmem2) bus_release_resource(dev, SYS_RES_MEMORY, 2, sc->res_gmem2); return (0); } /* cdev driver functions */ #ifdef FB_INSTALL_CDEV static int gdcopen(struct cdev *dev, int flag, int mode, struct thread *td) { gdc_softc_t *sc; sc = GDC_SOFTC(GDC_UNIT(dev)); if (sc == NULL) return ENXIO; if (mode & (O_CREAT | O_APPEND | O_TRUNC)) return ENODEV; return genfbopen(&sc->gensc, sc->adp, flag, mode, td); } static int gdcclose(struct cdev *dev, int flag, int mode, struct thread *td) { gdc_softc_t *sc; sc = GDC_SOFTC(GDC_UNIT(dev)); return genfbclose(&sc->gensc, sc->adp, flag, mode, td); } static int gdcread(struct cdev *dev, struct uio *uio, int flag) { gdc_softc_t *sc; sc = GDC_SOFTC(GDC_UNIT(dev)); return genfbread(&sc->gensc, sc->adp, uio, flag); } static int gdcwrite(struct cdev *dev, struct uio *uio, int flag) { gdc_softc_t *sc; sc = GDC_SOFTC(GDC_UNIT(dev)); return genfbread(&sc->gensc, sc->adp, uio, flag); } static int gdcioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) { gdc_softc_t *sc; sc = GDC_SOFTC(GDC_UNIT(dev)); return genfbioctl(&sc->gensc, sc->adp, cmd, arg, flag, td); } static int gdcmmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int prot, vm_memattr_t *memattr) { gdc_softc_t *sc; sc = GDC_SOFTC(GDC_UNIT(dev)); return genfbmmap(&sc->gensc, sc->adp, offset, paddr, prot, memattr); } #endif /* FB_INSTALL_CDEV */ static device_method_t gdc_methods[] = { DEVMETHOD(device_identify, gdc_identify), DEVMETHOD(device_probe, gdcprobe), DEVMETHOD(device_attach, gdc_attach), { 0, 0 } }; static driver_t gdcdriver = { DRIVER_NAME, gdc_methods, sizeof(gdc_softc_t), }; DRIVER_MODULE(gdc, isa, gdcdriver, gdc_devclass, 0, 0); /* LOW-LEVEL */ #include #define TEXT_BUF_BASE 0x000a0000 #define TEXT_BUF_SIZE 0x00008000 #define GRAPHICS_BUF_BASE 0x000a8000 #define GRAPHICS_BUF_SIZE 0x00040000 #define VIDEO_BUF_BASE 0x000a0000 #define VIDEO_BUF_SIZE 0x00048000 #define probe_done(adp) ((adp)->va_flags & V_ADP_PROBED) #define init_done(adp) ((adp)->va_flags & V_ADP_INITIALIZED) #define config_done(adp) ((adp)->va_flags & V_ADP_REGISTERED) /* * NOTE: `va_window' should have a virtual address, but is initialized * with a physical address in the following table, they will be * converted at run-time. */ static video_adapter_t adapter_init_value[] = { { 0, KD_PC98, "gdc", /* va_type, va_name */ 0, 0, /* va_unit, va_minor */ V_ADP_COLOR | V_ADP_MODECHANGE | V_ADP_BORDER, TEXT_GDC, 16, TEXT_GDC, /* va_io*, XXX */ VIDEO_BUF_BASE, VIDEO_BUF_SIZE, /* va_mem* */ TEXT_BUF_BASE, TEXT_BUF_SIZE, TEXT_BUF_SIZE, 0, /* va_window* */ 0, 0, /* va_buffer, va_buffer_size */ 0, M_PC98_80x25, 0, /* va_*mode* */ }, }; static video_adapter_t biosadapter[1]; /* video driver declarations */ static int gdc_configure(int flags); static int gdc_err(video_adapter_t *adp, ...); static vi_probe_t gdc_probe; static vi_init_t gdc_init; static vi_get_info_t gdc_get_info; static vi_query_mode_t gdc_query_mode; static vi_set_mode_t gdc_set_mode; static vi_set_border_t gdc_set_border; static vi_save_state_t gdc_save_state; static vi_load_state_t gdc_load_state; static vi_read_hw_cursor_t gdc_read_hw_cursor; static vi_set_hw_cursor_t gdc_set_hw_cursor; static vi_set_hw_cursor_shape_t gdc_set_hw_cursor_shape; static vi_blank_display_t gdc_blank_display; static vi_mmap_t gdc_mmap_buf; static vi_ioctl_t gdc_dev_ioctl; static vi_clear_t gdc_clear; static vi_fill_rect_t gdc_fill_rect; static vi_bitblt_t gdc_bitblt; static vi_diag_t gdc_diag; static vi_save_palette_t gdc_save_palette; static vi_load_palette_t gdc_load_palette; static vi_set_win_org_t gdc_set_origin; static video_switch_t gdcvidsw = { gdc_probe, gdc_init, gdc_get_info, gdc_query_mode, gdc_set_mode, (vi_save_font_t *)gdc_err, (vi_load_font_t *)gdc_err, (vi_show_font_t *)gdc_err, gdc_save_palette, gdc_load_palette, gdc_set_border, gdc_save_state, gdc_load_state, gdc_set_origin, gdc_read_hw_cursor, gdc_set_hw_cursor, gdc_set_hw_cursor_shape, gdc_blank_display, gdc_mmap_buf, gdc_dev_ioctl, gdc_clear, gdc_fill_rect, gdc_bitblt, (int (*)(void))gdc_err, (int (*)(void))gdc_err, gdc_diag, }; VIDEO_DRIVER(gdc, gdcvidsw, gdc_configure); /* GDC BIOS standard video modes */ #define EOT (-1) #define NA (-2) static video_info_t bios_vmode[] = { { M_PC98_80x25, V_INFO_COLOR, 80, 25, 8, 16, 4, 1, TEXT_BUF_BASE, TEXT_BUF_SIZE, TEXT_BUF_SIZE, 0, 0, V_INFO_MM_TEXT }, #ifdef LINE30 { M_PC98_80x30, V_INFO_COLOR, 80, 30, 8, 16, 4, 1, TEXT_BUF_BASE, TEXT_BUF_SIZE, TEXT_BUF_SIZE, 0, 0, V_INFO_MM_TEXT }, #endif #ifndef GDC_NOGRAPHICS { M_PC98_EGC640x400, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 400, 8, 16, 4, 4, GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0, V_INFO_MM_PLANAR }, { M_PC98_PEGC640x400, V_INFO_COLOR | V_INFO_GRAPHICS | V_INFO_VESA, 640, 400, 8, 16, 8, 1, GRAPHICS_BUF_BASE, 0x00008000, 0x00008000, 0, 0, V_INFO_MM_PACKED, 1 }, #ifdef LINE30 { M_PC98_PEGC640x480, V_INFO_COLOR | V_INFO_GRAPHICS | V_INFO_VESA, 640, 480, 8, 16, 8, 1, GRAPHICS_BUF_BASE, 0x00008000, 0x00008000, 0, 0, V_INFO_MM_PACKED, 1 }, #endif #endif { EOT }, }; static int gdc_init_done = FALSE; /* local functions */ static int map_gen_mode_num(int type, int color, int mode); static int probe_adapters(void); #define prologue(adp, flag, err) \ if (!gdc_init_done || !((adp)->va_flags & (flag))) \ return (err) /* a backdoor for the console driver */ static int gdc_configure(int flags) { probe_adapters(); biosadapter[0].va_flags |= V_ADP_INITIALIZED; if (!config_done(&biosadapter[0])) { if (vid_register(&biosadapter[0]) < 0) return 1; biosadapter[0].va_flags |= V_ADP_REGISTERED; } return 1; } /* local subroutines */ /* map a generic video mode to a known mode number */ static int map_gen_mode_num(int type, int color, int mode) { static struct { int from; int to; } mode_map[] = { { M_TEXT_80x25, M_PC98_80x25, }, #ifdef LINE30 { M_TEXT_80x30, M_PC98_80x30, }, #endif }; int i; - for (i = 0; i < sizeof(mode_map)/sizeof(mode_map[0]); ++i) { + for (i = 0; i < nitems(mode_map); ++i) { if (mode_map[i].from == mode) return mode_map[i].to; } return mode; } static int verify_adapter(video_adapter_t *adp) { #ifndef GDC_NOGRAPHICS int i; if (PC98_SYSTEM_PARAMETER(0x45c) & 0x40) { /* PEGC exists */ adp->va_flags |= V_ADP_VESA; /* XXX */ } else { for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) { if (bios_vmode[i].vi_flags & V_INFO_VESA) bios_vmode[i].vi_mode = NA; } } #endif return 0; } /* probe video adapters and return the number of detected adapters */ static int probe_adapters(void) { video_info_t info; /* do this test only once */ if (gdc_init_done) return 1; gdc_init_done = TRUE; biosadapter[0] = adapter_init_value[0]; biosadapter[0].va_flags |= V_ADP_PROBED; biosadapter[0].va_mode = biosadapter[0].va_initial_mode = biosadapter[0].va_initial_bios_mode; if ((PC98_SYSTEM_PARAMETER(0x597) & 0x80) || (PC98_SYSTEM_PARAMETER(0x458) & 0x80)) { gdc_FH = (inb(0x9a8) & 1) ? _31KHZ : _24KHZ; } else { gdc_FH = _24KHZ; } gdc_get_info(&biosadapter[0], biosadapter[0].va_initial_mode, &info); initialize_gdc(T25_G400, info.vi_flags & V_INFO_GRAPHICS); biosadapter[0].va_window = BIOS_PADDRTOVADDR(info.vi_window); biosadapter[0].va_window_size = info.vi_window_size; biosadapter[0].va_window_gran = info.vi_window_gran; biosadapter[0].va_buffer = 0; biosadapter[0].va_buffer_size = 0; if (info.vi_flags & V_INFO_GRAPHICS) { switch (info.vi_depth/info.vi_planes) { case 1: biosadapter[0].va_line_width = info.vi_width/8; break; case 2: biosadapter[0].va_line_width = info.vi_width/4; break; case 4: biosadapter[0].va_line_width = info.vi_width/2; break; case 8: default: /* shouldn't happen */ biosadapter[0].va_line_width = info.vi_width; break; } } else { biosadapter[0].va_line_width = info.vi_width; } bcopy(&info, &biosadapter[0].va_info, sizeof(info)); verify_adapter(&biosadapter[0]); return 1; } static void master_gdc_cmd(unsigned int cmd) { while ( (inb(TEXT_GDC) & 2) != 0); outb(TEXT_GDC+2, cmd); } static void master_gdc_prm(unsigned int pmtr) { while ( (inb(TEXT_GDC) & 2) != 0); outb(TEXT_GDC, pmtr); } static void master_gdc_word_prm(unsigned int wpmtr) { master_gdc_prm(wpmtr & 0x00ff); master_gdc_prm((wpmtr >> 8) & 0x00ff); } #ifdef LINE30 static void master_gdc_fifo_empty(void) { while ( (inb(TEXT_GDC) & 4) == 0); } #endif static void master_gdc_wait_vsync(void) { while ( (inb(TEXT_GDC) & 0x20) != 0); while ( (inb(TEXT_GDC) & 0x20) == 0); } static void gdc_cmd(unsigned int cmd) { while ( (inb(GRAPHIC_GDC) & 2) != 0); outb( GRAPHIC_GDC+2, cmd); } #ifdef LINE30 static void gdc_prm(unsigned int pmtr) { while ( (inb(GRAPHIC_GDC) & 2) != 0); outb( GRAPHIC_GDC, pmtr); } static void gdc_word_prm(unsigned int wpmtr) { gdc_prm(wpmtr & 0x00ff); gdc_prm((wpmtr >> 8) & 0x00ff); } static void gdc_fifo_empty(void) { while ( (inb(GRAPHIC_GDC) & 0x04) == 0); } #endif static void gdc_wait_vsync(void) { while ( (inb(GRAPHIC_GDC) & 0x20) != 0); while ( (inb(GRAPHIC_GDC) & 0x20) == 0); } #ifdef LINE30 static int check_gdc_clock(void) { if ((inb(IO_SYSPORT) & 0x80) == 0){ return _5MHZ; } else { return _2_5MHZ; } } #endif static void initialize_gdc(unsigned int mode, int isGraph) { #ifdef LINE30 /* start 30line initialize */ int m_mode, s_mode, gdc_clock, hsync_clock; gdc_clock = check_gdc_clock(); m_mode = (mode == T25_G400) ? _25L : _30L; s_mode = 2*mode+gdc_clock; gdc_INFO = m_mode; master_gdc_wait_vsync(); if ((PC98_SYSTEM_PARAMETER(0x597) & 0x80) || (PC98_SYSTEM_PARAMETER(0x458) & 0x80)) { if (PC98_SYSTEM_PARAMETER(0x481) & 0x08) { hsync_clock = (m_mode == _25L) ? gdc_FH : _31KHZ; outb(0x9a8, (hsync_clock == _31KHZ) ? 1 : 0); } else { hsync_clock = gdc_FH; } } else { hsync_clock = _24KHZ; } if ((gdc_clock == _2_5MHZ) && (slave_param[hsync_clock][s_mode][GDC_LF] > 400)) { outb(0x6a, 0x83); outb(0x6a, 0x85); gdc_clock = _5MHZ; s_mode = 2*mode+gdc_clock; } master_gdc_cmd(_GDC_RESET); master_gdc_cmd(_GDC_MASTER); gdc_cmd(_GDC_RESET); gdc_cmd(_GDC_SLAVE); /* GDC Master */ master_gdc_cmd(_GDC_SYNC); master_gdc_prm(0x00); /* flush less */ /* text & graph */ master_gdc_prm(master_param[hsync_clock][m_mode][GDC_CR]); master_gdc_word_prm(((master_param[hsync_clock][m_mode][GDC_HFP] << 10) + (master_param[hsync_clock][m_mode][GDC_VS] << 5) + master_param[hsync_clock][m_mode][GDC_HS])); master_gdc_prm(master_param[hsync_clock][m_mode][GDC_HBP]); master_gdc_prm(master_param[hsync_clock][m_mode][GDC_VFP]); master_gdc_word_prm(((master_param[hsync_clock][m_mode][GDC_VBP] << 10) + (master_param[hsync_clock][m_mode][GDC_LF]))); master_gdc_fifo_empty(); master_gdc_cmd(_GDC_PITCH); master_gdc_prm(MasterPCH); master_gdc_fifo_empty(); /* GDC slave */ gdc_cmd(_GDC_SYNC); gdc_prm(0x06); gdc_prm(slave_param[hsync_clock][s_mode][GDC_CR]); gdc_word_prm((slave_param[hsync_clock][s_mode][GDC_HFP] << 10) + (slave_param[hsync_clock][s_mode][GDC_VS] << 5) + (slave_param[hsync_clock][s_mode][GDC_HS])); gdc_prm(slave_param[hsync_clock][s_mode][GDC_HBP]); gdc_prm(slave_param[hsync_clock][s_mode][GDC_VFP]); gdc_word_prm((slave_param[hsync_clock][s_mode][GDC_VBP] << 10) + (slave_param[hsync_clock][s_mode][GDC_LF])); gdc_fifo_empty(); gdc_cmd(_GDC_PITCH); gdc_prm(SlavePCH[gdc_clock]); gdc_fifo_empty(); /* set Master GDC scroll param */ master_gdc_wait_vsync(); master_gdc_wait_vsync(); master_gdc_wait_vsync(); master_gdc_cmd(_GDC_SCROLL); master_gdc_word_prm(0); master_gdc_word_prm((master_param[hsync_clock][m_mode][GDC_LF] << 4) | 0x0000); master_gdc_fifo_empty(); /* set Slave GDC scroll param */ gdc_wait_vsync(); gdc_cmd(_GDC_SCROLL); gdc_word_prm(0); if (gdc_clock == _5MHZ) { gdc_word_prm((SlaveScrlLF[mode] << 4) | 0x4000); } else { gdc_word_prm(SlaveScrlLF[mode] << 4); } gdc_fifo_empty(); gdc_word_prm(0); if (gdc_clock == _5MHZ) { gdc_word_prm((SlaveScrlLF[mode] << 4) | 0x4000); } else { gdc_word_prm(SlaveScrlLF[mode] << 4); } gdc_fifo_empty(); /* sync start */ gdc_cmd(isGraph ? _GDC_START : _GDC_STOP); gdc_wait_vsync(); gdc_wait_vsync(); gdc_wait_vsync(); master_gdc_cmd(isGraph ? _GDC_STOP : _GDC_START); #else master_gdc_wait_vsync(); master_gdc_cmd(isGraph ? _GDC_STOP : _GDC_START); /* text */ gdc_wait_vsync(); gdc_cmd(isGraph ? _GDC_START : _GDC_STOP); /* graphics */ #endif } #ifndef GDC_NOGRAPHICS static u_char b_palette[] = { /* R G B */ 0x00, 0x00, 0x00, /* 0 */ 0x00, 0x00, 0x7f, /* 1 */ 0x7f, 0x00, 0x00, /* 2 */ 0x7f, 0x00, 0x7f, /* 3 */ 0x00, 0x7f, 0x00, /* 4 */ 0x00, 0x7f, 0x7f, /* 5 */ 0x7f, 0x7f, 0x00, /* 6 */ 0x7f, 0x7f, 0x7f, /* 7 */ 0x40, 0x40, 0x40, /* 8 */ 0x00, 0x00, 0xff, /* 9 */ 0xff, 0x00, 0x00, /* 10 */ 0xff, 0x00, 0xff, /* 11 */ 0x00, 0xff, 0x00, /* 12 */ 0x00, 0xff, 0xff, /* 13 */ 0xff, 0xff, 0x00, /* 14 */ 0xff, 0xff, 0xff, /* 15 */ }; #endif static int gdc_load_palette(video_adapter_t *adp, u_char *palette) { #ifndef GDC_NOGRAPHICS int i; if (adp->va_info.vi_flags & V_INFO_VESA) { gdc_wait_vsync(); for (i = 0; i < 256; ++i) { outb(0xa8, i); outb(0xac, *palette++); /* R */ outb(0xaa, *palette++); /* G */ outb(0xae, *palette++); /* B */ } } else { /* * XXX - Even though PC-98 text color is independent of palette, * we should set palette in text mode. * Because the background color of text mode is palette 0's one. */ outb(0x6a, 1); /* 16 colors mode */ bcopy(palette, b_palette, sizeof(b_palette)); gdc_wait_vsync(); for (i = 0; i < 16; ++i) { outb(0xa8, i); outb(0xac, *palette++ >> 4); /* R */ outb(0xaa, *palette++ >> 4); /* G */ outb(0xae, *palette++ >> 4); /* B */ } } #endif return 0; } static int gdc_save_palette(video_adapter_t *adp, u_char *palette) { #ifndef GDC_NOGRAPHICS int i; if (adp->va_info.vi_flags & V_INFO_VESA) { for (i = 0; i < 256; ++i) { outb(0xa8, i); *palette++ = inb(0xac); /* R */ *palette++ = inb(0xaa); /* G */ *palette++ = inb(0xae); /* B */ } } else { bcopy(b_palette, palette, sizeof(b_palette)); } #endif return 0; } static int gdc_set_origin(video_adapter_t *adp, off_t offset) { #ifndef GDC_NOGRAPHICS if (adp->va_info.vi_flags & V_INFO_VESA) { writew(BIOS_PADDRTOVADDR(0x000e0004), offset >> 15); } #endif return 0; } /* entry points */ static int gdc_err(video_adapter_t *adp, ...) { return ENODEV; } static int gdc_probe(int unit, video_adapter_t **adpp, void *arg, int flags) { probe_adapters(); if (unit >= 1) return ENXIO; *adpp = &biosadapter[unit]; return 0; } static int gdc_init(int unit, video_adapter_t *adp, int flags) { if ((unit >= 1) || (adp == NULL) || !probe_done(adp)) return ENXIO; if (!init_done(adp)) { /* nothing to do really... */ adp->va_flags |= V_ADP_INITIALIZED; } if (!config_done(adp)) { if (vid_register(adp) < 0) return ENXIO; adp->va_flags |= V_ADP_REGISTERED; } return 0; } /* * get_info(): * Return the video_info structure of the requested video mode. */ static int gdc_get_info(video_adapter_t *adp, int mode, video_info_t *info) { int i; if (!gdc_init_done) return ENXIO; mode = map_gen_mode_num(adp->va_type, adp->va_flags & V_ADP_COLOR, mode); for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) { if (bios_vmode[i].vi_mode == NA) continue; if (mode == bios_vmode[i].vi_mode) { *info = bios_vmode[i]; info->vi_buffer_size = info->vi_window_size*info->vi_planes; return 0; } } return EINVAL; } /* * query_mode(): * Find a video mode matching the requested parameters. * Fields filled with 0 are considered "don't care" fields and * match any modes. */ static int gdc_query_mode(video_adapter_t *adp, video_info_t *info) { int i; if (!gdc_init_done) return ENXIO; for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) { if (bios_vmode[i].vi_mode == NA) continue; if ((info->vi_width != 0) && (info->vi_width != bios_vmode[i].vi_width)) continue; if ((info->vi_height != 0) && (info->vi_height != bios_vmode[i].vi_height)) continue; if ((info->vi_cwidth != 0) && (info->vi_cwidth != bios_vmode[i].vi_cwidth)) continue; if ((info->vi_cheight != 0) && (info->vi_cheight != bios_vmode[i].vi_cheight)) continue; if ((info->vi_depth != 0) && (info->vi_depth != bios_vmode[i].vi_depth)) continue; if ((info->vi_planes != 0) && (info->vi_planes != bios_vmode[i].vi_planes)) continue; /* XXX: should check pixel format, memory model */ if ((info->vi_flags != 0) && (info->vi_flags != bios_vmode[i].vi_flags)) continue; /* verify if this mode is supported on this adapter */ if (gdc_get_info(adp, bios_vmode[i].vi_mode, info)) continue; return 0; } return ENODEV; } /* * set_mode(): * Change the video mode. */ static int gdc_set_mode(video_adapter_t *adp, int mode) { video_info_t info; prologue(adp, V_ADP_MODECHANGE, ENODEV); mode = map_gen_mode_num(adp->va_type, adp->va_flags & V_ADP_COLOR, mode); if (gdc_get_info(adp, mode, &info)) return EINVAL; switch (info.vi_mode) { #ifndef GDC_NOGRAPHICS case M_PC98_PEGC640x480: /* PEGC 640x480 */ initialize_gdc(T30_G480, info.vi_flags & V_INFO_GRAPHICS); break; case M_PC98_PEGC640x400: /* PEGC 640x400 */ case M_PC98_EGC640x400: /* EGC GRAPHICS */ #endif case M_PC98_80x25: /* VGA TEXT */ initialize_gdc(T25_G400, info.vi_flags & V_INFO_GRAPHICS); break; case M_PC98_80x30: /* VGA TEXT */ initialize_gdc(T30_G400, info.vi_flags & V_INFO_GRAPHICS); break; default: break; } #ifndef GDC_NOGRAPHICS if (info.vi_flags & V_INFO_VESA) { outb(0x6a, 0x07); /* enable mode F/F change */ outb(0x6a, 0x21); /* enhanced graphics */ if (info.vi_height > 400) outb(0x6a, 0x69); /* 800 lines */ writeb(BIOS_PADDRTOVADDR(0x000e0100), 0); /* packed pixel */ } else { if (adp->va_flags & V_ADP_VESA) { outb(0x6a, 0x07); /* enable mode F/F change */ outb(0x6a, 0x20); /* normal graphics */ outb(0x6a, 0x68); /* 400 lines */ } outb(0x6a, 1); /* 16 colors */ } #endif adp->va_mode = mode; adp->va_flags &= ~V_ADP_COLOR; adp->va_flags |= (info.vi_flags & V_INFO_COLOR) ? V_ADP_COLOR : 0; #if 0 adp->va_crtc_addr = (adp->va_flags & V_ADP_COLOR) ? COLOR_CRTC : MONO_CRTC; #endif adp->va_window = BIOS_PADDRTOVADDR(info.vi_window); adp->va_window_size = info.vi_window_size; adp->va_window_gran = info.vi_window_gran; if (info.vi_buffer_size == 0) { adp->va_buffer = 0; adp->va_buffer_size = 0; } else { adp->va_buffer = BIOS_PADDRTOVADDR(info.vi_buffer); adp->va_buffer_size = info.vi_buffer_size; } if (info.vi_flags & V_INFO_GRAPHICS) { switch (info.vi_depth/info.vi_planes) { case 1: adp->va_line_width = info.vi_width/8; break; case 2: adp->va_line_width = info.vi_width/4; break; case 4: adp->va_line_width = info.vi_width/2; break; case 8: default: /* shouldn't happen */ adp->va_line_width = info.vi_width; break; } } else { adp->va_line_width = info.vi_width; } bcopy(&info, &adp->va_info, sizeof(info)); /* move hardware cursor out of the way */ vidd_set_hw_cursor(adp, -1, -1); return 0; } /* * set_border(): * Change the border color. */ static int gdc_set_border(video_adapter_t *adp, int color) { outb(0x6c, color << 4); return 0; } /* * save_state(): * Read video card register values. */ static int gdc_save_state(video_adapter_t *adp, void *p, size_t size) { return ENODEV; } /* * load_state(): * Set video card registers at once. */ static int gdc_load_state(video_adapter_t *adp, void *p) { return ENODEV; } /* * read_hw_cursor(): * Read the position of the hardware text cursor. */ static int gdc_read_hw_cursor(video_adapter_t *adp, int *col, int *row) { u_int16_t off; int s; if (!gdc_init_done) return ENXIO; if (adp->va_info.vi_flags & V_INFO_GRAPHICS) return ENODEV; s = spltty(); master_gdc_cmd(0xe0); /* _GDC_CSRR */ while((inb(TEXT_GDC + 0) & 0x1) == 0) {} /* GDC wait */ off = inb(TEXT_GDC + 2); /* EADl */ off |= (inb(TEXT_GDC + 2) << 8); /* EADh */ inb(TEXT_GDC + 2); /* dummy */ inb(TEXT_GDC + 2); /* dummy */ inb(TEXT_GDC + 2); /* dummy */ splx(s); if (off >= ROW*COL) off = 0; *row = off / adp->va_info.vi_width; *col = off % adp->va_info.vi_width; return 0; } /* * set_hw_cursor(): * Move the hardware text cursor. If col and row are both -1, * the cursor won't be shown. */ static int gdc_set_hw_cursor(video_adapter_t *adp, int col, int row) { u_int16_t off; int s; if (!gdc_init_done) return ENXIO; if ((col == -1) && (row == -1)) { off = -1; } else { if (adp->va_info.vi_flags & V_INFO_GRAPHICS) return ENODEV; off = row*adp->va_info.vi_width + col; } s = spltty(); master_gdc_cmd(0x49); /* _GDC_CSRW */ master_gdc_word_prm(off); splx(s); return 0; } /* * set_hw_cursor_shape(): * Change the shape of the hardware text cursor. If the height is zero * or negative, the cursor won't be shown. */ static int gdc_set_hw_cursor_shape(video_adapter_t *adp, int base, int height, int celsize, int blink) { int start; int end; int s; if (!gdc_init_done) return ENXIO; start = celsize - (base + height); end = celsize - base - 1; #if 0 /* * muPD7220 GDC has anomaly that if end == celsize - 1 then start * must be 0, otherwise the cursor won't be correctly shown * in the first row in the screen. We shall set end to celsize - 2; * if end == celsize -1 && start > 0. XXX */ if ((end == celsize - 1) && (start > 0) && (start < end)) --end; #endif s = spltty(); master_gdc_cmd(0x4b); /* _GDC_CSRFORM */ master_gdc_prm(((height > 0) ? 0x80 : 0) /* cursor on/off */ | ((celsize - 1) & 0x1f)); /* cel size */ master_gdc_word_prm(((end & 0x1f) << 11) /* end line */ | (12 << 6) /* blink rate */ | (blink ? 0 : 0x20) /* blink on/off */ | (start & 0x1f)); /* start line */ splx(s); return 0; } /* * blank_display() * Put the display in power save/power off mode. */ static int gdc_blank_display(video_adapter_t *adp, int mode) { int s; static int standby = 0; if (!gdc_init_done) return ENXIO; s = splhigh(); switch (mode) { case V_DISPLAY_SUSPEND: case V_DISPLAY_STAND_BY: outb(0x09a2, 0x80 | 0x40); /* V/H-SYNC mask */ if (inb(0x09a2) == (0x80 | 0x40)) standby = 1; /* FALLTHROUGH */ case V_DISPLAY_BLANK: while (!(inb(TEXT_GDC) & 0x20)) /* V-SYNC wait */ ; outb(TEXT_GDC + 8, 0x0e); /* DISP off */ break; case V_DISPLAY_ON: while (!(inb(TEXT_GDC) & 0x20)) /* V-SYNC wait */ ; outb(TEXT_GDC + 8, 0x0f); /* DISP on */ if (standby) { outb(0x09a2, 0x00); /* V/H-SYNC unmask */ standby = 0; } break; } splx(s); return 0; } /* * mmap(): * Mmap frame buffer. */ static int gdc_mmap_buf(video_adapter_t *adp, vm_ooffset_t offset, vm_offset_t *paddr, int prot, vm_memattr_t *memattr) { /* FIXME: is this correct? XXX */ if (offset > VIDEO_BUF_SIZE - PAGE_SIZE) return -1; *paddr = adp->va_info.vi_window + offset; return 0; } #ifndef GDC_NOGRAPHICS static void planar_fill(video_adapter_t *adp, int val) { outb(0x7c, 0x80); /* GRCG on & TDW mode */ outb(0x7e, 0); /* tile B */ outb(0x7e, 0); /* tile R */ outb(0x7e, 0); /* tile G */ outb(0x7e, 0); /* tile I */ fillw_io(0, adp->va_window, 0x8000 / 2); /* XXX */ outb(0x7c, 0); /* GRCG off */ } static void packed_fill(video_adapter_t *adp, int val) { int length; int at; /* position in the frame buffer */ int l; at = 0; length = adp->va_line_width*adp->va_info.vi_height; while (length > 0) { l = imin(length, adp->va_window_size); vidd_set_win_org(adp, at); bzero_io(adp->va_window, l); length -= l; at += l; } } static int gdc_clear(video_adapter_t *adp) { switch (adp->va_info.vi_mem_model) { case V_INFO_MM_TEXT: /* do nothing? XXX */ break; case V_INFO_MM_PLANAR: planar_fill(adp, 0); break; case V_INFO_MM_PACKED: packed_fill(adp, 0); break; } return 0; } #else /* GDC_NOGRAPHICS */ static int gdc_clear(video_adapter_t *adp) { return 0; } #endif /* GDC_NOGRAPHICS */ static int gdc_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy) { return ENODEV; } static int gdc_bitblt(video_adapter_t *adp,...) { /* FIXME */ return ENODEV; } static int gdc_dev_ioctl(video_adapter_t *adp, u_long cmd, caddr_t arg) { switch (cmd) { case FBIO_GETWINORG: /* get frame buffer window origin */ *(u_int *)arg = 0; return 0; case FBIO_SETWINORG: /* set frame buffer window origin */ case FBIO_SETDISPSTART: /* set display start address */ case FBIO_SETLINEWIDTH: /* set scan line length in pixel */ case FBIO_GETPALETTE: /* get color palette */ case FBIO_SETPALETTE: /* set color palette */ case FBIOGETCMAP: /* get color palette */ case FBIOPUTCMAP: /* set color palette */ return ENODEV; case FBIOGTYPE: /* get frame buffer type info. */ ((struct fbtype *)arg)->fb_type = fb_type(adp->va_type); ((struct fbtype *)arg)->fb_height = adp->va_info.vi_height; ((struct fbtype *)arg)->fb_width = adp->va_info.vi_width; ((struct fbtype *)arg)->fb_depth = adp->va_info.vi_depth; if ((adp->va_info.vi_depth <= 1) || (adp->va_info.vi_depth > 8)) ((struct fbtype *)arg)->fb_cmsize = 0; else ((struct fbtype *)arg)->fb_cmsize = 1 << adp->va_info.vi_depth; ((struct fbtype *)arg)->fb_size = adp->va_buffer_size; return 0; default: return fb_commonioctl(adp, cmd, arg); } } /* * diag(): * Print some information about the video adapter and video modes, * with requested level of details. */ static int gdc_diag(video_adapter_t *adp, int level) { #if defined(FB_DEBUG) && FB_DEBUG > 1 int i; #endif if (!gdc_init_done) return ENXIO; fb_dump_adp_info(DRIVER_NAME, adp, level); #if defined(FB_DEBUG) && FB_DEBUG > 1 for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) { if (bios_vmode[i].vi_mode == NA) continue; if (get_mode_param(bios_vmode[i].vi_mode) == NULL) continue; fb_dump_mode_info(DRIVER_NAME, adp, &bios_vmode[i], level); } #endif return 0; } Index: head/sys/pc98/cbus/pckbd.c =================================================================== --- head/sys/pc98/cbus/pckbd.c (revision 298351) +++ head/sys/pc98/cbus/pckbd.c (revision 298352) @@ -1,1000 +1,999 @@ /*- * Copyright (c) 1999 FreeBSD(98) port team. * 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 as * the first lines of this file unmodified. * 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * $FreeBSD$ */ #include "opt_compat.h" #include "opt_kbd.h" #include #include #include #include #include #include #include #include #include #include #include #include #define DRIVER_NAME "pckbd" /* device configuration flags */ #define KB_CONF_FAIL_IF_NO_KBD (1 << 0) /* don't install if no kbd is found */ typedef caddr_t KBDC; typedef struct pckbd_state { KBDC kbdc; /* keyboard controller */ int ks_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */ int ks_flags; /* flags */ #define COMPOSE (1 << 0) int ks_state; /* shift/lock key state */ int ks_accents; /* accent key index (> 0) */ u_int ks_composed_char; /* composed char code (> 0) */ struct callout ks_timer; } pckbd_state_t; static devclass_t pckbd_devclass; static int pckbdprobe(device_t dev); static int pckbdattach(device_t dev); static int pckbdresume(device_t dev); static void pckbd_isa_intr(void *arg); static device_method_t pckbd_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pckbdprobe), DEVMETHOD(device_attach, pckbdattach), DEVMETHOD(device_resume, pckbdresume), { 0, 0 } }; static driver_t pckbd_driver = { DRIVER_NAME, pckbd_methods, 1, }; DRIVER_MODULE(pckbd, isa, pckbd_driver, pckbd_devclass, 0, 0); static bus_addr_t pckbd_iat[] = {0, 2}; static int pckbd_probe_unit(device_t dev, int port, int irq, int flags); static int pckbd_attach_unit(device_t dev, keyboard_t **kbd, int port, int irq, int flags); static timeout_t pckbd_timeout; static int pckbdprobe(device_t dev) { struct resource *res; int error, rid; /* Check isapnp ids */ if (isa_get_vendorid(dev)) return (ENXIO); device_set_desc(dev, "PC-98 Keyboard"); rid = 0; res = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, pckbd_iat, 2, RF_ACTIVE); if (res == NULL) return ENXIO; isa_load_resourcev(res, pckbd_iat, 2); error = pckbd_probe_unit(dev, isa_get_port(dev), (1 << isa_get_irq(dev)), device_get_flags(dev)); bus_release_resource(dev, SYS_RES_IOPORT, rid, res); return (error); } static int pckbdattach(device_t dev) { keyboard_t *kbd; void *ih; struct resource *res; int error, rid; rid = 0; res = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, pckbd_iat, 2, RF_ACTIVE); if (res == NULL) return ENXIO; isa_load_resourcev(res, pckbd_iat, 2); error = pckbd_attach_unit(dev, &kbd, isa_get_port(dev), (1 << isa_get_irq(dev)), device_get_flags(dev)); rid = 0; res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (res == NULL) return ENXIO; bus_setup_intr(dev, res, INTR_TYPE_TTY, NULL, pckbd_isa_intr, kbd, &ih); return 0; } static int pckbdresume(device_t dev) { keyboard_t *kbd; kbd = kbd_get_keyboard(kbd_find_keyboard(DRIVER_NAME, device_get_unit(dev))); if (kbd) kbdd_clear_state(kbd); return (0); } static void pckbd_isa_intr(void *arg) { keyboard_t *kbd = arg; kbdd_intr(kbd, NULL); } static int pckbd_probe_unit(device_t dev, int port, int irq, int flags) { keyboard_switch_t *sw; int args[2]; int error; sw = kbd_get_switch(DRIVER_NAME); if (sw == NULL) return ENXIO; args[0] = port; args[1] = irq; error = (*sw->probe)(device_get_unit(dev), args, flags); if (error) return error; return 0; } static int pckbd_attach_unit(device_t dev, keyboard_t **kbd, int port, int irq, int flags) { keyboard_switch_t *sw; pckbd_state_t *state; int args[2]; int error; int unit; sw = kbd_get_switch(DRIVER_NAME); if (sw == NULL) return ENXIO; /* reset, initialize and enable the device */ unit = device_get_unit(dev); args[0] = port; args[1] = irq; *kbd = NULL; error = (*sw->probe)(unit, args, flags); if (error) return error; error = (*sw->init)(unit, kbd, args, flags); if (error) return error; (*sw->enable)(*kbd); #ifdef KBD_INSTALL_CDEV /* attach a virtual keyboard cdev */ error = kbd_attach(*kbd); if (error) return error; #endif /* KBD_INSTALL_CDEV */ /* * This is a kludge to compensate for lost keyboard interrupts. * A similar code used to be in syscons. See below. XXX */ state = (pckbd_state_t *)(*kbd)->kb_data; callout_init(&state->ks_timer, 0); pckbd_timeout(*kbd); if (bootverbose) (*sw->diag)(*kbd, bootverbose); return 0; } static void pckbd_timeout(void *arg) { pckbd_state_t *state; keyboard_t *kbd; int s; /* The following comments are extracted from syscons.c (1.287) */ /* * With release 2.1 of the Xaccel server, the keyboard is left * hanging pretty often. Apparently an interrupt from the * keyboard is lost, and I don't know why (yet). * This ugly hack calls scintr if input is ready for the keyboard * and conveniently hides the problem. XXX */ /* * Try removing anything stuck in the keyboard controller; whether * it's a keyboard scan code or mouse data. `scintr()' doesn't * read the mouse data directly, but `kbdio' routines will, as a * side effect. */ s = spltty(); kbd = (keyboard_t *)arg; if (kbdd_lock(kbd, TRUE)) { /* * We have seen the lock flag is not set. Let's reset * the flag early, otherwise the LED update routine fails * which may want the lock during the interrupt routine. */ kbdd_lock(kbd, FALSE); if (kbdd_check_char(kbd)) kbdd_intr(kbd, NULL); } splx(s); state = (pckbd_state_t *)kbd->kb_data; callout_reset(&state->ks_timer, hz / 10, pckbd_timeout, arg); } /* LOW-LEVEL */ #include #define PC98KBD_DEFAULT 0 /* keyboard driver declaration */ static int pckbd_configure(int flags); static kbd_probe_t pckbd_probe; static kbd_init_t pckbd_init; static kbd_term_t pckbd_term; static kbd_intr_t pckbd_intr; static kbd_test_if_t pckbd_test_if; static kbd_enable_t pckbd_enable; static kbd_disable_t pckbd_disable; static kbd_read_t pckbd_read; static kbd_check_t pckbd_check; static kbd_read_char_t pckbd_read_char; static kbd_check_char_t pckbd_check_char; static kbd_ioctl_t pckbd_ioctl; static kbd_lock_t pckbd_lock; static kbd_clear_state_t pckbd_clear_state; static kbd_get_state_t pckbd_get_state; static kbd_set_state_t pckbd_set_state; static kbd_poll_mode_t pckbd_poll; keyboard_switch_t pckbdsw = { pckbd_probe, pckbd_init, pckbd_term, pckbd_intr, pckbd_test_if, pckbd_enable, pckbd_disable, pckbd_read, pckbd_check, pckbd_read_char, pckbd_check_char, pckbd_ioctl, pckbd_lock, pckbd_clear_state, pckbd_get_state, pckbd_set_state, genkbd_get_fkeystr, pckbd_poll, genkbd_diag, }; KEYBOARD_DRIVER(pckbd, pckbdsw, pckbd_configure); struct kbdc_softc { int port; /* base port address */ int lock; /* FIXME: XXX not quite a semaphore... */ }; /* local functions */ static int probe_keyboard(KBDC kbdc, int flags); static int init_keyboard(KBDC kbdc, int *type, int flags); static KBDC kbdc_open(int port); static int kbdc_lock(KBDC kbdc, int lock); static int kbdc_data_ready(KBDC kbdc); static int read_kbd_data(KBDC kbdc); static int read_kbd_data_no_wait(KBDC kbdc); static int wait_for_kbd_data(struct kbdc_softc *kbdc); /* local variables */ /* the initial key map, accent map and fkey strings */ #include /* structures for the default keyboard */ static keyboard_t default_kbd; static pckbd_state_t default_kbd_state; static keymap_t default_keymap; static accentmap_t default_accentmap; static fkeytab_t default_fkeytab[NUM_FKEYS]; /* * The back door to the keyboard driver! * This function is called by the console driver, via the kbdio module, * to tickle keyboard drivers when the low-level console is being initialized. * Almost nothing in the kernel has been initialied yet. Try to probe * keyboards if possible. * NOTE: because of the way the low-level conole is initialized, this routine * may be called more than once!! */ static int pckbd_configure(int flags) { keyboard_t *kbd; int arg[2]; int i; /* XXX: a kludge to obtain the device configuration flags */ if (resource_int_value(DRIVER_NAME, 0, "flags", &i) == 0) { flags |= i; /* if the driver is disabled, unregister the keyboard if any */ if (resource_disabled(DRIVER_NAME, 0)) { i = kbd_find_keyboard(DRIVER_NAME, PC98KBD_DEFAULT); if (i >= 0) { kbd = kbd_get_keyboard(i); kbd_unregister(kbd); kbd->kb_flags &= ~KB_REGISTERED; return 0; } } } /* probe the default keyboard */ arg[0] = -1; arg[1] = -1; kbd = NULL; if (pckbd_probe(PC98KBD_DEFAULT, arg, flags)) return 0; if (pckbd_init(PC98KBD_DEFAULT, &kbd, arg, flags)) return 0; /* return the number of found keyboards */ return 1; } /* low-level functions */ /* detect a keyboard */ static int pckbd_probe(int unit, void *arg, int flags) { KBDC kbdc; int *data = (int *)arg; if (unit != PC98KBD_DEFAULT) return ENXIO; if (KBD_IS_PROBED(&default_kbd)) return 0; kbdc = kbdc_open(data[0]); if (kbdc == NULL) return ENXIO; if (probe_keyboard(kbdc, flags)) { if (flags & KB_CONF_FAIL_IF_NO_KBD) return ENXIO; } return 0; } /* reset and initialize the device */ static int pckbd_init(int unit, keyboard_t **kbdp, void *arg, int flags) { keyboard_t *kbd; pckbd_state_t *state; keymap_t *keymap; accentmap_t *accmap; fkeytab_t *fkeymap; int fkeymap_size; int *data = (int *)arg; if (unit != PC98KBD_DEFAULT) /* shouldn't happen */ return ENXIO; *kbdp = kbd = &default_kbd; state = &default_kbd_state; if (!KBD_IS_PROBED(kbd)) { keymap = &default_keymap; accmap = &default_accentmap; fkeymap = default_fkeytab; - fkeymap_size = - sizeof(default_fkeytab)/sizeof(default_fkeytab[0]); + fkeymap_size = nitems(default_fkeytab); state->kbdc = kbdc_open(data[0]); if (state->kbdc == NULL) return ENXIO; kbd_init_struct(kbd, DRIVER_NAME, KB_OTHER, unit, flags, data[0], IO_KBDSIZE); bcopy(&key_map, keymap, sizeof(key_map)); bcopy(&accent_map, accmap, sizeof(accent_map)); bcopy(fkey_tab, fkeymap, imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab))); kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size); kbd->kb_data = (void *)state; if (probe_keyboard(state->kbdc, flags)) {/* shouldn't happen */ if (flags & KB_CONF_FAIL_IF_NO_KBD) return ENXIO; } else { KBD_FOUND_DEVICE(kbd); } pckbd_clear_state(kbd); state->ks_mode = K_XLATE; KBD_PROBE_DONE(kbd); } if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) { if (KBD_HAS_DEVICE(kbd) && init_keyboard(state->kbdc, &kbd->kb_type, kbd->kb_config) && (kbd->kb_config & KB_CONF_FAIL_IF_NO_KBD)) return ENXIO; pckbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state); KBD_INIT_DONE(kbd); } if (!KBD_IS_CONFIGURED(kbd)) { if (kbd_register(kbd) < 0) return ENXIO; KBD_CONFIG_DONE(kbd); } return 0; } /* finish using this keyboard */ static int pckbd_term(keyboard_t *kbd) { pckbd_state_t *state = (pckbd_state_t *)kbd->kb_data; kbd_unregister(kbd); callout_drain(&state->ks_timer); return 0; } /* keyboard interrupt routine */ static int pckbd_intr(keyboard_t *kbd, void *arg) { int c; if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) { /* let the callback function to process the input */ (*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT, kbd->kb_callback.kc_arg); } else { /* read and discard the input; no one is waiting for input */ do { c = pckbd_read_char(kbd, FALSE); } while (c != NOKEY); } return 0; } /* test the interface to the device */ static int pckbd_test_if(keyboard_t *kbd) { return 0; } /* * Enable the access to the device; until this function is called, * the client cannot read from the keyboard. */ static int pckbd_enable(keyboard_t *kbd) { int s; s = spltty(); KBD_ACTIVATE(kbd); splx(s); return 0; } /* disallow the access to the device */ static int pckbd_disable(keyboard_t *kbd) { int s; s = spltty(); KBD_DEACTIVATE(kbd); splx(s); return 0; } /* read one byte from the keyboard if it's allowed */ static int pckbd_read(keyboard_t *kbd, int wait) { int c; if (wait) c = read_kbd_data(((pckbd_state_t *)kbd->kb_data)->kbdc); else c = read_kbd_data_no_wait(((pckbd_state_t *)kbd->kb_data)->kbdc); if (c != -1) ++kbd->kb_count; return (KBD_IS_ACTIVE(kbd) ? c : -1); } /* check if data is waiting */ static int pckbd_check(keyboard_t *kbd) { if (!KBD_IS_ACTIVE(kbd)) return FALSE; return kbdc_data_ready(((pckbd_state_t *)kbd->kb_data)->kbdc); } /* read char from the keyboard */ static u_int pckbd_read_char(keyboard_t *kbd, int wait) { pckbd_state_t *state; u_int action; int scancode; int keycode; state = (pckbd_state_t *)kbd->kb_data; next_code: /* do we have a composed char to return? */ if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) { action = state->ks_composed_char; state->ks_composed_char = 0; if (action > UCHAR_MAX) return ERRKEY; return action; } /* see if there is something in the keyboard port */ if (wait) { do { scancode = read_kbd_data(state->kbdc); } while (scancode == -1); } else { scancode = read_kbd_data_no_wait(state->kbdc); if (scancode == -1) return NOKEY; } ++kbd->kb_count; #if 0 printf("pckbd_read_char(): scancode:0x%x\n", scancode); #endif /* return the byte as is for the K_RAW mode */ if (state->ks_mode == K_RAW) return scancode; /* translate the scan code into a keycode */ keycode = scancode & 0x7F; switch(scancode) { case 0xF3: /* GRPH (compose key) released */ if (state->ks_flags & COMPOSE) { state->ks_flags &= ~COMPOSE; if (state->ks_composed_char > UCHAR_MAX) state->ks_composed_char = 0; } break; case 0x73: /* GRPH (compose key) pressed */ if (!(state->ks_flags & COMPOSE)) { state->ks_flags |= COMPOSE; state->ks_composed_char = 0; } break; } /* return the key code in the K_CODE mode */ if (state->ks_mode == K_CODE) return (keycode | (scancode & 0x80)); /* compose a character code */ if (state->ks_flags & COMPOSE) { switch (scancode) { /* key pressed, process it */ case 0x42: case 0x43: case 0x44: /* keypad 7,8,9 */ state->ks_composed_char *= 10; state->ks_composed_char += scancode - 0x3B; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x46: case 0x47: case 0x48: /* keypad 4,5,6 */ state->ks_composed_char *= 10; state->ks_composed_char += scancode - 0x42; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x4A: case 0x4B: case 0x4C: /* keypad 1,2,3 */ state->ks_composed_char *= 10; state->ks_composed_char += scancode - 0x49; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x4E: /* keypad 0 */ state->ks_composed_char *= 10; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; /* key released, no interest here */ case 0xC2: case 0xC3: case 0xC4: /* keypad 7,8,9 */ case 0xC6: case 0xC7: case 0xC8: /* keypad 4,5,6 */ case 0xCA: case 0xCB: case 0xCC: /* keypad 1,2,3 */ case 0xCE: /* keypad 0 */ goto next_code; case 0x73: /* GRPH key */ break; default: if (state->ks_composed_char > 0) { state->ks_flags &= ~COMPOSE; state->ks_composed_char = 0; return ERRKEY; } break; } } /* keycode to key action */ action = genkbd_keyaction(kbd, keycode, scancode & 0x80, &state->ks_state, &state->ks_accents); if (action == NOKEY) goto next_code; else return action; } /* check if char is waiting */ static int pckbd_check_char(keyboard_t *kbd) { pckbd_state_t *state; if (!KBD_IS_ACTIVE(kbd)) return FALSE; state = (pckbd_state_t *)kbd->kb_data; if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) return TRUE; return kbdc_data_ready(state->kbdc); } /* some useful control functions */ static int pckbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) { pckbd_state_t *state = kbd->kb_data; int s; int i; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) int ival; #endif s = spltty(); switch (cmd) { case KDGKBMODE: /* get keyboard mode */ *(int *)arg = state->ks_mode; break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 7): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSKBMODE: /* set keyboard mode */ switch (*(int *)arg) { case K_XLATE: if (state->ks_mode != K_XLATE) { /* make lock key state and LED state match */ state->ks_state &= ~LOCK_MASK; state->ks_state |= KBD_LED_VAL(kbd); } /* FALLTHROUGH */ case K_RAW: case K_CODE: if (state->ks_mode != *(int *)arg) { pckbd_clear_state(kbd); state->ks_mode = *(int *)arg; } break; default: splx(s); return EINVAL; } break; case KDGETLED: /* get keyboard LED */ *(int *)arg = KBD_LED_VAL(kbd); break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 66): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSETLED: /* set keyboard LED */ /* NOTE: lock key state in ks_state won't be changed */ if (*(int *)arg & ~LOCK_MASK) { splx(s); return EINVAL; } i = *(int *)arg; /* replace CAPS LED with ALTGR LED for ALTGR keyboards */ if (kbd->kb_keymap->n_keys > ALTGR_OFFSET) { if (i & ALKED) i |= CLKED; else i &= ~CLKED; } KBD_LED_VAL(kbd) = *(int *)arg; break; case KDGKBSTATE: /* get lock key state */ *(int *)arg = state->ks_state & LOCK_MASK; break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 20): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSKBSTATE: /* set lock key state */ if (*(int *)arg & ~LOCK_MASK) { splx(s); return EINVAL; } state->ks_state &= ~LOCK_MASK; state->ks_state |= *(int *)arg; splx(s); /* set LEDs and quit */ return pckbd_ioctl(kbd, KDSETLED, arg); case KDSETRAD: /* set keyboard repeat rate (old interface)*/ break; case KDSETREPEAT: /* set keyboard repeat rate (new interface) */ break; case PIO_KEYMAP: /* set keyboard translation table */ case OPIO_KEYMAP: /* set keyboard translation table (compat) */ case PIO_KEYMAPENT: /* set keyboard translation table entry */ case PIO_DEADKEYMAP: /* set accent key translation table */ state->ks_accents = 0; /* FALLTHROUGH */ default: splx(s); return genkbd_commonioctl(kbd, cmd, arg); } splx(s); return 0; } /* lock the access to the keyboard */ static int pckbd_lock(keyboard_t *kbd, int lock) { return kbdc_lock(((pckbd_state_t *)kbd->kb_data)->kbdc, lock); } /* clear the internal state of the keyboard */ static void pckbd_clear_state(keyboard_t *kbd) { pckbd_state_t *state; state = (pckbd_state_t *)kbd->kb_data; state->ks_flags = 0; state->ks_state &= LOCK_MASK; /* preserve locking key state */ state->ks_accents = 0; state->ks_composed_char = 0; } /* save the internal state */ static int pckbd_get_state(keyboard_t *kbd, void *buf, size_t len) { if (len == 0) return sizeof(pckbd_state_t); if (len < sizeof(pckbd_state_t)) return -1; bcopy(kbd->kb_data, buf, sizeof(pckbd_state_t)); return 0; } /* set the internal state */ static int pckbd_set_state(keyboard_t *kbd, void *buf, size_t len) { if (len < sizeof(pckbd_state_t)) return ENOMEM; if (((pckbd_state_t *)kbd->kb_data)->kbdc != ((pckbd_state_t *)buf)->kbdc) return ENOMEM; bcopy(buf, kbd->kb_data, sizeof(pckbd_state_t)); return 0; } /* set polling mode */ static int pckbd_poll(keyboard_t *kbd, int on) { return 0; } /* local functions */ static int probe_keyboard(KBDC kbdc, int flags) { return 0; } static int init_keyboard(KBDC kbdc, int *type, int flags) { *type = KB_OTHER; return 0; } /* keyboard I/O routines */ /* retry count */ #ifndef KBD_MAXRETRY #define KBD_MAXRETRY 3 #endif /* timing parameters */ #ifndef KBD_RESETDELAY #define KBD_RESETDELAY 200 /* wait 200msec after kbd/mouse reset */ #endif #ifndef KBD_MAXWAIT #define KBD_MAXWAIT 5 /* wait 5 times at most after reset */ #endif /* I/O recovery time */ #define KBDC_DELAYTIME 37 #define KBDD_DELAYTIME 37 /* I/O ports */ #define KBD_STATUS_PORT 2 /* status port, read */ #define KBD_DATA_PORT 0 /* data port, read */ /* status bits (KBD_STATUS_PORT) */ #define KBDS_BUFFER_FULL 0x0002 /* macros */ #define kbdcp(p) ((struct kbdc_softc *)(p)) /* local variables */ static struct kbdc_softc kbdc_softc[1] = { { 0 }, }; /* associate a port number with a KBDC */ static KBDC kbdc_open(int port) { if (port <= 0) port = IO_KBD; /* PC-98 has only one keyboard I/F */ kbdc_softc[0].port = port; kbdc_softc[0].lock = FALSE; return (KBDC)&kbdc_softc[0]; } /* set/reset polling lock */ static int kbdc_lock(KBDC p, int lock) { int prevlock; prevlock = kbdcp(p)->lock; kbdcp(p)->lock = lock; return (prevlock != lock); } /* check if any data is waiting to be processed */ static int kbdc_data_ready(KBDC p) { return (inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL); } /* wait for data from the keyboard */ static int wait_for_kbd_data(struct kbdc_softc *kbdc) { /* CPU will stay inside the loop for 200msec at most */ int retry = 10000; int port = kbdc->port; while (!(inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL)) { DELAY(KBDD_DELAYTIME); DELAY(KBDC_DELAYTIME); if (--retry < 0) return 0; } DELAY(KBDD_DELAYTIME); return 1; } /* read one byte from the keyboard */ static int read_kbd_data(KBDC p) { if (!wait_for_kbd_data(kbdcp(p))) return -1; /* timeout */ DELAY(KBDC_DELAYTIME); return inb(kbdcp(p)->port + KBD_DATA_PORT); } /* read one byte from the keyboard, but return immediately if * no data is waiting */ static int read_kbd_data_no_wait(KBDC p) { if (inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); return inb(kbdcp(p)->port + KBD_DATA_PORT); } return -1; /* no data */ } Index: head/sys/pc98/cbus/scterm-sck.c =================================================================== --- head/sys/pc98/cbus/scterm-sck.c (revision 298351) +++ head/sys/pc98/cbus/scterm-sck.c (revision 298352) @@ -1,1229 +1,1229 @@ /*- * Copyright (c) 1999 FreeBSD(98) Porting Team. * 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 as * the first lines of this file unmodified. * 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 AUTHORS ``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 AUTHORS 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. * * $FreeBSD$ */ #include "opt_syscons.h" #include #include #include #include #include #include #include #include #define MAX_ESC_PAR 5 #ifdef KANJI #define IS_KTYPE_ASCII_or_HANKAKU(A) (!((A) & 0xee)) #define IS_KTYPE_KANA(A) ((A) & 0x11) #define KTYPE_MASK_CTRL(A) ((A) &= 0xF0) #endif /* KANJI */ /* attribute flags */ typedef struct { u_short fg; /* foreground color */ u_short bg; /* background color */ } color_t; typedef struct { int flags; #define SCTERM_BUSY (1 << 0) int esc; int num_param; int last_param; int param[MAX_ESC_PAR]; int saved_xpos; int saved_ypos; #ifdef KANJI u_char kanji_1st_char; u_char kanji_type; #define KTYPE_ASCII 0 /* ASCII */ #define KTYPE_KANA 1 /* HANKAKU */ #define KTYPE_JKANA 0x10 /* JIS HANKAKU */ #define KTYPE_7JIS 0x20 /* JIS */ #define KTYPE_SJIS 2 /* Shift JIS */ #define KTYPE_UJIS 4 /* UJIS */ #define KTYPE_SUKANA 3 /* Shift JIS or UJIS HANKAKU */ #define KTYPE_SUJIS 6 /* SHift JIS or UJIS */ #define KTYPE_KANIN 0x80 /* Kanji Invoke sequence */ #define KTYPE_ASCIN 0x40 /* ASCII Invoke sequence */ #endif /* KANJI */ int attr_mask; /* current logical attr mask */ #define NORMAL_ATTR 0x00 #define BLINK_ATTR 0x01 #define BOLD_ATTR 0x02 #define UNDERLINE_ATTR 0x04 #define REVERSE_ATTR 0x08 #define FG_CHANGED 0x10 #define BG_CHANGED 0x20 int cur_attr; /* current hardware attr word */ color_t cur_color; /* current hardware color */ color_t std_color; /* normal hardware color */ color_t rev_color; /* reverse hardware color */ color_t dflt_std_color; /* default normal color */ color_t dflt_rev_color; /* default reverse color */ } term_stat; static sc_term_init_t scterm_init; static sc_term_term_t scterm_term; static sc_term_puts_t scterm_puts; static sc_term_ioctl_t scterm_ioctl; static sc_term_reset_t scterm_reset; static sc_term_default_attr_t scterm_default_attr; static sc_term_clear_t scterm_clear; static sc_term_notify_t scterm_notify; static sc_term_input_t scterm_input; static sc_term_fkeystr_t scterm_fkeystr; static sc_term_sw_t sc_term_sc = { { NULL, NULL }, "sck", /* emulator name */ "syscons kanji terminal", /* description */ "*", /* matching renderer, any :-) */ sizeof(term_stat), /* softc size */ 0, scterm_init, scterm_term, scterm_puts, scterm_ioctl, scterm_reset, scterm_default_attr, scterm_clear, scterm_notify, scterm_input, scterm_fkeystr, }; SCTERM_MODULE(sc, sc_term_sc); static term_stat reserved_term_stat; static int default_kanji = UJIS; static void scterm_scan_esc(scr_stat *scp, term_stat *tcp, u_char c); static int mask2attr(term_stat *tcp); #ifdef KANJI static inline u_char iskanji1(u_char mode, u_char c) { if (c > 0x80) { if ((c >= 0xa1) && (c <= 0xdf)) { if (default_kanji == UJIS) { /* UJIS */ return KTYPE_UJIS; } if (default_kanji == SJIS) { /* SJIS HANKAKU */ return KTYPE_KANA; } } if (c <= 0x9f) { if (c == 0x8e) { /* SJIS or UJIS HANKAKU */ return KTYPE_SUKANA; } /* SJIS */ default_kanji = SJIS; return KTYPE_SJIS; } if ((c >= 0xe0) && (c <= 0xef)) { /* SJIS or UJIS */ return KTYPE_SUJIS; } if ((c >= 0xf0) && (c <= 0xfe)) { /* UJIS */ default_kanji = UJIS; return KTYPE_UJIS; } } else { if ((mode == KTYPE_7JIS) && (c >= 0x21) && (c <= 0x7e)) { /* JIS */ default_kanji = UJIS; return KTYPE_7JIS; } if ((mode == KTYPE_JKANA) && (c >= 0x21) && (c <= 0x5f)) { /* JIS HANKAKU */ default_kanji = UJIS; return KTYPE_JKANA; } } return KTYPE_ASCII; } static inline u_char iskanji2(u_char mode, u_char c) { switch (mode) { case KTYPE_7JIS: if ((c >= 0x21) && (c <= 0x7e)) { /* JIS */ return KTYPE_7JIS; } break; case KTYPE_SJIS: if ((c >= 0x40) && (c <= 0xfc) && (c != 0x7f)) { /* SJIS */ return KTYPE_SJIS; } break; case KTYPE_UJIS: if ((c >= 0xa1) && (c <= 0xfe)) { /* UJIS */ return KTYPE_UJIS; } break; case KTYPE_SUKANA: if ((c >= 0xa1) && (c <= 0xdf) && (default_kanji == UJIS)) { /* UJIS HANKAKU */ return KTYPE_KANA; } if ((c >= 0x40) && (c <= 0xfc) && (c != 0x7f)) { /* SJIS */ default_kanji = SJIS; return KTYPE_SJIS; } break; case KTYPE_SUJIS: if ((c >= 0x40) && (c <= 0xa0) && (c != 0x7f)) { /* SJIS */ default_kanji = SJIS; return KTYPE_SJIS; } if ((c == 0xfd) || (c == 0xfe)) { /* UJIS */ default_kanji = UJIS; return KTYPE_UJIS; } if ((c >= 0xa1) && (c <= 0xfc)) { if (default_kanji == SJIS) return KTYPE_SJIS; if (default_kanji == UJIS) return KTYPE_UJIS; } break; } return KTYPE_ASCII; } /* * JIS X0208-83 keisen conversion table */ static u_short keiConv[32] = { 0x240c, 0x260c, 0x300c, 0x340c, 0x3c0c, 0x380c, 0x400c, 0x500c, 0x480c, 0x580c, 0x600c, 0x250c, 0x270c, 0x330c, 0x370c, 0x3f0c, 0x3b0c, 0x470c, 0x570c, 0x4f0c, 0x5f0c, 0x6f0c, 0x440c, 0x530c, 0x4c0c, 0x5b0c, 0x630c, 0x410c, 0x540c, 0x490c, 0x5c0c, 0x660c }; static u_short kanji_convert(u_char mode, u_char h, u_char l) { u_short tmp, high, low, c; high = (u_short) h; low = (u_short) l; switch (mode) { case KTYPE_SJIS: /* SHIFT JIS */ if (low >= 0xe0) { low -= 0x40; } low = (low - 0x81) * 2 + 0x21; if (high > 0x7f) { high--; } if (high > 0x9d) { low++; high -= 0x9e - 0x21; } else { high -= 0x40 - 0x21; } high &= 0x7F; low &= 0x7F; tmp = ((high << 8) | low) - 0x20; break; case KTYPE_7JIS: /* JIS */ case KTYPE_UJIS: /* UJIS */ high &= 0x7F; low &= 0x7F; tmp = ((high << 8) | low) - 0x20; break; default: tmp = 0; break; } /* keisen */ c = ((tmp & 0xff) << 8) | (tmp >> 8); /* 0x2821 .. 0x2840 */ if (0x0821 <= c && c <= 0x0840) tmp = keiConv[c - 0x0821]; return (tmp); } #endif /* KANJI */ static int scterm_init(scr_stat *scp, void **softc, int code) { term_stat *tcp; if (*softc == NULL) { if (reserved_term_stat.flags & SCTERM_BUSY) return EINVAL; *softc = &reserved_term_stat; } tcp = *softc; switch (code) { case SC_TE_COLD_INIT: bzero(tcp, sizeof(*tcp)); tcp->flags = SCTERM_BUSY; tcp->esc = 0; tcp->saved_xpos = -1; tcp->saved_ypos = -1; #ifdef KANJI tcp->kanji_1st_char = 0; tcp->kanji_type = KTYPE_ASCII; #endif tcp->attr_mask = NORMAL_ATTR; /* XXX */ tcp->dflt_std_color.fg = SC_NORM_ATTR & 0x0f; tcp->dflt_std_color.bg = (SC_NORM_ATTR >> 4) & 0x0f; tcp->dflt_rev_color.fg = SC_NORM_REV_ATTR & 0x0f; tcp->dflt_rev_color.bg = (SC_NORM_REV_ATTR >> 4) & 0x0f; tcp->std_color = tcp->dflt_std_color; tcp->rev_color = tcp->dflt_rev_color; tcp->cur_color = tcp->std_color; tcp->cur_attr = mask2attr(tcp); ++sc_term_sc.te_refcount; break; case SC_TE_WARM_INIT: tcp->esc = 0; tcp->saved_xpos = -1; tcp->saved_ypos = -1; #if 0 tcp->std_color = tcp->dflt_std_color; tcp->rev_color = tcp->dflt_rev_color; #endif tcp->cur_color = tcp->std_color; tcp->cur_attr = mask2attr(tcp); break; } return 0; } static int scterm_term(scr_stat *scp, void **softc) { if (*softc == &reserved_term_stat) { *softc = NULL; bzero(&reserved_term_stat, sizeof(reserved_term_stat)); } --sc_term_sc.te_refcount; return 0; } static void scterm_scan_esc(scr_stat *scp, term_stat *tcp, u_char c) { static u_char ansi_col[16] = { FG_BLACK, FG_RED, FG_GREEN, FG_BROWN, FG_BLUE, FG_MAGENTA, FG_CYAN, FG_LIGHTGREY, FG_DARKGREY, FG_LIGHTRED, FG_LIGHTGREEN, FG_YELLOW, FG_LIGHTBLUE, FG_LIGHTMAGENTA, FG_LIGHTCYAN, FG_WHITE }; static int cattrs[] = { 0, /* block */ CONS_BLINK_CURSOR, /* blinking block */ CONS_CHAR_CURSOR, /* underline */ CONS_CHAR_CURSOR | CONS_BLINK_CURSOR, /* blinking underline */ CONS_RESET_CURSOR, /* reset to default */ CONS_HIDDEN_CURSOR, /* hide cursor */ }; static int tcattrs[] = { CONS_RESET_CURSOR | CONS_LOCAL_CURSOR, /* normal */ CONS_HIDDEN_CURSOR | CONS_LOCAL_CURSOR, /* invisible */ CONS_BLINK_CURSOR | CONS_LOCAL_CURSOR, /* very visible */ }; sc_softc_t *sc; int v0, v1, v2; int i, n; i = n = 0; sc = scp->sc; if (tcp->esc == 1) { /* seen ESC */ #ifdef KANJI switch (tcp->kanji_type) { case KTYPE_KANIN: /* Kanji Invoke sequence */ switch (c) { case 'B': case '@': tcp->kanji_type = KTYPE_7JIS; tcp->esc = 0; tcp->kanji_1st_char = 0; return; default: tcp->kanji_type = KTYPE_ASCII; tcp->esc = 0; break; } break; case KTYPE_ASCIN: /* Ascii Invoke sequence */ switch (c) { case 'J': case 'B': case 'H': tcp->kanji_type = KTYPE_ASCII; tcp->esc = 0; tcp->kanji_1st_char = 0; return; case 'I': tcp->kanji_type = KTYPE_JKANA; tcp->esc = 0; tcp->kanji_1st_char = 0; return; default: tcp->kanji_type = KTYPE_ASCII; tcp->esc = 0; break; } break; default: break; } #endif switch (c) { case '7': /* Save cursor position */ tcp->saved_xpos = scp->xpos; tcp->saved_ypos = scp->ypos; break; case '8': /* Restore saved cursor position */ if (tcp->saved_xpos >= 0 && tcp->saved_ypos >= 0) sc_move_cursor(scp, tcp->saved_xpos, tcp->saved_ypos); break; case '[': /* Start ESC [ sequence */ tcp->esc = 2; tcp->last_param = -1; for (i = tcp->num_param; i < MAX_ESC_PAR; i++) tcp->param[i] = 1; tcp->num_param = 0; return; #ifdef KANJI case '$': /* Kanji Invoke sequence */ tcp->kanji_type = KTYPE_KANIN; return; #endif case 'M': /* Move cursor up 1 line, scroll if at top */ sc_term_up_scroll(scp, 1, sc->scr_map[0x20], tcp->cur_attr, 0, 0); break; #ifdef notyet case 'Q': tcp->esc = 4; return; #endif case 'c': /* reset */ tcp->attr_mask = NORMAL_ATTR; tcp->cur_color = tcp->std_color = tcp->dflt_std_color; tcp->rev_color = tcp->dflt_rev_color; tcp->cur_attr = mask2attr(tcp); sc_change_cursor_shape(scp, CONS_RESET_CURSOR | CONS_LOCAL_CURSOR, -1, -1); sc_clear_screen(scp); break; case '(': /* iso-2022: designate 94 character set to G0 */ #ifdef KANJI tcp->kanji_type = KTYPE_ASCIN; #else tcp->esc = 5; #endif return; } } else if (tcp->esc == 2) { /* seen ESC [ */ if (c >= '0' && c <= '9') { if (tcp->num_param < MAX_ESC_PAR) { if (tcp->last_param != tcp->num_param) { tcp->last_param = tcp->num_param; tcp->param[tcp->num_param] = 0; } else { tcp->param[tcp->num_param] *= 10; } tcp->param[tcp->num_param] += c - '0'; return; } } tcp->num_param = tcp->last_param + 1; switch (c) { case ';': if (tcp->num_param < MAX_ESC_PAR) return; break; case '=': tcp->esc = 3; tcp->last_param = -1; for (i = tcp->num_param; i < MAX_ESC_PAR; i++) tcp->param[i] = 1; tcp->num_param = 0; return; case 'A': /* up n rows */ sc_term_up(scp, tcp->param[0], 0); break; case 'B': /* down n rows */ sc_term_down(scp, tcp->param[0], 0); break; case 'C': /* right n columns */ sc_term_right(scp, tcp->param[0]); break; case 'D': /* left n columns */ sc_term_left(scp, tcp->param[0]); break; case 'E': /* cursor to start of line n lines down */ n = tcp->param[0]; if (n < 1) n = 1; sc_move_cursor(scp, 0, scp->ypos + n); break; case 'F': /* cursor to start of line n lines up */ n = tcp->param[0]; if (n < 1) n = 1; sc_move_cursor(scp, 0, scp->ypos - n); break; case 'f': /* Cursor move */ case 'H': if (tcp->num_param == 0) sc_move_cursor(scp, 0, 0); else if (tcp->num_param == 2) sc_move_cursor(scp, tcp->param[1] - 1, tcp->param[0] - 1); break; case 'J': /* Clear all or part of display */ if (tcp->num_param == 0) n = 0; else n = tcp->param[0]; sc_term_clr_eos(scp, n, sc->scr_map[0x20], tcp->cur_attr); break; case 'K': /* Clear all or part of line */ if (tcp->num_param == 0) n = 0; else n = tcp->param[0]; sc_term_clr_eol(scp, n, sc->scr_map[0x20], tcp->cur_attr); break; case 'L': /* Insert n lines */ sc_term_ins_line(scp, scp->ypos, tcp->param[0], sc->scr_map[0x20], tcp->cur_attr, 0); break; case 'M': /* Delete n lines */ sc_term_del_line(scp, scp->ypos, tcp->param[0], sc->scr_map[0x20], tcp->cur_attr, 0); break; case 'P': /* Delete n chars */ sc_term_del_char(scp, tcp->param[0], sc->scr_map[0x20], tcp->cur_attr); break; case '@': /* Insert n chars */ sc_term_ins_char(scp, tcp->param[0], sc->scr_map[0x20], tcp->cur_attr); break; case 'S': /* scroll up n lines */ sc_term_del_line(scp, 0, tcp->param[0], sc->scr_map[0x20], tcp->cur_attr, 0); break; case 'T': /* scroll down n lines */ sc_term_ins_line(scp, 0, tcp->param[0], sc->scr_map[0x20], tcp->cur_attr, 0); break; case 'X': /* erase n characters in line */ n = tcp->param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; sc_vtb_erase(&scp->vtb, scp->cursor_pos, n, sc->scr_map[0x20], tcp->cur_attr); mark_for_update(scp, scp->cursor_pos); mark_for_update(scp, scp->cursor_pos + n - 1); break; case 'Z': /* move n tabs backwards */ sc_term_backtab(scp, tcp->param[0]); break; case '`': /* move cursor to column n */ sc_term_col(scp, tcp->param[0]); break; case 'a': /* move cursor n columns to the right */ sc_term_right(scp, tcp->param[0]); break; case 'd': /* move cursor to row n */ sc_term_row(scp, tcp->param[0]); break; case 'e': /* move cursor n rows down */ sc_term_down(scp, tcp->param[0], 0); break; case 'm': /* change attribute */ if (tcp->num_param == 0) { tcp->attr_mask = NORMAL_ATTR; tcp->cur_color = tcp->std_color; tcp->cur_attr = mask2attr(tcp); break; } for (i = 0; i < tcp->num_param; i++) { switch (n = tcp->param[i]) { case 0: /* back to normal */ tcp->attr_mask = NORMAL_ATTR; tcp->cur_color = tcp->std_color; tcp->cur_attr = mask2attr(tcp); break; case 1: /* bold */ tcp->attr_mask |= BOLD_ATTR; tcp->cur_attr = mask2attr(tcp); break; case 4: /* underline */ tcp->attr_mask |= UNDERLINE_ATTR; tcp->cur_attr = mask2attr(tcp); break; case 5: /* blink */ tcp->attr_mask |= BLINK_ATTR; tcp->cur_attr = mask2attr(tcp); break; case 7: /* reverse */ tcp->attr_mask |= REVERSE_ATTR; tcp->cur_attr = mask2attr(tcp); break; case 22: /* remove bold (or dim) */ tcp->attr_mask &= ~BOLD_ATTR; tcp->cur_attr = mask2attr(tcp); break; case 24: /* remove underline */ tcp->attr_mask &= ~UNDERLINE_ATTR; tcp->cur_attr = mask2attr(tcp); break; case 25: /* remove blink */ tcp->attr_mask &= ~BLINK_ATTR; tcp->cur_attr = mask2attr(tcp); break; case 27: /* remove reverse */ tcp->attr_mask &= ~REVERSE_ATTR; tcp->cur_attr = mask2attr(tcp); break; case 30: case 31: /* set ansi fg color */ case 32: case 33: case 34: case 35: case 36: case 37: tcp->attr_mask |= FG_CHANGED; tcp->cur_color.fg = ansi_col[n - 30]; tcp->cur_attr = mask2attr(tcp); break; case 39: /* restore fg color back to normal */ tcp->attr_mask &= ~(FG_CHANGED|BOLD_ATTR); tcp->cur_color.fg = tcp->std_color.fg; tcp->cur_attr = mask2attr(tcp); break; case 40: case 41: /* set ansi bg color */ case 42: case 43: case 44: case 45: case 46: case 47: tcp->attr_mask |= BG_CHANGED; tcp->cur_color.bg = ansi_col[n - 40]; tcp->cur_attr = mask2attr(tcp); break; case 49: /* restore bg color back to normal */ tcp->attr_mask &= ~BG_CHANGED; tcp->cur_color.bg = tcp->std_color.bg; tcp->cur_attr = mask2attr(tcp); break; } } break; case 's': /* Save cursor position */ tcp->saved_xpos = scp->xpos; tcp->saved_ypos = scp->ypos; break; case 'u': /* Restore saved cursor position */ if (tcp->saved_xpos >= 0 && tcp->saved_ypos >= 0) sc_move_cursor(scp, tcp->saved_xpos, tcp->saved_ypos); break; case 'x': if (tcp->num_param == 0) n = 0; else n = tcp->param[0]; switch (n) { case 0: /* reset colors and attributes back to normal */ tcp->attr_mask = NORMAL_ATTR; tcp->cur_color = tcp->std_color = tcp->dflt_std_color; tcp->rev_color = tcp->dflt_rev_color; tcp->cur_attr = mask2attr(tcp); break; case 1: /* set ansi background */ tcp->attr_mask &= ~BG_CHANGED; tcp->cur_color.bg = tcp->std_color.bg = ansi_col[tcp->param[1] & 0x0f]; tcp->cur_attr = mask2attr(tcp); break; case 2: /* set ansi foreground */ tcp->attr_mask &= ~FG_CHANGED; tcp->cur_color.fg = tcp->std_color.fg = ansi_col[tcp->param[1] & 0x0f]; tcp->cur_attr = mask2attr(tcp); break; case 3: /* set adapter attribute directly */ tcp->attr_mask &= ~(FG_CHANGED | BG_CHANGED); tcp->cur_color.fg = tcp->std_color.fg = tcp->param[1] & 0x0f; tcp->cur_color.bg = tcp->std_color.bg = (tcp->param[1] >> 4) & 0x0f; tcp->cur_attr = mask2attr(tcp); break; case 5: /* set ansi reverse background */ tcp->rev_color.bg = ansi_col[tcp->param[1] & 0x0f]; tcp->cur_attr = mask2attr(tcp); break; case 6: /* set ansi reverse foreground */ tcp->rev_color.fg = ansi_col[tcp->param[1] & 0x0f]; tcp->cur_attr = mask2attr(tcp); break; case 7: /* set adapter reverse attribute directly */ tcp->rev_color.fg = tcp->param[1] & 0x0f; tcp->rev_color.bg = (tcp->param[1] >> 4) & 0x0f; tcp->cur_attr = mask2attr(tcp); break; } break; case 'z': /* switch to (virtual) console n */ if (tcp->num_param == 1) sc_switch_scr(sc, tcp->param[0]); break; } } else if (tcp->esc == 3) { /* seen ESC [0-9]+ = */ if (c >= '0' && c <= '9') { if (tcp->num_param < MAX_ESC_PAR) { if (tcp->last_param != tcp->num_param) { tcp->last_param = tcp->num_param; tcp->param[tcp->num_param] = 0; } else { tcp->param[tcp->num_param] *= 10; } tcp->param[tcp->num_param] += c - '0'; return; } } tcp->num_param = tcp->last_param + 1; switch (c) { case ';': if (tcp->num_param < MAX_ESC_PAR) return; break; case 'A': /* set display border color */ if (tcp->num_param == 1) { scp->border=tcp->param[0] & 0xff; if (scp == sc->cur_scp) sc_set_border(scp, scp->border); } break; case 'B': /* set bell pitch and duration */ if (tcp->num_param == 2) { scp->bell_pitch = tcp->param[0]; scp->bell_duration = (tcp->param[1] * hz + 99) / 100; } break; case 'C': /* set global/parmanent cursor type & shape */ i = spltty(); n = tcp->num_param; v0 = tcp->param[0]; v1 = tcp->param[1]; v2 = tcp->param[2]; switch (n) { case 1: /* flags only */ - if (v0 < sizeof(cattrs)/sizeof(cattrs[0])) + if (v0 < nitems(cattrs)) v0 = cattrs[v0]; else /* backward compatibility */ v0 = cattrs[v0 & 0x3]; sc_change_cursor_shape(scp, v0, -1, -1); break; case 2: v2 = 0; v0 &= 0x1f; /* backward compatibility */ v1 &= 0x1f; /* FALL THROUGH */ case 3: /* base and height */ if (v2 == 0) /* count from top */ sc_change_cursor_shape(scp, -1, scp->font_size - v1 - 1, v1 - v0 + 1); else if (v2 == 1) /* count from bottom */ sc_change_cursor_shape(scp, -1, v0, v1 - v0 + 1); break; } splx(i); break; case 'F': /* set adapter foreground */ if (tcp->num_param == 1) { tcp->attr_mask &= ~FG_CHANGED; tcp->cur_color.fg = tcp->std_color.fg = tcp->param[0] & 0x0f; tcp->cur_attr = mask2attr(tcp); } break; case 'G': /* set adapter background */ if (tcp->num_param == 1) { tcp->attr_mask &= ~BG_CHANGED; tcp->cur_color.bg = tcp->std_color.bg = tcp->param[0] & 0x0f; tcp->cur_attr = mask2attr(tcp); } break; case 'H': /* set adapter reverse foreground */ if (tcp->num_param == 1) { tcp->rev_color.fg = tcp->param[0] & 0x0f; tcp->cur_attr = mask2attr(tcp); } break; case 'I': /* set adapter reverse background */ if (tcp->num_param == 1) { tcp->rev_color.bg = tcp->param[0] & 0x0f; tcp->cur_attr = mask2attr(tcp); } break; case 'S': /* set local/temporary cursor type & shape */ i = spltty(); n = tcp->num_param; v0 = tcp->param[0]; switch (n) { case 0: v0 = 0; /* FALL THROUGH */ case 1: - if (v0 < sizeof(tcattrs)/sizeof(tcattrs[0])) + if (v0 < nitems(tcattrs)) sc_change_cursor_shape(scp, tcattrs[v0], -1, -1); break; } splx(i); break; } #ifdef notyet } else if (tcp->esc == 4) { /* seen ESC Q */ /* to be filled */ #endif } else if (tcp->esc == 5) { /* seen ESC ( */ switch (c) { case 'B': /* iso-2022: desginate ASCII into G0 */ break; /* other items to be filled */ default: break; } } tcp->esc = 0; } static void scterm_puts(scr_stat *scp, u_char *buf, int len, int kernel) { term_stat *tcp; u_char *ptr; #ifdef KANJI u_short kanji_code; #endif color_t backup; tcp = scp->ts; ptr = buf; outloop: scp->sc->write_in_progress++; backup = tcp->cur_color; if (kernel) { tcp->cur_color.fg = SC_KERNEL_CONS_ATTR & 0x0f; tcp->cur_color.bg = (SC_KERNEL_CONS_ATTR >> 4) & 0x0f; } if (tcp->esc) { scterm_scan_esc(scp, tcp, *ptr++); len--; } else if (PRINTABLE(*ptr)) { /* Print only printables */ vm_offset_t p; u_char *map; int attr; int i; int cnt; #ifdef KANJI u_char c; #endif p = sc_vtb_pointer(&scp->vtb, scp->cursor_pos); map = scp->sc->scr_map; attr = tcp->cur_attr; #ifdef KANJI c = *ptr; if (tcp->kanji_1st_char == 0) { tcp->kanji_type = iskanji1(tcp->kanji_type, c); if (!IS_KTYPE_ASCII_or_HANKAKU(tcp->kanji_type)) { /* not Ascii & not HANKAKU */ tcp->kanji_1st_char = c; goto kanji_end; } else if (tcp->kanji_type == KTYPE_ASCII) { cnt = imin(len, scp->xsize - scp->xpos); i = cnt; do { p = sc_vtb_putchar(&scp->vtb, p, map[c], attr); c = *++ptr; --i; } while (i > 0 && PRINTABLE(c) && iskanji1(tcp->kanji_type, c) == KTYPE_ASCII); len -= cnt - i; mark_for_update(scp, scp->cursor_pos); scp->cursor_pos += cnt - i; mark_for_update(scp, scp->cursor_pos - 1); scp->xpos += cnt - i; KTYPE_MASK_CTRL(tcp->kanji_type); goto ascii_end; } } else { if ((tcp->kanji_type = iskanji2(tcp->kanji_type, c)) & 0xee) { /* print kanji on TEXT VRAM */ kanji_code = kanji_convert(tcp->kanji_type, c, tcp->kanji_1st_char); mark_for_update(scp, scp->cursor_pos); for (i = 0; i < 2; i++) { /* *cursor_pos = (kanji_code | (i*0x80)); */ p = sc_vtb_putchar(&scp->vtb, p, kanji_code | ((i == 0) ? 0x00 : 0x80), attr); ++scp->cursor_pos; if (++scp->xpos >= scp->xsize) { scp->xpos = 0; scp->ypos++; } } mark_for_update(scp, scp->cursor_pos - 1); KTYPE_MASK_CTRL(tcp->kanji_type); tcp->kanji_1st_char = 0; goto kanji_end; } else { tcp->kanji_1st_char = 0; } } if (IS_KTYPE_KANA(tcp->kanji_type)) c |= 0x80; KTYPE_MASK_CTRL(tcp->kanji_type); sc_vtb_putchar(&scp->vtb, p, map[c], attr); mark_for_update(scp, scp->cursor_pos); mark_for_update(scp, scp->cursor_pos); ++scp->cursor_pos; ++scp->xpos; kanji_end: ++ptr; --len; ascii_end: #else /* !KANJI */ cnt = imin(len, scp->xsize - scp->xpos); i = cnt; do { /* * gcc-2.6.3 generates poor (un)sign extension code. * Casting the pointers in the following to volatile should * have no effect, but in fact speeds up this inner loop * from 26 to 18 cycles (+ cache misses) on i486's. */ #define UCVP(ucp) ((u_char volatile *)(ucp)) p = sc_vtb_putchar(&scp->vtb, p, UCVP(map)[*UCVP(ptr)], attr); ++ptr; --i; } while (i > 0 && PRINTABLE(*ptr)); len -= cnt - i; mark_for_update(scp, scp->cursor_pos); scp->cursor_pos += cnt - i; mark_for_update(scp, scp->cursor_pos - 1); scp->xpos += cnt - i; #endif /* !KANJI */ if (scp->xpos >= scp->xsize) { scp->xpos = 0; scp->ypos++; } } else { switch (*ptr) { case 0x07: sc_bell(scp, scp->bell_pitch, scp->bell_duration); break; case 0x08: /* non-destructive backspace */ if (scp->cursor_pos > 0) { mark_for_update(scp, scp->cursor_pos); scp->cursor_pos--; mark_for_update(scp, scp->cursor_pos); if (scp->xpos > 0) scp->xpos--; else { scp->xpos += scp->xsize - 1; scp->ypos--; } } break; case 0x09: /* non-destructive tab */ mark_for_update(scp, scp->cursor_pos); scp->cursor_pos += (8 - scp->xpos % 8u); scp->xpos += (8 - scp->xpos % 8u); if (scp->xpos >= scp->xsize) { scp->xpos = 0; scp->ypos++; scp->cursor_pos = scp->xsize * scp->ypos; } mark_for_update(scp, scp->cursor_pos); break; case 0x0a: /* newline, same pos */ mark_for_update(scp, scp->cursor_pos); scp->cursor_pos += scp->xsize; mark_for_update(scp, scp->cursor_pos); scp->ypos++; break; case 0x0c: /* form feed, clears screen */ sc_clear_screen(scp); break; case 0x0d: /* return, return to pos 0 */ mark_for_update(scp, scp->cursor_pos); scp->cursor_pos -= scp->xpos; mark_for_update(scp, scp->cursor_pos); scp->xpos = 0; break; case 0x0e: /* ^N */ tcp->kanji_type = KTYPE_JKANA; tcp->esc = 0; tcp->kanji_1st_char = 0; break; case 0x0f: /* ^O */ tcp->kanji_type = KTYPE_ASCII; tcp->esc = 0; tcp->kanji_1st_char = 0; break; case 0x1b: /* start escape sequence */ tcp->esc = 1; tcp->num_param = 0; break; } ptr++; len--; } sc_term_gen_scroll(scp, scp->sc->scr_map[0x20], tcp->cur_attr); if (kernel) tcp->cur_color = backup; scp->sc->write_in_progress--; if (len) goto outloop; } static int scterm_ioctl(scr_stat *scp, struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { term_stat *tcp = scp->ts; vid_info_t *vi; switch (cmd) { case GIO_ATTR: /* get current attributes */ /* FIXME: */ *(int*)data = (tcp->cur_attr >> 8) & 0xff; return 0; case CONS_GETINFO: /* get current (virtual) console info */ vi = (vid_info_t *)data; if (vi->size != sizeof(struct vid_info)) return EINVAL; vi->mv_norm.fore = tcp->std_color.fg; vi->mv_norm.back = tcp->std_color.bg; vi->mv_rev.fore = tcp->rev_color.fg; vi->mv_rev.back = tcp->rev_color.bg; /* * The other fields are filled by the upper routine. XXX */ return ENOIOCTL; } return ENOIOCTL; } static int scterm_reset(scr_stat *scp, int code) { /* FIXME */ return 0; } static void scterm_default_attr(scr_stat *scp, int color, int rev_color) { term_stat *tcp = scp->ts; tcp->dflt_std_color.fg = color & 0x0f; tcp->dflt_std_color.bg = (color >> 4) & 0x0f; tcp->dflt_rev_color.fg = rev_color & 0x0f; tcp->dflt_rev_color.bg = (rev_color >> 4) & 0x0f; tcp->std_color = tcp->dflt_std_color; tcp->rev_color = tcp->dflt_rev_color; tcp->cur_color = tcp->std_color; tcp->cur_attr = mask2attr(tcp); } static void scterm_clear(scr_stat *scp) { term_stat *tcp = scp->ts; sc_move_cursor(scp, 0, 0); sc_vtb_clear(&scp->vtb, scp->sc->scr_map[0x20], tcp->cur_attr); mark_all(scp); } static void scterm_notify(scr_stat *scp, int event) { switch (event) { case SC_TE_NOTIFY_VTSWITCH_IN: break; case SC_TE_NOTIFY_VTSWITCH_OUT: break; } } static int scterm_input(scr_stat *scp, int c, struct tty *tp) { return FALSE; } static const char * scterm_fkeystr(scr_stat *scp, int c) { return (NULL); } /* * Calculate hardware attributes word using logical attributes mask and * hardware colors */ /* FIXME */ static int mask2attr(term_stat *tcp) { int attr, mask = tcp->attr_mask; if (mask & REVERSE_ATTR) { attr = ((mask & FG_CHANGED) ? tcp->cur_color.bg : tcp->rev_color.fg) | (((mask & BG_CHANGED) ? tcp->cur_color.fg : tcp->rev_color.bg) << 4); } else attr = tcp->cur_color.fg | (tcp->cur_color.bg << 4); /* XXX: underline mapping for Hercules adapter can be better */ if (mask & (BOLD_ATTR | UNDERLINE_ATTR)) attr ^= 0x08; if (mask & BLINK_ATTR) attr ^= 0x80; return (attr << 8); } Index: head/sys/powerpc/powerpc/db_trace.c =================================================================== --- head/sys/powerpc/powerpc/db_trace.c (revision 298351) +++ head/sys/powerpc/powerpc/db_trace.c (revision 298352) @@ -1,310 +1,310 @@ /* $FreeBSD$ */ /* $NetBSD: db_trace.c,v 1.20 2002/05/13 20:30:09 matt Exp $ */ /* $OpenBSD: db_trace.c,v 1.3 1997/03/21 02:10:48 niklas Exp $ */ /*- * Mach Operating System * Copyright (c) 1992 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static db_varfcn_t db_frame; #define DB_OFFSET(x) (db_expr_t *)offsetof(struct trapframe, x) #ifdef __powerpc64__ #define CALLOFFSET 8 /* Include TOC reload slot */ #else #define CALLOFFSET 4 #endif struct db_variable db_regs[] = { { "r0", DB_OFFSET(fixreg[0]), db_frame }, { "r1", DB_OFFSET(fixreg[1]), db_frame }, { "r2", DB_OFFSET(fixreg[2]), db_frame }, { "r3", DB_OFFSET(fixreg[3]), db_frame }, { "r4", DB_OFFSET(fixreg[4]), db_frame }, { "r5", DB_OFFSET(fixreg[5]), db_frame }, { "r6", DB_OFFSET(fixreg[6]), db_frame }, { "r7", DB_OFFSET(fixreg[7]), db_frame }, { "r8", DB_OFFSET(fixreg[8]), db_frame }, { "r9", DB_OFFSET(fixreg[9]), db_frame }, { "r10", DB_OFFSET(fixreg[10]), db_frame }, { "r11", DB_OFFSET(fixreg[11]), db_frame }, { "r12", DB_OFFSET(fixreg[12]), db_frame }, { "r13", DB_OFFSET(fixreg[13]), db_frame }, { "r14", DB_OFFSET(fixreg[14]), db_frame }, { "r15", DB_OFFSET(fixreg[15]), db_frame }, { "r16", DB_OFFSET(fixreg[16]), db_frame }, { "r17", DB_OFFSET(fixreg[17]), db_frame }, { "r18", DB_OFFSET(fixreg[18]), db_frame }, { "r19", DB_OFFSET(fixreg[19]), db_frame }, { "r20", DB_OFFSET(fixreg[20]), db_frame }, { "r21", DB_OFFSET(fixreg[21]), db_frame }, { "r22", DB_OFFSET(fixreg[22]), db_frame }, { "r23", DB_OFFSET(fixreg[23]), db_frame }, { "r24", DB_OFFSET(fixreg[24]), db_frame }, { "r25", DB_OFFSET(fixreg[25]), db_frame }, { "r26", DB_OFFSET(fixreg[26]), db_frame }, { "r27", DB_OFFSET(fixreg[27]), db_frame }, { "r28", DB_OFFSET(fixreg[28]), db_frame }, { "r29", DB_OFFSET(fixreg[29]), db_frame }, { "r30", DB_OFFSET(fixreg[30]), db_frame }, { "r31", DB_OFFSET(fixreg[31]), db_frame }, { "srr0", DB_OFFSET(srr0), db_frame }, { "srr1", DB_OFFSET(srr1), db_frame }, { "lr", DB_OFFSET(lr), db_frame }, { "ctr", DB_OFFSET(ctr), db_frame }, { "cr", DB_OFFSET(cr), db_frame }, { "xer", DB_OFFSET(xer), db_frame }, { "dar", DB_OFFSET(dar), db_frame }, #ifdef AIM { "dsisr", DB_OFFSET(cpu.aim.dsisr), db_frame }, #endif #if defined(BOOKE) { "esr", DB_OFFSET(cpu.booke.esr), db_frame }, #endif }; -struct db_variable *db_eregs = db_regs + sizeof (db_regs)/sizeof (db_regs[0]); +struct db_variable *db_eregs = db_regs + nitems(db_regs); /* * register variable handling */ static int db_frame(struct db_variable *vp, db_expr_t *valuep, int op) { register_t *reg; if (kdb_frame == NULL) return (0); reg = (register_t*)((uintptr_t)kdb_frame + (uintptr_t)vp->valuep); if (op == DB_VAR_GET) *valuep = *reg; else *reg = *valuep; return (1); } /* * Frame tracing. */ static int db_backtrace(struct thread *td, db_addr_t fp, int count) { db_addr_t stackframe, lr, *args; boolean_t kernel_only = TRUE; boolean_t full = FALSE; #if 0 { register char *cp = modif; register char c; while ((c = *cp++) != 0) { if (c == 't') trace_thread = TRUE; if (c == 'u') kernel_only = FALSE; if (c == 'f') full = TRUE; } } #endif stackframe = fp; while (!db_pager_quit) { if (stackframe < PAGE_SIZE) break; /* * Locate the next frame by grabbing the backchain ptr * from frame[0] */ stackframe = *(db_addr_t *)stackframe; next_frame: #ifdef __powerpc64__ /* The saved arg values start at frame[6] */ args = (db_addr_t *)(stackframe + 48); #else /* The saved arg values start at frame[2] */ args = (db_addr_t *)(stackframe + 8); #endif if (stackframe < PAGE_SIZE) break; if (count-- == 0) break; /* * Extract link register from frame and subtract * 4 to convert into calling address (as opposed to * return address) */ #ifdef __powerpc64__ lr = *(db_addr_t *)(stackframe + 16) - 4; #else lr = *(db_addr_t *)(stackframe + 4) - 4; #endif if ((lr & 3) || (lr < 0x100)) { db_printf("saved LR(0x%zx) is invalid.", lr); break; } #ifdef __powerpc64__ db_printf("0x%016lx: ", stackframe); #else db_printf("0x%08x: ", stackframe); #endif /* * The trap code labels the return addresses from the * call to C code as 'trapexit' and 'asttrapexit. Use this * to determine if the callframe has to traverse a saved * trap context */ if ((lr + CALLOFFSET == (db_addr_t) &trapexit) || (lr + CALLOFFSET == (db_addr_t) &asttrapexit)) { const char *trapstr; struct trapframe *tf = (struct trapframe *)(args); db_printf("%s ", tf->srr1 & PSL_PR ? "user" : "kernel"); switch (tf->exc) { case EXC_DSI: /* XXX take advantage of the union. */ db_printf("DSI %s trap @ %#zx by ", (tf->cpu.aim.dsisr & DSISR_STORE) ? "write" : "read", tf->dar); goto print_trap; case EXC_ALI: /* XXX take advantage of the union. */ db_printf("ALI trap @ %#zx (xSR %#x) ", tf->dar, (uint32_t)tf->cpu.aim.dsisr); goto print_trap; #ifdef __powerpc64__ case EXC_DSE: db_printf("DSE trap @ %#zx by ", tf->dar); goto print_trap; case EXC_ISE: db_printf("ISE trap @ %#zx by ", tf->srr0); goto print_trap; #endif case EXC_ISI: trapstr = "ISI"; break; case EXC_PGM: trapstr = "PGM"; break; case EXC_SC: trapstr = "SC"; break; case EXC_EXI: trapstr = "EXI"; break; case EXC_MCHK: trapstr = "MCHK"; break; #if !defined(BOOKE) case EXC_VEC: trapstr = "VEC"; break; case EXC_FPA: trapstr = "FPA"; break; case EXC_BPT: trapstr = "BPT"; break; case EXC_TRC: trapstr = "TRC"; break; case EXC_RUNMODETRC: trapstr = "RUNMODETRC"; break; case EXC_SMI: trapstr = "SMI"; break; case EXC_RST: trapstr = "RST"; break; #endif case EXC_FPU: trapstr = "FPU"; break; case EXC_DECR: trapstr = "DECR"; break; case EXC_PERF: trapstr = "PERF"; break; case EXC_VSX: trapstr = "VSX"; break; default: trapstr = NULL; break; } if (trapstr != NULL) { db_printf("%s trap by ", trapstr); } else { db_printf("trap %#zx by ", tf->exc); } print_trap: lr = (db_addr_t) tf->srr0; db_printsym(lr, DB_STGY_ANY); db_printf(": srr1=%#zx\n", tf->srr1); db_printf("%-10s r1=%#zx cr=%#x xer=%#x ctr=%#zx", "", tf->fixreg[1], (uint32_t)tf->cr, (uint32_t)tf->xer, tf->ctr); #ifdef __powerpc64__ db_printf(" r2=%#zx", tf->fixreg[2]); #endif if (tf->exc == EXC_DSI) db_printf(" sr=%#x", (uint32_t)tf->cpu.aim.dsisr); db_printf("\n"); stackframe = (db_addr_t) tf->fixreg[1]; if (kernel_only && (tf->srr1 & PSL_PR)) break; goto next_frame; } db_printf("at "); db_printsym(lr, DB_STGY_PROC); if (full) /* Print all the args stored in that stackframe. */ db_printf("(%zx, %zx, %zx, %zx, %zx, %zx, %zx, %zx)", args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); db_printf("\n"); } return (0); } void db_trace_self(void) { db_addr_t addr; addr = (db_addr_t)__builtin_frame_address(1); db_backtrace(curthread, addr, -1); } int db_trace_thread(struct thread *td, int count) { struct pcb *ctx; ctx = kdb_thr_ctx(td); return (db_backtrace(td, (db_addr_t)ctx->pcb_sp, count)); } Index: head/sys/powerpc/pseries/xics.c =================================================================== --- head/sys/powerpc/pseries/xics.c (revision 298351) +++ head/sys/powerpc/pseries/xics.c (revision 298352) @@ -1,332 +1,332 @@ /*- * Copyright 2011 Nathan Whitehorn * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "phyp-hvcall.h" #include "pic_if.h" #define XICP_PRIORITY 5 /* Random non-zero number */ #define XICP_IPI 2 #define MAX_XICP_IRQS (1<<24) /* 24-bit XIRR field */ static int xicp_probe(device_t); static int xicp_attach(device_t); static int xics_probe(device_t); static int xics_attach(device_t); static void xicp_bind(device_t dev, u_int irq, cpuset_t cpumask); static void xicp_dispatch(device_t, struct trapframe *); static void xicp_enable(device_t, u_int, u_int); static void xicp_eoi(device_t, u_int); static void xicp_ipi(device_t, u_int); static void xicp_mask(device_t, u_int); static void xicp_unmask(device_t, u_int); static device_method_t xicp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, xicp_probe), DEVMETHOD(device_attach, xicp_attach), /* PIC interface */ DEVMETHOD(pic_bind, xicp_bind), DEVMETHOD(pic_dispatch, xicp_dispatch), DEVMETHOD(pic_enable, xicp_enable), DEVMETHOD(pic_eoi, xicp_eoi), DEVMETHOD(pic_ipi, xicp_ipi), DEVMETHOD(pic_mask, xicp_mask), DEVMETHOD(pic_unmask, xicp_unmask), { 0, 0 }, }; static device_method_t xics_methods[] = { /* Device interface */ DEVMETHOD(device_probe, xics_probe), DEVMETHOD(device_attach, xics_attach), { 0, 0 }, }; struct xicp_softc { struct mtx sc_mtx; int ibm_int_on; int ibm_int_off; int ibm_get_xive; int ibm_set_xive; /* XXX: inefficient -- hash table? tree? */ struct { int irq; int vector; } intvecs[256]; int nintvecs; }; static driver_t xicp_driver = { "xicp", xicp_methods, sizeof(struct xicp_softc) }; static driver_t xics_driver = { "xics", xics_methods, 0 }; static devclass_t xicp_devclass; static devclass_t xics_devclass; EARLY_DRIVER_MODULE(xicp, ofwbus, xicp_driver, xicp_devclass, 0, 0, BUS_PASS_INTERRUPT-1); EARLY_DRIVER_MODULE(xics, ofwbus, xics_driver, xics_devclass, 0, 0, BUS_PASS_INTERRUPT); static int xicp_probe(device_t dev) { if (ofw_bus_get_name(dev) == NULL || strcmp(ofw_bus_get_name(dev), "interrupt-controller") != 0) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ibm,ppc-xicp")) return (ENXIO); device_set_desc(dev, "PAPR virtual interrupt controller"); return (BUS_PROBE_GENERIC); } static int xics_probe(device_t dev) { if (ofw_bus_get_name(dev) == NULL || strcmp(ofw_bus_get_name(dev), "interrupt-controller") != 0) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ibm,ppc-xics")) return (ENXIO); device_set_desc(dev, "PAPR virtual interrupt source"); return (BUS_PROBE_GENERIC); } static int xicp_attach(device_t dev) { struct xicp_softc *sc = device_get_softc(dev); phandle_t phandle = ofw_bus_get_node(dev); mtx_init(&sc->sc_mtx, "XICP", NULL, MTX_DEF); sc->nintvecs = 0; sc->ibm_int_on = rtas_token_lookup("ibm,int-on"); sc->ibm_int_off = rtas_token_lookup("ibm,int-off"); sc->ibm_set_xive = rtas_token_lookup("ibm,set-xive"); sc->ibm_get_xive = rtas_token_lookup("ibm,get-xive"); powerpc_register_pic(dev, OF_xref_from_node(phandle), MAX_XICP_IRQS, 1 /* Number of IPIs */, FALSE); root_pic = dev; return (0); } static int xics_attach(device_t dev) { phandle_t phandle = ofw_bus_get_node(dev); /* The XICP (root PIC) will handle all our interrupts */ powerpc_register_pic(root_pic, OF_xref_from_node(phandle), MAX_XICP_IRQS, 1 /* Number of IPIs */, FALSE); return (0); } /* * PIC I/F methods. */ static void xicp_bind(device_t dev, u_int irq, cpuset_t cpumask) { struct xicp_softc *sc = device_get_softc(dev); cell_t status, cpu; int ncpus, i, error; /* * This doesn't appear to actually support affinity groups, so pick a * random CPU. */ ncpus = 0; CPU_FOREACH(cpu) if (CPU_ISSET(cpu, &cpumask)) ncpus++; i = mftb() % ncpus; ncpus = 0; CPU_FOREACH(cpu) { if (!CPU_ISSET(cpu, &cpumask)) continue; if (ncpus == i) break; ncpus++; } error = rtas_call_method(sc->ibm_set_xive, 3, 1, irq, cpu, XICP_PRIORITY, &status); if (error < 0) panic("Cannot bind interrupt %d to CPU %d", irq, cpu); } static void xicp_dispatch(device_t dev, struct trapframe *tf) { struct xicp_softc *sc; uint64_t xirr, junk; int i; sc = device_get_softc(dev); for (;;) { /* Return value in R4, use the PFT call */ phyp_pft_hcall(H_XIRR, 0, 0, 0, 0, &xirr, &junk, &junk); xirr &= 0x00ffffff; if (xirr == 0) { /* No more pending interrupts? */ phyp_hcall(H_CPPR, (uint64_t)0xff); break; } if (xirr == XICP_IPI) { /* Magic number for IPIs */ xirr = MAX_XICP_IRQS; /* Map to FreeBSD magic */ phyp_hcall(H_IPI, (uint64_t)(PCPU_GET(cpuid)), 0xff); /* Clear IPI */ } /* XXX: super inefficient */ for (i = 0; i < sc->nintvecs; i++) { if (sc->intvecs[i].irq == xirr) break; } KASSERT(i < sc->nintvecs, ("Unmapped XIRR")); powerpc_dispatch_intr(sc->intvecs[i].vector, tf); } } static void xicp_enable(device_t dev, u_int irq, u_int vector) { struct xicp_softc *sc; cell_t status, cpu; sc = device_get_softc(dev); - KASSERT(sc->nintvecs + 1 < sizeof(sc->intvecs)/sizeof(sc->intvecs[0]), - ("Too many XICP interrupts")); + KASSERT(sc->nintvecs + 1 < nitems(sc->intvecs), + ("Too many XICP interrupts")); mtx_lock(&sc->sc_mtx); sc->intvecs[sc->nintvecs].irq = irq; sc->intvecs[sc->nintvecs].vector = vector; mb(); sc->nintvecs++; mtx_unlock(&sc->sc_mtx); /* IPIs are also enabled */ if (irq == MAX_XICP_IRQS) return; /* Bind to this CPU to start: distrib. ID is last entry in gserver# */ cpu = PCPU_GET(cpuid); rtas_call_method(sc->ibm_set_xive, 3, 1, irq, cpu, XICP_PRIORITY, &status); xicp_unmask(dev, irq); } static void xicp_eoi(device_t dev, u_int irq) { uint64_t xirr; if (irq == MAX_XICP_IRQS) /* Remap IPI interrupt to internal value */ irq = XICP_IPI; xirr = irq | (XICP_PRIORITY << 24); phyp_hcall(H_EOI, xirr); } static void xicp_ipi(device_t dev, u_int cpu) { phyp_hcall(H_IPI, (uint64_t)cpu, XICP_PRIORITY); } static void xicp_mask(device_t dev, u_int irq) { struct xicp_softc *sc = device_get_softc(dev); cell_t status; if (irq == MAX_XICP_IRQS) return; rtas_call_method(sc->ibm_int_off, 1, 1, irq, &status); } static void xicp_unmask(device_t dev, u_int irq) { struct xicp_softc *sc = device_get_softc(dev); cell_t status; if (irq == MAX_XICP_IRQS) return; rtas_call_method(sc->ibm_int_on, 1, 1, irq, &status); } Index: head/sys/sparc64/sparc64/db_trace.c =================================================================== --- head/sys/sparc64/sparc64/db_trace.c (revision 298351) +++ head/sys/sparc64/sparc64/db_trace.c (revision 298352) @@ -1,291 +1,291 @@ /*- * Copyright (c) 2001 Jake Burkholder. * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static db_varfcn_t db_frame; #define DB_OFFSET(x) (db_expr_t *)offsetof(struct trapframe, x) struct db_variable db_regs[] = { { "g0", DB_OFFSET(tf_global[0]), db_frame }, { "g1", DB_OFFSET(tf_global[1]), db_frame }, { "g2", DB_OFFSET(tf_global[2]), db_frame }, { "g3", DB_OFFSET(tf_global[3]), db_frame }, { "g4", DB_OFFSET(tf_global[4]), db_frame }, { "g5", DB_OFFSET(tf_global[5]), db_frame }, { "g6", DB_OFFSET(tf_global[6]), db_frame }, { "g7", DB_OFFSET(tf_global[7]), db_frame }, { "i0", DB_OFFSET(tf_out[0]), db_frame }, { "i1", DB_OFFSET(tf_out[1]), db_frame }, { "i2", DB_OFFSET(tf_out[2]), db_frame }, { "i3", DB_OFFSET(tf_out[3]), db_frame }, { "i4", DB_OFFSET(tf_out[4]), db_frame }, { "i5", DB_OFFSET(tf_out[5]), db_frame }, { "i6", DB_OFFSET(tf_out[6]), db_frame }, { "i7", DB_OFFSET(tf_out[7]), db_frame }, { "tnpc", DB_OFFSET(tf_tnpc), db_frame }, { "tpc", DB_OFFSET(tf_tpc), db_frame }, { "tstate", DB_OFFSET(tf_tstate), db_frame }, }; -struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); +struct db_variable *db_eregs = db_regs + nitems(db_regs); static int db_frame(struct db_variable *vp, db_expr_t *valuep, int op) { uint64_t *reg; if (kdb_frame == NULL) return (0); reg = (uint64_t*)((uintptr_t)kdb_frame + (uintptr_t)vp->valuep); if (op == DB_VAR_GET) *valuep = *reg; else *reg = *valuep; return (1); } /* * User stack trace (debugging aid). */ static void db_utrace(struct thread *td, struct trapframe *tf, int count) { struct pcb *pcb; db_addr_t sp, rsp, o7, pc; int i, found; pcb = td->td_pcb; sp = db_get_value((db_addr_t)&tf->tf_sp, sizeof(tf->tf_sp), FALSE); o7 = db_get_value((db_addr_t)&tf->tf_out[7], sizeof(tf->tf_out[7]), FALSE); pc = db_get_value((db_addr_t)&tf->tf_tpc, sizeof(tf->tf_tpc), FALSE); db_printf("user trace: trap %%o7=%#lx\n", o7); while (count-- && sp != 0 && !db_pager_quit) { db_printf("pc %#lx, sp %#lx\n", pc, sp); /* First, check whether the frame is in the pcb. */ found = 0; for (i = 0; i < pcb->pcb_nsaved; i++) { if (pcb->pcb_rwsp[i] == sp) { found = 1; sp = pcb->pcb_rw[i].rw_in[6]; pc = pcb->pcb_rw[i].rw_in[7]; break; } } if (!found) { rsp = sp + SPOFF; sp = 0; if (copyin((void *)(rsp + offsetof(struct frame, fr_fp)), &sp, sizeof(sp)) != 0 || copyin((void *)(rsp + offsetof(struct frame, fr_pc)), &pc, sizeof(pc)) != 0) break; } } db_printf("done\n"); } static int db_print_trap(struct thread *td, struct trapframe *tf, int count) { struct proc *p; const char *symname; c_db_sym_t sym; db_expr_t diff; db_addr_t func; db_addr_t tpc; u_long type; u_long sfar; u_long sfsr; u_long tar; u_long level; u_long pil; u_long code; u_long o7; int user; p = td->td_proc; type = db_get_value((db_addr_t)&tf->tf_type, sizeof(tf->tf_type), FALSE); db_printf("-- %s", trap_msg[type & ~T_KERNEL]); switch (type & ~T_KERNEL) { case T_DATA_PROTECTION: tar = (u_long)db_get_value((db_addr_t)&tf->tf_tar, sizeof(tf->tf_tar), FALSE); db_printf(" tar=%#lx", tar); /* fall through */ case T_DATA_EXCEPTION: case T_INSTRUCTION_EXCEPTION: case T_MEM_ADDRESS_NOT_ALIGNED: sfar = (u_long)db_get_value((db_addr_t)&tf->tf_sfar, sizeof(tf->tf_sfar), FALSE); sfsr = (u_long)db_get_value((db_addr_t)&tf->tf_sfsr, sizeof(tf->tf_sfsr), FALSE); db_printf(" sfar=%#lx sfsr=%#lx", sfar, sfsr); break; case T_DATA_MISS: case T_INSTRUCTION_MISS: tar = (u_long)db_get_value((db_addr_t)&tf->tf_tar, sizeof(tf->tf_tar), FALSE); db_printf(" tar=%#lx", tar); break; case T_SYSCALL: code = db_get_value((db_addr_t)&tf->tf_global[1], sizeof(tf->tf_global[1]), FALSE); db_printf(" (%ld", code); if (code >= 0 && code < p->p_sysent->sv_size) { func = (db_addr_t)p->p_sysent->sv_table[code].sy_call; sym = db_search_symbol(func, DB_STGY_ANY, &diff); if (sym != DB_SYM_NULL && diff == 0) { db_symbol_values(sym, &symname, NULL); db_printf(", %s, %s", p->p_sysent->sv_name, symname); } db_printf(")"); } break; case T_INTERRUPT: level = (u_long)db_get_value((db_addr_t)&tf->tf_level, sizeof(tf->tf_level), FALSE); pil = (u_long)db_get_value((db_addr_t)&tf->tf_pil, sizeof(tf->tf_pil), FALSE); db_printf(" level=%#lx pil=%#lx", level, pil); break; default: break; } o7 = (u_long)db_get_value((db_addr_t)&tf->tf_out[7], sizeof(tf->tf_out[7]), FALSE); db_printf(" %%o7=%#lx --\n", o7); user = (type & T_KERNEL) == 0; if (user) { tpc = db_get_value((db_addr_t)&tf->tf_tpc, sizeof(tf->tf_tpc), FALSE); db_printf("userland() at "); db_printsym(tpc, DB_STGY_PROC); db_printf("\n"); db_utrace(td, tf, count); } return (user); } static int db_backtrace(struct thread *td, struct frame *fp, int count) { struct trapframe *tf; const char *name; c_db_sym_t sym; db_expr_t offset; db_expr_t value; db_addr_t npc; db_addr_t pc; int trap; int user; if (count == -1) count = 1024; trap = 0; user = 0; npc = 0; while (count-- && !user && !db_pager_quit) { pc = (db_addr_t)db_get_value((db_addr_t)&fp->fr_pc, sizeof(fp->fr_pc), FALSE); if (trap) { pc = npc; trap = 0; } if (!INKERNEL((vm_offset_t)pc)) break; sym = db_search_symbol(pc, DB_STGY_ANY, &offset); if (sym == C_DB_SYM_NULL) { value = 0; name = NULL; } else db_symbol_values(sym, &name, &value); if (name == NULL) name = "(null)"; fp = (struct frame *)(db_get_value((db_addr_t)&fp->fr_fp, sizeof(fp->fr_fp), FALSE) + SPOFF); if ((value > (u_long)tl_trap_begin && value < (u_long)tl_trap_end) || (value > (u_long)tl_text_begin && value < (u_long)tl_text_end)) { tf = (struct trapframe *)(fp + 1); npc = db_get_value((db_addr_t)&tf->tf_tpc, sizeof(tf->tf_tpc), FALSE); user = db_print_trap(td, tf, count); trap = 1; } else { db_printf("%s() at ", name); db_printsym(pc, DB_STGY_PROC); db_printf("\n"); } } return (0); } void db_trace_self(void) { db_backtrace(curthread, (struct frame *)__builtin_frame_address(1), -1); } int db_trace_thread(struct thread *td, int count) { struct pcb *ctx; ctx = kdb_thr_ctx(td); return (db_backtrace(td, (struct frame *)(ctx->pcb_sp + SPOFF), count)); } Index: head/sys/sparc64/sparc64/elf_machdep.c =================================================================== --- head/sys/sparc64/sparc64/elf_machdep.c (revision 298351) +++ head/sys/sparc64/sparc64/elf_machdep.c (revision 298352) @@ -1,424 +1,423 @@ /*- * Copyright (c) 2001 Jake Burkholder. * Copyright (c) 2000 Eduardo Horvath. * Copyright (c) 1999 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Paul Kranenburg. * * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``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 FOUNDATION OR CONTRIBUTORS * 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. * * from: NetBSD: mdreloc.c,v 1.42 2008/04/28 20:23:04 martin Exp */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "linker_if.h" static struct sysentvec elf64_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, .sv_mask = 0, .sv_errsize = 0, .sv_errtbl = NULL, .sv_transtrap = NULL, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = sendsig, .sv_sigcode = NULL, .sv_szsigcode = NULL, .sv_name = "FreeBSD ELF64", .sv_coredump = __elfN(coredump), .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_pagesize = PAGE_SIZE, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_stackprot = VM_PROT_READ | VM_PROT_WRITE, .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_FREEBSD | SV_LP64, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, }; static Elf64_Brandinfo freebsd_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_SPARCV9, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf64_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf64_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(elf64, SI_SUB_EXEC, SI_ORDER_FIRST, (sysinit_cfunc_t)elf64_insert_brand_entry, &freebsd_brand_info); static Elf64_Brandinfo freebsd_brand_oinfo = { .brand = ELFOSABI_FREEBSD, .machine = EM_SPARCV9, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/usr/libexec/ld-elf.so.1", .sysvec = &elf64_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf64_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(oelf64, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t)elf64_insert_brand_entry, &freebsd_brand_oinfo); void elf64_dump_thread(struct thread *td __unused, void *dst __unused, size_t *off __unused) { } /* * The following table holds for each relocation type: * - the width in bits of the memory location the relocation * applies to (not currently used) * - the number of bits the relocation value must be shifted to the * right (i.e. discard least significant bits) to fit into * the appropriate field in the instruction word. * - flags indicating whether * * the relocation involves a symbol * * the relocation is relative to the current position * * the relocation is for a GOT entry * * the relocation is relative to the load address * */ #define _RF_S 0x80000000 /* Resolve symbol */ #define _RF_A 0x40000000 /* Use addend */ #define _RF_P 0x20000000 /* Location relative */ #define _RF_G 0x10000000 /* GOT offset */ #define _RF_B 0x08000000 /* Load address relative */ #define _RF_U 0x04000000 /* Unaligned */ #define _RF_X 0x02000000 /* Bare symbols, needs proc */ #define _RF_D 0x01000000 /* Use dynamic TLS offset */ #define _RF_O 0x00800000 /* Use static TLS offset */ #define _RF_I 0x00400000 /* Use TLS object ID */ #define _RF_SZ(s) (((s) & 0xff) << 8) /* memory target size */ #define _RF_RS(s) ( (s) & 0xff) /* right shift */ static const int reloc_target_flags[] = { 0, /* NONE */ _RF_S|_RF_A| _RF_SZ(8) | _RF_RS(0), /* 8 */ _RF_S|_RF_A| _RF_SZ(16) | _RF_RS(0), /* 16 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 32 */ _RF_S|_RF_A|_RF_P| _RF_SZ(8) | _RF_RS(0), /* DISP_8 */ _RF_S|_RF_A|_RF_P| _RF_SZ(16) | _RF_RS(0), /* DISP_16 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* DISP_32 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_30 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_22 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(10), /* HI22 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 22 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 13 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* LO10 */ _RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT10 */ _RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT13 */ _RF_G| _RF_SZ(32) | _RF_RS(10), /* GOT22 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PC10 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PC22 */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WPLT30 */ _RF_SZ(32) | _RF_RS(0), /* COPY */ _RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* GLOB_DAT */ _RF_SZ(32) | _RF_RS(0), /* JMP_SLOT */ _RF_A| _RF_B| _RF_SZ(64) | _RF_RS(0), /* RELATIVE */ _RF_S|_RF_A| _RF_U| _RF_SZ(32) | _RF_RS(0), /* UA_32 */ _RF_A| _RF_SZ(32) | _RF_RS(0), /* PLT32 */ _RF_A| _RF_SZ(32) | _RF_RS(10), /* HIPLT22 */ _RF_A| _RF_SZ(32) | _RF_RS(0), /* LOPLT10 */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PCPLT32 */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PCPLT22 */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PCPLT10 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 10 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 11 */ _RF_S|_RF_A|_RF_X| _RF_SZ(64) | _RF_RS(0), /* 64 */ _RF_S|_RF_A|/*extra*/ _RF_SZ(32) | _RF_RS(0), /* OLO10 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(42), /* HH22 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(32), /* HM10 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(10), /* LM22 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(42), /* PC_HH22 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(32), /* PC_HM10 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PC_LM22 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP16 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP19 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* GLOB_JMP */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 7 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 5 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 6 */ _RF_S|_RF_A|_RF_P| _RF_SZ(64) | _RF_RS(0), /* DISP64 */ _RF_A| _RF_SZ(64) | _RF_RS(0), /* PLT64 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(10), /* HIX22 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* LOX10 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(22), /* H44 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(12), /* M44 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* L44 */ _RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* REGISTER */ _RF_S|_RF_A| _RF_U| _RF_SZ(64) | _RF_RS(0), /* UA64 */ _RF_S|_RF_A| _RF_U| _RF_SZ(16) | _RF_RS(0), /* UA16 */ #if 0 /* TLS */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* GD_HI22 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* GD_LO10 */ 0, /* GD_ADD */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* GD_CALL */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* LDM_HI22 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* LDM_LO10 */ 0, /* LDM_ADD */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* LDM_CALL */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* LDO_HIX22 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* LDO_LOX10 */ 0, /* LDO_ADD */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* IE_HI22 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* IE_LO10 */ 0, /* IE_LD */ 0, /* IE_LDX */ 0, /* IE_ADD */ _RF_S|_RF_A| _RF_O| _RF_SZ(32) | _RF_RS(10), /* LE_HIX22 */ _RF_S|_RF_A| _RF_O| _RF_SZ(32) | _RF_RS(0), /* LE_LOX10 */ _RF_S| _RF_I| _RF_SZ(32) | _RF_RS(0), /* DTPMOD32 */ _RF_S| _RF_I| _RF_SZ(64) | _RF_RS(0), /* DTPMOD64 */ _RF_S|_RF_A| _RF_D| _RF_SZ(32) | _RF_RS(0), /* DTPOFF32 */ _RF_S|_RF_A| _RF_D| _RF_SZ(64) | _RF_RS(0), /* DTPOFF64 */ _RF_S|_RF_A| _RF_O| _RF_SZ(32) | _RF_RS(0), /* TPOFF32 */ _RF_S|_RF_A| _RF_O| _RF_SZ(64) | _RF_RS(0) /* TPOFF64 */ #endif }; #if 0 static const char *const reloc_names[] = { "NONE", "8", "16", "32", "DISP_8", "DISP_16", "DISP_32", "WDISP_30", "WDISP_22", "HI22", "22", "13", "LO10", "GOT10", "GOT13", "GOT22", "PC10", "PC22", "WPLT30", "COPY", "GLOB_DAT", "JMP_SLOT", "RELATIVE", "UA_32", "PLT32", "HIPLT22", "LOPLT10", "LOPLT10", "PCPLT22", "PCPLT32", "10", "11", "64", "OLO10", "HH22", "HM10", "LM22", "PC_HH22", "PC_HM10", "PC_LM22", "WDISP16", "WDISP19", "GLOB_JMP", "7", "5", "6", "DISP64", "PLT64", "HIX22", "LOX10", "H44", "M44", "L44", "REGISTER", "UA64", "UA16", "GD_HI22", "GD_LO10", "GD_ADD", "GD_CALL", "LDM_HI22", "LDMO10", "LDM_ADD", "LDM_CALL", "LDO_HIX22", "LDO_LOX10", "LDO_ADD", "IE_HI22", "IE_LO10", "IE_LD", "IE_LDX", "IE_ADD", "LE_HIX22", "LE_LOX10", "DTPMOD32", "DTPMOD64", "DTPOFF32", "DTPOFF64", "TPOFF32", "TPOFF64" }; #endif #define RELOC_RESOLVE_SYMBOL(t) ((reloc_target_flags[t] & _RF_S) != 0) #define RELOC_PC_RELATIVE(t) ((reloc_target_flags[t] & _RF_P) != 0) #define RELOC_BASE_RELATIVE(t) ((reloc_target_flags[t] & _RF_B) != 0) #define RELOC_UNALIGNED(t) ((reloc_target_flags[t] & _RF_U) != 0) #define RELOC_USE_ADDEND(t) ((reloc_target_flags[t] & _RF_A) != 0) #define RELOC_BARE_SYMBOL(t) ((reloc_target_flags[t] & _RF_X) != 0) #define RELOC_USE_TLS_DOFF(t) ((reloc_target_flags[t] & _RF_D) != 0) #define RELOC_USE_TLS_OFF(t) ((reloc_target_flags[t] & _RF_O) != 0) #define RELOC_USE_TLS_ID(t) ((reloc_target_flags[t] & _RF_I) != 0) #define RELOC_TARGET_SIZE(t) ((reloc_target_flags[t] >> 8) & 0xff) #define RELOC_VALUE_RIGHTSHIFT(t) (reloc_target_flags[t] & 0xff) static const long reloc_target_bitmask[] = { #define _BM(x) (~(-(1ULL << (x)))) 0, /* NONE */ _BM(8), _BM(16), _BM(32), /* 8, 16, 32 */ _BM(8), _BM(16), _BM(32), /* DISP8, DISP16, DISP32 */ _BM(30), _BM(22), /* WDISP30, WDISP22 */ _BM(22), _BM(22), /* HI22, 22 */ _BM(13), _BM(10), /* 13, LO10 */ _BM(10), _BM(13), _BM(22), /* GOT10, GOT13, GOT22 */ _BM(10), _BM(22), /* PC10, PC22 */ _BM(30), 0, /* WPLT30, COPY */ _BM(32), _BM(32), _BM(32), /* GLOB_DAT, JMP_SLOT, RELATIVE */ _BM(32), _BM(32), /* UA32, PLT32 */ _BM(22), _BM(10), /* HIPLT22, LOPLT10 */ _BM(32), _BM(22), _BM(10), /* PCPLT32, PCPLT22, PCPLT10 */ _BM(10), _BM(11), -1, /* 10, 11, 64 */ _BM(13), _BM(22), /* OLO10, HH22 */ _BM(10), _BM(22), /* HM10, LM22 */ _BM(22), _BM(10), _BM(22), /* PC_HH22, PC_HM10, PC_LM22 */ _BM(16), _BM(19), /* WDISP16, WDISP19 */ -1, /* GLOB_JMP */ _BM(7), _BM(5), _BM(6), /* 7, 5, 6 */ -1, -1, /* DISP64, PLT64 */ _BM(22), _BM(13), /* HIX22, LOX10 */ _BM(22), _BM(10), _BM(13), /* H44, M44, L44 */ -1, -1, _BM(16), /* REGISTER, UA64, UA16 */ #if 0 _BM(22), _BM(10), 0, _BM(30), /* GD_HI22, GD_LO10, GD_ADD, GD_CALL */ _BM(22), _BM(10), 0, /* LDM_HI22, LDMO10, LDM_ADD */ _BM(30), /* LDM_CALL */ _BM(22), _BM(10), 0, /* LDO_HIX22, LDO_LOX10, LDO_ADD */ _BM(22), _BM(10), 0, 0, /* IE_HI22, IE_LO10, IE_LD, IE_LDX */ 0, /* IE_ADD */ _BM(22), _BM(13), /* LE_HIX22, LE_LOX10 */ _BM(32), -1, /* DTPMOD32, DTPMOD64 */ _BM(32), -1, /* DTPOFF32, DTPOFF64 */ _BM(32), -1 /* TPOFF32, TPOFF64 */ #endif #undef _BM }; #define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t]) int elf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup __unused) { const Elf_Rela *rela; Elf_Addr *where; if (type != ELF_RELOC_RELA) return (-1); rela = (const Elf_Rela *)data; if (ELF64_R_TYPE_ID(rela->r_info) != R_SPARC_RELATIVE) return (-1); where = (Elf_Addr *)(relocbase + rela->r_offset); *where = elf_relocaddr(lf, rela->r_addend + relocbase); return (0); } /* Process one elf relocation with addend. */ int elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { const Elf_Rela *rela; Elf_Word *where32; Elf_Addr *where; Elf_Size rtype, symidx; Elf_Addr value; Elf_Addr mask; Elf_Addr addr; int error; if (type != ELF_RELOC_RELA) return (-1); rela = (const Elf_Rela *)data; where = (Elf_Addr *)(relocbase + rela->r_offset); where32 = (Elf_Word *)where; rtype = ELF64_R_TYPE_ID(rela->r_info); symidx = ELF_R_SYM(rela->r_info); if (rtype == R_SPARC_NONE || rtype == R_SPARC_RELATIVE) return (0); if (rtype == R_SPARC_JMP_SLOT || rtype == R_SPARC_COPY || - rtype >= sizeof(reloc_target_bitmask) / - sizeof(*reloc_target_bitmask)) { + rtype >= nitems(reloc_target_bitmask)) { printf("kldload: unexpected relocation type %ld\n", rtype); return (-1); } if (RELOC_UNALIGNED(rtype)) { printf("kldload: unaligned relocation type %ld\n", rtype); return (-1); } value = rela->r_addend; if (RELOC_RESOLVE_SYMBOL(rtype)) { error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); value += addr; if (RELOC_BARE_SYMBOL(rtype)) value = elf_relocaddr(lf, value); } if (rtype == R_SPARC_OLO10) value = (value & 0x3ff) + ELF64_R_TYPE_DATA(rela->r_info); if (rtype == R_SPARC_HIX22) value ^= 0xffffffffffffffff; if (RELOC_PC_RELATIVE(rtype)) value -= (Elf_Addr)where; if (RELOC_BASE_RELATIVE(rtype)) value = elf_relocaddr(lf, value + relocbase); mask = RELOC_VALUE_BITMASK(rtype); value >>= RELOC_VALUE_RIGHTSHIFT(rtype); value &= mask; if (rtype == R_SPARC_LOX10) value |= 0x1c00; if (RELOC_TARGET_SIZE(rtype) > 32) { *where &= ~mask; *where |= value; } else { *where32 &= ~mask; *where32 |= value; } return (0); } int elf_cpu_load_file(linker_file_t lf __unused) { return (0); } int elf_cpu_unload_file(linker_file_t lf __unused) { return (0); } Index: head/sys/sparc64/sparc64/trap.c =================================================================== --- head/sys/sparc64/sparc64/trap.c (revision 298351) +++ head/sys/sparc64/sparc64/trap.c (revision 298352) @@ -1,617 +1,617 @@ /*- * Copyright (c) 2001, Jake Burkholder * Copyright (C) 1994, David Greenman * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the University of Utah, and William Jolitz. * * 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. * * from: @(#)trap.c 7.4 (Berkeley) 5/13/91 * from: FreeBSD: src/sys/i386/i386/trap.c,v 1.197 2001/07/19 */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include "opt_ktr.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void trap(struct trapframe *tf); void syscall(struct trapframe *tf); static int trap_cecc(void); static int trap_pfault(struct thread *td, struct trapframe *tf); extern char copy_fault[]; extern char copy_nofault_begin[]; extern char copy_nofault_end[]; extern char fs_fault[]; extern char fs_nofault_begin[]; extern char fs_nofault_end[]; extern char fs_nofault_intr_begin[]; extern char fs_nofault_intr_end[]; extern char fas_fault[]; extern char fas_nofault_begin[]; extern char fas_nofault_end[]; const char *const trap_msg[] = { "reserved", "instruction access exception", "instruction access error", "instruction access protection", "illtrap instruction", "illegal instruction", "privileged opcode", "floating point disabled", "floating point exception ieee 754", "floating point exception other", "tag overflow", "division by zero", "data access exception", "data access error", "data access protection", "memory address not aligned", "privileged action", "async data error", "trap instruction 16", "trap instruction 17", "trap instruction 18", "trap instruction 19", "trap instruction 20", "trap instruction 21", "trap instruction 22", "trap instruction 23", "trap instruction 24", "trap instruction 25", "trap instruction 26", "trap instruction 27", "trap instruction 28", "trap instruction 29", "trap instruction 30", "trap instruction 31", "fast instruction access mmu miss", "fast data access mmu miss", "interrupt", "physical address watchpoint", "virtual address watchpoint", "corrected ecc error", "spill", "fill", "fill", "breakpoint", "clean window", "range check", "fix alignment", "integer overflow", "syscall", "restore physical watchpoint", "restore virtual watchpoint", "kernel stack fault", }; static const int trap_sig[] = { SIGILL, /* reserved */ SIGILL, /* instruction access exception */ SIGILL, /* instruction access error */ SIGILL, /* instruction access protection */ SIGILL, /* illtrap instruction */ SIGILL, /* illegal instruction */ SIGBUS, /* privileged opcode */ SIGFPE, /* floating point disabled */ SIGFPE, /* floating point exception ieee 754 */ SIGFPE, /* floating point exception other */ SIGEMT, /* tag overflow */ SIGFPE, /* division by zero */ SIGILL, /* data access exception */ SIGILL, /* data access error */ SIGBUS, /* data access protection */ SIGBUS, /* memory address not aligned */ SIGBUS, /* privileged action */ SIGBUS, /* async data error */ SIGILL, /* trap instruction 16 */ SIGILL, /* trap instruction 17 */ SIGILL, /* trap instruction 18 */ SIGILL, /* trap instruction 19 */ SIGILL, /* trap instruction 20 */ SIGILL, /* trap instruction 21 */ SIGILL, /* trap instruction 22 */ SIGILL, /* trap instruction 23 */ SIGILL, /* trap instruction 24 */ SIGILL, /* trap instruction 25 */ SIGILL, /* trap instruction 26 */ SIGILL, /* trap instruction 27 */ SIGILL, /* trap instruction 28 */ SIGILL, /* trap instruction 29 */ SIGILL, /* trap instruction 30 */ SIGILL, /* trap instruction 31 */ SIGSEGV, /* fast instruction access mmu miss */ SIGSEGV, /* fast data access mmu miss */ -1, /* interrupt */ -1, /* physical address watchpoint */ -1, /* virtual address watchpoint */ -1, /* corrected ecc error */ SIGILL, /* spill */ SIGILL, /* fill */ SIGILL, /* fill */ SIGTRAP, /* breakpoint */ SIGILL, /* clean window */ SIGILL, /* range check */ SIGILL, /* fix alignment */ SIGILL, /* integer overflow */ SIGSYS, /* syscall */ -1, /* restore physical watchpoint */ -1, /* restore virtual watchpoint */ -1, /* kernel stack fault */ }; -CTASSERT(sizeof(trap_msg) / sizeof(*trap_msg) == T_MAX); -CTASSERT(sizeof(trap_sig) / sizeof(*trap_sig) == T_MAX); +CTASSERT(nitems(trap_msg) == T_MAX); +CTASSERT(nitems(trap_sig) == T_MAX); CTASSERT(sizeof(struct trapframe) == 256); int debugger_on_signal = 0; SYSCTL_INT(_debug, OID_AUTO, debugger_on_signal, CTLFLAG_RW, &debugger_on_signal, 0, ""); u_int corrected_ecc = 0; SYSCTL_UINT(_machdep, OID_AUTO, corrected_ecc, CTLFLAG_RD, &corrected_ecc, 0, "corrected ECC errors"); /* * SUNW,set-trap-table allows to take over %tba from the PROM, which * will turn off interrupts and handle outstanding ones while doing so, * in a safe way. */ void sun4u_set_traptable(void *tba_addr) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t tba_addr; } args = { (cell_t)"SUNW,set-trap-table", 1, 0, }; args.tba_addr = (cell_t)tba_addr; ofw_entry(&args); } void trap(struct trapframe *tf) { struct thread *td; struct proc *p; int error; int sig; register_t addr; ksiginfo_t ksi; td = curthread; CTR4(KTR_TRAP, "trap: %p type=%s (%s) pil=%#lx", td, trap_msg[tf->tf_type & ~T_KERNEL], (TRAPF_USERMODE(tf) ? "user" : "kernel"), rdpr(pil)); PCPU_INC(cnt.v_trap); if ((tf->tf_tstate & TSTATE_PRIV) == 0) { KASSERT(td != NULL, ("trap: curthread NULL")); KASSERT(td->td_proc != NULL, ("trap: curproc NULL")); p = td->td_proc; td->td_pticks = 0; td->td_frame = tf; addr = tf->tf_tpc; if (td->td_cowgen != p->p_cowgen) thread_cow_update(td); switch (tf->tf_type) { case T_DATA_MISS: case T_DATA_PROTECTION: addr = tf->tf_sfar; /* FALLTHROUGH */ case T_INSTRUCTION_MISS: sig = trap_pfault(td, tf); break; case T_FILL: sig = rwindow_load(td, tf, 2); break; case T_FILL_RET: sig = rwindow_load(td, tf, 1); break; case T_SPILL: sig = rwindow_save(td); break; case T_CORRECTED_ECC_ERROR: sig = trap_cecc(); break; default: if (tf->tf_type > T_MAX) panic("trap: bad trap type %#lx (user)", tf->tf_type); else if (trap_sig[tf->tf_type] == -1) panic("trap: %s (user)", trap_msg[tf->tf_type]); sig = trap_sig[tf->tf_type]; break; } if (sig != 0) { /* Translate fault for emulators. */ if (p->p_sysent->sv_transtrap != NULL) { sig = p->p_sysent->sv_transtrap(sig, tf->tf_type); } if (debugger_on_signal && (sig == 4 || sig == 10 || sig == 11)) kdb_enter(KDB_WHY_TRAPSIG, "trapsig"); ksiginfo_init_trap(&ksi); ksi.ksi_signo = sig; ksi.ksi_code = (int)tf->tf_type; /* XXX not POSIX */ ksi.ksi_addr = (void *)addr; ksi.ksi_trapno = (int)tf->tf_type; trapsignal(td, &ksi); } userret(td, tf); } else { KASSERT((tf->tf_type & T_KERNEL) != 0, ("trap: kernel trap isn't")); if (kdb_active) { kdb_reenter(); return; } switch (tf->tf_type & ~T_KERNEL) { case T_BREAKPOINT: case T_KSTACK_FAULT: error = (kdb_trap(tf->tf_type, 0, tf) == 0); TF_DONE(tf); break; #ifdef notyet case T_PA_WATCHPOINT: case T_VA_WATCHPOINT: error = db_watch_trap(tf); break; #endif case T_DATA_MISS: case T_DATA_PROTECTION: case T_INSTRUCTION_MISS: error = trap_pfault(td, tf); break; case T_DATA_EXCEPTION: case T_MEM_ADDRESS_NOT_ALIGNED: if ((tf->tf_sfsr & MMU_SFSR_FV) != 0 && MMU_SFSR_GET_ASI(tf->tf_sfsr) == ASI_AIUP) { if (tf->tf_tpc >= (u_long)copy_nofault_begin && tf->tf_tpc <= (u_long)copy_nofault_end) { tf->tf_tpc = (u_long)copy_fault; tf->tf_tnpc = tf->tf_tpc + 4; error = 0; break; } if (tf->tf_tpc >= (u_long)fs_nofault_begin && tf->tf_tpc <= (u_long)fs_nofault_end) { tf->tf_tpc = (u_long)fs_fault; tf->tf_tnpc = tf->tf_tpc + 4; error = 0; break; } } error = 1; break; case T_DATA_ERROR: /* * Handle PCI poke/peek as per UltraSPARC IIi * User's Manual 16.2.1, modulo checking the * TPC as USIII CPUs generate a precise trap * instead of a special deferred one. */ if (tf->tf_tpc > (u_long)fas_nofault_begin && tf->tf_tpc < (u_long)fas_nofault_end) { cache_flush(); cache_enable(PCPU_GET(impl)); tf->tf_tpc = (u_long)fas_fault; tf->tf_tnpc = tf->tf_tpc + 4; error = 0; break; } error = 1; break; case T_CORRECTED_ECC_ERROR: error = trap_cecc(); break; default: error = 1; break; } if (error != 0) { tf->tf_type &= ~T_KERNEL; if (tf->tf_type > T_MAX) panic("trap: bad trap type %#lx (kernel)", tf->tf_type); panic("trap: %s (kernel)", trap_msg[tf->tf_type]); } } CTR1(KTR_TRAP, "trap: td=%p return", td); } static int trap_cecc(void) { u_long eee; /* * Turn off (non-)correctable error reporting while we're dealing * with the error. */ eee = ldxa(0, ASI_ESTATE_ERROR_EN_REG); stxa_sync(0, ASI_ESTATE_ERROR_EN_REG, eee & ~(AA_ESTATE_NCEEN | AA_ESTATE_CEEN)); /* Flush the caches in order ensure no corrupt data got installed. */ cache_flush(); /* Ensure the caches are still turned on (should be). */ cache_enable(PCPU_GET(impl)); /* Clear the error from the AFSR. */ stxa_sync(0, ASI_AFSR, ldxa(0, ASI_AFSR)); corrected_ecc++; printf("corrected ECC error\n"); /* Turn (non-)correctable error reporting back on. */ stxa_sync(0, ASI_ESTATE_ERROR_EN_REG, eee); return (0); } static int trap_pfault(struct thread *td, struct trapframe *tf) { vm_map_t map; struct proc *p; vm_offset_t va; vm_prot_t prot; vm_map_entry_t entry; u_long ctx; int type; int rv; if (td == NULL) return (-1); KASSERT(td->td_pcb != NULL, ("trap_pfault: pcb NULL")); KASSERT(td->td_proc != NULL, ("trap_pfault: curproc NULL")); KASSERT(td->td_proc->p_vmspace != NULL, ("trap_pfault: vmspace NULL")); p = td->td_proc; rv = KERN_SUCCESS; ctx = TLB_TAR_CTX(tf->tf_tar); type = tf->tf_type & ~T_KERNEL; va = TLB_TAR_VA(tf->tf_tar); CTR4(KTR_TRAP, "trap_pfault: td=%p pm_ctx=%#lx va=%#lx ctx=%#lx", td, p->p_vmspace->vm_pmap.pm_context[curcpu], va, ctx); if (type == T_DATA_PROTECTION) prot = VM_PROT_WRITE; else { if (type == T_DATA_MISS) prot = VM_PROT_READ; else prot = VM_PROT_READ | VM_PROT_EXECUTE; } if (ctx != TLB_CTX_KERNEL) { if ((tf->tf_tstate & TSTATE_PRIV) != 0 && (tf->tf_tpc >= (u_long)fs_nofault_intr_begin && tf->tf_tpc <= (u_long)fs_nofault_intr_end)) { tf->tf_tpc = (u_long)fs_fault; tf->tf_tnpc = tf->tf_tpc + 4; return (0); } /* This is a fault on non-kernel virtual memory. */ map = &p->p_vmspace->vm_map; } else { /* * This is a fault on kernel virtual memory. Attempts to * access kernel memory from user mode cause privileged * action traps, not page fault. */ KASSERT(tf->tf_tstate & TSTATE_PRIV, ("trap_pfault: fault on nucleus context from user mode")); if (tf->tf_tpc >= (u_long)copy_nofault_begin && tf->tf_tpc <= (u_long)copy_nofault_end) { vm_map_lock_read(kernel_map); if (vm_map_lookup_entry(kernel_map, va, &entry) && (entry->eflags & MAP_ENTRY_NOFAULT) != 0) { tf->tf_tpc = (u_long)copy_fault; tf->tf_tnpc = tf->tf_tpc + 4; vm_map_unlock_read(kernel_map); return (0); } vm_map_unlock_read(kernel_map); } map = kernel_map; } /* Fault in the page. */ rv = vm_fault(map, va, prot, VM_FAULT_NORMAL); CTR3(KTR_TRAP, "trap_pfault: return td=%p va=%#lx rv=%d", td, va, rv); if (rv == KERN_SUCCESS) return (0); if (ctx != TLB_CTX_KERNEL && (tf->tf_tstate & TSTATE_PRIV) != 0) { if (tf->tf_tpc >= (u_long)fs_nofault_begin && tf->tf_tpc <= (u_long)fs_nofault_end) { tf->tf_tpc = (u_long)fs_fault; tf->tf_tnpc = tf->tf_tpc + 4; return (0); } if (tf->tf_tpc >= (u_long)copy_nofault_begin && tf->tf_tpc <= (u_long)copy_nofault_end) { tf->tf_tpc = (u_long)copy_fault; tf->tf_tnpc = tf->tf_tpc + 4; return (0); } } return ((rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV); } /* Maximum number of arguments that can be passed via the out registers. */ #define REG_MAXARGS 6 int cpu_fetch_syscall_args(struct thread *td, struct syscall_args *sa) { struct trapframe *tf; struct proc *p; register_t *argp; int reg; int regcnt; int error; p = td->td_proc; tf = td->td_frame; reg = 0; regcnt = REG_MAXARGS; sa->code = tf->tf_global[1]; if (sa->code == SYS_syscall || sa->code == SYS___syscall) { sa->code = tf->tf_out[reg++]; regcnt--; } if (p->p_sysent->sv_mask) sa->code &= p->p_sysent->sv_mask; if (sa->code >= p->p_sysent->sv_size) sa->callp = &p->p_sysent->sv_table[0]; else sa->callp = &p->p_sysent->sv_table[sa->code]; sa->narg = sa->callp->sy_narg; KASSERT(sa->narg <= sizeof(sa->args) / sizeof(sa->args[0]), ("Too many syscall arguments!")); error = 0; argp = sa->args; bcopy(&tf->tf_out[reg], sa->args, sizeof(sa->args[0]) * regcnt); if (sa->narg > regcnt) error = copyin((void *)(tf->tf_out[6] + SPOFF + offsetof(struct frame, fr_pad[6])), &sa->args[regcnt], (sa->narg - regcnt) * sizeof(sa->args[0])); if (error == 0) { td->td_retval[0] = 0; td->td_retval[1] = 0; } return (error); } #include "../../kern/subr_syscall.c" /* * Syscall handler * The arguments to the syscall are passed in the out registers by the caller, * and are saved in the trap frame. The syscall number is passed in %g1 (and * also saved in the trap frame). */ void syscall(struct trapframe *tf) { struct thread *td; struct syscall_args sa; int error; td = curthread; td->td_frame = tf; KASSERT(td != NULL, ("trap: curthread NULL")); KASSERT(td->td_proc != NULL, ("trap: curproc NULL")); /* * For syscalls, we don't want to retry the faulting instruction * (usually), instead we need to advance one instruction. */ td->td_pcb->pcb_tpc = tf->tf_tpc; TF_DONE(tf); error = syscallenter(td, &sa); syscallret(td, error, &sa); }