diff options
Diffstat (limited to 'sys')
663 files changed, 34942 insertions, 6862 deletions
diff --git a/sys/amd64/amd64/autoconf.c b/sys/amd64/amd64/autoconf.c deleted file mode 100644 index ee32740..0000000 --- a/sys/amd64/amd64/autoconf.c +++ /dev/null @@ -1,132 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * 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 REGENTS 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 REGENTS 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: @(#)autoconf.c 7.1 (Berkeley) 5/9/91 - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -/* - * Setup the system to run on the current machine. - * - * Configure() is called at boot time and initializes the vba - * device tables and the memory controller monitoring. Available - * devices are determined (from possibilities mentioned in ioconf.c), - * and the drivers are initialized. - */ -#include "opt_bootp.h" -#include "opt_isa.h" -#include "opt_bus.h" - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/bus.h> -#include <sys/conf.h> -#include <sys/reboot.h> -#include <sys/kernel.h> -#include <sys/malloc.h> -#include <sys/mount.h> -#include <sys/cons.h> - -#include <sys/socket.h> -#include <net/if.h> -#include <net/if_dl.h> -#include <net/if_types.h> -#include <net/if_var.h> -#include <net/ethernet.h> -#include <netinet/in.h> - -#include <machine/md_var.h> - -#ifdef DEV_ISA -#include <isa/isavar.h> - -device_t isa_bus_device = 0; -#endif - -static void configure_first(void *); -static void configure(void *); -static void configure_final(void *); - -SYSINIT(configure1, SI_SUB_CONFIGURE, SI_ORDER_FIRST, configure_first, NULL); -/* SI_ORDER_SECOND is hookable */ -SYSINIT(configure2, SI_SUB_CONFIGURE, SI_ORDER_THIRD, configure, NULL); -/* SI_ORDER_MIDDLE is hookable */ -SYSINIT(configure3, SI_SUB_CONFIGURE, SI_ORDER_ANY, configure_final, NULL); - -/* - * Determine i/o configuration for a machine. - */ -static void -configure_first(dummy) - void *dummy; -{ - - /* nexus0 is the top of the amd64 device tree */ - device_add_child(root_bus, "nexus", 0); -} - -static void -configure(dummy) - void *dummy; -{ - - /* - * Enable interrupts on the processor. The interrupts are still - * disabled in the interrupt controllers until interrupt handlers - * are registered. - */ - enable_intr(); - - /* initialize new bus architecture */ - root_bus_configure(); - -#ifdef DEV_ISA - /* - * Explicitly probe and attach ISA last. The isa bus saves - * it's device node at attach time for us here. - */ - if (isa_bus_device) - isa_probe_children(isa_bus_device); -#endif -} - -static void -configure_final(dummy) - void *dummy; -{ - - cninit_finish(); - if (bootverbose) - printf("Device configuration finished.\n"); - cold = 0; -} diff --git a/sys/amd64/amd64/sys_machdep.c b/sys/amd64/amd64/sys_machdep.c index 7d9a748..7f6c50e 100644 --- a/sys/amd64/amd64/sys_machdep.c +++ b/sys/amd64/amd64/sys_machdep.c @@ -580,8 +580,8 @@ amd64_set_ldt(td, uap, descs) struct i386_ldt_args *uap; struct user_segment_descriptor *descs; { - int error = 0, i; - int largest_ld; + int error = 0; + unsigned int largest_ld, i; struct mdproc *mdp = &td->td_proc->p_md; struct proc_ldt *pldt; struct user_segment_descriptor *dp; diff --git a/sys/amd64/amd64/vm_machdep.c b/sys/amd64/amd64/vm_machdep.c index 13241ac..ee6dccd 100644 --- a/sys/amd64/amd64/vm_machdep.c +++ b/sys/amd64/amd64/vm_machdep.c @@ -102,8 +102,8 @@ get_pcb_user_save_td(struct thread *td) vm_offset_t p; p = td->td_kstack + td->td_kstack_pages * PAGE_SIZE - - cpu_max_ext_state_size; - KASSERT((p % 64) == 0, ("Unaligned pcb_user_save area")); + roundup2(cpu_max_ext_state_size, XSAVE_AREA_ALIGN); + KASSERT((p % XSAVE_AREA_ALIGN) == 0, ("Unaligned pcb_user_save area")); return ((struct savefpu *)p); } @@ -122,7 +122,8 @@ get_pcb_td(struct thread *td) vm_offset_t p; p = td->td_kstack + td->td_kstack_pages * PAGE_SIZE - - cpu_max_ext_state_size - sizeof(struct pcb); + roundup2(cpu_max_ext_state_size, XSAVE_AREA_ALIGN) - + sizeof(struct pcb); return ((struct pcb *)p); } diff --git a/sys/amd64/cloudabi64/cloudabi64_sysvec.c b/sys/amd64/cloudabi64/cloudabi64_sysvec.c index 7075488..16d3ef9 100644 --- a/sys/amd64/cloudabi64/cloudabi64_sysvec.c +++ b/sys/amd64/cloudabi64/cloudabi64_sysvec.c @@ -156,5 +156,6 @@ Elf64_Brandinfo cloudabi64_brand = { .brand = ELFOSABI_CLOUDABI, .machine = EM_X86_64, .sysvec = &cloudabi64_elf_sysvec, + .flags = BI_CAN_EXEC_DYN, .compat_3_brand = "CloudABI", }; diff --git a/sys/amd64/conf/GENERIC.hints b/sys/amd64/conf/GENERIC.hints index 39beae1..31311d0 100644 --- a/sys/amd64/conf/GENERIC.hints +++ b/sys/amd64/conf/GENERIC.hints @@ -30,6 +30,5 @@ hint.atrtc.0.irq="8" hint.attimer.0.at="isa" hint.attimer.0.port="0x40" hint.attimer.0.irq="0" -hint.wbwd.0.at="isa" hint.acpi_throttle.0.disabled="1" hint.p4tcc.0.disabled="1" diff --git a/sys/amd64/include/intr_machdep.h b/sys/amd64/include/intr_machdep.h index d37c795..22f9a49 100644 --- a/sys/amd64/include/intr_machdep.h +++ b/sys/amd64/include/intr_machdep.h @@ -83,7 +83,7 @@ #ifndef LOCORE -typedef void inthand_t(u_int cs, u_int ef, u_int esp, u_int ss); +typedef void inthand_t(void); #define IDTVEC(name) __CONCAT(X,name) diff --git a/sys/amd64/include/md_var.h b/sys/amd64/include/md_var.h index ddeb257..e4c50eb 100644 --- a/sys/amd64/include/md_var.h +++ b/sys/amd64/include/md_var.h @@ -36,9 +36,6 @@ extern uint64_t *vm_page_dump; -/* XXX */ -typedef void alias_for_inthand_t(u_int cs, u_int ef, u_int esp, u_int ss); - struct savefpu; void amd64_db_resume_dbreg(void); diff --git a/sys/amd64/linux32/linux32_dummy.c b/sys/amd64/linux32/linux32_dummy.c index e4e9f50..1104651 100644 --- a/sys/amd64/linux32/linux32_dummy.c +++ b/sys/amd64/linux32/linux32_dummy.c @@ -69,7 +69,6 @@ DUMMY(mincore); DUMMY(ptrace); DUMMY(lookup_dcookie); DUMMY(remap_file_pages); -DUMMY(fstatfs64); DUMMY(mbind); DUMMY(get_mempolicy); DUMMY(set_mempolicy); diff --git a/sys/amd64/linux32/linux32_proto.h b/sys/amd64/linux32/linux32_proto.h index e76384f..fd74495 100644 --- a/sys/amd64/linux32/linux32_proto.h +++ b/sys/amd64/linux32/linux32_proto.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/amd64/linux32/syscalls.master 293907 2016-01-14 10:13:58Z glebius + * created from FreeBSD: head/sys/amd64/linux32/syscalls.master 297061 2016-03-20 13:21:20Z dchagin */ #ifndef _LINUX32_SYSPROTO_H_ @@ -844,7 +844,9 @@ struct linux_statfs64_args { char buf_l_[PADL_(struct l_statfs64_buf *)]; struct l_statfs64_buf * buf; char buf_r_[PADR_(struct l_statfs64_buf *)]; }; struct linux_fstatfs64_args { - register_t dummy; + char fd_l_[PADL_(l_uint)]; l_uint fd; char fd_r_[PADR_(l_uint)]; + char bufsize_l_[PADL_(size_t)]; size_t bufsize; char bufsize_r_[PADR_(size_t)]; + char buf_l_[PADL_(struct l_statfs64_buf *)]; struct l_statfs64_buf * buf; char buf_r_[PADR_(struct l_statfs64_buf *)]; }; struct linux_tgkill_args { char tgid_l_[PADL_(int)]; int tgid; char tgid_r_[PADR_(int)]; diff --git a/sys/amd64/linux32/linux32_syscall.h b/sys/amd64/linux32/linux32_syscall.h index 990adf3..f213377 100644 --- a/sys/amd64/linux32/linux32_syscall.h +++ b/sys/amd64/linux32/linux32_syscall.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/amd64/linux32/syscalls.master 293907 2016-01-14 10:13:58Z glebius + * created from FreeBSD: head/sys/amd64/linux32/syscalls.master 297061 2016-03-20 13:21:20Z dchagin */ #define LINUX32_SYS_linux_exit 1 diff --git a/sys/amd64/linux32/linux32_syscalls.c b/sys/amd64/linux32/linux32_syscalls.c index 79b96a7..beb6efc 100644 --- a/sys/amd64/linux32/linux32_syscalls.c +++ b/sys/amd64/linux32/linux32_syscalls.c @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/amd64/linux32/syscalls.master 293907 2016-01-14 10:13:58Z glebius + * created from FreeBSD: head/sys/amd64/linux32/syscalls.master 297061 2016-03-20 13:21:20Z dchagin */ const char *linux32_syscallnames[] = { diff --git a/sys/amd64/linux32/linux32_sysent.c b/sys/amd64/linux32/linux32_sysent.c index 7d6d220..baa9baa 100644 --- a/sys/amd64/linux32/linux32_sysent.c +++ b/sys/amd64/linux32/linux32_sysent.c @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/amd64/linux32/syscalls.master 293907 2016-01-14 10:13:58Z glebius + * created from FreeBSD: head/sys/amd64/linux32/syscalls.master 297061 2016-03-20 13:21:20Z dchagin */ #include "opt_compat.h" @@ -288,7 +288,7 @@ struct sysent linux32_sysent[] = { { AS(linux_clock_getres_args), (sy_call_t *)linux_clock_getres, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC }, /* 266 = linux_clock_getres */ { AS(linux_clock_nanosleep_args), (sy_call_t *)linux_clock_nanosleep, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC }, /* 267 = linux_clock_nanosleep */ { AS(linux_statfs64_args), (sy_call_t *)linux_statfs64, AUE_STATFS, NULL, 0, 0, 0, SY_THR_STATIC }, /* 268 = linux_statfs64 */ - { 0, (sy_call_t *)linux_fstatfs64, AUE_FSTATFS, NULL, 0, 0, 0, SY_THR_STATIC }, /* 269 = linux_fstatfs64 */ + { AS(linux_fstatfs64_args), (sy_call_t *)linux_fstatfs64, AUE_FSTATFS, NULL, 0, 0, 0, SY_THR_STATIC }, /* 269 = linux_fstatfs64 */ { AS(linux_tgkill_args), (sy_call_t *)linux_tgkill, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC }, /* 270 = linux_tgkill */ { AS(linux_utimes_args), (sy_call_t *)linux_utimes, AUE_UTIMES, NULL, 0, 0, 0, SY_THR_STATIC }, /* 271 = linux_utimes */ { AS(linux_fadvise64_64_args), (sy_call_t *)linux_fadvise64_64, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC }, /* 272 = linux_fadvise64_64 */ diff --git a/sys/amd64/linux32/linux32_systrace_args.c b/sys/amd64/linux32/linux32_systrace_args.c index 80889bf..fc4c89a 100644 --- a/sys/amd64/linux32/linux32_systrace_args.c +++ b/sys/amd64/linux32/linux32_systrace_args.c @@ -1820,7 +1820,11 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) } /* linux_fstatfs64 */ case 269: { - *n_args = 0; + struct linux_fstatfs64_args *p = params; + iarg[0] = p->fd; /* l_uint */ + uarg[1] = p->bufsize; /* size_t */ + uarg[2] = (intptr_t) p->buf; /* struct l_statfs64_buf * */ + *n_args = 3; break; } /* linux_tgkill */ @@ -5118,6 +5122,19 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; /* linux_fstatfs64 */ case 269: + switch(ndx) { + case 0: + p = "l_uint"; + break; + case 1: + p = "size_t"; + break; + case 2: + p = "struct l_statfs64_buf *"; + break; + default: + break; + }; break; /* linux_tgkill */ case 270: @@ -6878,6 +6895,9 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; /* linux_fstatfs64 */ case 269: + if (ndx == 0 || ndx == 1) + p = "int"; + break; /* linux_tgkill */ case 270: if (ndx == 0 || ndx == 1) diff --git a/sys/amd64/linux32/syscalls.master b/sys/amd64/linux32/syscalls.master index 0396b30..e40247e 100644 --- a/sys/amd64/linux32/syscalls.master +++ b/sys/amd64/linux32/syscalls.master @@ -450,7 +450,7 @@ 267 AUE_NULL STD { int linux_clock_nanosleep(clockid_t which, int flags, \ struct l_timespec *rqtp, struct l_timespec *rmtp); } 268 AUE_STATFS STD { int linux_statfs64(char *path, size_t bufsize, struct l_statfs64_buf *buf); } -269 AUE_FSTATFS STD { int linux_fstatfs64(void); } +269 AUE_FSTATFS STD { int linux_fstatfs64(l_uint fd, size_t bufsize, struct l_statfs64_buf *buf); } 270 AUE_NULL STD { int linux_tgkill(int tgid, int pid, int sig); } 271 AUE_UTIMES STD { int linux_utimes(char *fname, \ struct l_timeval *tptr); } diff --git a/sys/arm/allwinner/a10_gpio.c b/sys/arm/allwinner/a10_gpio.c index df774b3..0eae992 100644 --- a/sys/arm/allwinner/a10_gpio.c +++ b/sys/arm/allwinner/a10_gpio.c @@ -136,8 +136,6 @@ extern const struct allwinner_padconf a31s_padconf; #define A10_GPIO_GP_INT_STA 0x214 #define A10_GPIO_GP_INT_DEB 0x218 -static struct a10_gpio_softc *a10_gpio_sc; - #define A10_GPIO_WRITE(_sc, _off, _val) \ bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val) #define A10_GPIO_READ(_sc, _off) \ @@ -562,12 +560,6 @@ a10_gpio_attach(device_t dev) /* Node is not a GPIO controller. */ goto fail; - a10_gpio_sc = sc; - sc->sc_busdev = gpiobus_attach_bus(dev); - if (sc->sc_busdev == NULL) - goto fail; - - /* Use the right pin data for the current SoC */ switch (allwinner_soc_type()) { #ifdef SOC_ALLWINNER_A10 @@ -594,6 +586,10 @@ a10_gpio_attach(device_t dev) return (ENOENT); } + sc->sc_busdev = gpiobus_attach_bus(dev); + if (sc->sc_busdev == NULL) + goto fail; + /* * Register as a pinctrl device */ diff --git a/sys/arm/allwinner/a10_hdmi.c b/sys/arm/allwinner/a10_hdmi.c index 9bb9869..ff86fa0 100644 --- a/sys/arm/allwinner/a10_hdmi.c +++ b/sys/arm/allwinner/a10_hdmi.c @@ -195,6 +195,15 @@ __FBSDID("$FreeBSD$"); #define CEA_TAG_ID 0x02 #define CEA_DTD 0x03 #define DTD_BASIC_AUDIO (1 << 6) +#define CEA_REV 0x02 +#define CEA_DATA_OFF 0x03 +#define CEA_DATA_START 4 +#define BLOCK_TAG(x) (((x) >> 5) & 0x7) +#define BLOCK_TAG_VSDB 3 +#define BLOCK_LEN(x) ((x) & 0x1f) +#define HDMI_VSDB_MINLEN 5 +#define HDMI_OUI "\x03\x0c\x00" +#define HDMI_OUI_LEN 3 struct a10hdmi_softc { struct resource *res; @@ -372,6 +381,41 @@ a10hdmi_ddc_read(struct a10hdmi_softc *sc, int block, uint8_t *edid) return (0); } +static int +a10hdmi_detect_hdmi_vsdb(uint8_t *edid) +{ + int off, p, btag, blen; + + if (edid[EXT_TAG] != CEA_TAG_ID) + return (0); + + off = edid[CEA_DATA_OFF]; + + /* CEA data block collection starts at byte 4 */ + if (off <= CEA_DATA_START) + return (0); + + /* Parse the CEA data blocks */ + for (p = CEA_DATA_START; p < off;) { + btag = BLOCK_TAG(edid[p]); + blen = BLOCK_LEN(edid[p]); + + /* Make sure the length is sane */ + if (p + blen + 1 > off) + break; + + /* Look for a VSDB with the HDMI 24-bit IEEE registration ID */ + if (btag == BLOCK_TAG_VSDB && blen >= HDMI_VSDB_MINLEN && + memcmp(&edid[p + 1], HDMI_OUI, HDMI_OUI_LEN) == 0) + return (1); + + /* Next data block */ + p += (1 + blen); + } + + return (0); +} + static void a10hdmi_detect_hdmi(struct a10hdmi_softc *sc, int *phdmi, int *paudio) { @@ -389,7 +433,7 @@ a10hdmi_detect_hdmi(struct a10hdmi_softc *sc, int *phdmi, int *paudio) if (a10hdmi_ddc_read(sc, block, edid) != 0) return; - if (edid[EXT_TAG] == CEA_TAG_ID) { + if (a10hdmi_detect_hdmi_vsdb(edid) != 0) { *phdmi = 1; *paudio = ((edid[CEA_DTD] & DTD_BASIC_AUDIO) != 0); return; @@ -518,6 +562,9 @@ a10hdmi_set_videomode(device_t dev, const struct videomode *mode) PLLCTRL0_VCO_S); /* Setup display settings */ + if (bootverbose) + device_printf(dev, "HDMI: %s, Audio: %s\n", + sc->has_hdmi ? "yes" : "no", sc->has_audio ? "yes" : "no"); val = 0; if (sc->has_hdmi) val |= VID_CTRL_HDMI_MODE; diff --git a/sys/arm/allwinner/a10_mmc.c b/sys/arm/allwinner/a10_mmc.c index b236c4b..dc341dd 100644 --- a/sys/arm/allwinner/a10_mmc.c +++ b/sys/arm/allwinner/a10_mmc.c @@ -943,3 +943,4 @@ static driver_t a10_mmc_driver = { DRIVER_MODULE(a10_mmc, simplebus, a10_mmc_driver, a10_mmc_devclass, 0, 0); DRIVER_MODULE(mmc, a10_mmc, mmc_driver, mmc_devclass, NULL, NULL); +MODULE_DEPEND(a10_mmc, mmc, 1, 1, 1); diff --git a/sys/arm/allwinner/allwinner_machdep.c b/sys/arm/allwinner/allwinner_machdep.c index 549fdcb..7159acd 100644 --- a/sys/arm/allwinner/allwinner_machdep.c +++ b/sys/arm/allwinner/allwinner_machdep.c @@ -140,6 +140,7 @@ cpu_reset() while (1); } +#if defined(SOC_ALLWINNER_A10) static platform_method_t a10_methods[] = { PLATFORMMETHOD(platform_attach, a10_attach), PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), @@ -147,7 +148,10 @@ static platform_method_t a10_methods[] = { PLATFORMMETHOD_END, }; +FDT_PLATFORM_DEF(a10, "a10", 0, "allwinner,sun4i-a10"); +#endif +#if defined(SOC_ALLWINNER_A20) static platform_method_t a20_methods[] = { PLATFORMMETHOD(platform_attach, a20_attach), PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), @@ -159,7 +163,10 @@ static platform_method_t a20_methods[] = { #endif PLATFORMMETHOD_END, }; +FDT_PLATFORM_DEF(a20, "a20", 0, "allwinner,sun7i-a20"); +#endif +#if defined(SOC_ALLWINNER_A31) static platform_method_t a31_methods[] = { PLATFORMMETHOD(platform_attach, a31_attach), PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), @@ -171,7 +178,10 @@ static platform_method_t a31_methods[] = { #endif PLATFORMMETHOD_END, }; +FDT_PLATFORM_DEF(a31, "a31", 0, "allwinner,sun6i-a31"); +#endif +#if defined(SOC_ALLWINNER_A31S) static platform_method_t a31s_methods[] = { PLATFORMMETHOD(platform_attach, a31s_attach), PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), @@ -183,6 +193,8 @@ static platform_method_t a31s_methods[] = { #endif PLATFORMMETHOD_END, }; +FDT_PLATFORM_DEF(a31s, "a31s", 0, "allwinner,sun6i-a31s"); +#endif u_int allwinner_soc_type(void) @@ -195,8 +207,3 @@ allwinner_soc_family(void) { return (soc_family); } - -FDT_PLATFORM_DEF(a10, "a10", 0, "allwinner,sun4i-a10"); -FDT_PLATFORM_DEF(a20, "a20", 0, "allwinner,sun7i-a20"); -FDT_PLATFORM_DEF(a31, "a31", 0, "allwinner,sun6i-a31"); -FDT_PLATFORM_DEF(a31s, "a31s", 0, "allwinner,sun6i-a31s"); diff --git a/sys/arm/amlogic/aml8726/aml8726_mmc.c b/sys/arm/amlogic/aml8726/aml8726_mmc.c index a7cc9c1..0e24975 100644 --- a/sys/arm/amlogic/aml8726/aml8726_mmc.c +++ b/sys/arm/amlogic/aml8726/aml8726_mmc.c @@ -1099,3 +1099,4 @@ DRIVER_MODULE(aml8726_mmc, simplebus, aml8726_mmc_driver, aml8726_mmc_devclass, 0, 0); MODULE_DEPEND(aml8726_mmc, aml8726_gpio, 1, 1, 1); DRIVER_MODULE(mmc, aml8726_mmc, mmc_driver, mmc_devclass, NULL, NULL); +MODULE_DEPEND(aml8726_mmc, mmc, 1, 1, 1); diff --git a/sys/arm/amlogic/aml8726/aml8726_sdxc-m8.c b/sys/arm/amlogic/aml8726/aml8726_sdxc-m8.c index fc6a66e..3daa99f 100644 --- a/sys/arm/amlogic/aml8726/aml8726_sdxc-m8.c +++ b/sys/arm/amlogic/aml8726/aml8726_sdxc-m8.c @@ -1378,3 +1378,4 @@ DRIVER_MODULE(aml8726_sdxc, simplebus, aml8726_sdxc_driver, aml8726_sdxc_devclass, 0, 0); MODULE_DEPEND(aml8726_sdxc, aml8726_gpio, 1, 1, 1); DRIVER_MODULE(mmc, aml8726_sdxc, mmc_driver, mmc_devclass, NULL, NULL); +MODULE_DEPEND(aml8726_sdxc, mmc, 1, 1, 1); diff --git a/sys/arm/arm/gic.c b/sys/arm/arm/gic.c index cf72820..23b9577 100644 --- a/sys/arm/arm/gic.c +++ b/sys/arm/arm/gic.c @@ -36,8 +36,6 @@ __FBSDID("$FreeBSD$"); #include "opt_platform.h" -#include "opt_platform.h" - #include <sys/param.h> #include <sys/systm.h> #include <sys/bus.h> @@ -86,6 +84,7 @@ __FBSDID("$FreeBSD$"); #define GICD_ITARGETSR(n) (0x0800 + ((n) * 4)) /* v1 ICDIPTR */ #define GICD_ICFGR(n) (0x0C00 + ((n) * 4)) /* v1 ICDICFR */ #define GICD_SGIR(n) (0x0F00 + ((n) * 4)) /* v1 ICDSGIR */ +#define GICD_SGI_TARGET_SHIFT 16 /* CPU Registers */ #define GICC_CTLR 0x0000 /* v1 ICCICR */ @@ -118,16 +117,28 @@ __FBSDID("$FreeBSD$"); #endif #ifdef ARM_INTRNG +struct gic_irqsrc { + struct intr_irqsrc gi_isrc; + uint32_t gi_irq; + enum intr_polarity gi_pol; + enum intr_trigger gi_trig; +}; + static u_int gic_irq_cpu; static int arm_gic_intr(void *); -static int arm_gic_bind(device_t dev, struct intr_irqsrc *isrc); +static int arm_gic_bind_intr(device_t dev, struct intr_irqsrc *isrc); + +#ifdef SMP +u_int sgi_to_ipi[GIC_LAST_SGI - GIC_FIRST_SGI + 1]; +u_int sgi_first_unused = GIC_FIRST_SGI; +#endif #endif struct arm_gic_softc { device_t gic_dev; #ifdef ARM_INTRNG void * gic_intrhand; - struct intr_irqsrc ** gic_irqs; + struct gic_irqsrc * gic_irqs; #endif struct resource * gic_res[3]; bus_space_tag_t gic_c_bst; @@ -142,6 +153,10 @@ struct arm_gic_softc { #endif }; +#ifdef ARM_INTRNG +#define GIC_INTR_ISRC(sc, irq) (&sc->gic_irqs[irq].gi_isrc) +#endif + static struct resource_spec arm_gic_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Distributor registers */ { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* CPU Interrupt Intf. registers */ @@ -151,6 +166,8 @@ static struct resource_spec arm_gic_spec[] = { { -1, 0 } }; +static u_int arm_gic_map[MAXCPU]; + static struct arm_gic_softc *gic_sc = NULL; #define gic_c_read_4(_sc, _reg) \ @@ -210,6 +227,29 @@ gic_irq_mask(struct arm_gic_softc *sc, u_int irq) } #endif +static uint8_t +gic_cpu_mask(struct arm_gic_softc *sc) +{ + uint32_t mask; + int i; + + /* Read the current cpuid mask by reading ITARGETSR{0..7} */ + for (i = 0; i < 8; i++) { + mask = gic_d_read_4(sc, GICD_ITARGETSR(i)); + if (mask != 0) + break; + } + /* No mask found, assume we are on CPU interface 0 */ + if (mask == 0) + return (1); + + /* Collect the mask in the lower byte */ + mask |= mask >> 16; + mask |= mask >> 8; + + return (mask); +} + #ifdef SMP #ifdef ARM_INTRNG static void @@ -219,6 +259,9 @@ arm_gic_init_secondary(device_t dev) struct intr_irqsrc *isrc; u_int irq; + /* Set the mask so we can find this CPU to send it IPIs */ + arm_gic_map[PCPU_GET(cpuid)] = gic_cpu_mask(sc); + for (irq = 0; irq < sc->nirqs; irq += 4) gic_d_write_4(sc, GICD_IPRIORITYR(irq >> 2), 0); @@ -238,7 +281,7 @@ arm_gic_init_secondary(device_t dev) /* Unmask attached SGI interrupts. */ for (irq = GIC_FIRST_SGI; irq <= GIC_LAST_SGI; irq++) { - isrc = sc->gic_irqs[irq]; + isrc = GIC_INTR_ISRC(sc, irq); if (isrc != NULL && isrc->isrc_handlers != 0) { CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); gic_irq_unmask(sc, irq); @@ -247,7 +290,7 @@ arm_gic_init_secondary(device_t dev) /* Unmask attached PPI interrupts. */ for (irq = GIC_FIRST_PPI; irq <= GIC_LAST_PPI; irq++) { - isrc = sc->gic_irqs[irq]; + isrc = GIC_INTR_ISRC(sc, irq); if (isrc == NULL || isrc->isrc_handlers == 0) continue; if (isrc->isrc_flags & INTR_ISRCF_BOUND) { @@ -266,6 +309,9 @@ arm_gic_init_secondary(device_t dev) struct arm_gic_softc *sc = device_get_softc(dev); int i; + /* Set the mask so we can find this CPU to send it IPIs */ + arm_gic_map[PCPU_GET(cpuid)] = gic_cpu_mask(sc); + for (i = 0; i < sc->nirqs; i += 4) gic_d_write_4(sc, GICD_IPRIORITYR(i >> 2), 0); @@ -364,6 +410,46 @@ gic_xref(device_t dev) return (0); #endif } + +static int +arm_gic_register_isrcs(struct arm_gic_softc *sc, uint32_t num) +{ + int error; + uint32_t irq; + struct gic_irqsrc *irqs; + struct intr_irqsrc *isrc; + const char *name; + + irqs = malloc(num * sizeof(struct gic_irqsrc), M_DEVBUF, + M_WAITOK | M_ZERO); + + name = device_get_nameunit(sc->gic_dev); + for (irq = 0; irq < num; irq++) { + irqs[irq].gi_irq = irq; + irqs[irq].gi_pol = INTR_POLARITY_CONFORM; + irqs[irq].gi_trig = INTR_TRIGGER_CONFORM; + + isrc = &irqs[irq].gi_isrc; + if (irq <= GIC_LAST_SGI) { + error = intr_isrc_register(isrc, sc->gic_dev, + INTR_ISRCF_IPI, "%s,i%u", name, irq - GIC_FIRST_SGI); + } else if (irq <= GIC_LAST_PPI) { + error = intr_isrc_register(isrc, sc->gic_dev, + INTR_ISRCF_PPI, "%s,p%u", name, irq - GIC_FIRST_PPI); + } else { + error = intr_isrc_register(isrc, sc->gic_dev, 0, + "%s,s%u", name, irq - GIC_FIRST_SPI); + } + if (error != 0) { + /* XXX call intr_isrc_deregister() */ + free(irqs, M_DEVBUF); + return (error); + } + } + sc->gic_irqs = irqs; + sc->nirqs = num; + return (0); +} #endif static int @@ -371,7 +457,7 @@ arm_gic_attach(device_t dev) { struct arm_gic_softc *sc; int i; - uint32_t icciidr; + uint32_t icciidr, mask, nirqs; #ifdef ARM_INTRNG phandle_t pxref; intptr_t xref = gic_xref(dev); @@ -405,13 +491,17 @@ arm_gic_attach(device_t dev) gic_d_write_4(sc, GICD_CTLR, 0x00); /* Get the number of interrupts */ - sc->nirqs = gic_d_read_4(sc, GICD_TYPER); - sc->nirqs = 32 * ((sc->nirqs & 0x1f) + 1); + nirqs = gic_d_read_4(sc, GICD_TYPER); + nirqs = 32 * ((nirqs & 0x1f) + 1); #ifdef ARM_INTRNG - sc->gic_irqs = malloc(sc->nirqs * sizeof (*sc->gic_irqs), M_DEVBUF, - M_WAITOK | M_ZERO); + if (arm_gic_register_isrcs(sc, nirqs)) { + device_printf(dev, "could not register irqs\n"); + goto cleanup; + } #else + sc->nirqs = nirqs; + /* Set up function pointers */ arm_post_filter = gic_post_filter; arm_config_irq = gic_config_irq; @@ -432,10 +522,19 @@ arm_gic_attach(device_t dev) gic_d_write_4(sc, GICD_ICENABLER(i >> 5), 0xFFFFFFFF); } + /* Find the current cpu mask */ + mask = gic_cpu_mask(sc); + /* Set the mask so we can find this CPU to send it IPIs */ + arm_gic_map[PCPU_GET(cpuid)] = mask; + /* Set all four targets to this cpu */ + mask |= mask << 8; + mask |= mask << 16; + for (i = 0; i < sc->nirqs; i += 4) { gic_d_write_4(sc, GICD_IPRIORITYR(i >> 2), 0); - gic_d_write_4(sc, GICD_ITARGETSR(i >> 2), - 1 << 0 | 1 << 8 | 1 << 16 | 1 << 24); + if (i > 32) { + gic_d_write_4(sc, GICD_ITARGETSR(i >> 2), mask); + } } /* Set all the interrupts to be in Group 0 (secure) */ @@ -473,20 +572,20 @@ arm_gic_attach(device_t dev) if (intr_pic_claim_root(dev, xref, arm_gic_intr, sc, GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) { device_printf(dev, "could not set PIC as a root\n"); - intr_pic_unregister(dev, xref); + intr_pic_deregister(dev, xref); goto cleanup; } } else { if (sc->gic_res[2] == NULL) { device_printf(dev, "not root PIC must have defined interrupt\n"); - intr_pic_unregister(dev, xref); + intr_pic_deregister(dev, xref); goto cleanup; } if (bus_setup_intr(dev, sc->gic_res[2], INTR_TYPE_CLK, arm_gic_intr, NULL, sc, &sc->gic_intrhand)) { device_printf(dev, "could not setup irq handler\n"); - intr_pic_unregister(dev, xref); + intr_pic_deregister(dev, xref); goto cleanup; } } @@ -510,7 +609,7 @@ static int arm_gic_intr(void *arg) { struct arm_gic_softc *sc = arg; - struct intr_irqsrc *isrc; + struct gic_irqsrc *gi; uint32_t irq_active_reg, irq; struct trapframe *tf; @@ -546,14 +645,7 @@ arm_gic_intr(void *arg) tf = curthread->td_intr_frame; dispatch_irq: - isrc = sc->gic_irqs[irq]; - if (isrc == NULL) { - device_printf(sc->gic_dev, "Stray interrupt %u detected\n", irq); - gic_irq_mask(sc, irq); - gic_c_write_4(sc, GICC_EOIR, irq_active_reg); - goto next_irq; - } - + gi = sc->gic_irqs + irq; /* * Note that GIC_FIRST_SGI is zero and is not used in 'if' statement * as compiler complains that comparing u_int >= 0 is always true. @@ -562,7 +654,7 @@ dispatch_irq: #ifdef SMP /* Call EOI for all IPI before dispatch. */ gic_c_write_4(sc, GICC_EOIR, irq_active_reg); - intr_ipi_dispatch(isrc, tf); + intr_ipi_dispatch(sgi_to_ipi[gi->gi_irq], tf); goto next_irq; #else device_printf(sc->gic_dev, "SGI %u on UP system detected\n", @@ -575,10 +667,15 @@ dispatch_irq: #ifdef GIC_DEBUG_SPURIOUS sc->last_irq[PCPU_GET(cpuid)] = irq; #endif - if (isrc->isrc_trig == INTR_TRIGGER_EDGE) + if (gi->gi_trig == INTR_TRIGGER_EDGE) gic_c_write_4(sc, GICC_EOIR, irq_active_reg); - intr_irq_dispatch(isrc, tf); + if (intr_isrc_dispatch(&gi->gi_isrc, tf) != 0) { + gic_irq_mask(sc, irq); + if (gi->gi_trig != INTR_TRIGGER_EDGE) + gic_c_write_4(sc, GICC_EOIR, irq_active_reg); + device_printf(sc->gic_dev, "Stray irq %u disabled\n", irq); + } next_irq: arm_irq_memory_barrier(irq); @@ -590,52 +687,6 @@ next_irq: return (FILTER_HANDLED); } -static int -gic_attach_isrc(struct arm_gic_softc *sc, struct intr_irqsrc *isrc, u_int irq) -{ - const char *name; - - /* - * 1. The link between ISRC and controller must be set atomically. - * 2. Just do things only once in rare case when consumers - * of shared interrupt came here at the same moment. - */ - mtx_lock_spin(&sc->mutex); - if (sc->gic_irqs[irq] != NULL) { - mtx_unlock_spin(&sc->mutex); - return (sc->gic_irqs[irq] == isrc ? 0 : EEXIST); - } - sc->gic_irqs[irq] = isrc; - isrc->isrc_data = irq; - mtx_unlock_spin(&sc->mutex); - - name = device_get_nameunit(sc->gic_dev); - if (irq <= GIC_LAST_SGI) - intr_irq_set_name(isrc, "%s,i%u", name, irq - GIC_FIRST_SGI); - else if (irq <= GIC_LAST_PPI) - intr_irq_set_name(isrc, "%s,p%u", name, irq - GIC_FIRST_PPI); - else - intr_irq_set_name(isrc, "%s,s%u", name, irq - GIC_FIRST_SPI); - return (0); -} - -static int -gic_detach_isrc(struct arm_gic_softc *sc, struct intr_irqsrc *isrc, u_int irq) -{ - - mtx_lock_spin(&sc->mutex); - if (sc->gic_irqs[irq] != isrc) { - mtx_unlock_spin(&sc->mutex); - return (sc->gic_irqs[irq] == NULL ? 0 : EINVAL); - } - sc->gic_irqs[irq] = NULL; - isrc->isrc_data = 0; - mtx_unlock_spin(&sc->mutex); - - intr_irq_set_name(isrc, ""); - return (0); -} - static void gic_config(struct arm_gic_softc *sc, u_int irq, enum intr_trigger trig, enum intr_polarity pol) @@ -693,127 +744,163 @@ gic_bind(struct arm_gic_softc *sc, u_int irq, cpuset_t *cpus) return (0); } +#ifdef FDT static int -gic_irq_from_nspc(struct arm_gic_softc *sc, u_int type, u_int num, u_int *irqp) +gic_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp, + enum intr_polarity *polp, enum intr_trigger *trigp) { - switch (type) { - case INTR_IRQ_NSPC_PLAIN: - *irqp = num; - return (*irqp < sc->nirqs ? 0 : EINVAL); + if (ncells == 1) { + *irqp = cells[0]; + *polp = INTR_POLARITY_CONFORM; + *trigp = INTR_TRIGGER_CONFORM; + return (0); + } + if (ncells == 3) { + u_int irq, tripol; - case INTR_IRQ_NSPC_IRQ: - *irqp = num + GIC_FIRST_PPI; - return (*irqp < sc->nirqs ? 0 : EINVAL); + /* + * The 1st cell is the interrupt type: + * 0 = SPI + * 1 = PPI + * The 2nd cell contains the interrupt number: + * [0 - 987] for SPI + * [0 - 15] for PPI + * The 3rd cell is the flags, encoded as follows: + * bits[3:0] trigger type and level flags + * 1 = low-to-high edge triggered + * 2 = high-to-low edge triggered + * 4 = active high level-sensitive + * 8 = active low level-sensitive + * bits[15:8] PPI interrupt cpu mask + * Each bit corresponds to each of the 8 possible cpus + * attached to the GIC. A bit set to '1' indicated + * the interrupt is wired to that CPU. + */ + switch (cells[0]) { + case 0: + irq = GIC_FIRST_SPI + cells[1]; + /* SPI irq is checked later. */ + break; + case 1: + irq = GIC_FIRST_PPI + cells[1]; + if (irq > GIC_LAST_PPI) { + device_printf(dev, "unsupported PPI interrupt " + "number %u\n", cells[1]); + return (EINVAL); + } + break; + default: + device_printf(dev, "unsupported interrupt type " + "configuration %u\n", cells[0]); + return (EINVAL); + } - case INTR_IRQ_NSPC_IPI: - *irqp = num + GIC_FIRST_SGI; - return (*irqp < GIC_LAST_SGI ? 0 : EINVAL); + tripol = cells[2] & 0xff; + if (tripol & 0xf0 || (tripol & 0x0a && cells[0] == 0)) + device_printf(dev, "unsupported trigger/polarity " + "configuration 0x%02x\n", tripol); - default: - return (EINVAL); + *irqp = irq; + *polp = INTR_POLARITY_CONFORM; + *trigp = tripol & 0x03 ? INTR_TRIGGER_EDGE : INTR_TRIGGER_LEVEL; + return (0); } + return (EINVAL); } +#endif static int -gic_map_nspc(struct arm_gic_softc *sc, struct intr_irqsrc *isrc, u_int *irqp) -{ - int error; - - error = gic_irq_from_nspc(sc, isrc->isrc_nspc_type, isrc->isrc_nspc_num, - irqp); - if (error != 0) - return (error); - return (gic_attach_isrc(sc, isrc, *irqp)); -} - -#ifdef FDT -static int -gic_map_fdt(struct arm_gic_softc *sc, struct intr_irqsrc *isrc, u_int *irqp) +gic_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp, + enum intr_polarity *polp, enum intr_trigger *trigp) { - u_int irq, tripol; - enum intr_trigger trig; + u_int irq; enum intr_polarity pol; - int error; - - if (isrc->isrc_ncells == 1) { - irq = isrc->isrc_cells[0]; - pol = INTR_POLARITY_CONFORM; - trig = INTR_TRIGGER_CONFORM; - } else if (isrc->isrc_ncells == 3) { - if (isrc->isrc_cells[0] == 0) - irq = isrc->isrc_cells[1] + GIC_FIRST_SPI; - else - irq = isrc->isrc_cells[1] + GIC_FIRST_PPI; + enum intr_trigger trig; + struct arm_gic_softc *sc; - /* - * In intr[2], bits[3:0] are trigger type and level flags. - * 1 = low-to-high edge triggered - * 2 = high-to-low edge triggered - * 4 = active high level-sensitive - * 8 = active low level-sensitive - * The hardware only supports active-high-level or rising-edge. - */ - tripol = isrc->isrc_cells[2]; - if (tripol & 0x0a && irq >= GIC_FIRST_SPI) { - device_printf(sc->gic_dev, - "unsupported trigger/polarity configuration " - "0x%02x\n", tripol & 0x0f); - } - pol = INTR_POLARITY_CONFORM; - if (tripol & 0x03) - trig = INTR_TRIGGER_EDGE; - else - trig = INTR_TRIGGER_LEVEL; - } else + sc = device_get_softc(dev); + switch (data->type) { +#ifdef FDT + case INTR_MAP_DATA_FDT: + if (gic_map_fdt(dev, data->fdt.ncells, data->fdt.cells, &irq, + &pol, &trig) != 0) + return (EINVAL); + break; +#endif + default: return (EINVAL); + } if (irq >= sc->nirqs) return (EINVAL); - - error = gic_attach_isrc(sc, isrc, irq); - if (error != 0) - return (error); - - isrc->isrc_nspc_type = INTR_IRQ_NSPC_PLAIN; - isrc->isrc_nspc_num = irq; - isrc->isrc_trig = trig; - isrc->isrc_pol = pol; + if (pol != INTR_POLARITY_CONFORM && pol != INTR_POLARITY_LOW && + pol != INTR_POLARITY_HIGH) + return (EINVAL); + if (trig != INTR_TRIGGER_CONFORM && trig != INTR_TRIGGER_EDGE && + trig != INTR_TRIGGER_LEVEL) + return (EINVAL); *irqp = irq; + if (polp != NULL) + *polp = pol; + if (trigp != NULL) + *trigp = trig; return (0); } -#endif static int -arm_gic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu) +arm_gic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) { - struct arm_gic_softc *sc = device_get_softc(dev); - u_int irq; int error; + u_int irq; + struct arm_gic_softc *sc; - if (isrc->isrc_type == INTR_ISRCT_NAMESPACE) - error = gic_map_nspc(sc, isrc, &irq); -#ifdef FDT - else if (isrc->isrc_type == INTR_ISRCT_FDT) - error = gic_map_fdt(sc, isrc, &irq); -#endif - else - return (EINVAL); - - if (error == 0) - *is_percpu = irq < GIC_FIRST_SPI ? TRUE : FALSE; + error = gic_map_intr(dev, data, &irq, NULL, NULL); + if (error == 0) { + sc = device_get_softc(dev); + *isrcp = GIC_INTR_ISRC(sc, irq); + } return (error); } -static void -arm_gic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +static int +arm_gic_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) { struct arm_gic_softc *sc = device_get_softc(dev); - u_int irq = isrc->isrc_data; + struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; + u_int irq; + enum intr_trigger trig; + enum intr_polarity pol; - if (isrc->isrc_trig == INTR_TRIGGER_CONFORM) - isrc->isrc_trig = INTR_TRIGGER_LEVEL; + if (data == NULL) + return (ENOTSUP); + + /* Get config for resource. */ + if (gic_map_intr(dev, data, &irq, &pol, &trig)) + return (EINVAL); + + if (gi->gi_irq != irq) + return (EINVAL); + + /* Compare config if this is not first setup. */ + if (isrc->isrc_handlers != 0) { + if ((pol != INTR_POLARITY_CONFORM && pol != gi->gi_pol) || + (trig != INTR_TRIGGER_CONFORM && trig != gi->gi_trig)) + return (EINVAL); + else + return (0); + } + + if (pol == INTR_POLARITY_CONFORM) + pol = INTR_POLARITY_LOW; /* just pick some */ + if (trig == INTR_TRIGGER_CONFORM) + trig = INTR_TRIGGER_EDGE; /* just pick some */ + + gi->gi_pol = pol; + gi->gi_trig = trig; /* * XXX - In case that per CPU interrupt is going to be enabled in time @@ -822,48 +909,54 @@ arm_gic_enable_intr(device_t dev, struct intr_irqsrc *isrc) * pic_enable_source() and pic_disable_source() should act on * per CPU basis only. Thus, it should be solved here somehow. */ - if (isrc->isrc_flags & INTR_ISRCF_PERCPU) + if (isrc->isrc_flags & INTR_ISRCF_PPI) CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); - gic_config(sc, irq, isrc->isrc_trig, isrc->isrc_pol); - arm_gic_bind(dev, isrc); + gic_config(sc, gi->gi_irq, trig, pol); + arm_gic_bind_intr(dev, isrc); + return (0); } -static void -arm_gic_enable_source(device_t dev, struct intr_irqsrc *isrc) +static int +arm_gic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) { - struct arm_gic_softc *sc = device_get_softc(dev); - u_int irq = isrc->isrc_data; + struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; - arm_irq_memory_barrier(irq); - gic_irq_unmask(sc, irq); + if (isrc->isrc_handlers == 0) { + gi->gi_pol = INTR_POLARITY_CONFORM; + gi->gi_trig = INTR_TRIGGER_CONFORM; + } + return (0); } static void -arm_gic_disable_source(device_t dev, struct intr_irqsrc *isrc) +arm_gic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct arm_gic_softc *sc = device_get_softc(dev); - u_int irq = isrc->isrc_data; + struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; - gic_irq_mask(sc, irq); + arm_irq_memory_barrier(gi->gi_irq); + gic_irq_unmask(sc, gi->gi_irq); } -static int -arm_gic_unregister(device_t dev, struct intr_irqsrc *isrc) +static void +arm_gic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct arm_gic_softc *sc = device_get_softc(dev); - u_int irq = isrc->isrc_data; + struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; - return (gic_detach_isrc(sc, isrc, irq)); + gic_irq_mask(sc, gi->gi_irq); } static void arm_gic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { struct arm_gic_softc *sc = device_get_softc(dev); + struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; - arm_gic_disable_source(dev, isrc); - gic_c_write_4(sc, GICC_EOIR, isrc->isrc_data); + arm_gic_disable_intr(dev, isrc); + gic_c_write_4(sc, GICC_EOIR, gi->gi_irq); } static void @@ -871,52 +964,66 @@ arm_gic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { arm_irq_memory_barrier(0); - arm_gic_enable_source(dev, isrc); + arm_gic_enable_intr(dev, isrc); } static void arm_gic_post_filter(device_t dev, struct intr_irqsrc *isrc) { struct arm_gic_softc *sc = device_get_softc(dev); + struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; /* EOI for edge-triggered done earlier. */ - if (isrc->isrc_trig == INTR_TRIGGER_EDGE) + if (gi->gi_trig == INTR_TRIGGER_EDGE) return; arm_irq_memory_barrier(0); - gic_c_write_4(sc, GICC_EOIR, isrc->isrc_data); + gic_c_write_4(sc, GICC_EOIR, gi->gi_irq); } static int -arm_gic_bind(device_t dev, struct intr_irqsrc *isrc) +arm_gic_bind_intr(device_t dev, struct intr_irqsrc *isrc) { struct arm_gic_softc *sc = device_get_softc(dev); - uint32_t irq = isrc->isrc_data; + struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; - if (irq < GIC_FIRST_SPI) + if (gi->gi_irq < GIC_FIRST_SPI) return (EINVAL); if (CPU_EMPTY(&isrc->isrc_cpu)) { gic_irq_cpu = intr_irq_next_cpu(gic_irq_cpu, &all_cpus); CPU_SETOF(gic_irq_cpu, &isrc->isrc_cpu); } - return (gic_bind(sc, irq, &isrc->isrc_cpu)); + return (gic_bind(sc, gi->gi_irq, &isrc->isrc_cpu)); } #ifdef SMP static void -arm_gic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus) +arm_gic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus, + u_int ipi) { struct arm_gic_softc *sc = device_get_softc(dev); - uint32_t irq, val = 0, i; - - irq = isrc->isrc_data; + struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; + uint32_t val = 0, i; for (i = 0; i < MAXCPU; i++) if (CPU_ISSET(i, &cpus)) - val |= 1 << (16 + i); + val |= arm_gic_map[i] << GICD_SGI_TARGET_SHIFT; + + gic_d_write_4(sc, GICD_SGIR(0), val | gi->gi_irq); +} + +static int +arm_gic_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc **isrcp) +{ + struct arm_gic_softc *sc = device_get_softc(dev); - gic_d_write_4(sc, GICD_SGIR(0), val | irq); + if (sgi_first_unused > GIC_LAST_SGI) + return (ENOSPC); + + *isrcp = GIC_INTR_ISRC(sc, sgi_first_unused); + sgi_to_ipi[sgi_first_unused++] = ipi; + return (0); } #endif #else @@ -1029,7 +1136,7 @@ arm_gic_ipi_send(device_t dev, cpuset_t cpus, u_int ipi) for (i = 0; i < MAXCPU; i++) if (CPU_ISSET(i, &cpus)) - val |= 1 << (16 + i); + val |= arm_gic_map[i] << GICD_SGI_TARGET_SHIFT; gic_d_write_4(sc, GICD_SGIR(0), val | ipi); } @@ -1134,18 +1241,19 @@ static device_method_t arm_gic_methods[] = { DEVMETHOD(device_attach, arm_gic_attach), #ifdef ARM_INTRNG /* Interrupt controller interface */ - DEVMETHOD(pic_disable_source, arm_gic_disable_source), + DEVMETHOD(pic_disable_intr, arm_gic_disable_intr), DEVMETHOD(pic_enable_intr, arm_gic_enable_intr), - DEVMETHOD(pic_enable_source, arm_gic_enable_source), + DEVMETHOD(pic_map_intr, arm_gic_map_intr), + DEVMETHOD(pic_setup_intr, arm_gic_setup_intr), + DEVMETHOD(pic_teardown_intr, arm_gic_teardown_intr), DEVMETHOD(pic_post_filter, arm_gic_post_filter), DEVMETHOD(pic_post_ithread, arm_gic_post_ithread), DEVMETHOD(pic_pre_ithread, arm_gic_pre_ithread), - DEVMETHOD(pic_register, arm_gic_register), - DEVMETHOD(pic_unregister, arm_gic_unregister), #ifdef SMP - DEVMETHOD(pic_bind, arm_gic_bind), + DEVMETHOD(pic_bind_intr, arm_gic_bind_intr), DEVMETHOD(pic_init_secondary, arm_gic_init_secondary), DEVMETHOD(pic_ipi_send, arm_gic_ipi_send), + DEVMETHOD(pic_ipi_setup, arm_gic_ipi_setup), #endif #endif { 0, 0 } diff --git a/sys/arm/arm/machdep.c b/sys/arm/arm/machdep.c index e2310f1..ad0e699 100644 --- a/sys/arm/arm/machdep.c +++ b/sys/arm/arm/machdep.c @@ -60,6 +60,7 @@ __FBSDID("$FreeBSD$"); #include <sys/bus.h> #include <sys/cons.h> #include <sys/cpu.h> +#include <sys/ctype.h> #include <sys/efi.h> #include <sys/exec.h> #include <sys/imgact.h> @@ -74,6 +75,7 @@ __FBSDID("$FreeBSD$"); #include <sys/pcpu.h> #include <sys/ptrace.h> #include <sys/reboot.h> +#include <sys/boot.h> #include <sys/rwlock.h> #include <sys/sched.h> #include <sys/signalvar.h> @@ -115,6 +117,7 @@ __FBSDID("$FreeBSD$"); #include <machine/sysarch.h> #ifdef FDT +#include <contrib/libfdt/libfdt.h> #include <dev/fdt/fdt_common.h> #include <dev/ofw/openfirm.h> #endif @@ -178,6 +181,12 @@ DB_SHOW_COMMAND(vtop, db_show_vtop) #define debugf(fmt, args...) #endif +#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ + defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) || \ + defined(COMPAT_FREEBSD9) +#error FreeBSD/arm doesn't provide compatibility with releases prior to 10 +#endif + struct pcpu __pcpu[MAXCPU]; struct pcpu *pcpup = &__pcpu[0]; @@ -225,6 +234,7 @@ static struct pv_addr kernelstack; #if defined(LINUX_BOOT_ABI) #define LBABI_MAX_BANKS 10 +#define CMDLINE_GUARD "FreeBSD:" uint32_t board_id; struct arm_lbabi_tag *atag_list; char linux_command_line[LBABI_MAX_COMMAND_LINE + 1]; @@ -953,7 +963,8 @@ makectx(struct trapframe *tf, struct pcb *pcb) * Fake up a boot descriptor table */ vm_offset_t -fake_preload_metadata(struct arm_boot_params *abp __unused) +fake_preload_metadata(struct arm_boot_params *abp __unused, void *dtb_ptr, + size_t dtb_size) { #ifdef DDB vm_offset_t zstart = 0, zend = 0; @@ -991,6 +1002,16 @@ fake_preload_metadata(struct arm_boot_params *abp __unused) } else #endif lastaddr = (vm_offset_t)&end; + if (dtb_ptr != NULL) { + /* Copy DTB to KVA space and insert it into module chain. */ + lastaddr = roundup(lastaddr, sizeof(int)); + fake_preload[i++] = MODINFO_METADATA | MODINFOMD_DTBP; + fake_preload[i++] = sizeof(uint32_t); + fake_preload[i++] = (uint32_t)lastaddr; + memmove((void *)lastaddr, dtb_ptr, dtb_size); + lastaddr += dtb_size; + lastaddr = roundup(lastaddr, sizeof(int)); + } fake_preload[i++] = 0; fake_preload[i] = 0; preload_metadata = (void *)fake_preload; @@ -1011,26 +1032,83 @@ pcpu0_init(void) } #if defined(LINUX_BOOT_ABI) + +/* Convert the U-Boot command line into FreeBSD kenv and boot options. */ +static void +cmdline_set_env(char *cmdline, const char *guard) +{ + char *cmdline_next, *env; + size_t size, guard_len; + int i; + + size = strlen(cmdline); + /* Skip leading spaces. */ + for (; isspace(*cmdline) && (size > 0); cmdline++) + size--; + + /* Test and remove guard. */ + if (guard != NULL && guard[0] != '\0') { + guard_len = strlen(guard); + if (strncasecmp(cmdline, guard, guard_len) != 0) + return; + cmdline += guard_len; + size -= guard_len; + } + + /* Skip leading spaces. */ + for (; isspace(*cmdline) && (size > 0); cmdline++) + size--; + + /* Replace ',' with '\0'. */ + /* TODO: implement escaping for ',' character. */ + cmdline_next = cmdline; + while(strsep(&cmdline_next, ",") != NULL) + ; + init_static_kenv(cmdline, 0); + /* Parse boothowto. */ + for (i = 0; howto_names[i].ev != NULL; i++) { + env = kern_getenv(howto_names[i].ev); + if (env != NULL) { + if (strtoul(env, NULL, 10) != 0) + boothowto |= howto_names[i].mask; + freeenv(env); + } + } +} + vm_offset_t linux_parse_boot_param(struct arm_boot_params *abp) { struct arm_lbabi_tag *walker; uint32_t revision; uint64_t serial; + int size; + vm_offset_t lastaddr; +#ifdef FDT + struct fdt_header *dtb_ptr; + uint32_t dtb_size; +#endif /* * Linux boot ABI: r0 = 0, r1 is the board type (!= 0) and r2 * is atags or dtb pointer. If all of these aren't satisfied, - * then punt. + * then punt. Unfortunately, it looks like DT enabled kernels + * doesn't uses board type and U-Boot delivers 0 in r1 for them. */ - if (!(abp->abp_r0 == 0 && abp->abp_r1 != 0 && abp->abp_r2 != 0)) - return 0; + if (abp->abp_r0 != 0 || abp->abp_r2 == 0) + return (0); +#ifdef FDT + /* Test if r2 point to valid DTB. */ + dtb_ptr = (struct fdt_header *)abp->abp_r2; + if (fdt_check_header(dtb_ptr) == 0) { + dtb_size = fdt_totalsize(dtb_ptr); + return (fake_preload_metadata(abp, dtb_ptr, dtb_size)); + } +#endif board_id = abp->abp_r1; - walker = (struct arm_lbabi_tag *) - (abp->abp_r2 + KERNVIRTADDR - abp->abp_physaddr); + walker = (struct arm_lbabi_tag *)abp->abp_r2; - /* xxx - Need to also look for binary device tree */ if (ATAG_TAG(walker) != ATAG_CORE) return 0; @@ -1046,8 +1124,9 @@ linux_parse_boot_param(struct arm_boot_params *abp) case ATAG_INITRD2: break; case ATAG_SERIAL: - serial = walker->u.tag_sn.low | - ((uint64_t)walker->u.tag_sn.high << 32); + serial = walker->u.tag_sn.high; + serial <<= 32; + serial |= walker->u.tag_sn.low; board_set_serial(serial); break; case ATAG_REVISION: @@ -1055,9 +1134,12 @@ linux_parse_boot_param(struct arm_boot_params *abp) board_set_revision(revision); break; case ATAG_CMDLINE: - /* XXX open question: Parse this for boothowto? */ - bcopy(walker->u.tag_cmd.command, linux_command_line, - ATAG_SIZE(walker)); + size = ATAG_SIZE(walker) - + sizeof(struct arm_lbabi_header); + size = min(size, LBABI_MAX_COMMAND_LINE); + strncpy(linux_command_line, walker->u.tag_cmd.command, + size); + linux_command_line[size] = '\0'; break; default: break; @@ -1069,9 +1151,9 @@ linux_parse_boot_param(struct arm_boot_params *abp) bcopy(atag_list, atags, (char *)walker - (char *)atag_list + ATAG_SIZE(walker)); - init_static_kenv(NULL, 0); - - return fake_preload_metadata(abp); + lastaddr = fake_preload_metadata(abp, NULL, 0); + cmdline_set_env(linux_command_line, CMDLINE_GUARD); + return lastaddr; } #endif @@ -1129,7 +1211,7 @@ default_parse_boot_param(struct arm_boot_params *abp) return lastaddr; #endif /* Fall back to hardcoded metadata. */ - lastaddr = fake_preload_metadata(abp); + lastaddr = fake_preload_metadata(abp, NULL, 0); return lastaddr; } @@ -1747,6 +1829,12 @@ initarm(struct arm_boot_params *abp) if (OF_init((void *)dtbp) != 0) panic("OF_init failed with the found device tree"); +#if defined(LINUX_BOOT_ABI) + if (loader_envp == NULL && fdt_get_chosen_bootargs(linux_command_line, + LBABI_MAX_COMMAND_LINE) == 0) + cmdline_set_env(linux_command_line, CMDLINE_GUARD); +#endif + #ifdef EFI efihdr = (struct efi_map_header *)preload_search_info(kmdp, MODINFO_METADATA | MODINFOMD_EFI_MAP); diff --git a/sys/arm/arm/machdep_intr.c b/sys/arm/arm/machdep_intr.c index 875f05e..504d262 100644 --- a/sys/arm/arm/machdep_intr.c +++ b/sys/arm/arm/machdep_intr.c @@ -1,8 +1,6 @@ -/* $NetBSD: intr.c,v 1.12 2003/07/15 00:24:41 lukem Exp $ */ - /*- - * Copyright (c) 2004 Olivier Houchard. - * Copyright (c) 1994-1998 Mark Brinicombe. + * Copyright (c) 2015-2016 Svatopluk Kraus + * Copyright (c) 2015-2016 Michal Meloun * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -13,27 +11,18 @@ * 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 Mark Brinicombe - * for the NetBSD Project. - * 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 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 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) + * 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. - * - * Soft interrupt and other generic interrupt functions. */ #include "opt_platform.h" @@ -64,8 +53,18 @@ __FBSDID("$FreeBSD$"); #include "pic_if.h" #ifdef SMP -static struct intr_irqsrc ipi_sources[INTR_IPI_COUNT]; -static u_int ipi_next_num; +#define INTR_IPI_NAMELEN (MAXCOMLEN + 1) + +struct intr_ipi { + intr_ipi_handler_t * ii_handler; + void * ii_handler_arg; + intr_ipi_send_t * ii_send; + void * ii_send_arg; + char ii_name[INTR_IPI_NAMELEN]; + u_long * ii_count; +}; + +static struct intr_ipi ipi_sources[INTR_IPI_COUNT]; #endif #endif @@ -134,10 +133,7 @@ arm_irq_memory_barrier(uintptr_t irq) #ifdef ARM_INTRNG #ifdef SMP -/* - * Lookup IPI source. - */ -static struct intr_irqsrc * +static inline struct intr_ipi * intr_ipi_lookup(u_int ipi) { @@ -147,112 +143,90 @@ intr_ipi_lookup(u_int ipi) return (&ipi_sources[ipi]); } -/* - * interrupt controller dispatch function for IPIs. It should - * be called straight from the interrupt controller, when associated - * interrupt source is learned. Or from anybody who has an interrupt - * source mapped. - */ void -intr_ipi_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf) +intr_ipi_dispatch(u_int ipi, struct trapframe *tf) { void *arg; + struct intr_ipi *ii; - KASSERT(isrc != NULL, ("%s: no source", __func__)); + ii = intr_ipi_lookup(ipi); + if (ii->ii_count == NULL) + panic("%s: not setup IPI %u", __func__, ipi); - intr_ipi_increment_count(isrc->isrc_count, PCPU_GET(cpuid)); + intr_ipi_increment_count(ii->ii_count, PCPU_GET(cpuid)); /* * Supply ipi filter with trapframe argument * if none is registered. */ - arg = isrc->isrc_arg != NULL ? isrc->isrc_arg : tf; - isrc->isrc_ipifilter(arg); + arg = ii->ii_handler_arg != NULL ? ii->ii_handler_arg : tf; + ii->ii_handler(arg); } -/* - * Map IPI into interrupt controller. - * - * Not SMP coherent. - */ -static int -ipi_map(struct intr_irqsrc *isrc, u_int ipi) +void +intr_ipi_send(cpuset_t cpus, u_int ipi) { - boolean_t is_percpu; - int error; - - if (ipi >= INTR_IPI_COUNT) - panic("%s: no such IPI %u", __func__, ipi); - - KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__)); + struct intr_ipi *ii; - isrc->isrc_type = INTR_ISRCT_NAMESPACE; - isrc->isrc_nspc_type = INTR_IRQ_NSPC_IPI; - isrc->isrc_nspc_num = ipi_next_num; + ii = intr_ipi_lookup(ipi); + if (ii->ii_count == NULL) + panic("%s: not setup IPI %u", __func__, ipi); - error = PIC_REGISTER(intr_irq_root_dev, isrc, &is_percpu); - if (error == 0) { - isrc->isrc_dev = intr_irq_root_dev; - ipi_next_num++; - } - return (error); + ii->ii_send(ii->ii_send_arg, cpus, ipi); } -/* - * Setup IPI handler to interrupt source. - * - * Note that there could be more ways how to send and receive IPIs - * on a platform like fast interrupts for example. In that case, - * one can call this function with ASIF_NOALLOC flag set and then - * call intr_ipi_dispatch() when appropriate. - * - * Not SMP coherent. - */ -int -intr_ipi_set_handler(u_int ipi, const char *name, intr_ipi_filter_t *filter, - void *arg, u_int flags) +void +intr_ipi_setup(u_int ipi, const char *name, intr_ipi_handler_t *hand, + void *h_arg, intr_ipi_send_t *send, void *s_arg) { - struct intr_irqsrc *isrc; - int error; + struct intr_ipi *ii; - if (filter == NULL) - return(EINVAL); + ii = intr_ipi_lookup(ipi); - isrc = intr_ipi_lookup(ipi); - if (isrc->isrc_ipifilter != NULL) - return (EEXIST); + KASSERT(hand != NULL, ("%s: ipi %u no handler", __func__, ipi)); + KASSERT(send != NULL, ("%s: ipi %u no sender", __func__, ipi)); + KASSERT(ii->ii_count == NULL, ("%s: ipi %u reused", __func__, ipi)); - if ((flags & AISHF_NOALLOC) == 0) { - error = ipi_map(isrc, ipi); - if (error != 0) - return (error); - } + ii->ii_handler = hand; + ii->ii_handler_arg = h_arg; + ii->ii_send = send; + ii->ii_send_arg = s_arg; + strlcpy(ii->ii_name, name, INTR_IPI_NAMELEN); + ii->ii_count = intr_ipi_setup_counters(name); +} - isrc->isrc_ipifilter = filter; - isrc->isrc_arg = arg; - isrc->isrc_handlers = 1; - isrc->isrc_count = intr_ipi_setup_counters(name); - isrc->isrc_index = 0; /* it should not be used in IPI case */ +/* + * Send IPI thru interrupt controller. + */ +static void +pic_ipi_send(void *arg, cpuset_t cpus, u_int ipi) +{ - if (isrc->isrc_dev != NULL) { - PIC_ENABLE_INTR(isrc->isrc_dev, isrc); - PIC_ENABLE_SOURCE(isrc->isrc_dev, isrc); - } - return (0); + KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__)); + PIC_IPI_SEND(intr_irq_root_dev, arg, cpus, ipi); } /* - * Send IPI thru interrupt controller. + * Setup IPI handler on interrupt controller. + * + * Not SMP coherent. */ -void -pic_ipi_send(cpuset_t cpus, u_int ipi) +int +intr_pic_ipi_setup(u_int ipi, const char *name, intr_ipi_handler_t *hand, + void *arg) { + int error; struct intr_irqsrc *isrc; - isrc = intr_ipi_lookup(ipi); - KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__)); - PIC_IPI_SEND(intr_irq_root_dev, isrc, cpus); + + error = PIC_IPI_SETUP(intr_irq_root_dev, ipi, &isrc); + if (error != 0) + return (error); + + isrc->isrc_handlers++; + intr_ipi_setup(ipi, name, hand, arg, pic_ipi_send, isrc); + return (0); } #endif #endif diff --git a/sys/arm/arm/mp_machdep.c b/sys/arm/arm/mp_machdep.c index 41958a8..699881c 100644 --- a/sys/arm/arm/mp_machdep.c +++ b/sys/arm/arm/mp_machdep.c @@ -429,12 +429,11 @@ release_aps(void *dummy __unused) return; #ifdef ARM_INTRNG - intr_ipi_set_handler(IPI_RENDEZVOUS, "rendezvous", ipi_rendezvous, NULL, 0); - intr_ipi_set_handler(IPI_AST, "ast", ipi_ast, NULL, 0); - intr_ipi_set_handler(IPI_STOP, "stop", ipi_stop, NULL, 0); - intr_ipi_set_handler(IPI_PREEMPT, "preempt", ipi_preempt, NULL, 0); - intr_ipi_set_handler(IPI_HARDCLOCK, "hardclock", ipi_hardclock, NULL, 0); - + intr_pic_ipi_setup(IPI_RENDEZVOUS, "rendezvous", ipi_rendezvous, NULL); + intr_pic_ipi_setup(IPI_AST, "ast", ipi_ast, NULL); + intr_pic_ipi_setup(IPI_STOP, "stop", ipi_stop, NULL); + intr_pic_ipi_setup(IPI_PREEMPT, "preempt", ipi_preempt, NULL); + intr_pic_ipi_setup(IPI_HARDCLOCK, "hardclock", ipi_hardclock, NULL); #else #ifdef IPI_IRQ_START start = IPI_IRQ_START; @@ -502,7 +501,11 @@ ipi_all_but_self(u_int ipi) other_cpus = all_cpus; CPU_CLR(PCPU_GET(cpuid), &other_cpus); CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi); +#ifdef ARM_INTRNG + intr_ipi_send(other_cpus, ipi); +#else pic_ipi_send(other_cpus, ipi); +#endif } void @@ -514,7 +517,11 @@ ipi_cpu(int cpu, u_int ipi) CPU_SET(cpu, &cpus); CTR3(KTR_SMP, "%s: cpu: %d, ipi: %x", __func__, cpu, ipi); +#ifdef ARM_INTRNG + intr_ipi_send(cpus, ipi); +#else pic_ipi_send(cpus, ipi); +#endif } void @@ -522,6 +529,9 @@ ipi_selected(cpuset_t cpus, u_int ipi) { CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi); +#ifdef ARM_INTRNG + intr_ipi_send(cpus, ipi); +#else pic_ipi_send(cpus, ipi); +#endif } - diff --git a/sys/arm/arm/nexus.c b/sys/arm/arm/nexus.c index 035e81f..b718c1d 100644 --- a/sys/arm/arm/nexus.c +++ b/sys/arm/arm/nexus.c @@ -281,7 +281,8 @@ nexus_config_intr(device_t dev, int irq, enum intr_trigger trig, int ret = ENODEV; #ifdef ARM_INTRNG - ret = intr_irq_config(irq, trig, pol); + device_printf(dev, "bus_config_intr is obsolete and not supported!\n"); + ret = EOPNOTSUPP; #else if (arm_config_irq) ret = (*arm_config_irq)(irq, trig, pol); @@ -293,22 +294,23 @@ static int nexus_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) { +#ifndef ARM_INTRNG int irq; +#endif if ((rman_get_flags(res) & RF_SHAREABLE) == 0) flags |= INTR_EXCL; - for (irq = rman_get_start(res); irq <= rman_get_end(res); irq++) { #ifdef ARM_INTRNG - intr_irq_add_handler(child, filt, intr, arg, irq, flags, - cookiep); + return(intr_setup_irq(child, res, filt, intr, arg, flags, cookiep)); #else + for (irq = rman_get_start(res); irq <= rman_get_end(res); irq++) { arm_setup_irqhandler(device_get_nameunit(child), filt, intr, arg, irq, flags, cookiep); arm_unmask_irq(irq); -#endif } return (0); +#endif } static int @@ -316,7 +318,7 @@ nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) { #ifdef ARM_INTRNG - return (intr_irq_remove_handler(child, rman_get_start(r), ih)); + return (intr_teardown_irq(child, r, ih)); #else return (arm_remove_irqhandler(rman_get_start(r), ih)); #endif @@ -328,7 +330,7 @@ nexus_describe_intr(device_t dev, device_t child, struct resource *irq, void *cookie, const char *descr) { - return (intr_irq_describe(rman_get_start(irq), cookie, descr)); + return (intr_describe_irq(child, irq, cookie, descr)); } #ifdef SMP @@ -336,7 +338,7 @@ static int nexus_bind_intr(device_t dev, device_t child, struct resource *irq, int cpu) { - return (intr_irq_bind(rman_get_start(irq), cpu)); + return (intr_bind_irq(child, irq, cpu)); } #endif #endif diff --git a/sys/arm/arm/vm_machdep.c b/sys/arm/arm/vm_machdep.c index 70d6c70..fdab1c6 100644 --- a/sys/arm/arm/vm_machdep.c +++ b/sys/arm/arm/vm_machdep.c @@ -191,11 +191,7 @@ cpu_set_syscall_retval(struct thread *td, int error) register_t code = ap[_QUAD_LOWWORD]; if (td->td_proc->p_sysent->sv_mask) code &= td->td_proc->p_sysent->sv_mask; - fixup = ( -#if defined(COMPAT_FREEBSD6) && defined(SYS_freebsd6_lseek) - code != SYS_freebsd6_lseek && -#endif - code != SYS_lseek) ? 1 : 0; + fixup = (code != SYS_lseek); } #endif diff --git a/sys/arm/at91/at91.c b/sys/arm/at91/at91.c index 0947ec7..34c19d7 100644 --- a/sys/arm/at91/at91.c +++ b/sys/arm/at91/at91.c @@ -281,9 +281,9 @@ at91_print_child(device_t dev, device_t child) retval += bus_print_child_header(dev, child); - retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx"); - retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); - retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#jx"); + retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); if (device_get_flags(dev)) retval += printf(" flags %#x", device_get_flags(dev)); diff --git a/sys/arm/at91/at91_mci.c b/sys/arm/at91/at91_mci.c index 5bab815..ba6040f 100644 --- a/sys/arm/at91/at91_mci.c +++ b/sys/arm/at91/at91_mci.c @@ -1412,3 +1412,4 @@ DRIVER_MODULE(at91_mci, atmelarm, at91_mci_driver, at91_mci_devclass, NULL, NULL); #endif DRIVER_MODULE(mmc, at91_mci, mmc_driver, mmc_devclass, NULL, NULL); +MODULE_DEPEND(at91_mci, mmc, 1, 1, 1); diff --git a/sys/arm/at91/at91_pinctrl.c b/sys/arm/at91/at91_pinctrl.c index e5652d1..b795d91 100644 --- a/sys/arm/at91/at91_pinctrl.c +++ b/sys/arm/at91/at91_pinctrl.c @@ -330,8 +330,8 @@ pinctrl_print_res(struct pinctrl_devinfo *di) int rv; rv = 0; - rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#lx"); - rv += resource_list_print_type(&di->rl, "irq", SYS_RES_IRQ, "%ld"); + rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#jx"); + rv += resource_list_print_type(&di->rl, "irq", SYS_RES_IRQ, "%jd"); return (rv); } diff --git a/sys/arm/at91/board_tsc4370.c b/sys/arm/at91/board_tsc4370.c index 1bc0426..9c0658b 100644 --- a/sys/arm/at91/board_tsc4370.c +++ b/sys/arm/at91/board_tsc4370.c @@ -601,7 +601,7 @@ parse_boot_param(struct arm_boot_params *abp) inkernel_bootinfo = *(struct tsc_bootinfo *)(abp->abp_r1); } - return fake_preload_metadata(abp); + return fake_preload_metadata(abp, NULL, 0); } ARM_BOARD(NONE, "TSC4370 Controller Board"); diff --git a/sys/arm/broadcom/bcm2835/bcm2835_common.c b/sys/arm/broadcom/bcm2835/bcm2835_common.c index 08f01a8..bcb84b7 100644 --- a/sys/arm/broadcom/bcm2835/bcm2835_common.c +++ b/sys/arm/broadcom/bcm2835/bcm2835_common.c @@ -56,14 +56,21 @@ fdt_intc_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, int *pol) { - if (!fdt_is_compatible(node, "broadcom,bcm2835-armctrl-ic")) - return (ENXIO); - - *interrupt = fdt32_to_cpu(intr[0]); - *trig = INTR_TRIGGER_CONFORM; - *pol = INTR_POLARITY_CONFORM; - - return (0); + if (fdt_is_compatible(node, "broadcom,bcm2835-armctrl-ic")) { + *interrupt = fdt32_to_cpu(intr[0]); + *trig = INTR_TRIGGER_CONFORM; + *pol = INTR_POLARITY_CONFORM; + return (0); + } +#ifdef SOC_BCM2836 + if (fdt_is_compatible(node, "brcm,bcm2836-l1-intc")) { + *interrupt = fdt32_to_cpu(intr[0]) + 72; + *trig = INTR_TRIGGER_CONFORM; + *pol = INTR_POLARITY_CONFORM; + return (0); + } +#endif + return (ENXIO); } diff --git a/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c b/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c index 981d73b..700edcc 100644 --- a/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c +++ b/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c @@ -672,3 +672,4 @@ static driver_t bcm_sdhci_driver = { DRIVER_MODULE(sdhci_bcm, simplebus, bcm_sdhci_driver, bcm_sdhci_devclass, 0, 0); MODULE_DEPEND(sdhci_bcm, sdhci, 1, 1, 1); DRIVER_MODULE(mmc, sdhci_bcm, mmc_driver, mmc_devclass, NULL, NULL); +MODULE_DEPEND(sdhci_bcm, mmc, 1, 1, 1); diff --git a/sys/arm/broadcom/bcm2835/std.rpi b/sys/arm/broadcom/bcm2835/std.rpi index c53c720..793eab1 100644 --- a/sys/arm/broadcom/bcm2835/std.rpi +++ b/sys/arm/broadcom/bcm2835/std.rpi @@ -2,5 +2,4 @@ options KERNVIRTADDR=0xc0100000 makeoptions KERNVIRTADDR=0xc0100000 -options FREEBSD_BOOT_LOADER options LINUX_BOOT_ABI diff --git a/sys/arm/cavium/cns11xx/econa.c b/sys/arm/cavium/cns11xx/econa.c index d3dbf06..df49f1f 100644 --- a/sys/arm/cavium/cns11xx/econa.c +++ b/sys/arm/cavium/cns11xx/econa.c @@ -526,9 +526,9 @@ econa_print_child(device_t dev, device_t child) retval += bus_print_child_header(dev, child); - retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx"); - retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); - retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#jx"); + retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); if (device_get_flags(dev)) retval += printf(" flags %#x", device_get_flags(dev)); diff --git a/sys/arm/conf/A10 b/sys/arm/conf/A10 index 678b564..31a5e24 100644 --- a/sys/arm/conf/A10 +++ b/sys/arm/conf/A10 @@ -61,7 +61,7 @@ device ahci # AHCI-compatible SATA controllers # Console and misc device uart -device uart_ns8250 +device uart_snps device pty device snp device md diff --git a/sys/arm/conf/A20 b/sys/arm/conf/A20 index 9c7983c..ad3b29a 100644 --- a/sys/arm/conf/A20 +++ b/sys/arm/conf/A20 @@ -71,7 +71,7 @@ device ahci # AHCI-compatible SATA controllers # Console and misc device uart -device uart_ns8250 +device uart_snps device pty device snp device md diff --git a/sys/arm/conf/ARMADA38X b/sys/arm/conf/ARMADA38X index 7cc7702..13c3aa2 100644 --- a/sys/arm/conf/ARMADA38X +++ b/sys/arm/conf/ARMADA38X @@ -23,7 +23,6 @@ options SCHED_ULE # ULE scheduler #options SCHED_4BSD # 4BSD scheduler options SMP -options ARM_INTRNG # Debugging #options DEBUG @@ -49,7 +48,7 @@ device md # Serial ports device uart -device uart_ns8250 +device uart_snps # Network device ether diff --git a/sys/arm/conf/ATMEL b/sys/arm/conf/ATMEL index f86f8e8..63cd785 100644 --- a/sys/arm/conf/ATMEL +++ b/sys/arm/conf/ATMEL @@ -57,11 +57,6 @@ options GEOM_PART_BSD # BSD partition scheme options GEOM_PART_MBR # MBR partition scheme options GEOM_PART_GPT # GUID Partition Tables. options GEOM_LABEL # Provides labelization -options COMPAT_FREEBSD5 # Compatible with FreeBSD5 -options COMPAT_FREEBSD6 # Compatible with FreeBSD6 -options COMPAT_FREEBSD7 # Compatible with FreeBSD7 -options COMPAT_FREEBSD9 # Compatible with FreeBSD9 -options COMPAT_FREEBSD10 # Compatible with FreeBSD10 options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI options KTRACE # ktrace(1) support options STACK # stack(9) support diff --git a/sys/arm/conf/BEAGLEBONE b/sys/arm/conf/BEAGLEBONE index 3950e17..1885c0c 100644 --- a/sys/arm/conf/BEAGLEBONE +++ b/sys/arm/conf/BEAGLEBONE @@ -28,6 +28,8 @@ include "../ti/am335x/std.am335x" makeoptions MODULES_EXTRA="dtb/am335x am335x_dmtpps" +options ARM_INTRNG + options HZ=100 options SCHED_4BSD # 4BSD scheduler options PLATFORM @@ -87,6 +89,10 @@ device gpio device gpioled device gpiobacklight +# SPI +device ti_spi +device spibus + # ADC support device ti_adc diff --git a/sys/arm/conf/CNS11XXNAS b/sys/arm/conf/CNS11XXNAS index 2bb5920..6fc8876 100644 --- a/sys/arm/conf/CNS11XXNAS +++ b/sys/arm/conf/CNS11XXNAS @@ -46,11 +46,6 @@ options DDB # Enable the kernel debugger #options DIAGNOSTIC -#options COMPAT_FREEBSD5 -#options COMPAT_FREEBSD6 -#options COMPAT_FREEBSD7n - - options SCHED_ULE # ULE scheduler #options SCHED_4BSD # 4BSD scheduler options GEOM_PART_BSD # BSD partition scheme diff --git a/sys/arm/conf/EFIKA_MX b/sys/arm/conf/EFIKA_MX index 7f978b2..8b4b4aa 100644 --- a/sys/arm/conf/EFIKA_MX +++ b/sys/arm/conf/EFIKA_MX @@ -30,9 +30,6 @@ options SOC_IMX51 options SCHED_4BSD # 4BSD scheduler #options MD_ROOT # MD is a potential root device #options NFSD # Network Filesystem Server -#options COMPAT_FREEBSD5 # Compatible with FreeBSD5 -#options COMPAT_FREEBSD6 # Compatible with FreeBSD6 -#options COMPAT_FREEBSD7 # Compatible with FreeBSD7 options PLATFORM options INCLUDE_CONFIG_FILE # Include this file in kernel diff --git a/sys/arm/conf/ETHERNUT5 b/sys/arm/conf/ETHERNUT5 index 4604ff2..e54513b 100644 --- a/sys/arm/conf/ETHERNUT5 +++ b/sys/arm/conf/ETHERNUT5 @@ -53,9 +53,6 @@ options GEOM_PART_BSD # BSD partition scheme options GEOM_PART_MBR # MBR partition scheme #options GEOM_PART_GPT # GUID Partition Tables. #options GEOM_LABEL # Provides labelization -#options COMPAT_FREEBSD5 # Compatible with FreeBSD5 -#options COMPAT_FREEBSD6 # Compatible with FreeBSD6 -#options COMPAT_FREEBSD7 # Compatible with FreeBSD7 options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI options KTRACE # ktrace(1) support #options STACK # stack(9) support diff --git a/sys/arm/conf/IMX53 b/sys/arm/conf/IMX53 index 9724375..e320562 100644 --- a/sys/arm/conf/IMX53 +++ b/sys/arm/conf/IMX53 @@ -27,9 +27,6 @@ options SOC_IMX53 options SCHED_4BSD # 4BSD scheduler #options NFSD # Network Filesystem Server -#options COMPAT_FREEBSD5 # Compatible with FreeBSD5 -#options COMPAT_FREEBSD6 # Compatible with FreeBSD6 -#options COMPAT_FREEBSD7 # Compatible with FreeBSD7 options PLATFORM options INCLUDE_CONFIG_FILE # Include this file in kernel diff --git a/sys/arm/conf/JETSON-TK1 b/sys/arm/conf/JETSON-TK1 new file mode 100644 index 0000000..242e835 --- /dev/null +++ b/sys/arm/conf/JETSON-TK1 @@ -0,0 +1,37 @@ +# Kernel configuration for Jetson TK1 board +# +# For more information on this file, please read the config(5) manual page, +# and/or the handbook section on Kernel Configuration Files: +# +# http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html +# +# The handbook is also available locally in /usr/share/doc/handbook +# if you've installed the doc distribution, otherwise always see the +# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the +# latest information. +# +# An exhaustive list of options and more detailed explanations of the +# device lines is also present in the ../../conf/NOTES and NOTES files. +# If you are in doubt as to the purpose or necessity of a line, check first +# in NOTES. +# +# $FreeBSD$ + +#NO_UNIVERSE + +include "TEGRA124" +ident JETSON-TK1 + +# Flattened Device Tree +options FDT_DTB_STATIC +makeoptions FDT_DTS_FILE=tegra124-jetson-tk1-fbsd.dts + +makeoptions MODULES_OVERRIDE="" +#options BOOTVERBOSE +#options BOOTHOWTO=RB_SINGLE + +#options ROOTDEVNAME=\"ufs:mmcsd0s2a\" +options ROOTDEVNAME=\"ufs:ada0s1a\" + +# CTF doesn't works yet +makeoptions WITHOUT_CTF=1 diff --git a/sys/arm/conf/NOTES b/sys/arm/conf/NOTES index d32e6d6..3cd43af 100644 --- a/sys/arm/conf/NOTES +++ b/sys/arm/conf/NOTES @@ -58,6 +58,10 @@ nooptions SMP nooptions MAXCPU nooptions COMPAT_FREEBSD4 +nooptions COMPAT_FREEBSD5 +nooptions COMPAT_FREEBSD6 +nooptions COMPAT_FREEBSD7 +nooptions COMPAT_FREEBSD9 nooption PPC_PROBE_CHIPSET nodevice fdc diff --git a/sys/arm/conf/RK3188 b/sys/arm/conf/RK3188 index 49c8eae..ec6ddb8 100644 --- a/sys/arm/conf/RK3188 +++ b/sys/arm/conf/RK3188 @@ -59,7 +59,7 @@ device dwmmc # Console and misc device uart -device uart_ns8250 +device uart_snps device pty device snp device md @@ -101,8 +101,6 @@ device wlan # 802.11 support device wlan_wep # 802.11 WEP support device wlan_ccmp # 802.11 CCMP support device wlan_tkip # 802.11 TKIP support -device urtwn -device urtwnfw device firmware # Used by the above # USB Ethernet support, requires miibus diff --git a/sys/arm/conf/SAM9260EK b/sys/arm/conf/SAM9260EK index 1edee77..7248046 100644 --- a/sys/arm/conf/SAM9260EK +++ b/sys/arm/conf/SAM9260EK @@ -64,9 +64,6 @@ options GEOM_PART_BSD # BSD partition scheme options GEOM_PART_MBR # MBR partition scheme #options GEOM_PART_GPT # GUID Partition Tables. #options GEOM_LABEL # Provides labelization -#options COMPAT_FREEBSD5 # Compatible with FreeBSD5 -#options COMPAT_FREEBSD6 # Compatible with FreeBSD6 -#options COMPAT_FREEBSD7 # Compatible with FreeBSD7 options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI options KTRACE # ktrace(1) support #options STACK # stack(9) support diff --git a/sys/arm/conf/TEGRA124 b/sys/arm/conf/TEGRA124 new file mode 100644 index 0000000..1b3ec3f --- /dev/null +++ b/sys/arm/conf/TEGRA124 @@ -0,0 +1,157 @@ +# +# Kernel configuration for NVIDIA Tegra124 based boards. +# +# For more information on this file, please read the config(5) manual page, +# and/or the handbook section on Kernel Configuration Files: +# +# http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html +# +# The handbook is also available locally in /usr/share/doc/handbook +# if you've installed the doc distribution, otherwise always see the +# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the +# latest information. +# +# An exhaustive list of options and more detailed explanations of the +# device lines is also present in the ../../conf/NOTES and NOTES files. +# If you are in doubt as to the purpose or necessity of a line, check first +# in NOTES. +# +# $FreeBSD$ + +include "std.armv6" +include "../nvidia/tegra124/std.tegra124" + +ident TEGRA124 + +options HZ=100 # Scheduling quantum is 10 milliseconds. +options SCHED_ULE # ULE scheduler +options PLATFORM # Platform based SoC +options PLATFORM_SMP +options SMP # Enable multiple cores +options LINUX_BOOT_ABI + +# Debugging for use in -current +makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols +options BREAK_TO_DEBUGGER +options ALT_BREAK_TO_DEBUGGER +#options VERBOSE_SYSINIT # Enable verbose sysinit messages +options KDB # Enable kernel debugger support +# For minimum debugger support (stable branch) use: +#options KDB_TRACE # Print a stack trace for a panic +# For full debugger support use this instead: +options DDB # Enable the kernel debugger +options INVARIANTS # Enable calls of extra sanity checking +options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS +options WITNESS # Enable checks to detect deadlocks and cycles +options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed + +# Interrupt controller +device gic + +# ARM Generic Timer +device generic_timer + +# EXT_RESOURCES pseudo devices +options EXT_RESOURCES +device clk +device phy +device hwreset +device regulator + +# Pseudo devices. +device loop # Network loopback +device random # Entropy device +device vlan # 802.1Q VLAN support +#device tun # Packet tunnel. +device md # Memory "disks" +#device gif # IPv6 and IPv4 tunneling +#device firmware # firmware assist module +device ether # Ethernet support +device miibus # Required for ethernet +device bpf # Berkeley packet filter (required for DHCP) + + +# General-purpose input/output +device gpio +#device gpioled + +# I2C support +device iic +device iicbus +device icee + +# Serial (COM) ports +device uart # Multi-uart driver +device uart_ns8250 + +# MMC/SD/SDIO Card slot support +device sdhci # SD controller +device mmc # SD/MMC protocol +device mmcsd # SDCard disk device + +# ATA controllers +device ahci # AHCI-compatible SATA controllers + +# SCSI peripherals +device scbus # SCSI bus (required for ATA/SCSI) +device da # Direct Access (disks) +device cd # CD +device pass # Passthrough device (direct ATA/SCSI access) + +# USB support +options USB_HOST_ALIGN=64 # Align usb buffers to cache line size. +options USB_DEBUG # enable debug msgs +device ehci # EHCI USB interface +device usb # USB Bus (required) +device umass # Disks/Mass storage - Requires scbus and da +device uhid # "Human Interface Devices" +#device u3g # USB modems +device ukbd # Allow keyboard like HIDs to control console +device ums # USB mouse + +# USB Ethernet, requires miibus +#device aue # ADMtek USB Ethernet +#device axe # ASIX Electronics USB Ethernet +#device cdce # Generic USB over Ethernet +#device cue # CATC USB Ethernet +#device kue # Kawasaki LSI USB Ethernet +#device rue # RealTek RTL8150 USB Ethernet +#device udav # Davicom DM9601E USB + +# USB Wireless +#device rum # Ralink Technology RT2501USB wireless NICs + +# Wireless NIC cards +#device wlan # 802.11 support +#device wlan_wep # 802.11 WEP support +#device wlan_ccmp # 802.11 CCMP support +#device wlan_tkip # 802.11 TKIP support +#device wlan_amrr # AMRR transmit rate control algorithm + +# PCI +options NEW_PCIB +device pci + +# PCI Ethernet NICs that use the common MII bus controller code. +# NOTE: Be sure to keep the 'device miibus' line in order to use these NICs! +device re # RealTek 8139C+/8169/8169S/8110S + +# DRM2 +#device fbd +#device vt +#device splash +#device kbdmux +#device drm2 + +# Sound +#device sound +#device snd_hda + +# Flattened Device Tree +options FDT # Configure using FDT/DTB data +device fdt_pinctrl + +# SoC-specific devices + +#device hwpmc +#options HWPMC_HOOKS diff --git a/sys/arm/conf/std.arm b/sys/arm/conf/std.arm index 88675f0..b9ac640 100644 --- a/sys/arm/conf/std.arm +++ b/sys/arm/conf/std.arm @@ -2,4 +2,5 @@ # # $FreeBSD$ +options COMPAT_FREEBSD10 # Compatible with FreeBSD10 diff --git a/sys/arm/conf/std.armv6 b/sys/arm/conf/std.armv6 index 142c7d3..4dc4906 100644 --- a/sys/arm/conf/std.armv6 +++ b/sys/arm/conf/std.armv6 @@ -39,6 +39,8 @@ options KBD_INSTALL_CDEV # install a CDEV entry in /dev options FREEBSD_BOOT_LOADER # Process metadata passed from loader(8) options VFP # Enable floating point hardware support +options COMPAT_FREEBSD10 # Compatible with FreeBSD10 + # DTrace support options KDTRACE_HOOKS # Kernel DTrace hooks options DDB_CTF # all architectures - kernel ELF linker loads CTF data diff --git a/sys/arm/freescale/imx/imx_gpio.c b/sys/arm/freescale/imx/imx_gpio.c index 3c81e28..cdff020 100644 --- a/sys/arm/freescale/imx/imx_gpio.c +++ b/sys/arm/freescale/imx/imx_gpio.c @@ -91,6 +91,15 @@ __FBSDID("$FreeBSD$"); #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) #define NGPIO 32 +#ifdef ARM_INTRNG +struct gpio_irqsrc { + struct intr_irqsrc gi_isrc; + u_int gi_irq; + enum intr_polarity gi_pol; + enum intr_trigger gi_trig; +}; +#endif + struct imx51_gpio_softc { device_t dev; device_t sc_busdev; @@ -101,7 +110,9 @@ struct imx51_gpio_softc { bus_space_handle_t sc_ioh; int gpio_npins; struct gpio_pin gpio_pins[NGPIO]; - struct intr_irqsrc *gpio_pic_irqsrc[NGPIO]; +#ifdef ARM_INTRNG + struct gpio_irqsrc gpio_pic_irqsrc[NGPIO]; +#endif }; static struct ofw_compat_data compat_data[] = { @@ -145,8 +156,30 @@ static int imx51_gpio_pin_get(device_t, uint32_t, unsigned int *); static int imx51_gpio_pin_toggle(device_t, uint32_t pin); #ifdef ARM_INTRNG +static int +gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct imx51_gpio_softc *sc; + struct gpio_irqsrc *gi; + + sc = device_get_softc(dev); + if (isrc->isrc_handlers == 0) { + gi = (struct gpio_irqsrc *)isrc; + gi->gi_pol = INTR_POLARITY_CONFORM; + gi->gi_trig = INTR_TRIGGER_CONFORM; + + // XXX Not sure this is necessary + mtx_lock_spin(&sc->sc_mtx); + CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << gi->gi_irq)); + WRITE4(sc, IMX_GPIO_ISR_REG, (1U << gi->gi_irq)); + mtx_unlock_spin(&sc->sc_mtx); + } + return (0); +} + /* - * this is teardown_intr + * this is mask_intr */ static void gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) @@ -155,55 +188,143 @@ gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) u_int irq; sc = device_get_softc(dev); - irq = isrc->isrc_data; + irq = ((struct gpio_irqsrc *)isrc)->gi_irq; - // XXX Not sure this is necessary mtx_lock_spin(&sc->sc_mtx); CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << irq)); - WRITE4(sc, IMX_GPIO_ISR_REG, (1U << irq)); mtx_unlock_spin(&sc->sc_mtx); } -/* - * this is mask_intr - */ -static void -gpio_pic_disable_source(device_t dev, struct intr_irqsrc *isrc) +static int +gpio_pic_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp, + enum intr_polarity *polp, enum intr_trigger *trigp) { struct imx51_gpio_softc *sc; + u_int irq, tripol; + enum intr_polarity pol; + enum intr_trigger trig; sc = device_get_softc(dev); - mtx_lock_spin(&sc->sc_mtx); - CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << isrc->isrc_data)); - mtx_unlock_spin(&sc->sc_mtx); + /* + * From devicetree/bindings/gpio/fsl-imx-gpio.txt: + * #interrupt-cells: 2. The first cell is the GPIO number. The second + * cell bits[3:0] is used to specify trigger type and level flags: + * 1 = low-to-high edge triggered. + * 2 = high-to-low edge triggered. + * 4 = active high level-sensitive. + * 8 = active low level-sensitive. + * We can do any single one of these modes, but nothing in combo. + */ + + if (ncells != 2) { + device_printf(sc->dev, "Invalid #interrupt-cells"); + return (EINVAL); + } + + irq = cells[0]; + tripol = cells[1]; + if (irq >= sc->gpio_npins) { + device_printf(sc->dev, "Invalid interrupt number %d", irq); + return (EINVAL); + } + switch (tripol) { + case 1: + trig = INTR_TRIGGER_EDGE; + pol = INTR_POLARITY_HIGH; + break; + case 2: + trig = INTR_TRIGGER_EDGE; + pol = INTR_POLARITY_LOW; + break; + case 4: + trig = INTR_TRIGGER_LEVEL; + pol = INTR_POLARITY_HIGH; + break; + case 8: + trig = INTR_TRIGGER_LEVEL; + pol = INTR_POLARITY_LOW; + break; + default: + device_printf(sc->dev, "unsupported trigger/polarity 0x%2x\n", + tripol); + return (ENOTSUP); + } + *irqp = irq; + if (polp != NULL) + *polp = pol; + if (trigp != NULL) + *trigp = trig; + return (0); } -/* - * this is setup_intr - */ -static void -gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +static int +gpio_pic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + int error; + u_int irq; + struct imx51_gpio_softc *sc; + + if (data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + + error = gpio_pic_map_fdt(dev, data->fdt.ncells, data->fdt.cells, &irq, + NULL, NULL); + if (error == 0) { + sc = device_get_softc(dev); + *isrcp = &sc->gpio_pic_irqsrc[irq].gi_isrc; + } + return (error); +} + +static int +gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) { struct imx51_gpio_softc *sc; - int icfg; + struct gpio_irqsrc *gi; + int error, icfg; u_int irq, reg, shift, wrk; + enum intr_trigger trig; + enum intr_polarity pol; sc = device_get_softc(dev); + gi = (struct gpio_irqsrc *)isrc; + + /* Get config for interrupt. */ + if (data == NULL || data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + error = gpio_pic_map_fdt(dev, data->fdt.ncells, data->fdt.cells, &irq, + &pol, &trig); + if (error != 0) + return (error); + if (gi->gi_irq != irq) + return (EINVAL); - if (isrc->isrc_trig == INTR_TRIGGER_LEVEL) { - if (isrc->isrc_pol == INTR_POLARITY_LOW) + /* Compare config if this is not first setup. */ + if (isrc->isrc_handlers != 0) { + if (pol != gi->gi_pol || trig != gi->gi_trig) + return (EINVAL); + else + return (0); + } + + gi->gi_pol = pol; + gi->gi_trig = trig; + + if (trig == INTR_TRIGGER_LEVEL) { + if (pol == INTR_POLARITY_LOW) icfg = GPIO_ICR_COND_LOW; else icfg = GPIO_ICR_COND_HIGH; } else { - if (isrc->isrc_pol == INTR_POLARITY_HIGH) + if (pol == INTR_POLARITY_HIGH) icfg = GPIO_ICR_COND_FALL; else icfg = GPIO_ICR_COND_RISE; } - irq = isrc->isrc_data; if (irq < 16) { reg = IMX_GPIO_ICR1_REG; shift = 2 * irq; @@ -220,20 +341,23 @@ gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) wrk |= icfg << shift; WRITE4(sc, reg, wrk); mtx_unlock_spin(&sc->sc_mtx); + return (0); } /* * this is unmask_intr */ static void -gpio_pic_enable_source(device_t dev, struct intr_irqsrc *isrc) +gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct imx51_gpio_softc *sc; + u_int irq; sc = device_get_softc(dev); + irq = ((struct gpio_irqsrc *)isrc)->gi_irq; mtx_lock_spin(&sc->sc_mtx); - SET4(sc, IMX_GPIO_IMR_REG, (1U << isrc->isrc_data)); + SET4(sc, IMX_GPIO_IMR_REG, (1U << irq)); mtx_unlock_spin(&sc->sc_mtx); } @@ -241,12 +365,14 @@ static void gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) { struct imx51_gpio_softc *sc; + u_int irq; sc = device_get_softc(dev); + irq = ((struct gpio_irqsrc *)isrc)->gi_irq; arm_irq_memory_barrier(0); /* EOI. W1C reg so no r-m-w, no locking needed. */ - WRITE4(sc, IMX_GPIO_ISR_REG, (1U << isrc->isrc_data)); + WRITE4(sc, IMX_GPIO_ISR_REG, (1U << irq)); } static void @@ -254,119 +380,21 @@ gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { arm_irq_memory_barrier(0); - gpio_pic_enable_source(dev, isrc); + gpio_pic_enable_intr(dev, isrc); } static void gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { - gpio_pic_disable_source(dev, isrc); -} - -/* - * intrng calls this to make a new isrc known to us. - */ -static int -gpio_pic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu) -{ - struct imx51_gpio_softc *sc; - u_int irq, tripol; - - sc = device_get_softc(dev); - - /* - * From devicetree/bindings/gpio/fsl-imx-gpio.txt: - * #interrupt-cells: 2. The first cell is the GPIO number. The second - * cell bits[3:0] is used to specify trigger type and level flags: - * 1 = low-to-high edge triggered. - * 2 = high-to-low edge triggered. - * 4 = active high level-sensitive. - * 8 = active low level-sensitive. - * We can do any single one of these modes, but nothing in combo. - */ - - if (isrc->isrc_ncells != 2) { - device_printf(sc->dev, "Invalid #interrupt-cells"); - return (EINVAL); - } - - irq = isrc->isrc_cells[0]; - tripol = isrc->isrc_cells[1]; - if (irq >= sc->gpio_npins) { - device_printf(sc->dev, "Invalid interrupt number %d", irq); - return (EINVAL); - } - switch (tripol) - { - case 1: - isrc->isrc_trig = INTR_TRIGGER_EDGE; - isrc->isrc_pol = INTR_POLARITY_HIGH; - break; - case 2: - isrc->isrc_trig = INTR_TRIGGER_EDGE; - isrc->isrc_pol = INTR_POLARITY_LOW; - break; - case 4: - isrc->isrc_trig = INTR_TRIGGER_LEVEL; - isrc->isrc_pol = INTR_POLARITY_HIGH; - break; - case 8: - isrc->isrc_trig = INTR_TRIGGER_LEVEL; - isrc->isrc_pol = INTR_POLARITY_LOW; - break; - default: - device_printf(sc->dev, "unsupported trigger/polarity 0x%2x\n", - tripol); - return (ENOTSUP); - } - isrc->isrc_nspc_type = INTR_IRQ_NSPC_PLAIN; - isrc->isrc_nspc_num = irq; - - /* - * 1. The link between ISRC and controller must be set atomically. - * 2. Just do things only once in rare case when consumers - * of shared interrupt came here at the same moment. - */ - mtx_lock_spin(&sc->sc_mtx); - if (sc->gpio_pic_irqsrc[irq] != NULL) { - mtx_unlock_spin(&sc->sc_mtx); - return (sc->gpio_pic_irqsrc[irq] == isrc ? 0 : EEXIST); - } - sc->gpio_pic_irqsrc[irq] = isrc; - isrc->isrc_data = irq; - mtx_unlock_spin(&sc->sc_mtx); - - intr_irq_set_name(isrc, "%s,%u", device_get_nameunit(sc->dev), irq); - return (0); -} - -static int -gpio_pic_unregister(device_t dev, struct intr_irqsrc *isrc) -{ - struct imx51_gpio_softc *sc; - u_int irq; - - sc = device_get_softc(dev); - - mtx_lock_spin(&sc->sc_mtx); - irq = isrc->isrc_data; - if (sc->gpio_pic_irqsrc[irq] != isrc) { - mtx_unlock_spin(&sc->sc_mtx); - return (sc->gpio_pic_irqsrc[irq] == NULL ? 0 : EINVAL); - } - sc->gpio_pic_irqsrc[irq] = NULL; - isrc->isrc_data = 0; - mtx_unlock_spin(&sc->sc_mtx); - - intr_irq_set_name(isrc, ""); - return (0); + gpio_pic_disable_intr(dev, isrc); } static int gpio_pic_filter(void *arg) { struct imx51_gpio_softc *sc; + struct intr_irqsrc *isrc; uint32_t i, interrupts; sc = arg; @@ -377,14 +405,43 @@ gpio_pic_filter(void *arg) for (i = 0; interrupts != 0; i++, interrupts >>= 1) { if ((interrupts & 0x1) == 0) continue; - if (sc->gpio_pic_irqsrc[i]) - intr_irq_dispatch(sc->gpio_pic_irqsrc[i], curthread->td_intr_frame); - else - device_printf(sc->dev, "spurious interrupt %d\n", i); + isrc = &sc->gpio_pic_irqsrc[i].gi_isrc; + if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) { + gpio_pic_disable_intr(sc->dev, isrc); + gpio_pic_post_filter(sc->dev, isrc); + device_printf(sc->dev, "Stray irq %u disabled\n", i); + } } return (FILTER_HANDLED); } + +/* + * register our isrcs into intrng to make it known about them. + */ +static int +gpio_pic_register_isrcs(struct imx51_gpio_softc *sc) +{ + int error; + uint32_t irq; + const char *name; + + name = device_get_nameunit(sc->dev); + for (irq = 0; irq < NGPIO; irq++) { + sc->gpio_pic_irqsrc[irq].gi_irq = irq; + sc->gpio_pic_irqsrc[irq].gi_pol = INTR_POLARITY_CONFORM; + sc->gpio_pic_irqsrc[irq].gi_trig = INTR_TRIGGER_CONFORM; + + error = intr_isrc_register(&sc->gpio_pic_irqsrc[irq].gi_isrc, + sc->dev, 0, "%s,%u", name, irq); + if (error != 0) { + /* XXX call intr_isrc_deregister() */ + device_printf(sc->dev, "%s failed", __func__); + return (error); + } + } + return (0); +} #endif /* @@ -656,6 +713,7 @@ imx51_gpio_attach(device_t dev) } #ifdef ARM_INTRNG + gpio_pic_register_isrcs(sc); intr_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev))); #endif sc->sc_busdev = gpiobus_attach_bus(dev); @@ -695,14 +753,13 @@ static device_method_t imx51_gpio_methods[] = { #ifdef ARM_INTRNG /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, gpio_pic_disable_intr), - DEVMETHOD(pic_disable_source, gpio_pic_disable_source), DEVMETHOD(pic_enable_intr, gpio_pic_enable_intr), - DEVMETHOD(pic_enable_source, gpio_pic_enable_source), + DEVMETHOD(pic_map_intr, gpio_pic_map_intr), + DEVMETHOD(pic_setup_intr, gpio_pic_setup_intr), + DEVMETHOD(pic_teardown_intr, gpio_pic_teardown_intr), DEVMETHOD(pic_post_filter, gpio_pic_post_filter), DEVMETHOD(pic_post_ithread, gpio_pic_post_ithread), DEVMETHOD(pic_pre_ithread, gpio_pic_pre_ithread), - DEVMETHOD(pic_register, gpio_pic_register), - DEVMETHOD(pic_unregister, gpio_pic_unregister), #endif /* GPIO protocol */ diff --git a/sys/arm/freescale/imx/imx_sdhci.c b/sys/arm/freescale/imx/imx_sdhci.c index fa9d8ed..2dc41db 100644 --- a/sys/arm/freescale/imx/imx_sdhci.c +++ b/sys/arm/freescale/imx/imx_sdhci.c @@ -836,3 +836,4 @@ static driver_t imx_sdhci_driver = { DRIVER_MODULE(sdhci_imx, simplebus, imx_sdhci_driver, imx_sdhci_devclass, 0, 0); MODULE_DEPEND(sdhci_imx, sdhci, 1, 1, 1); DRIVER_MODULE(mmc, sdhci_imx, mmc_driver, mmc_devclass, NULL, NULL); +MODULE_DEPEND(sdhci_imx, mmc, 1, 1, 1); diff --git a/sys/arm/include/atags.h b/sys/arm/include/atags.h index 3665890..6d400af 100644 --- a/sys/arm/include/atags.h +++ b/sys/arm/include/atags.h @@ -123,7 +123,7 @@ struct arm_lbabi_tag }; #define ATAG_TAG(a) (a)->tag_hdr.tag -#define ATAG_SIZE(a) (a)->tag_hdr.size +#define ATAG_SIZE(a) ((a)->tag_hdr.size * sizeof(uint32_t)) #define ATAG_NEXT(a) (struct arm_lbabi_tag *)((char *)(a) + ATAG_SIZE(a)) #endif /* __MACHINE_ATAGS_H__ */ diff --git a/sys/arm/include/intr.h b/sys/arm/include/intr.h index 74d7e4d..e81bc7d 100644 --- a/sys/arm/include/intr.h +++ b/sys/arm/include/intr.h @@ -52,14 +52,17 @@ #include <sys/intr.h> #ifdef SMP -void intr_ipi_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf); +typedef void intr_ipi_send_t(void *, cpuset_t, u_int); +typedef void intr_ipi_handler_t(void *); -#define AISHF_NOALLOC 0x0001 +void intr_ipi_dispatch(u_int, struct trapframe *); +void intr_ipi_send(cpuset_t, u_int); -int intr_ipi_set_handler(u_int ipi, const char *name, intr_ipi_filter_t *filter, - void *arg, u_int flags); -#endif +void intr_ipi_setup(u_int, const char *, intr_ipi_handler_t *, void *, + intr_ipi_send_t *, void *); +int intr_pic_ipi_setup(u_int, const char *, intr_ipi_handler_t *, void *); +#endif #else /* ARM_INTRNG */ /* XXX move to std.* files? */ diff --git a/sys/arm/include/machdep.h b/sys/arm/include/machdep.h index 36c2f44..b156efe 100644 --- a/sys/arm/include/machdep.h +++ b/sys/arm/include/machdep.h @@ -37,7 +37,8 @@ struct arm_boot_params; vm_offset_t default_parse_boot_param(struct arm_boot_params *abp); vm_offset_t freebsd_parse_boot_param(struct arm_boot_params *abp); vm_offset_t linux_parse_boot_param(struct arm_boot_params *abp); -vm_offset_t fake_preload_metadata(struct arm_boot_params *abp); +vm_offset_t fake_preload_metadata(struct arm_boot_params *abp, + void *dtb_ptr, size_t dtb_size); vm_offset_t parse_boot_param(struct arm_boot_params *abp); void arm_generic_initclocks(void); diff --git a/sys/arm/include/smp.h b/sys/arm/include/smp.h index e4d6385..e685cc3 100644 --- a/sys/arm/include/smp.h +++ b/sys/arm/include/smp.h @@ -37,8 +37,8 @@ void ipi_cpu(int cpu, u_int ipi); void ipi_selected(cpuset_t cpus, u_int ipi); /* PIC interface */ -void pic_ipi_send(cpuset_t cpus, u_int ipi); #ifndef ARM_INTRNG +void pic_ipi_send(cpuset_t cpus, u_int ipi); void pic_ipi_clear(int ipi); int pic_ipi_read(int arg); #endif diff --git a/sys/arm/lpc/lpc_mmc.c b/sys/arm/lpc/lpc_mmc.c index ad3b6aa..6c33215 100644 --- a/sys/arm/lpc/lpc_mmc.c +++ b/sys/arm/lpc/lpc_mmc.c @@ -776,3 +776,4 @@ static driver_t lpc_mmc_driver = { DRIVER_MODULE(lpcmmc, simplebus, lpc_mmc_driver, lpc_mmc_devclass, 0, 0); DRIVER_MODULE(mmc, lpcmmc, mmc_driver, mmc_devclass, NULL, NULL); +MODULE_DEPEND(lpcmmc, mmc, 1, 1, 1); diff --git a/sys/arm/mv/files.mv b/sys/arm/mv/files.mv index 635173d..8c303c6 100644 --- a/sys/arm/mv/files.mv +++ b/sys/arm/mv/files.mv @@ -26,6 +26,7 @@ dev/mge/if_mge.c optional mge dev/nand/nfc_mv.c optional nand dev/mvs/mvs_soc.c optional mvs dev/uart/uart_dev_ns8250.c optional uart +dev/uart/uart_dev_snps.c optional uart dev/usb/controller/ehci_mv.c optional ehci dev/usb/controller/xhci_mv.c optional xhci diff --git a/sys/arm/mv/mpic.c b/sys/arm/mv/mpic.c index e81820c..4f246a4 100644 --- a/sys/arm/mv/mpic.c +++ b/sys/arm/mv/mpic.c @@ -74,7 +74,7 @@ __FBSDID("$FreeBSD$"); #define MPIC_INT_ERR 4 #define MPIC_INT_MSI 96 -#define IRQ_MASK 0x3ff +#define MPIC_IRQ_MASK 0x3ff #define MPIC_CTRL 0x0 #define MPIC_SOFT_INT 0x4 @@ -98,6 +98,13 @@ __FBSDID("$FreeBSD$"); #define MPIC_PPI 32 +#ifdef ARM_INTRNG +struct mv_mpic_irqsrc { + struct intr_irqsrc mmi_isrc; + u_int mmi_irq; +}; +#endif + struct mv_mpic_softc { device_t sc_dev; struct resource * mpic_res[4]; @@ -108,8 +115,9 @@ struct mv_mpic_softc { bus_space_tag_t drbl_bst; bus_space_handle_t drbl_bsh; struct mtx mtx; - - struct intr_irqsrc ** mpic_isrcs; +#ifdef ARM_INTRNG + struct mv_mpic_irqsrc * mpic_isrcs; +#endif int nirqs; void * intr_hand; }; @@ -177,6 +185,40 @@ mv_mpic_probe(device_t dev) return (0); } +#ifdef ARM_INTRNG +static int +mv_mpic_register_isrcs(struct mv_mpic_softc *sc) +{ + int error; + uint32_t irq; + struct intr_irqsrc *isrc; + const char *name; + + sc->mpic_isrcs = malloc(sc->nirqs * sizeof (*sc->mpic_isrcs), M_DEVBUF, + M_WAITOK | M_ZERO); + + name = device_get_nameunit(sc->sc_dev); + for (irq = 0; irq < sc->nirqs; irq++) { + sc->mpic_isrcs[irq].mmi_irq = irq; + + isrc = &sc->mpic_isrcs[irq].mmi_isrc; + if (irq < MPIC_PPI) { + error = intr_isrc_register(isrc, sc->sc_dev, + INTR_ISRCF_PPI, "%s", name); + } else { + error = intr_isrc_register(isrc, sc->sc_dev, 0, "%s", + name); + } + if (error != 0) { + /* XXX call intr_isrc_deregister() */ + device_printf(sc->sc_dev, "%s failed", __func__); + return (error); + } + } + return (0); +} +#endif + static int mv_mpic_attach(device_t dev) { @@ -227,9 +269,11 @@ mv_mpic_attach(device_t dev) sc->nirqs = MPIC_CTRL_NIRQS(val); #ifdef ARM_INTRNG - sc->mpic_isrcs = malloc(sc->nirqs * sizeof (*sc->mpic_isrcs), M_DEVBUF, - M_WAITOK | M_ZERO); - + if (mv_mpic_register_isrcs(sc) != 0) { + device_printf(dev, "could not register PIC ISRCs\n"); + bus_release_resources(dev, mv_mpic_spec, sc->mpic_res); + return (ENXIO); + } if (intr_pic_register(dev, OF_xref_from_device(dev)) != 0) { device_printf(dev, "could not register PIC\n"); bus_release_resources(dev, mv_mpic_spec, sc->mpic_res); @@ -247,14 +291,11 @@ static int mpic_intr(void *arg) { struct mv_mpic_softc *sc; - struct trapframe *tf; - struct intr_irqsrc *isrc; uint32_t cause, irqsrc; unsigned int irq; u_int cpuid; sc = arg; - tf = curthread->td_intr_frame; cpuid = PCPU_GET(cpuid); irq = 0; @@ -264,117 +305,64 @@ mpic_intr(void *arg) irqsrc = MPIC_READ(sc, MPIC_INT_CTL(irq)); if ((irqsrc & MPIC_INT_IRQ_FIQ_MASK(cpuid)) == 0) continue; - isrc = sc->mpic_isrcs[irq]; - if (isrc == NULL) { - device_printf(sc->sc_dev, "Stray interrupt %u detected\n", irq); + if (intr_isrc_dispatch(&sc->mpic_isrcs[irq].mmi_isrc, + curthread->td_intr_frame) != 0) { mpic_mask_irq(irq); - continue; + device_printf(sc->sc_dev, "Stray irq %u " + "disabled\n", irq); } - intr_irq_dispatch(isrc, tf); } } return (FILTER_HANDLED); } -static int -mpic_attach_isrc(struct mv_mpic_softc *sc, struct intr_irqsrc *isrc, u_int irq) +static void +mpic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { - const char *name; - - mtx_lock_spin(&sc->mtx); - if (sc->mpic_isrcs[irq] != NULL) { - mtx_unlock_spin(&sc->mtx); - return (sc->mpic_isrcs[irq] == isrc ? 0 : EEXIST); - } - sc->mpic_isrcs[irq] = isrc; - isrc->isrc_data = irq; - mtx_unlock_spin(&sc->mtx); - - name = device_get_nameunit(sc->sc_dev); - intr_irq_set_name(isrc, "%s", name); + u_int irq; - return (0); + irq = ((struct mv_mpic_irqsrc *)isrc)->mmi_irq; + mpic_mask_irq(irq); } -#ifdef FDT -static int -mpic_map_fdt(struct mv_mpic_softc *sc, struct intr_irqsrc *isrc, u_int *irqp) +static void +mpic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { u_int irq; - int error; - - if (isrc->isrc_ncells != 1) - return (EINVAL); - irq = isrc->isrc_cells[0]; - - error = mpic_attach_isrc(sc, isrc, irq); - if (error != 0) - return (error); - - isrc->isrc_nspc_num = irq; - isrc->isrc_trig = INTR_TRIGGER_CONFORM; - isrc->isrc_pol = INTR_POLARITY_CONFORM; - isrc->isrc_nspc_type = INTR_IRQ_NSPC_PLAIN; - - *irqp = irq; - - return (0); + irq = ((struct mv_mpic_irqsrc *)isrc)->mmi_irq; + mpic_unmask_irq(irq); } -#endif static int -mpic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu) +mpic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) { struct mv_mpic_softc *sc; - int error; - u_int irq = 0; sc = device_get_softc(dev); -#ifdef FDT - if (isrc->isrc_type == INTR_ISRCT_FDT) - error = mpic_map_fdt(sc, isrc, &irq); - else -#endif - error = EINVAL; - - if (error == 0) - *is_percpu = irq < MPIC_PPI; - - return (error); -} - -static void -mpic_disable_source(device_t dev, struct intr_irqsrc *isrc) -{ - u_int irq; + if (data->type != INTR_MAP_DATA_FDT || data->fdt.ncells !=1 || + data->fdt.cells[0] >= sc->nirqs) + return (EINVAL); - irq = isrc->isrc_data; - mpic_mask_irq(irq); + *isrcp = &sc->mpic_isrcs[data->fdt.cells[0]].mmi_isrc; + return (0); } static void -mpic_enable_source(device_t dev, struct intr_irqsrc *isrc) -{ - u_int irq; - - irq = isrc->isrc_data; - mpic_unmask_irq(irq); -} -static void mpic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { - mpic_disable_source(dev, isrc); + mpic_disable_intr(dev, isrc); } static void mpic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { - mpic_enable_source(dev, isrc); + mpic_enable_intr(dev, isrc); } #endif @@ -383,9 +371,9 @@ static device_method_t mv_mpic_methods[] = { DEVMETHOD(device_attach, mv_mpic_attach), #ifdef ARM_INTRNG - DEVMETHOD(pic_register, mpic_register), - DEVMETHOD(pic_disable_source, mpic_disable_source), - DEVMETHOD(pic_enable_source, mpic_enable_source), + DEVMETHOD(pic_disable_intr, mpic_disable_intr), + DEVMETHOD(pic_enable_intr, mpic_enable_intr), + DEVMETHOD(pic_map_intr, mpic_map_intr), DEVMETHOD(pic_post_ithread, mpic_post_ithread), DEVMETHOD(pic_pre_ithread, mpic_pre_ithread), #endif @@ -409,10 +397,10 @@ arm_get_next_irq(int last) { u_int irq, next = -1; - irq = mv_mpic_get_cause() & IRQ_MASK; + irq = mv_mpic_get_cause() & MPIC_IRQ_MASK; CTR2(KTR_INTR, "%s: irq:%#x", __func__, irq); - if (irq != IRQ_MASK) { + if (irq != MPIC_IRQ_MASK) { if (irq == MPIC_INT_ERR) irq = mv_mpic_get_cause_err(); if (irq == MPIC_INT_MSI) diff --git a/sys/arm/mv/mv_localbus.c b/sys/arm/mv/mv_localbus.c index 936bc18..fabb694 100644 --- a/sys/arm/mv/mv_localbus.c +++ b/sys/arm/mv/mv_localbus.c @@ -323,8 +323,8 @@ localbus_print_child(device_t dev, device_t child) rv = 0; rv += bus_print_child_header(dev, child); - rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); - rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); rv += bus_print_child_footer(dev, child); return (rv); diff --git a/sys/arm/nvidia/as3722.c b/sys/arm/nvidia/as3722.c new file mode 100644 index 0000000..3a03322 --- /dev/null +++ b/sys/arm/nvidia/as3722.c @@ -0,0 +1,411 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * AS3722 PMIC driver + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/gpio.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/malloc.h> +#include <sys/rman.h> +#include <sys/sx.h> + +#include <machine/bus.h> + +#include <dev/extres/regulator/regulator.h> +#include <dev/fdt/fdt_pinctrl.h> +#include <dev/gpio/gpiobusvar.h> +#include <dev/iicbus/iiconf.h> +#include <dev/iicbus/iicbus.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <gnu/dts/include/dt-bindings/mfd/as3722.h> + +#include "clock_if.h" +#include "regdev_if.h" + +#include "as3722.h" + +static struct ofw_compat_data compat_data[] = { + {"ams,as3722", 1}, + {NULL, 0}, +}; + +#define LOCK(_sc) sx_xlock(&(_sc)->lock) +#define UNLOCK(_sc) sx_xunlock(&(_sc)->lock) +#define LOCK_INIT(_sc) sx_init(&(_sc)->lock, "as3722") +#define LOCK_DESTROY(_sc) sx_destroy(&(_sc)->lock); +#define ASSERT_LOCKED(_sc) sx_assert(&(_sc)->lock, SA_XLOCKED); +#define ASSERT_UNLOCKED(_sc) sx_assert(&(_sc)->lock, SA_UNLOCKED); + +#define AS3722_DEVICE_ID 0x0C + +/* + * Raw register access function. + */ +int +as3722_read(struct as3722_softc *sc, uint8_t reg, uint8_t *val) +{ + uint8_t addr; + int rv; + struct iic_msg msgs[2] = { + {0, IIC_M_WR, 1, &addr}, + {0, IIC_M_RD, 1, val}, + }; + + msgs[0].slave = sc->bus_addr; + msgs[1].slave = sc->bus_addr; + addr = reg; + + rv = iicbus_transfer(sc->dev, msgs, 2); + if (rv != 0) { + device_printf(sc->dev, + "Error when reading reg 0x%02X, rv: %d\n", reg, rv); + return (EIO); + } + + return (0); +} + +int as3722_read_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf, + size_t size) +{ + uint8_t addr; + int rv; + struct iic_msg msgs[2] = { + {0, IIC_M_WR, 1, &addr}, + {0, IIC_M_RD, size, buf}, + }; + + msgs[0].slave = sc->bus_addr; + msgs[1].slave = sc->bus_addr; + addr = reg; + + rv = iicbus_transfer(sc->dev, msgs, 2); + if (rv != 0) { + device_printf(sc->dev, + "Error when reading reg 0x%02X, rv: %d\n", reg, rv); + return (EIO); + } + + return (0); +} + +int +as3722_write(struct as3722_softc *sc, uint8_t reg, uint8_t val) +{ + uint8_t data[2]; + int rv; + + struct iic_msg msgs[1] = { + {0, IIC_M_WR, 2, data}, + }; + + msgs[0].slave = sc->bus_addr; + data[0] = reg; + data[1] = val; + + rv = iicbus_transfer(sc->dev, msgs, 1); + if (rv != 0) { + device_printf(sc->dev, + "Error when writing reg 0x%02X, rv: %d\n", reg, rv); + return (EIO); + } + return (0); +} + +int as3722_write_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf, + size_t size) +{ + uint8_t data[1]; + int rv; + struct iic_msg msgs[2] = { + {0, IIC_M_WR, 1, data}, + {0, IIC_M_WR | IIC_M_NOSTART, size, buf}, + }; + + msgs[0].slave = sc->bus_addr; + msgs[1].slave = sc->bus_addr; + data[0] = reg; + + rv = iicbus_transfer(sc->dev, msgs, 2); + if (rv != 0) { + device_printf(sc->dev, + "Error when writing reg 0x%02X, rv: %d\n", reg, rv); + return (EIO); + } + return (0); +} + +int +as3722_modify(struct as3722_softc *sc, uint8_t reg, uint8_t clear, uint8_t set) +{ + uint8_t val; + int rv; + + rv = as3722_read(sc, reg, &val); + if (rv != 0) + return (rv); + + val &= ~clear; + val |= set; + + rv = as3722_write(sc, reg, val); + if (rv != 0) + return (rv); + + return (0); +} + +static int +as3722_get_version(struct as3722_softc *sc) +{ + uint8_t reg; + int rv; + + /* Verify AS3722 ID and version. */ + rv = RD1(sc, AS3722_ASIC_ID1, ®); + if (rv != 0) + return (ENXIO); + + if (reg != AS3722_DEVICE_ID) { + device_printf(sc->dev, "Invalid chip ID is 0x%x\n", reg); + return (ENXIO); + } + + rv = RD1(sc, AS3722_ASIC_ID2, &sc->chip_rev); + if (rv != 0) + return (ENXIO); + + if (bootverbose) + device_printf(sc->dev, "AS3722 rev: 0x%x\n", sc->chip_rev); + return (0); +} + +static int +as3722_init(struct as3722_softc *sc) +{ + uint32_t reg; + int rv; + + reg = 0; + if (sc->int_pullup) + reg |= AS3722_INT_PULL_UP; + if (sc->i2c_pullup) + reg |= AS3722_I2C_PULL_UP; + + rv = RM1(sc, AS3722_IO_VOLTAGE, + AS3722_INT_PULL_UP | AS3722_I2C_PULL_UP, reg); + if (rv != 0) + return (ENXIO); + + /* mask interrupts */ + rv = WR1(sc, AS3722_INTERRUPT_MASK1, 0); + if (rv != 0) + return (ENXIO); + rv = WR1(sc, AS3722_INTERRUPT_MASK2, 0); + if (rv != 0) + return (ENXIO); + rv = WR1(sc, AS3722_INTERRUPT_MASK3, 0); + if (rv != 0) + return (ENXIO); + rv = WR1(sc, AS3722_INTERRUPT_MASK4, 0); + if (rv != 0) + return (ENXIO); + return (0); +} + +static int +as3722_parse_fdt(struct as3722_softc *sc, phandle_t node) +{ + + sc->int_pullup = + OF_hasprop(node, "ams,enable-internal-int-pullup") ? 1 : 0; + sc->i2c_pullup = + OF_hasprop(node, "ams,enable-internal-i2c-pullup") ? 1 : 0; + return 0; +} + +static void +as3722_intr(void *arg) +{ + struct as3722_softc *sc; + + sc = (struct as3722_softc *)arg; + /* XXX Finish temperature alarms. */ +} + +static int +as3722_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + + device_set_desc(dev, "AS3722 PMIC"); + return (BUS_PROBE_DEFAULT); +} + +static int +as3722_attach(device_t dev) +{ + struct as3722_softc *sc; + const char *dname; + int dunit, rv, rid; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->bus_addr = iicbus_get_addr(dev); + node = ofw_bus_get_node(sc->dev); + dname = device_get_name(dev); + dunit = device_get_unit(dev); + rv = 0; + LOCK_INIT(sc); + + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "Cannot allocate interrupt.\n"); + rv = ENXIO; + goto fail; + } + + rv = as3722_parse_fdt(sc, node); + if (rv != 0) + goto fail; + rv = as3722_get_version(sc); + if (rv != 0) + goto fail; + rv = as3722_init(sc); + if (rv != 0) + goto fail; + rv = as3722_regulator_attach(sc, node); + if (rv != 0) + goto fail; + rv = as3722_gpio_attach(sc, node); + if (rv != 0) + goto fail; + rv = as3722_rtc_attach(sc, node); + if (rv != 0) + goto fail; + + fdt_pinctrl_register(dev, NULL); + fdt_pinctrl_configure_by_name(dev, "default"); + + /* Setup interrupt. */ + rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, as3722_intr, sc, &sc->irq_h); + if (rv) { + device_printf(dev, "Cannot setup interrupt.\n"); + goto fail; + } + return (bus_generic_attach(dev)); + +fail: + if (sc->irq_h != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_h); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + LOCK_DESTROY(sc); + return (rv); +} + +static int +as3722_detach(device_t dev) +{ + struct as3722_softc *sc; + + sc = device_get_softc(dev); + if (sc->irq_h != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_h); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + LOCK_DESTROY(sc); + + return (bus_generic_detach(dev)); +} + +static phandle_t +as3722_gpio_get_node(device_t bus, device_t dev) +{ + + /* We only have one child, the GPIO bus, which needs our own node. */ + return (ofw_bus_get_node(bus)); +} + +static device_method_t as3722_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, as3722_probe), + DEVMETHOD(device_attach, as3722_attach), + DEVMETHOD(device_detach, as3722_detach), + + /* Regdev interface */ + DEVMETHOD(regdev_map, as3722_regulator_map), + + /* RTC interface */ + DEVMETHOD(clock_gettime, as3722_rtc_gettime), + DEVMETHOD(clock_settime, as3722_rtc_settime), + + /* GPIO protocol interface */ + DEVMETHOD(gpio_get_bus, as3722_gpio_get_bus), + DEVMETHOD(gpio_pin_max, as3722_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, as3722_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, as3722_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, as3722_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, as3722_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, as3722_gpio_pin_get), + DEVMETHOD(gpio_pin_set, as3722_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, as3722_gpio_pin_toggle), + DEVMETHOD(gpio_map_gpios, as3722_gpio_map_gpios), + + /* fdt_pinctrl interface */ + DEVMETHOD(fdt_pinctrl_configure, as3722_pinmux_configure), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, as3722_gpio_get_node), + + DEVMETHOD_END +}; + +static devclass_t as3722_devclass; +DEFINE_CLASS_0(gpio, as3722_driver, as3722_methods, + sizeof(struct as3722_softc)); +EARLY_DRIVER_MODULE(as3722, iicbus, as3722_driver, as3722_devclass, + 0, 0, 74); diff --git a/sys/arm/nvidia/as3722.h b/sys/arm/nvidia/as3722.h new file mode 100644 index 0000000..c559a8a --- /dev/null +++ b/sys/arm/nvidia/as3722.h @@ -0,0 +1,323 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#ifndef _AS3722_H_ + +#include <sys/clock.h> + +#define AS3722_SD0_VOLTAGE 0x00 +#define AS3722_SD_VSEL_MASK 0x7F /* For all SD */ +#define AS3722_SD0_VSEL_MIN 0x01 +#define AS3722_SD0_VSEL_MAX 0x5A +#define AS3722_SD0_VSEL_LOW_VOL_MAX 0x6E + +#define AS3722_SD1_VOLTAGE 0x01 +#define AS3722_SD2_VOLTAGE 0x02 +#define AS3722_SD2_VSEL_MIN 0x01 +#define AS3722_SD2_VSEL_MAX 0x7F +#define AS3722_SD3_VOLTAGE 0x03 +#define AS3722_SD4_VOLTAGE 0x04 +#define AS3722_SD5_VOLTAGE 0x05 +#define AS3722_SD6_VOLTAGE 0x06 +#define AS3722_GPIO0_CONTROL 0x08 +#define AS3722_GPIO_INVERT 0x80 +#define AS3722_GPIO_IOSF_MASK 0x0F +#define AS3722_GPIO_IOSF_SHIFT 3 +#define AS3722_GPIO_MODE_MASK 0x07 +#define AS3722_GPIO_MODE_SHIFT 0 + +#define AS3722_GPIO1_CONTROL 0x09 +#define AS3722_GPIO2_CONTROL 0x0A +#define AS3722_GPIO3_CONTROL 0x0B +#define AS3722_GPIO4_CONTROL 0x0C +#define AS3722_GPIO5_CONTROL 0x0D +#define AS3722_GPIO6_CONTROL 0x0E +#define AS3722_GPIO7_CONTROL 0x0F +#define AS3722_LDO0_VOLTAGE 0x10 +#define AS3722_LDO0_VSEL_MASK 0x1F +#define AS3722_LDO0_VSEL_MIN 0x01 +#define AS3722_LDO0_VSEL_MAX 0x12 +#define AS3722_LDO0_NUM_VOLT 0x12 + +#define AS3722_LDO1_VOLTAGE 0x11 +#define AS3722_LDO_VSEL_MASK 0x7F +#define AS3722_LDO_VSEL_MIN 0x01 +#define AS3722_LDO_VSEL_MAX 0x7F +#define AS3722_LDO_VSEL_DNU_MIN 0x25 +#define AS3722_LDO_VSEL_DNU_MAX 0x3F +#define AS3722_LDO_NUM_VOLT 0x80 + +#define AS3722_LDO2_VOLTAGE 0x12 +#define AS3722_LDO3_VOLTAGE 0x13 +#define AS3722_LDO3_VSEL_MASK 0x3F +#define AS3722_LDO3_VSEL_MIN 0x01 +#define AS3722_LDO3_VSEL_MAX 0x2D +#define AS3722_LDO3_NUM_VOLT 0x2D +#define AS3722_LDO3_MODE_MASK (0x3 << 6) +#define AS3722_LDO3_MODE_GET(x) (((x) >> 6) & 0x3) +#define AS3722_LDO3_MODE(x) (((x) & 0x3) << 6) +#define AS3722_LDO3_MODE_PMOS AS3722_LDO3_MODE(0) +#define AS3722_LDO3_MODE_PMOS_TRACKING AS3722_LDO3_MODE(1) +#define AS3722_LDO3_MODE_NMOS AS3722_LDO3_MODE(2) +#define AS3722_LDO3_MODE_SWITCH AS3722_LDO3_MODE(3) + +#define AS3722_LDO4_VOLTAGE 0x14 +#define AS3722_LDO5_VOLTAGE 0x15 +#define AS3722_LDO6_VOLTAGE 0x16 +#define AS3722_LDO6_SEL_BYPASS 0x3F +#define AS3722_LDO7_VOLTAGE 0x17 +#define AS3722_LDO9_VOLTAGE 0x19 +#define AS3722_LDO10_VOLTAGE 0x1A +#define AS3722_LDO11_VOLTAGE 0x1B +#define AS3722_LDO3_SETTINGS 0x1D +#define AS3722_GPIO_DEB1 0x1E +#define AS3722_GPIO_DEB2 0x1F +#define AS3722_GPIO_SIGNAL_OUT 0x20 +#define AS3722_GPIO_SIGNAL_IN 0x21 +#define AS3722_REG_SEQU_MOD1 0x22 +#define AS3722_REG_SEQU_MOD2 0x23 +#define AS3722_REG_SEQU_MOD3 0x24 +#define AS3722_SD_PHSW_CTRL 0x27 +#define AS3722_SD_PHSW_STATUS 0x28 + +#define AS3722_SD0_CONTROL 0x29 +#define AS3722_SD0_MODE_FAST (1 << 4) + +#define AS3722_SD1_CONTROL 0x2A +#define AS3722_SD1_MODE_FAST (1 << 4) + +#define AS3722_SDMPH_CONTROL 0x2B +#define AS3722_SD23_CONTROL 0x2C +#define AS3722_SD3_MODE_FAST (1 << 6) +#define AS3722_SD2_MODE_FAST (1 << 2) + +#define AS3722_SD4_CONTROL 0x2D +#define AS3722_SD4_MODE_FAST (1 << 2) + +#define AS3722_SD5_CONTROL 0x2E +#define AS3722_SD5_MODE_FAST (1 << 2) + +#define AS3722_SD6_CONTROL 0x2F +#define AS3722_SD6_MODE_FAST (1 << 4) + +#define AS3722_SD_DVM 0x30 +#define AS3722_RESET_REASON 0x31 +#define AS3722_BATTERY_VOLTAGE_MONITOR 0x32 +#define AS3722_STARTUP_CONTROL 0x33 +#define AS3722_RESET_TIMER 0x34 +#define AS3722_REFERENCE_CONTROL 0x35 +#define AS3722_RESET_CONTROL 0x36 +#define AS3722_OVERTEMPERATURE_CONTROL 0x37 +#define AS3722_WATCHDOG_CONTROL 0x38 +#define AS3722_REG_STANDBY_MOD1 0x39 +#define AS3722_REG_STANDBY_MOD2 0x3A +#define AS3722_REG_STANDBY_MOD3 0x3B +#define AS3722_ENABLE_CTRL1 0x3C +#define AS3722_SD3_EXT_ENABLE_MASK 0xC0 +#define AS3722_SD2_EXT_ENABLE_MASK 0x30 +#define AS3722_SD1_EXT_ENABLE_MASK 0x0C +#define AS3722_SD0_EXT_ENABLE_MASK 0x03 + +#define AS3722_ENABLE_CTRL2 0x3D +#define AS3722_SD6_EXT_ENABLE_MASK 0x30 +#define AS3722_SD5_EXT_ENABLE_MASK 0x0C +#define AS3722_SD4_EXT_ENABLE_MASK 0x03 + +#define AS3722_ENABLE_CTRL3 0x3E +#define AS3722_LDO3_EXT_ENABLE_MASK 0xC0 +#define AS3722_LDO2_EXT_ENABLE_MASK 0x30 +#define AS3722_LDO1_EXT_ENABLE_MASK 0x0C +#define AS3722_LDO0_EXT_ENABLE_MASK 0x03 + +#define AS3722_ENABLE_CTRL4 0x3F +#define AS3722_LDO7_EXT_ENABLE_MASK 0xC0 +#define AS3722_LDO6_EXT_ENABLE_MASK 0x30 +#define AS3722_LDO5_EXT_ENABLE_MASK 0x0C +#define AS3722_LDO4_EXT_ENABLE_MASK 0x03 + +#define AS3722_ENABLE_CTRL5 0x40 +#define AS3722_LDO11_EXT_ENABLE_MASK 0xC0 +#define AS3722_LDO10_EXT_ENABLE_MASK 0x30 +#define AS3722_LDO9_EXT_ENABLE_MASK 0x0C + +#define AS3722_PWM_CONTROL_L 0x41 +#define AS3722_PWM_CONTROL_H 0x42 +#define AS3722_WATCHDOG_TIMER 0x46 +#define AS3722_WATCHDOG_SOFTWARE_SIGNAL 0x48 +#define AS3722_IO_VOLTAGE 0x49 +#define AS3722_I2C_PULL_UP (1 << 4) +#define AS3722_INT_PULL_UP (1 << 5) + +#define AS3722_BATTERY_VOLTAGE_MONITOR2 0x4A +#define AS3722_SD_CONTROL 0x4D +#define AS3722_SDN_CTRL(x) (1 << (x)) + +#define AS3722_LDO_CONTROL0 0x4E +#define AS3722_LDO7_CTRL (1 << 7) +#define AS3722_LDO6_CTRL (1 << 6) +#define AS3722_LDO5_CTRL (1 << 5) +#define AS3722_LDO4_CTRL (1 << 4) +#define AS3722_LDO3_CTRL (1 << 3) +#define AS3722_LDO2_CTRL (1 << 2) +#define AS3722_LDO1_CTRL (1 << 1) +#define AS3722_LDO0_CTRL (1 << 0) + +#define AS3722_LDO_CONTROL1 0x4F +#define AS3722_LDO11_CTRL (1 << 3) +#define AS3722_LDO10_CTRL (1 << 2) +#define AS3722_LDO9_CTRL (1 << 1) + +#define AS3722_SD0_PROTECT 0x50 +#define AS3722_SD6_PROTECT 0x51 +#define AS3722_PWM_VCONTROL1 0x52 +#define AS3722_PWM_VCONTROL2 0x53 +#define AS3722_PWM_VCONTROL3 0x54 +#define AS3722_PWM_VCONTROL4 0x55 +#define AS3722_BB_CHARGER 0x57 +#define AS3722_CTRL_SEQU1 0x58 +#define AS3722_CTRL_SEQU2 0x59 +#define AS3722_OV_CURRENT 0x5A +#define AS3722_OV_CURRENT_DEB 0x5B +#define AS3722_SDLV_DEB 0x5C +#define AS3722_OC_PG_CTRL 0x5D +#define AS3722_OC_PG_CTRL2 0x5E +#define AS3722_CTRL_STATUS 0x5F +#define AS3722_RTC_CONTROL 0x60 +#define AS3722_RTC_AM_PM_MODE (1 << 7) +#define AS3722_RTC_CLK32K_OUT_EN (1 << 5) +#define AS3722_RTC_IRQ_MODE (1 << 3) +#define AS3722_RTC_ON (1 << 2) +#define AS3722_RTC_ALARM_WAKEUP_EN (1 << 1) +#define AS3722_RTC_REP_WAKEUP_EN (1 << 0) + +#define AS3722_RTC_SECOND 0x61 +#define AS3722_RTC_MINUTE 0x62 +#define AS3722_RTC_HOUR 0x63 +#define AS3722_RTC_DAY 0x64 +#define AS3722_RTC_MONTH 0x65 +#define AS3722_RTC_YEAR 0x66 +#define AS3722_RTC_ALARM_SECOND 0x67 +#define AS3722_RTC_ALARM_MINUTE 0x68 +#define AS3722_RTC_ALARM_HOUR 0x69 +#define AS3722_RTC_ALARM_DAY 0x6A +#define AS3722_RTC_ALARM_MONTH 0x6B +#define AS3722_RTC_ALARM_YEAR 0x6C +#define AS3722_SRAM 0x6D +#define AS3722_RTC_ACCESS 0x6F +#define AS3722_REG_STATUS 0x73 +#define AS3722_INTERRUPT_MASK1 0x74 +#define AS3722_INTERRUPT_MASK2 0x75 +#define AS3722_INTERRUPT_MASK3 0x76 +#define AS3722_INTERRUPT_MASK4 0x77 +#define AS3722_INTERRUPT_STATUS1 0x78 +#define AS3722_INTERRUPT_STATUS2 0x79 +#define AS3722_INTERRUPT_STATUS3 0x7A +#define AS3722_INTERRUPT_STATUS4 0x7B +#define AS3722_TEMP_STATUS 0x7D +#define AS3722_ADC0_CONTROL 0x80 +#define AS3722_ADC1_CONTROL 0x81 +#define AS3722_ADC0_MSB_RESULT 0x82 +#define AS3722_ADC0_LSB_RESULT 0x83 +#define AS3722_ADC1_MSB_RESULT 0x84 +#define AS3722_ADC1_LSB_RESULT 0x85 +#define AS3722_ADC1_THRESHOLD_HI_MSB 0x86 +#define AS3722_ADC1_THRESHOLD_HI_LSB 0x87 +#define AS3722_ADC1_THRESHOLD_LO_MSB 0x88 +#define AS3722_ADC1_THRESHOLD_LO_LSB 0x89 +#define AS3722_ADC_CONFIGURATION 0x8A +#define AS3722_ASIC_ID1 0x90 +#define AS3722_ASIC_ID2 0x91 +#define AS3722_LOCK 0x9E +#define AS3722_FUSE7 0x9E +#define AS3722_FUSE7_SD0_LOW_VOLTAGE (1 << 4) + +struct as3722_reg_sc; +struct as3722_gpio_pin; + +struct as3722_softc { + device_t dev; + struct sx lock; + int bus_addr; + struct resource *irq_res; + void *irq_h; + + uint8_t chip_rev; + int int_pullup; + int i2c_pullup; + + /* Regulators. */ + struct as3722_reg_sc **regs; + int nregs; + + /* GPIO */ + device_t gpio_busdev; + struct as3722_gpio_pin **gpio_pins; + int gpio_npins; + struct sx gpio_lock; + +}; + +#define RD1(sc, reg, val) as3722_read(sc, reg, val) +#define WR1(sc, reg, val) as3722_write(sc, reg, val) +#define RM1(sc, reg, clr, set) as3722_modify(sc, reg, clr, set) + +int as3722_read(struct as3722_softc *sc, uint8_t reg, uint8_t *val); +int as3722_write(struct as3722_softc *sc, uint8_t reg, uint8_t val); +int as3722_modify(struct as3722_softc *sc, uint8_t reg, uint8_t clear, + uint8_t set); +int as3722_read_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf, + size_t size); +int as3722_write_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf, + size_t size); + +/* Regulators */ +int as3722_regulator_attach(struct as3722_softc *sc, phandle_t node); +int as3722_regulator_map(device_t dev, phandle_t xref, int ncells, + pcell_t *cells, int *num); + +/* RTC */ +int as3722_rtc_attach(struct as3722_softc *sc, phandle_t node); +int as3722_rtc_gettime(device_t dev, struct timespec *ts); +int as3722_rtc_settime(device_t dev, struct timespec *ts); + +/* GPIO */ +device_t as3722_gpio_get_bus(device_t dev); +int as3722_gpio_pin_max(device_t dev, int *maxpin); +int as3722_gpio_pin_getname(device_t dev, uint32_t pin, char *name); +int as3722_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags); +int as3722_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); +int as3722_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags); +int as3722_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); +int as3722_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val); +int as3722_gpio_pin_toggle(device_t dev, uint32_t pin); +int as3722_gpio_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent, + int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags); +int as3722_gpio_attach(struct as3722_softc *sc, phandle_t node); +int as3722_pinmux_configure(device_t dev, phandle_t cfgxref); + +#endif /* _AS3722_H_ */ diff --git a/sys/arm/nvidia/as3722_gpio.c b/sys/arm/nvidia/as3722_gpio.c new file mode 100644 index 0000000..8e53bce --- /dev/null +++ b/sys/arm/nvidia/as3722_gpio.c @@ -0,0 +1,577 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/gpio.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/sx.h> + +#include <machine/bus.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/gpio/gpiobusvar.h> + +#include "as3722.h" + +MALLOC_DEFINE(M_AS3722_GPIO, "AS3722 gpio", "AS3722 GPIO"); + +/* AS3722_GPIOx_CONTROL MODE and IOSF definition. */ +#define AS3722_IOSF_GPIO 0x00 +#define AS3722_IOSF_INTERRUPT_OUT 0x01 +#define AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT 0x02 +#define AS3722_IOSF_GPIO_IN_INTERRUPT 0x03 +#define AS3722_IOSF_PWM_IN 0x04 +#define AS3722_IOSF_VOLTAGE_IN_STANDBY 0x05 +#define AS3722_IOSF_OC_PG_SD0 0x06 +#define AS3722_IOSF_POWERGOOD_OUT 0x07 +#define AS3722_IOSF_CLK32K_OUT 0x08 +#define AS3722_IOSF_WATCHDOG_IN 0x09 +#define AS3722_IOSF_SOFT_RESET_IN 0x0b +#define AS3722_IOSF_PWM_OUT 0x0c +#define AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT 0x0d +#define AS3722_IOSF_OC_PG_SD6 0x0e + +#define AS3722_MODE_INPUT 0 +#define AS3722_MODE_PUSH_PULL 1 +#define AS3722_MODE_OPEN_DRAIN 2 +#define AS3722_MODE_TRISTATE 3 +#define AS3722_MODE_INPUT_PULL_UP_LV 4 +#define AS3722_MODE_INPUT_PULL_DOWN 5 +#define AS3722_MODE_OPEN_DRAIN_LV 6 +#define AS3722_MODE_PUSH_PULL_LV 7 + +#define NGPIO 8 + +#define GPIO_LOCK(_sc) sx_slock(&(_sc)->gpio_lock) +#define GPIO_UNLOCK(_sc) sx_unlock(&(_sc)->gpio_lock) +#define GPIO_ASSERT(_sc) sx_assert(&(_sc)->gpio_lock, SA_LOCKED) + +#define AS3722_CFG_BIAS_DISABLE 0x0001 +#define AS3722_CFG_BIAS_PULL_UP 0x0002 +#define AS3722_CFG_BIAS_PULL_DOWN 0x0004 +#define AS3722_CFG_BIAS_HIGH_IMPEDANCE 0x0008 +#define AS3722_CFG_OPEN_DRAIN 0x0010 + +static const struct { + const char *name; + int config; /* AS3722_CFG_ */ +} as3722_cfg_names[] = { + {"bias-disable", AS3722_CFG_BIAS_DISABLE}, + {"bias-pull-up", AS3722_CFG_BIAS_PULL_UP}, + {"bias-pull-down", AS3722_CFG_BIAS_PULL_DOWN}, + {"bias-high-impedance", AS3722_CFG_BIAS_HIGH_IMPEDANCE}, + {"drive-open-drain", AS3722_CFG_OPEN_DRAIN}, +}; + +static struct { + const char *name; + int fnc_val; +} as3722_fnc_table[] = { + {"gpio", AS3722_IOSF_GPIO}, + {"interrupt-out", AS3722_IOSF_INTERRUPT_OUT}, + {"vsup-vbat-low-undebounce-out", AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT}, + {"gpio-in-interrupt", AS3722_IOSF_GPIO_IN_INTERRUPT}, + {"pwm-in", AS3722_IOSF_PWM_IN}, + {"voltage-in-standby", AS3722_IOSF_VOLTAGE_IN_STANDBY}, + {"oc-pg-sd0", AS3722_IOSF_OC_PG_SD0}, + {"powergood-out", AS3722_IOSF_POWERGOOD_OUT}, + {"clk32k-out", AS3722_IOSF_CLK32K_OUT}, + {"watchdog-in", AS3722_IOSF_WATCHDOG_IN}, + {"soft-reset-in", AS3722_IOSF_SOFT_RESET_IN}, + {"pwm-out", AS3722_IOSF_PWM_OUT}, + {"vsup-vbat-low-debounce-out", AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT}, + {"oc-pg-sd6", AS3722_IOSF_OC_PG_SD6}, +}; + +struct as3722_pincfg { + char *function; + int flags; +}; + +struct as3722_gpio_pin { + int pin_caps; + uint8_t pin_ctrl_reg; + char pin_name[GPIOMAXNAME]; + int pin_cfg_flags; +}; + + +/* -------------------------------------------------------------------------- + * + * Pinmux functions. + */ +static int +as3722_pinmux_get_function(struct as3722_softc *sc, char *name) +{ + int i; + + for (i = 0; i < nitems(as3722_fnc_table); i++) { + if (strcmp(as3722_fnc_table[i].name, name) == 0) + return (as3722_fnc_table[i].fnc_val); + } + return (-1); +} + + + +static int +as3722_pinmux_config_node(struct as3722_softc *sc, char *pin_name, + struct as3722_pincfg *cfg) +{ + uint8_t ctrl; + int rv, fnc, pin; + + for (pin = 0; pin < sc->gpio_npins; pin++) { + if (strcmp(sc->gpio_pins[pin]->pin_name, pin_name) == 0) + break; + } + if (pin >= sc->gpio_npins) { + device_printf(sc->dev, "Unknown pin: %s\n", pin_name); + return (ENXIO); + } + + ctrl = sc->gpio_pins[pin]->pin_ctrl_reg; + sc->gpio_pins[pin]->pin_cfg_flags = cfg->flags; + if (cfg->function != NULL) { + fnc = as3722_pinmux_get_function(sc, cfg->function); + if (fnc == -1) { + device_printf(sc->dev, + "Unknown function %s for pin %s\n", cfg->function, + sc->gpio_pins[pin]->pin_name); + return (ENXIO); + } + switch (fnc) { + case AS3722_IOSF_INTERRUPT_OUT: + case AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT: + case AS3722_IOSF_OC_PG_SD0: + case AS3722_IOSF_POWERGOOD_OUT: + case AS3722_IOSF_CLK32K_OUT: + case AS3722_IOSF_PWM_OUT: + case AS3722_IOSF_OC_PG_SD6: + ctrl &= ~(AS3722_GPIO_MODE_MASK << + AS3722_GPIO_MODE_SHIFT); + ctrl |= AS3722_MODE_PUSH_PULL << AS3722_GPIO_MODE_SHIFT; + /* XXX Handle flags (OC + pullup) */ + break; + case AS3722_IOSF_GPIO_IN_INTERRUPT: + case AS3722_IOSF_PWM_IN: + case AS3722_IOSF_VOLTAGE_IN_STANDBY: + case AS3722_IOSF_WATCHDOG_IN: + case AS3722_IOSF_SOFT_RESET_IN: + ctrl &= ~(AS3722_GPIO_MODE_MASK << + AS3722_GPIO_MODE_SHIFT); + ctrl |= AS3722_MODE_INPUT << AS3722_GPIO_MODE_SHIFT; + /* XXX Handle flags (pulldown + pullup) */ + + default: + break; + } + ctrl &= ~(AS3722_GPIO_IOSF_MASK << AS3722_GPIO_IOSF_SHIFT); + ctrl |= fnc << AS3722_GPIO_IOSF_SHIFT; + } + rv = 0; + if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) { + rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl); + sc->gpio_pins[pin]->pin_ctrl_reg = ctrl; + } + return (rv); +} + +static int +as3722_pinmux_read_node(struct as3722_softc *sc, phandle_t node, + struct as3722_pincfg *cfg, char **pins, int *lpins) +{ + int rv, i; + + *lpins = OF_getprop_alloc(node, "pins", 1, (void **)pins); + if (*lpins <= 0) + return (ENOENT); + + /* Read function (mux) settings. */ + rv = OF_getprop_alloc(node, "function", 1, (void **)&cfg->function); + if (rv <= 0) + cfg->function = NULL; + + /* Read boolean properties. */ + for (i = 0; i < nitems(as3722_cfg_names); i++) { + if (OF_hasprop(node, as3722_cfg_names[i].name)) + cfg->flags |= as3722_cfg_names[i].config; + } + return (0); +} + +static int +as3722_pinmux_process_node(struct as3722_softc *sc, phandle_t node) +{ + struct as3722_pincfg cfg; + char *pins, *pname; + int i, len, lpins, rv; + + rv = as3722_pinmux_read_node(sc, node, &cfg, &pins, &lpins); + if (rv != 0) + return (rv); + + len = 0; + pname = pins; + do { + i = strlen(pname) + 1; + rv = as3722_pinmux_config_node(sc, pname, &cfg); + if (rv != 0) { + device_printf(sc->dev, + "Cannot configure pin: %s: %d\n", pname, rv); + } + len += i; + pname += i; + } while (len < lpins); + + if (pins != NULL) + free(pins, M_OFWPROP); + if (cfg.function != NULL) + free(cfg.function, M_OFWPROP); + + return (rv); +} + +int as3722_pinmux_configure(device_t dev, phandle_t cfgxref) +{ + struct as3722_softc *sc; + phandle_t node, cfgnode; + int rv; + + sc = device_get_softc(dev); + cfgnode = OF_node_from_xref(cfgxref); + + for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) { + if (!fdt_is_enabled(node)) + continue; + rv = as3722_pinmux_process_node(sc, node); + if (rv != 0) + device_printf(dev, "Failed to process pinmux"); + + } + return (0); +} + +/* -------------------------------------------------------------------------- + * + * GPIO + */ +device_t +as3722_gpio_get_bus(device_t dev) +{ + struct as3722_softc *sc; + + sc = device_get_softc(dev); + return (sc->gpio_busdev); +} + +int +as3722_gpio_pin_max(device_t dev, int *maxpin) +{ + + *maxpin = NGPIO - 1; + return (0); +} + +int +as3722_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct as3722_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + GPIO_LOCK(sc); + *caps = sc->gpio_pins[pin]->pin_caps; + GPIO_UNLOCK(sc); + return (0); +} + +int +as3722_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct as3722_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + GPIO_LOCK(sc); + memcpy(name, sc->gpio_pins[pin]->pin_name, GPIOMAXNAME); + GPIO_UNLOCK(sc); + return (0); +} + +int +as3722_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *out_flags) +{ + struct as3722_softc *sc; + uint8_t tmp, mode, iosf; + uint32_t flags; + bool inverted; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + tmp = sc->gpio_pins[pin]->pin_ctrl_reg; + GPIO_UNLOCK(sc); + iosf = (tmp >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK; + mode = (tmp >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK; + inverted = (tmp & AS3722_GPIO_INVERT) != 0; + /* Is pin in GPIO mode ? */ + if (iosf != AS3722_IOSF_GPIO) + return (ENXIO); + + flags = 0; + switch (mode) { + case AS3722_MODE_INPUT: + flags = GPIO_PIN_INPUT; + break; + case AS3722_MODE_PUSH_PULL: + case AS3722_MODE_PUSH_PULL_LV: + flags = GPIO_PIN_OUTPUT; + break; + case AS3722_MODE_OPEN_DRAIN: + case AS3722_MODE_OPEN_DRAIN_LV: + flags = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN; + break; + case AS3722_MODE_TRISTATE: + flags = GPIO_PIN_TRISTATE; + break; + case AS3722_MODE_INPUT_PULL_UP_LV: + flags = GPIO_PIN_INPUT | GPIO_PIN_PULLUP; + break; + + case AS3722_MODE_INPUT_PULL_DOWN: + flags = GPIO_PIN_OUTPUT | GPIO_PIN_PULLDOWN; + break; + } + if (inverted) + flags |= GPIO_PIN_INVIN | GPIO_PIN_INVOUT; + *out_flags = flags; + return (0); +} + +static int +as3722_gpio_get_mode(struct as3722_softc *sc, uint32_t pin, uint32_t gpio_flags) +{ + uint8_t ctrl; + int flags; + + ctrl = sc->gpio_pins[pin]->pin_ctrl_reg; + flags = sc->gpio_pins[pin]->pin_cfg_flags; + + /* Tristate mode. */ + if (flags & AS3722_CFG_BIAS_HIGH_IMPEDANCE || + gpio_flags & GPIO_PIN_TRISTATE) + return (AS3722_MODE_TRISTATE); + + /* Open drain modes. */ + if (flags & AS3722_CFG_OPEN_DRAIN || gpio_flags & GPIO_PIN_OPENDRAIN) { + /* Only pull up have effect */ + if (flags & AS3722_CFG_BIAS_PULL_UP || + gpio_flags & GPIO_PIN_PULLUP) + return (AS3722_MODE_OPEN_DRAIN_LV); + return (AS3722_MODE_OPEN_DRAIN); + } + /* Input modes. */ + if (gpio_flags & GPIO_PIN_INPUT) { + /* Accept pull up or pull down. */ + if (flags & AS3722_CFG_BIAS_PULL_UP || + gpio_flags & GPIO_PIN_PULLUP) + return (AS3722_MODE_INPUT_PULL_UP_LV); + + if (flags & AS3722_CFG_BIAS_PULL_DOWN || + gpio_flags & GPIO_PIN_PULLDOWN) + return (AS3722_MODE_INPUT_PULL_DOWN); + return (AS3722_MODE_INPUT); + } + /* + * Output modes. + * Pull down is used as indicator of low voltage output. + */ + if (flags & AS3722_CFG_BIAS_PULL_DOWN || + gpio_flags & GPIO_PIN_PULLDOWN) + return (AS3722_MODE_PUSH_PULL_LV); + return (AS3722_MODE_PUSH_PULL); +} + +int +as3722_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct as3722_softc *sc; + uint8_t ctrl, mode, iosf; + int rv; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + ctrl = sc->gpio_pins[pin]->pin_ctrl_reg; + iosf = (ctrl >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK; + /* Is pin in GPIO mode ? */ + if (iosf != AS3722_IOSF_GPIO) { + GPIO_UNLOCK(sc); + return (ENXIO); + } + mode = as3722_gpio_get_mode(sc, pin, flags); + ctrl &= ~(AS3722_GPIO_MODE_MASK << AS3722_GPIO_MODE_SHIFT); + ctrl |= AS3722_MODE_PUSH_PULL << AS3722_GPIO_MODE_SHIFT; + rv = 0; + if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) { + rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl); + sc->gpio_pins[pin]->pin_ctrl_reg = ctrl; + } + GPIO_UNLOCK(sc); + return (rv); +} + +int +as3722_gpio_pin_set(device_t dev, uint32_t pin, uint32_t val) +{ + struct as3722_softc *sc; + uint8_t tmp; + int rv; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + tmp = (val != 0) ? 1 : 0; + if (sc->gpio_pins[pin]->pin_ctrl_reg & AS3722_GPIO_INVERT) + tmp ^= 1; + + GPIO_LOCK(sc); + rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), (tmp << pin)); + GPIO_UNLOCK(sc); + return (rv); +} + +int +as3722_gpio_pin_get(device_t dev, uint32_t pin, uint32_t *val) +{ + struct as3722_softc *sc; + uint8_t tmp, mode, ctrl; + int rv; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + ctrl = sc->gpio_pins[pin]->pin_ctrl_reg; + mode = (ctrl >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK; + if ((mode == AS3722_MODE_PUSH_PULL) || + (mode == AS3722_MODE_PUSH_PULL_LV)) + rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp); + else + rv = RD1(sc, AS3722_GPIO_SIGNAL_IN, &tmp); + GPIO_UNLOCK(sc); + if (rv != 0) + return (rv); + + *val = tmp & (1 << pin) ? 1 : 0; + if (ctrl & AS3722_GPIO_INVERT) + *val ^= 1; + return (0); +} + +int +as3722_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct as3722_softc *sc; + uint8_t tmp; + int rv; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp); + if (rv != 0) { + GPIO_UNLOCK(sc); + return (rv); + } + tmp ^= (1 <<pin); + rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), tmp); + GPIO_UNLOCK(sc); + return (0); +} + +int +as3722_gpio_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent, + int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) +{ + + if (gcells != 2) + return (ERANGE); + *pin = gpios[0]; + *flags= gpios[1]; + return (0); +} + +int +as3722_gpio_attach(struct as3722_softc *sc, phandle_t node) +{ + struct as3722_gpio_pin *pin; + int i, rv; + + sx_init(&sc->gpio_lock, "AS3722 GPIO lock"); + sc->gpio_npins = NGPIO; + sc->gpio_pins = malloc(sizeof(struct as3722_gpio_pin *) * + sc->gpio_npins, M_AS3722_GPIO, M_WAITOK | M_ZERO); + + + sc->gpio_busdev = gpiobus_attach_bus(sc->dev); + if (sc->gpio_busdev == NULL) + return (ENXIO); + for (i = 0; i < sc->gpio_npins; i++) { + sc->gpio_pins[i] = malloc(sizeof(struct as3722_gpio_pin), + M_AS3722_GPIO, M_WAITOK | M_ZERO); + pin = sc->gpio_pins[i]; + sprintf(pin->pin_name, "gpio%d", i); + pin->pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | + GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE | + GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_PIN_INVIN | + GPIO_PIN_INVOUT; + rv = RD1(sc, AS3722_GPIO0_CONTROL + i, &pin->pin_ctrl_reg); + if (rv != 0) { + device_printf(sc->dev, + "Cannot read configuration for pin %s\n", + sc->gpio_pins[i]->pin_name); + } + } + return (0); +} diff --git a/sys/arm/nvidia/as3722_regulators.c b/sys/arm/nvidia/as3722_regulators.c new file mode 100644 index 0000000..0bca8e1 --- /dev/null +++ b/sys/arm/nvidia/as3722_regulators.c @@ -0,0 +1,811 @@ +/*- + * Copyright 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/gpio.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/malloc.h> +#include <sys/rman.h> +#include <sys/sx.h> + +#include <machine/bus.h> + +#include <dev/extres/regulator/regulator.h> +#include <dev/gpio/gpiobusvar.h> + +#include <gnu/dts/include/dt-bindings/mfd/as3722.h> + +#include "as3722.h" + +MALLOC_DEFINE(M_AS3722_REG, "AS3722 regulator", "AS3722 power regulator"); + +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) + +enum as3722_reg_id { + AS3722_REG_ID_SD0, + AS3722_REG_ID_SD1, + AS3722_REG_ID_SD2, + AS3722_REG_ID_SD3, + AS3722_REG_ID_SD4, + AS3722_REG_ID_SD5, + AS3722_REG_ID_SD6, + AS3722_REG_ID_LDO0, + AS3722_REG_ID_LDO1, + AS3722_REG_ID_LDO2, + AS3722_REG_ID_LDO3, + AS3722_REG_ID_LDO4, + AS3722_REG_ID_LDO5, + AS3722_REG_ID_LDO6, + AS3722_REG_ID_LDO7, + AS3722_REG_ID_LDO9, + AS3722_REG_ID_LDO10, + AS3722_REG_ID_LDO11, +}; + +struct regulator_range { + u_int min_uvolt; + u_int step_uvolt; + u_int min_sel; + u_int max_sel; +}; + + +/* Regulator HW definition. */ +struct reg_def { + intptr_t id; /* ID */ + char *name; /* Regulator name */ + char *supply_name; /* Source property name */ + uint8_t volt_reg; + uint8_t volt_vsel_mask; + uint8_t enable_reg; + uint8_t enable_mask; + uint8_t ext_enable_reg; + uint8_t ext_enable_mask; + struct regulator_range *ranges; + int nranges; +}; + +struct as3722_reg_sc { + struct regnode *regnode; + struct as3722_softc *base_sc; + struct reg_def *def; + phandle_t xref; + + struct regnode_std_param *param; + int ext_control; + int enable_tracking; + + int enable_usec; +}; + +#define RANGE_INIT(_min_sel, _max_sel, _min_uvolt, _step_uvolt) \ +{ \ + .min_sel = _min_sel, \ + .max_sel = _max_sel, \ + .min_uvolt = _min_uvolt, \ + .step_uvolt = _step_uvolt, \ +} + +static struct regulator_range as3722_sd016_ranges[] = { + RANGE_INIT(0x00, 0x00, 0, 0), + RANGE_INIT(0x01, 0x5A, 610000, 10000), +}; + +static struct regulator_range as3722_sd0_lv_ranges[] = { + RANGE_INIT(0x00, 0x00, 0, 0), + RANGE_INIT(0x01, 0x6E, 410000, 10000), +}; + +static struct regulator_range as3722_sd_ranges[] = { + RANGE_INIT(0x00, 0x00, 0, 0), + RANGE_INIT(0x01, 0x40, 612500, 12500), + RANGE_INIT(0x41, 0x70, 1425000, 25000), + RANGE_INIT(0x71, 0x7F, 2650000, 50000), +}; + +static struct regulator_range as3722_ldo3_ranges[] = { + RANGE_INIT(0x00, 0x00, 0, 0), + RANGE_INIT(0x01, 0x2D, 620000, 20000), +}; + +static struct regulator_range as3722_ldo_ranges[] = { + RANGE_INIT(0x00, 0x00, 0, 0), + RANGE_INIT(0x01, 0x24, 825000, 25000), + RANGE_INIT(0x40, 0x7F, 1725000, 25000), +}; + +static struct reg_def as3722s_def[] = { + { + .id = AS3722_REG_ID_SD0, + .name = "sd0", + .volt_reg = AS3722_SD0_VOLTAGE, + .volt_vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL, + .enable_mask = AS3722_SDN_CTRL(0), + .ext_enable_reg = AS3722_ENABLE_CTRL1, + .ext_enable_mask = AS3722_SD0_EXT_ENABLE_MASK, + .ranges = as3722_sd016_ranges, + .nranges = nitems(as3722_sd016_ranges), + }, + { + .id = AS3722_REG_ID_SD1, + .name = "sd1", + .volt_reg = AS3722_SD1_VOLTAGE, + .volt_vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL, + .enable_mask = AS3722_SDN_CTRL(1), + .ext_enable_reg = AS3722_ENABLE_CTRL1, + .ext_enable_mask = AS3722_SD1_EXT_ENABLE_MASK, + .ranges = as3722_sd_ranges, + .nranges = nitems(as3722_sd_ranges), + }, + { + .id = AS3722_REG_ID_SD2, + .name = "sd2", + .supply_name = "vsup-sd2", + .volt_reg = AS3722_SD2_VOLTAGE, + .volt_vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL, + .enable_mask = AS3722_SDN_CTRL(2), + .ext_enable_reg = AS3722_ENABLE_CTRL1, + .ext_enable_mask = AS3722_SD2_EXT_ENABLE_MASK, + .ranges = as3722_sd_ranges, + .nranges = nitems(as3722_sd_ranges), + }, + { + .id = AS3722_REG_ID_SD3, + .name = "sd3", + .supply_name = "vsup-sd3", + .volt_reg = AS3722_SD3_VOLTAGE, + .volt_vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL, + .enable_mask = AS3722_SDN_CTRL(3), + .ext_enable_reg = AS3722_ENABLE_CTRL1, + .ext_enable_mask = AS3722_SD3_EXT_ENABLE_MASK, + .ranges = as3722_sd_ranges, + .nranges = nitems(as3722_sd_ranges), + }, + { + .id = AS3722_REG_ID_SD4, + .name = "sd4", + .supply_name = "vsup-sd4", + .volt_reg = AS3722_SD4_VOLTAGE, + .volt_vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL, + .enable_mask = AS3722_SDN_CTRL(4), + .ext_enable_reg = AS3722_ENABLE_CTRL2, + .ext_enable_mask = AS3722_SD4_EXT_ENABLE_MASK, + .ranges = as3722_sd_ranges, + .nranges = nitems(as3722_sd_ranges), + }, + { + .id = AS3722_REG_ID_SD5, + .name = "sd5", + .supply_name = "vsup-sd5", + .volt_reg = AS3722_SD5_VOLTAGE, + .volt_vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL, + .enable_mask = AS3722_SDN_CTRL(5), + .ext_enable_reg = AS3722_ENABLE_CTRL2, + .ext_enable_mask = AS3722_SD5_EXT_ENABLE_MASK, + .ranges = as3722_sd_ranges, + .nranges = nitems(as3722_sd_ranges), + }, + { + .id = AS3722_REG_ID_SD6, + .name = "sd6", + .volt_reg = AS3722_SD6_VOLTAGE, + .volt_vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL, + .enable_mask = AS3722_SDN_CTRL(6), + .ext_enable_reg = AS3722_ENABLE_CTRL2, + .ext_enable_mask = AS3722_SD6_EXT_ENABLE_MASK, + .ranges = as3722_sd016_ranges, + .nranges = nitems(as3722_sd016_ranges), + }, + { + .id = AS3722_REG_ID_LDO0, + .name = "ldo0", + .supply_name = "vin-ldo0", + .volt_reg = AS3722_LDO0_VOLTAGE, + .volt_vsel_mask = AS3722_LDO0_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL0, + .enable_mask = AS3722_LDO0_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL3, + .ext_enable_mask = AS3722_LDO0_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO1, + .name = "ldo1", + .supply_name = "vin-ldo1-6", + .volt_reg = AS3722_LDO1_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL0, + .enable_mask = AS3722_LDO1_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL3, + .ext_enable_mask = AS3722_LDO1_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO2, + .name = "ldo2", + .supply_name = "vin-ldo2-5-7", + .volt_reg = AS3722_LDO2_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL0, + .enable_mask = AS3722_LDO2_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL3, + .ext_enable_mask = AS3722_LDO2_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO3, + .name = "ldo3", + .supply_name = "vin-ldo3-4", + .volt_reg = AS3722_LDO3_VOLTAGE, + .volt_vsel_mask = AS3722_LDO3_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL0, + .enable_mask = AS3722_LDO3_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL3, + .ext_enable_mask = AS3722_LDO3_EXT_ENABLE_MASK, + .ranges = as3722_ldo3_ranges, + .nranges = nitems(as3722_ldo3_ranges), + }, + { + .id = AS3722_REG_ID_LDO4, + .name = "ldo4", + .supply_name = "vin-ldo3-4", + .volt_reg = AS3722_LDO4_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL0, + .enable_mask = AS3722_LDO4_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL4, + .ext_enable_mask = AS3722_LDO4_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO5, + .name = "ldo5", + .supply_name = "vin-ldo2-5-7", + .volt_reg = AS3722_LDO5_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL0, + .enable_mask = AS3722_LDO5_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL4, + .ext_enable_mask = AS3722_LDO5_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO6, + .name = "ldo6", + .supply_name = "vin-ldo1-6", + .volt_reg = AS3722_LDO6_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL0, + .enable_mask = AS3722_LDO6_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL4, + .ext_enable_mask = AS3722_LDO6_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO7, + .name = "ldo7", + .supply_name = "vin-ldo2-5-7", + .volt_reg = AS3722_LDO7_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL0, + .enable_mask = AS3722_LDO7_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL4, + .ext_enable_mask = AS3722_LDO7_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO9, + .name = "ldo9", + .supply_name = "vin-ldo9-10", + .volt_reg = AS3722_LDO9_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL1, + .enable_mask = AS3722_LDO9_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL5, + .ext_enable_mask = AS3722_LDO9_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO10, + .name = "ldo10", + .supply_name = "vin-ldo9-10", + .volt_reg = AS3722_LDO10_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL1, + .enable_mask = AS3722_LDO10_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL5, + .ext_enable_mask = AS3722_LDO10_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO11, + .name = "ldo11", + .supply_name = "vin-ldo11", + .volt_reg = AS3722_LDO11_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL1, + .enable_mask = AS3722_LDO11_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL5, + .ext_enable_mask = AS3722_LDO11_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, +}; + + +struct as3722_regnode_init_def { + struct regnode_init_def reg_init_def; + int ext_control; + int enable_tracking; +}; + +static int as3722_regnode_init(struct regnode *regnode); +static int as3722_regnode_enable(struct regnode *regnode, bool enable, + int *udelay); +static int as3722_regnode_set_volt(struct regnode *regnode, int min_uvolt, + int max_uvolt, int *udelay); +static int as3722_regnode_get_volt(struct regnode *regnode, int *uvolt); +static regnode_method_t as3722_regnode_methods[] = { + /* Regulator interface */ + REGNODEMETHOD(regnode_init, as3722_regnode_init), + REGNODEMETHOD(regnode_enable, as3722_regnode_enable), + REGNODEMETHOD(regnode_set_voltage, as3722_regnode_set_volt), + REGNODEMETHOD(regnode_get_voltage, as3722_regnode_get_volt), + REGNODEMETHOD_END +}; +DEFINE_CLASS_1(as3722_regnode, as3722_regnode_class, as3722_regnode_methods, + sizeof(struct as3722_reg_sc), regnode_class); + +static int +regulator_range_sel_to_volt(struct as3722_reg_sc *sc, uint8_t sel, int *volt) +{ + struct regulator_range *range; + struct reg_def *def; + int i; + + def = sc->def; + if (def->nranges == 0) + panic("Voltage regulator have zero ranges\n"); + + for (i = 0; i < def->nranges ; i++) { + range = def->ranges + i; + + if (!(sel >= range->min_sel && + sel <= range->max_sel)) + continue; + + sel -= range->min_sel; + + *volt = range->min_uvolt + sel * range->step_uvolt; + return (0); + } + + return (ERANGE); +} + +static int +regulator_range_volt_to_sel(struct as3722_reg_sc *sc, int min_uvolt, + int max_uvolt, uint8_t *out_sel) +{ + struct regulator_range *range; + struct reg_def *def; + uint8_t sel; + int uvolt; + int rv, i; + + def = sc->def; + if (def->nranges == 0) + panic("Voltage regulator have zero ranges\n"); + + for (i = 0; i < def->nranges; i++) { + range = def->ranges + i; + uvolt = range->min_uvolt + + (range->max_sel - range->min_sel) * range->step_uvolt; + + if ((min_uvolt > uvolt) || + (max_uvolt < range->min_uvolt)) + continue; + + if (min_uvolt <= range->min_uvolt) + min_uvolt = range->min_uvolt; + + /* If step is zero then range is fixed voltage range. */ + if (range->step_uvolt == 0) + sel = 0; + else + sel = DIV_ROUND_UP(min_uvolt - range->min_uvolt, + range->step_uvolt); + + + sel += range->min_sel; + + break; + } + + if (i >= def->nranges) + return (ERANGE); + + /* Verify new settings. */ + rv = regulator_range_sel_to_volt(sc, sel, &uvolt); + if (rv != 0) + return (rv); + if ((uvolt < min_uvolt) || (uvolt > max_uvolt)) + return (ERANGE); + + *out_sel = sel; + return (0); +} + + +static int +as3722_read_sel(struct as3722_reg_sc *sc, uint8_t *sel) +{ + int rv; + + rv = RD1(sc->base_sc, sc->def->volt_reg, sel); + if (rv != 0) + return (rv); + *sel &= sc->def->volt_vsel_mask; + *sel >>= ffs(sc->def->volt_vsel_mask) - 1; + return (0); +} + +static int +as3722_write_sel(struct as3722_reg_sc *sc, uint8_t sel) +{ + int rv; + + sel <<= ffs(sc->def->volt_vsel_mask) - 1; + sel &= sc->def->volt_vsel_mask; + + rv = RM1(sc->base_sc, sc->def->volt_reg, + sc->def->volt_vsel_mask, sel); + if (rv != 0) + return (rv); + return (rv); +} + +static bool +as3722_sd0_is_low_voltage(struct as3722_reg_sc *sc) +{ + uint8_t val; + int rv; + + rv = RD1(sc->base_sc, AS3722_FUSE7, &val); + if (rv != 0) + return (rv); + return (val & AS3722_FUSE7_SD0_LOW_VOLTAGE ? true : false); +} + +static int +as3722_reg_extreg_setup(struct as3722_reg_sc *sc, int ext_pwr_ctrl) +{ + uint8_t val; + int rv; + + val = ext_pwr_ctrl << (ffs(sc->def->ext_enable_mask) - 1); + rv = RM1(sc->base_sc, sc->def->ext_enable_reg, + sc->def->ext_enable_mask, val); + return (rv); +} + +static int +as3722_reg_enable(struct as3722_reg_sc *sc) +{ + int rv; + + rv = RM1(sc->base_sc, sc->def->enable_reg, + sc->def->enable_mask, sc->def->enable_mask); + return (rv); +} + +static int +as3722_reg_disable(struct as3722_reg_sc *sc) +{ + int rv; + + rv = RM1(sc->base_sc, sc->def->enable_reg, + sc->def->enable_mask, 0); + return (rv); +} + +static int +as3722_regnode_init(struct regnode *regnode) +{ + struct as3722_reg_sc *sc; + int rv; + + sc = regnode_get_softc(regnode); + + sc->enable_usec = 500; + if (sc->def->id == AS3722_REG_ID_SD0) { + if (as3722_sd0_is_low_voltage(sc)) { + sc->def->ranges = as3722_sd0_lv_ranges; + sc->def->nranges = nitems(as3722_sd0_lv_ranges); + } + sc->enable_usec = 600; + } else if (sc->def->id == AS3722_REG_ID_LDO3) { + if (sc->enable_tracking) { + rv = RM1(sc->base_sc, sc->def->volt_reg, + AS3722_LDO3_MODE_MASK, + AS3722_LDO3_MODE_PMOS_TRACKING); + if (rv < 0) { + device_printf(sc->base_sc->dev, + "LDO3 tracking failed: %d\n", rv); + return (rv); + } + } + } + + if (sc->ext_control) { + + rv = as3722_reg_enable(sc); + if (rv < 0) { + device_printf(sc->base_sc->dev, + "Failed to enable %s regulator: %d\n", + sc->def->name, rv); + return (rv); + } + rv = as3722_reg_extreg_setup(sc, sc->ext_control); + if (rv < 0) { + device_printf(sc->base_sc->dev, + "%s ext control failed: %d", sc->def->name, rv); + return (rv); + } + } + return (0); +} + +static void +as3722_fdt_parse(struct as3722_softc *sc, phandle_t node, struct reg_def *def, +struct as3722_regnode_init_def *init_def) +{ + int rv; + phandle_t parent, supply_node; + char prop_name[64]; /* Maximum OFW property name length. */ + + rv = regulator_parse_ofw_stdparam(sc->dev, node, + &init_def->reg_init_def); + + rv = OF_getencprop(node, "ams,ext-control", &init_def->ext_control, + sizeof(init_def->ext_control)); + if (rv <= 0) + init_def->ext_control = 0; + if (init_def->ext_control > 3) { + device_printf(sc->dev, + "Invalid value for ams,ext-control property: %d\n", + init_def->ext_control); + init_def->ext_control = 0; + } + if (OF_hasprop(node, "ams,enable-tracking")) + init_def->enable_tracking = 1; + + + /* Get parent supply. */ + if (def->supply_name == NULL) + return; + + parent = OF_parent(node); + snprintf(prop_name, sizeof(prop_name), "%s-supply", + def->supply_name); + rv = OF_getencprop(parent, prop_name, &supply_node, + sizeof(supply_node)); + if (rv <= 0) + return; + supply_node = OF_node_from_xref(supply_node); + rv = OF_getprop_alloc(supply_node, "regulator-name", 1, + (void **)&init_def->reg_init_def.parent_name); + if (rv <= 0) + init_def->reg_init_def.parent_name = NULL; +} + +static struct as3722_reg_sc * +as3722_attach(struct as3722_softc *sc, phandle_t node, struct reg_def *def) +{ + struct as3722_reg_sc *reg_sc; + struct as3722_regnode_init_def init_def; + struct regnode *regnode; + + bzero(&init_def, sizeof(init_def)); + + as3722_fdt_parse(sc, node, def, &init_def); + init_def.reg_init_def.id = def->id; + init_def.reg_init_def.ofw_node = node; + regnode = regnode_create(sc->dev, &as3722_regnode_class, + &init_def.reg_init_def); + if (regnode == NULL) { + device_printf(sc->dev, "Cannot create regulator.\n"); + return (NULL); + } + reg_sc = regnode_get_softc(regnode); + + /* Init regulator softc. */ + reg_sc->regnode = regnode; + reg_sc->base_sc = sc; + reg_sc->def = def; + reg_sc->xref = OF_xref_from_node(node); + + reg_sc->param = regnode_get_stdparam(regnode); + reg_sc->ext_control = init_def.ext_control; + reg_sc->enable_tracking = init_def.enable_tracking; + + regnode_register(regnode); + if (bootverbose) { + int volt, rv; + regnode_topo_slock(); + rv = regnode_get_voltage(regnode, &volt); + if (rv == ENODEV) { + device_printf(sc->dev, + " Regulator %s: parent doesn't exist yet.\n", + regnode_get_name(regnode)); + } else if (rv != 0) { + device_printf(sc->dev, + " Regulator %s: voltage: INVALID!!!\n", + regnode_get_name(regnode)); + } else { + device_printf(sc->dev, + " Regulator %s: voltage: %d uV\n", + regnode_get_name(regnode), volt); + } + regnode_topo_unlock(); + } + + return (reg_sc); +} + +int +as3722_regulator_attach(struct as3722_softc *sc, phandle_t node) +{ + struct as3722_reg_sc *reg; + phandle_t child, rnode; + int i; + + rnode = ofw_bus_find_child(node, "regulators"); + if (rnode <= 0) { + device_printf(sc->dev, " Cannot find regulators subnode\n"); + return (ENXIO); + } + + sc->nregs = nitems(as3722s_def); + sc->regs = malloc(sizeof(struct as3722_reg_sc *) * sc->nregs, + M_AS3722_REG, M_WAITOK | M_ZERO); + + + /* Attach all known regulators if exist in DT. */ + for (i = 0; i < sc->nregs; i++) { + child = ofw_bus_find_child(rnode, as3722s_def[i].name); + if (child == 0) { + if (bootverbose) + device_printf(sc->dev, + "Regulator %s missing in DT\n", + as3722s_def[i].name); + continue; + } + reg = as3722_attach(sc, child, as3722s_def + i); + if (reg == NULL) { + device_printf(sc->dev, "Cannot attach regulator: %s\n", + as3722s_def[i].name); + return (ENXIO); + } + sc->regs[i] = reg; + } + return (0); +} + +int +as3722_regulator_map(device_t dev, phandle_t xref, int ncells, + pcell_t *cells, int *num) +{ + struct as3722_softc *sc; + int i; + + sc = device_get_softc(dev); + for (i = 0; i < sc->nregs; i++) { + if (sc->regs[i] == NULL) + continue; + if (sc->regs[i]->xref == xref) { + *num = sc->regs[i]->def->id; + return (0); + } + } + return (ENXIO); +} + +static int +as3722_regnode_enable(struct regnode *regnode, bool val, int *udelay) +{ + struct as3722_reg_sc *sc; + int rv; + + sc = regnode_get_softc(regnode); + + if (val) + rv = as3722_reg_enable(sc); + else + rv = as3722_reg_disable(sc); + *udelay = sc->enable_usec; + return (rv); +} + +static int +as3722_regnode_set_volt(struct regnode *regnode, int min_uvolt, int max_uvolt, + int *udelay) +{ + struct as3722_reg_sc *sc; + uint8_t sel; + int rv; + + sc = regnode_get_softc(regnode); + + *udelay = 0; + rv = regulator_range_volt_to_sel(sc, min_uvolt, max_uvolt, &sel); + if (rv != 0) + return (rv); + rv = as3722_write_sel(sc, sel); + return (rv); + +} + +static int +as3722_regnode_get_volt(struct regnode *regnode, int *uvolt) +{ + struct as3722_reg_sc *sc; + uint8_t sel; + int rv; + + sc = regnode_get_softc(regnode); + rv = as3722_read_sel(sc, &sel); + if (rv != 0) + return (rv); + + /* LDO6 have bypass. */ + if (sc->def->id == AS3722_REG_ID_LDO6 && sel == AS3722_LDO6_SEL_BYPASS) + return (ENOENT); + rv = regulator_range_sel_to_volt(sc, sel, uvolt); + return (rv); +} diff --git a/sys/arm/nvidia/as3722_rtc.c b/sys/arm/nvidia/as3722_rtc.c new file mode 100644 index 0000000..8f13104 --- /dev/null +++ b/sys/arm/nvidia/as3722_rtc.c @@ -0,0 +1,115 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/clock.h> +#include <sys/kernel.h> + +#include <dev/ofw/ofw_bus.h> + +#include "clock_if.h" +#include "as3722.h" + +#define AS3722_RTC_START_YEAR 2000 + +int +as3722_rtc_gettime(device_t dev, struct timespec *ts) +{ + struct as3722_softc *sc; + struct clocktime ct; + uint8_t buf[6]; + int rv; + + sc = device_get_softc(dev); + + rv = as3722_read_buf(sc, AS3722_RTC_SECOND, buf, 6); + if (rv != 0) { + device_printf(sc->dev, "Failed to read RTC data\n"); + return (rv); + } + ct.nsec = 0; + ct.sec = bcd2bin(buf[0] & 0x7F); + ct.min = bcd2bin(buf[1] & 0x7F); + ct.hour = bcd2bin(buf[2] & 0x3F); + ct.day = bcd2bin(buf[3] & 0x3F); + ct.mon = bcd2bin(buf[4] & 0x1F); + ct.year = bcd2bin(buf[5] & 0x7F) + AS3722_RTC_START_YEAR; + ct.dow = -1; + + return clock_ct_to_ts(&ct, ts); +} + +int +as3722_rtc_settime(device_t dev, struct timespec *ts) +{ + struct as3722_softc *sc; + struct clocktime ct; + uint8_t buf[6]; + int rv; + + sc = device_get_softc(dev); + clock_ts_to_ct(ts, &ct); + + if (ct.year < AS3722_RTC_START_YEAR) + return (EINVAL); + + buf[0] = bin2bcd(ct.sec); + buf[1] = bin2bcd(ct.min); + buf[2] = bin2bcd(ct.hour); + buf[3] = bin2bcd(ct.day); + buf[4] = bin2bcd(ct.mon); + buf[5] = bin2bcd(ct.year - AS3722_RTC_START_YEAR); + + rv = as3722_write_buf(sc, AS3722_RTC_SECOND, buf, 6); + if (rv != 0) { + device_printf(sc->dev, "Failed to write RTC data\n"); + return (rv); + } + return (0); +} + +int +as3722_rtc_attach(struct as3722_softc *sc, phandle_t node) +{ + int rv; + + /* Enable RTC, set 24 hours mode and alarms */ + rv = RM1(sc, AS3722_RTC_CONTROL, + AS3722_RTC_ON | AS3722_RTC_ALARM_WAKEUP_EN | AS3722_RTC_AM_PM_MODE, + AS3722_RTC_ON | AS3722_RTC_ALARM_WAKEUP_EN); + if (rv < 0) { + device_printf(sc->dev, "Failed to initialize RTC controller\n"); + return (ENXIO); + } + clock_register(sc->dev, 1000000); + + return (0); +} diff --git a/sys/arm/nvidia/tegra124/files.tegra124 b/sys/arm/nvidia/tegra124/files.tegra124 new file mode 100644 index 0000000..d4d38f7 --- /dev/null +++ b/sys/arm/nvidia/tegra124/files.tegra124 @@ -0,0 +1,57 @@ +# $FreeBSD$ + +# +# Standard ARM support. +# +kern/kern_clocksource.c standard +dev/ofw/ofw_cpu.c optional fdt + +# +# Standard tegra124 devices and support. +# +arm/nvidia/tegra124/tegra124_machdep.c standard +arm/nvidia/tegra124/tegra124_mp.c optional smp +arm/nvidia/tegra124/tegra124_car.c standard +arm/nvidia/tegra124/tegra124_clk_pll.c standard +arm/nvidia/tegra124/tegra124_clk_per.c standard +arm/nvidia/tegra124/tegra124_clk_super.c standard +arm/nvidia/tegra124/tegra124_xusbpadctl.c standard +arm/nvidia/tegra124/tegra124_pmc.c standard +arm/nvidia/tegra124/tegra124_cpufreq.c standard +arm/nvidia/tegra124/tegra124_coretemp.c standard +arm/nvidia/tegra_usbphy.c standard +arm/nvidia/tegra_pinmux.c standard +arm/nvidia/tegra_uart.c optional uart +arm/nvidia/tegra_sdhci.c optional sdhci +arm/nvidia/tegra_gpio.c optional gpio +arm/nvidia/tegra_ehci.c optional ehci +arm/nvidia/tegra_ahci.c optional ahci +arm/nvidia/tegra_pcie.c optional pci +arm/nvidia/tegra_i2c.c optional iic +arm/nvidia/tegra_rtc.c standard +arm/nvidia/tegra_abpmisc.c standard +arm/nvidia/tegra_efuse.c standard +arm/nvidia/tegra_soctherm_if.m standard +arm/nvidia/tegra_soctherm.c standard +arm/nvidia/tegra_lic.c standard +#arm/nvidia/tegra_hda.c optional snd_hda +#arm/nvidia/drm2/hdmi.c optional drm2 +#arm/nvidia/drm2/tegra_drm_if.m optional drm2 +#arm/nvidia/drm2/tegra_drm_subr.c optional drm2 +#arm/nvidia/drm2/tegra_host1x.c optional drm2 +#arm/nvidia/drm2/tegra_hdmi.c optional drm2 +#arm/nvidia/drm2/tegra_dc_if.m optional drm2 +#arm/nvidia/drm2/tegra_dc.c optional drm2 +#arm/nvidia/drm2/tegra_fb.c optional drm2 +#arm/nvidia/drm2/tegra_bo.c optional drm2 +# +# Optional devices. +# + +# +# Temporary/ to be moved stuff +# +arm/nvidia/as3722.c optional iic +arm/nvidia/as3722_regulators.c optional iic +arm/nvidia/as3722_rtc.c optional iic +arm/nvidia/as3722_gpio.c optional iic diff --git a/sys/arm/nvidia/tegra124/std.tegra124 b/sys/arm/nvidia/tegra124/std.tegra124 new file mode 100644 index 0000000..127b001 --- /dev/null +++ b/sys/arm/nvidia/tegra124/std.tegra124 @@ -0,0 +1,14 @@ +# $FreeBSD$ +cpu CPU_CORTEXA +machine arm armv6 +makeoptions CONF_CFLAGS="-march=armv7a" + +options KERNVIRTADDR = 0xc0200000 +makeoptions KERNVIRTADDR = 0xc0200000 + +options ARM_INTRNG + +options IPI_IRQ_START=0 +options IPI_IRQ_END=15 + +files "../nvidia/tegra124/files.tegra124" diff --git a/sys/arm/nvidia/tegra124/tegra124_car.c b/sys/arm/nvidia/tegra124/tegra124_car.c new file mode 100644 index 0000000..c0e93c7 --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_car.c @@ -0,0 +1,613 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/kobj.h> +#include <sys/module.h> +#include <sys/malloc.h> +#include <sys/rman.h> +#include <sys/lock.h> +#include <sys/mutex.h> + +#include <machine/bus.h> +#include <machine/cpu.h> + +#include <dev/extres/clk/clk_div.h> +#include <dev/extres/clk/clk_fixed.h> +#include <dev/extres/clk/clk_gate.h> +#include <dev/extres/clk/clk_mux.h> +#include <dev/extres/hwreset/hwreset.h> +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <gnu/dts/include/dt-bindings/clock/tegra124-car.h> + +#include "clkdev_if.h" +#include "hwreset_if.h" +#include "tegra124_car.h" + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-car", 1}, + {NULL, 0}, +}; + +#define PLIST(x) static const char *x[] + +/* Pure multiplexer. */ +#define MUX(_id, cname, plists, o, s, w) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = plists, \ + .clkdef.parent_cnt = nitems(plists), \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = o, \ + .shift = s, \ + .width = w, \ +} + +/* Fractional divider (7.1). */ +#define DIV7_1(_id, cname, plist, o, s) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = (const char *[]){plist}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = o, \ + .i_shift = (s) + 1, \ + .i_width = 7, \ + .f_shift = s, \ + .f_width = 1, \ +} + +/* Integer divider. */ +#define DIV(_id, cname, plist, o, s, w, f) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = (const char *[]){plist}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = o, \ + .i_shift = s, \ + .i_width = w, \ + .div_flags = f, \ +} + +/* Gate in PLL block. */ +#define GATE_PLL(_id, cname, plist, o, s) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = (const char *[]){plist}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = o, \ + .shift = s, \ + .mask = 3, \ + .on_value = 3, \ + .off_value = 0, \ +} + +/* Standard gate. */ +#define GATE(_id, cname, plist, o, s) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = (const char *[]){plist}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = o, \ + .shift = s, \ + .mask = 1, \ + .on_value = 1, \ + .off_value = 0, \ +} + +/* Inverted gate. */ +#define GATE_INV(_id, cname, plist, o, s) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = (const char *[]){plist}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = o, \ + .shift = s, \ + .mask = 1, \ + .on_value = 0, \ + .off_value = 1, \ +} + +/* Fixed rate clock. */ +#define FRATE(_id, cname, _freq) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = NULL, \ + .clkdef.parent_cnt = 0, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .freq = _freq, \ +} + +/* Fixed rate multipier/divider. */ +#define FACT(_id, cname, pname, _mult, _div) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = (const char *[]){pname}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .mult = _mult, \ + .div = _div, \ +} + +static uint32_t osc_freqs[16] = { + [0] = 13000000, + [1] = 16800000, + [4] = 19200000, + [5] = 38400000, + [8] = 12000000, + [9] = 48000000, + [12] = 260000000, +}; + + +/* Parent lists. */ +PLIST(mux_pll_srcs) = {"osc_div_clk", NULL, "pllP_out0", NULL}; /* FIXME */ +PLIST(mux_plle_src1) = {"osc_div_clk", "pllP_out0"}; +PLIST(mux_plle_src) = {"pllE_src1", "pllREFE_out"}; +PLIST(mux_plld_out0_plld2_out0) = {"pllD_out0", "pllD2_out0"}; +PLIST(mux_pllmcp_clkm) = {"pllM_out0", "pllC_out0", "pllP_out0", "clk_m", + "pllM_UD", "pllC2_out0", "pllC3_out0", "pllC_UD"}; +PLIST(mux_xusb_hs) = {"pc_xusb_ss", "pllU_60"}; +PLIST(mux_xusb_ss) = {"pc_xusb_ss", "osc_div_clk"}; + + +/* Clocks ajusted online. */ +static struct clk_fixed_def fixed_clk_m = + FRATE(0, "clk_m", 12000000); +static struct clk_fixed_def fixed_osc_div_clk = + FACT(0, "osc_div_clk", "clk_m", 1, 1); + +static struct clk_fixed_def tegra124_fixed_clks[] = { + /* Core clocks. */ + FRATE(0, "clk_s", 32768), + FACT(0, "clk_m_div2", "clk_m", 1, 2), + FACT(0, "clk_m_div4", "clk_m", 1, 3), + FACT(0, "pllU_60", "pllU_out", 1, 8), + FACT(0, "pllU_48", "pllU_out", 1, 10), + FACT(0, "pllU_12", "pllU_out", 1, 40), + FACT(TEGRA124_CLK_PLL_D_OUT0, "pllD_out0", "pllD_out", 1, 2), + FACT(TEGRA124_CLK_PLL_D2_OUT0, "pllD2_out0", "pllD2_out", 1, 1), + FACT(0, "pllX_out0", "pllX_out", 1, 2), + FACT(0, "pllC_UD", "pllC_out0", 1, 1), + FACT(0, "pllM_UD", "pllM_out0", 1, 1), + + /* Audio clocks. */ + FRATE(0, "audio0", 10000000), + FRATE(0, "audio1", 10000000), + FRATE(0, "audio2", 10000000), + FRATE(0, "audio3", 10000000), + FRATE(0, "audio4", 10000000), + FRATE(0, "ext_vimclk", 10000000), +}; + + +static struct clk_mux_def tegra124_mux_clks[] = { + /* Core clocks. */ + MUX(0, "pllD2_src", mux_pll_srcs, PLLD2_BASE, 25, 2), + MUX(0, "pllDP_src", mux_pll_srcs, PLLDP_BASE, 25, 2), + MUX(0, "pllC4_src", mux_pll_srcs, PLLC4_BASE, 25, 2), + MUX(0, "pllE_src1", mux_plle_src1, PLLE_AUX, 2, 1), + MUX(0, "pllE_src", mux_plle_src, PLLE_AUX, 28, 1), + + /* Base peripheral clocks. */ + MUX(0, "dsia_mux", mux_plld_out0_plld2_out0, PLLD_BASE, 25, 1), + MUX(0, "dsib_mux", mux_plld_out0_plld2_out0, PLLD2_BASE, 25, 1), + MUX(0, "emc_mux", mux_pllmcp_clkm, CLK_SOURCE_EMC, 29, 3), + + /* USB. */ + MUX(0, "xusb_hs", mux_xusb_hs, CLK_SOURCE_XUSB_SS, 25, 1), + MUX(0, "xusb_ss_mux", mux_xusb_ss, CLK_SOURCE_XUSB_SS, 24, 1), + +}; + + +static struct clk_gate_def tegra124_gate_clks[] = { + /* Core clocks. */ + GATE_PLL(0, "pllC_out1", "pllC_out1_div", PLLC_OUT, 0), + GATE_PLL(0, "pllM_out1", "pllM_out1_div", PLLM_OUT, 0), + GATE_PLL(0, "pllU_480", "pllU_out", PLLU_BASE, 22), + GATE_PLL(0, "pllP_outX0", "pllP_outX0_div", PLLP_RESHIFT, 0), + GATE_PLL(0, "pllP_out1", "pllP_out1_div", PLLP_OUTA, 0), + GATE_PLL(0, "pllP_out2", "pllP_out2_div", PLLP_OUTA, 16), + GATE_PLL(0, "pllP_out3", "pllP_out3_div", PLLP_OUTB, 0), + GATE_PLL(0, "pllP_out4", "pllP_out4_div", PLLP_OUTB, 16), + GATE_PLL(0, "pllP_out5", "pllP_out5_div", PLLP_OUTC, 16), + GATE_PLL(0, "pllA_out0", "pllA_out1_div", PLLA_OUT, 0), + + /* Base peripheral clocks. */ + GATE(TEGRA124_CLK_CML0, "cml0", "pllE_out0", PLLE_AUX, 0), + GATE(TEGRA124_CLK_CML1, "cml1", "pllE_out0", PLLE_AUX, 1), + GATE_INV(TEGRA124_CLK_HCLK, "hclk", "hclk_div", CLK_SYSTEM_RATE, 7), + GATE_INV(TEGRA124_CLK_PCLK, "pclk", "pclk_div", CLK_SYSTEM_RATE, 3), +}; + +static struct clk_div_def tegra124_div_clks[] = { + /* Core clocks. */ + DIV7_1(0, "pllC_out1_div", "pllC_out0", PLLC_OUT, 2), + DIV7_1(0, "pllM_out1_div", "pllM_out0", PLLM_OUT, 8), + DIV7_1(0, "pllP_outX0_div", "pllP_out0", PLLP_RESHIFT, 2), + DIV7_1(0, "pllP_out1_div", "pllP_out0", PLLP_OUTA, 8), + DIV7_1(0, "pllP_out2_div", "pllP_out0", PLLP_OUTA, 24), + DIV7_1(0, "pllP_out3_div", "pllP_out0", PLLP_OUTB, 8), + DIV7_1(0, "pllP_out4_div", "pllP_out0", PLLP_OUTB, 24), + DIV7_1(0, "pllP_out5_div", "pllP_out0", PLLP_OUTC, 24), + DIV7_1(0, "pllA_out1_div", "pllA_out", PLLA_OUT, 8), + + /* Base peripheral clocks. */ + DIV(0, "hclk_div", "sclk", CLK_SYSTEM_RATE, 4, 2, 0), + DIV(0, "pclk_div", "hclk", CLK_SYSTEM_RATE, 0, 2, 0), +}; + +/* Initial setup table. */ +static struct tegra124_init_item clk_init_table[] = { + /* clock, partent, frequency, enable */ + {"uarta", "pllP_out0", 408000000, 0}, + {"uartb", "pllP_out0", 408000000, 0}, + {"uartc", "pllP_out0", 408000000, 0}, + {"uartd", "pllP_out0", 408000000, 0}, + {"pllA_out", NULL, 282240000, 1}, + {"pllA_out0", NULL, 11289600, 1}, + {"extperiph1", "pllA_out0", 0, 1}, + {"i2s0", "pllA_out0", 11289600, 0}, + {"i2s1", "pllA_out0", 11289600, 0}, + {"i2s2", "pllA_out0", 11289600, 0}, + {"i2s3", "pllA_out0", 11289600, 0}, + {"i2s4", "pllA_out0", 11289600, 0}, + {"vde", "pllP_out0", 0, 0}, + {"host1x", "pllP_out0", 136000000, 1}, + {"sclk", "pllP_out2", 102000000, 1}, + {"dvfs_soc", "pllP_out0", 51000000, 1}, + {"dvfs_ref", "pllP_out0", 51000000, 1}, + {"pllC_out0", NULL, 600000000, 0}, + {"pllC_out1", NULL, 100000000, 0}, + {"spi4", "pllP_out0", 12000000, 1}, + {"tsec", "pllC3_out0", 0, 0}, + {"msenc", "pllC3_out0", 0, 0}, + {"pllREFE_out", NULL, 672000000, 0}, + {"pc_xusb_ss", "pllU_480", 120000000, 0}, + {"xusb_ss", "pc_xusb_ss", 120000000, 0}, + {"pc_xusb_fs", "pllU_48", 48000000, 0}, + {"xusb_hs", "pllU_60", 60000000, 0}, + {"pc_xusb_falcon", "pllREFE_out", 224000000, 0}, + {"xusb_core_host", "pllREFE_out", 112000000, 0}, + {"sata", "pllP_out0", 102000000, 0}, + {"sata_oob", "pllP_out0", 204000000, 0}, + {"sata_cold", NULL, 0, 1}, + {"emc", NULL, 0, 1}, + {"mselect", NULL, 0, 1}, + {"csite", NULL, 0, 1}, + {"tsensor", "clk_m", 400000, 0}, + + /* tegra124 only*/ + {"soc_therm", "pllP_out0", 51000000, 0}, + {"cclk_g", NULL, 0, 1}, + {"hda", "pllP_out0", 102000000, 0}, + {"hda2codec_2x", "pllP_out0", 48000000, 0}, +}; + +static void +init_divs(struct tegra124_car_softc *sc, struct clk_div_def *clks, int nclks) +{ + int i, rv; + + for (i = 0; i < nclks; i++) { + rv = clknode_div_register(sc->clkdom, clks + i); + if (rv != 0) + panic("clk_div_register failed"); + } +} + +static void +init_gates(struct tegra124_car_softc *sc, struct clk_gate_def *clks, int nclks) +{ + int i, rv; + + + for (i = 0; i < nclks; i++) { + rv = clknode_gate_register(sc->clkdom, clks + i); + if (rv != 0) + panic("clk_gate_register failed"); + } +} + +static void +init_muxes(struct tegra124_car_softc *sc, struct clk_mux_def *clks, int nclks) +{ + int i, rv; + + + for (i = 0; i < nclks; i++) { + rv = clknode_mux_register(sc->clkdom, clks + i); + if (rv != 0) + panic("clk_mux_register failed"); + } +} + +static void +init_fixeds(struct tegra124_car_softc *sc, struct clk_fixed_def *clks, + int nclks) +{ + int i, rv; + uint32_t val; + int osc_idx; + + CLKDEV_READ_4(sc->dev, OSC_CTRL, &val); + osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT; + fixed_clk_m.freq = osc_freqs[osc_idx]; + if (fixed_clk_m.freq == 0) + panic("Undefined input frequency"); + rv = clknode_fixed_register(sc->clkdom, &fixed_clk_m); + if (rv != 0) panic("clk_fixed_register failed"); + + val = (val >> OSC_CTRL_PLL_REF_DIV_SHIFT) & 3; + fixed_osc_div_clk.div = 1 << val; + rv = clknode_fixed_register(sc->clkdom, &fixed_osc_div_clk); + if (rv != 0) panic("clk_fixed_register failed"); + + for (i = 0; i < nclks; i++) { + rv = clknode_fixed_register(sc->clkdom, clks + i); + if (rv != 0) + panic("clk_fixed_register failed"); + } +} + +static void +postinit_clock(struct tegra124_car_softc *sc) +{ + int i; + struct tegra124_init_item *tbl; + struct clknode *clknode; + int rv; + + for (i = 0; i < nitems(clk_init_table); i++) { + tbl = &clk_init_table[i]; + + clknode = clknode_find_by_name(tbl->name); + if (clknode == NULL) { + device_printf(sc->dev, "Cannot find clock %s\n", + tbl->name); + continue; + } + if (tbl->parent != NULL) { + rv = clknode_set_parent_by_name(clknode, tbl->parent); + if (rv != 0) { + device_printf(sc->dev, + "Cannot set parent for %s (to %s): %d\n", + tbl->name, tbl->parent, rv); + continue; + } + } + if (tbl->frequency != 0) { + rv = clknode_set_freq(clknode, tbl->frequency, 0 , 9999); + if (rv != 0) { + device_printf(sc->dev, + "Cannot set frequency for %s: %d\n", + tbl->name, rv); + continue; + } + } + if (tbl->enable!= 0) { + rv = clknode_enable(clknode); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable %s: %d\n", tbl->name, rv); + continue; + } + } + } +} + +static void +register_clocks(device_t dev) +{ + struct tegra124_car_softc *sc; + + sc = device_get_softc(dev); + sc->clkdom = clkdom_create(dev); + if (sc->clkdom == NULL) + panic("clkdom == NULL"); + + tegra124_init_plls(sc); + init_fixeds(sc, tegra124_fixed_clks, nitems(tegra124_fixed_clks)); + init_muxes(sc, tegra124_mux_clks, nitems(tegra124_mux_clks)); + init_divs(sc, tegra124_div_clks, nitems(tegra124_div_clks)); + init_gates(sc, tegra124_gate_clks, nitems(tegra124_gate_clks)); + tegra124_periph_clock(sc); + tegra124_super_mux_clock(sc); + clkdom_finit(sc->clkdom); + clkdom_xlock(sc->clkdom); + postinit_clock(sc); + clkdom_unlock(sc->clkdom); + if (bootverbose) + clkdom_dump(sc->clkdom); +} + +static int +tegra124_car_clkdev_read_4(device_t dev, bus_addr_t addr, uint32_t *val) +{ + struct tegra124_car_softc *sc; + + sc = device_get_softc(dev); + *val = bus_read_4(sc->mem_res, addr); + return (0); +} + +static int +tegra124_car_clkdev_write_4(device_t dev, bus_addr_t addr, uint32_t val) +{ + struct tegra124_car_softc *sc; + + sc = device_get_softc(dev); + bus_write_4(sc->mem_res, addr, val); + return (0); +} + +static int +tegra124_car_clkdev_modify_4(device_t dev, bus_addr_t addr, uint32_t clear_mask, + uint32_t set_mask) +{ + struct tegra124_car_softc *sc; + uint32_t reg; + + sc = device_get_softc(dev); + reg = bus_read_4(sc->mem_res, addr); + reg &= ~clear_mask; + reg |= set_mask; + bus_write_4(sc->mem_res, addr, reg); + return (0); +} + +static void +tegra124_car_clkdev_device_lock(device_t dev) +{ + struct tegra124_car_softc *sc; + + sc = device_get_softc(dev); + mtx_lock(&sc->mtx); +} + +static void +tegra124_car_clkdev_device_unlock(device_t dev) +{ + struct tegra124_car_softc *sc; + + sc = device_get_softc(dev); + mtx_unlock(&sc->mtx); +} + +static int +tegra124_car_detach(device_t dev) +{ + + device_printf(dev, "Error: Clock driver cannot be detached\n"); + return (EBUSY); +} + +static int +tegra124_car_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { + device_set_desc(dev, "Tegra Clock Driver"); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +tegra124_car_attach(device_t dev) +{ + struct tegra124_car_softc *sc = device_get_softc(dev); + int rid, rv; + + sc->dev = dev; + + mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + /* Resource setup. */ + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (!sc->mem_res) { + device_printf(dev, "cannot allocate memory resource\n"); + rv = ENXIO; + goto fail; + } + + register_clocks(dev); + hwreset_register_ofw_provider(dev); + return (0); + +fail: + if (sc->mem_res) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + return (rv); +} + +static int +tegra124_car_hwreset_assert(device_t dev, intptr_t id, bool value) +{ + struct tegra124_car_softc *sc = device_get_softc(dev); + + return (tegra124_hwreset_by_idx(sc, id, value)); +} + +static device_method_t tegra124_car_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra124_car_probe), + DEVMETHOD(device_attach, tegra124_car_attach), + DEVMETHOD(device_detach, tegra124_car_detach), + + /* Clkdev interface*/ + DEVMETHOD(clkdev_read_4, tegra124_car_clkdev_read_4), + DEVMETHOD(clkdev_write_4, tegra124_car_clkdev_write_4), + DEVMETHOD(clkdev_modify_4, tegra124_car_clkdev_modify_4), + DEVMETHOD(clkdev_device_lock, tegra124_car_clkdev_device_lock), + DEVMETHOD(clkdev_device_unlock, tegra124_car_clkdev_device_unlock), + + /* Reset interface */ + DEVMETHOD(hwreset_assert, tegra124_car_hwreset_assert), + + DEVMETHOD_END +}; + +static devclass_t tegra124_car_devclass; + +static driver_t tegra124_car_driver = { + "tegra124_car", + tegra124_car_methods, + sizeof(struct tegra124_car_softc), +}; + +EARLY_DRIVER_MODULE(tegra124_car, simplebus, tegra124_car_driver, + tegra124_car_devclass, 0, 0, BUS_PASS_TIMER); diff --git a/sys/arm/nvidia/tegra124/tegra124_car.h b/sys/arm/nvidia/tegra124/tegra124_car.h new file mode 100644 index 0000000..b11742c --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_car.h @@ -0,0 +1,337 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#ifndef _TEGRA124_CAR_ +#define _TEGRA124_CAR_ + +#include "clkdev_if.h" + +#define RD4(sc, reg, val) CLKDEV_READ_4((sc)->clkdev, reg, val) +#define WR4(sc, reg, val) CLKDEV_WRITE_4((sc)->clkdev, reg, val) +#define MD4(sc, reg, mask, set) CLKDEV_MODIFY_4((sc)->clkdev, reg, mask, set) +#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) +#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) + +#define RST_DEVICES_L 0x004 +#define RST_DEVICES_H 0x008 +#define RST_DEVICES_U 0x00C +#define CLK_OUT_ENB_L 0x010 +#define CLK_OUT_ENB_H 0x014 +#define CLK_OUT_ENB_U 0x018 +#define CCLK_BURST_POLICY 0x020 +#define SUPER_CCLK_DIVIDER 0x024 +#define SCLK_BURST_POLICY 0x028 +#define SUPER_SCLK_DIVIDER 0x02c +#define CLK_SYSTEM_RATE 0x030 + +#define OSC_CTRL 0x050 + #define OSC_CTRL_OSC_FREQ_SHIFT 28 + #define OSC_CTRL_PLL_REF_DIV_SHIFT 26 + +#define PLLE_SS_CNTL 0x068 +#define PLLE_SS_CNTL_SSCINCINTRV_MASK (0x3f << 24) +#define PLLE_SS_CNTL_SSCINCINTRV_VAL (0x20 << 24) +#define PLLE_SS_CNTL_SSCINC_MASK (0xff << 16) +#define PLLE_SS_CNTL_SSCINC_VAL (0x1 << 16) +#define PLLE_SS_CNTL_SSCINVERT (1 << 15) +#define PLLE_SS_CNTL_SSCCENTER (1 << 14) +#define PLLE_SS_CNTL_SSCBYP (1 << 12) +#define PLLE_SS_CNTL_INTERP_RESET (1 << 11) +#define PLLE_SS_CNTL_BYPASS_SS (1 << 10) +#define PLLE_SS_CNTL_SSCMAX_MASK 0x1ff +#define PLLE_SS_CNTL_SSCMAX_VAL 0x25 +#define PLLE_SS_CNTL_DISABLE (PLLE_SS_CNTL_BYPASS_SS | \ + PLLE_SS_CNTL_INTERP_RESET | \ + PLLE_SS_CNTL_SSCBYP) +#define PLLE_SS_CNTL_COEFFICIENTS_MASK (PLLE_SS_CNTL_SSCMAX_MASK | \ + PLLE_SS_CNTL_SSCINC_MASK | \ + PLLE_SS_CNTL_SSCINCINTRV_MASK) +#define PLLE_SS_CNTL_COEFFICIENTS_VAL (PLLE_SS_CNTL_SSCMAX_VAL | \ + PLLE_SS_CNTL_SSCINC_VAL | \ + PLLE_SS_CNTL_SSCINCINTRV_VAL) + +#define PLLC_BASE 0x080 +#define PLLC_OUT 0x084 +#define PLLC_MISC2 0x088 +#define PLLC_MISC 0x08c +#define PLLM_BASE 0x090 +#define PLLM_OUT 0x094 +#define PLLM_MISC 0x09c +#define PLLP_BASE 0x0a0 +#define PLLP_MISC 0x0ac +#define PLLP_OUTA 0x0a4 +#define PLLP_OUTB 0x0a8 +#define PLLA_BASE 0x0b0 +#define PLLA_OUT 0x0b4 +#define PLLA_MISC 0x0bc +#define PLLU_BASE 0x0c0 +#define PLLU_MISC 0x0cc +#define PLLD_BASE 0x0d0 +#define PLLD_MISC 0x0dc +#define PLLX_BASE 0x0e0 +#define PLLX_MISC 0x0e4 +#define PLLE_BASE 0x0e8 +#define PLLE_BASE_LOCK_OVERRIDE (1 << 29) +#define PLLE_BASE_DIVCML_SHIFT 24 +#define PLLE_BASE_DIVCML_MASK 0xf + +#define PLLE_MISC 0x0ec +#define PLLE_MISC_SETUP_BASE_SHIFT 16 +#define PLLE_MISC_SETUP_BASE_MASK (0xffff << PLLE_MISC_SETUP_BASE_SHIFT) +#define PLLE_MISC_READY (1 << 15) +#define PLLE_MISC_IDDQ_SWCTL (1 << 14) +#define PLLE_MISC_IDDQ_OVERRIDE_VALUE (1 << 13) +#define PLLE_MISC_LOCK (1 << 11) +#define PLLE_MISC_REF_ENABLE (1 << 10) +#define PLLE_MISC_LOCK_ENABLE (1 << 9) +#define PLLE_MISC_PTS (1 << 8) +#define PLLE_MISC_VREG_BG_CTRL_SHIFT 4 +#define PLLE_MISC_VREG_BG_CTRL_MASK (3 << PLLE_MISC_VREG_BG_CTRL_SHIFT) +#define PLLE_MISC_VREG_CTRL_SHIFT 2 +#define PLLE_MISC_VREG_CTRL_MASK (2 << PLLE_MISC_VREG_CTRL_SHIFT) + +#define CLK_SOURCE_I2S1 0x100 +#define CLK_SOURCE_I2S2 0x104 +#define CLK_SOURCE_SPDIF_OUT 0x108 +#define CLK_SOURCE_SPDIF_IN 0x10c +#define CLK_SOURCE_PWM 0x110 +#define CLK_SOURCE_SPI2 0x118 +#define CLK_SOURCE_SPI3 0x11c +#define CLK_SOURCE_I2C1 0x124 +#define CLK_SOURCE_I2C5 0x128 +#define CLK_SOURCE_SPI1 0x134 +#define CLK_SOURCE_DISP1 0x138 +#define CLK_SOURCE_DISP2 0x13c +#define CLK_SOURCE_ISP 0x144 +#define CLK_SOURCE_VI 0x148 +#define CLK_SOURCE_SDMMC1 0x150 +#define CLK_SOURCE_SDMMC2 0x154 +#define CLK_SOURCE_SDMMC4 0x164 +#define CLK_SOURCE_VFIR 0x168 +#define CLK_SOURCE_HSI 0x174 +#define CLK_SOURCE_UARTA 0x178 +#define CLK_SOURCE_UARTB 0x17c +#define CLK_SOURCE_HOST1X 0x180 +#define CLK_SOURCE_HDMI 0x18c +#define CLK_SOURCE_I2C2 0x198 +#define CLK_SOURCE_EMC 0x19c +#define CLK_SOURCE_UARTC 0x1a0 +#define CLK_SOURCE_VI_SENSOR 0x1a8 +#define CLK_SOURCE_SPI4 0x1b4 +#define CLK_SOURCE_I2C3 0x1b8 +#define CLK_SOURCE_SDMMC3 0x1bc +#define CLK_SOURCE_UARTD 0x1c0 +#define CLK_SOURCE_VDE 0x1c8 +#define CLK_SOURCE_OWR 0x1cc +#define CLK_SOURCE_NOR 0x1d0 +#define CLK_SOURCE_CSITE 0x1d4 +#define CLK_SOURCE_I2S0 0x1d8 +#define CLK_SOURCE_DTV 0x1dc +#define CLK_SOURCE_MSENC 0x1f0 +#define CLK_SOURCE_TSEC 0x1f4 +#define CLK_SOURCE_SPARE2 0x1f8 + +#define CLK_OUT_ENB_X 0x280 +#define RST_DEVICES_X 0x28C + +#define RST_DEVICES_V 0x358 +#define RST_DEVICES_W 0x35C +#define CLK_OUT_ENB_V 0x360 +#define CLK_OUT_ENB_W 0x364 +#define CCLKG_BURST_POLICY 0x368 +#define SUPER_CCLKG_DIVIDER 0x36C +#define CCLKLP_BURST_POLICY 0x370 +#define SUPER_CCLKLP_DIVIDER 0x374 + +#define CLK_SOURCE_MSELECT 0x3b4 +#define CLK_SOURCE_TSENSOR 0x3b8 +#define CLK_SOURCE_I2S3 0x3bc +#define CLK_SOURCE_I2S4 0x3c0 +#define CLK_SOURCE_I2C4 0x3c4 +#define CLK_SOURCE_SPI5 0x3c8 +#define CLK_SOURCE_SPI6 0x3cc +#define CLK_SOURCE_AUDIO 0x3d0 +#define CLK_SOURCE_DAM0 0x3d8 +#define CLK_SOURCE_DAM1 0x3dc +#define CLK_SOURCE_DAM2 0x3e0 +#define CLK_SOURCE_HDA2CODEC_2X 0x3e4 +#define CLK_SOURCE_ACTMON 0x3e8 +#define CLK_SOURCE_EXTPERIPH1 0x3ec +#define CLK_SOURCE_EXTPERIPH2 0x3f0 +#define CLK_SOURCE_EXTPERIPH3 0x3f4 +#define CLK_SOURCE_I2C_SLOW 0x3fc + +#define CLK_SOURCE_SYS 0x400 +#define CLK_SOURCE_SOR0 0x414 +#define CLK_SOURCE_SATA_OOB 0x420 +#define CLK_SOURCE_SATA 0x424 +#define CLK_SOURCE_HDA 0x428 +#define UTMIP_PLL_CFG0 0x480 +#define UTMIP_PLL_CFG1 0x484 +#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP (1 << 17) +#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN (1 << 16) +#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP (1 << 15) +#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN (1 << 14) +#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN (1 << 12) +#define UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 6) +#define UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) + +#define UTMIP_PLL_CFG2 0x488 +#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18) +#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xffff) << 6) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN (1 << 4) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN (1 << 2) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN (1 << 0) + +#define PLLE_AUX 0x48c +#define PLLE_AUX_PLLRE_SEL (1 << 28) +#define PLLE_AUX_SEQ_START_STATE (1 << 25) +#define PLLE_AUX_SEQ_ENABLE (1 << 24) +#define PLLE_AUX_SS_SWCTL (1 << 6) +#define PLLE_AUX_ENABLE_SWCTL (1 << 4) +#define PLLE_AUX_USE_LOCKDET (1 << 3) +#define PLLE_AUX_PLLP_SEL (1 << 2) + +#define SATA_PLL_CFG0 0x490 +#define SATA_PLL_CFG0_SEQ_START_STATE (1 << 25) +#define SATA_PLL_CFG0_SEQ_ENABLE (1 << 24) +#define SATA_PLL_CFG0_SEQ_PADPLL_PD_INPUT_VALUE (1 << 7) +#define SATA_PLL_CFG0_SEQ_LANE_PD_INPUT_VALUE (1 << 6) +#define SATA_PLL_CFG0_SEQ_RESET_INPUT_VALUE (1 << 5) +#define SATA_PLL_CFG0_SEQ_IN_SWCTL (1 << 4) +#define SATA_PLL_CFG0_PADPLL_USE_LOCKDET (1 << 2) +#define SATA_PLL_CFG0_PADPLL_RESET_OVERRIDE_VALUE (1 << 1) +#define SATA_PLL_CFG0_PADPLL_RESET_SWCTL (1 << 0) + +#define SATA_PLL_CFG1 0x494 +#define PCIE_PLL_CFG0 0x498 +#define PCIE_PLL_CFG0_SEQ_START_STATE (1 << 25) +#define PCIE_PLL_CFG0_SEQ_ENABLE (1 << 24) + +#define PLLD2_BASE 0x4b8 +#define PLLD2_MISC 0x4bc +#define UTMIP_PLL_CFG3 0x4c0 +#define PLLRE_BASE 0x4c4 +#define PLLRE_MISC 0x4c8 +#define PLLC2_BASE 0x4e8 +#define PLLC2_MISC 0x4ec +#define PLLC3_BASE 0x4fc + +#define PLLC3_MISC 0x500 +#define PLLX_MISC2 0x514 +#define PLLX_MISC2 0x514 +#define PLLX_MISC3 0x518 +#define PLLX_MISC3_DYNRAMP_STEPB_MASK 0xFF +#define PLLX_MISC3_DYNRAMP_STEPB_SHIFT 24 +#define PLLX_MISC3_DYNRAMP_STEPA_MASK 0xFF +#define PLLX_MISC3_DYNRAMP_STEPA_SHIFT 16 +#define PLLX_MISC3_NDIV_NEW_MASK 0xFF +#define PLLX_MISC3_NDIV_NEW_SHIFT 8 +#define PLLX_MISC3_EN_FSTLCK (1 << 5) +#define PLLX_MISC3_LOCK_OVERRIDE (1 << 4) +#define PLLX_MISC3_PLL_FREQLOCK (1 << 3) +#define PLLX_MISC3_DYNRAMP_DONE (1 << 2) +#define PLLX_MISC3_CLAMP_NDIV (1 << 1) +#define PLLX_MISC3_EN_DYNRAMP (1 << 0) +#define XUSBIO_PLL_CFG0 0x51c +#define XUSBIO_PLL_CFG0_SEQ_START_STATE (1 << 25) +#define XUSBIO_PLL_CFG0_SEQ_ENABLE (1 << 24) +#define XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET (1 << 6) +#define XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL (1 << 2) +#define XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL (1 << 0) + +#define PLLP_RESHIFT 0x528 +#define UTMIPLL_HW_PWRDN_CFG0 0x52c +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE (1 << 25) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE (1 << 24) +#define UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET (1 << 6) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_RESET_INPUT_VALUE (1 << 5) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_IN_SWCTL (1 << 4) +#define UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL (1 << 2) +#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE (1 << 1) +#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL (1 << 0) + +#define PLLDP_BASE 0x590 +#define PLLDP_MISC 0x594 +#define PLLC4_BASE 0x5a4 +#define PLLC4_MISC 0x5a8 + +#define CLK_SOURCE_XUSB_CORE_HOST 0x600 +#define CLK_SOURCE_XUSB_FALCON 0x604 +#define CLK_SOURCE_XUSB_FS 0x608 +#define CLK_SOURCE_XUSB_CORE_DEV 0x60c +#define CLK_SOURCE_XUSB_SS 0x610 +#define CLK_SOURCE_CILAB 0x614 +#define CLK_SOURCE_CILCD 0x618 +#define CLK_SOURCE_CILE 0x61c +#define CLK_SOURCE_DSIA_LP 0x620 +#define CLK_SOURCE_DSIB_LP 0x624 +#define CLK_SOURCE_ENTROPY 0x628 +#define CLK_SOURCE_DVFS_REF 0x62c +#define CLK_SOURCE_DVFS_SOC 0x630 +#define CLK_SOURCE_TRACECLKIN 0x634 +#define CLK_SOURCE_ADX 0x638 +#define CLK_SOURCE_AMX 0x63c +#define CLK_SOURCE_EMC_LATENCY 0x640 +#define CLK_SOURCE_SOC_THERM 0x644 +#define CLK_SOURCE_VI_SENSOR2 0x658 +#define CLK_SOURCE_I2C6 0x65c +#define CLK_SOURCE_EMC_DLL 0x664 +#define CLK_SOURCE_HDMI_AUDIO 0x668 +#define CLK_SOURCE_CLK72MHZ 0x66c +#define CLK_SOURCE_ADX1 0x670 +#define CLK_SOURCE_AMX1 0x674 +#define CLK_SOURCE_VIC 0x678 +#define PLLP_OUTC 0x67c +#define PLLP_MISC1 0x680 + + +struct tegra124_car_softc { + device_t dev; + struct resource * mem_res; + struct mtx mtx; + struct clkdom *clkdom; + int type; +}; + +struct tegra124_init_item { + char *name; + char *parent; + uint64_t frequency; + int enable; +}; + +void tegra124_init_plls(struct tegra124_car_softc *sc); + +void tegra124_periph_clock(struct tegra124_car_softc *sc); +void tegra124_super_mux_clock(struct tegra124_car_softc *sc); + +int tegra124_hwreset_by_idx(struct tegra124_car_softc *sc, intptr_t idx, + bool reset); + +#endif /*_TEGRA124_CAR_*/
\ No newline at end of file diff --git a/sys/arm/nvidia/tegra124/tegra124_clk_per.c b/sys/arm/nvidia/tegra124/tegra124_clk_per.c new file mode 100644 index 0000000..65ae5d9 --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_clk_per.c @@ -0,0 +1,810 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/rman.h> + +#include <machine/bus.h> + +#include <dev/extres/clk/clk.h> + +#include <gnu/dts/include/dt-bindings/clock/tegra124-car.h> +#include "tegra124_car.h" + +/* Bits in base register. */ +#define PERLCK_AMUX_MASK 0x0F +#define PERLCK_AMUX_SHIFT 16 +#define PERLCK_AMUX_DIS (1 << 20) +#define PERLCK_UDIV_DIS (1 << 24) +#define PERLCK_ENA_MASK (1 << 28) +#define PERLCK_MUX_SHIFT 29 +#define PERLCK_MUX_MASK 0x07 + + +struct periph_def { + struct clknode_init_def clkdef; + uint32_t base_reg; + uint32_t div_width; + uint32_t div_mask; + uint32_t div_f_width; + uint32_t div_f_mask; + uint32_t flags; +}; + +struct pgate_def { + struct clknode_init_def clkdef; + uint32_t idx; + uint32_t flags; +}; +#define PLIST(x) static const char *x[] + +#define GATE(_id, cname, plist, _idx) \ +{ \ + .clkdef.id = TEGRA124_CLK_##_id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = (const char *[]){plist}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .idx = _idx, \ + .flags = 0, \ +} + +/* Sources for multiplexors. */ +PLIST(mux_a_N_audio_N_p_N_clkm) = + {"pllA_out0", NULL, "audio", NULL, + "pllP_out0", NULL, "clk_m"}; +PLIST(mux_a_N_audio0_N_p_N_clkm) = + {"pllA_out0", NULL, "audio0", NULL, + "pllP_out0", NULL, "clk_m"}; +PLIST(mux_a_N_audio1_N_p_N_clkm) = + {"pllA_out0", NULL, "audio1", NULL, + "pllP_out0", NULL, "clk_m"}; +PLIST(mux_a_N_audio2_N_p_N_clkm) = + {"pllA_out0", NULL, "audio2", NULL, + "pllP_out0", NULL, "clk_m"}; +PLIST(mux_a_N_audio3_N_p_N_clkm) = + {"pllA_out0", NULL, "audio3", NULL, + "pllP_out0", NULL, "clk_m"}; +PLIST(mux_a_N_audio4_N_p_N_clkm) = + {"pllA_out0", NULL, "audio4", NULL, + "pllP_out0", NULL, "clk_m"}; +PLIST(mux_a_clks_p_clkm_e) = + {"pllA_out0", "clk_s", "pllP_out0", + "clk_m", "pllE_out0"}; +PLIST(mux_a_c2_c_c3_p_N_clkm) = + {"pllA_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "pllP_out0", NULL, "clk_m"}; + +PLIST(mux_m_c_p_a_c2_c3) = + {"pllM_out0", "pllC_out0", "pllP_out0", "pllA_out0", + "pllC2_out0", "pllC3_out0"}; +PLIST(mux_m_c_p_a_c2_c3_clkm) = + {"pllM_out0", "pllC_out0", "pllP_out0", "pllA_out0", + "pllC2_out0", "pllC3_out0", "clk_m"}; +PLIST(mux_m_c_p_a_c2_c3_clkm_c4) = + {"pllM_out0", "pllC_out0", "pllP_out0", "pllA_out0", + "pllC2_out0", "pllC3_out0", "clk_m", "pllC4_out0"}; +PLIST(mux_m_c_p_clkm_mud_c2_c3) = + {"pllM_out0", "pllC_out0", "pllP_out0", "clk_m", + "pllM_UD", "pllC2_out0", "pllC3_out0"}; +PLIST(mux_m_c2_c_c3_p_N_a) = + {"pllM_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "pllP_out0", NULL, "pllA_out0"}; +PLIST(mux_m_c2_c_c3_p_N_a_c4) = + {"pllM_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + NULL, "pllA_out0", "pllC4_out0"}; + +PLIST(mux_p_N_c_N_N_N_clkm) = + {"pllP_out0", NULL, "pllC_out0", NULL, + NULL, NULL, "clk_m"}; +PLIST(mux_p_N_c_N_m_N_clkm) = + {"pllP_out0", NULL, "pllC_out0", NULL, + "pllM_out0", NULL, "clk_m"}; +PLIST(mux_p_c_c2_clkm) = + {"pllP_out0", "pllC_out0", "pllC2_out0", "clk_m"}; +PLIST(mux_p_c2_c_c3_m) = + {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "pllM_out0"}; +PLIST(mux_p_c2_c_c3_m_N_clkm) = + {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "pllM_out0", NULL, "clk_m"}; +PLIST(mux_p_c2_c_c3_m_e_clkm) = + {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "pllM_out0", "pllE_out0", "clk_m"}; +PLIST(mux_p_c2_c_c3_m_a_clkm) = + {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "pllM_out0", "pllA_out0", "clk_m"}; +PLIST(mux_p_c2_c_c3_m_clks_clkm) = + {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "pllM_out0", "clk_s", "clk_m"}; +PLIST(mux_p_c2_c_c3_clks_N_clkm) = + {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "clk_s", NULL, "clk_m"}; +PLIST(mux_p_c2_c_c3_clkm_N_clks) = + {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "clk_m", NULL, "clk_s"}; +PLIST(mux_p_clkm_clks_E) = + {"pllP_out0", "clk_m", "clk_s", "pllE_out0"}; +PLIST(mux_p_m_d_a_c_d2_clkm) = + {"pllP_out0", "pllM_out0", "pllD_out0", "pllA_out0", + "pllC_out0", "pllD2_out0", "clk_m"}; + +PLIST(mux_clkm_N_u48_N_p_N_u480) = + {"clk_m", NULL, "pllU_48", NULL, + "pllP_out0", NULL, "pllU_480"}; +PLIST(mux_clkm_p_c2_c_c3_refre) = + {"clk_m", "pllP_out0", "pllC2_out0", "pllC_out0", + "pllC3_out0", "pllREFE_out"}; +PLIST(mux_clkm_refe_clks_u480_c_c2_c3_oscdiv) = + {"clk_m", "pllREFE_out", "clk_s", "pllU_480", + "pllC_out0", "pllC2_out0", "pllC3_out0", "osc_div_clk"}; + +PLIST(mux_sep_audio) = + {"pllA_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "pllP_out0", NULL, "clk_m", NULL, + "spdif_in", "i2s0", "i2s1", "i2s2", + "i2s4", "pllA_out0", "ext_vimclk"}; + +static uint32_t clk_enabale_reg[] = { + CLK_OUT_ENB_L, + CLK_OUT_ENB_H, + CLK_OUT_ENB_U, + CLK_OUT_ENB_V, + CLK_OUT_ENB_W, + CLK_OUT_ENB_X, +}; + +static uint32_t clk_reset_reg[] = { + RST_DEVICES_L, + RST_DEVICES_H, + RST_DEVICES_U, + RST_DEVICES_V, + RST_DEVICES_W, + RST_DEVICES_X, +}; + +#define L(n) ((0 * 32) + (n)) +#define H(n) ((1 * 32) + (n)) +#define U(n) ((2 * 32) + (n)) +#define V(n) ((3 * 32) + (n)) +#define W(n) ((4 * 32) + (n)) +#define X(n) ((5 * 32) + (n)) + +static struct pgate_def pgate_def[] = { + /* bank L -> 0-31 */ + /* GATE(CPU, "cpu", "clk_m", L(0)), */ + GATE(ISPB, "ispb", "clk_m", L(3)), + GATE(RTC, "rtc", "clk_s", L(4)), + GATE(TIMER, "timer", "clk_m", L(5)), + GATE(UARTA, "uarta", "pc_uarta" , L(6)), + GATE(UARTB, "uartb", "pc_uartb", L(7)), + GATE(VFIR, "vfir", "pc_vfir", L(7)), + /* GATE(GPIO, "gpio", "clk_m", L(8)), */ + GATE(SDMMC2, "sdmmc2", "pc_sdmmc2", L(9)), + GATE(SPDIF_OUT, "spdif_out", "pc_spdif_out", L(10)), + GATE(SPDIF_IN, "spdif_in", "pc_spdif_in", L(10)), + GATE(I2S1, "i2s1", "pc_i2s1", L(11)), + GATE(I2C1, "i2c1", "pc_i2c1", L(12)), + GATE(SDMMC1, "sdmmc1", "pc_sdmmc1", L(14)), + GATE(SDMMC4, "sdmmc4", "pc_sdmmc4", L(15)), + GATE(PWM, "pwm", "pc_pwm", L(17)), + GATE(I2S2, "i2s2", "pc_i2s2", L(18)), + GATE(VI, "vi", "pc_vi", L(20)), + GATE(USBD, "usbd", "clk_m", L(22)), + GATE(ISP, "isp", "pc_isp", L(23)), + GATE(DISP2, "disp2", "pc_disp2", L(26)), + GATE(DISP1, "disp1", "pc_disp1", L(27)), + GATE(HOST1X, "host1x", "pc_host1x", L(28)), + GATE(VCP, "vcp", "clk_m", L(29)), + GATE(I2S0, "i2s0", "pc_i2s0", L(30)), + /* GATE(CACHE2, "ccache2", "clk_m", L(31)), */ + + /* bank H -> 32-63 */ + GATE(MC, "mem", "clk_m", H(0)), + /* GATE(AHBDMA, "ahbdma", "clk_m", H(1)), */ + GATE(APBDMA, "apbdma", "clk_m", H(2)), + GATE(KBC, "kbc", "clk_s", H(4)), + /* GATE(STAT_MON, "stat_mon", "clk_s", H(5)), */ + /* GATE(PMC, "pmc", "clk_s", H(6)), */ + GATE(FUSE, "fuse", "clk_m", H(7)), + GATE(KFUSE, "kfuse", "clk_m", H(8)), + GATE(SBC1, "spi1", "pc_spi1", H(9)), + GATE(NOR, "snor", "pc_snor", H(10)), + /* GATE(JTAG2TBC, "jtag2tbc", "clk_m", H(11)), */ + GATE(SBC2, "spi2", "pc_spi2", H(12)), + GATE(SBC3, "spi3", "pc_spi3", H(14)), + GATE(I2C5, "i2c5", "pc_i2c5", H(15)), + GATE(DSIA, "dsia", "dsia_mux", H(16)), + GATE(MIPI, "hsi", "pc_hsi", H(18)), + GATE(HDMI, "hdmi", "pc_hdmi", H(19)), + GATE(CSI, "csi", "pllP_out3", H(20)), + GATE(I2C2, "i2c2", "pc_i2c2", H(22)), + GATE(UARTC, "uartc", "pc_uartc", H(23)), + GATE(MIPI_CAL, "mipi_cal", "clk_m", H(24)), + GATE(EMC, "emc", "emc_mux", H(25)), + GATE(USB2, "usb2", "clk_m", H(26)), + GATE(USB3, "usb3", "clk_m", H(27)), + GATE(VDE, "vde", "pc_vde", H(29)), + GATE(BSEA, "bsea", "clk_m", H(30)), + GATE(BSEV, "bsev", "clk_m", H(31)), + + /* bank U -> 64-95 */ + GATE(UARTD, "uartd", "pc_uartd", U(1)), + GATE(I2C3, "i2c3", "pc_i2c3", U(3)), + GATE(SBC4, "spi4", "pc_spi4", U(4)), + GATE(SDMMC3, "sdmmc3", "pc_sdmmc3", U(5)), + GATE(PCIE, "pcie", "clk_m", U(6)), + GATE(OWR, "owr", "pc_owr", U(7)), + GATE(AFI, "afi", "clk_m", U(8)), + GATE(CSITE, "csite", "pc_csite", U(9)), + /* GATE(AVPUCQ, "avpucq", clk_m, U(11)), */ + GATE(TRACE, "traceclkin", "pc_traceclkin", U(13)), + GATE(SOC_THERM, "soc_therm", "pc_soc_therm", U(14)), + GATE(DTV, "dtv", "clk_m", U(15)), + GATE(I2CSLOW, "i2c_slow", "pc_i2c_slow", U(17)), + GATE(DSIB, "dsib", "dsib_mux", U(18)), + GATE(TSEC, "tsec", "pc_tsec", U(19)), + /* GATE(IRAMA, "irama", "clk_m", U(20)), */ + /* GATE(IRAMB, "iramb", "clk_m", U(21)), */ + /* GATE(IRAMC, "iramc", "clk_m", U(22)), */ + /* GATE(IRAMD, "iramd", "clk_m", U(23)), */ + /* GATE(CRAM2, "cram2", "clk_m", U(24)), */ + GATE(XUSB_HOST, "xusb_core_host", "pc_xusb_core_host", U(25)), + /* GATE(M_DOUBLER, "m_doubler", "clk_m", U(26)), */ + GATE(MSENC, "msenc", "pc_msenc", U(27)), + GATE(CSUS, "sus_out", "clk_m", U(28)), + /* GATE(DEVD2_OUT, "devd2_out", "clk_m", U(29)), */ + /* GATE(DEVD1_OUT, "devd1_out", "clk_m", U(30)), */ + GATE(XUSB_DEV_SRC, "xusb_core_dev", "pc_xusb_core_dev", U(31)), + + /* bank V -> 96-127 */ + /* GATE(CPUG, "cpug", "clk_m", V(0)), */ + /* GATE(CPULP, "cpuLP", "clk_m", V(1)), */ + GATE(MSELECT, "mselect", "pc_mselect", V(3)), + GATE(TSENSOR, "tsensor", "pc_tsensor", V(4)), + GATE(I2S3, "i2s3", "pc_i2s3", V(5)), + GATE(I2S4, "i2s4", "pc_i2s4", V(6)), + GATE(I2C4, "i2c4", "pc_i2c4", V(7)), + GATE(SBC5, "spi5", "pc_spi5", V(8)), + GATE(SBC6, "spi6", "pc_spi6", V(9)), + GATE(D_AUDIO, "audio", "pc_audio", V(10)), + GATE(APBIF, "apbif", "clk_m", V(11)), + GATE(DAM0, "dam0", "pc_dam0", V(12)), + GATE(DAM1, "dam1", "pc_dam1", V(13)), + GATE(DAM2, "dam2", "pc_dam2", V(14)), + GATE(HDA2CODEC_2X, "hda2codec_2x", "pc_hda2codec_2x", V(15)), + /* GATE(ATOMICS, "atomics", "clk_m", V(16)), */ + /* GATE(SPDIF_DOUBLER, "spdif_doubler", "clk_m", V(22)), */ + GATE(ACTMON, "actmon", "pc_actmon", V(23)), + GATE(EXTERN1, "extperiph1", "pc_extperiph1", V(24)), + GATE(EXTERN2, "extperiph2", "pc_extperiph2", V(25)), + GATE(EXTERN3, "extperiph3", "pc_extperiph3", V(26)), + GATE(SATA_OOB, "sata_oob", "pc_sata_oob", V(27)), + GATE(SATA, "sata", "pc_sata", V(28)), + GATE(HDA, "hda", "pc_hda", V(29)), + + /* bank W -> 128-159*/ + GATE(HDA2HDMI, "hda2hdmi", "clk_m", W(0)), + GATE(SATA_COLD, "sata_cold", "clk_m", W(1)), /* Reset only */ + /* GATE(PCIERX0, "pcierx0", "clk_m", W(2)), */ + /* GATE(PCIERX1, "pcierx1", "clk_m", W(3)), */ + /* GATE(PCIERX2, "pcierx2", "clk_m", W(4)), */ + /* GATE(PCIERX3, "pcierx3", "clk_m", W(5)), */ + /* GATE(PCIERX4, "pcierx4", "clk_m", W(6)), */ + /* GATE(PCIERX5, "pcierx5", "clk_m", W(7)), */ + /* GATE(CEC, "cec", "clk_m", W(8)), */ + /* GATE(PCIE2_IOBIST, "pcie2_iobist", "clk_m", W(9)), */ + /* GATE(EMC_IOBIST, "emc_iobist", "clk_m", W(10)), */ + /* GATE(HDMI_IOBIST, "hdmi_iobist", "clk_m", W(11)), */ + /* GATE(SATA_IOBIST, "sata_iobist", "clk_m", W(12)), */ + /* GATE(MIPI_IOBIST, "mipi_iobist", "clk_m", W(13)), */ + /* GATE(XUSB_IOBIST, "xusb_iobist", "clk_m", W(15)), */ + GATE(CILAB, "cilab", "pc_cilab", W(16)), + GATE(CILCD, "cilcd", "pc_cilcd", W(17)), + GATE(CILE, "cile", "pc_cile", W(18)), + GATE(DSIALP, "dsia_lp", "pc_dsia_lp", W(19)), + GATE(DSIBLP, "dsib_lp", "pc_dsib_lp", W(20)), + GATE(ENTROPY, "entropy", "pc_entropy", W(21)), + GATE(AMX, "amx", "pc_amx", W(25)), + GATE(ADX, "adx", "pc_adx", W(26)), + GATE(DFLL_REF, "dvfs_ref", "pc_dvfs_ref", X(27)), + GATE(DFLL_SOC, "dvfs_soc", "pc_dvfs_soc", X(27)), + GATE(XUSB_SS_SRC, "xusb_ss", "xusb_ss_mux", X(28)), + /* GATE(EMC_LATENCY, "emc_latency", "pc_emc_latency", X(29)), */ + + /* bank X -> 160-191*/ + /* GATE(SPARE, "spare", "clk_m", X(0)), */ + /* GATE(CAM_MCLK, "CAM_MCLK", "clk_m", X(4)), */ + /* GATE(CAM_MCLK2, "CAM_MCLK2", "clk_m", X(5)), */ + GATE(I2C6, "i2c6", "pc_i2c6", X(6)), + /* GATE(VIM2_CLK, "vim2_clk", clk_m, X(11)), */ + /* GATE(EMC_DLL, "emc_dll", "pc_emc_dll", X(14)), */ + GATE(HDMI_AUDIO, "hdmi_audio", "pc_hdmi_audio", X(16)), + GATE(CLK72MHZ, "clk72mhz", "pc_clk72mhz", X(17)), + GATE(VIC03, "vic", "pc_vic", X(18)), + GATE(ADX1, "adx1", "pc_adx1", X(20)), + GATE(DPAUX, "dpaux", "clk_m", X(21)), + GATE(SOR0_LVDS, "sor0", "pc_sor0", X(22)), + GATE(GPU, "gpu", "osc_div_clk", X(24)), + GATE(AMX1, "amx1", "pc_amx1", X(26)), +}; + +/* Peripheral clock clock */ +#define DCF_HAVE_MUX 0x0100 /* Block with multipexor */ +#define DCF_HAVE_ENA 0x0200 /* Block with enable bit */ +#define DCF_HAVE_DIV 0x0400 /* Block with divider */ + +/* Mark block with additional bis / functionality. */ +#define DCF_IS_MASK 0x00FF +#define DCF_IS_UART 0x0001 +#define DCF_IS_VI 0x0002 +#define DCF_IS_HOST1X 0x0003 +#define DCF_IS_XUSB_SS 0x0004 +#define DCF_IS_EMC_DLL 0x0005 +#define FDS_IS_SATA 0x0006 +#define DCF_IS_VIC 0x0007 +#define DCF_IS_AUDIO 0x0008 +#define DCF_IS_SOR0 0x0009 + +/* Basic pheripheral clock */ +#define PER_CLK(_id, cn, pl, r, diw, fiw, f) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cn, \ + .clkdef.parent_names = pl, \ + .clkdef.parent_cnt = nitems(pl), \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .base_reg = r, \ + .div_width = diw, \ + .div_f_width = fiw, \ + .flags = f, \ +} + +/* Mux with fractional 8.1 divider. */ +#define CLK_8_1(cn, pl, r, f) \ + PER_CLK(0, cn, pl, r, 8, 1, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV) +/* Mux with fractional 16.1 divider. */ +#define CLK16_1(cn, pl, r, f) \ + PER_CLK(0, cn, pl, r, 16, 1, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV) +/* Mux with integer 16bits divider. */ +#define CLK16_0(cn, pl, r, f) \ + PER_CLK(0, cn, pl, r, 16, 0, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV) +/* Mux wihout divider. */ +#define CLK_0_0(cn, pl, r, f) \ + PER_CLK(0, cn, pl, r, 0, 0, (f) | DCF_HAVE_MUX) + +static struct periph_def periph_def[] = { + CLK_8_1("pc_i2s1", mux_a_N_audio1_N_p_N_clkm, CLK_SOURCE_I2S1, DCF_HAVE_ENA), + CLK_8_1("pc_i2s2", mux_a_N_audio2_N_p_N_clkm, CLK_SOURCE_I2S2, DCF_HAVE_ENA), + CLK_8_1("pc_spdif_out", mux_a_N_audio_N_p_N_clkm, CLK_SOURCE_SPDIF_OUT, 0), + CLK_8_1("pc_spdif_in", mux_p_c2_c_c3_m, CLK_SOURCE_SPDIF_IN, 0), + CLK_8_1("pc_pwm", mux_p_c2_c_c3_clks_N_clkm, CLK_SOURCE_PWM, 0), + CLK_8_1("pc_spi2", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI2, 0), + CLK_8_1("pc_spi3", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI3, 0), + CLK16_0("pc_i2c5", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C5, 0), + CLK16_0("pc_i2c1", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C1, 0), + CLK_8_1("pc_spi1", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI1, 0), + CLK_0_0("pc_disp1", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_DISP1, 0), + CLK_0_0("pc_disp2", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_DISP2, 0), + CLK_8_1("pc_isp", mux_m_c_p_a_c2_c3_clkm_c4, CLK_SOURCE_ISP, 0), + CLK_8_1("pc_vi", mux_m_c2_c_c3_p_N_a_c4, CLK_SOURCE_VI, DCF_IS_VI), + CLK_8_1("pc_sdmmc1", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC1, 0), + CLK_8_1("pc_sdmmc2", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC2, 0), + CLK_8_1("pc_sdmmc4", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC4, 0), + CLK_8_1("pc_vfir", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_VFIR, 0), + CLK_8_1("pc_hsi", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HSI, 0), + CLK16_1("pc_uarta", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTA, DCF_IS_UART), + CLK16_1("pc_uartb", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTB, DCF_IS_UART), + CLK_8_1("pc_host1x", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HOST1X, DCF_IS_HOST1X), + CLK_8_1("pc_hdmi", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_HDMI, 0), + CLK16_0("pc_i2c2", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C2, 0), +/* EMC 8 */ + CLK16_1("pc_uartc", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTC, DCF_IS_UART), + CLK_8_1("pc_vi_sensor", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_VI_SENSOR, 0), + CLK_8_1("pc_spi4", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI4, 0), + CLK16_0("pc_i2c3", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C3, 0), + CLK_8_1("pc_sdmmc3", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC3, 0), + CLK16_1("pc_uartd", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTD, DCF_IS_UART), + CLK_8_1("pc_vde", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_VDE, 0), + CLK_8_1("pc_owr", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_OWR, 0), + CLK_8_1("pc_snor", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_NOR, 0), + CLK_8_1("pc_csite", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_CSITE, 0), + CLK_8_1("pc_i2s0", mux_a_N_audio0_N_p_N_clkm, CLK_SOURCE_I2S0, 0), +/* DTV xxx */ + CLK_8_1("pc_msenc", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_MSENC, 0), + CLK_8_1("pc_tsec", mux_p_c2_c_c3_m_a_clkm, CLK_SOURCE_TSEC, 0), +/* SPARE2 */ + + + CLK_8_1("pc_mselect", mux_p_c2_c_c3_m_clks_clkm, CLK_SOURCE_MSELECT, 0), + CLK_8_1("pc_tsensor", mux_p_c2_c_c3_clkm_N_clks, CLK_SOURCE_TSENSOR, 0), + CLK_8_1("pc_i2s3", mux_a_N_audio3_N_p_N_clkm, CLK_SOURCE_I2S3, DCF_HAVE_ENA), + CLK_8_1("pc_i2s4", mux_a_N_audio4_N_p_N_clkm, CLK_SOURCE_I2S4, DCF_HAVE_ENA), + CLK16_0("pc_i2c4", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C4, 0), + CLK_8_1("pc_spi5", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI5, 0), + CLK_8_1("pc_spi6", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI6, 0), + CLK_8_1("pc_audio", mux_sep_audio, CLK_SOURCE_AUDIO, DCF_IS_AUDIO), + CLK_8_1("pc_dam0", mux_sep_audio, CLK_SOURCE_DAM0, DCF_IS_AUDIO), + CLK_8_1("pc_dam1", mux_sep_audio, CLK_SOURCE_DAM1, DCF_IS_AUDIO), + CLK_8_1("pc_dam2", mux_sep_audio, CLK_SOURCE_DAM2, DCF_IS_AUDIO), + CLK_8_1("pc_hda2codec_2x", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HDA2CODEC_2X, 0), + CLK_8_1("pc_actmon", mux_p_c2_c_c3_clks_N_clkm, CLK_SOURCE_ACTMON, 0), + CLK_8_1("pc_extperiph1", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH1, 0), + CLK_8_1("pc_extperiph2", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH2, 0), + CLK_8_1("pc_extperiph3", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH3, 0), + CLK_8_1("pc_i2c_slow", mux_p_c2_c_c3_clks_N_clkm, CLK_SOURCE_I2C_SLOW, 0), +/* SYS */ + CLK_8_1("pc_sor0", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_SOR0, DCF_IS_SOR0), + CLK_8_1("pc_sata_oob", mux_p_N_c_N_m_N_clkm, CLK_SOURCE_SATA_OOB, 0), + CLK_8_1("pc_sata", mux_p_N_c_N_m_N_clkm, CLK_SOURCE_SATA, FDS_IS_SATA), + CLK_8_1("pc_hda", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HDA, 0), + + + CLK_8_1("pc_xusb_core_host", mux_clkm_p_c2_c_c3_refre, CLK_SOURCE_XUSB_CORE_HOST, 0), + CLK_8_1("pc_xusb_falcon", mux_clkm_p_c2_c_c3_refre, CLK_SOURCE_XUSB_FALCON, 0), + CLK_8_1("pc_xusb_fs", mux_clkm_N_u48_N_p_N_u480, CLK_SOURCE_XUSB_FS, 0), + CLK_8_1("pc_xusb_core_dev", mux_clkm_p_c2_c_c3_refre, CLK_SOURCE_XUSB_CORE_DEV, 0), + CLK_8_1("pc_xusb_ss", mux_clkm_refe_clks_u480_c_c2_c3_oscdiv, CLK_SOURCE_XUSB_SS, DCF_IS_XUSB_SS), + CLK_8_1("pc_cilab", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_CILAB, 0), + CLK_8_1("pc_cilcd", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_CILCD, 0), + CLK_8_1("pc_cile", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_CILE, 0), + CLK_8_1("pc_dsia_lp", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_DSIA_LP, 0), + CLK_8_1("pc_dsib_lp", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_DSIB_LP, 0), + CLK_8_1("pc_entropy", mux_p_clkm_clks_E, CLK_SOURCE_ENTROPY, 0), + CLK_8_1("pc_dvfs_ref", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_DVFS_REF, DCF_HAVE_ENA), + CLK_8_1("pc_dvfs_soc", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_DVFS_SOC, DCF_HAVE_ENA), + CLK_8_1("pc_traceclkin", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_TRACECLKIN, 0), + CLK_8_1("pc_adx", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_ADX, DCF_HAVE_ENA), + CLK_8_1("pc_amx", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_AMX, DCF_HAVE_ENA), + CLK_8_1("pc_emc_latency", mux_m_c_p_clkm_mud_c2_c3, CLK_SOURCE_EMC_LATENCY, 0), + CLK_8_1("pc_soc_therm", mux_m_c_p_a_c2_c3, CLK_SOURCE_SOC_THERM, 0), + CLK_8_1("pc_vi_sensor2", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_VI_SENSOR2, 0), + CLK16_0("pc_i2c6", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C6, 0), + CLK_8_1("pc_emc_dll", mux_m_c_p_clkm_mud_c2_c3, CLK_SOURCE_EMC_DLL, DCF_IS_EMC_DLL), + CLK_8_1("pc_hdmi_audio", mux_p_c_c2_clkm, CLK_SOURCE_HDMI_AUDIO, 0), + CLK_8_1("pc_clk72mhz", mux_p_c_c2_clkm, CLK_SOURCE_CLK72MHZ, 0), + CLK_8_1("pc_adx1", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_ADX1, DCF_HAVE_ENA), + CLK_8_1("pc_amx1", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_AMX1, DCF_HAVE_ENA), + CLK_8_1("pc_vic", mux_m_c_p_a_c2_c3_clkm, CLK_SOURCE_VIC, DCF_IS_VIC), +}; + +static int periph_init(struct clknode *clk, device_t dev); +static int periph_recalc(struct clknode *clk, uint64_t *freq); +static int periph_set_freq(struct clknode *clk, uint64_t fin, + uint64_t *fout, int flags, int *stop); +static int periph_set_mux(struct clknode *clk, int idx); + +struct periph_sc { + device_t clkdev; + uint32_t base_reg; + uint32_t div_shift; + uint32_t div_width; + uint32_t div_mask; + uint32_t div_f_width; + uint32_t div_f_mask; + uint32_t flags; + + uint32_t divider; + int mux; +}; + +static clknode_method_t periph_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, periph_init), + CLKNODEMETHOD(clknode_recalc_freq, periph_recalc), + CLKNODEMETHOD(clknode_set_freq, periph_set_freq), + CLKNODEMETHOD(clknode_set_mux, periph_set_mux), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(tegra124_periph, tegra124_periph_class, periph_methods, + sizeof(struct periph_sc), clknode_class); +static int +periph_init(struct clknode *clk, device_t dev) +{ + struct periph_sc *sc; + uint32_t reg; + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + if (sc->flags & DCF_HAVE_ENA) + MD4(sc, sc->base_reg, PERLCK_ENA_MASK, PERLCK_ENA_MASK); + + RD4(sc, sc->base_reg, ®); + DEVICE_UNLOCK(sc); + + /* Stnadard mux. */ + if (sc->flags & DCF_HAVE_MUX) + sc->mux = (reg >> PERLCK_MUX_SHIFT) & PERLCK_MUX_MASK; + else + sc->mux = 0; + if (sc->flags & DCF_HAVE_DIV) + sc->divider = (reg & sc->div_mask) + 2; + else + sc->divider = 1; + if ((sc->flags & DCF_IS_MASK) == DCF_IS_UART) { + if (!(reg & PERLCK_UDIV_DIS)) + sc->divider = 2; + } + + /* AUDIO MUX */ + if ((sc->flags & DCF_IS_MASK) == DCF_IS_AUDIO) { + if (!(reg & PERLCK_AMUX_DIS) && (sc->mux == 7)) { + sc->mux = 8 + + ((reg >> PERLCK_AMUX_SHIFT) & PERLCK_MUX_MASK); + } + } + clknode_init_parent_idx(clk, sc->mux); + return(0); +} + +static int +periph_set_mux(struct clknode *clk, int idx) +{ + struct periph_sc *sc; + uint32_t reg; + + + sc = clknode_get_softc(clk); + if (!(sc->flags & DCF_HAVE_MUX)) + return (ENXIO); + + sc->mux = idx; + DEVICE_LOCK(sc); + RD4(sc, sc->base_reg, ®); + reg &= ~(PERLCK_MUX_MASK << PERLCK_MUX_SHIFT); + if ((sc->flags & DCF_IS_MASK) == DCF_IS_AUDIO) { + reg &= ~PERLCK_AMUX_DIS; + reg &= ~(PERLCK_MUX_MASK << PERLCK_AMUX_SHIFT); + + if (idx <= 7) { + reg |= idx << PERLCK_MUX_SHIFT; + } else { + reg |= 7 << PERLCK_MUX_SHIFT; + reg |= (idx - 8) << PERLCK_AMUX_SHIFT; + } + } else { + reg |= idx << PERLCK_MUX_SHIFT; + } + WR4(sc, sc->base_reg, reg); + DEVICE_UNLOCK(sc); + + return(0); +} + +static int +periph_recalc(struct clknode *clk, uint64_t *freq) +{ + struct periph_sc *sc; + uint32_t reg; + + sc = clknode_get_softc(clk); + + if (sc->flags & DCF_HAVE_DIV) { + DEVICE_LOCK(sc); + RD4(sc, sc->base_reg, ®); + DEVICE_UNLOCK(sc); + *freq = (*freq << sc->div_f_width) / sc->divider; + } + return (0); +} + +static int +periph_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct periph_sc *sc; + uint64_t tmp, divider; + + sc = clknode_get_softc(clk); + if (!(sc->flags & DCF_HAVE_DIV)) { + *stop = 0; + return (0); + } + + tmp = fin << sc->div_f_width; + divider = tmp / *fout; + if ((tmp % *fout) != 0) + divider++; + + if (divider < (1 << sc->div_f_width)) + divider = 1 << sc->div_f_width; + + if ((*stop != 0) && + ((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) && + (*fout != (tmp / divider))) + return (ERANGE); + + if ((flags & CLK_SET_DRYRUN) == 0) { + DEVICE_LOCK(sc); + MD4(sc, sc->base_reg, sc->div_mask, + (divider - (1 << sc->div_f_width))); + DEVICE_UNLOCK(sc); + sc->divider = divider; + } + *fout = tmp / divider; + *stop = 1; + return (0); +} + +static int +periph_register(struct clkdom *clkdom, struct periph_def *clkdef) +{ + struct clknode *clk; + struct periph_sc *sc; + + clk = clknode_create(clkdom, &tegra124_periph_class, &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + sc->clkdev = clknode_get_device(clk); + sc->base_reg = clkdef->base_reg; + sc->div_width = clkdef->div_width; + sc->div_mask = (1 <<clkdef->div_width) - 1; + sc->div_f_width = clkdef->div_f_width; + sc->div_f_mask = (1 <<clkdef->div_f_width) - 1; + sc->flags = clkdef->flags; + + clknode_register(clkdom, clk); + return (0); +} + +/* -------------------------------------------------------------------------- */ +static int pgate_init(struct clknode *clk, device_t dev); +static int pgate_set_gate(struct clknode *clk, bool enable); + +struct pgate_sc { + device_t clkdev; + uint32_t idx; + uint32_t flags; + uint32_t enabled; + +}; + +static clknode_method_t pgate_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, pgate_init), + CLKNODEMETHOD(clknode_set_gate, pgate_set_gate), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(tegra124_pgate, tegra124_pgate_class, pgate_methods, + sizeof(struct pgate_sc), clknode_class); + +static uint32_t +get_enable_reg(int idx) +{ + KASSERT(idx / 32 < nitems(clk_enabale_reg), + ("Invalid clock index for enable: %d", idx)); + return (clk_enabale_reg[idx / 32]); +} + +static uint32_t +get_reset_reg(int idx) +{ + KASSERT(idx / 32 < nitems(clk_reset_reg), + ("Invalid clock index for reset: %d", idx)); + return (clk_reset_reg[idx / 32]); +} + +static int +pgate_init(struct clknode *clk, device_t dev) +{ + struct pgate_sc *sc; + uint32_t ena_reg, rst_reg, mask; + + sc = clknode_get_softc(clk); + mask = 1 << (sc->idx % 32); + + DEVICE_LOCK(sc); + RD4(sc, get_enable_reg(sc->idx), &ena_reg); + RD4(sc, get_reset_reg(sc->idx), &rst_reg); + DEVICE_UNLOCK(sc); + + sc->enabled = ena_reg & mask ? 1 : 0; + clknode_init_parent_idx(clk, 0); + + return(0); +} + +static int +pgate_set_gate(struct clknode *clk, bool enable) +{ + struct pgate_sc *sc; + uint32_t reg, mask, base_reg; + + sc = clknode_get_softc(clk); + mask = 1 << (sc->idx % 32); + sc->enabled = enable; + base_reg = get_enable_reg(sc->idx); + + DEVICE_LOCK(sc); + MD4(sc, base_reg, mask, enable ? mask : 0); + RD4(sc, base_reg, ®); + DEVICE_UNLOCK(sc); + + DELAY(2); + return(0); +} + +int +tegra124_hwreset_by_idx(struct tegra124_car_softc *sc, intptr_t idx, bool reset) +{ + uint32_t reg, mask, reset_reg; + + mask = 1 << (idx % 32); + reset_reg = get_reset_reg(idx); + + CLKDEV_DEVICE_LOCK(sc->dev); + CLKDEV_MODIFY_4(sc->dev, reset_reg, mask, reset ? mask : 0); + CLKDEV_READ_4(sc->dev, reset_reg, ®); + CLKDEV_DEVICE_UNLOCK(sc->dev); + + return(0); +} + +static int +pgate_register(struct clkdom *clkdom, struct pgate_def *clkdef) +{ + struct clknode *clk; + struct pgate_sc *sc; + + clk = clknode_create(clkdom, &tegra124_pgate_class, &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + sc->clkdev = clknode_get_device(clk); + sc->idx = clkdef->idx; + sc->flags = clkdef->flags; + + clknode_register(clkdom, clk); + return (0); +} + +void +tegra124_periph_clock(struct tegra124_car_softc *sc) +{ + int i, rv; + + for (i = 0; i < nitems(periph_def); i++) { + rv = periph_register(sc->clkdom, &periph_def[i]); + if (rv != 0) + panic("tegra124_periph_register failed"); + } + for (i = 0; i < nitems(pgate_def); i++) { + rv = pgate_register(sc->clkdom, &pgate_def[i]); + if (rv != 0) + panic("tegra124_pgate_register failed"); + } + +} diff --git a/sys/arm/nvidia/tegra124/tegra124_clk_pll.c b/sys/arm/nvidia/tegra124/tegra124_clk_pll.c new file mode 100644 index 0000000..6adfd32 --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_clk_pll.c @@ -0,0 +1,1066 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/rman.h> + +#include <machine/bus.h> + +#include <dev/extres/clk/clk.h> + +#include <gnu/dts/include/dt-bindings/clock/tegra124-car.h> +#include "tegra124_car.h" + +/* #define TEGRA_PLL_DEBUG */ +#ifdef TEGRA_PLL_DEBUG +#define dprintf(...) printf(__VA_ARGS__) +#else +#define dprintf(...) +#endif + +/* All PLLs. */ +enum pll_type { + PLL_M, + PLL_X, + PLL_C, + PLL_C2, + PLL_C3, + PLL_C4, + PLL_P, + PLL_A, + PLL_U, + PLL_D, + PLL_D2, + PLL_DP, + PLL_E, + PLL_REFE}; + +/* Common base register bits. */ +#define PLL_BASE_BYPASS (1U << 31) +#define PLL_BASE_ENABLE (1 << 30) +#define PLL_BASE_REFDISABLE (1 << 29) +#define PLL_BASE_LOCK (1 << 27) +#define PLL_BASE_DIVM_SHIFT 0 +#define PLL_BASE_DIVN_SHIFT 8 + +#define PLLRE_MISC_LOCK (1 << 24) + +#define PLL_MISC_LOCK_ENABLE (1 << 18) +#define PLLC_MISC_LOCK_ENABLE (1 << 24) +#define PLLDU_MISC_LOCK_ENABLE (1 << 22) +#define PLLRE_MISC_LOCK_ENABLE (1 << 30) +#define PLLSS_MISC_LOCK_ENABLE (1 << 30) + +#define PLLC_IDDQ_BIT 26 +#define PLLX_IDDQ_BIT 3 +#define PLLRE_IDDQ_BIT 16 +#define PLLSS_IDDQ_BIT 19 + +#define PLL_LOCK_TIMEOUT 1000 + +/* Post divider <-> register value mapping. */ +struct pdiv_table { + uint32_t divider; /* real divider */ + uint32_t value; /* register value */ +}; + +/* Bits definition of M, N and P fields. */ +struct mnp_bits { + uint32_t m_width; + uint32_t n_width; + uint32_t p_width; + uint32_t p_shift; +}; + +struct clk_pll_def { + struct clknode_init_def clkdef; + enum pll_type type; + uint32_t base_reg; + uint32_t misc_reg; + uint32_t lock_mask; + uint32_t lock_enable; + uint32_t iddq_reg; + uint32_t iddq_mask; + uint32_t flags; + struct pdiv_table *pdiv_table; + struct mnp_bits mnp_bits; +}; + +#define PLL(_id, cname, pname) \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = (const char *[]){pname}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS + +/* Tegra K1 PLLs + PLLM: Clock source for EMC 2x clock + PLLX: Clock source for the fast CPU cluster and the shadow CPU + PLLC: Clock source for general use + PLLC2: Clock source for engine scaling + PLLC3: Clock source for engine scaling + PLLC4: Clock source for ISP/VI units + PLLP: Clock source for most peripherals + PLLA: Audio clock sources: (11.2896 MHz, 12.288 MHz, 24.576 MHz) + PLLU: Clock source for USB PHY, provides 12/60/480 MHz + PLLD: Clock sources for the DSI and display subsystem + PLLD2: Clock sources for the DSI and display subsystem + refPLLe: + PLLE: generate the 100 MHz reference clock for USB 3.0 (spread spectrum) + PLLDP: Clock source for eDP/LVDS (spread spectrum) + + DFLLCPU: DFLL clock source for the fast CPU cluster + GPCPLL: Clock source for the GPU +*/ + +static struct pdiv_table pllm_map[] = { + {1, 0}, + {2, 1}, + {0, 0} +}; + +static struct pdiv_table pllxc_map[] = { + { 1, 0}, + { 2, 1}, + { 3, 2}, + { 4, 3}, + { 5, 4}, + { 6, 5}, + { 8, 6}, + {10, 7}, + {12, 8}, + {16, 9}, + {12, 10}, + {16, 11}, + {20, 12}, + {24, 13}, + {32, 14}, + { 0, 0} +}; + +static struct pdiv_table pllc_map[] = { + { 1, 0}, + { 2, 1}, + { 3, 2}, + { 4, 3}, + { 6, 4}, + { 8, 5}, + {12, 6}, + {16, 7}, + { 0, 0} +}; + +static struct pdiv_table pll12g_ssd_esd_map[] = { + { 1, 0}, + { 2, 1}, + { 3, 2}, + { 4, 3}, + { 5, 4}, + { 6, 5}, + { 8, 6}, + {10, 7}, + {12, 8}, + {16, 9}, + {12, 10}, + {16, 11}, + {20, 12}, + {24, 13}, + {32, 14}, + { 0, 0} +}; + +static struct pdiv_table pllu_map[] = { + {1, 1}, + {2, 0}, + {0, 0} +}; + +static struct clk_pll_def pll_clks[] = { +/* PLLM: 880 MHz Clock source for EMC 2x clock */ + { + PLL(TEGRA124_CLK_PLL_M, "pllM_out0", "osc_div_clk"), + .type = PLL_M, + .base_reg = PLLM_BASE, + .misc_reg = PLLM_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLL_MISC_LOCK_ENABLE, + .pdiv_table = pllm_map, + .mnp_bits = {8, 8, 1, 20}, + }, +/* PLLX: 1GHz Clock source for the fast CPU cluster and the shadow CPU */ + { + PLL(TEGRA124_CLK_PLL_X, "pllX_out", "osc_div_clk"), + .type = PLL_X, + .base_reg = PLLX_BASE, + .misc_reg = PLLX_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLL_MISC_LOCK_ENABLE, + .iddq_reg = PLLX_MISC3, + .iddq_mask = 1 << PLLX_IDDQ_BIT, + .pdiv_table = pllxc_map, + .mnp_bits = {8, 8, 4, 20}, + }, +/* PLLC: 600 MHz Clock source for general use */ + { + PLL(TEGRA124_CLK_PLL_C, "pllC_out0", "osc_div_clk"), + .type = PLL_C, + .base_reg = PLLC_BASE, + .misc_reg = PLLC_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLLC_MISC_LOCK_ENABLE, + .iddq_reg = PLLC_MISC, + .iddq_mask = 1 << PLLC_IDDQ_BIT, + .pdiv_table = pllc_map, + .mnp_bits = {8, 8, 4, 20}, + }, +/* PLLC2: 600 MHz Clock source for engine scaling */ + { + PLL(TEGRA124_CLK_PLL_C2, "pllC2_out0", "osc_div_clk"), + .type = PLL_C2, + .base_reg = PLLC2_BASE, + .misc_reg = PLLC2_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLL_MISC_LOCK_ENABLE, + .pdiv_table = pllc_map, + .mnp_bits = {2, 8, 3, 20}, + }, +/* PLLC3: 600 MHz Clock source for engine scaling */ + { + PLL(TEGRA124_CLK_PLL_C3, "pllC3_out0", "osc_div_clk"), + .type = PLL_C3, + .base_reg = PLLC3_BASE, + .misc_reg = PLLC3_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLL_MISC_LOCK_ENABLE, + .pdiv_table = pllc_map, + .mnp_bits = {2, 8, 3, 20}, + }, +/* PLLC4: 600 MHz Clock source for ISP/VI units */ + { + PLL(TEGRA124_CLK_PLL_C4, "pllC4_out0", "pllC4_src"), + .type = PLL_C4, + .base_reg = PLLC4_BASE, + .misc_reg = PLLC4_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLLSS_MISC_LOCK_ENABLE, + .iddq_reg = PLLC4_BASE, + .iddq_mask = 1 << PLLSS_IDDQ_BIT, + .pdiv_table = pll12g_ssd_esd_map, + .mnp_bits = {8, 8, 4, 20}, + }, +/* PLLP: 408 MHz Clock source for most peripherals */ + { + PLL(TEGRA124_CLK_PLL_P, "pllP_out0", "osc_div_clk"), + .type = PLL_P, + .base_reg = PLLP_BASE, + .misc_reg = PLLP_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLL_MISC_LOCK_ENABLE, + .mnp_bits = {5, 10, 3, 20}, + }, +/* PLLA: Audio clock sources: (11.2896 MHz, 12.288 MHz, 24.576 MHz) */ + { + PLL(TEGRA124_CLK_PLL_A, "pllA_out", "pllP_out1"), + .type = PLL_A, + .base_reg = PLLA_BASE, + .misc_reg = PLLA_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLL_MISC_LOCK_ENABLE, + .mnp_bits = {5, 10, 3, 20}, + }, +/* PLLU: 480 MHz Clock source for USB PHY, provides 12/60/480 MHz */ + { + PLL(TEGRA124_CLK_PLL_U, "pllU_out", "osc_div_clk"), + .type = PLL_U, + .base_reg = PLLU_BASE, + .misc_reg = PLLU_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLLDU_MISC_LOCK_ENABLE, + .pdiv_table = pllu_map, + .mnp_bits = {5, 10, 1, 20}, + }, +/* PLLD: 600 MHz Clock sources for the DSI and display subsystem */ + { + PLL(TEGRA124_CLK_PLL_D, "pllD_out", "osc_div_clk"), + .type = PLL_D, + .base_reg = PLLD_BASE, + .misc_reg = PLLD_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLL_MISC_LOCK_ENABLE, + .mnp_bits = {5, 11, 3, 20}, + }, +/* PLLD2: 600 MHz Clock sources for the DSI and display subsystem */ + { + PLL(TEGRA124_CLK_PLL_D2, "pllD2_out", "pllD2_src"), + .type = PLL_D2, + .base_reg = PLLD2_BASE, + .misc_reg = PLLD2_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLLSS_MISC_LOCK_ENABLE, + .iddq_reg = PLLD2_BASE, + .iddq_mask = 1 << PLLSS_IDDQ_BIT, + .pdiv_table = pll12g_ssd_esd_map, + .mnp_bits = {8, 8, 4, 20}, + }, +/* refPLLe: */ + { + PLL(0, "pllREFE_out", "osc_div_clk"), + .type = PLL_REFE, + .base_reg = PLLRE_BASE, + .misc_reg = PLLRE_MISC, + .lock_mask = PLLRE_MISC_LOCK, + .lock_enable = PLLRE_MISC_LOCK_ENABLE, + .iddq_reg = PLLRE_MISC, + .iddq_mask = 1 << PLLRE_IDDQ_BIT, + .mnp_bits = {8, 8, 4, 16}, + }, +/* PLLE: generate the 100 MHz reference clock for USB 3.0 (spread spectrum) */ + { + PLL(TEGRA124_CLK_PLL_E, "pllE_out0", "pllE_src"), + .type = PLL_E, + .base_reg = PLLE_BASE, + .misc_reg = PLLE_MISC, + .lock_mask = PLLE_MISC_LOCK, + .lock_enable = PLLE_MISC_LOCK_ENABLE, + .mnp_bits = {8, 8, 4, 24}, + }, +/* PLLDP: 600 MHz Clock source for eDP/LVDS (spread spectrum) */ + { + PLL(0, "pllDP_out0", "pllDP_src"), + .type = PLL_DP, + .base_reg = PLLDP_BASE, + .misc_reg = PLLDP_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLLSS_MISC_LOCK_ENABLE, + .iddq_reg = PLLDP_BASE, + .iddq_mask = 1 << PLLSS_IDDQ_BIT, + .pdiv_table = pll12g_ssd_esd_map, + .mnp_bits = {8, 8, 4, 20}, + }, +}; + +static int tegra124_pll_init(struct clknode *clk, device_t dev); +static int tegra124_pll_set_gate(struct clknode *clk, bool enable); +static int tegra124_pll_recalc(struct clknode *clk, uint64_t *freq); +static int tegra124_pll_set_freq(struct clknode *clknode, uint64_t fin, + uint64_t *fout, int flags, int *stop); +struct pll_sc { + device_t clkdev; + enum pll_type type; + uint32_t base_reg; + uint32_t misc_reg; + uint32_t lock_mask; + uint32_t lock_enable; + uint32_t iddq_reg; + uint32_t iddq_mask; + uint32_t flags; + struct pdiv_table *pdiv_table; + struct mnp_bits mnp_bits; +}; + +static clknode_method_t tegra124_pll_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, tegra124_pll_init), + CLKNODEMETHOD(clknode_set_gate, tegra124_pll_set_gate), + CLKNODEMETHOD(clknode_recalc_freq, tegra124_pll_recalc), + CLKNODEMETHOD(clknode_set_freq, tegra124_pll_set_freq), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(tegra124_pll, tegra124_pll_class, tegra124_pll_methods, + sizeof(struct pll_sc), clknode_class); + +static int +pll_enable(struct pll_sc *sc) +{ + uint32_t reg; + + + RD4(sc, sc->base_reg, ®); + if (sc->type != PLL_E) + reg &= ~PLL_BASE_BYPASS; + reg |= PLL_BASE_ENABLE; + WR4(sc, sc->base_reg, reg); + return (0); +} + +static int +pll_disable(struct pll_sc *sc) +{ + uint32_t reg; + + RD4(sc, sc->base_reg, ®); + if (sc->type != PLL_E) + reg |= PLL_BASE_BYPASS; + reg &= ~PLL_BASE_ENABLE; + WR4(sc, sc->base_reg, reg); + return (0); +} + +static uint32_t +pdiv_to_reg(struct pll_sc *sc, uint32_t p_div) +{ + struct pdiv_table *tbl; + + tbl = sc->pdiv_table; + if (tbl == NULL) + return (ffs(p_div)); + + while (tbl->divider != 0) { + if (p_div <= tbl->divider) + return (tbl->value); + tbl++; + } + return ~0; +} + +static uint32_t +reg_to_pdiv(struct pll_sc *sc, uint32_t reg) +{ + struct pdiv_table *tbl; + + tbl = sc->pdiv_table; + if (tbl != NULL) { + while (tbl->divider) { + if (reg == tbl->value) + return (tbl->divider); + tbl++; + } + return (0); + } + return (1 << reg); +} + +static uint32_t +get_masked(uint32_t val, uint32_t shift, uint32_t width) +{ + + return ((val >> shift) & ((1 << width) - 1)); +} + +static uint32_t +set_masked(uint32_t val, uint32_t v, uint32_t shift, uint32_t width) +{ + + val &= ~(((1 << width) - 1) << shift); + val |= (v & ((1 << width) - 1)) << shift; + return (val); +} + +static void +get_divisors(struct pll_sc *sc, uint32_t *m, uint32_t *n, uint32_t *p) +{ + uint32_t val; + struct mnp_bits *mnp_bits; + + mnp_bits = &sc->mnp_bits; + RD4(sc, sc->base_reg, &val); + *m = get_masked(val, PLL_BASE_DIVM_SHIFT, mnp_bits->m_width); + *n = get_masked(val, PLL_BASE_DIVN_SHIFT, mnp_bits->n_width); + *p = get_masked(val, mnp_bits->p_shift, mnp_bits->p_width); +} + +static uint32_t +set_divisors(struct pll_sc *sc, uint32_t val, uint32_t m, uint32_t n, + uint32_t p) +{ + struct mnp_bits *mnp_bits; + + mnp_bits = &sc->mnp_bits; + val = set_masked(val, m, PLL_BASE_DIVM_SHIFT, mnp_bits->m_width); + val = set_masked(val, n, PLL_BASE_DIVN_SHIFT, mnp_bits->n_width); + val = set_masked(val, p, mnp_bits->p_shift, mnp_bits->p_width); + return (val); +} + +static bool +is_locked(struct pll_sc *sc) +{ + uint32_t reg; + + switch (sc->type) { + case PLL_REFE: + RD4(sc, sc->misc_reg, ®); + reg &= PLLRE_MISC_LOCK; + break; + + case PLL_E: + RD4(sc, sc->misc_reg, ®); + reg &= PLLE_MISC_LOCK; + break; + + default: + RD4(sc, sc->base_reg, ®); + reg &= PLL_BASE_LOCK; + break; + } + return (reg != 0); +} + +static int +wait_for_lock(struct pll_sc *sc) +{ + int i; + + for (i = PLL_LOCK_TIMEOUT / 10; i > 0; i--) { + if (is_locked(sc)) + break; + DELAY(10); + } + if (i <= 0) { + printf("PLL lock timeout\n"); + return (ETIMEDOUT); + } + return (0); +} + +static int +plle_enable(struct pll_sc *sc) +{ + uint32_t reg; + int rv; + struct mnp_bits *mnp_bits; + uint32_t pll_m = 1; + uint32_t pll_n = 200; + uint32_t pll_p = 13; + uint32_t pll_cml = 13; + + mnp_bits = &sc->mnp_bits; + + + /* Disable lock override. */ + RD4(sc, sc->base_reg, ®); + reg &= ~PLLE_BASE_LOCK_OVERRIDE; + WR4(sc, sc->base_reg, reg); + + RD4(sc, PLLE_AUX, ®); + reg |= PLLE_AUX_ENABLE_SWCTL; + reg &= ~PLLE_AUX_SEQ_ENABLE; + WR4(sc, PLLE_AUX, reg); + DELAY(10); + + RD4(sc, sc->misc_reg, ®); + reg |= PLLE_MISC_LOCK_ENABLE; + reg |= PLLE_MISC_IDDQ_SWCTL; + reg &= ~PLLE_MISC_IDDQ_OVERRIDE_VALUE; + reg |= PLLE_MISC_PTS; + reg |= PLLE_MISC_VREG_BG_CTRL_MASK; + reg |= PLLE_MISC_VREG_CTRL_MASK; + WR4(sc, sc->misc_reg, reg); + DELAY(10); + + RD4(sc, PLLE_SS_CNTL, ®); + reg |= PLLE_SS_CNTL_DISABLE; + WR4(sc, PLLE_SS_CNTL, reg); + + RD4(sc, sc->base_reg, ®); + reg = set_divisors(sc, reg, pll_m, pll_n, pll_p); + reg &= ~(PLLE_BASE_DIVCML_MASK << PLLE_BASE_DIVCML_SHIFT); + reg |= pll_cml << PLLE_BASE_DIVCML_SHIFT; + WR4(sc, sc->base_reg, reg); + DELAY(10); + + pll_enable(sc); + rv = wait_for_lock(sc); + if (rv != 0) + return (rv); + + RD4(sc, PLLE_SS_CNTL, ®); + reg &= ~PLLE_SS_CNTL_SSCCENTER; + reg &= ~PLLE_SS_CNTL_SSCINVERT; + reg &= ~PLLE_SS_CNTL_COEFFICIENTS_MASK; + reg |= PLLE_SS_CNTL_COEFFICIENTS_VAL; + WR4(sc, PLLE_SS_CNTL, reg); + reg &= ~PLLE_SS_CNTL_SSCBYP; + reg &= ~PLLE_SS_CNTL_BYPASS_SS; + WR4(sc, PLLE_SS_CNTL, reg); + DELAY(10); + + reg &= ~PLLE_SS_CNTL_INTERP_RESET; + WR4(sc, PLLE_SS_CNTL, reg); + DELAY(10); + + /* HW control of brick pll. */ + RD4(sc, sc->misc_reg, ®); + reg &= ~PLLE_MISC_IDDQ_SWCTL; + WR4(sc, sc->misc_reg, reg); + + RD4(sc, PLLE_AUX, ®); + reg |= PLLE_AUX_USE_LOCKDET; + reg |= PLLE_AUX_SEQ_START_STATE; + reg &= ~PLLE_AUX_ENABLE_SWCTL; + reg &= ~PLLE_AUX_SS_SWCTL; + WR4(sc, PLLE_AUX, reg); + reg |= PLLE_AUX_SEQ_START_STATE; + DELAY(10); + reg |= PLLE_AUX_SEQ_ENABLE; + WR4(sc, PLLE_AUX, reg); + + RD4(sc, XUSBIO_PLL_CFG0, ®); + reg |= XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET; + reg |= XUSBIO_PLL_CFG0_SEQ_START_STATE; + reg &= ~XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL; + reg &= ~XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL; + WR4(sc, XUSBIO_PLL_CFG0, reg); + DELAY(10); + + reg |= XUSBIO_PLL_CFG0_SEQ_ENABLE; + WR4(sc, XUSBIO_PLL_CFG0, reg); + + + /* Enable HW control and unreset SATA PLL. */ + RD4(sc, SATA_PLL_CFG0, ®); + reg &= ~SATA_PLL_CFG0_PADPLL_RESET_SWCTL; + reg &= ~SATA_PLL_CFG0_PADPLL_RESET_OVERRIDE_VALUE; + reg |= SATA_PLL_CFG0_PADPLL_USE_LOCKDET; + reg &= ~SATA_PLL_CFG0_SEQ_IN_SWCTL; + reg &= ~SATA_PLL_CFG0_SEQ_RESET_INPUT_VALUE; + reg &= ~SATA_PLL_CFG0_SEQ_LANE_PD_INPUT_VALUE; + reg &= ~SATA_PLL_CFG0_SEQ_PADPLL_PD_INPUT_VALUE; + reg &= ~SATA_PLL_CFG0_SEQ_ENABLE; + reg |= SATA_PLL_CFG0_SEQ_START_STATE; + WR4(sc, SATA_PLL_CFG0, reg); + DELAY(10); + reg |= SATA_PLL_CFG0_SEQ_ENABLE; + WR4(sc, SATA_PLL_CFG0, reg); + + /* Enable HW control of PCIe PLL. */ + RD4(sc, PCIE_PLL_CFG0, ®); + reg |= PCIE_PLL_CFG0_SEQ_ENABLE; + WR4(sc, PCIE_PLL_CFG0, reg); + + return (0); +} + +static int +tegra124_pll_set_gate(struct clknode *clknode, bool enable) +{ + int rv; + struct pll_sc *sc; + + sc = clknode_get_softc(clknode); + if (enable == 0) { + rv = pll_disable(sc); + return(rv); + } + + if (sc->type == PLL_E) + rv = plle_enable(sc); + else + rv = pll_enable(sc); + return (rv); +} + +static int +pll_set_std(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags, + uint32_t m, uint32_t n, uint32_t p) +{ + uint32_t reg; + struct mnp_bits *mnp_bits; + int rv; + + mnp_bits = &sc->mnp_bits; + if (m >= (1 << mnp_bits->m_width)) + return (ERANGE); + if (n >= (1 << mnp_bits->n_width)) + return (ERANGE); + if (pdiv_to_reg(sc, p) >= (1 << mnp_bits->p_width)) + return (ERANGE); + + if (flags & CLK_SET_DRYRUN) { + if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) && + (*fout != (((fin / m) * n) /p))) + return (ERANGE); + + *fout = ((fin / m) * n) /p; + return (0); + } + + pll_disable(sc); + + /* take pll out of IDDQ */ + if (sc->iddq_reg != 0) + MD4(sc, sc->iddq_reg, sc->iddq_mask, 0); + + RD4(sc, sc->base_reg, ®); + reg = set_masked(reg, m, PLL_BASE_DIVM_SHIFT, mnp_bits->m_width); + reg = set_masked(reg, n, PLL_BASE_DIVN_SHIFT, mnp_bits->n_width); + reg = set_masked(reg, pdiv_to_reg(sc, p), mnp_bits->p_shift, + mnp_bits->p_width); + WR4(sc, sc->base_reg, reg); + + /* Enable PLL. */ + RD4(sc, sc->base_reg, ®); + reg |= PLL_BASE_ENABLE; + WR4(sc, sc->base_reg, reg); + + /* Enable lock detection. */ + RD4(sc, sc->misc_reg, ®); + reg |= sc->lock_enable; + WR4(sc, sc->misc_reg, reg); + + rv = wait_for_lock(sc); + if (rv != 0) { + /* Disable PLL */ + RD4(sc, sc->base_reg, ®); + reg &= ~PLL_BASE_ENABLE; + WR4(sc, sc->base_reg, reg); + return (rv); + } + RD4(sc, sc->misc_reg, ®); + + pll_enable(sc); + *fout = ((fin / m) * n) / p; + return 0; +} + +static int +plla_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) +{ + uint32_t m, n, p; + + p = 1; + m = 5; + n = (*fout * p * m + fin / 2)/ fin; + dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p); + return (pll_set_std(sc, fin, fout, flags, m, n, p)); +} + +static int +pllc_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) +{ + uint32_t m, n, p; + + + p = 2; + m = 1; + n = (*fout * p * m + fin / 2)/ fin; + dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p); + return (pll_set_std( sc, fin, fout, flags, m, n, p)); +} + +static int +plld2_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) +{ + uint32_t m, n, p; + + p = 2; + m = 1; + n = (*fout * p * m + fin / 2)/ fin; + dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p); + return (pll_set_std(sc, fin, fout, flags, m, n, p)); +} + + + +static int +pllrefe_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) +{ + uint32_t m, n, p; + + m = 1; + p = 1; + n = *fout * p * m / fin; + return (pll_set_std(sc, fin, fout, flags, m, n, p)); +} + +static int +pllx_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) +{ + uint32_t reg; + uint32_t m, n, p; + struct mnp_bits *mnp_bits; + int rv; + + mnp_bits = &sc->mnp_bits; + + p = 1; + m = 1; + n = (*fout * p * m + fin / 2)/ fin; + dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p); + + if (m >= (1 << mnp_bits->m_width)) + return (ERANGE); + if (n >= (1 << mnp_bits->n_width)) + return (ERANGE); + if (pdiv_to_reg(sc, p) >= (1 << mnp_bits->p_width)) + return (ERANGE); + + if (flags & CLK_SET_DRYRUN) { + if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) && + (*fout != (((fin / m) * n) /p))) + return (ERANGE); + *fout = ((fin / m) * n) /p; + return (0); + } + + /* Set bypass. */ + RD4(sc, sc->base_reg, ®); + reg |= PLL_BASE_BYPASS; + WR4(sc, sc->base_reg, reg); + RD4(sc, sc->base_reg, ®); + DELAY(100); + + /* Set PLL. */ + RD4(sc, sc->base_reg, ®); + reg = set_masked(reg, m, PLL_BASE_DIVM_SHIFT, mnp_bits->m_width); + reg = set_masked(reg, n, PLL_BASE_DIVN_SHIFT, mnp_bits->n_width); + reg = set_masked(reg, pdiv_to_reg(sc, p), mnp_bits->p_shift, + mnp_bits->p_width); + WR4(sc, sc->base_reg, reg); + RD4(sc, sc->base_reg, ®); + DELAY(100); + + /* Enable PLL. */ + RD4(sc, sc->base_reg, ®); + reg |= PLL_BASE_ENABLE; + WR4(sc, sc->base_reg, reg); + + /* Enable lock detection */ + RD4(sc, sc->misc_reg, ®); + reg |= sc->lock_enable; + WR4(sc, sc->misc_reg, reg); + + rv = wait_for_lock(sc); + if (rv != 0) { + /* Disable PLL */ + RD4(sc, sc->base_reg, ®); + reg &= ~PLL_BASE_ENABLE; + WR4(sc, sc->base_reg, reg); + return (rv); + } + RD4(sc, sc->misc_reg, ®); + + /* Clear bypass. */ + RD4(sc, sc->base_reg, ®); + reg &= ~PLL_BASE_BYPASS; + WR4(sc, sc->base_reg, reg); + *fout = ((fin / m) * n) / p; + return (0); +} + +static int +tegra124_pll_set_freq(struct clknode *clknode, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + *stop = 1; + int rv; + struct pll_sc *sc; + + sc = clknode_get_softc(clknode); + dprintf("%s: Requested freq: %llu, input freq: %llu\n", __func__, + *fout, fin); + switch (sc->type) { + case PLL_A: + rv = plla_set_freq(sc, fin, fout, flags); + break; + case PLL_C: + rv = pllc_set_freq(sc, fin, fout, flags); + break; + case PLL_D2: + rv = plld2_set_freq(sc, fin, fout, flags); + break; + + case PLL_REFE: + rv = pllrefe_set_freq(sc, fin, fout, flags); + break; + + case PLL_X: + rv = pllx_set_freq(sc, fin, fout, flags); + break; + + case PLL_U: + if (*fout == 480000000) /* PLLU is fixed to 480 MHz */ + rv = 0; + else + rv = ERANGE; + break; + default: + rv = ENXIO; + break; + } + return (rv); +} + + +static int +tegra124_pll_init(struct clknode *clk, device_t dev) +{ + struct pll_sc *sc; + uint32_t reg; + + sc = clknode_get_softc(clk); + + /* If PLL is enabled, enable lock detect too. */ + RD4(sc, sc->base_reg, ®); + if (reg & PLL_BASE_ENABLE) { + RD4(sc, sc->misc_reg, ®); + reg |= sc->lock_enable; + WR4(sc, sc->misc_reg, reg); + } + + clknode_init_parent_idx(clk, 0); + return(0); +} + +static int +tegra124_pll_recalc(struct clknode *clk, uint64_t *freq) +{ + struct pll_sc *sc; + uint32_t m, n, p, pr; + uint32_t reg, misc_reg; + int locked; + + sc = clknode_get_softc(clk); + + RD4(sc, sc->base_reg, ®); + RD4(sc, sc->misc_reg, &misc_reg); + + get_divisors(sc, &m, &n, &pr); + if (sc->type != PLL_E) + p = reg_to_pdiv(sc, pr); + else + p = 2 * (pr - 1); + locked = is_locked(sc); + + dprintf("%s: %s (0x%08x, 0x%08x) - m: %d, n: %d, p: %d (%d): " + "e: %d, r: %d, o: %d - %s\n", __func__, + clknode_get_name(clk), reg, misc_reg, m, n, p, pr, + (reg >> 30) & 1, (reg >> 29) & 1, (reg >> 28) & 1, + locked ? "locked" : "unlocked"); + + if ((m == 0) || (n == 0) || (p == 0)) { + *freq = 0; + return (EINVAL); + } + *freq = ((*freq / m) * n) / p; + return (0); +} + +static int +pll_register(struct clkdom *clkdom, struct clk_pll_def *clkdef) +{ + struct clknode *clk; + struct pll_sc *sc; + + clk = clknode_create(clkdom, &tegra124_pll_class, &clkdef->clkdef); + if (clk == NULL) + return (ENXIO); + + sc = clknode_get_softc(clk); + sc->clkdev = clknode_get_device(clk); + sc->type = clkdef->type; + sc->base_reg = clkdef->base_reg; + sc->misc_reg = clkdef->misc_reg; + sc->lock_mask = clkdef->lock_mask; + sc->lock_enable = clkdef->lock_enable; + sc->iddq_reg = clkdef->iddq_reg; + sc->iddq_mask = clkdef->iddq_mask; + sc->flags = clkdef->flags; + sc->pdiv_table = clkdef->pdiv_table; + sc->mnp_bits = clkdef->mnp_bits; + clknode_register(clkdom, clk); + return (0); +} + +static void config_utmi_pll(struct tegra124_car_softc *sc) +{ + uint32_t reg; + /* + * XXX Simplified UTMIP settings for 12MHz base clock. + */ +#define ENABLE_DELAY_COUNT 0x02 +#define STABLE_COUNT 0x2F +#define ACTIVE_DELAY_COUNT 0x04 +#define XTAL_FREQ_COUNT 0x76 + + CLKDEV_READ_4(sc->dev, UTMIP_PLL_CFG2, ®); + reg &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0); + reg |= UTMIP_PLL_CFG2_STABLE_COUNT(STABLE_COUNT); + reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0); + reg |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(ACTIVE_DELAY_COUNT); + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN; + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN; + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN; + CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG2, reg); + + CLKDEV_READ_4(sc->dev, UTMIP_PLL_CFG1, ®); + reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0); + reg |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(ENABLE_DELAY_COUNT); + reg &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0); + reg |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(XTAL_FREQ_COUNT); + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN; + CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG1, reg); + + /* Prepare UTMIP requencer. */ + CLKDEV_READ_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, ®); + reg |= UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET; + reg &= ~UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL; + reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE; + CLKDEV_WRITE_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, reg); + + /* Powerup UTMIP. */ + CLKDEV_READ_4(sc->dev, UTMIP_PLL_CFG1, ®); + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; + CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG1, reg); + DELAY(10); + + /* SW override for UTMIPLL */ + CLKDEV_READ_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, ®); + reg |= UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL; + reg &= ~UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE; + CLKDEV_WRITE_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, reg); + DELAY(10); + + /* HW control of UTMIPLL. */ + CLKDEV_READ_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, ®); + reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE; + CLKDEV_WRITE_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, reg); +} + +void +tegra124_init_plls(struct tegra124_car_softc *sc) +{ + int i, rv; + + for (i = 0; i < nitems(pll_clks); i++) { + rv = pll_register(sc->clkdom, pll_clks + i); + if (rv != 0) + panic("pll_register failed"); + } + config_utmi_pll(sc); + +} diff --git a/sys/arm/nvidia/tegra124/tegra124_clk_super.c b/sys/arm/nvidia/tegra124/tegra124_clk_super.c new file mode 100644 index 0000000..7f32eba --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_clk_super.c @@ -0,0 +1,265 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/rman.h> + +#include <machine/bus.h> + +#include <dev/extres/clk/clk.h> + +#include <gnu/dts/include/dt-bindings/clock/tegra124-car.h> +#include "tegra124_car.h" + + +/* Flags */ +#define SMF_HAVE_DIVIDER_2 1 + +struct super_mux_def { + struct clknode_init_def clkdef; + uint32_t base_reg; + uint32_t flags; + int src_pllx; + int src_div2; +}; + +#define PLIST(x) static const char *x[] +#define SM(_id, cn, pl, r, x, d, f) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cn, \ + .clkdef.parent_names = pl, \ + .clkdef.parent_cnt = nitems(pl), \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .base_reg = r, \ + .src_pllx = x, \ + .src_div2 = d, \ + .flags = f, \ +} + +PLIST(cclk_g_parents) = { + "clk_m", "pllC_out0", "clk_s", "pllM_out0", + "pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0", + "pllX_out", NULL, NULL, NULL, + NULL, NULL, NULL,NULL, // "dfllCPU_out0" +}; + +PLIST(cclk_lp_parents) = { + "clk_m", "pllC_out0", "clk_s", "pllM_out0", + "pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0", + "pllX_out", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + "pllX_out0" +}; + +PLIST(sclk_parents) = { + "clk_m", "pllC_out1", "pllP_out4", "pllP_out0", + "pllP_out2", "pllC_out0", "clk_s", "pllM_out1", +}; + +static struct super_mux_def super_mux_def[] = { + SM(TEGRA124_CLK_CCLK_G, "cclk_g", cclk_g_parents, CCLKG_BURST_POLICY, 0, 0, 0), + SM(TEGRA124_CLK_CCLK_LP, "cclk_lp", cclk_lp_parents, CCLKLP_BURST_POLICY, 8, 16, SMF_HAVE_DIVIDER_2), + SM(TEGRA124_CLK_SCLK, "sclk", sclk_parents, SCLK_BURST_POLICY, 0, 0, 0), +}; + +static int super_mux_init(struct clknode *clk, device_t dev); +static int super_mux_set_mux(struct clknode *clk, int idx); + +struct super_mux_sc { + device_t clkdev; + uint32_t base_reg; + int src_pllx; + int src_div2; + uint32_t flags; + + int mux; +}; + +static clknode_method_t super_mux_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, super_mux_init), + CLKNODEMETHOD(clknode_set_mux, super_mux_set_mux), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(tegra124_super_mux, tegra124_super_mux_class, super_mux_methods, + sizeof(struct super_mux_sc), clknode_class); + +/* Mux status. */ +#define SUPER_MUX_STATE_STDBY 0 +#define SUPER_MUX_STATE_IDLE 1 +#define SUPER_MUX_STATE_RUN 2 +#define SUPER_MUX_STATE_IRQ 3 +#define SUPER_MUX_STATE_FIQ 4 + +/* Mux register bits. */ +#define SUPER_MUX_STATE_BIT_SHIFT 28 +#define SUPER_MUX_STATE_BIT_MASK 0xF +/* State is Priority encoded */ +#define SUPER_MUX_STATE_BIT_STDBY 0x00 +#define SUPER_MUX_STATE_BIT_IDLE 0x01 +#define SUPER_MUX_STATE_BIT_RUN 0x02 +#define SUPER_MUX_STATE_BIT_IRQ 0x04 +#define SUPER_MUX_STATE_BIT_FIQ 0x08 + +#define SUPER_MUX_MUX_WIDTH 4 +#define SUPER_MUX_LP_DIV2_BYPASS (1 << 16) + +static uint32_t +super_mux_get_state(uint32_t reg) +{ + reg = (reg >> SUPER_MUX_STATE_BIT_SHIFT) & SUPER_MUX_STATE_BIT_MASK; + if (reg & SUPER_MUX_STATE_BIT_FIQ) + return (SUPER_MUX_STATE_FIQ); + if (reg & SUPER_MUX_STATE_BIT_IRQ) + return (SUPER_MUX_STATE_IRQ); + if (reg & SUPER_MUX_STATE_BIT_RUN) + return (SUPER_MUX_STATE_RUN); + if (reg & SUPER_MUX_STATE_BIT_IDLE) + return (SUPER_MUX_STATE_IDLE); + return (SUPER_MUX_STATE_STDBY); +} + +static int +super_mux_init(struct clknode *clk, device_t dev) +{ + struct super_mux_sc *sc; + uint32_t reg; + int shift, state; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + RD4(sc, sc->base_reg, ®); + DEVICE_UNLOCK(sc); + state = super_mux_get_state(reg); + + if ((state != SUPER_MUX_STATE_RUN) && + (state != SUPER_MUX_STATE_IDLE)) { + panic("Unexpected super mux state: %u", state); + } + + shift = state * SUPER_MUX_MUX_WIDTH; + + sc->mux = (reg >> shift) & ((1 << SUPER_MUX_MUX_WIDTH) - 1); + + /* + * CCLKLP uses PLLX/2 as source if LP_DIV2_BYPASS isn't set + * and source mux is set to PLLX. + */ + if (sc->flags & SMF_HAVE_DIVIDER_2) { + if (((reg & SUPER_MUX_LP_DIV2_BYPASS) == 0) && + (sc->mux == sc->src_pllx)) + sc->mux = sc->src_div2; + } + clknode_init_parent_idx(clk, sc->mux); + + return(0); +} + +static int +super_mux_set_mux(struct clknode *clk, int idx) +{ + + struct super_mux_sc *sc; + int shift, state; + uint32_t reg, dummy; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + RD4(sc, sc->base_reg, ®); + state = super_mux_get_state(reg); + + if ((state != SUPER_MUX_STATE_RUN) && + (state != SUPER_MUX_STATE_IDLE)) { + panic("Unexpected super mux state: %u", state); + } + + shift = state * SUPER_MUX_MUX_WIDTH; + sc->mux = idx; + if (sc->flags & SMF_HAVE_DIVIDER_2) { + if (idx == sc->src_div2) { + idx = sc->src_pllx; + reg &= ~SUPER_MUX_LP_DIV2_BYPASS; + WR4(sc, sc->base_reg, reg); + RD4(sc, sc->base_reg, &dummy); + } else if (idx == sc->src_pllx) { + reg = SUPER_MUX_LP_DIV2_BYPASS; + WR4(sc, sc->base_reg, reg); + RD4(sc, sc->base_reg, &dummy); + } + } + reg &= ~(((1 << SUPER_MUX_MUX_WIDTH) - 1) << shift); + reg |= idx << shift; + WR4(sc, sc->base_reg, reg); + RD4(sc, sc->base_reg, &dummy); + DEVICE_UNLOCK(sc); + + return(0); +} + +static int +super_mux_register(struct clkdom *clkdom, struct super_mux_def *clkdef) +{ + struct clknode *clk; + struct super_mux_sc *sc; + + clk = clknode_create(clkdom, &tegra124_super_mux_class, + &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + sc->clkdev = clknode_get_device(clk); + sc->base_reg = clkdef->base_reg; + sc->src_pllx = clkdef->src_pllx; + sc->src_div2 = clkdef->src_div2; + sc->flags = clkdef->flags; + + clknode_register(clkdom, clk); + return (0); +} + +void +tegra124_super_mux_clock(struct tegra124_car_softc *sc) +{ + int i, rv; + + for (i = 0; i < nitems(super_mux_def); i++) { + rv = super_mux_register(sc->clkdom, &super_mux_def[i]); + if (rv != 0) + panic("super_mux_register failed"); + } + +} diff --git a/sys/arm/nvidia/tegra124/tegra124_coretemp.c b/sys/arm/nvidia/tegra124/tegra124_coretemp.c new file mode 100644 index 0000000..c29fe8e --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_coretemp.c @@ -0,0 +1,273 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/cpu.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/sysctl.h> + +#include <machine/bus.h> +#include <machine/cpu.h> + +#include <dev/extres/clk/clk.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include "tegra_soctherm_if.h" + + +enum therm_info { + CORETEMP_TEMP, + CORETEMP_DELTA, + CORETEMP_RESOLUTION, + CORETEMP_TJMAX, +}; + +struct tegra124_coretemp_softc { + device_t dev; + int overheat_log; + int core_max_temp; + int cpu_id; + device_t tsens_dev; + intptr_t tsens_id; +}; + +static int +coretemp_get_val_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev; + int val, temp, rv; + struct tegra124_coretemp_softc *sc; + enum therm_info type; + char stemp[16]; + + + dev = (device_t) arg1; + sc = device_get_softc(dev); + type = arg2; + + + rv = TEGRA_SOCTHERM_GET_TEMPERATURE(sc->tsens_dev, sc->dev, + sc->tsens_id, &temp); + if (rv != 0) { + device_printf(sc->dev, + "Cannot read temperature sensor %d: %d\n", + sc->tsens_id, rv); + return (rv); + } + + switch (type) { + case CORETEMP_TEMP: + val = temp / 100; + val += 2731; + break; + case CORETEMP_DELTA: + val = (sc->core_max_temp - temp) / 1000; + break; + case CORETEMP_RESOLUTION: + val = 1; + break; + case CORETEMP_TJMAX: + val = sc->core_max_temp / 100; + val += 2731; + break; + } + + + if ((temp > sc->core_max_temp) && !sc->overheat_log) { + sc->overheat_log = 1; + + /* + * Check for Critical Temperature Status and Critical + * Temperature Log. It doesn't really matter if the + * current temperature is invalid because the "Critical + * Temperature Log" bit will tell us if the Critical + * Temperature has * been reached in past. It's not + * directly related to the current temperature. + * + * If we reach a critical level, allow devctl(4) + * to catch this and shutdown the system. + */ + device_printf(dev, "critical temperature detected, " + "suggest system shutdown\n"); + snprintf(stemp, sizeof(stemp), "%d", val); + devctl_notify("coretemp", "Thermal", stemp, + "notify=0xcc"); + } else { + sc->overheat_log = 0; + } + + return (sysctl_handle_int(oidp, 0, val, req)); +} + +static int +tegra124_coretemp_ofw_parse(struct tegra124_coretemp_softc *sc) +{ + int rv, ncells; + phandle_t node, xnode; + pcell_t *cells; + + node = OF_peer(0); + node = ofw_bus_find_child(node, "thermal-zones"); + if (node <= 0) { + device_printf(sc->dev, "Cannot find 'thermal-zones'.\n"); + return (ENXIO); + } + + node = ofw_bus_find_child(node, "cpu"); + if (node <= 0) { + device_printf(sc->dev, "Cannot find 'cpu'\n"); + return (ENXIO); + } + rv = ofw_bus_parse_xref_list_alloc(node, "thermal-sensors", + "#thermal-sensor-cells", 0, &xnode, &ncells, &cells); + if (rv != 0) { + device_printf(sc->dev, + "Cannot parse 'thermal-sensors' property.\n"); + return (ENXIO); + } + if (ncells != 1) { + device_printf(sc->dev, + "Invalid format of 'thermal-sensors' property(%d).\n", + ncells); + return (ENXIO); + } + + sc->tsens_id = 0x100 + sc->cpu_id; //cells[0]; + free(cells, M_OFWPROP); + + sc->tsens_dev = OF_device_from_xref(xnode); + if (sc->tsens_dev == NULL) { + device_printf(sc->dev, + "Cannot find thermal sensors device."); + return (ENXIO); + } + return (0); +} + +static void +tegra124_coretemp_identify(driver_t *driver, device_t parent) +{ + + if (device_find_child(parent, "tegra124_coretemp", -1) != NULL) + return; + if (BUS_ADD_CHILD(parent, 0, "tegra124_coretemp", -1) == NULL) + device_printf(parent, "add child failed\n"); +} + +static int +tegra124_coretemp_probe(device_t dev) +{ + + device_set_desc(dev, "CPU Frequency Control"); + return (0); +} + +static int +tegra124_coretemp_attach(device_t dev) +{ + struct tegra124_coretemp_softc *sc; + device_t pdev; + struct sysctl_oid *oid; + struct sysctl_ctx_list *ctx; + int rv; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->cpu_id = device_get_unit(dev); + sc->core_max_temp = 102000; + pdev = device_get_parent(dev); + + rv = tegra124_coretemp_ofw_parse(sc); + if (rv != 0) + return (rv); + + ctx = device_get_sysctl_ctx(dev); + + oid = SYSCTL_ADD_NODE(ctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)), OID_AUTO, + "coretemp", CTLFLAG_RD, NULL, "Per-CPU thermal information"); + + /* + * Add the MIBs to dev.cpu.N and dev.cpu.N.coretemp. + */ + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)), + OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, CORETEMP_TEMP, coretemp_get_val_sysctl, "IK", + "Current temperature"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "delta", + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_DELTA, + coretemp_get_val_sysctl, "I", + "Delta between TCC activation and current temperature"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "resolution", + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_RESOLUTION, + coretemp_get_val_sysctl, "I", + "Resolution of CPU thermal sensor"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "tjmax", + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_TJMAX, + coretemp_get_val_sysctl, "IK", + "TCC activation temperature"); + + return (0); +} + +static int +tegra124_coretemp_detach(device_t dev) +{ + struct tegra124_coretemp_softc *sc; + + sc = device_get_softc(dev); + return (0); +} + + +static device_method_t tegra124_coretemp_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, tegra124_coretemp_identify), + DEVMETHOD(device_probe, tegra124_coretemp_probe), + DEVMETHOD(device_attach, tegra124_coretemp_attach), + DEVMETHOD(device_detach, tegra124_coretemp_detach), + + + DEVMETHOD_END +}; + +static devclass_t tegra124_coretemp_devclass; +static driver_t tegra124_coretemp_driver = { + "tegra124_coretemp", + tegra124_coretemp_methods, + sizeof(struct tegra124_coretemp_softc), +}; + +DRIVER_MODULE(tegra124_coretemp, cpu, tegra124_coretemp_driver, + tegra124_coretemp_devclass, 0, 0); diff --git a/sys/arm/nvidia/tegra124/tegra124_cpufreq.c b/sys/arm/nvidia/tegra124/tegra124_cpufreq.c new file mode 100644 index 0000000..1227c81 --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_cpufreq.c @@ -0,0 +1,583 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/cpu.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> + +#include <machine/bus.h> +#include <machine/cpu.h> + +#include <dev/extres/clk/clk.h> +#include <dev/extres/regulator/regulator.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <arm/nvidia/tegra_efuse.h> + +#include "cpufreq_if.h" + +#define XXX + +/* CPU voltage table entry */ +struct speedo_entry { + uint64_t freq; /* Frequency point */ + int c0; /* Coeeficient values for */ + int c1; /* quadratic equation: */ + int c2; /* c2 * speedo^2 + c1 * speedo + c0 */ +}; + +struct cpu_volt_def { + int min_uvolt; /* Min allowed CPU voltage */ + int max_uvolt; /* Max allowed CPU voltage */ + int step_uvolt; /* Step of CPU voltage */ + int speedo_scale; /* Scaling factor for cvt */ + int speedo_nitems; /* Size of speedo table */ + struct speedo_entry *speedo_tbl; /* CPU voltage table */ +}; + +struct cpu_speed_point { + uint64_t freq; /* Frequecy */ + int uvolt; /* Requested voltage */ +}; + +static struct speedo_entry tegra124_speedo_dpll_tbl[] = +{ + { 204000000ULL, 1112619, -29295, 402}, + { 306000000ULL, 1150460, -30585, 402}, + { 408000000ULL, 1190122, -31865, 402}, + { 510000000ULL, 1231606, -33155, 402}, + { 612000000ULL, 1274912, -34435, 402}, + { 714000000ULL, 1320040, -35725, 402}, + { 816000000ULL, 1366990, -37005, 402}, + { 918000000ULL, 1415762, -38295, 402}, + {1020000000ULL, 1466355, -39575, 402}, + {1122000000ULL, 1518771, -40865, 402}, + {1224000000ULL, 1573009, -42145, 402}, + {1326000000ULL, 1629068, -43435, 402}, + {1428000000ULL, 1686950, -44715, 402}, + {1530000000ULL, 1746653, -46005, 402}, + {1632000000ULL, 1808179, -47285, 402}, + {1734000000ULL, 1871526, -48575, 402}, + {1836000000ULL, 1936696, -49855, 402}, + {1938000000ULL, 2003687, -51145, 402}, + {2014500000ULL, 2054787, -52095, 402}, + {2116500000ULL, 2124957, -53385, 402}, + {2218500000ULL, 2196950, -54665, 402}, + {2320500000ULL, 2270765, -55955, 402}, + {2320500000ULL, 2270765, -55955, 402}, + {2422500000ULL, 2346401, -57235, 402}, + {2524500000ULL, 2437299, -58535, 402}, +}; + +static struct cpu_volt_def tegra124_cpu_volt_dpll_def = +{ + .min_uvolt = 900000, /* 0.9 V */ + .max_uvolt = 1260000, /* 1.26 */ + .step_uvolt = 10000, /* 10 mV */ + .speedo_scale = 100, + .speedo_nitems = nitems(tegra124_speedo_dpll_tbl), + .speedo_tbl = tegra124_speedo_dpll_tbl, +}; + +static struct speedo_entry tegra124_speedo_pllx_tbl[] = +{ + { 204000000ULL, 800000, 0, 0}, + { 306000000ULL, 800000, 0, 0}, + { 408000000ULL, 800000, 0, 0}, + { 510000000ULL, 800000, 0, 0}, + { 612000000ULL, 800000, 0, 0}, + { 714000000ULL, 800000, 0, 0}, + { 816000000ULL, 820000, 0, 0}, + { 918000000ULL, 840000, 0, 0}, + {1020000000ULL, 880000, 0, 0}, + {1122000000ULL, 900000, 0, 0}, + {1224000000ULL, 930000, 0, 0}, + {1326000000ULL, 960000, 0, 0}, + {1428000000ULL, 990000, 0, 0}, + {1530000000ULL, 1020000, 0, 0}, + {1632000000ULL, 1070000, 0, 0}, + {1734000000ULL, 1100000, 0, 0}, + {1836000000ULL, 1140000, 0, 0}, + {1938000000ULL, 1180000, 0, 0}, + {2014500000ULL, 1220000, 0, 0}, + {2116500000ULL, 1260000, 0, 0}, + {2218500000ULL, 1310000, 0, 0}, + {2320500000ULL, 1360000, 0, 0}, + {2397000000ULL, 1400000, 0, 0}, + {2499000000ULL, 1400000, 0, 0}, +}; + + +static struct cpu_volt_def tegra124_cpu_volt_pllx_def = +{ + .min_uvolt = 900000, /* 0.9 V */ + .max_uvolt = 1260000, /* 1.26 */ + .step_uvolt = 10000, /* 10 mV */ + .speedo_scale = 100, + .speedo_nitems = nitems(tegra124_speedo_pllx_tbl), + .speedo_tbl = tegra124_speedo_pllx_tbl, +}; + +static uint64_t cpu_freq_tbl[] = { + 204000000ULL, + 306000000ULL, + 408000000ULL, + 510000000ULL, + 612000000ULL, + 714000000ULL, + 816000000ULL, + 918000000ULL, + 1020000000ULL, + 1122000000ULL, + 1224000000ULL, + 1326000000ULL, + 1428000000ULL, + 1530000000ULL, + 1632000000ULL, + 1734000000ULL, + 1836000000ULL, + 1938000000ULL, + 2014000000ULL, + 2116000000ULL, + 2218000000ULL, + 2320000000ULL, + 2320000000ULL, + 2422000000ULL, + 2524000000ULL, +}; + +static uint64_t cpu_max_freq[] = { + 2014500000ULL, + 2320500000ULL, + 2116500000ULL, + 2524500000ULL, +}; + +struct tegra124_cpufreq_softc { + device_t dev; + phandle_t node; + + regulator_t supply_vdd_cpu; + clk_t clk_cpu_g; + clk_t clk_cpu_lp; + clk_t clk_pll_x; + clk_t clk_pll_p; + clk_t clk_dfll; + + int process_id; + int speedo_id; + int speedo_value; + + uint64_t cpu_max_freq; + struct cpu_volt_def *cpu_def; + struct cpu_speed_point *speed_points; + int nspeed_points; + + struct cpu_speed_point *act_speed_point; + + int latency; +}; + +static int cpufreq_lowest_freq = 1; +TUNABLE_INT("hw.tegra124.cpufreq.lowest_freq", &cpufreq_lowest_freq); + +#define DIV_ROUND_CLOSEST(val, div) (((val) + ((div) / 2)) / (div)) + +#define ROUND_UP(val, div) ((((val) + ((div) - 1)) / (div)) * (div)) +#define ROUND_DOWN(val, div) (((val) / (div)) * (div)) + +/* + * Compute requesetd voltage for given frequency and SoC process variations, + * - compute base voltage from speedo value using speedo table + * - round up voltage to next regulator step + * - clamp it to regulator limits + */ +static int +freq_to_voltage(struct tegra124_cpufreq_softc *sc, uint64_t freq) +{ + int uv, scale, min_uvolt, max_uvolt, step_uvolt; + struct speedo_entry *ent; + int i; + + /* Get speedo entry with higher frequency */ + ent = NULL; + for (i = 0; i < sc->cpu_def->speedo_nitems; i++) { + if (sc->cpu_def->speedo_tbl[i].freq >= freq) { + ent = &sc->cpu_def->speedo_tbl[i]; + break; + } + } + if (ent == NULL) + ent = &sc->cpu_def->speedo_tbl[sc->cpu_def->speedo_nitems - 1]; + scale = sc->cpu_def->speedo_scale; + + + /* uV = (c2 * speedo / scale + c1) * speedo / scale + c0) */ + uv = DIV_ROUND_CLOSEST(ent->c2 * sc->speedo_value, scale); + uv = DIV_ROUND_CLOSEST((uv + ent->c1) * sc->speedo_value, scale) + + ent->c0; + step_uvolt = sc->cpu_def->step_uvolt; + /* Round up it to next regulator step */ + uv = ROUND_UP(uv, step_uvolt); + + /* Clamp result */ + min_uvolt = ROUND_UP(sc->cpu_def->min_uvolt, step_uvolt); + max_uvolt = ROUND_DOWN(sc->cpu_def->max_uvolt, step_uvolt); + if (uv < min_uvolt) + uv = min_uvolt; + if (uv > max_uvolt) + uv = max_uvolt; + return (uv); + +} + +static void +build_speed_points(struct tegra124_cpufreq_softc *sc) { + int i; + + sc->nspeed_points = nitems(cpu_freq_tbl); + sc->speed_points = malloc(sizeof(struct cpu_speed_point) * + sc->nspeed_points, M_DEVBUF, M_NOWAIT); + for (i = 0; i < sc->nspeed_points; i++) { + sc->speed_points[i].freq = cpu_freq_tbl[i]; + sc->speed_points[i].uvolt = freq_to_voltage(sc, + cpu_freq_tbl[i]); + } +} + +static struct cpu_speed_point * +get_speed_point(struct tegra124_cpufreq_softc *sc, uint64_t freq) +{ + int i; + + if (sc->speed_points[0].freq >= freq) + return (sc->speed_points + 0); + + for (i = 0; i < sc->nspeed_points - 1; i++) { + if (sc->speed_points[i + 1].freq > freq) + return (sc->speed_points + i); + } + + return (sc->speed_points + sc->nspeed_points - 1); +} + +static int +tegra124_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count) +{ + struct tegra124_cpufreq_softc *sc; + int i, j, max_cnt; + + if (sets == NULL || count == NULL) + return (EINVAL); + + sc = device_get_softc(dev); + memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count)); + + max_cnt = min(sc->nspeed_points, *count); + for (i = 0, j = sc->nspeed_points - 1; j >= 0; j--) { + if (sc->cpu_max_freq < sc->speed_points[j].freq) + continue; + sets[i].freq = sc->speed_points[j].freq / 1000000; + sets[i].volts = sc->speed_points[j].uvolt / 1000; + sets[i].lat = sc->latency; + sets[i].dev = dev; + i++; + } + *count = i; + + return (0); +} + +static int +set_cpu_freq(struct tegra124_cpufreq_softc *sc, uint64_t freq) +{ + struct cpu_speed_point *point; + int rv; + + point = get_speed_point(sc, freq); + + if (sc->act_speed_point->uvolt < point->uvolt) { + /* set cpu voltage */ + rv = regulator_set_voltage(sc->supply_vdd_cpu, + point->uvolt, point->uvolt); + DELAY(10000); + if (rv != 0) + return (rv); + } + rv = clk_set_freq(sc->clk_cpu_g, point->freq, CLK_SET_ROUND_DOWN); + if (rv != 0) { + device_printf(sc->dev, "Can't set CPU clock frequency\n"); + return (rv); + } + + if (sc->act_speed_point->uvolt > point->uvolt) { + /* set cpu voltage */ + rv = regulator_set_voltage(sc->supply_vdd_cpu, + point->uvolt, point->uvolt); + if (rv != 0) + return (rv); + } + + sc->act_speed_point = point; + + return (0); +} + +static int +tegra124_cpufreq_set(device_t dev, const struct cf_setting *cf) +{ + struct tegra124_cpufreq_softc *sc; + uint64_t freq; + int rv; + + if (cf == NULL || cf->freq < 0) + return (EINVAL); + + sc = device_get_softc(dev); + + freq = cf->freq; + if (freq < cpufreq_lowest_freq) + freq = cpufreq_lowest_freq; + freq *= 1000000; + if (freq >= sc->cpu_max_freq) + freq = sc->cpu_max_freq; + rv = set_cpu_freq(sc, freq); + + return (rv); +} + +static int +tegra124_cpufreq_get(device_t dev, struct cf_setting *cf) +{ + struct tegra124_cpufreq_softc *sc; + + if (cf == NULL) + return (EINVAL); + + sc = device_get_softc(dev); + memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf)); + cf->dev = NULL; + cf->freq = sc->act_speed_point->freq / 1000000; + cf->volts = sc->act_speed_point->uvolt / 1000; + /* Transition latency in us. */ + cf->lat = sc->latency; + /* Driver providing this setting. */ + cf->dev = dev; + + return (0); +} + + +static int +tegra124_cpufreq_type(device_t dev, int *type) +{ + + if (type == NULL) + return (EINVAL); + *type = CPUFREQ_TYPE_ABSOLUTE; + + return (0); +} + +static int +get_fdt_resources(struct tegra124_cpufreq_softc *sc, phandle_t node) +{ + int rv; + device_t parent_dev; + + parent_dev = device_get_parent(sc->dev); + rv = regulator_get_by_ofw_property(parent_dev, "vdd-cpu-supply", + &sc->supply_vdd_cpu); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'vdd-cpu' regulator\n"); + return (rv); + } + + rv = clk_get_by_ofw_name(parent_dev, "cpu_g", &sc->clk_cpu_g); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'cpu_g' clock: %d\n", rv); + return (ENXIO); + } + + rv = clk_get_by_ofw_name(parent_dev, "cpu_lp", &sc->clk_cpu_lp); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'cpu_lp' clock\n"); + return (ENXIO); + } + + rv = clk_get_by_ofw_name(parent_dev, "pll_x", &sc->clk_pll_x); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pll_x' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(parent_dev, "pll_p", &sc->clk_pll_p); + if (rv != 0) { + device_printf(parent_dev, "Cannot get 'pll_p' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(parent_dev, "dfll", &sc->clk_dfll); + if (rv != 0) { + /* XXX DPLL is not implemented yet */ +/* + device_printf(sc->dev, "Cannot get 'dfll' clock\n"); + return (ENXIO); +*/ + } + return (0); +} + +static void +tegra124_cpufreq_identify(driver_t *driver, device_t parent) +{ + + if (device_find_child(parent, "tegra124_cpufreq", -1) != NULL) + return; + if (BUS_ADD_CHILD(parent, 0, "tegra124_cpufreq", -1) == NULL) + device_printf(parent, "add child failed\n"); +} + +static int +tegra124_cpufreq_probe(device_t dev) +{ + + if (device_get_unit(dev) != 0) + return (ENXIO); + device_set_desc(dev, "CPU Frequency Control"); + + return (0); +} + +static int +tegra124_cpufreq_attach(device_t dev) +{ + struct tegra124_cpufreq_softc *sc; + uint64_t freq; + int rv; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->node = ofw_bus_get_node(device_get_parent(dev)); + + sc->process_id = tegra_sku_info.cpu_process_id; + sc->speedo_id = tegra_sku_info.cpu_speedo_id; + sc->speedo_value = tegra_sku_info.cpu_speedo_value; + + /* Tegra 124 */ + /* XXX DPLL is not implemented yet */ + if (1) + sc->cpu_def = &tegra124_cpu_volt_pllx_def; + else + sc->cpu_def = &tegra124_cpu_volt_dpll_def; + + + rv = get_fdt_resources(sc, sc->node); + if (rv != 0) { + return (rv); + } + + build_speed_points(sc); + + rv = clk_get_freq(sc->clk_cpu_g, &freq); + if (rv != 0) { + device_printf(dev, "Can't get CPU clock frequency\n"); + return (rv); + } + if (sc->speedo_id < nitems(cpu_max_freq)) + sc->cpu_max_freq = cpu_max_freq[sc->speedo_id]; + else + sc->cpu_max_freq = cpu_max_freq[0]; + sc->act_speed_point = get_speed_point(sc, freq); + + /* Set safe startup CPU frequency. */ + rv = set_cpu_freq(sc, 1632000000); + if (rv != 0) { + device_printf(dev, "Can't set initial CPU clock frequency\n"); + return (rv); + } + + /* This device is controlled by cpufreq(4). */ + cpufreq_register(dev); + + return (0); +} + +static int +tegra124_cpufreq_detach(device_t dev) +{ + struct tegra124_cpufreq_softc *sc; + + sc = device_get_softc(dev); + cpufreq_unregister(dev); + + if (sc->supply_vdd_cpu != NULL) + regulator_release(sc->supply_vdd_cpu); + + if (sc->clk_cpu_g != NULL) + clk_release(sc->clk_cpu_g); + if (sc->clk_cpu_lp != NULL) + clk_release(sc->clk_cpu_lp); + if (sc->clk_pll_x != NULL) + clk_release(sc->clk_pll_x); + if (sc->clk_pll_p != NULL) + clk_release(sc->clk_pll_p); + if (sc->clk_dfll != NULL) + clk_release(sc->clk_dfll); + return (0); +} + +static device_method_t tegra124_cpufreq_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, tegra124_cpufreq_identify), + DEVMETHOD(device_probe, tegra124_cpufreq_probe), + DEVMETHOD(device_attach, tegra124_cpufreq_attach), + DEVMETHOD(device_detach, tegra124_cpufreq_detach), + + /* cpufreq interface */ + DEVMETHOD(cpufreq_drv_set, tegra124_cpufreq_set), + DEVMETHOD(cpufreq_drv_get, tegra124_cpufreq_get), + DEVMETHOD(cpufreq_drv_settings, tegra124_cpufreq_settings), + DEVMETHOD(cpufreq_drv_type, tegra124_cpufreq_type), + + DEVMETHOD_END +}; + +static devclass_t tegra124_cpufreq_devclass; +static driver_t tegra124_cpufreq_driver = { + "tegra124_cpufreq", + tegra124_cpufreq_methods, + sizeof(struct tegra124_cpufreq_softc), +}; + +DRIVER_MODULE(tegra124_cpufreq, cpu, tegra124_cpufreq_driver, + tegra124_cpufreq_devclass, 0, 0); diff --git a/sys/arm/nvidia/tegra124/tegra124_machdep.c b/sys/arm/nvidia/tegra124/tegra124_machdep.c new file mode 100644 index 0000000..e74a05b --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_machdep.c @@ -0,0 +1,173 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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. + */ + +#define _ARM32_BUS_DMA_PRIVATE +#include "opt_platform.h" + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/reboot.h> + +#include <vm/vm.h> + +#include <machine/bus.h> +#include <machine/devmap.h> +#include <machine/fdt.h> +#include <machine/intr.h> +#include <machine/machdep.h> +#include <machine/platformvar.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/openfirm.h> + +#include <arm/nvidia/tegra124/tegra124_mp.h> + +#include "platform_if.h" + +#define PMC_PHYSBASE 0x7000e400 +#define PMC_SIZE 0x400 +#define PMC_CONTROL_REG 0x0 +#define PMC_SCRATCH0 0x50 +#define PMC_SCRATCH0_MODE_RECOVERY (1 << 31) +#define PMC_SCRATCH0_MODE_BOOTLOADER (1 << 30) +#define PMC_SCRATCH0_MODE_RCM (1 << 1) +#define PMC_SCRATCH0_MODE_MASK (PMC_SCRATCH0_MODE_RECOVERY | \ + PMC_SCRATCH0_MODE_BOOTLOADER | \ + PMC_SCRATCH0_MODE_RCM) + +struct fdt_fixup_entry fdt_fixup_table[] = { + { NULL, NULL } +}; + +struct arm32_dma_range * +bus_dma_get_range(void) +{ + + return (NULL); +} + +int +bus_dma_get_range_nb(void) +{ + + return (0); +} + +static vm_offset_t +tegra124_lastaddr(platform_t plat) +{ + + return (arm_devmap_lastaddr()); +} + +static int +tegra124_attach(platform_t plat) +{ + + return (0); +} + +static void +tegra124_late_init(platform_t plat) +{ + +} + +/* + * Set up static device mappings. + * + */ +static int +tegra124_devmap_init(platform_t plat) +{ + + arm_devmap_add_entry(0x70000000, 0x01000000); + return (0); +} + +void +cpu_reset(void) +{ + bus_space_handle_t pmc; + uint32_t reg; + + printf("Resetting...\n"); + bus_space_map(fdtbus_bs_tag, PMC_PHYSBASE, PMC_SIZE, 0, &pmc); + + reg = bus_space_read_4(fdtbus_bs_tag, pmc, PMC_SCRATCH0); + reg &= PMC_SCRATCH0_MODE_MASK; + bus_space_write_4(fdtbus_bs_tag, pmc, PMC_SCRATCH0, + reg | PMC_SCRATCH0_MODE_BOOTLOADER); /* boot to bootloader */ + bus_space_read_4(fdtbus_bs_tag, pmc, PMC_SCRATCH0); + + reg = bus_space_read_4(fdtbus_bs_tag, pmc, PMC_CONTROL_REG); + spinlock_enter(); + dsb(); + bus_space_write_4(fdtbus_bs_tag, pmc, PMC_CONTROL_REG, reg | 0x10); + bus_space_read_4(fdtbus_bs_tag, pmc, PMC_CONTROL_REG); + while(1) + ; + +} + +/* + * Early putc routine for EARLY_PRINTF support. To use, add to kernel config: + * option SOCDEV_PA=0x02000000 + * option SOCDEV_VA=0x02000000 + * option EARLY_PRINTF + */ +#if 0 +static void +tegra124_early_putc(int c) +{ + volatile uint32_t * UART_STAT_REG = (uint32_t *)0x02020098; + volatile uint32_t * UART_TX_REG = (uint32_t *)0x02020040; + const uint32_t UART_TXRDY = (1 << 3); + + while ((*UART_STAT_REG & UART_TXRDY) == 0) + continue; + *UART_TX_REG = c; +} +early_putc_t *early_putc = tegra124_early_putc; +#endif + +static platform_method_t tegra124_methods[] = { + PLATFORMMETHOD(platform_attach, tegra124_attach), + PLATFORMMETHOD(platform_lastaddr, tegra124_lastaddr), + PLATFORMMETHOD(platform_devmap_init, tegra124_devmap_init), + PLATFORMMETHOD(platform_late_init, tegra124_late_init), +#ifdef SMP + PLATFORMMETHOD(platform_mp_start_ap, tegra124_mp_start_ap), + PLATFORMMETHOD(platform_mp_setmaxid, tegra124_mp_setmaxid), +#endif + PLATFORMMETHOD_END, +}; + +FDT_PLATFORM_DEF(tegra124, "Nvidia Jetson-TK1", 0, "nvidia,jetson-tk1"); diff --git a/sys/arm/nvidia/tegra124/tegra124_mp.c b/sys/arm/nvidia/tegra124/tegra124_mp.c new file mode 100644 index 0000000..36b3b7c --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_mp.c @@ -0,0 +1,127 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/smp.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <machine/cpu.h> +#include <machine/intr.h> +#include <machine/fdt.h> +#include <machine/smp.h> +#include <machine/platformvar.h> +#include <machine/pmap.h> + +#include <arm/nvidia/tegra124/tegra124_mp.h> + +#define PMC_PHYSBASE 0x7000e400 +#define PMC_SIZE 0x400 +#define PMC_CONTROL_REG 0x0 +#define PMC_PWRGATE_TOGGLE 0x30 +#define PCM_PWRGATE_TOGGLE_START (1 << 8) +#define PMC_PWRGATE_STATUS 0x38 + +#define TEGRA_EXCEPTION_VECTORS_BASE 0x6000F000 /* exception vectors */ +#define TEGRA_EXCEPTION_VECTORS_SIZE 1024 +#define TEGRA_EXCEPTION_VECTOR_ENTRY 0x100 + +void +tegra124_mp_setmaxid(platform_t plat) +{ + int ncpu; + + /* If we've already set the global vars don't bother to do it again. */ + if (mp_ncpus != 0) + return; + + /* Read current CP15 Cache Size ID Register */ + ncpu = cp15_l2ctlr_get(); + ncpu = CPUV7_L2CTLR_NPROC(ncpu); + + mp_ncpus = ncpu; + mp_maxid = ncpu - 1; +} + +void +tegra124_mp_start_ap(platform_t plat) +{ + bus_space_handle_t pmc; + bus_space_handle_t exvec; + int i; + uint32_t val; + uint32_t mask; + + if (bus_space_map(fdtbus_bs_tag, PMC_PHYSBASE, PMC_SIZE, 0, &pmc) != 0) + panic("Couldn't map the PMC\n"); + if (bus_space_map(fdtbus_bs_tag, TEGRA_EXCEPTION_VECTORS_BASE, + TEGRA_EXCEPTION_VECTORS_SIZE, 0, &exvec) != 0) + panic("Couldn't map the exception vectors\n"); + + bus_space_write_4(fdtbus_bs_tag, exvec , TEGRA_EXCEPTION_VECTOR_ENTRY, + pmap_kextract((vm_offset_t)mpentry)); + bus_space_read_4(fdtbus_bs_tag, exvec , TEGRA_EXCEPTION_VECTOR_ENTRY); + + + /* Wait until POWERGATE is ready (max 20 APB cycles). */ + do { + val = bus_space_read_4(fdtbus_bs_tag, pmc, + PMC_PWRGATE_TOGGLE); + } while ((val & PCM_PWRGATE_TOGGLE_START) != 0); + + for (i = 1; i < mp_ncpus; i++) { + val = bus_space_read_4(fdtbus_bs_tag, pmc, PMC_PWRGATE_STATUS); + mask = 1 << (i + 8); /* cpu mask */ + if ((val & mask) == 0) { + /* Wait until POWERGATE is ready (max 20 APB cycles). */ + do { + val = bus_space_read_4(fdtbus_bs_tag, pmc, + PMC_PWRGATE_TOGGLE); + } while ((val & PCM_PWRGATE_TOGGLE_START) != 0); + bus_space_write_4(fdtbus_bs_tag, pmc, + PMC_PWRGATE_TOGGLE, + PCM_PWRGATE_TOGGLE_START | (8 + i)); + + /* Wait until CPU is powered */ + do { + val = bus_space_read_4(fdtbus_bs_tag, pmc, + PMC_PWRGATE_STATUS); + } while ((val & mask) == 0); + } + + } + armv7_sev(); + bus_space_unmap(fdtbus_bs_tag, pmc, PMC_SIZE); + bus_space_unmap(fdtbus_bs_tag, exvec, TEGRA_EXCEPTION_VECTORS_SIZE); +} diff --git a/sys/compat/cloudabi/cloudabi_syscalldefs.h b/sys/arm/nvidia/tegra124/tegra124_mp.h index fc86eff..57785aa 100644 --- a/sys/compat/cloudabi/cloudabi_syscalldefs.h +++ b/sys/arm/nvidia/tegra124/tegra124_mp.h @@ -1,5 +1,6 @@ /*- - * Copyright (c) 2015 Nuxi, https://nuxi.nl/ + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,24 +26,10 @@ * $FreeBSD$ */ -#ifndef _CLOUDABI_SYSCALLDEFS_H_ -#define _CLOUDABI_SYSCALLDEFS_H_ +#ifndef _TEGRA124_MP_H_ +#define _TEGRA124_MP_H_ -#ifdef _KERNEL -#include <sys/types.h> -#include <sys/stdint.h> +void tegra124_mp_setmaxid(platform_t plat); +void tegra124_mp_start_ap(platform_t plat); -#define alignas _Alignas -#define alignof _Alignof -#define static_assert _Static_assert -#else -#include <assert.h> -#include <stdalign.h> -#include <stddef.h> -#include <stdint.h> -#endif - -/* Import machine-independent CloudABI definitions. */ -#include <contrib/cloudabi/syscalldefs_mi.h> - -#endif +#endif /*_TEGRA124_MP_H_*/
\ No newline at end of file diff --git a/sys/arm/nvidia/tegra124/tegra124_pmc.c b/sys/arm/nvidia/tegra124/tegra124_pmc.c new file mode 100644 index 0000000..7ff7307 --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_pmc.c @@ -0,0 +1,566 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/malloc.h> +#include <sys/rman.h> + +#include <machine/bus.h> + +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <arm/nvidia/tegra_pmc.h> + +#define PMC_CNTRL 0x000 +#define PMC_CNTRL_CPUPWRGOOD_SEL_MASK (0x3 << 20) +#define PMC_CNTRL_CPUPWRGOOD_SEL_SHIFT 20 +#define PMC_CNTRL_CPUPWRGOOD_EN (1 << 19) +#define PMC_CNTRL_FUSE_OVERRIDE (1 << 18) +#define PMC_CNTRL_INTR_POLARITY (1 << 17) +#define PMC_CNTRL_CPU_PWRREQ_OE (1 << 16) +#define PMC_CNTRL_CPU_PWRREQ_POLARITY (1 << 15) +#define PMC_CNTRL_SIDE_EFFECT_LP0 (1 << 14) +#define PMC_CNTRL_AOINIT (1 << 13) +#define PMC_CNTRL_PWRGATE_DIS (1 << 12) +#define PMC_CNTRL_SYSCLK_OE (1 << 11) +#define PMC_CNTRL_SYSCLK_POLARITY (1 << 10) +#define PMC_CNTRL_PWRREQ_OE (1 << 9) +#define PMC_CNTRL_PWRREQ_POLARITY (1 << 8) +#define PMC_CNTRL_BLINK_EN (1 << 7) +#define PMC_CNTRL_GLITCHDET_DIS (1 << 6) +#define PMC_CNTRL_LATCHWAKE_EN (1 << 5) +#define PMC_CNTRL_MAIN_RST (1 << 4) +#define PMC_CNTRL_KBC_RST (1 << 3) +#define PMC_CNTRL_RTC_RST (1 << 2) +#define PMC_CNTRL_RTC_CLK_DIS (1 << 1) +#define PMC_CNTRL_KBC_CLK_DIS (1 << 0) + +#define PMC_DPD_SAMPLE 0x020 + +#define PMC_CLAMP_STATUS 0x02C +#define PMC_CLAMP_STATUS_PARTID(x) (1 << ((x) & 0x1F)) + +#define PMC_PWRGATE_TOGGLE 0x030 +#define PMC_PWRGATE_TOGGLE_START (1 << 8) +#define PMC_PWRGATE_TOGGLE_PARTID(x) (((x) & 0x1F) << 0) + +#define PMC_REMOVE_CLAMPING_CMD 0x034 +#define PMC_REMOVE_CLAMPING_CMD_PARTID(x) (1 << ((x) & 0x1F)) + +#define PMC_PWRGATE_STATUS 0x038 +#define PMC_PWRGATE_STATUS_PARTID(x) (1 << ((x) & 0x1F)) + +#define PMC_SCRATCH0 0x050 +#define PMC_SCRATCH0_MODE_RECOVERY (1 << 31) +#define PMC_SCRATCH0_MODE_BOOTLOADER (1 << 30) +#define PMC_SCRATCH0_MODE_RCM (1 << 1) +#define PMC_SCRATCH0_MODE_MASK (PMC_SCRATCH0_MODE_RECOVERY | \ + PMC_SCRATCH0_MODE_BOOTLOADER | \ + PMC_SCRATCH0_MODE_RCM) + +#define PMC_CPUPWRGOOD_TIMER 0x0c8 +#define PMC_CPUPWROFF_TIMER 0x0cc + +#define PMC_SCRATCH41 0x140 + +#define PMC_SENSOR_CTRL 0x1b0 +#define PMC_SENSOR_CTRL_BLOCK_SCRATCH_WRITE (1 << 2) +#define PMC_SENSOR_CTRL_ENABLE_RST (1 << 1) +#define PMC_SENSOR_CTRL_ENABLE_PG (1 << 0) + +#define PMC_IO_DPD_REQ 0x1b8 +#define PMC_IO_DPD_REQ_CODE_IDLE (0 << 30) +#define PMC_IO_DPD_REQ_CODE_OFF (1 << 30) +#define PMC_IO_DPD_REQ_CODE_ON (2 << 30) +#define PMC_IO_DPD_REQ_CODE_MASK (3 << 30) + +#define PMC_IO_DPD_STATUS 0x1bc +#define PMC_IO_DPD_STATUS_HDMI (1 << 28) +#define PMC_IO_DPD2_REQ 0x1c0 +#define PMC_IO_DPD2_STATUS 0x1c4 +#define PMC_IO_DPD2_STATUS_HV (1 << 6) +#define PMC_SEL_DPD_TIM 0x1c8 + +#define PMC_SCRATCH54 0x258 +#define PMC_SCRATCH54_DATA_SHIFT 8 +#define PMC_SCRATCH54_ADDR_SHIFT 0 + +#define PMC_SCRATCH55 0x25c +#define PMC_SCRATCH55_RST_ENABLE (1 << 31) +#define PMC_SCRATCH55_CNTRL_TYPE (1 << 30) +#define PMC_SCRATCH55_CNTRL_ID_SHIFT 27 +#define PMC_SCRATCH55_CNTRL_ID_MASK 0x07 +#define PMC_SCRATCH55_PINMUX_SHIFT 24 +#define PMC_SCRATCH55_PINMUX_MASK 0x07 +#define PMC_SCRATCH55_CHECKSUM_SHIFT 16 +#define PMC_SCRATCH55_CHECKSUM_MASK 0xFF +#define PMC_SCRATCH55_16BITOP (1 << 15) +#define PMC_SCRATCH55_I2CSLV1_SHIFT 0 +#define PMC_SCRATCH55_I2CSLV1_MASK 0x7F + +#define PMC_GPU_RG_CNTRL 0x2d4 + +#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) + +#define PMC_LOCK(_sc) mtx_lock(&(_sc)->mtx) +#define PMC_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) +#define PMC_LOCK_INIT(_sc) mtx_init(&(_sc)->mtx, \ + device_get_nameunit(_sc->dev), "tegra124_pmc", MTX_DEF) +#define PMC_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->mtx); +#define PMC_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED); +#define PMC_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED); + +struct tegra124_pmc_softc { + device_t dev; + struct resource *mem_res; + clk_t clk; + struct mtx mtx; + + uint32_t rate; + enum tegra_suspend_mode suspend_mode; + uint32_t cpu_good_time; + uint32_t cpu_off_time; + uint32_t core_osc_time; + uint32_t core_pmu_time; + uint32_t core_off_time; + int corereq_high; + int sysclkreq_high; + int combined_req; + int cpu_pwr_good_en; + uint32_t lp0_vec_phys; + uint32_t lp0_vec_size; +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-pmc", 1}, + {NULL, 0}, +}; + +static struct tegra124_pmc_softc *pmc_sc; + +static inline struct tegra124_pmc_softc * +tegra124_pmc_get_sc(void) +{ + if (pmc_sc == NULL) + panic("To early call to Tegra PMC driver.\n"); + return (pmc_sc); +} + +static int +tegra124_pmc_set_powergate(struct tegra124_pmc_softc *sc, + enum tegra_powergate_id id, int ena) +{ + uint32_t reg; + int i; + + PMC_LOCK(sc); + + reg = RD4(sc, PMC_PWRGATE_STATUS) & PMC_PWRGATE_STATUS_PARTID(id); + if (((reg != 0) && ena) || ((reg == 0) && !ena)) { + PMC_UNLOCK(sc); + return (0); + } + + for (i = 100; i > 0; i--) { + reg = RD4(sc, PMC_PWRGATE_TOGGLE); + if ((reg & PMC_PWRGATE_TOGGLE_START) == 0) + break; + DELAY(1); + } + if (i <= 0) + device_printf(sc->dev, + "Timeout when waiting for TOGGLE_START\n"); + + WR4(sc, PMC_PWRGATE_TOGGLE, + PMC_PWRGATE_TOGGLE_START | PMC_PWRGATE_TOGGLE_PARTID(id)); + + for (i = 100; i > 0; i--) { + reg = RD4(sc, PMC_PWRGATE_TOGGLE); + if ((reg & PMC_PWRGATE_TOGGLE_START) == 0) + break; + DELAY(1); + } + if (i <= 0) + device_printf(sc->dev, + "Timeout when waiting for TOGGLE_START\n"); + PMC_UNLOCK(sc); + return (0); +} + +int +tegra_powergate_remove_clamping(enum tegra_powergate_id id) +{ + struct tegra124_pmc_softc *sc; + uint32_t reg; + enum tegra_powergate_id swid; + int i; + + sc = tegra124_pmc_get_sc(); + + if (id == TEGRA_POWERGATE_3D) { + WR4(sc, PMC_GPU_RG_CNTRL, 0); + return (0); + } + + reg = RD4(sc, PMC_PWRGATE_STATUS); + if ((reg & PMC_PWRGATE_STATUS_PARTID(id)) == 0) + panic("Attempt to remove clamping for unpowered partition.\n"); + + if (id == TEGRA_POWERGATE_PCX) + swid = TEGRA_POWERGATE_VDE; + else if (id == TEGRA_POWERGATE_VDE) + swid = TEGRA_POWERGATE_PCX; + else + swid = id; + WR4(sc, PMC_REMOVE_CLAMPING_CMD, PMC_REMOVE_CLAMPING_CMD_PARTID(swid)); + + for (i = 100; i > 0; i--) { + reg = RD4(sc, PMC_REMOVE_CLAMPING_CMD); + if ((reg & PMC_REMOVE_CLAMPING_CMD_PARTID(swid)) == 0) + break; + DELAY(1); + } + if (i <= 0) + device_printf(sc->dev, "Timeout when remove clamping\n"); + + reg = RD4(sc, PMC_CLAMP_STATUS); + if ((reg & PMC_CLAMP_STATUS_PARTID(id)) != 0) + panic("Cannot remove clamping\n"); + + return (0); +} + +int +tegra_powergate_is_powered(enum tegra_powergate_id id) +{ + struct tegra124_pmc_softc *sc; + uint32_t reg; + + sc = tegra124_pmc_get_sc(); + + reg = RD4(sc, PMC_PWRGATE_STATUS); + return ((reg & PMC_PWRGATE_STATUS_PARTID(id)) ? 1 : 0); +} + +int +tegra_powergate_power_on(enum tegra_powergate_id id) +{ + struct tegra124_pmc_softc *sc; + int rv, i; + + sc = tegra124_pmc_get_sc(); + + rv = tegra124_pmc_set_powergate(sc, id, 1); + if (rv != 0) { + device_printf(sc->dev, "Cannot set powergate: %d\n", id); + return (rv); + } + + for (i = 100; i > 0; i--) { + if (tegra_powergate_is_powered(id)) + break; + DELAY(1); + } + if (i <= 0) + device_printf(sc->dev, "Timeout when waiting on power up\n"); + + return (rv); +} + +int +tegra_powergate_power_off(enum tegra_powergate_id id) +{ + struct tegra124_pmc_softc *sc; + int rv, i; + + sc = tegra124_pmc_get_sc(); + + rv = tegra124_pmc_set_powergate(sc, id, 0); + if (rv != 0) { + device_printf(sc->dev, "Cannot set powergate: %d\n", id); + return (rv); + } + for (i = 100; i > 0; i--) { + if (!tegra_powergate_is_powered(id)) + break; + DELAY(1); + } + if (i <= 0) + device_printf(sc->dev, "Timeout when waiting on power off\n"); + + return (rv); +} + +int +tegra_powergate_sequence_power_up(enum tegra_powergate_id id, clk_t clk, + hwreset_t rst) +{ + struct tegra124_pmc_softc *sc; + int rv; + + sc = tegra124_pmc_get_sc(); + + rv = hwreset_assert(rst); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert reset\n"); + return (rv); + } + + rv = clk_stop(clk); + if (rv != 0) { + device_printf(sc->dev, "Cannot stop clock\n"); + goto clk_fail; + } + + rv = tegra_powergate_power_on(id); + if (rv != 0) { + device_printf(sc->dev, "Cannot power on powergate\n"); + goto clk_fail; + } + + rv = clk_enable(clk); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable clock\n"); + goto clk_fail; + } + DELAY(20); + + rv = tegra_powergate_remove_clamping(id); + if (rv != 0) { + device_printf(sc->dev, "Cannot remove clamping\n"); + goto fail; + } + rv = hwreset_deassert(rst); + if (rv != 0) { + device_printf(sc->dev, "Cannot unreset reset\n"); + goto fail; + } + return 0; + +fail: + clk_disable(clk); +clk_fail: + hwreset_assert(rst); + tegra_powergate_power_off(id); + return (rv); +} + +static int +tegra124_pmc_parse_fdt(struct tegra124_pmc_softc *sc, phandle_t node) +{ + int rv; + uint32_t tmp; + uint32_t tmparr[2]; + + rv = OF_getencprop(node, "nvidia,suspend-mode", &tmp, sizeof(tmp)); + if (rv > 0) { + switch (tmp) { + case 0: + sc->suspend_mode = TEGRA_SUSPEND_LP0; + break; + + case 1: + sc->suspend_mode = TEGRA_SUSPEND_LP1; + break; + + case 2: + sc->suspend_mode = TEGRA_SUSPEND_LP2; + break; + + default: + sc->suspend_mode = TEGRA_SUSPEND_NONE; + break; + } + } + + rv = OF_getencprop(node, "nvidia,cpu-pwr-good-time", &tmp, sizeof(tmp)); + if (rv > 0) { + sc->cpu_good_time = tmp; + sc->suspend_mode = TEGRA_SUSPEND_NONE; + } + + rv = OF_getencprop(node, "nvidia,cpu-pwr-off-time", &tmp, sizeof(tmp)); + if (rv > 0) { + sc->cpu_off_time = tmp; + sc->suspend_mode = TEGRA_SUSPEND_NONE; + } + + rv = OF_getencprop(node, "nvidia,core-pwr-good-time", tmparr, + sizeof(tmparr)); + if (rv == sizeof(tmparr)) { + sc->core_osc_time = tmparr[0]; + sc->core_pmu_time = tmparr[1]; + sc->suspend_mode = TEGRA_SUSPEND_NONE; + } + + rv = OF_getencprop(node, "nvidia,core-pwr-off-time", &tmp, sizeof(tmp)); + if (rv > 0) { + sc->core_off_time = tmp; + sc->suspend_mode = TEGRA_SUSPEND_NONE; + } + + sc->corereq_high = + OF_hasprop(node, "nvidia,core-power-req-active-high"); + sc->sysclkreq_high = + OF_hasprop(node, "nvidia,sys-clock-req-active-high"); + sc->combined_req = + OF_hasprop(node, "nvidia,combined-power-req"); + sc->cpu_pwr_good_en = + OF_hasprop(node, "nvidia,cpu-pwr-good-en"); + + rv = OF_getencprop(node, "nvidia,lp0-vec", tmparr, sizeof(tmparr)); + if (rv == sizeof(tmparr)) { + + sc->lp0_vec_phys = tmparr[0]; + sc->core_pmu_time = tmparr[1]; + sc->lp0_vec_size = TEGRA_SUSPEND_NONE; + if (sc->suspend_mode == TEGRA_SUSPEND_LP0) + sc->suspend_mode = TEGRA_SUSPEND_LP1; + } + return 0; +} + +static int +tegra124_pmc_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + + device_set_desc(dev, "Tegra PMC"); + return (BUS_PROBE_DEFAULT); +} + +static int +tegra124_pmc_detach(device_t dev) +{ + + /* This device is always present. */ + return (EBUSY); +} + +static int +tegra124_pmc_attach(device_t dev) +{ + struct tegra124_pmc_softc *sc; + int rid, rv; + uint32_t reg; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + rv = tegra124_pmc_parse_fdt(sc, node); + if (rv != 0) { + device_printf(sc->dev, "Cannot parse FDT data\n"); + return (rv); + } + + rv = clk_get_by_ofw_name(sc->dev, "pclk", &sc->clk); + if (rv != 0) { + device_printf(sc->dev, "Cannot get \"pclk\" clock\n"); + return (ENXIO); + } + + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + PMC_LOCK_INIT(sc); + + /* Enable CPU power request. */ + reg = RD4(sc, PMC_CNTRL); + reg |= PMC_CNTRL_CPU_PWRREQ_OE; + WR4(sc, PMC_CNTRL, reg); + + /* Set sysclk output polarity */ + reg = RD4(sc, PMC_CNTRL); + if (sc->sysclkreq_high) + reg &= ~PMC_CNTRL_SYSCLK_POLARITY; + else + reg |= PMC_CNTRL_SYSCLK_POLARITY; + WR4(sc, PMC_CNTRL, reg); + + /* Enable sysclk request. */ + reg = RD4(sc, PMC_CNTRL); + reg |= PMC_CNTRL_SYSCLK_OE; + WR4(sc, PMC_CNTRL, reg); + + /* + * Remove HDMI from deep power down mode. + * XXX mote this to HDMI driver + */ + reg = RD4(sc, PMC_IO_DPD_STATUS); + reg &= ~ PMC_IO_DPD_STATUS_HDMI; + WR4(sc, PMC_IO_DPD_STATUS, reg); + + reg = RD4(sc, PMC_IO_DPD2_STATUS); + reg &= ~ PMC_IO_DPD2_STATUS_HV; + WR4(sc, PMC_IO_DPD2_STATUS, reg); + + if (pmc_sc != NULL) + panic("tegra124_pmc: double driver attach"); + pmc_sc = sc; + return (0); +} + +static device_method_t tegra124_pmc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra124_pmc_probe), + DEVMETHOD(device_attach, tegra124_pmc_attach), + DEVMETHOD(device_detach, tegra124_pmc_detach), + + DEVMETHOD_END +}; + +static driver_t tegra124_pmc_driver = { + "tegra124_pmc", + tegra124_pmc_methods, + sizeof(struct tegra124_pmc_softc), +}; + +static devclass_t tegra124_pmc_devclass; +EARLY_DRIVER_MODULE(tegra124_pmc, simplebus, tegra124_pmc_driver, + tegra124_pmc_devclass, 0, 0, 70); diff --git a/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c b/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c new file mode 100644 index 0000000..e06d4bf --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c @@ -0,0 +1,603 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/malloc.h> +#include <sys/rman.h> + +#include <machine/bus.h> +#include <machine/fdt.h> + +#include <dev/extres/hwreset/hwreset.h> +#include <dev/extres/phy/phy.h> +#include <dev/fdt/fdt_common.h> +#include <dev/fdt/fdt_pinctrl.h> +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <gnu/dts/include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h> + +#include "phy_if.h" + +#define XUSB_PADCTL_USB2_PAD_MUX 0x004 + +#define XUSB_PADCTL_ELPG_PROGRAM 0x01C +#define ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 26) +#define ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 25) +#define ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN (1 << 24) + +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040 +#define IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19) +#define IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK (0xf<< 12) +#define IOPHY_PLL_P0_CTL1_PLL_RST (1 << 1) + +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2 0x044 +#define IOPHY_PLL_P0_CTL2_REFCLKBUF_EN (1 << 6) +#define IOPHY_PLL_P0_CTL2_TXCLKREF_EN (1 << 5) +#define IOPHY_PLL_P0_CTL2_TXCLKREF_SEL (1 << 4) + + +#define XUSB_PADCTL_USB3_PAD_MUX 0x134 + +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1 0x138 +#define IOPHY_PLL_S0_CTL1_PLL1_LOCKDET (1 << 27) +#define IOPHY_PLL_S0_CTL1_PLL1_MODE (1 << 24) +#define IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD (1 << 3) +#define IOPHY_PLL_S0_CTL1_PLL_RST_L (1 << 1) +#define IOPHY_PLL_S0_CTL1_PLL_IDDQ (1 << 0) + +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2 0x13C +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL3 0x140 +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL4 0x144 + +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1 0x148 +#define IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD (1 << 1) +#define IOPHY_MISC_PAD_S0_CTL1_IDDQ (1 << 0) + +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 0x14C +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL3 0x150 +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL4 0x154 +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 0x158 +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 0x15C + +struct lane_cfg { + char *function; + char **lanes; + int iddq; +}; + +struct xusbpadctl_softc { + device_t dev; + struct resource *mem_res; + hwreset_t rst; + int phy_ena_cnt; +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-xusb-padctl", 1}, + {NULL, 0}, +}; + +struct padctl_lane { + const char *name; + bus_size_t reg; + uint32_t shift; + uint32_t mask; + int iddq; + char **mux; + int nmux; +}; + +static char *otg_mux[] = {"snps", "xusb", "uart", "rsvd"}; +static char *usb_mux[] = {"snps", "xusb"}; +static char *pci_mux[] = {"pcie", "usb3", "sata", "rsvd"}; + +#define LANE(n, r, s, m, i, mx) \ +{ \ + .name = n, \ + .reg = r, \ + .shift = s, \ + .mask = m, \ + .iddq = i, \ + .mux = mx, \ + .nmux = nitems(mx), \ +} + +static const struct padctl_lane lanes_tbl[] = { + LANE("otg-0", XUSB_PADCTL_USB2_PAD_MUX, 0, 0x3, -1, otg_mux), + LANE("otg-1", XUSB_PADCTL_USB2_PAD_MUX, 2, 0x3, -1, otg_mux), + LANE("otg-2", XUSB_PADCTL_USB2_PAD_MUX, 4, 0x3, -1, otg_mux), + LANE("ulpi-0", XUSB_PADCTL_USB2_PAD_MUX, 12, 0x1, -1, usb_mux), + LANE("hsic-0", XUSB_PADCTL_USB2_PAD_MUX, 14, 0x1, -1, usb_mux), + LANE("hsic-1", XUSB_PADCTL_USB2_PAD_MUX, 15, 0x1, -1, usb_mux), + LANE("pcie-0", XUSB_PADCTL_USB3_PAD_MUX, 16, 0x3, 1, pci_mux), + LANE("pcie-1", XUSB_PADCTL_USB3_PAD_MUX, 18, 0x3, 2, pci_mux), + LANE("pcie-2", XUSB_PADCTL_USB3_PAD_MUX, 20, 0x3, 3, pci_mux), + LANE("pcie-3", XUSB_PADCTL_USB3_PAD_MUX, 22, 0x3, 4, pci_mux), + LANE("pcie-4", XUSB_PADCTL_USB3_PAD_MUX, 24, 0x3, 5, pci_mux), + LANE("sata-0", XUSB_PADCTL_USB3_PAD_MUX, 26, 0x3, 6, pci_mux), +}; + +static int +xusbpadctl_mux_function(const struct padctl_lane *lane, char *fnc_name) +{ + int i; + + for (i = 0; i < lane->nmux; i++) { + if (strcmp(fnc_name, lane->mux[i]) == 0) + return (i); + } + + return (-1); +} + +static int +xusbpadctl_config_lane(struct xusbpadctl_softc *sc, char *lane_name, + const struct padctl_lane *lane, struct lane_cfg *cfg) +{ + + int tmp; + uint32_t reg; + + reg = bus_read_4(sc->mem_res, lane->reg); + if (cfg->function != NULL) { + tmp = xusbpadctl_mux_function(lane, cfg->function); + if (tmp == -1) { + device_printf(sc->dev, + "Unknown function %s for lane %s\n", cfg->function, + lane_name); + return (EINVAL); + } + reg &= ~(lane->mask << lane->shift); + reg |= (tmp & lane->mask) << lane->shift; + } + if (cfg->iddq != -1) { + if (lane->iddq == -1) { + device_printf(sc->dev, "Invalid IDDQ for lane %s\n", + lane_name); + return (EINVAL); + } + if (cfg->iddq != 0) + reg &= ~(1 << lane->iddq); + else + reg |= 1 << lane->iddq; + } + + bus_write_4(sc->mem_res, lane->reg, reg); + return (0); +} + +static const struct padctl_lane * +xusbpadctl_search_lane(char *lane_name) +{ + int i; + + for (i = 0; i < nitems(lanes_tbl); i++) { + if (strcmp(lane_name, lanes_tbl[i].name) == 0) + return (&lanes_tbl[i]); + } + + return (NULL); +} + +static int +xusbpadctl_config_node(struct xusbpadctl_softc *sc, char *lane_name, + struct lane_cfg *cfg) +{ + const struct padctl_lane *lane; + int rv; + + lane = xusbpadctl_search_lane(lane_name); + if (lane == NULL) { + device_printf(sc->dev, "Unknown lane: %s\n", lane_name); + return (ENXIO); + } + rv = xusbpadctl_config_lane(sc, lane_name, lane, cfg); + return (rv); +} + +static int +xusbpadctl_read_node(struct xusbpadctl_softc *sc, phandle_t node, + struct lane_cfg *cfg, char **lanes, int *llanes) +{ + int rv; + + *llanes = OF_getprop_alloc(node, "nvidia,lanes", 1, (void **)lanes); + if (*llanes <= 0) + return (ENOENT); + + /* Read function (mux) settings. */ + rv = OF_getprop_alloc(node, "nvidia,function", 1, + (void **)&cfg->function); + if (rv <= 0) + cfg->function = NULL; + /* Read numeric properties. */ + rv = OF_getencprop(node, "nvidia,iddq", &cfg->iddq, + sizeof(cfg->iddq)); + if (rv <= 0) + cfg->iddq = -1; + return (0); +} + +static int +xusbpadctl_process_node(struct xusbpadctl_softc *sc, phandle_t node) +{ + struct lane_cfg cfg; + char *lanes, *lname; + int i, len, llanes, rv; + + rv = xusbpadctl_read_node(sc, node, &cfg, &lanes, &llanes); + if (rv != 0) + return (rv); + + len = 0; + lname = lanes; + do { + i = strlen(lname) + 1; + rv = xusbpadctl_config_node(sc, lname, &cfg); + if (rv != 0) + device_printf(sc->dev, + "Cannot configure lane: %s: %d\n", lname, rv); + + len += i; + lname += i; + } while (len < llanes); + + if (lanes != NULL) + free(lanes, M_OFWPROP); + if (cfg.function != NULL) + free(cfg.function, M_OFWPROP); + return (rv); +} + + +static int +xusbpadctl_pinctrl_cfg(device_t dev, phandle_t cfgxref) +{ + struct xusbpadctl_softc *sc; + phandle_t node, cfgnode; + int rv; + + sc = device_get_softc(dev); + cfgnode = OF_node_from_xref(cfgxref); + + rv = 0; + for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) { + if (!fdt_is_enabled(node)) + continue; + rv = xusbpadctl_process_node(sc, node); + if (rv != 0) + return (rv); + } + + return (rv); +} + +static int +xusbpadctl_phy_pcie_powerup(struct xusbpadctl_softc *sc) +{ + uint32_t reg; + int i; + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + reg &= ~IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL2); + reg |= IOPHY_PLL_P0_CTL2_REFCLKBUF_EN; + reg |= IOPHY_PLL_P0_CTL2_TXCLKREF_EN; + reg |= IOPHY_PLL_P0_CTL2_TXCLKREF_SEL; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL2, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + reg |= IOPHY_PLL_P0_CTL1_PLL_RST; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); + DELAY(100); + + for (i = 0; i < 100; i++) { + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + if (reg & IOPHY_PLL_P0_CTL1_PLL0_LOCKDET) + return (0); + DELAY(10); + } + + return (ETIMEDOUT); +} + + +static int +xusbpadctl_phy_pcie_powerdown(struct xusbpadctl_softc *sc) +{ + uint32_t reg; + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + reg &= ~IOPHY_PLL_P0_CTL1_PLL_RST; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); + DELAY(100); + return (0); + +} + +static int +xusbpadctl_phy_sata_powerup(struct xusbpadctl_softc *sc) +{ + uint32_t reg; + int i; + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); + reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD; + reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg &= ~IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD; + reg &= ~IOPHY_PLL_S0_CTL1_PLL_IDDQ; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg |= IOPHY_PLL_S0_CTL1_PLL1_MODE; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + + for (i = 100; i >= 0; i--) { + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + if (reg & IOPHY_PLL_S0_CTL1_PLL1_LOCKDET) + break; + DELAY(100); + } + if (i <= 0) { + device_printf(sc->dev, "Failed to power up SATA phy\n"); + return (ETIMEDOUT); + } + + return (0); +} + +static int +xusbpadctl_phy_sata_powerdown(struct xusbpadctl_softc *sc) +{ + uint32_t reg; + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg &= ~IOPHY_PLL_S0_CTL1_PLL_RST_L; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg &= ~IOPHY_PLL_S0_CTL1_PLL1_MODE; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg |= IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD; + reg |= IOPHY_PLL_S0_CTL1_PLL_IDDQ; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); + reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD; + reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg); + DELAY(100); + + return (0); +} + +static int +xusbpadctl_phy_powerup(struct xusbpadctl_softc *sc) +{ + uint32_t reg; + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); + reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN; + bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); + reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY; + bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); + reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN; + bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + return (0); +} + +static int +xusbpadctl_phy_powerdown(struct xusbpadctl_softc *sc) +{ + uint32_t reg; + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); + reg |= ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN; + bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); + reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY; + bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); + reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN; + bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + return (0); +} + +static int +xusbpadctl_phy_enable(device_t dev, intptr_t id, bool enable) +{ + struct xusbpadctl_softc *sc; + int rv; + + sc = device_get_softc(dev); + + if ((id != TEGRA_XUSB_PADCTL_PCIE) && + (id != TEGRA_XUSB_PADCTL_SATA)) { + device_printf(dev, "Unknown phy: %d\n", id); + return (ENXIO); + } + + rv = 0; + if (enable) { + if (sc->phy_ena_cnt == 0) { + rv = xusbpadctl_phy_powerup(sc); + if (rv != 0) + return (rv); + } + sc->phy_ena_cnt++; + } + + if (id == TEGRA_XUSB_PADCTL_PCIE) { + if (enable) + rv = xusbpadctl_phy_pcie_powerup(sc); + else + rv = xusbpadctl_phy_pcie_powerdown(sc); + if (rv != 0) + return (rv); + } else if (id == TEGRA_XUSB_PADCTL_SATA) { + if (enable) + rv = xusbpadctl_phy_sata_powerup(sc); + else + rv = xusbpadctl_phy_sata_powerdown(sc); + if (rv != 0) + return (rv); + } + if (!enable) { + if (sc->phy_ena_cnt == 1) { + rv = xusbpadctl_phy_powerdown(sc); + if (rv != 0) + return (rv); + } + sc->phy_ena_cnt--; + } + + return (0); +} + +static int +xusbpadctl_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + + device_set_desc(dev, "Tegra XUSB phy"); + return (BUS_PROBE_DEFAULT); +} + +static int +xusbpadctl_detach(device_t dev) +{ + + /* This device is always present. */ + return (EBUSY); +} + +static int +xusbpadctl_attach(device_t dev) +{ + struct xusbpadctl_softc * sc; + int rid, rv; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + node = ofw_bus_get_node(dev); + rv = hwreset_get_by_ofw_name(dev, "padctl", &sc->rst); + if (rv != 0) { + device_printf(dev, "Cannot get 'padctl' reset: %d\n", rv); + return (rv); + } + rv = hwreset_deassert(sc->rst); + if (rv != 0) { + device_printf(dev, "Cannot unreset 'padctl' reset: %d\n", rv); + return (rv); + } + + /* Register as a pinctrl device and use default configuration */ + fdt_pinctrl_register(dev, NULL); + fdt_pinctrl_configure_by_name(dev, "default"); + phy_register_provider(dev); + + return (0); +} + + +static device_method_t tegra_xusbpadctl_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, xusbpadctl_probe), + DEVMETHOD(device_attach, xusbpadctl_attach), + DEVMETHOD(device_detach, xusbpadctl_detach), + + /* fdt_pinctrl interface */ + DEVMETHOD(fdt_pinctrl_configure, xusbpadctl_pinctrl_cfg), + + /* phy interface */ + DEVMETHOD(phy_enable, xusbpadctl_phy_enable), + + DEVMETHOD_END +}; + +static driver_t tegra_xusbpadctl_driver = { + "tegra_xusbpadctl", + tegra_xusbpadctl_methods, + sizeof(struct xusbpadctl_softc), +}; + +static devclass_t tegra_xusbpadctl_devclass; + +EARLY_DRIVER_MODULE(tegra_xusbpadctl, simplebus, tegra_xusbpadctl_driver, + tegra_xusbpadctl_devclass, 0, 0, 73); diff --git a/sys/arm/nvidia/tegra_abpmisc.c b/sys/arm/nvidia/tegra_abpmisc.c new file mode 100644 index 0000000..83ee44f --- /dev/null +++ b/sys/arm/nvidia/tegra_abpmisc.c @@ -0,0 +1,194 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * SoC misc configuration and indentification driver. + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/clock.h> +#include <sys/kernel.h> +#include <sys/limits.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/module.h> +#include <sys/resource.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <arm/nvidia/tegra_efuse.h> + +#define PMC_STRAPPING_OPT_A 0 /* 0x464 */ + +#define PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT 4 +#define PMC_STRAPPING_OPT_A_RAM_CODE_MASK_LONG \ + (0xf << PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT) +#define PMC_STRAPPING_OPT_A_RAM_CODE_MASK_SHORT \ + (0x3 << PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT) + + +#define ABP_RD4(_sc, _r) bus_read_4((_sc)->abp_misc_res, (_r)) +#define STR_RD4(_sc, _r) bus_read_4((_sc)->strap_opt_res, (_r)) + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-apbmisc", 1}, + {NULL, 0} +}; + +struct tegra_abpmisc_softc { + device_t dev; + + struct resource *abp_misc_res; + struct resource *strap_opt_res; +}; + +static struct tegra_abpmisc_softc *dev_sc; + +static void +tegra_abpmisc_read_revision(struct tegra_abpmisc_softc *sc) +{ + uint32_t id, chip_id, minor_rev; + int rev; + + id = ABP_RD4(sc, 4); + chip_id = (id >> 8) & 0xff; + minor_rev = (id >> 16) & 0xf; + + switch (minor_rev) { + case 1: + rev = TEGRA_REVISION_A01; + break; + case 2: + rev = TEGRA_REVISION_A02; + break; + case 3: + rev = TEGRA_REVISION_A03; + break; + case 4: + rev = TEGRA_REVISION_A04; + break; + default: + rev = TEGRA_REVISION_UNKNOWN; + } + + tegra_sku_info.chip_id = chip_id; + tegra_sku_info.revision = rev; +} + +static int +tegra_abpmisc_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + return (BUS_PROBE_DEFAULT); +} + +static int +tegra_abpmisc_attach(device_t dev) +{ + int rid; + struct tegra_abpmisc_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + rid = 0; + sc->abp_misc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->abp_misc_res == NULL) { + device_printf(dev, "Cannot map ABP misc registers.\n"); + goto fail; + } + + rid = 1; + sc->strap_opt_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->strap_opt_res == NULL) { + device_printf(dev, "Cannot map strapping options registers.\n"); + goto fail; + } + + tegra_abpmisc_read_revision(sc); + + /* XXX - Hack - address collision with pinmux. */ + if (sc->abp_misc_res != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->abp_misc_res); + sc->abp_misc_res = NULL; + } + + dev_sc = sc; + return (bus_generic_attach(dev)); + +fail: + if (sc->abp_misc_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->abp_misc_res); + if (sc->strap_opt_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->strap_opt_res); + + return (ENXIO); +} + +static int +tegra_abpmisc_detach(device_t dev) +{ + struct tegra_abpmisc_softc *sc; + + sc = device_get_softc(dev); + if (sc->abp_misc_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->abp_misc_res); + if (sc->strap_opt_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->strap_opt_res); + return (bus_generic_detach(dev)); +} + +static device_method_t tegra_abpmisc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_abpmisc_probe), + DEVMETHOD(device_attach, tegra_abpmisc_attach), + DEVMETHOD(device_detach, tegra_abpmisc_detach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(tegra_abpmisc, tegra_abpmisc_driver, tegra_abpmisc_methods, + sizeof(struct tegra_abpmisc_softc)); +static devclass_t tegra_abpmisc_devclass; +EARLY_DRIVER_MODULE(tegra_abpmisc, simplebus, tegra_abpmisc_driver, + tegra_abpmisc_devclass, 0, 0, BUS_PASS_TIMER); diff --git a/sys/arm/nvidia/tegra_ahci.c b/sys/arm/nvidia/tegra_ahci.c new file mode 100644 index 0000000..4afba17 --- /dev/null +++ b/sys/arm/nvidia/tegra_ahci.c @@ -0,0 +1,627 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * AHCI driver for Tegra SoCs. + */ +#include <sys/param.h> +#include <sys/module.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/endian.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/rman.h> + +#include <machine/bus.h> +#include <machine/resource.h> + +#include <dev/ahci/ahci.h> +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> +#include <dev/extres/phy/phy.h> +#include <dev/extres/regulator/regulator.h> +#include <dev/fdt/fdt_common.h> +#include <dev/fdt/fdt_pinctrl.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <arm/nvidia/tegra_efuse.h> +#include <arm/nvidia/tegra_pmc.h> + +#define AHCI_WR4(_sc, _r, _v) bus_write_4((_sc)->ctlr.r_mem, (_r), (_v)) +#define AHCI_RD4(_sc, _r) bus_read_4((_sc)->ctlr.r_mem, (_r)) +#define SATA_WR4(_sc, _r, _v) bus_write_4((_sc)->sata_mem, (_r), (_v)) +#define SATA_RD4(_sc, _r) bus_read_4((_sc)->sata_mem, (_r)) + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-ahci", 1}, + {NULL, 0} +}; + +struct tegra_ahci_sc { + struct ahci_controller ctlr; /* Must be first */ + device_t dev; + struct resource *sata_mem; + clk_t clk_sata; + clk_t clk_sata_oob; + clk_t clk_pll_e; + clk_t clk_cml; + hwreset_t hwreset_sata; + hwreset_t hwreset_sata_oob; + hwreset_t hwreset_sata_cold; + regulator_t supply_hvdd; + regulator_t supply_vddio; + regulator_t supply_avdd; + regulator_t supply_target_5v; + regulator_t supply_target_12v; + phy_t phy; +}; + +struct sata_pad_calibration { + uint32_t gen1_tx_amp; + uint32_t gen1_tx_peak; + uint32_t gen2_tx_amp; + uint32_t gen2_tx_peak; +}; + +static const struct sata_pad_calibration tegra124_pad_calibration[] = { + {0x18, 0x04, 0x18, 0x0a}, + {0x0e, 0x04, 0x14, 0x0a}, + {0x0e, 0x07, 0x1a, 0x0e}, + {0x14, 0x0e, 0x1a, 0x0e}, +}; + +#define SATA_CONFIGURATION 0x180 +#define SATA_CONFIGURATION_EN_FPCI (1 << 0) + +#define SATA_FPCI_BAR5 0x94 +#define SATA_FPCI_BAR5_START_SHIFT 4 + +#define SATA_INTR_MASK 0x188 +#define SATA_INTR_MASK_IP_INT_MASK (1 << 16) + +#define SCFG_OFFSET 0x1000 + +#define T_SATA0_CFG_1 0x04 +#define T_SATA0_CFG_1_IO_SPACE (1 << 0) +#define T_SATA0_CFG_1_MEMORY_SPACE (1 << 1) +#define T_SATA0_CFG_1_BUS_MASTER (1 << 2) +#define T_SATA0_CFG_1_SERR (1 << 8) + +#define T_SATA0_CFG_9 0x24 +#define T_SATA0_CFG_9_BASE_ADDRESS_SHIFT 13 + +#define T_SATA0_AHCI_HBA_CAP_BKDR 0x300 +#define T_SATA0_BKDOOR_CC 0x4a4 +#define T_SATA0_CFG_SATA 0x54c +#define T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN (1 << 12) + +#define T_SATA0_CFG_MISC 0x550 +#define T_SATA0_INDEX 0x680 + +#define T_SATA0_CHX_PHY_CTRL1_GEN1 0x690 +#define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK 0xff +#define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT 8 +#define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK 0xff +#define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT 0 + + +#define T_SATA0_CHX_PHY_CTRL1_GEN2 0x694 +#define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK 0xff +#define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT 12 +#define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK 0xff +#define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT 0 + +#define T_SATA0_CHX_PHY_CTRL2 0x69c +#define T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1 0x23 + +#define T_SATA0_CHX_PHY_CTRL11 0x6d0 +#define T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ (0x2800 << 16) + +#define FUSE_SATA_CALIB 0x124 +#define FUSE_SATA_CALIB_MASK 0x3 + + +#define SATA_AUX_MISC_CNTL 0x1108 +#define SATA_AUX_PAD_PLL_CTRL_0 0x1120 +#define SATA_AUX_PAD_PLL_CTRL_1 0x1124 +#define SATA_AUX_PAD_PLL_CTRL_2 0x1128 +#define SATA_AUX_PAD_PLL_CTRL_3 0x112c + +#define T_AHCI_HBA_CCC_PORTS 0x0018 +#define T_AHCI_HBA_CAP_BKDR 0x00A0 +#define T_AHCI_HBA_CAP_BKDR_S64A (1 << 31) +#define T_AHCI_HBA_CAP_BKDR_SNCQ (1 << 30) +#define T_AHCI_HBA_CAP_BKDR_SSNTF (1 << 29) +#define T_AHCI_HBA_CAP_BKDR_SMPS (1 << 28) +#define T_AHCI_HBA_CAP_BKDR_SUPP_STG_SPUP (1 << 27) +#define T_AHCI_HBA_CAP_BKDR_SALP (1 << 26) +#define T_AHCI_HBA_CAP_BKDR_SAL (1 << 25) +#define T_AHCI_HBA_CAP_BKDR_SUPP_CLO (1 << 24) +#define T_AHCI_HBA_CAP_BKDR_INTF_SPD_SUPP(x) (((x) & 0xF) << 20) +#define T_AHCI_HBA_CAP_BKDR_SUPP_NONZERO_OFFSET (1 << 19) +#define T_AHCI_HBA_CAP_BKDR_SUPP_AHCI_ONLY (1 << 18) +#define T_AHCI_HBA_CAP_BKDR_SUPP_PM (1 << 17) +#define T_AHCI_HBA_CAP_BKDR_FIS_SWITCHING (1 << 16) +#define T_AHCI_HBA_CAP_BKDR_PIO_MULT_DRQ_BLK (1 << 15) +#define T_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP (1 << 14) +#define T_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP (1 << 13) +#define T_AHCI_HBA_CAP_BKDR_NUM_CMD_SLOTS(x) (((x) & 0x1F) << 8) +#define T_AHCI_HBA_CAP_BKDR_CMD_CMPL_COALESING (1 << 7) +#define T_AHCI_HBA_CAP_BKDR_ENCL_MGMT_SUPP (1 << 6) +#define T_AHCI_HBA_CAP_BKDR_EXT_SATA (1 << 5) +#define T_AHCI_HBA_CAP_BKDR_NUM_PORTS(x) (((x) & 0xF) << 0) + +#define T_AHCI_PORT_BKDR 0x0170 + +#define T_AHCI_PORT_BKDR_PXDEVSLP_DETO_OVERRIDE_VAL(x) (((x) & 0xFF) << 24) +#define T_AHCI_PORT_BKDR_PXDEVSLP_MDAT_OVERRIDE_VAL(x) (((x) & 0x1F) << 16) +#define T_AHCI_PORT_BKDR_PXDEVSLP_DETO_OVERRIDE (1 << 15) +#define T_AHCI_PORT_BKDR_PXDEVSLP_MDAT_OVERRIDE (1 << 14) +#define T_AHCI_PORT_BKDR_PXDEVSLP_DM(x) (((x) & 0xF) << 10) +#define T_AHCI_PORT_BKDR_PORT_UNCONNECTED (1 << 9) +#define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_CLAMP_THIS_CH (1 << 8) +#define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_TXRXCLK_UNCLAMP (1 << 7) +#define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_TXRXCLK_CLAMP (1 << 6) +#define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_DEVCLK_UNCLAMP (1 << 5) +#define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_DEVCLK_CLAMP (1 << 4) +#define T_AHCI_PORT_BKDR_HOTPLUG_CAP (1 << 3) +#define T_AHCI_PORT_BKDR_MECH_SWITCH (1 << 2) +#define T_AHCI_PORT_BKDR_COLD_PRSN_DET (1 << 1) +#define T_AHCI_PORT_BKDR_EXT_SATA_SUPP (1 << 0) + +static int +get_fdt_resources(struct tegra_ahci_sc *sc, phandle_t node) +{ + int rv; + + + rv = regulator_get_by_ofw_property(sc->dev, "hvdd-supply", + &sc->supply_hvdd ); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'hvdd' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "vddio-supply", + &sc->supply_vddio); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'vddio' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "avdd-supply", + &sc->supply_avdd); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'avdd' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "target-5v-supply", + &sc->supply_target_5v); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'target-5v' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "target-12v-supply", + &sc->supply_target_12v); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'target-12v' regulator\n"); + return (ENXIO); + } + + rv = hwreset_get_by_ofw_name(sc->dev, "sata", &sc->hwreset_sata ); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'sata' reset\n"); + return (ENXIO); + } + rv = hwreset_get_by_ofw_name(sc->dev, "sata-oob", + &sc->hwreset_sata_oob); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'sata oob' reset\n"); + return (ENXIO); + } + rv = hwreset_get_by_ofw_name(sc->dev, "sata-cold", + &sc->hwreset_sata_cold); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'sata cold' reset\n"); + return (ENXIO); + } + + rv = phy_get_by_ofw_name(sc->dev, "sata-phy", &sc->phy); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'sata' phy\n"); + return (ENXIO); + } + + rv = clk_get_by_ofw_name(sc->dev, "sata", &sc->clk_sata); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'sata' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, "sata-oob", &sc->clk_sata_oob); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'sata oob' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, "cml1", &sc->clk_cml); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'cml1' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, "pll_e", &sc->clk_pll_e); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pll_e' clock\n"); + return (ENXIO); + } + return (0); +} + +static int +enable_fdt_resources(struct tegra_ahci_sc *sc) +{ + int rv; + + rv = regulator_enable(sc->supply_hvdd); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'hvdd' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_vddio); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'vddio' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_avdd); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'avdd' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_target_5v); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'target-5v' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_target_12v); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'sc->target-12v' regulator\n"); + return (rv); + } + + /* Stop clocks */ + clk_stop(sc->clk_sata); + clk_stop(sc->clk_sata_oob); + tegra_powergate_power_off(TEGRA_POWERGATE_SAX); + + rv = hwreset_assert(sc->hwreset_sata); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert 'sata' reset\n"); + return (rv); + } + rv = hwreset_assert(sc->hwreset_sata_oob); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert 'sata oob' reset\n"); + return (rv); + } + + rv = hwreset_assert(sc->hwreset_sata_cold); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert 'sata cold' reset\n"); + return (rv); + } + rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_SAX, + sc->clk_sata, sc->hwreset_sata); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'SAX' powergate\n"); + return (rv); + } + + rv = clk_enable(sc->clk_sata_oob); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'sata oob' clock\n"); + return (rv); + } + rv = clk_enable(sc->clk_cml); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'cml' clock\n"); + return (rv); + } + rv = clk_enable(sc->clk_pll_e); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'pll e' clock\n"); + return (rv); + } + + rv = hwreset_deassert(sc->hwreset_sata_cold); + if (rv != 0) { + device_printf(sc->dev, "Cannot unreset 'sata cold' reset\n"); + return (rv); + } + rv = hwreset_deassert(sc->hwreset_sata_oob); + if (rv != 0) { + device_printf(sc->dev, "Cannot unreset 'sata oob' reset\n"); + return (rv); + } + + rv = phy_enable(sc->dev, sc->phy); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable SATA phy\n"); + return (rv); + } + + return (0); +} + +static int +tegra_ahci_ctrl_init(struct tegra_ahci_sc *sc) +{ + uint32_t val; + const struct sata_pad_calibration *calib; + + val = SATA_RD4(sc, SATA_CONFIGURATION); + val |= SATA_CONFIGURATION_EN_FPCI; + SATA_WR4(sc, SATA_CONFIGURATION, val); + + + /* Pad calibration. */ + val = tegra_fuse_read_4(FUSE_SATA_CALIB); + calib = tegra124_pad_calibration + (val & FUSE_SATA_CALIB_MASK); + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_INDEX, 1); + + val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN1); + val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK << + T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT); + val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK << + T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT); + val |= calib->gen1_tx_amp << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT; + val |= calib->gen1_tx_peak << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT; + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN1, val); + + val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN2); + val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK << + T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT); + val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK << + T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT); + val |= calib->gen2_tx_amp << T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT; + val |= calib->gen2_tx_peak << T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT; + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN2, val); + + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL11, + T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ); + + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL2, + T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1); + + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_INDEX, 0); + + /* Set device ID. */ + val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA); + val |= T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN; + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA, val); + + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_BKDOOR_CC, 0x01060100); + + val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA); + val &= ~T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN; + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA, val); + + /* Enable IO & memory access, bus master mode */ + val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_1); + val |= T_SATA0_CFG_1_IO_SPACE; + val |= T_SATA0_CFG_1_MEMORY_SPACE; + val |= T_SATA0_CFG_1_BUS_MASTER; + val |= T_SATA0_CFG_1_SERR; + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_1, val); + + /* SATA MMIO. */ + SATA_WR4(sc, SATA_FPCI_BAR5, 0x10000 << SATA_FPCI_BAR5_START_SHIFT); + /* AHCI bar */ + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_9, + 0x08000 << T_SATA0_CFG_9_BASE_ADDRESS_SHIFT); + + /* Unmask interrupts. */ + val = SATA_RD4(sc, SATA_INTR_MASK); + val |= SATA_INTR_MASK_IP_INT_MASK; + SATA_WR4(sc, SATA_INTR_MASK, val); + + return (0); +} + +static int +tegra_ahci_ctlr_reset(device_t dev) +{ + struct tegra_ahci_sc *sc; + int rv; + uint32_t reg; + + sc = device_get_softc(dev); + rv = ahci_ctlr_reset(dev); + if (rv != 0) + return (0); + AHCI_WR4(sc, T_AHCI_HBA_CCC_PORTS, 1); + + /* Overwrite AHCI capabilites. */ + reg = AHCI_RD4(sc, T_AHCI_HBA_CAP_BKDR); + reg &= ~T_AHCI_HBA_CAP_BKDR_NUM_PORTS(~0); + reg |= T_AHCI_HBA_CAP_BKDR_NUM_PORTS(0); + reg |= T_AHCI_HBA_CAP_BKDR_EXT_SATA; + reg |= T_AHCI_HBA_CAP_BKDR_ENCL_MGMT_SUPP; + reg |= T_AHCI_HBA_CAP_BKDR_CMD_CMPL_COALESING; + reg |= T_AHCI_HBA_CAP_BKDR_FIS_SWITCHING; + reg |= T_AHCI_HBA_CAP_BKDR_SUPP_PM; + reg |= T_AHCI_HBA_CAP_BKDR_SUPP_CLO; + reg |= T_AHCI_HBA_CAP_BKDR_SUPP_STG_SPUP; + AHCI_WR4(sc, T_AHCI_HBA_CAP_BKDR, reg); + + /* Overwrite AHCI portcapabilites. */ + reg = AHCI_RD4(sc, T_AHCI_PORT_BKDR); + reg |= T_AHCI_PORT_BKDR_COLD_PRSN_DET; + reg |= T_AHCI_PORT_BKDR_HOTPLUG_CAP; + reg |= T_AHCI_PORT_BKDR_EXT_SATA_SUPP; + AHCI_WR4(sc, T_AHCI_PORT_BKDR, reg); + + return (0); +} + +static int +tegra_ahci_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + + device_set_desc_copy(dev, "AHCI SATA controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +tegra_ahci_attach(device_t dev) +{ + struct tegra_ahci_sc *sc; + struct ahci_controller *ctlr; + phandle_t node; + int rv, rid; + + sc = device_get_softc(dev); + sc->dev = dev; + ctlr = &sc->ctlr; + node = ofw_bus_get_node(dev); + + ctlr->r_rid = 0; + ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &ctlr->r_rid, RF_ACTIVE); + if (ctlr->r_mem == NULL) + return (ENXIO); + + rid = 1; + sc->sata_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &rid, RF_ACTIVE); + if (sc->sata_mem == NULL) { + rv = ENXIO; + goto fail; + } + rv = get_fdt_resources(sc, node); + if (rv != 0) { + device_printf(sc->dev, "Failed to allocate FDT resource(s)\n"); + goto fail; + } + + rv = enable_fdt_resources(sc); + if (rv != 0) { + device_printf(sc->dev, "Failed to enable FDT resource(s)\n"); + goto fail; + } + rv = tegra_ahci_ctrl_init(sc); + if (rv != 0) { + device_printf(sc->dev, "Failed to initialize controller)\n"); + goto fail; + } + + /* Setup controller defaults. */ + ctlr->msi = 0; + ctlr->numirqs = 1; + ctlr->ccc = 0; + + /* Reset controller. */ + rv = tegra_ahci_ctlr_reset(dev); + if (rv != 0) + goto fail; + rv = ahci_attach(dev); + return (rv); + +fail: + /* XXX FDT stuff */ + if (sc->sata_mem != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->sata_mem); + if (ctlr->r_mem != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, + ctlr->r_mem); + return (rv); +} + +static int +tegra_ahci_detach(device_t dev) +{ + + ahci_detach(dev); + return (0); +} + +static int +tegra_ahci_suspend(device_t dev) +{ + struct tegra_ahci_sc *sc = device_get_softc(dev); + + bus_generic_suspend(dev); + /* Disable interupts, so the state change(s) doesn't trigger. */ + ATA_OUTL(sc->ctlr.r_mem, AHCI_GHC, + ATA_INL(sc->ctlr.r_mem, AHCI_GHC) & (~AHCI_GHC_IE)); + return (0); +} + +static int +tegra_ahci_resume(device_t dev) +{ + int res; + + if ((res = tegra_ahci_ctlr_reset(dev)) != 0) + return (res); + ahci_ctlr_setup(dev); + return (bus_generic_resume(dev)); +} + +devclass_t genahci_devclass; +static device_method_t genahci_methods[] = { + DEVMETHOD(device_probe, tegra_ahci_probe), + DEVMETHOD(device_attach, tegra_ahci_attach), + DEVMETHOD(device_detach, tegra_ahci_detach), + DEVMETHOD(device_suspend, tegra_ahci_suspend), + DEVMETHOD(device_resume, tegra_ahci_resume), + DEVMETHOD(bus_print_child, ahci_print_child), + DEVMETHOD(bus_alloc_resource, ahci_alloc_resource), + DEVMETHOD(bus_release_resource, ahci_release_resource), + DEVMETHOD(bus_setup_intr, ahci_setup_intr), + DEVMETHOD(bus_teardown_intr, ahci_teardown_intr), + DEVMETHOD(bus_child_location_str, ahci_child_location_str), + DEVMETHOD(bus_get_dma_tag, ahci_get_dma_tag), + + DEVMETHOD_END +}; +static driver_t genahci_driver = { + "ahci", + genahci_methods, + sizeof(struct tegra_ahci_sc) +}; +DRIVER_MODULE(genahci, simplebus, genahci_driver, genahci_devclass, NULL, NULL); diff --git a/sys/arm/nvidia/tegra_efuse.c b/sys/arm/nvidia/tegra_efuse.c new file mode 100644 index 0000000..ae3f9ef --- /dev/null +++ b/sys/arm/nvidia/tegra_efuse.c @@ -0,0 +1,368 @@ +/*- + * Copyright (c) 2015 Michal Meloun + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/clock.h> +#include <sys/kernel.h> +#include <sys/limits.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/module.h> +#include <sys/resource.h> +#include <sys/rman.h> + +#include <machine/bus.h> +#include <machine/resource.h> + +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <arm/nvidia/tegra_efuse.h> + + +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_sc)->fuse_begin + (_r)) + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-efuse", 1}, + {NULL, 0} +}; + +struct tegra_efuse_softc { + device_t dev; + struct resource *mem_res; + + int fuse_begin; + clk_t clk; + hwreset_t reset; +}; +struct tegra_efuse_softc *dev_sc; + +struct tegra_sku_info tegra_sku_info; +static char *tegra_rev_name[] = { + [TEGRA_REVISION_UNKNOWN] = "unknown", + [TEGRA_REVISION_A01] = "A01", + [TEGRA_REVISION_A02] = "A02", + [TEGRA_REVISION_A03] = "A03", + [TEGRA_REVISION_A03p] = "A03 prime", + [TEGRA_REVISION_A04] = "A04", +}; + +/* Tegra30 and later */ +#define FUSE_VENDOR_CODE 0x100 +#define FUSE_FAB_CODE 0x104 +#define FUSE_LOT_CODE_0 0x108 +#define FUSE_LOT_CODE_1 0x10c +#define FUSE_WAFER_ID 0x110 +#define FUSE_X_COORDINATE 0x114 +#define FUSE_Y_COORDINATE 0x118 + +/* ---------------------- Tegra 124 specific code & data --------------- */ +#define TEGRA124_FUSE_BEGIN 0x100 + +#define TEGRA124_CPU_PROCESS_CORNERS 2 +#define TEGRA124_GPU_PROCESS_CORNERS 2 +#define TEGRA124_SOC_PROCESS_CORNERS 2 + +#define TEGRA124_FUSE_SKU_INFO 0x10 +#define TEGRA124_FUSE_CPU_SPEEDO_0 0x14 +#define TEGRA124_FUSE_CPU_IDDQ 0x18 +#define TEGRA124_FUSE_FT_REV 0x28 +#define TEGRA124_FUSE_CPU_SPEEDO_1 0x2c +#define TEGRA124_FUSE_CPU_SPEEDO_2 0x30 +#define TEGRA124_FUSE_SOC_SPEEDO_0 0x34 +#define TEGRA124_FUSE_SOC_SPEEDO_1 0x38 +#define TEGRA124_FUSE_SOC_SPEEDO_2 0x3c +#define TEGRA124_FUSE_SOC_IDDQ 0x40 +#define TEGRA124_FUSE_GPU_IDDQ 0x128 + +enum { + TEGRA124_THRESHOLD_INDEX_0, + TEGRA124_THRESHOLD_INDEX_1, + TEGRA124_THRESHOLD_INDEX_COUNT, +}; + +static uint32_t tegra124_cpu_process_speedos[][TEGRA124_CPU_PROCESS_CORNERS] = +{ + {2190, UINT_MAX}, + {0, UINT_MAX}, +}; + +static uint32_t tegra124_gpu_process_speedos[][TEGRA124_GPU_PROCESS_CORNERS] = +{ + {1965, UINT_MAX}, + {0, UINT_MAX}, +}; + +static uint32_t tegra124_soc_process_speedos[][TEGRA124_SOC_PROCESS_CORNERS] = +{ + {2101, UINT_MAX}, + {0, UINT_MAX}, +}; + +static void +tegra124_rev_sku_to_speedo_ids(struct tegra_efuse_softc *sc, + struct tegra_sku_info *sku, int *threshold) +{ + + /* Assign to default */ + sku->cpu_speedo_id = 0; + sku->soc_speedo_id = 0; + sku->gpu_speedo_id = 0; + *threshold = TEGRA124_THRESHOLD_INDEX_0; + + switch (sku->sku_id) { + case 0x00: /* Eng sku */ + case 0x0F: + case 0x23: + /* Using the default */ + break; + case 0x83: + sku->cpu_speedo_id = 2; + break; + + case 0x1F: + case 0x87: + case 0x27: + sku->cpu_speedo_id = 2; + sku->soc_speedo_id = 0; + sku->gpu_speedo_id = 1; + *threshold = TEGRA124_THRESHOLD_INDEX_0; + break; + case 0x81: + case 0x21: + case 0x07: + sku->cpu_speedo_id = 1; + sku->soc_speedo_id = 1; + sku->gpu_speedo_id = 1; + *threshold = TEGRA124_THRESHOLD_INDEX_1; + break; + case 0x49: + case 0x4A: + case 0x48: + sku->cpu_speedo_id = 4; + sku->soc_speedo_id = 2; + sku->gpu_speedo_id = 3; + *threshold = TEGRA124_THRESHOLD_INDEX_1; + break; + default: + device_printf(sc->dev, " Unknown SKU ID %d\n", sku->sku_id); + break; + } +} + + +static void +tegra124_init_speedo(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku) +{ + int i, threshold; + + sku->sku_id = RD4(sc, TEGRA124_FUSE_SKU_INFO); + sku->soc_iddq_value = RD4(sc, TEGRA124_FUSE_SOC_IDDQ); + sku->cpu_iddq_value = RD4(sc, TEGRA124_FUSE_CPU_IDDQ); + sku->gpu_iddq_value = RD4(sc, TEGRA124_FUSE_GPU_IDDQ); + sku->soc_speedo_value = RD4(sc, TEGRA124_FUSE_SOC_SPEEDO_0); + sku->cpu_speedo_value = RD4(sc, TEGRA124_FUSE_CPU_SPEEDO_0); + sku->gpu_speedo_value = RD4(sc, TEGRA124_FUSE_CPU_SPEEDO_2); + + if (sku->cpu_speedo_value == 0) { + device_printf(sc->dev, "CPU Speedo value is not fused.\n"); + return; + } + + tegra124_rev_sku_to_speedo_ids(sc, sku, &threshold); + + for (i = 0; i < TEGRA124_SOC_PROCESS_CORNERS; i++) { + if (sku->soc_speedo_value < + tegra124_soc_process_speedos[threshold][i]) + break; + } + sku->soc_process_id = i; + + for (i = 0; i < TEGRA124_CPU_PROCESS_CORNERS; i++) { + if (sku->cpu_speedo_value < + tegra124_cpu_process_speedos[threshold][i]) + break; + } + sku->cpu_process_id = i; + + for (i = 0; i < TEGRA124_GPU_PROCESS_CORNERS; i++) { + if (sku->gpu_speedo_value < + tegra124_gpu_process_speedos[threshold][i]) + break; + } + sku->gpu_process_id = i; + +} + +/* ----------------- End of Tegra 124 specific code & data --------------- */ + +uint32_t +tegra_fuse_read_4(int addr) { + + if (dev_sc == NULL) + panic("tegra_fuse_read_4 called too early"); + return (RD4(dev_sc, addr)); +} + + +static void +tegra_efuse_dump_sku() +{ + printf(" TEGRA SKU Info:\n"); + printf(" chip_id: %u\n", tegra_sku_info.chip_id); + printf(" sku_id: %u\n", tegra_sku_info.sku_id); + printf(" cpu_process_id: %u\n", tegra_sku_info.cpu_process_id); + printf(" cpu_speedo_id: %u\n", tegra_sku_info.cpu_speedo_id); + printf(" cpu_speedo_value: %u\n", tegra_sku_info.cpu_speedo_value); + printf(" cpu_iddq_value: %u\n", tegra_sku_info.cpu_iddq_value); + printf(" soc_process_id: %u\n", tegra_sku_info.soc_process_id); + printf(" soc_speedo_id: %u\n", tegra_sku_info.soc_speedo_id); + printf(" soc_speedo_value: %u\n", tegra_sku_info.soc_speedo_value); + printf(" soc_iddq_value: %u\n", tegra_sku_info.soc_iddq_value); + printf(" gpu_process_id: %u\n", tegra_sku_info.gpu_process_id); + printf(" gpu_speedo_id: %u\n", tegra_sku_info.gpu_speedo_id); + printf(" gpu_speedo_value: %u\n", tegra_sku_info.gpu_speedo_value); + printf(" gpu_iddq_value: %u\n", tegra_sku_info.gpu_iddq_value); + printf(" revision: %s\n", tegra_rev_name[tegra_sku_info.revision]); +} + +static int +tegra_efuse_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + return (BUS_PROBE_DEFAULT); +} + +static int +tegra_efuse_attach(device_t dev) +{ + int rv, rid; + phandle_t node; + struct tegra_efuse_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + /* Get the memory resource for the register mapping. */ + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot map registers.\n"); + rv = ENXIO; + goto fail; + } + + /* OFW resources. */ + rv = clk_get_by_ofw_name(dev, "fuse", &sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot get fuse clock: %d\n", rv); + goto fail; + } + rv = clk_enable(sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot enable clock: %d\n", rv); + goto fail; + } + rv = hwreset_get_by_ofw_name(sc->dev, "fuse", &sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot get fuse reset\n"); + goto fail; + } + rv = hwreset_deassert(sc->reset); + if (rv != 0) { + device_printf(sc->dev, "Cannot clear reset\n"); + goto fail; + } + + /* Tegra124 specific init. */ + sc->fuse_begin = TEGRA124_FUSE_BEGIN; + tegra124_init_speedo(sc, &tegra_sku_info); + + dev_sc = sc; + + if (bootverbose) + tegra_efuse_dump_sku(); + return (bus_generic_attach(dev)); + +fail: + dev_sc = NULL; + if (sc->clk != NULL) + clk_release(sc->clk); + if (sc->reset != NULL) + hwreset_release(sc->reset); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + return (rv); +} + +static int +tegra_efuse_detach(device_t dev) +{ + struct tegra_efuse_softc *sc; + + sc = device_get_softc(dev); + dev_sc = NULL; + if (sc->clk != NULL) + clk_release(sc->clk); + if (sc->reset != NULL) + hwreset_release(sc->reset); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + return (bus_generic_detach(dev)); +} + +static device_method_t tegra_efuse_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_efuse_probe), + DEVMETHOD(device_attach, tegra_efuse_attach), + DEVMETHOD(device_detach, tegra_efuse_detach), + + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(tegra_efuse, tegra_efuse_driver, tegra_efuse_methods, + sizeof(struct tegra_efuse_softc)); +static devclass_t tegra_efuse_devclass; +EARLY_DRIVER_MODULE(tegra_efuse, simplebus, tegra_efuse_driver, + tegra_efuse_devclass, 0, 0, BUS_PASS_TIMER); diff --git a/sys/arm/nvidia/tegra_efuse.h b/sys/arm/nvidia/tegra_efuse.h new file mode 100644 index 0000000..36804d06 --- /dev/null +++ b/sys/arm/nvidia/tegra_efuse.h @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#ifndef _TEGRA_EFUSE_H_ + +enum tegra_revision { + TEGRA_REVISION_UNKNOWN = 0, + TEGRA_REVISION_A01, + TEGRA_REVISION_A02, + TEGRA_REVISION_A03, + TEGRA_REVISION_A03p, + TEGRA_REVISION_A04, +}; + +struct tegra_sku_info { + u_int chip_id; + u_int sku_id; + u_int cpu_process_id; + u_int cpu_speedo_id; + u_int cpu_speedo_value; + u_int cpu_iddq_value; + u_int soc_process_id; + u_int soc_speedo_id; + u_int soc_speedo_value; + u_int soc_iddq_value; + u_int gpu_process_id; + u_int gpu_speedo_id; + u_int gpu_speedo_value; + u_int gpu_iddq_value; + enum tegra_revision revision; +}; + +extern struct tegra_sku_info tegra_sku_info; +uint32_t tegra_fuse_read_4(int addr); + +#endif /* _TEGRA_EFUSE_H_ */ diff --git a/sys/arm/nvidia/tegra_ehci.c b/sys/arm/nvidia/tegra_ehci.c new file mode 100644 index 0000000..e7f7b22 --- /dev/null +++ b/sys/arm/nvidia/tegra_ehci.c @@ -0,0 +1,322 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * EHCI driver for Tegra SoCs. + */ +#include "opt_bus.h" +#include "opt_platform.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/condvar.h> +#include <sys/rman.h> + +#include <machine/bus.h> +#include <machine/resource.h> + +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> +#include <dev/extres/phy/phy.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_controller.h> +#include <dev/usb/usb_bus.h> +#include <dev/usb/controller/ehci.h> +#include <dev/usb/controller/ehcireg.h> + +#include "usbdevs.h" + +#define TEGRA_EHCI_REG_OFF 0x100 +#define TEGRA_EHCI_REG_SIZE 0x100 + +/* Compatible devices. */ +#define TEGRA124_EHCI 1 +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-ehci", (uintptr_t)TEGRA124_EHCI}, + {NULL, 0}, +}; + +struct tegra_ehci_softc { + ehci_softc_t ehci_softc; + device_t dev; + struct resource *ehci_mem_res; /* EHCI core regs. */ + struct resource *ehci_irq_res; /* EHCI core IRQ. */ + int usb_alloc_called; + clk_t clk; + phy_t phy; + hwreset_t reset; +}; + +static void +tegra_ehci_post_reset(struct ehci_softc *ehci_softc) +{ + uint32_t usbmode; + + /* Force HOST mode. */ + usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_LPM); + usbmode &= ~EHCI_UM_CM; + usbmode |= EHCI_UM_CM_HOST; + device_printf(ehci_softc->sc_bus.bdev, "set host controller mode\n"); + EOWRITE4(ehci_softc, EHCI_USBMODE_LPM, usbmode); +} + +static int +tegra_ehci_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { + device_set_desc(dev, "Nvidia Tegra EHCI controller"); + return (BUS_PROBE_DEFAULT); + } + return (ENXIO); +} + +static int +tegra_ehci_detach(device_t dev) +{ + struct tegra_ehci_softc *sc; + ehci_softc_t *esc; + + sc = device_get_softc(dev); + + esc = &sc->ehci_softc; + if (sc->clk != NULL) + clk_release(sc->clk); + if (esc->sc_bus.bdev != NULL) + device_delete_child(dev, esc->sc_bus.bdev); + if (esc->sc_flags & EHCI_SCFLG_DONEINIT) + ehci_detach(esc); + if (esc->sc_intr_hdl != NULL) + bus_teardown_intr(dev, esc->sc_irq_res, + esc->sc_intr_hdl); + if (sc->ehci_irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, + sc->ehci_irq_res); + if (sc->ehci_mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, + sc->ehci_mem_res); + if (sc->usb_alloc_called) + usb_bus_mem_free_all(&esc->sc_bus, &ehci_iterate_hw_softc); + + /* During module unload there are lots of children leftover. */ + device_delete_children(dev); + + return (0); +} + +static int +tegra_ehci_attach(device_t dev) +{ + struct tegra_ehci_softc *sc; + ehci_softc_t *esc; + int rv, rid; + uint64_t freq; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + esc = &sc->ehci_softc; + + /* Allocate resources. */ + rid = 0; + sc->ehci_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->ehci_mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + rv = ENXIO; + goto out; + } + + rid = 0; + sc->ehci_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->ehci_irq_res == NULL) { + device_printf(dev, "Cannot allocate IRQ resources\n"); + rv = ENXIO; + goto out; + } + + rv = hwreset_get_by_ofw_name(dev, "usb", &sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot get reset\n"); + rv = ENXIO; + goto out; + } + + rv = phy_get_by_ofw_property(sc->dev, "nvidia,phy", &sc->phy); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'nvidia,phy' phy\n"); + rv = ENXIO; + goto out; + } + + rv = clk_get_by_ofw_index(sc->dev, 0, &sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot get clock\n"); + goto out; + } + + rv = clk_enable(sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot enable clock\n"); + goto out; + } + + freq = 0; + rv = clk_get_freq(sc->clk, &freq); + if (rv != 0) { + device_printf(dev, "Cannot get clock frequency\n"); + goto out; + } + + rv = hwreset_deassert(sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot clear reset: %d\n", rv); + rv = ENXIO; + goto out; + } + + rv = phy_enable(sc->dev, sc->phy); + if (rv != 0) { + device_printf(dev, "Cannot enable phy: %d\n", rv); + goto out; + } + + /* Fill data for EHCI driver. */ + esc->sc_vendor_get_port_speed = ehci_get_port_speed_hostc; + esc->sc_vendor_post_reset = tegra_ehci_post_reset; + esc->sc_io_tag = rman_get_bustag(sc->ehci_mem_res); + esc->sc_bus.parent = dev; + esc->sc_bus.devices = esc->sc_devices; + esc->sc_bus.devices_max = EHCI_MAX_DEVICES; + esc->sc_bus.dma_bits = 32; + + /* Allocate all DMA memory. */ + rv = usb_bus_mem_alloc_all(&esc->sc_bus, USB_GET_DMA_TAG(dev), + &ehci_iterate_hw_softc); + sc->usb_alloc_called = 1; + if (rv != 0) { + device_printf(dev, "usb_bus_mem_alloc_all() failed\n"); + rv = ENOMEM; + goto out; + } + + /* + * Set handle to USB related registers subregion used by + * generic EHCI driver. + */ + rv = bus_space_subregion(esc->sc_io_tag, + rman_get_bushandle(sc->ehci_mem_res), + TEGRA_EHCI_REG_OFF, TEGRA_EHCI_REG_SIZE, &esc->sc_io_hdl); + if (rv != 0) { + device_printf(dev, "Could not create USB memory subregion\n"); + rv = ENXIO; + goto out; + } + + /* Setup interrupt handler. */ + rv = bus_setup_intr(dev, sc->ehci_irq_res, INTR_TYPE_BIO, NULL, + (driver_intr_t *)ehci_interrupt, esc, &esc->sc_intr_hdl); + if (rv != 0) { + device_printf(dev, "Could not setup IRQ\n"); + goto out; + } + + /* Add USB bus device. */ + esc->sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (esc->sc_bus.bdev == NULL) { + device_printf(dev, "Could not add USB device\n"); + goto out; + } + device_set_ivars(esc->sc_bus.bdev, &esc->sc_bus); + + esc->sc_id_vendor = USB_VENDOR_FREESCALE; + strlcpy(esc->sc_vendor, "Nvidia", sizeof(esc->sc_vendor)); + + /* Set flags that affect ehci_init() behavior. */ + esc->sc_flags |= EHCI_SCFLG_TT; + esc->sc_flags |= EHCI_SCFLG_NORESTERM; + rv = ehci_init(esc); + if (rv != 0) { + device_printf(dev, "USB init failed: %d\n", + rv); + goto out; + } + esc->sc_flags |= EHCI_SCFLG_DONEINIT; + + /* Probe the bus. */ + rv = device_probe_and_attach(esc->sc_bus.bdev); + if (rv != 0) { + device_printf(dev, + "device_probe_and_attach() failed\n"); + goto out; + } + return (0); + +out: + tegra_ehci_detach(dev); + return (rv); +} + +static device_method_t ehci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_ehci_probe), + DEVMETHOD(device_attach, tegra_ehci_attach), + DEVMETHOD(device_detach, tegra_ehci_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + DEVMETHOD_END +}; + +static driver_t ehci_driver = { + "ehci", + ehci_methods, + sizeof(struct tegra_ehci_softc) +}; + +static devclass_t ehci_devclass; +DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); +MODULE_DEPEND(ehci, usb, 1, 1, 1);
\ No newline at end of file diff --git a/sys/arm/nvidia/tegra_gpio.c b/sys/arm/nvidia/tegra_gpio.c new file mode 100644 index 0000000..f9a1d4d --- /dev/null +++ b/sys/arm/nvidia/tegra_gpio.c @@ -0,0 +1,480 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Tegra GPIO driver. + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> + +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/gpio.h> + +#include <machine/bus.h> +#include <machine/resource.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/gpio/gpiobusvar.h> +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + + +#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define GPIO_LOCK_INIT(_sc) mtx_init(&_sc->sc_mtx, \ + device_get_nameunit(_sc->sc_dev), "tegra_gpio", MTX_DEF) +#define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define GPIO_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + +#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) + +#define GPIO_BANK_OFFS 0x100 /* Bank offset */ +#define GPIO_NUM_BANKS 8 /* Total number per bank */ +#define GPIO_REGS_IN_BANK 4 /* Total registers in bank */ +#define GPIO_PINS_IN_REG 8 /* Total pin in register */ + +#define GPIO_BANKNUM(n) ((n) / (GPIO_REGS_IN_BANK * GPIO_PINS_IN_REG)) +#define GPIO_PORTNUM(n) (((n) / GPIO_PINS_IN_REG) % GPIO_REGS_IN_BANK) +#define GPIO_BIT(n) ((n) % GPIO_PINS_IN_REG) + +#define GPIO_REGNUM(n) (GPIO_BANKNUM(n) * GPIO_BANK_OFFS + \ + GPIO_PORTNUM(n) * 4) + +#define NGPIO ((GPIO_NUM_BANKS * GPIO_REGS_IN_BANK * GPIO_PINS_IN_REG) - 8) + +/* Register offsets */ +#define GPIO_CNF 0x00 +#define GPIO_OE 0x10 +#define GPIO_OUT 0x20 +#define GPIO_IN 0x30 +#define GPIO_INT_STA 0x40 +#define GPIO_INT_ENB 0x50 +#define GPIO_INT_LVL 0x60 +#define GPIO_INT_CLR 0x70 +#define GPIO_MSK_CNF 0x80 +#define GPIO_MSK_OE 0x90 +#define GPIO_MSK_OUT 0xA0 +#define GPIO_MSK_INT_STA 0xC0 +#define GPIO_MSK_INT_ENB 0xD0 +#define GPIO_MSK_INT_LVL 0xE0 + +char *tegra_gpio_port_names[] = { + "A", "B", "C", "D", /* Bank 0 */ + "E", "F", "G", "H", /* Bank 1 */ + "I", "J", "K", "L", /* Bank 2 */ + "M", "N", "O", "P", /* Bank 3 */ + "Q", "R", "S", "T", /* Bank 4 */ + "U", "V", "W", "X", /* Bank 5 */ + "Y", "Z", "AA", "BB", /* Bank 5 */ + "CC", "DD", "EE" /* Bank 5 */ +}; + +struct tegra_gpio_softc { + device_t dev; + device_t sc_busdev; + struct mtx sc_mtx; + struct resource *mem_res; + struct resource *irq_res; + void *gpio_ih; + int gpio_npins; + struct gpio_pin gpio_pins[NGPIO]; +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-gpio", 1}, + {NULL, 0} +}; + +static inline void +gpio_write_masked(struct tegra_gpio_softc *sc, bus_size_t reg, + struct gpio_pin *pin, uint32_t val) +{ + uint32_t tmp; + int bit; + + bit = GPIO_BIT(pin->gp_pin); + tmp = 0x100 << bit; /* mask */ + tmp |= (val & 1) << bit; /* value */ + bus_write_4(sc->mem_res, reg + GPIO_REGNUM(pin->gp_pin), tmp); +} +static inline uint32_t +gpio_read(struct tegra_gpio_softc *sc, bus_size_t reg, struct gpio_pin *pin) +{ + int bit; + uint32_t val; + + bit = GPIO_BIT(pin->gp_pin); + val = bus_read_4(sc->mem_res, reg + GPIO_REGNUM(pin->gp_pin)); + return (val >> bit) & 1; +} + +static void +tegra_gpio_pin_configure(struct tegra_gpio_softc *sc, struct gpio_pin *pin, + unsigned int flags) +{ + + if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == 0) + return; + + /* Manage input/output */ + pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT); + if (flags & GPIO_PIN_OUTPUT) { + pin->gp_flags |= GPIO_PIN_OUTPUT; + gpio_write_masked(sc, GPIO_MSK_OE, pin, 1); + } else { + pin->gp_flags |= GPIO_PIN_INPUT; + gpio_write_masked(sc, GPIO_MSK_OE, pin, 0); + } +} + +static device_t +tegra_gpio_get_bus(device_t dev) +{ + struct tegra_gpio_softc *sc; + + sc = device_get_softc(dev); + return (sc->sc_busdev); +} + +static int +tegra_gpio_pin_max(device_t dev, int *maxpin) +{ + + *maxpin = NGPIO - 1; + return (0); +} + +static int +tegra_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct tegra_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + *caps = sc->gpio_pins[pin].gp_caps; + GPIO_UNLOCK(sc); + + return (0); +} + +static int +tegra_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct tegra_gpio_softc *sc; + int cnf; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + cnf = gpio_read(sc, GPIO_CNF, &sc->gpio_pins[pin]); + if (cnf == 0) { + GPIO_UNLOCK(sc); + return (ENXIO); + } + *flags = sc->gpio_pins[pin].gp_flags; + GPIO_UNLOCK(sc); + + return (0); +} + +static int +tegra_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct tegra_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + memcpy(name, sc->gpio_pins[pin].gp_name, GPIOMAXNAME); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +tegra_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct tegra_gpio_softc *sc; + int cnf; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + cnf = gpio_read(sc, GPIO_CNF, &sc->gpio_pins[pin]); + if (cnf == 0) { + /* XXX - allow this for while .... + GPIO_UNLOCK(sc); + return (ENXIO); + */ + gpio_write_masked(sc, GPIO_MSK_CNF, &sc->gpio_pins[pin], 1); + } + tegra_gpio_pin_configure(sc, &sc->gpio_pins[pin], flags); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +tegra_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) +{ + struct tegra_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + GPIO_LOCK(sc); + gpio_write_masked(sc, GPIO_MSK_OUT, &sc->gpio_pins[pin], value); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +tegra_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) +{ + struct tegra_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + *val = gpio_read(sc, GPIO_IN, &sc->gpio_pins[pin]); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +tegra_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct tegra_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + gpio_write_masked(sc, GPIO_MSK_OE, &sc->gpio_pins[pin], + gpio_read(sc, GPIO_IN, &sc->gpio_pins[pin]) ^ 1); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +tegra_gpio_intr(void *arg) +{ + struct tegra_gpio_softc *sc; + uint32_t val; + int i; + + sc = arg; + for (i = 0; i < NGPIO; i += GPIO_PINS_IN_REG) { + /* Clear interrupt */ + val = bus_read_4(sc->mem_res, GPIO_INT_STA + GPIO_REGNUM(i)); + val &= bus_read_4(sc->mem_res, GPIO_INT_ENB + GPIO_REGNUM(i)); + bus_write_4(sc->mem_res, GPIO_INT_CLR + GPIO_REGNUM(i), val); + /* Interrupt handling */ +#ifdef not_yet + for (j = 0; j < GPIO_PINS_IN_REG; j++) { + if (val & (1 << j)) + handle_irq(i + j); + } + */ +#endif + } + return (FILTER_HANDLED); +} + +static int +tegra_gpio_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { + device_set_desc(dev, "Tegra GPIO Controller"); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +tegra_gpio_detach(device_t dev) +{ + struct tegra_gpio_softc *sc; + + sc = device_get_softc(dev); + + KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized")); + + gpiobus_detach_bus(dev); + if (sc->gpio_ih != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->gpio_ih); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + mtx_destroy(&sc->sc_mtx); + + return(0); +} + +static int +tegra_gpio_attach(device_t dev) +{ + struct tegra_gpio_softc *sc; + int i, rid; + + sc = device_get_softc(dev); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + /* Allocate bus_space resources. */ + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + tegra_gpio_detach(dev); + return (ENXIO); + } + + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "Cannot allocate IRQ resources\n"); + tegra_gpio_detach(dev); + return (ENXIO); + } + + sc->dev = dev; + sc->gpio_npins = NGPIO; + + if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC, + tegra_gpio_intr, NULL, sc, &sc->gpio_ih))) { + device_printf(dev, + "WARNING: unable to register interrupt handler\n"); + tegra_gpio_detach(dev); + return (ENXIO); + } + + for (i = 0; i < sc->gpio_npins; i++) { + sc->gpio_pins[i].gp_pin = i; + sc->gpio_pins[i].gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT; + snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "gpio_%s.%d", + tegra_gpio_port_names[ i / GPIO_PINS_IN_REG], + i % GPIO_PINS_IN_REG); + sc->gpio_pins[i].gp_flags = + gpio_read(sc, GPIO_OE, &sc->gpio_pins[i]) != 0 ? + GPIO_PIN_OUTPUT : GPIO_PIN_INPUT; + } + + sc->sc_busdev = gpiobus_attach_bus(dev); + if (sc->sc_busdev == NULL) { + tegra_gpio_detach(dev); + return (ENXIO); + } + + return (bus_generic_attach(dev)); +} + +static int +tegra_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent, + int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) +{ + + if (gcells != 2) + return (ERANGE); + *pin = gpios[0]; + *flags= gpios[1]; + return (0); +} + +static phandle_t +tegra_gpio_get_node(device_t bus, device_t dev) +{ + + /* We only have one child, the GPIO bus, which needs our own node. */ + return (ofw_bus_get_node(bus)); +} + +static device_method_t tegra_gpio_methods[] = { + DEVMETHOD(device_probe, tegra_gpio_probe), + DEVMETHOD(device_attach, tegra_gpio_attach), + DEVMETHOD(device_detach, tegra_gpio_detach), + + /* GPIO protocol */ + DEVMETHOD(gpio_get_bus, tegra_gpio_get_bus), + DEVMETHOD(gpio_pin_max, tegra_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, tegra_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, tegra_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, tegra_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, tegra_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, tegra_gpio_pin_get), + DEVMETHOD(gpio_pin_set, tegra_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, tegra_gpio_pin_toggle), + DEVMETHOD(gpio_map_gpios, tegra_map_gpios), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, tegra_gpio_get_node), + + DEVMETHOD_END +}; + +static driver_t tegra_gpio_driver = { + "gpio", + tegra_gpio_methods, + sizeof(struct tegra_gpio_softc), +}; +static devclass_t tegra_gpio_devclass; + +EARLY_DRIVER_MODULE(tegra_gpio, simplebus, tegra_gpio_driver, + tegra_gpio_devclass, 0, 0, 70); diff --git a/sys/arm/nvidia/tegra_i2c.c b/sys/arm/nvidia/tegra_i2c.c new file mode 100644 index 0000000..65d8935 --- /dev/null +++ b/sys/arm/nvidia/tegra_i2c.c @@ -0,0 +1,804 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * I2C driver for Tegra SoCs. + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/limits.h> +#include <sys/module.h> +#include <sys/resource.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <sys/lock.h> +#include <sys/mutex.h> + +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> +#include <dev/fdt/fdt_common.h> +#include <dev/iicbus/iiconf.h> +#include <dev/iicbus/iicbus.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include "iicbus_if.h" + +#define I2C_CNFG 0x000 +#define I2C_CNFG_MSTR_CLR_BUS_ON_TIMEOUT (1 << 15) +#define I2C_CNFG_DEBOUNCE_CNT(x) (((x) & 0x07) << 12) +#define I2C_CNFG_NEW_MASTER_FSM (1 << 11) +#define I2C_CNFG_PACKET_MODE_EN (1 << 10) +#define I2C_CNFG_SEND (1 << 9) +#define I2C_CNFG_NOACK (1 << 8) +#define I2C_CNFG_CMD2 (1 << 7) +#define I2C_CNFG_CMD1 (1 << 6) +#define I2C_CNFG_START (1 << 5) +#define I2C_CNFG_SLV2 (1 << 4) +#define I2C_CNFG_LENGTH_SHIFT 1 +#define I2C_CNFG_LENGTH_MASK 0x7 +#define I2C_CNFG_A_MOD (1 << 0) + +#define I2C_CMD_ADDR0 0x004 +#define I2C_CMD_ADDR1 0x008 +#define I2C_CMD_DATA1 0x00c +#define I2C_CMD_DATA2 0x010 +#define I2C_STATUS 0x01c +#define I2C_SL_CNFG 0x020 +#define I2C_SL_RCVD 0x024 +#define I2C_SL_STATUS 0x028 +#define I2C_SL_ADDR1 0x02c +#define I2C_SL_ADDR2 0x030 +#define I2C_TLOW_SEXT 0x034 +#define I2C_SL_DELAY_COUNT 0x03c +#define I2C_SL_INT_MASK 0x040 +#define I2C_SL_INT_SOURCE 0x044 +#define I2C_SL_INT_SET 0x048 +#define I2C_TX_PACKET_FIFO 0x050 +#define I2C_RX_FIFO 0x054 +#define I2C_PACKET_TRANSFER_STATUS 0x058 +#define I2C_FIFO_CONTROL 0x05c +#define I2C_FIFO_CONTROL_SLV_TX_FIFO_TRIG(x) (((x) & 0x07) << 13) +#define I2C_FIFO_CONTROL_SLV_RX_FIFO_TRIG(x) (((x) & 0x07) << 10) +#define I2C_FIFO_CONTROL_SLV_TX_FIFO_FLUSH (1 << 9) +#define I2C_FIFO_CONTROL_SLV_RX_FIFO_FLUSH (1 << 8) +#define I2C_FIFO_CONTROL_TX_FIFO_TRIG(x) (((x) & 0x07) << 5) +#define I2C_FIFO_CONTROL_RX_FIFO_TRIG(x) (((x) & 0x07) << 2) +#define I2C_FIFO_CONTROL_TX_FIFO_FLUSH (1 << 1) +#define I2C_FIFO_CONTROL_RX_FIFO_FLUSH (1 << 0) + +#define I2C_FIFO_STATUS 0x060 +#define I2C_FIFO_STATUS_SLV_XFER_ERR_REASON (1 << 25) +#define I2C_FIFO_STATUS_TX_FIFO_SLV_EMPTY_CNT(x) (((x) >> 20) & 0xF) +#define I2C_FIFO_STATUS_RX_FIFO_SLV_FULL_CNT(x) (((x) >> 16) & 0xF) +#define I2C_FIFO_STATUS_TX_FIFO_EMPTY_CNT(x) (((x) >> 4) & 0xF) +#define I2C_FIFO_STATUS_RX_FIFO_FULL_CNT(x) (((x) >> 0) & 0xF) + +#define I2C_INTERRUPT_MASK_REGISTER 0x064 +#define I2C_INTERRUPT_STATUS_REGISTER 0x068 +#define I2C_INT_SLV_ACK_WITHHELD (1 << 28) +#define I2C_INT_SLV_RD2WR (1 << 27) +#define I2C_INT_SLV_WR2RD (1 << 26) +#define I2C_INT_SLV_PKT_XFER_ERR (1 << 25) +#define I2C_INT_SLV_TX_BUFFER_REQ (1 << 24) +#define I2C_INT_SLV_RX_BUFFER_FILLED (1 << 23) +#define I2C_INT_SLV_PACKET_XFER_COMPLETE (1 << 22) +#define I2C_INT_SLV_TFIFO_OVF (1 << 21) +#define I2C_INT_SLV_RFIFO_UNF (1 << 20) +#define I2C_INT_SLV_TFIFO_DATA_REQ (1 << 17) +#define I2C_INT_SLV_RFIFO_DATA_REQ (1 << 16) +#define I2C_INT_BUS_CLEAR_DONE (1 << 11) +#define I2C_INT_TLOW_MEXT_TIMEOUT (1 << 10) +#define I2C_INT_TLOW_SEXT_TIMEOUT (1 << 9) +#define I2C_INT_TIMEOUT (1 << 8) +#define I2C_INT_PACKET_XFER_COMPLETE (1 << 7) +#define I2C_INT_ALL_PACKETS_XFER_COMPLETE (1 << 6) +#define I2C_INT_TFIFO_OVR (1 << 5) +#define I2C_INT_RFIFO_UNF (1 << 4) +#define I2C_INT_NOACK (1 << 3) +#define I2C_INT_ARB_LOST (1 << 2) +#define I2C_INT_TFIFO_DATA_REQ (1 << 1) +#define I2C_INT_RFIFO_DATA_REQ (1 << 0) +#define I2C_ERROR_MASK (I2C_INT_ARB_LOST | I2C_INT_NOACK | \ + I2C_INT_RFIFO_UNF | I2C_INT_TFIFO_OVR) + +#define I2C_CLK_DIVISOR 0x06c +#define I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT 16 +#define I2C_CLK_DIVISOR_STD_FAST_MODE_MASK 0xffff +#define I2C_CLK_DIVISOR_HSMODE_SHIFT 0 +#define I2C_CLK_DIVISOR_HSMODE_MASK 0xffff +#define I2C_INTERRUPT_SOURCE_REGISTER 0x070 +#define I2C_INTERRUPT_SET_REGISTER 0x074 +#define I2C_SLV_TX_PACKET_FIFO 0x07c +#define I2C_SLV_PACKET_STATUS 0x080 +#define I2C_BUS_CLEAR_CONFIG 0x084 +#define I2C_BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD(x) (((x) & 0xFF) << 16) +#define I2C_BUS_CLEAR_CONFIG_BC_STOP_COND (1 << 2) +#define I2C_BUS_CLEAR_CONFIG_BC_TERMINATE (1 << 1) +#define I2C_BUS_CLEAR_CONFIG_BC_ENABLE (1 << 0) + +#define I2C_BUS_CLEAR_STATUS 0x088 +#define I2C_BUS_CLEAR_STATUS_BC_STATUS (1 << 0) + +#define I2C_CONFIG_LOAD 0x08c +#define I2C_CONFIG_LOAD_TIMEOUT_CONFIG_LOAD (1 << 2) +#define I2C_CONFIG_LOAD_SLV_CONFIG_LOAD (1 << 1) +#define I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD (1 << 0) + +#define I2C_INTERFACE_TIMING_0 0x094 +#define I2C_INTERFACE_TIMING_1 0x098 +#define I2C_HS_INTERFACE_TIMING_0 0x09c +#define I2C_HS_INTERFACE_TIMING_1 0x0a0 + +/* Protocol header 0 */ +#define PACKET_HEADER0_HEADER_SIZE_SHIFT 28 +#define PACKET_HEADER0_HEADER_SIZE_MASK 0x3 +#define PACKET_HEADER0_PACKET_ID_SHIFT 16 +#define PACKET_HEADER0_PACKET_ID_MASK 0xff +#define PACKET_HEADER0_CONT_ID_SHIFT 12 +#define PACKET_HEADER0_CONT_ID_MASK 0xf +#define PACKET_HEADER0_PROTOCOL_I2C (1 << 4) +#define PACKET_HEADER0_TYPE_SHIFT 0 +#define PACKET_HEADER0_TYPE_MASK 0x7 + +/* I2C header */ +#define I2C_HEADER_HIGHSPEED_MODE (1 << 22) +#define I2C_HEADER_CONT_ON_NAK (1 << 21) +#define I2C_HEADER_SEND_START_BYTE (1 << 20) +#define I2C_HEADER_READ (1 << 19) +#define I2C_HEADER_10BIT_ADDR (1 << 18) +#define I2C_HEADER_IE_ENABLE (1 << 17) +#define I2C_HEADER_REPEAT_START (1 << 16) +#define I2C_HEADER_CONTINUE_XFER (1 << 15) +#define I2C_HEADER_MASTER_ADDR_SHIFT 12 +#define I2C_HEADER_MASTER_ADDR_MASK 0x7 +#define I2C_HEADER_SLAVE_ADDR_SHIFT 0 +#define I2C_HEADER_SLAVE_ADDR_MASK 0x3ff + +#define I2C_CLK_DIVISOR_STD_FAST_MODE 0x19 +#define I2C_CLK_MULTIPLIER_STD_FAST_MODE 8 + +#define I2C_REQUEST_TIMEOUT (5 * hz) + +#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) + +#define LOCK(_sc) mtx_lock(&(_sc)->mtx) +#define UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) +#define SLEEP(_sc, timeout) \ + mtx_sleep(sc, &sc->mtx, 0, "i2cbuswait", timeout); +#define LOCK_INIT(_sc) \ + mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_i2c", MTX_DEF) +#define LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx) +#define ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED) +#define ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED) + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-i2c", 1}, + {NULL, 0} +}; +enum tegra_i2c_xfer_type { + XFER_STOP, /* Send stop condition after xfer */ + XFER_REPEAT_START, /* Send repeated start after xfer */ + XFER_CONTINUE /* Don't send nothing */ +} ; + +struct tegra_i2c_softc { + device_t dev; + struct mtx mtx; + + struct resource *mem_res; + struct resource *irq_res; + void *irq_h; + + device_t iicbus; + clk_t clk; + hwreset_t reset; + uint32_t core_freq; + uint32_t bus_freq; + int bus_inuse; + + struct iic_msg *msg; + int msg_idx; + uint32_t bus_err; + int done; +}; + +static int +tegra_i2c_flush_fifo(struct tegra_i2c_softc *sc) +{ + int timeout; + uint32_t reg; + + reg = RD4(sc, I2C_FIFO_CONTROL); + reg |= I2C_FIFO_CONTROL_TX_FIFO_FLUSH | I2C_FIFO_CONTROL_RX_FIFO_FLUSH; + WR4(sc, I2C_FIFO_CONTROL, reg); + + timeout = 10; + while (timeout > 0) { + reg = RD4(sc, I2C_FIFO_CONTROL); + reg &= I2C_FIFO_CONTROL_TX_FIFO_FLUSH | + I2C_FIFO_CONTROL_RX_FIFO_FLUSH; + if (reg == 0) + break; + DELAY(10); + } + if (timeout <= 0) { + device_printf(sc->dev, "FIFO flush timedout\n"); + return (ETIMEDOUT); + } + return (0); +} + +static void +tegra_i2c_setup_clk(struct tegra_i2c_softc *sc, int clk_freq) +{ + int div; + + div = ((sc->core_freq / clk_freq) / 10) - 1; + if ((sc->core_freq / (10 * (div + 1))) > clk_freq) + div++; + if (div > 65535) + div = 65535; + WR4(sc, I2C_CLK_DIVISOR, + (1 << I2C_CLK_DIVISOR_HSMODE_SHIFT) | + (div << I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT)); +} + +static void +tegra_i2c_bus_clear(struct tegra_i2c_softc *sc) +{ + int timeout; + uint32_t reg, status; + + WR4(sc, I2C_BUS_CLEAR_CONFIG, + I2C_BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD(18) | + I2C_BUS_CLEAR_CONFIG_BC_STOP_COND | + I2C_BUS_CLEAR_CONFIG_BC_TERMINATE); + + WR4(sc, I2C_CONFIG_LOAD, I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD); + for (timeout = 1000; timeout > 0; timeout--) { + if (RD4(sc, I2C_CONFIG_LOAD) == 0) + break; + DELAY(10); + } + if (timeout <= 0) + device_printf(sc->dev, "config load timeouted\n"); + reg = RD4(sc, I2C_BUS_CLEAR_CONFIG); + reg |= I2C_BUS_CLEAR_CONFIG_BC_ENABLE; + WR4(sc, I2C_BUS_CLEAR_CONFIG,reg); + + for (timeout = 1000; timeout > 0; timeout--) { + if ((RD4(sc, I2C_BUS_CLEAR_CONFIG) & + I2C_BUS_CLEAR_CONFIG_BC_ENABLE) == 0) + break; + DELAY(10); + } + if (timeout <= 0) + device_printf(sc->dev, "bus clear timeouted\n"); + + status = RD4(sc, I2C_BUS_CLEAR_STATUS); + if ((status & I2C_BUS_CLEAR_STATUS_BC_STATUS) == 0) + device_printf(sc->dev, "bus clear failed\n"); +} + +static int +tegra_i2c_hw_init(struct tegra_i2c_softc *sc) +{ + int rv, timeout; + + /* Reset the core. */ + rv = hwreset_assert(sc->reset); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert reset\n"); + return (rv); + } + DELAY(10); + rv = hwreset_deassert(sc->reset); + if (rv != 0) { + device_printf(sc->dev, "Cannot clear reset\n"); + return (rv); + } + + WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0); + WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, 0xFFFFFFFF); + WR4(sc, I2C_CNFG, I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN | + I2C_CNFG_DEBOUNCE_CNT(2)); + + tegra_i2c_setup_clk(sc, sc->bus_freq); + + WR4(sc, I2C_FIFO_CONTROL, I2C_FIFO_CONTROL_TX_FIFO_TRIG(7) | + I2C_FIFO_CONTROL_RX_FIFO_TRIG(0)); + + WR4(sc, I2C_CONFIG_LOAD, I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD); + for (timeout = 1000; timeout > 0; timeout--) { + if (RD4(sc, I2C_CONFIG_LOAD) == 0) + break; + DELAY(10); + } + if (timeout <= 0) + device_printf(sc->dev, "config load timeouted\n"); + + tegra_i2c_bus_clear(sc); + return (0); +} + +static int +tegra_i2c_tx(struct tegra_i2c_softc *sc) +{ + uint32_t reg; + int cnt, i; + + if (sc->msg_idx >= sc->msg->len) + panic("Invalid call to tegra_i2c_tx\n"); + + while(sc->msg_idx < sc->msg->len) { + reg = RD4(sc, I2C_FIFO_STATUS); + if (I2C_FIFO_STATUS_TX_FIFO_EMPTY_CNT(reg) == 0) + break; + cnt = min(4, sc->msg->len - sc->msg_idx); + reg = 0; + for (i = 0; i < cnt; i++) { + reg |= sc->msg->buf[sc->msg_idx] << (i * 8); + sc->msg_idx++; + } + WR4(sc, I2C_TX_PACKET_FIFO, reg); + } + if (sc->msg_idx >= sc->msg->len) + return (0); + return (sc->msg->len - sc->msg_idx - 1); +} + +static int +tegra_i2c_rx(struct tegra_i2c_softc *sc) +{ + uint32_t reg; + int cnt, i; + + if (sc->msg_idx >= sc->msg->len) + panic("Invalid call to tegra_i2c_rx\n"); + + while(sc->msg_idx < sc->msg->len) { + reg = RD4(sc, I2C_FIFO_STATUS); + if (I2C_FIFO_STATUS_RX_FIFO_FULL_CNT(reg) == 0) + break; + cnt = min(4, sc->msg->len - sc->msg_idx); + reg = RD4(sc, I2C_RX_FIFO); + for (i = 0; i < cnt; i++) { + sc->msg->buf[sc->msg_idx] = (reg >> (i * 8)) & 0xFF; + sc->msg_idx++; + } + } + + if (sc->msg_idx >= sc->msg->len) + return (0); + return (sc->msg->len - sc->msg_idx - 1); +} + +static void +tegra_i2c_intr(void *arg) +{ + struct tegra_i2c_softc *sc; + uint32_t status, reg; + int rv; + + sc = (struct tegra_i2c_softc *)arg; + + LOCK(sc); + status = RD4(sc, I2C_INTERRUPT_SOURCE_REGISTER); + if (sc->msg == NULL) { + /* Unexpected interrupt - disable FIFOs, clear reset. */ + reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER); + reg &= ~I2C_INT_TFIFO_DATA_REQ; + reg &= ~I2C_INT_RFIFO_DATA_REQ; + WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0); + WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, status); + UNLOCK(sc); + return; + } + + if ((status & I2C_ERROR_MASK) != 0) { + if (status & I2C_INT_NOACK) + sc->bus_err = IIC_ENOACK; + if (status & I2C_INT_ARB_LOST) + sc->bus_err = IIC_EBUSERR; + if ((status & I2C_INT_TFIFO_OVR) || + (status & I2C_INT_RFIFO_UNF)) + sc->bus_err = IIC_EBUSERR; + sc->done = 1; + } else if ((status & I2C_INT_RFIFO_DATA_REQ) && + (sc->msg != NULL) && (sc->msg->flags & IIC_M_RD)) { + rv = tegra_i2c_rx(sc); + if (rv == 0) { + reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER); + reg &= ~I2C_INT_RFIFO_DATA_REQ; + WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg); + } + } else if ((status & I2C_INT_TFIFO_DATA_REQ) && + (sc->msg != NULL) && !(sc->msg->flags & IIC_M_RD)) { + rv = tegra_i2c_tx(sc); + if (rv == 0) { + reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER); + reg &= ~I2C_INT_TFIFO_DATA_REQ; + WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg); + } + } else if ((status & I2C_INT_RFIFO_DATA_REQ) || + (status & I2C_INT_TFIFO_DATA_REQ)) { + device_printf(sc->dev, "Unexpected data interrupt: 0x%08X\n", + status); + reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER); + reg &= ~I2C_INT_TFIFO_DATA_REQ; + reg &= ~I2C_INT_RFIFO_DATA_REQ; + WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg); + } + if (status & I2C_INT_PACKET_XFER_COMPLETE) + sc->done = 1; + WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, status); + if (sc->done) { + WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0); + wakeup(&(sc->done)); + } + UNLOCK(sc); +} + +static void +tegra_i2c_start_msg(struct tegra_i2c_softc *sc, struct iic_msg *msg, + enum tegra_i2c_xfer_type xtype) +{ + uint32_t tmp, mask; + + /* Packet header. */ + tmp = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) | + PACKET_HEADER0_PROTOCOL_I2C | + (1 << PACKET_HEADER0_CONT_ID_SHIFT) | + (1 << PACKET_HEADER0_PACKET_ID_SHIFT); + WR4(sc, I2C_TX_PACKET_FIFO, tmp); + + + /* Packet size. */ + WR4(sc, I2C_TX_PACKET_FIFO, msg->len - 1); + + /* I2C header. */ + tmp = I2C_HEADER_IE_ENABLE; + if (xtype == XFER_CONTINUE) + tmp |= I2C_HEADER_CONTINUE_XFER; + else if (xtype == XFER_REPEAT_START) + tmp |= I2C_HEADER_REPEAT_START; + tmp |= msg->slave << I2C_HEADER_SLAVE_ADDR_SHIFT; + if (msg->flags & IIC_M_RD) { + tmp |= I2C_HEADER_READ; + tmp |= 1 << I2C_HEADER_SLAVE_ADDR_SHIFT; + } else + tmp &= ~(1 << I2C_HEADER_SLAVE_ADDR_SHIFT); + + WR4(sc, I2C_TX_PACKET_FIFO, tmp); + + /* Interrupt mask. */ + mask = I2C_INT_NOACK | I2C_INT_ARB_LOST | I2C_INT_PACKET_XFER_COMPLETE; + if (msg->flags & IIC_M_RD) + mask |= I2C_INT_RFIFO_DATA_REQ; + else + mask |= I2C_INT_TFIFO_DATA_REQ; + WR4(sc, I2C_INTERRUPT_MASK_REGISTER, mask); +} + +static int +tegra_i2c_poll(struct tegra_i2c_softc *sc) +{ + int timeout; + + for(timeout = 10000; timeout > 0; timeout--) { + UNLOCK(sc); + tegra_i2c_intr(sc); + LOCK(sc); + if (sc->done != 0) + break; + DELAY(1); + } + if (timeout <= 0) + return (ETIMEDOUT); + return (0); +} + +static int +tegra_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) +{ + int rv, i; + struct tegra_i2c_softc *sc; + enum tegra_i2c_xfer_type xtype; + + sc = device_get_softc(dev); + LOCK(sc); + + /* Get the bus. */ + while (sc->bus_inuse == 1) + SLEEP(sc, 0); + sc->bus_inuse = 1; + + rv = 0; + for (i = 0; i < nmsgs; i++) { + sc->msg = &msgs[i]; + sc->msg_idx = 0; + sc->bus_err = 0; + sc->done = 0; + /* Check for valid parameters. */ + if (sc->msg == NULL || sc->msg->buf == NULL || + sc->msg->len == 0) { + rv = EINVAL; + break; + } + + /* Get flags for next transfer. */ + if (i == (nmsgs - 1)) { + if (msgs[i].flags & IIC_M_NOSTOP) + xtype = XFER_CONTINUE; + else + xtype = XFER_STOP; + } else { + if (msgs[i + 1].flags & IIC_M_NOSTART) + xtype = XFER_CONTINUE; + else + xtype = XFER_REPEAT_START; + } + tegra_i2c_start_msg(sc, sc->msg, xtype); + if (cold) + rv = tegra_i2c_poll(sc); + else + rv = msleep(&sc->done, &sc->mtx, PZERO, "iic", + I2C_REQUEST_TIMEOUT); + + WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0); + WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, 0xFFFFFFFF); + if (rv == 0) + rv = sc->bus_err; + if (rv != 0) + break; + } + + if (rv != 0) { + tegra_i2c_hw_init(sc); + tegra_i2c_flush_fifo(sc); + } + + sc->msg = NULL; + sc->msg_idx = 0; + sc->bus_err = 0; + sc->done = 0; + + /* Wake up the processes that are waiting for the bus. */ + sc->bus_inuse = 0; + wakeup(sc); + UNLOCK(sc); + + return (rv); +} + +static int +tegra_i2c_iicbus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) +{ + struct tegra_i2c_softc *sc; + int busfreq; + + sc = device_get_softc(dev); + busfreq = IICBUS_GET_FREQUENCY(sc->iicbus, speed); + sc = device_get_softc(dev); + LOCK(sc); + tegra_i2c_setup_clk(sc, busfreq); + UNLOCK(sc); + return (0); +} + +static int +tegra_i2c_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + return (BUS_PROBE_DEFAULT); +} + +static int +tegra_i2c_attach(device_t dev) +{ + int rv, rid; + phandle_t node; + struct tegra_i2c_softc *sc; + uint64_t freq; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + LOCK_INIT(sc); + + /* Get the memory resource for the register mapping. */ + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot map registers.\n"); + rv = ENXIO; + goto fail; + } + + /* Allocate our IRQ resource. */ + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "Cannot allocate interrupt.\n"); + rv = ENXIO; + goto fail; + } + + /* FDT resources. */ + rv = clk_get_by_ofw_name(dev, "div-clk", &sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot get i2c clock: %d\n", rv); + goto fail; + } + rv = hwreset_get_by_ofw_name(sc->dev, "i2c", &sc->reset); + if (rv != 0) { + device_printf(sc->dev, "Cannot get i2c reset\n"); + return (ENXIO); + } + rv = OF_getencprop(node, "clock-frequency", &sc->bus_freq, + sizeof(sc->bus_freq)); + if (rv != sizeof(sc->bus_freq)) { + sc->bus_freq = 100000; + goto fail; + } + + /* Request maximum frequency for I2C block 136MHz (408MHz / 3). */ + rv = clk_set_freq(sc->clk, 136000000, CLK_SET_ROUND_DOWN); + if (rv != 0) { + device_printf(dev, "Cannot set clock frequency\n"); + goto fail; + } + rv = clk_get_freq(sc->clk, &freq); + if (rv != 0) { + device_printf(dev, "Cannot get clock frequency\n"); + goto fail; + } + sc->core_freq = (uint32_t)freq; + + rv = clk_enable(sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot enable clock: %d\n", rv); + goto fail; + } + + /* Init hardware. */ + rv = tegra_i2c_hw_init(sc); + if (rv) { + device_printf(dev, "tegra_i2c_activate failed\n"); + goto fail; + } + + /* Setup interrupt. */ + rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, tegra_i2c_intr, sc, &sc->irq_h); + if (rv) { + device_printf(dev, "Cannot setup interrupt.\n"); + goto fail; + } + + /* Attach the iicbus. */ + sc->iicbus = device_add_child(dev, "iicbus", -1); + if (sc->iicbus == NULL) { + device_printf(dev, "Could not allocate iicbus instance.\n"); + rv = ENXIO; + goto fail; + } + + /* Probe and attach the iicbus. */ + return (bus_generic_attach(dev)); + +fail: + if (sc->irq_h != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_h); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + LOCK_DESTROY(sc); + + return (rv); +} + +static int +tegra_i2c_detach(device_t dev) +{ + struct tegra_i2c_softc *sc; + int rv; + + sc = device_get_softc(dev); + tegra_i2c_hw_init(sc); + if (sc->irq_h != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_h); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + LOCK_DESTROY(sc); + if (sc->iicbus) + rv = device_delete_child(dev, sc->iicbus); + return (bus_generic_detach(dev)); +} + +static phandle_t +tegra_i2c_get_node(device_t bus, device_t dev) +{ + + /* Share controller node with iibus device. */ + return (ofw_bus_get_node(bus)); +} + +static device_method_t tegra_i2c_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_i2c_probe), + DEVMETHOD(device_attach, tegra_i2c_attach), + DEVMETHOD(device_detach, tegra_i2c_detach), + + /* Bus interface */ + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), + DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), + DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), + + /* OFW methods */ + DEVMETHOD(ofw_bus_get_node, tegra_i2c_get_node), + + /* iicbus interface */ + DEVMETHOD(iicbus_callback, iicbus_null_callback), + DEVMETHOD(iicbus_reset, tegra_i2c_iicbus_reset), + DEVMETHOD(iicbus_transfer, tegra_i2c_transfer), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(iichb, tegra_i2c_driver, tegra_i2c_methods, + sizeof(struct tegra_i2c_softc)); +static devclass_t tegra_i2c_devclass; +EARLY_DRIVER_MODULE(iichb, simplebus, tegra_i2c_driver, tegra_i2c_devclass, 0, + 0, 73); diff --git a/sys/arm/nvidia/tegra_lic.c b/sys/arm/nvidia/tegra_lic.c new file mode 100644 index 0000000..6228c01 --- /dev/null +++ b/sys/arm/nvidia/tegra_lic.c @@ -0,0 +1,288 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Local interrupt controller driver for Tegra SoCs. + */ +#include <sys/param.h> +#include <sys/module.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/rman.h> + +#include <machine/fdt.h> +#include <machine/intr.h> +#include <machine/resource.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include "pic_if.h" + +#define LIC_VIRQ_CPU 0x00 +#define LIC_VIRQ_COP 0x04 +#define LIC_VFRQ_CPU 0x08 +#define LIC_VFRQ_COP 0x0c +#define LIC_ISR 0x10 +#define LIC_FIR 0x14 +#define LIC_FIR_SET 0x18 +#define LIC_FIR_CLR 0x1c +#define LIC_CPU_IER 0x20 +#define LIC_CPU_IER_SET 0x24 +#define LIC_CPU_IER_CLR 0x28 +#define LIC_CPU_IEP_CLASS 0x2C +#define LIC_COP_IER 0x30 +#define LIC_COP_IER_SET 0x34 +#define LIC_COP_IER_CLR 0x38 +#define LIC_COP_IEP_CLASS 0x3c + +#define WR4(_sc, _b, _r, _v) bus_write_4((_sc)->mem_res[_b], (_r), (_v)) +#define RD4(_sc, _b, _r) bus_read_4((_sc)->mem_res[_b], (_r)) + +static struct resource_spec lic_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_MEMORY, 1, RF_ACTIVE }, + { SYS_RES_MEMORY, 2, RF_ACTIVE }, + { SYS_RES_MEMORY, 3, RF_ACTIVE }, + { SYS_RES_MEMORY, 4, RF_ACTIVE }, + { -1, 0 } +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-ictlr", 1}, + {NULL, 0} +}; + +struct tegra_lic_sc { + device_t dev; + struct resource *mem_res[nitems(lic_spec)]; + device_t parent; +}; + +static int +tegra_lic_alloc_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + return (PIC_ALLOC_INTR(sc->parent, isrc, res, data)); +} + +static void +tegra_lic_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + PIC_DISABLE_INTR(sc->parent, isrc); +} + +static void +tegra_lic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + PIC_ENABLE_INTR(sc->parent, isrc); +} + +static int +tegra_lic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + return (PIC_MAP_INTR(sc->parent, data, isrcp)); +} + +static int +tegra_lic_release_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + return (PIC_RELEASE_INTR(sc->parent, isrc, res, data)); +} + +static int +tegra_lic_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + return (PIC_SETUP_INTR(sc->parent, isrc, res, data)); +} + +static int +tegra_lic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data)); +} + +static void +tegra_lic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + PIC_PRE_ITHREAD(sc->parent, isrc); +} + + +static void +tegra_lic_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + PIC_POST_ITHREAD(sc->parent, isrc); +} + +static void +tegra_lic_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + PIC_POST_FILTER(sc->parent, isrc); +} + +#ifdef SMP +static int +tegra_lic_bind_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + return (PIC_BIND_INTR(sc->parent, isrc)); +} +#endif + +static int +tegra_lic_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + return (BUS_PROBE_DEFAULT); +} + +static int +tegra_lic_attach(device_t dev) +{ + struct tegra_lic_sc *sc; + phandle_t node; + phandle_t parent_xref; + int i, rv; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + rv = OF_getencprop(node, "interrupt-parent", &parent_xref, + sizeof(parent_xref)); + if (rv <= 0) { + device_printf(dev, "Cannot read parent node property\n"); + goto fail; + } + sc->parent = OF_device_from_xref(parent_xref); + if (sc->parent == NULL) { + device_printf(dev, "Cannott find parent controller\n"); + goto fail; + } + + if (bus_alloc_resources(dev, lic_spec, sc->mem_res)) { + device_printf(dev, "Cannott allocate resources\n"); + goto fail; + } + + /* Disable all interrupts, route all to irq */ + for (i = 0; i < nitems(lic_spec); i++) { + if (sc->mem_res[i] == NULL) + continue; + WR4(sc, i, LIC_CPU_IER_CLR, 0xFFFFFFFF); + WR4(sc, i, LIC_CPU_IEP_CLASS, 0); + } + + + if (intr_pic_register(dev, OF_xref_from_node(node)) != 0) { + device_printf(dev, "Cannot register PIC\n"); + goto fail; + } + return (0); + +fail: + bus_release_resources(dev, lic_spec, sc->mem_res); + return (ENXIO); +} + +static int +tegra_lic_detach(device_t dev) +{ + struct tegra_lic_sc *sc; + int i; + + sc = device_get_softc(dev); + for (i = 0; i < nitems(lic_spec); i++) { + if (sc->mem_res[i] == NULL) + continue; + bus_release_resource(dev, SYS_RES_MEMORY, i, + sc->mem_res[i]); + } + return (0); +} + +static device_method_t tegra_lic_methods[] = { + DEVMETHOD(device_probe, tegra_lic_probe), + DEVMETHOD(device_attach, tegra_lic_attach), + DEVMETHOD(device_detach, tegra_lic_detach), + + /* Interrupt controller interface */ + DEVMETHOD(pic_alloc_intr, tegra_lic_alloc_intr), + DEVMETHOD(pic_disable_intr, tegra_lic_disable_intr), + DEVMETHOD(pic_enable_intr, tegra_lic_enable_intr), + DEVMETHOD(pic_map_intr, tegra_lic_map_intr), + DEVMETHOD(pic_release_intr, tegra_lic_release_intr), + DEVMETHOD(pic_setup_intr, tegra_lic_setup_intr), + DEVMETHOD(pic_teardown_intr, tegra_lic_teardown_intr), + DEVMETHOD(pic_pre_ithread, tegra_lic_pre_ithread), + DEVMETHOD(pic_post_ithread, tegra_lic_post_ithread), + DEVMETHOD(pic_post_filter, tegra_lic_post_filter), +#ifdef SMP + DEVMETHOD(pic_bind_intr, tegra_lic_bind_intr), +#endif + DEVMETHOD_END +}; +devclass_t tegra_lic_devclass; +DEFINE_CLASS_0(tegra_lic, tegra_lic_driver, tegra_lic_methods, + sizeof(struct tegra_lic_sc)); +EARLY_DRIVER_MODULE(tegra_lic, simplebus, tegra_lic_driver, tegra_lic_devclass, + NULL, NULL, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE + 1); diff --git a/sys/arm/nvidia/tegra_pcie.c b/sys/arm/nvidia/tegra_pcie.c new file mode 100644 index 0000000..e63ea47 --- /dev/null +++ b/sys/arm/nvidia/tegra_pcie.c @@ -0,0 +1,1691 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Nvidia Integrated PCI/PCI-Express controller driver. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/queue.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/endian.h> + +#include <machine/intr.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> +#include <dev/extres/phy/phy.h> +#include <dev/extres/regulator/regulator.h> +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_pci.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcib_private.h> + +#include <machine/devmap.h> +#include <machine/resource.h> +#include <machine/bus.h> + +#include "ofw_bus_if.h" +#include "pcib_if.h" + +#include <arm/nvidia/tegra_pmc.h> + +/* --- Move to ofw_pci.c/.h ----------------------- */ + +struct tegra_pci_range { + /* parsed phys.hi */ + int nonrelocatable; + int prefetchable; + int aliased; + int space_code; /* In native format (not shifted)*/ + int bus; + int device; + int function; + int reg; + pci_addr_t pci_addr; /* PCI Address */ + bus_addr_t host_addr; /* Host bus address*/ + bus_size_t size; /* Range size */ +}; + +static int +tegra_pci_get_ranges(phandle_t node, struct tegra_pci_range **ranges) +{ + int host_address_cells, pci_address_cells, size_cells; + cell_t *base_ranges; + ssize_t nbase_ranges; + int nranges; + int i, j, k; + uint32_t flags; + uint64_t tmp; + + host_address_cells = 1; + pci_address_cells = 3; + size_cells = 2; + OF_getencprop(OF_parent(node), "#address-cells", &host_address_cells, + sizeof(host_address_cells)); + OF_getencprop(node, "#address-cells", &pci_address_cells, + sizeof(pci_address_cells)); + OF_getencprop(node, "#size-cells", &size_cells, sizeof(size_cells)); + + nbase_ranges = OF_getproplen(node, "ranges"); + if (nbase_ranges <= 0) + return (-1); + nranges = nbase_ranges / sizeof(cell_t) / + (pci_address_cells + host_address_cells + size_cells); + + *ranges = malloc(nranges * sizeof(struct tegra_pci_range), + M_DEVBUF, M_WAITOK); + base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); + OF_getencprop(node, "ranges", base_ranges, nbase_ranges); + + for (i = 0, j = 0; i < nranges; i++) { + flags = base_ranges[j++]; + (*ranges)[i].nonrelocatable = + flags & OFW_PCI_PHYS_HI_NONRELOCATABLE ? 1 : 0; + (*ranges)[i].prefetchable = + flags & OFW_PCI_PHYS_HI_PREFETCHABLE ? 1 : 0; + (*ranges)[i].aliased = + flags & OFW_PCI_PHYS_HI_ALIASED ? 1 : 0; + (*ranges)[i].space_code = flags & OFW_PCI_PHYS_HI_SPACEMASK; + (*ranges)[i].bus = OFW_PCI_PHYS_HI_BUS(flags); + (*ranges)[i].device = OFW_PCI_PHYS_HI_DEVICE(flags); + (*ranges)[i].function = OFW_PCI_PHYS_HI_FUNCTION(flags); + (*ranges)[i].reg = flags & OFW_PCI_PHYS_HI_REGISTERMASK; + + tmp = 0; + for (k = 0; k < pci_address_cells - 1; k++) { + tmp <<= 32; + tmp |= base_ranges[j++]; + } + (*ranges)[i].pci_addr = (pci_addr_t)tmp; + + tmp = 0; + for (k = 0; k < host_address_cells; k++) { + tmp <<= 32; + tmp |= base_ranges[j++]; + } + (*ranges)[i].host_addr = (bus_addr_t)tmp; + tmp = 0; + + for (k = 0; k < size_cells; k++) { + tmp <<= 32; + tmp |= base_ranges[j++]; + } + (*ranges)[i].size = (bus_size_t)tmp; + } + + free(base_ranges, M_DEVBUF); + return (nranges); +} + +/* -------------------------------------------------------------------------- */ +#define AFI_AXI_BAR0_SZ 0x000 +#define AFI_AXI_BAR1_SZ 0x004 +#define AFI_AXI_BAR2_SZ 0x008 +#define AFI_AXI_BAR3_SZ 0x00c +#define AFI_AXI_BAR4_SZ 0x010 +#define AFI_AXI_BAR5_SZ 0x014 +#define AFI_AXI_BAR0_START 0x018 +#define AFI_AXI_BAR1_START 0x01c +#define AFI_AXI_BAR2_START 0x020 +#define AFI_AXI_BAR3_START 0x024 +#define AFI_AXI_BAR4_START 0x028 +#define AFI_AXI_BAR5_START 0x02c +#define AFI_FPCI_BAR0 0x030 +#define AFI_FPCI_BAR1 0x034 +#define AFI_FPCI_BAR2 0x038 +#define AFI_FPCI_BAR3 0x03c +#define AFI_FPCI_BAR4 0x040 +#define AFI_FPCI_BAR5 0x044 +#define AFI_MSI_BAR_SZ 0x060 +#define AFI_MSI_FPCI_BAR_ST 0x064 +#define AFI_MSI_AXI_BAR_ST 0x068 + + +#define AFI_AXI_BAR6_SZ 0x134 +#define AFI_AXI_BAR7_SZ 0x138 +#define AFI_AXI_BAR8_SZ 0x13c +#define AFI_AXI_BAR6_START 0x140 +#define AFI_AXI_BAR7_START 0x144 +#define AFI_AXI_BAR8_START 0x148 +#define AFI_FPCI_BAR6 0x14c +#define AFI_FPCI_BAR7 0x150 +#define AFI_FPCI_BAR8 0x154 + +#define AFI_CONFIGURATION 0x0ac +#define AFI_CONFIGURATION_EN_FPCI (1 << 0) + +#define AFI_FPCI_ERROR_MASKS 0x0b0 +#define AFI_INTR_MASK 0x0b4 +#define AFI_INTR_MASK_MSI_MASK (1 << 8) +#define AFI_INTR_MASK_INT_MASK (1 << 0) + +#define AFI_INTR_CODE 0x0b8 +#define AFI_INTR_CODE_MASK 0xf +#define AFI_INTR_CODE_INT_CODE_INI_SLVERR 1 +#define AFI_INTR_CODE_INT_CODE_INI_DECERR 2 +#define AFI_INTR_CODE_INT_CODE_TGT_SLVERR 3 +#define AFI_INTR_CODE_INT_CODE_TGT_DECERR 4 +#define AFI_INTR_CODE_INT_CODE_TGT_WRERR 5 +#define AFI_INTR_CODE_INT_CODE_SM_MSG 6 +#define AFI_INTR_CODE_INT_CODE_DFPCI_DECERR 7 +#define AFI_INTR_CODE_INT_CODE_AXI_DECERR 8 +#define AFI_INTR_CODE_INT_CODE_FPCI_TIMEOUT 9 +#define AFI_INTR_CODE_INT_CODE_PE_PRSNT_SENSE 10 +#define AFI_INTR_CODE_INT_CODE_PE_CLKREQ_SENSE 11 +#define AFI_INTR_CODE_INT_CODE_CLKCLAMP_SENSE 12 +#define AFI_INTR_CODE_INT_CODE_RDY4PD_SENSE 13 +#define AFI_INTR_CODE_INT_CODE_P2P_ERROR 14 + + +#define AFI_INTR_SIGNATURE 0x0bc +#define AFI_UPPER_FPCI_ADDRESS 0x0c0 +#define AFI_SM_INTR_ENABLE 0x0c4 +#define AFI_SM_INTR_RP_DEASSERT (1 << 14) +#define AFI_SM_INTR_RP_ASSERT (1 << 13) +#define AFI_SM_INTR_HOTPLUG (1 << 12) +#define AFI_SM_INTR_PME (1 << 11) +#define AFI_SM_INTR_FATAL_ERROR (1 << 10) +#define AFI_SM_INTR_UNCORR_ERROR (1 << 9) +#define AFI_SM_INTR_CORR_ERROR (1 << 8) +#define AFI_SM_INTR_INTD_DEASSERT (1 << 7) +#define AFI_SM_INTR_INTC_DEASSERT (1 << 6) +#define AFI_SM_INTR_INTB_DEASSERT (1 << 5) +#define AFI_SM_INTR_INTA_DEASSERT (1 << 4) +#define AFI_SM_INTR_INTD_ASSERT (1 << 3) +#define AFI_SM_INTR_INTC_ASSERT (1 << 2) +#define AFI_SM_INTR_INTB_ASSERT (1 << 1) +#define AFI_SM_INTR_INTA_ASSERT (1 << 0) + +#define AFI_AFI_INTR_ENABLE 0x0c8 +#define AFI_AFI_INTR_ENABLE_CODE(code) (1 << (code)) + +#define AFI_PCIE_CONFIG 0x0f8 +#define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1)) +#define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0x6 +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR2_1 (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR4_1 (0x1 << 20) + +#define AFI_FUSE 0x104 +#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2) + +#define AFI_PEX0_CTRL 0x110 +#define AFI_PEX1_CTRL 0x118 +#define AFI_PEX2_CTRL 0x128 +#define AFI_PEX_CTRL_OVERRIDE_EN (1 << 4) +#define AFI_PEX_CTRL_REFCLK_EN (1 << 3) +#define AFI_PEX_CTRL_CLKREQ_EN (1 << 1) +#define AFI_PEX_CTRL_RST_L (1 << 0) + +#define AFI_AXI_BAR6_SZ 0x134 +#define AFI_AXI_BAR7_SZ 0x138 +#define AFI_AXI_BAR8_SZ 0x13c +#define AFI_AXI_BAR6_START 0x140 +#define AFI_AXI_BAR7_START 0x144 +#define AFI_AXI_BAR8_START 0x148 +#define AFI_FPCI_BAR6 0x14c +#define AFI_FPCI_BAR7 0x150 +#define AFI_FPCI_BAR8 0x154 +#define AFI_PLLE_CONTROL 0x160 +#define AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL (1 << 9) +#define AFI_PLLE_CONTROL_BYPASS_PCIE2PLLE_CONTROL (1 << 8) +#define AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN (1 << 1) +#define AFI_PLLE_CONTROL_PCIE2PLLE_CONTROL_EN (1 << 0) + +#define AFI_PEXBIAS_CTRL 0x168 + +/* FPCI Address space */ +#define FPCI_MAP_IO 0xfdfc000000ULL +#define FPCI_MAP_TYPE0_CONFIG 0xfdfc000000ULL +#define FPCI_MAP_TYPE1_CONFIG 0xfdff000000ULL +#define FPCI_MAP_EXT_TYPE0_CONFIG 0xfe00000000ULL +#define FPCI_MAP_EXT_TYPE1_CONFIG 0xfe10000000ULL + +/* Configuration space */ +#define RP_VEND_XP 0x00000F00 +#define RP_VEND_XP_DL_UP (1 << 30) + +#define RP_PRIV_MISC 0x00000FE0 +#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xE << 0) +#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xF << 0) + +#define RP_LINK_CONTROL_STATUS 0x00000090 +#define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000 +#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 + +#define TEGRA_PCIE_LINKUP_TIMEOUT 200 + +#define DEBUG +#ifdef DEBUG +#define debugf(fmt, args...) do { printf(fmt,##args); } while (0) +#else +#define debugf(fmt, args...) +#endif + +/* + * Configuration space format: + * [27:24] extended register + * [23:16] bus + * [15:11] slot (device) + * [10: 8] function + * [ 7: 0] register + */ +#define PCI_CFG_EXT_REG(reg) ((((reg) >> 8) & 0x0f) << 24) +#define PCI_CFG_BUS(bus) (((bus) & 0xff) << 16) +#define PCI_CFG_DEV(dev) (((dev) & 0x1f) << 11) +#define PCI_CFG_FUN(fun) (((fun) & 0x07) << 8) +#define PCI_CFG_BASE_REG(reg) ((reg) & 0xff) + +#define PADS_WR4(_sc, _r, _v) bus_write_4((_sc)-pads_mem_res, (_r), (_v)) +#define PADS_RD4(_sc, _r) bus_read_4((_sc)->pads_mem_res, (_r)) +#define AFI_WR4(_sc, _r, _v) bus_write_4((_sc)->afi_mem_res, (_r), (_v)) +#define AFI_RD4(_sc, _r) bus_read_4((_sc)->afi_mem_res, (_r)) + +static struct { + bus_size_t axi_start; + bus_size_t fpci_start; + bus_size_t size; +} bars[] = { + {AFI_AXI_BAR0_START, AFI_FPCI_BAR0, AFI_AXI_BAR0_SZ}, /* BAR 0 */ + {AFI_AXI_BAR1_START, AFI_FPCI_BAR1, AFI_AXI_BAR1_SZ}, /* BAR 1 */ + {AFI_AXI_BAR2_START, AFI_FPCI_BAR2, AFI_AXI_BAR2_SZ}, /* BAR 2 */ + {AFI_AXI_BAR3_START, AFI_FPCI_BAR3, AFI_AXI_BAR3_SZ}, /* BAR 3 */ + {AFI_AXI_BAR4_START, AFI_FPCI_BAR4, AFI_AXI_BAR4_SZ}, /* BAR 4 */ + {AFI_AXI_BAR5_START, AFI_FPCI_BAR5, AFI_AXI_BAR5_SZ}, /* BAR 5 */ + {AFI_AXI_BAR6_START, AFI_FPCI_BAR6, AFI_AXI_BAR6_SZ}, /* BAR 6 */ + {AFI_AXI_BAR7_START, AFI_FPCI_BAR7, AFI_AXI_BAR7_SZ}, /* BAR 7 */ + {AFI_AXI_BAR8_START, AFI_FPCI_BAR8, AFI_AXI_BAR8_SZ}, /* BAR 8 */ + {AFI_MSI_AXI_BAR_ST, AFI_MSI_FPCI_BAR_ST, AFI_MSI_BAR_SZ}, /* MSI 9 */ +}; + +/* Compatible devices. */ +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-pcie", 1}, + {NULL, 0}, +}; + +struct tegra_pcib_port { + int enabled; + int port_idx; /* chip port index */ + int num_lanes; /* number of lanes */ + bus_size_t afi_pex_ctrl; /* offset of afi_pex_ctrl */ + + /* Config space properties. */ + bus_addr_t rp_base_addr; /* PA of config window */ + bus_size_t rp_size; /* size of config window */ + bus_space_handle_t cfg_handle; /* handle of config window */ +}; + +#define TEGRA_PCIB_MAX_PORTS 3 +struct tegra_pcib_softc { + device_t dev; + struct mtx mtx; + struct ofw_bus_iinfo pci_iinfo; + struct rman pref_mem_rman; + struct rman mem_rman; + struct rman io_rman; + struct resource *pads_mem_res; + struct resource *afi_mem_res; + struct resource *cfg_mem_res; + struct resource *irq_res; + struct resource *msi_irq_res; + void *intr_cookie; + void *msi_intr_cookie; + + struct tegra_pci_range mem_range; + struct tegra_pci_range pref_mem_range; + struct tegra_pci_range io_range; + + phy_t phy; + clk_t clk_pex; + clk_t clk_afi; + clk_t clk_pll_e; + clk_t clk_cml; + hwreset_t hwreset_pex; + hwreset_t hwreset_afi; + hwreset_t hwreset_pcie_x; + regulator_t supply_avddio_pex; + regulator_t supply_dvddio_pex; + regulator_t supply_avdd_pex_pll; + regulator_t supply_hvdd_pex; + regulator_t supply_hvdd_pex_pll_e; + regulator_t supply_vddio_pex_ctl; + regulator_t supply_avdd_pll_erefe; + + int busnr; /* host bridge bus number */ + uint32_t msi_bitmap; + bus_addr_t cfg_base_addr; /* base address of config */ + bus_size_t cfg_cur_offs; /* currently mapped window */ + bus_space_handle_t cfg_handle; /* handle of config window */ + bus_space_tag_t bus_tag; /* tag of config window */ + int lanes_cfg; + int num_ports; + struct tegra_pcib_port *ports[TEGRA_PCIB_MAX_PORTS]; +}; + +/* ------------------------------------------------------------------------- */ +/* + * Resource manager + */ +static int +tegra_pcib_rman_init(struct tegra_pcib_softc *sc) +{ + int err; + char buf[64]; + + /* Memory management. */ + sc->pref_mem_rman.rm_type = RMAN_ARRAY; + snprintf(buf, sizeof(buf), "%s prefetchable memory space", + device_get_nameunit(sc->dev)); + sc->pref_mem_rman.rm_descr = strdup(buf, M_DEVBUF); + err = rman_init(&sc->pref_mem_rman); + if (err) + return (err); + + sc->mem_rman.rm_type = RMAN_ARRAY; + snprintf(buf, sizeof(buf), "%s non prefetchable memory space", + device_get_nameunit(sc->dev)); + sc->mem_rman.rm_descr = strdup(buf, M_DEVBUF); + err = rman_init(&sc->mem_rman); + if (err) + return (err); + + sc->io_rman.rm_type = RMAN_ARRAY; + snprintf(buf, sizeof(buf), "%s I/O space", + device_get_nameunit(sc->dev)); + sc->io_rman.rm_descr = strdup(buf, M_DEVBUF); + err = rman_init(&sc->io_rman); + if (err) { + rman_fini(&sc->mem_rman); + return (err); + } + + err = rman_manage_region(&sc->pref_mem_rman, + sc->pref_mem_range.host_addr, + sc->pref_mem_range.host_addr + sc->pref_mem_range.size - 1); + if (err) + goto error; + err = rman_manage_region(&sc->mem_rman, + sc->mem_range.host_addr, + sc->mem_range.host_addr + sc->mem_range.size - 1); + if (err) + goto error; + err = rman_manage_region(&sc->io_rman, + sc->io_range.pci_addr, + sc->io_range.pci_addr + sc->io_range.size - 1); + if (err) + goto error; + return (0); + +error: + rman_fini(&sc->pref_mem_rman); + rman_fini(&sc->mem_rman); + rman_fini(&sc->io_rman); + return (err); +} + +static struct rman * +tegra_pcib_rman(struct tegra_pcib_softc *sc, int type, u_int flags) +{ + + switch (type) { + case SYS_RES_IOPORT: + return (&sc->io_rman); + case SYS_RES_MEMORY: + if (flags & RF_PREFETCHABLE) + return (&sc->pref_mem_rman); + else + return (&sc->mem_rman); + default: + break; + } + + return (NULL); +} + +static struct resource * +tegra_pcib_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 tegra_pcib_softc *sc; + struct rman *rm; + struct resource *res; + + debugf("%s: enter %d start %#jx end %#jx count %#jx\n", __func__, + type, start, end, count); + sc = device_get_softc(dev); + +#if defined(NEW_PCIB) && defined(PCI_RES_BUS) + if (type == PCI_RES_BUS) { + return (pci_domain_alloc_bus(0, child, rid, start, end, count, + flags)); + } +#endif + + rm = tegra_pcib_rman(sc, type, flags); + + if (rm == NULL) { + res = BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, + type, rid, start, end, count, flags); + + return (res); + } + + if (bootverbose) { + device_printf(dev, + "rman_reserve_resource: start=%#jx, end=%#jx, count=%#jx\n", + start, end, count); + } + + res = rman_reserve_resource(rm, start, end, count, flags, child); + if (res == NULL) + goto fail; + rman_set_rid(res, *rid); + if (flags & RF_ACTIVE) { + if (bus_activate_resource(child, type, *rid, res)) { + rman_release_resource(res); + goto fail; + } + } + return (res); + +fail: + if (bootverbose) { + device_printf(dev, "%s FAIL: type=%d, rid=%d, " + "start=%016jx, end=%016jx, count=%016jx, flags=%x\n", + __func__, type, *rid, start, end, count, flags); + } + + return (NULL); +} + +static int +tegra_pcib_release_resource(device_t dev, device_t child, int type, int rid, + struct resource *res) +{ + struct tegra_pcib_softc *sc; + struct rman *rm; + + sc = device_get_softc(dev); + debugf("%s: %d rid %x\n", __func__, type, rid); + +#if defined(NEW_PCIB) && defined(PCI_RES_BUS) + if (type == PCI_RES_BUS) + return (pci_domain_release_bus(0, child, rid, res)); +#endif + + rm = tegra_pcib_rman(sc, type, rman_get_flags(res)); + if (rm != NULL) { + KASSERT(rman_is_region_manager(res, rm), ("rman mismatch")); + rman_release_resource(res); + } + + return (bus_generic_release_resource(dev, child, type, rid, res)); +} + +static int +tegra_pcib_adjust_resource(device_t dev, device_t child, int type, + struct resource *res, rman_res_t start, rman_res_t end) +{ + struct tegra_pcib_softc *sc; + struct rman *rm; + + sc = device_get_softc(dev); + debugf("%s: %d start %jx end %jx \n", __func__, type, start, end); + +#if defined(NEW_PCIB) && defined(PCI_RES_BUS) + if (type == PCI_RES_BUS) + return (pci_domain_adjust_bus(0, child, res, start, end)); +#endif + + rm = tegra_pcib_rman(sc, type, rman_get_flags(res)); + if (rm != NULL) + return (rman_adjust_resource(res, start, end)); + return (bus_generic_adjust_resource(dev, child, type, res, start, end)); +} +extern bus_space_tag_t fdtbus_bs_tag; +static int +tegra_pcib_pcie_activate_resource(device_t dev, device_t child, int type, + int rid, struct resource *r) +{ + struct tegra_pcib_softc *sc; + vm_offset_t start; + void *p; + int rv; + + sc = device_get_softc(dev); + rv = rman_activate_resource(r); + if (rv != 0) + return (rv); + switch(type) { + case SYS_RES_IOPORT: + start = rman_get_start(r) + sc->io_range.host_addr; + break; + default: + start = rman_get_start(r); + rman_get_start(r); + break; + } + + if (bootverbose) + printf("%s: start %zx, len %jd\n", __func__, start, + rman_get_size(r)); + + p = pmap_mapdev(start, (vm_size_t)rman_get_size(r)); + rman_set_virtual(r, p); + rman_set_bustag(r, fdtbus_bs_tag); + rman_set_bushandle(r, (u_long)p); + return (0); +} + +/* ------------------------------------------------------------------------- */ +/* + * IVARs + */ +static int +tegra_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +{ + struct tegra_pcib_softc *sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_BUS: + *result = sc->busnr; + return (0); + case PCIB_IVAR_DOMAIN: + *result = device_get_unit(dev); + return (0); + } + + return (ENOENT); +} + +static int +tegra_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value) +{ + struct tegra_pcib_softc *sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_BUS: + sc->busnr = value; + return (0); + } + + return (ENOENT); +} + +static int +tegra_pcib_maxslots(device_t dev) +{ + return (16); +} + + +static int +tegra_pcib_route_interrupt(device_t bus, device_t dev, int pin) +{ + struct tegra_pcib_softc *sc; + + sc = device_get_softc(bus); + device_printf(bus, "route pin %d for device %d.%d to %ju\n", + pin, pci_get_slot(dev), pci_get_function(dev), + rman_get_start(sc->irq_res)); + + return (rman_get_start(sc->irq_res)); +} + +static int +tegra_pcbib_map_cfg(struct tegra_pcib_softc *sc, u_int bus, u_int slot, + u_int func, u_int reg) +{ + bus_size_t offs; + int rv; + + offs = sc->cfg_base_addr; + offs |= PCI_CFG_BUS(bus) | PCI_CFG_DEV(slot) | PCI_CFG_FUN(func) | + PCI_CFG_EXT_REG(reg); + if ((sc->cfg_handle != 0) && (sc->cfg_cur_offs == offs)) + return (0); + if (sc->cfg_handle != 0) + bus_space_unmap(sc->bus_tag, sc->cfg_handle, 0x800); + + rv = bus_space_map(sc->bus_tag, offs, 0x800, 0, &sc->cfg_handle); + if (rv != 0) + device_printf(sc->dev, "Cannot map config space\n"); + else + sc->cfg_cur_offs = offs; + return (rv); +} + +static uint32_t +tegra_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func, + u_int reg, int bytes) +{ + struct tegra_pcib_softc *sc; + bus_space_handle_t hndl; + uint32_t off; + uint32_t val; + int rv, i; + + sc = device_get_softc(dev); + if (bus == 0) { + if (func != 0) + return (0xFFFFFFFF); + for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { + if ((sc->ports[i] != NULL) && + (sc->ports[i]->port_idx == slot)) { + hndl = sc->ports[i]->cfg_handle; + off = reg & 0xFFF; + break; + } + } + if (i >= TEGRA_PCIB_MAX_PORTS) + return (0xFFFFFFFF); + } else { + rv = tegra_pcbib_map_cfg(sc, bus, slot, func, reg); + if (rv != 0) + return (0xFFFFFFFF); + hndl = sc->cfg_handle; + off = PCI_CFG_BASE_REG(reg); + } + + val = bus_space_read_4(sc->bus_tag, hndl, off & ~3); + switch (bytes) { + case 4: + break; + case 2: + if (off & 3) + val >>= 16; + val &= 0xffff; + break; + case 1: + val >>= ((off & 3) << 3); + val &= 0xff; + break; + } + return val; +} + +static void +tegra_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func, + u_int reg, uint32_t val, int bytes) +{ + struct tegra_pcib_softc *sc; + bus_space_handle_t hndl; + uint32_t off; + uint32_t val2; + int rv, i; + + sc = device_get_softc(dev); + if (bus == 0) { + if (func != 0) + return; + for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { + if ((sc->ports[i] != NULL) && + (sc->ports[i]->port_idx == slot)) { + hndl = sc->ports[i]->cfg_handle; + off = reg & 0xFFF; + break; + } + } + if (i >= TEGRA_PCIB_MAX_PORTS) + return; + } else { + rv = tegra_pcbib_map_cfg(sc, bus, slot, func, reg); + if (rv != 0) + return; + hndl = sc->cfg_handle; + off = PCI_CFG_BASE_REG(reg); + } + + switch (bytes) { + case 4: + bus_space_write_4(sc->bus_tag, hndl, off, val); + break; + case 2: + val2 = bus_space_read_4(sc->bus_tag, hndl, off & ~3); + val2 &= ~(0xffff << ((off & 3) << 3)); + val2 |= ((val & 0xffff) << ((off & 3) << 3)); + bus_space_write_4(sc->bus_tag, hndl, off & ~3, val2); + break; + case 1: + val2 = bus_space_read_4(sc->bus_tag, hndl, off & ~3); + val2 &= ~(0xff << ((off & 3) << 3)); + val2 |= ((val & 0xff) << ((off & 3) << 3)); + bus_space_write_4(sc->bus_tag, hndl, off & ~3, val2); + break; + } +} + +static int tegra_pci_intr(void *arg) +{ + struct tegra_pcib_softc *sc = arg; + uint32_t code, signature; + + code = bus_read_4(sc->afi_mem_res, AFI_INTR_CODE) & AFI_INTR_CODE_MASK; + signature = bus_read_4(sc->afi_mem_res, AFI_INTR_SIGNATURE); + bus_write_4(sc->afi_mem_res, AFI_INTR_CODE, 0); + if (code == AFI_INTR_CODE_INT_CODE_SM_MSG) + return(FILTER_STRAY); + + printf("tegra_pci_intr: code %x sig %x\n", code, signature); + return (FILTER_HANDLED); +} + +#if defined(TEGRA_PCI_MSI) +static int +tegra_pcib_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, + uint32_t *data) +{ + struct tegra_pcib_softc *sc; + + sc = device_get_softc(dev); + irq = irq - MSI_IRQ; + + /* validate parameters */ + if (isclr(&sc->msi_bitmap, irq)) { + device_printf(dev, "invalid MSI 0x%x\n", irq); + return (EINVAL); + } + + tegra_msi_data(irq, addr, data); + + debugf("%s: irq: %d addr: %jx data: %x\n", + __func__, irq, *addr, *data); + + return (0); +} + +static int +tegra_pcib_alloc_msi(device_t dev, device_t child, int count, + int maxcount __unused, int *irqs) +{ + struct tegra_pcib_softc *sc; + u_int start = 0, i; + + if (powerof2(count) == 0 || count > MSI_IRQ_NUM) + return (EINVAL); + + sc = device_get_softc(dev); + mtx_lock(&sc->mtx); + + for (start = 0; (start + count) < MSI_IRQ_NUM; start++) { + for (i = start; i < start + count; i++) { + if (isset(&sc->msi_bitmap, i)) + break; + } + if (i == start + count) + break; + } + + if ((start + count) == MSI_IRQ_NUM) { + mtx_unlock(&sc->mtx); + return (ENXIO); + } + + for (i = start; i < start + count; i++) { + setbit(&sc->msi_bitmap, i); + irqs[i] = MSI_IRQ + i; + } + debugf("%s: start: %x count: %x\n", __func__, start, count); + + mtx_unlock(&sc->mtx); + return (0); +} + +static int +tegra_pcib_release_msi(device_t dev, device_t child, int count, int *irqs) +{ + struct tegra_pcib_softc *sc; + u_int i; + + sc = device_get_softc(dev); + mtx_lock(&sc->mtx); + + for (i = 0; i < count; i++) + clrbit(&sc->msi_bitmap, irqs[i] - MSI_IRQ); + + mtx_unlock(&sc->mtx); + return (0); +} +#endif + +static bus_size_t +tegra_pcib_pex_ctrl(struct tegra_pcib_softc *sc, int port) +{ + if (port >= TEGRA_PCIB_MAX_PORTS) + panic("invalid port number: %d\n", port); + + if (port == 0) + return (AFI_PEX0_CTRL); + else if (port == 1) + return (AFI_PEX1_CTRL); + else if (port == 2) + return (AFI_PEX2_CTRL); + else + panic("invalid port number: %d\n", port); +} + +static int +tegra_pcib_enable_fdt_resources(struct tegra_pcib_softc *sc) +{ + int rv; + + rv = hwreset_assert(sc->hwreset_pcie_x); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert 'pcie_x' reset\n"); + return (rv); + } + rv = hwreset_assert(sc->hwreset_afi); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert 'afi' reset\n"); + return (rv); + } + rv = hwreset_assert(sc->hwreset_pex); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert 'pex' reset\n"); + return (rv); + } + + tegra_powergate_power_off(TEGRA_POWERGATE_PCX); + + /* Power supplies. */ + rv = regulator_enable(sc->supply_avddio_pex); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'avddio_pex' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_dvddio_pex); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'dvddio_pex' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_avdd_pex_pll); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'avdd-pex-pll' regulator\n"); + return (rv); + } + + rv = regulator_enable(sc->supply_hvdd_pex); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'hvdd-pex-supply' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_hvdd_pex_pll_e); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'hvdd-pex-pll-e-supply' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_vddio_pex_ctl); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'vddio-pex-ctl' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_avdd_pll_erefe); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'avdd-pll-erefe-supply' regulator\n"); + return (rv); + } + + rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCX, + sc->clk_pex, sc->hwreset_pex); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'PCX' powergate\n"); + return (rv); + } + + rv = hwreset_deassert(sc->hwreset_afi); + if (rv != 0) { + device_printf(sc->dev, "Cannot unreset 'afi' reset\n"); + return (rv); + } + + rv = clk_enable(sc->clk_afi); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'afi' clock\n"); + return (rv); + } + + rv = clk_enable(sc->clk_cml); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'cml' clock\n"); + return (rv); + } + + rv = clk_enable(sc->clk_pll_e); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'pll_e' clock\n"); + return (rv); + } + return (0); +} + +static struct tegra_pcib_port * +tegra_pcib_parse_port(struct tegra_pcib_softc *sc, phandle_t node) +{ + struct tegra_pcib_port *port; + uint32_t tmp[5]; + char tmpstr[6]; + int rv; + + port = malloc(sizeof(struct tegra_pcib_port), M_DEVBUF, M_WAITOK); + + rv = OF_getprop(node, "status", tmpstr, sizeof(tmpstr)); + if (rv <= 0 || strcmp(tmpstr, "okay") == 0 || + strcmp(tmpstr, "ok") == 0) + port->enabled = 1; + else + port->enabled = 0; + + rv = OF_getencprop(node, "assigned-addresses", tmp, sizeof(tmp)); + if (rv != sizeof(tmp)) { + device_printf(sc->dev, "Cannot parse assigned-address: %d\n", + rv); + goto fail; + } + port->rp_base_addr = tmp[2]; + port->rp_size = tmp[4]; + port->port_idx = OFW_PCI_PHYS_HI_DEVICE(tmp[0]) - 1; + if (port->port_idx >= TEGRA_PCIB_MAX_PORTS) { + device_printf(sc->dev, "Invalid port index: %d\n", + port->port_idx); + goto fail; + } + /* XXX - TODO: + * Implement proper function for parsing pci "reg" property: + * - it have PCI bus format + * - its relative to matching "assigned-addresses" + */ + rv = OF_getencprop(node, "reg", tmp, sizeof(tmp)); + if (rv != sizeof(tmp)) { + device_printf(sc->dev, "Cannot parse reg: %d\n", rv); + goto fail; + } + port->rp_base_addr += tmp[2]; + + rv = OF_getencprop(node, "nvidia,num-lanes", &port->num_lanes, + sizeof(port->num_lanes)); + if (rv != sizeof(port->num_lanes)) { + device_printf(sc->dev, "Cannot parse nvidia,num-lanes: %d\n", + rv); + goto fail; + } + if (port->num_lanes > 4) { + device_printf(sc->dev, "Invalid nvidia,num-lanes: %d\n", + port->num_lanes); + goto fail; + } + + port->afi_pex_ctrl = tegra_pcib_pex_ctrl(sc, port->port_idx); + sc->lanes_cfg |= port->num_lanes << (4 * port->port_idx); + + return (port); +fail: + free(port, M_DEVBUF); + return (NULL); +} + + +static int +tegra_pcib_parse_fdt_resources(struct tegra_pcib_softc *sc, phandle_t node) +{ + phandle_t child; + struct tegra_pcib_port *port; + int rv; + + /* Power supplies. */ + rv = regulator_get_by_ofw_property(sc->dev, "avddio-pex-supply", + &sc->supply_avddio_pex); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'avddio-pex' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "dvddio-pex-supply", + &sc->supply_dvddio_pex); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'dvddio-pex' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "avdd-pex-pll-supply", + &sc->supply_avdd_pex_pll); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'avdd-pex-pll' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "hvdd-pex-supply", + &sc->supply_hvdd_pex); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'hvdd-pex' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "hvdd-pex-pll-e-supply", + &sc->supply_hvdd_pex_pll_e); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'hvdd-pex-pll-e' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "vddio-pex-ctl-supply", + &sc->supply_vddio_pex_ctl); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'vddio-pex-ctl' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "avdd-pll-erefe-supply", + &sc->supply_avdd_pll_erefe); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'avdd-pll-erefe' regulator\n"); + return (ENXIO); + } + + /* Resets. */ + rv = hwreset_get_by_ofw_name(sc->dev, "pex", &sc->hwreset_pex); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pex' reset\n"); + return (ENXIO); + } + rv = hwreset_get_by_ofw_name(sc->dev, "afi", &sc->hwreset_afi); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'afi' reset\n"); + return (ENXIO); + } + rv = hwreset_get_by_ofw_name(sc->dev, "pcie_x", &sc->hwreset_pcie_x); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pcie_x' reset\n"); + return (ENXIO); + } + + /* Clocks. */ + rv = clk_get_by_ofw_name(sc->dev, "pex", &sc->clk_pex); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pex' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, "afi", &sc->clk_afi); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'afi' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, "pll_e", &sc->clk_pll_e); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pll_e' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, "cml", &sc->clk_cml); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'cml' clock\n"); + return (ENXIO); + } + + /* Phy. */ + rv = phy_get_by_ofw_name(sc->dev, "pcie", &sc->phy); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pcie' phy\n"); + return (ENXIO); + } + + /* Ports */ + sc->num_ports = 0; + for (child = OF_child(node); child != 0; child = OF_peer(child)) { + port = tegra_pcib_parse_port(sc, child); + if (port == NULL) { + device_printf(sc->dev, "Cannot parse PCIe port node\n"); + return (ENXIO); + } + sc->ports[sc->num_ports++] = port; + } + + return (0); +} + +static int +tegra_pcib_decode_ranges(struct tegra_pcib_softc *sc, + struct tegra_pci_range *ranges, int nranges) +{ + int i; + + for (i = 2; i < nranges; i++) { + if (ranges[i].space_code == OFW_PCI_PHYS_HI_SPACE_IO) { + if (sc->io_range.size != 0) { + device_printf(sc->dev, + "Duplicated IO range found in DT\n"); + return (ENXIO); + } + sc->io_range = ranges[i]; + } + if ((ranges[i].space_code == OFW_PCI_PHYS_HI_SPACE_MEM32) && + !ranges[i].prefetchable) { + if (sc->mem_range.size != 0) { + device_printf(sc->dev, + "Duplicated memory range found in DT\n"); + return (ENXIO); + } + sc->mem_range = ranges[i]; + } + if ((ranges[i].space_code == OFW_PCI_PHYS_HI_SPACE_MEM32) && + ranges[i].prefetchable) { + if (sc->pref_mem_range.size != 0) { + device_printf(sc->dev, + "Duplicated memory range found in DT\n"); + return (ENXIO); + } + sc->pref_mem_range = ranges[i]; + } + } + if ((sc->io_range.size == 0) || (sc->mem_range.size == 0) + || (sc->pref_mem_range.size == 0)) { + device_printf(sc->dev, + " Not all required ranges are found in DT\n"); + return (ENXIO); + } + return (0); +} + +/* + * Hardware config. + */ +static int +tegra_pcib_wait_for_link(struct tegra_pcib_softc *sc, + struct tegra_pcib_port *port) +{ + uint32_t reg; + int i; + + + /* Setup link detection. */ + reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0, + RP_PRIV_MISC, 4); + reg &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT; + reg |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT; + tegra_pcib_write_config(sc->dev, 0, port->port_idx, 0, + RP_PRIV_MISC, reg, 4); + + for (i = TEGRA_PCIE_LINKUP_TIMEOUT; i > 0; i--) { + reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0, + RP_VEND_XP, 4); + if (reg & RP_VEND_XP_DL_UP) + break; + + } + if (i <= 0) + return (ETIMEDOUT); + + for (i = TEGRA_PCIE_LINKUP_TIMEOUT; i > 0; i--) { + reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0, + RP_LINK_CONTROL_STATUS, 4); + if (reg & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE) + break; + + } + if (i <= 0) + return (ETIMEDOUT); + return (0); +} + +static void +tegra_pcib_port_enable(struct tegra_pcib_softc *sc, int port_num) +{ + struct tegra_pcib_port *port; + uint32_t reg; + int rv; + + port = sc->ports[port_num]; + + /* Put port to reset. */ + reg = AFI_RD4(sc, port->afi_pex_ctrl); + reg &= ~AFI_PEX_CTRL_RST_L; + AFI_WR4(sc, port->afi_pex_ctrl, reg); + AFI_RD4(sc, port->afi_pex_ctrl); + DELAY(10); + + /* Enable clocks. */ + reg |= AFI_PEX_CTRL_REFCLK_EN; + reg |= AFI_PEX_CTRL_CLKREQ_EN; + reg |= AFI_PEX_CTRL_OVERRIDE_EN; + AFI_WR4(sc, port->afi_pex_ctrl, reg); + AFI_RD4(sc, port->afi_pex_ctrl); + DELAY(100); + + /* Release reset. */ + reg |= AFI_PEX_CTRL_RST_L; + AFI_WR4(sc, port->afi_pex_ctrl, reg); + + rv = tegra_pcib_wait_for_link(sc, port); + if (bootverbose) + device_printf(sc->dev, " port %d (%d lane%s): Link is %s\n", + port->port_idx, port->num_lanes, + port->num_lanes > 1 ? "s": "", + rv == 0 ? "up": "down"); +} + + +static void +tegra_pcib_port_disable(struct tegra_pcib_softc *sc, uint32_t port_num) +{ + struct tegra_pcib_port *port; + uint32_t reg; + + port = sc->ports[port_num]; + + /* Put port to reset. */ + reg = AFI_RD4(sc, port->afi_pex_ctrl); + reg &= ~AFI_PEX_CTRL_RST_L; + AFI_WR4(sc, port->afi_pex_ctrl, reg); + AFI_RD4(sc, port->afi_pex_ctrl); + DELAY(10); + + /* Disable clocks. */ + reg &= ~AFI_PEX_CTRL_CLKREQ_EN; + reg &= ~AFI_PEX_CTRL_REFCLK_EN; + AFI_WR4(sc, port->afi_pex_ctrl, reg); + + if (bootverbose) + device_printf(sc->dev, " port %d (%d lane%s): Disabled\n", + port->port_idx, port->num_lanes, + port->num_lanes > 1 ? "s": ""); +} + +static void +tegra_pcib_set_bar(struct tegra_pcib_softc *sc, int bar, uint32_t axi, + uint64_t fpci, uint32_t size, int is_memory) +{ + uint32_t fpci_reg; + uint32_t axi_reg; + uint32_t size_reg; + + axi_reg = axi & ~0xFFF; + size_reg = size >> 12; + fpci_reg = (uint32_t)(fpci >> 8) & ~0xF; + fpci_reg |= is_memory ? 0x1 : 0x0; + AFI_WR4(sc, bars[bar].axi_start, axi_reg); + AFI_WR4(sc, bars[bar].size, size_reg); + AFI_WR4(sc, bars[bar].fpci_start, fpci_reg); +} + +static int +tegra_pcib_enable(struct tegra_pcib_softc *sc, uint32_t port) +{ + int rv; + int i; + uint32_t reg; + + rv = tegra_pcib_enable_fdt_resources(sc); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable FDT resources\n"); + return (rv); + } + /* Enable PLLE control. */ + reg = AFI_RD4(sc, AFI_PLLE_CONTROL); + reg &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL; + reg |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN; + AFI_WR4(sc, AFI_PLLE_CONTROL, reg); + + /* Set bias pad. */ + AFI_WR4(sc, AFI_PEXBIAS_CTRL, 0); + + /* Configure mode and ports. */ + reg = AFI_RD4(sc, AFI_PCIE_CONFIG); + reg &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK; + if (sc->lanes_cfg == 0x14) { + if (bootverbose) + device_printf(sc->dev, + "Using x1,x4 configuration\n"); + reg |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR4_1; + } else if (sc->lanes_cfg == 0x12) { + if (bootverbose) + device_printf(sc->dev, + "Using x1,x2 configuration\n"); + reg |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR2_1; + } else { + device_printf(sc->dev, + "Unsupported lanes configuration: 0x%X\n", sc->lanes_cfg); + } + reg |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL; + for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { + if ((sc->ports[i] != NULL)) + reg &= + ~AFI_PCIE_CONFIG_PCIE_DISABLE(sc->ports[i]->port_idx); + } + AFI_WR4(sc, AFI_PCIE_CONFIG, reg); + + /* Enable Gen2 support. */ + reg = AFI_RD4(sc, AFI_FUSE); + reg &= ~AFI_FUSE_PCIE_T0_GEN2_DIS; + AFI_WR4(sc, AFI_FUSE, reg); + + /* Enable PCIe phy. */ + rv = phy_enable(sc->dev, sc->phy); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable phy\n"); + return (rv); + } + + rv = hwreset_deassert(sc->hwreset_pcie_x); + if (rv != 0) { + device_printf(sc->dev, "Cannot unreset 'pci_x' reset\n"); + return (rv); + } + + /* Enable config space. */ + reg = AFI_RD4(sc, AFI_CONFIGURATION); + reg |= AFI_CONFIGURATION_EN_FPCI; + AFI_WR4(sc, AFI_CONFIGURATION, reg); + + /* Enable AFI errors. */ + reg = 0; + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_INI_SLVERR); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_INI_DECERR); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_TGT_SLVERR); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_TGT_DECERR); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_TGT_WRERR); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_SM_MSG); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_DFPCI_DECERR); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_AXI_DECERR); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_FPCI_TIMEOUT); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_PE_PRSNT_SENSE); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_PE_CLKREQ_SENSE); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_CLKCLAMP_SENSE); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_RDY4PD_SENSE); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_P2P_ERROR); + AFI_WR4(sc, AFI_AFI_INTR_ENABLE, reg); + AFI_WR4(sc, AFI_SM_INTR_ENABLE, 0xffffffff); + + /* Enable INT, disable MSI. */ + AFI_WR4(sc, AFI_INTR_MASK, AFI_INTR_MASK_INT_MASK); + + /* Mask all FPCI errors. */ + AFI_WR4(sc, AFI_FPCI_ERROR_MASKS, 0); + + /* Setup AFI translation windows. */ + /* BAR 0 - type 1 extended configuration. */ + tegra_pcib_set_bar(sc, 0, rman_get_start(sc->cfg_mem_res), + FPCI_MAP_EXT_TYPE1_CONFIG, rman_get_size(sc->cfg_mem_res), 0); + + /* BAR 1 - downstream I/O. */ + tegra_pcib_set_bar(sc, 1, sc->io_range.host_addr, FPCI_MAP_IO, + sc->io_range.size, 0); + + /* BAR 2 - downstream prefetchable memory 1:1. */ + tegra_pcib_set_bar(sc, 2, sc->pref_mem_range.host_addr, + sc->pref_mem_range.host_addr, sc->pref_mem_range.size, 1); + + /* BAR 3 - downstream not prefetchable memory 1:1 .*/ + tegra_pcib_set_bar(sc, 3, sc->mem_range.host_addr, + sc->mem_range.host_addr, sc->mem_range.size, 1); + + /* BAR 3-8 clear. */ + tegra_pcib_set_bar(sc, 4, 0, 0, 0, 0); + tegra_pcib_set_bar(sc, 5, 0, 0, 0, 0); + tegra_pcib_set_bar(sc, 6, 0, 0, 0, 0); + tegra_pcib_set_bar(sc, 7, 0, 0, 0, 0); + tegra_pcib_set_bar(sc, 8, 0, 0, 0, 0); + + /* MSI BAR - clear. */ + tegra_pcib_set_bar(sc, 9, 0, 0, 0, 0); + return(0); +} + +static int +tegra_pcib_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { + device_set_desc(dev, "Nvidia Integrated PCI/PCI-E Controller"); + return (BUS_PROBE_DEFAULT); + } + return (ENXIO); +} + +static int +tegra_pcib_attach(device_t dev) +{ + struct tegra_pcib_softc *sc; + phandle_t node; + uint32_t unit; + int rv; + int rid; + int nranges; + struct tegra_pci_range *ranges; + struct tegra_pcib_port *port; + int i; + + sc = device_get_softc(dev); + sc->dev = dev; + unit = fdt_get_unit(dev); + mtx_init(&sc->mtx, "msi_mtx", NULL, MTX_DEF); + + + node = ofw_bus_get_node(dev); + + rv = tegra_pcib_parse_fdt_resources(sc, node); + if (rv != 0) { + device_printf(dev, "Cannot get FDT resources\n"); + return (rv); + } + + nranges = tegra_pci_get_ranges(node, &ranges); + if (nranges != 5) { + device_printf(sc->dev, "Unexpected number of ranges: %d\n", + nranges); + rv = ENXIO; + goto out; + } + + /* Allocate bus_space resources. */ + rid = 0; + sc->pads_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->pads_mem_res == NULL) { + device_printf(dev, "Cannot allocate PADS register\n"); + rv = ENXIO; + goto out; + } + /* + * XXX - FIXME + * tag for config space is not filled when RF_ALLOCATED flag is used. + */ + sc->bus_tag = rman_get_bustag(sc->pads_mem_res); + + rid = 1; + sc->afi_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->afi_mem_res == NULL) { + device_printf(dev, "Cannot allocate AFI register\n"); + rv = ENXIO; + goto out; + } + + rid = 2; + sc->cfg_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ALLOCATED); + if (sc->cfg_mem_res == NULL) { + device_printf(dev, "Cannot allocate config space memory\n"); + rv = ENXIO; + goto out; + } + sc->cfg_base_addr = rman_get_start(sc->cfg_mem_res); + + + /* Map RP slots */ + for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { + if (sc->ports[i] == NULL) + continue; + port = sc->ports[i]; + rv = bus_space_map(sc->bus_tag, port->rp_base_addr, + port->rp_size, 0, &port->cfg_handle); + if (rv != 0) { + device_printf(sc->dev, "Cannot allocate memory for " + "port: %d\n", i); + rv = ENXIO; + goto out; + } + } + + /* + * Get PCI interrupt info. + */ + ofw_bus_setup_iinfo(node, &sc->pci_iinfo, sizeof(pcell_t)); + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->irq_res == NULL) { + device_printf(dev, "Cannot allocate IRQ resources\n"); + rv = ENXIO; + goto out; + } + + rid = 1; + sc->msi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "Cannot allocate MSI IRQ resources\n"); + rv = ENXIO; + goto out; + } + + if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + tegra_pci_intr, NULL, sc, &sc->intr_cookie)) { + device_printf(dev, "cannot setup interrupt handler\n"); + rv = ENXIO; + goto out; + } + + /* Memory management. */ + rv = tegra_pcib_decode_ranges(sc, ranges, nranges); + if (rv != 0) + goto out; + + rv = tegra_pcib_rman_init(sc); + if (rv != 0) + goto out; + free(ranges, M_DEVBUF); + ranges = NULL; + + /* + * Enable PCIE device. + */ + rv = tegra_pcib_enable(sc, unit); + if (rv != 0) + goto out; + for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { + if (sc->ports[i] == NULL) + continue; + if (sc->ports[i]->enabled) + tegra_pcib_port_enable(sc, i); + else + tegra_pcib_port_disable(sc, i); + } + + device_add_child(dev, "pci", -1); + + return (bus_generic_attach(dev)); + +out: + if (ranges != NULL) + free(ranges, M_DEVBUF); + + return (rv); +} + + +static device_method_t tegra_pcib_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_pcib_probe), + DEVMETHOD(device_attach, tegra_pcib_attach), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, tegra_pcib_read_ivar), + DEVMETHOD(bus_write_ivar, tegra_pcib_write_ivar), + DEVMETHOD(bus_alloc_resource, tegra_pcib_alloc_resource), + DEVMETHOD(bus_adjust_resource, tegra_pcib_adjust_resource), + DEVMETHOD(bus_release_resource, tegra_pcib_release_resource), + DEVMETHOD(bus_activate_resource, tegra_pcib_pcie_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + /* pcib interface */ + DEVMETHOD(pcib_maxslots, tegra_pcib_maxslots), + DEVMETHOD(pcib_read_config, tegra_pcib_read_config), + DEVMETHOD(pcib_write_config, tegra_pcib_write_config), + DEVMETHOD(pcib_route_interrupt, tegra_pcib_route_interrupt), + +#if defined(TEGRA_PCI_MSI) + DEVMETHOD(pcib_alloc_msi, tegra_pcib_alloc_msi), + DEVMETHOD(pcib_release_msi, tegra_pcib_release_msi), + DEVMETHOD(pcib_map_msi, tegra_pcib_map_msi), +#endif + + /* OFW bus interface */ + DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), + DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), + DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), + DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), + DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), + + DEVMETHOD_END +}; + +static driver_t tegra_pcib_driver = { + "pcib", + tegra_pcib_methods, + sizeof(struct tegra_pcib_softc), +}; + +devclass_t pcib_devclass; + +DRIVER_MODULE(pcib, simplebus, tegra_pcib_driver, pcib_devclass, 0, 0);
\ No newline at end of file diff --git a/sys/arm/nvidia/tegra_pinmux.c b/sys/arm/nvidia/tegra_pinmux.c new file mode 100644 index 0000000..6123746 --- /dev/null +++ b/sys/arm/nvidia/tegra_pinmux.c @@ -0,0 +1,804 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Pin multiplexer driver for Tegra SoCs. + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/malloc.h> +#include <sys/rman.h> + +#include <machine/bus.h> +#include <machine/fdt.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/fdt/fdt_pinctrl.h> +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +/* Pin multipexor register. */ +#define TEGRA_MUX_FUNCTION_MASK 0x03 +#define TEGRA_MUX_FUNCTION_SHIFT 0 +#define TEGRA_MUX_PUPD_MASK 0x03 +#define TEGRA_MUX_PUPD_SHIFT 2 +#define TEGRA_MUX_TRISTATE_SHIFT 4 +#define TEGRA_MUX_ENABLE_INPUT_SHIFT 5 +#define TEGRA_MUX_OPEN_DRAIN_SHIFT 6 +#define TEGRA_MUX_LOCK_SHIFT 7 +#define TEGRA_MUX_IORESET_SHIFT 8 +#define TEGRA_MUX_RCV_SEL_SHIFT 9 + + +/* Pin goup register. */ +#define TEGRA_GRP_HSM_SHIFT 2 +#define TEGRA_GRP_SCHMT_SHIFT 3 +#define TEGRA_GRP_DRV_TYPE_SHIFT 6 +#define TEGRA_GRP_DRV_TYPE_MASK 0x03 +#define TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT 28 +#define TEGRA_GRP_DRV_DRVDN_SLWR_MASK 0x03 +#define TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT 30 +#define TEGRA_GRP_DRV_DRVUP_SLWF_MASK 0x03 + +struct pinmux_softc { + device_t dev; + struct resource *pad_mem_res; + struct resource *mux_mem_res; + struct resource *mipi_mem_res; +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-pinmux", 1}, + {NULL, 0}, +}; + +enum prop_id { + PROP_ID_PULL, + PROP_ID_TRISTATE, + PROP_ID_ENABLE_INPUT, + PROP_ID_OPEN_DRAIN, + PROP_ID_LOCK, + PROP_ID_IORESET, + PROP_ID_RCV_SEL, + PROP_ID_HIGH_SPEED_MODE, + PROP_ID_SCHMITT, + PROP_ID_LOW_POWER_MODE, + PROP_ID_DRIVE_DOWN_STRENGTH, + PROP_ID_DRIVE_UP_STRENGTH, + PROP_ID_SLEW_RATE_FALLING, + PROP_ID_SLEW_RATE_RISING, + PROP_ID_DRIVE_TYPE, + + PROP_ID_MAX_ID +}; + +/* Numeric based parameters. */ +static const struct prop_name { + const char *name; + enum prop_id id; +} prop_names[] = { + {"nvidia,pull", PROP_ID_PULL}, + {"nvidia,tristate", PROP_ID_TRISTATE}, + {"nvidia,enable-input", PROP_ID_ENABLE_INPUT}, + {"nvidia,open-drain", PROP_ID_OPEN_DRAIN}, + {"nvidia,lock", PROP_ID_LOCK}, + {"nvidia,io-reset", PROP_ID_IORESET}, + {"nvidia,rcv-sel", PROP_ID_RCV_SEL}, + {"nvidia,high-speed-mode", PROP_ID_HIGH_SPEED_MODE}, + {"nvidia,schmitt", PROP_ID_SCHMITT}, + {"nvidia,low-power-mode", PROP_ID_LOW_POWER_MODE}, + {"nvidia,pull-down-strength", PROP_ID_DRIVE_DOWN_STRENGTH}, + {"nvidia,pull-up-strength", PROP_ID_DRIVE_UP_STRENGTH}, + {"nvidia,slew-rate-falling", PROP_ID_SLEW_RATE_FALLING}, + {"nvidia,slew-rate-rising", PROP_ID_SLEW_RATE_RISING}, + {"nvidia,drive-type", PROP_ID_DRIVE_TYPE}, +}; + +/* + * configuration for one pin group. + */ +struct pincfg { + char *function; + int params[PROP_ID_MAX_ID]; +}; +#define GPIO_BANK_A 0 +#define GPIO_BANK_B 1 +#define GPIO_BANK_C 2 +#define GPIO_BANK_D 3 +#define GPIO_BANK_E 4 +#define GPIO_BANK_F 5 +#define GPIO_BANK_G 6 +#define GPIO_BANK_H 7 +#define GPIO_BANK_I 8 +#define GPIO_BANK_J 9 +#define GPIO_BANK_K 10 +#define GPIO_BANK_L 11 +#define GPIO_BANK_M 12 +#define GPIO_BANK_N 13 +#define GPIO_BANK_O 14 +#define GPIO_BANK_P 15 +#define GPIO_BANK_Q 16 +#define GPIO_BANK_R 17 +#define GPIO_BANK_S 18 +#define GPIO_BANK_T 19 +#define GPIO_BANK_U 20 +#define GPIO_BANK_V 21 +#define GPIO_BANK_W 22 +#define GPIO_BANK_X 23 +#define GPIO_BANK_Y 24 +#define GPIO_BANK_Z 25 +#define GPIO_BANK_AA 26 +#define GPIO_BANK_BB 27 +#define GPIO_BANK_CC 28 +#define GPIO_BANK_DD 29 +#define GPIO_BANK_EE 30 +#define GPIO_BANK_FF 31 +#define GPIO_NUM(b, p) (8 * (b) + (p)) + +struct tegra_mux { + char *name; + bus_size_t reg; + char *functions[4]; + int gpio_num; +}; + +#define GMUX(r, gb, gi, nm, f1, f2, f3, f4) \ +{ \ + .name = #nm, \ + .reg = r, \ + .gpio_num = GPIO_NUM(GPIO_BANK_##gb, gi), \ + .functions = {#f1, #f2, #f3, #f4}, \ +} + +#define FMUX(r, nm, f1, f2, f3, f4) \ +{ \ + .name = #nm, \ + .reg = r, \ + .gpio_num = -1, \ + .functions = {#f1, #f2, #f3, #f4}, \ +} + +static const struct tegra_mux pin_mux_tbl[] = { + GMUX(0x000, O, 1, ulpi_data0_po1, spi3, hsi, uarta, ulpi), + GMUX(0x004, O, 2, ulpi_data1_po2, spi3, hsi, uarta, ulpi), + GMUX(0x008, O, 3, ulpi_data2_po3, spi3, hsi, uarta, ulpi), + GMUX(0x00C, O, 4, ulpi_data3_po4, spi3, hsi, uarta, ulpi), + GMUX(0x010, O, 5, ulpi_data4_po5, spi2, hsi, uarta, ulpi), + GMUX(0x014, O, 6, ulpi_data5_po6, spi2, hsi, uarta, ulpi), + GMUX(0x018, O, 7, ulpi_data6_po7, spi2, hsi, uarta, ulpi), + GMUX(0x01C, O, 0, ulpi_data7_po0, spi2, hsi, uarta, ulpi), + GMUX(0x020, P, 9, ulpi_clk_py0, spi1, spi5, uartd, ulpi), + GMUX(0x024, P, 1, ulpi_dir_py1, spi1, spi5, uartd, ulpi), + GMUX(0x028, P, 2, ulpi_nxt_py2, spi1, spi5, uartd, ulpi), + GMUX(0x02C, P, 3, ulpi_stp_py3, spi1, spi5, uartd, ulpi), + GMUX(0x030, P, 0, dap3_fs_pp0, i2s2, spi5, displaya, displayb), + GMUX(0x034, P, 1, dap3_din_pp1, i2s2, spi5, displaya, displayb), + GMUX(0x038, P, 2, dap3_dout_pp2, i2s2, spi5, displaya, rsvd4), + GMUX(0x03C, P, 3, dap3_sclk_pp3, i2s2, spi5, rsvd3, displayb), + GMUX(0x040, V, 0, pv0, rsvd1, rsvd2, rsvd3, rsvd4), + GMUX(0x044, V, 1, pv1, rsvd1, rsvd2, rsvd3, rsvd4), + GMUX(0x048, Z, 0, sdmmc1_clk_pz0, sdmmc1, clk12, rsvd3, rsvd4), + GMUX(0x04C, Z, 1, sdmmc1_cmd_pz1, sdmmc1, spdif, spi4, uarta), + GMUX(0x050, Y, 4, sdmmc1_dat3_py4, sdmmc1, spdif, spi4, uarta), + GMUX(0x054, Y, 5, sdmmc1_dat2_py5, sdmmc1, pwm0, spi4, uarta), + GMUX(0x058, Y, 6, sdmmc1_dat1_py6, sdmmc1, pwm1, spi4, uarta), + GMUX(0x05C, Y, 7, sdmmc1_dat0_py7, sdmmc1, rsvd2, spi4, uarta), + GMUX(0x068, W, 5, clk2_out_pw5, extperiph2, rsvd2, rsvd3, rsvd4), + GMUX(0x06C, CC, 5, clk2_req_pcc5, dap, rsvd2, rsvd3, rsvd4), + GMUX(0x110, N, 7, hdmi_int_pn7, rsvd1, rsvd2, rsvd3, rsvd4), + GMUX(0x114, V, 4, ddc_scl_pv4, i2c4, rsvd2, rsvd3, rsvd4), + GMUX(0x118, V, 5, ddc_sda_pv5, i2c4, rsvd2, rsvd3, rsvd4), + GMUX(0x164, V, 3, uart2_rxd_pc3, irda, spdif, uarta, spi4), + GMUX(0x168, C, 2, uart2_txd_pc2, irda, spdif, uarta, spi4), + GMUX(0x16C, J, 6, uart2_rts_n_pj6, uarta, uartb, gmi, spi4), + GMUX(0x170, J, 5, uart2_cts_n_pj5, uarta, uartb, gmi, spi4), + GMUX(0x174, W, 6, uart3_txd_pw6, uartc, rsvd2, gmi, spi4), + GMUX(0x178, W, 7, uart3_rxd_pw7, uartc, rsvd2, gmi, spi4), + GMUX(0x17C, S, 1, uart3_cts_n_pa1, uartc, sdmmc1, dtv, gmi), + GMUX(0x180, C, 0, uart3_rts_n_pc0, uartc, pwm0, dtv, gmi), + GMUX(0x184, U, 0, pu0, owr, uarta, gmi, rsvd4), + GMUX(0x188, U, 1, pu1, rsvd1, uarta, gmi, rsvd4), + GMUX(0x18C, U, 2, pu2, rsvd1, uarta, gmi, rsvd4), + GMUX(0x190, U, 3, pu3, pwm0, uarta, gmi, displayb), + GMUX(0x194, U, 4, pu4, pwm1, uarta, gmi, displayb), + GMUX(0x198, U, 5, pu5, pwm2, uarta, gmi, displayb), + GMUX(0x19C, U, 6, pu6, pwm3, uarta, rsvd3, gmi), + GMUX(0x1A0, C, 5, gen1_i2c_sda_pc5, i2c1, rsvd2, rsvd3, rsvd4), + GMUX(0x1A4, C, 4, gen1_i2c_scl_pc4, i2c1, rsvd2, rsvd3, rsvd4), + GMUX(0x1A8, P, 3, dap4_fs_pp4, i2s3, gmi, dtv, rsvd4), + GMUX(0x1AC, P, 4, dap4_din_pp5, i2s3, gmi, rsvd3, rsvd4), + GMUX(0x1B0, P, 5, dap4_dout_pp6, i2s3, gmi, dtv, rsvd4), + GMUX(0x1B4, P, 7, dap4_sclk_pp7, i2s3, gmi, rsvd3, rsvd4), + GMUX(0x1B8, P, 0, clk3_out_pee0, extperiph3, rsvd2, rsvd3, rsvd4), + GMUX(0x1BC, EE, 1, clk3_req_pee1, dev3, rsvd2, rsvd3, rsvd4), + GMUX(0x1C0, C, 7, pc7, rsvd1, rsvd2, gmi, gmi_alt), + GMUX(0x1C4, I, 5, pi5, sdmmc2, rsvd2, gmi, rsvd4), + GMUX(0x1C8, I, 7, pi7, rsvd1, trace, gmi, dtv), + GMUX(0x1CC, K, 0, pk0, rsvd1, sdmmc3, gmi, soc), + GMUX(0x1D0, K, 1, pk1, sdmmc2, trace, gmi, rsvd4), + GMUX(0x1D4, J, 0, pj0, rsvd1, rsvd2, gmi, usb), + GMUX(0x1D8, J, 2, pj2, rsvd1, rsvd2, gmi, soc), + GMUX(0x1DC, K, 3, pk3, sdmmc2, trace, gmi, ccla), + GMUX(0x1E0, K, 4, pk4, sdmmc2, rsvd2, gmi, gmi_alt), + GMUX(0x1E4, K, 2, pk2, rsvd1, rsvd2, gmi, rsvd4), + GMUX(0x1E8, I, 3, pi3, rsvd1, rsvd2, gmi, spi4), + GMUX(0x1EC, I, 6, pi6, rsvd1, rsvd2, gmi, sdmmc2), + GMUX(0x1F0, G, 0, pg0, rsvd1, rsvd2, gmi, rsvd4), + GMUX(0x1F4, G, 1, pg1, rsvd1, rsvd2, gmi, rsvd4), + GMUX(0x1F8, G, 2, pg2, rsvd1, trace, gmi, rsvd4), + GMUX(0x1FC, G, 3, pg3, rsvd1, trace, gmi, rsvd4), + GMUX(0x200, G, 4, pg4, rsvd1, tmds, gmi, spi4), + GMUX(0x204, G, 5, pg5, rsvd1, rsvd2, gmi, spi4), + GMUX(0x208, G, 6, pg6, rsvd1, rsvd2, gmi, spi4), + GMUX(0x20C, G, 7, pg7, rsvd1, rsvd2, gmi, spi4), + GMUX(0x210, H, 0, ph0, pwm0, trace, gmi, dtv), + GMUX(0x214, H, 1, ph1, pwm1, tmds, gmi, displaya), + GMUX(0x218, H, 2, ph2, pwm2, tmds, gmi, cldvfs), + GMUX(0x21C, H, 3, ph3, pwm3, spi4, gmi, cldvfs), + GMUX(0x220, H, 4, ph4, sdmmc2, rsvd2, gmi, rsvd4), + GMUX(0x224, H, 5, ph5, sdmmc2, rsvd2, gmi, rsvd4), + GMUX(0x228, H, 6, ph6, sdmmc2, trace, gmi, dtv), + GMUX(0x22C, H, 7, ph7, sdmmc2, trace, gmi, dtv), + GMUX(0x230, J, 7, pj7, uartd, rsvd2, gmi, gmi_alt), + GMUX(0x234, B, 0, pb0, uartd, rsvd2, gmi, rsvd4), + GMUX(0x238, B, 1, pb1, uartd, rsvd2, gmi, rsvd4), + GMUX(0x23C, K, 7, pk7, uartd, rsvd2, gmi, rsvd4), + GMUX(0x240, I, 0, pi0, rsvd1, rsvd2, gmi, rsvd4), + GMUX(0x244, I, 1, pi1, rsvd1, rsvd2, gmi, rsvd4), + GMUX(0x248, I, 2, pi2, sdmmc2, trace, gmi, rsvd4), + GMUX(0x24C, I, 4, pi4, spi4, trace, gmi, displaya), + GMUX(0x250, T, 5, gen2_i2c_scl_pt5, i2c2, rsvd2, gmi, rsvd4), + GMUX(0x254, T, 6, gen2_i2c_sda_pt6, i2c2, rsvd2, gmi, rsvd4), + GMUX(0x258, CC, 4, sdmmc4_clk_pcc4, sdmmc4, rsvd2, gmi, rsvd4), + GMUX(0x25C, T, 7, sdmmc4_cmd_pt7, sdmmc4, rsvd2, gmi, rsvd4), + GMUX(0x260, AA, 0, sdmmc4_dat0_paa0, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x264, AA, 1, sdmmc4_dat1_paa1, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x268, AA, 2, sdmmc4_dat2_paa2, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x26C, AA, 3, sdmmc4_dat3_paa3, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x270, AA, 4, sdmmc4_dat4_paa4, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x274, AA, 5, sdmmc4_dat5_paa5, sdmmc4, spi3, rsvd3, rsvd4), + GMUX(0x278, AA, 6, sdmmc4_dat6_paa6, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x27C, AA, 7, sdmmc4_dat7_paa7, sdmmc4, rsvd2, gmi, rsvd4), + GMUX(0x284, CC, 0, cam_mclk_pcc0, vi, vi_alt1, vi_alt3, sdmmc2), + GMUX(0x288, CC, 1, pcc1, i2s4, rsvd2, rsvd3, sdmmc2), + GMUX(0x28C, BB, 0, pbb0, vgp6, vimclk2, sdmmc2, vimclk2_alt), + GMUX(0x290, BB, 1, cam_i2c_scl_pbb1, vgp1, i2c3, rsvd3, sdmmc2), + GMUX(0x294, BB, 2, cam_i2c_sda_pbb2, vgp2, i2c3, rsvd3, sdmmc2), + GMUX(0x298, BB, 3, pbb3, vgp3, displaya, displayb, sdmmc2), + GMUX(0x29C, BB, 4, pbb4, vgp4, displaya, displayb, sdmmc2), + GMUX(0x2A0, BB, 5, pbb5, vgp5, displaya, rsvd3, sdmmc2), + GMUX(0x2A4, BB, 6, pbb6, i2s4, rsvd2, displayb, sdmmc2), + GMUX(0x2A8, BB, 7, pbb7, i2s4, rsvd2, rsvd3, sdmmc2), + GMUX(0x2AC, CC, 2, pcc2, i2s4, rsvd2, sdmmc3, sdmmc2), + FMUX(0x2B0, jtag_rtck, rtck, rsvd2, rsvd3, rsvd4), + GMUX(0x2B4, Z, 6, pwr_i2c_scl_pz6, i2cpwr, rsvd2, rsvd3, rsvd4), + GMUX(0x2B8, Z, 7, pwr_i2c_sda_pz7, i2cpwr, rsvd2, rsvd3, rsvd4), + GMUX(0x2BC, R, 0, kb_row0_pr0, kbc, rsvd2, rsvd3, rsvd4), + GMUX(0x2C0, R, 1, kb_row1_pr1, kbc, rsvd2, rsvd3, rsvd4), + GMUX(0x2C4, R, 2, kb_row2_pr2, kbc, rsvd2, rsvd3, rsvd4), + GMUX(0x2C8, R, 3, kb_row3_pr3, kbc, displaya, sys, displayb), + GMUX(0x2CC, R, 4, kb_row4_pr4, kbc, displaya, rsvd3, displayb), + GMUX(0x2D0, R, 5, kb_row5_pr5, kbc, displaya, rsvd3, displayb), + GMUX(0x2D4, R, 6, kb_row6_pr6, kbc, displaya, displaya_alt, displayb), + GMUX(0x2D8, R, 7, kb_row7_pr7, kbc, rsvd2, cldvfs, uarta), + GMUX(0x2DC, S, 0, kb_row8_ps0, kbc, rsvd2, cldvfs, uarta), + GMUX(0x2E0, S, 1, kb_row9_ps1, kbc, rsvd2, rsvd3, uarta), + GMUX(0x2E4, S, 2, kb_row10_ps2, kbc, rsvd2, rsvd3, uarta), + GMUX(0x2E8, S, 3, kb_row11_ps3, kbc, rsvd2, rsvd3, irda), + GMUX(0x2EC, S, 4, kb_row12_ps4, kbc, rsvd2, rsvd3, irda), + GMUX(0x2F0, S, 5, kb_row13_ps5, kbc, rsvd2, spi2, rsvd4), + GMUX(0x2F4, S, 6, kb_row14_ps6, kbc, rsvd2, spi2, rsvd4), + GMUX(0x2F8, S, 7, kb_row15_ps7, kbc, soc, rsvd3, rsvd4), + GMUX(0x2FC, Q, 0, kb_col0_pq0, kbc, rsvd2, spi2, rsvd4), + GMUX(0x300, Q, 1, kb_col1_pq1, kbc, rsvd2, spi2, rsvd4), + GMUX(0x304, Q, 2, kb_col2_pq2, kbc, rsvd2, spi2, rsvd4), + GMUX(0x308, Q, 3, kb_col3_pq3, kbc, displaya, pwm2, uarta), + GMUX(0x30C, Q, 4, kb_col4_pq4, kbc, owr, sdmmc3, uarta), + GMUX(0x310, Q, 5, kb_col5_pq5, kbc, rsvd2, sdmmc3, rsvd4), + GMUX(0x314, Q, 6, kb_col6_pq6, kbc, rsvd2, spi2, uartd), + GMUX(0x318, Q, 7, kb_col7_pq7, kbc, rsvd2, spi2, uartd), + GMUX(0x31C, A, 0, clk_32k_out_pa0, blink, soc, rsvd3, rsvd4), + FMUX(0x324, core_pwr_req, pwron, rsvd2, rsvd3, rsvd4), + FMUX(0x328, cpu_pwr_req, cpu, rsvd2, rsvd3, rsvd4), + FMUX(0x32C, pwr_int_n, pmi, rsvd2, rsvd3, rsvd4), + FMUX(0x330, clk_32k_in, clk, rsvd2, rsvd3, rsvd4), + FMUX(0x334, owr, owr, rsvd2, rsvd3, rsvd4), + GMUX(0x338, N, 0, dap1_fs_pn0, i2s0, hda, gmi, rsvd4), + GMUX(0x33C, N, 1, dap1_din_pn1, i2s0, hda, gmi, rsvd4), + GMUX(0x340, N, 2, dap1_dout_pn2, i2s0, hda, gmi, sata), + GMUX(0x344, N, 3, dap1_sclk_pn3, i2s0, hda, gmi, rsvd4), + GMUX(0x348, EE, 2, dap_mclk1_req_pee2, dap, dap1, sata, rsvd4), + GMUX(0x34C, W, 4, dap_mclk1_pw4, extperiph1, dap2, rsvd3, rsvd4), + GMUX(0x350, K, 6, spdif_in_pk6, spdif, rsvd2, rsvd3, i2c3), + GMUX(0x354, K, 5, spdif_out_pk5, spdif, rsvd2, rsvd3, i2c3), + GMUX(0x358, A, 2, dap2_fs_pa2, i2s1, hda, gmi, rsvd4), + GMUX(0x35C, A, 4, dap2_din_pa4, i2s1, hda, gmi, rsvd4), + GMUX(0x360, A, 5, dap2_dout_pa5, i2s1, hda, gmi, rsvd4), + GMUX(0x364, A, 3, dap2_sclk_pa3, i2s1, hda, gmi, rsvd4), + GMUX(0x368, X, 0, dvfs_pwm_px0, spi6, cldvfs, gmi, rsvd4), + GMUX(0x36C, X, 1, gpio_x1_aud_px1, spi6, rsvd2, gmi, rsvd4), + GMUX(0x370, X, 3, gpio_x3_aud_px3, spi6, spi1, gmi, rsvd4), + GMUX(0x374, X, 2, dvfs_clk_px2, spi6, cldvfs, gmi, rsvd4), + GMUX(0x378, X, 4, gpio_x4_aud_px4, gmi, spi1, spi2, dap2), + GMUX(0x37C, X, 5, gpio_x5_aud_px5, gmi, spi1, spi2, rsvd4), + GMUX(0x380, X, 6, gpio_x6_aud_px6, spi6, spi1, spi2, gmi), + GMUX(0x384, X, 7, gpio_x7_aud_px7, rsvd1, spi1, spi2, rsvd4), + GMUX(0x390, A, 6, sdmmc3_clk_pa6, sdmmc3, rsvd2, rsvd3, spi3), + GMUX(0x394, A, 7, sdmmc3_cmd_pa7, sdmmc3, pwm3, uarta, spi3), + GMUX(0x398, B, 7, sdmmc3_dat0_pb7, sdmmc3, rsvd2, rsvd3, spi3), + GMUX(0x39C, B, 6, sdmmc3_dat1_pb6, sdmmc3, pwm2, uarta, spi3), + GMUX(0x3A0, B, 5, sdmmc3_dat2_pb5, sdmmc3, pwm1, displaya, spi3), + GMUX(0x3A4, B, 4, sdmmc3_dat3_pb4, sdmmc3, pwm0, displayb, spi3), + GMUX(0x3BC, DD, 1, pex_l0_rst_n_pdd1, pe0, rsvd2, rsvd3, rsvd4), + GMUX(0x3C0, DD, 2, pex_l0_clkreq_n_pdd2, pe0, rsvd2, rsvd3, rsvd4), + GMUX(0x3C4, DD, 3, pex_wake_n_pdd3, pe, rsvd2, rsvd3, rsvd4), + GMUX(0x3CC, DD, 5, pex_l1_rst_n_pdd5, pe1, rsvd2, rsvd3, rsvd4), + GMUX(0x3D0, DD, 6, pex_l1_clkreq_n_pdd6, pe1, rsvd2, rsvd3, rsvd4), + GMUX(0x3E0, EE, 3, hdmi_cec_pee3, cec, rsvd2, rsvd3, rsvd4), + GMUX(0x3E4, V, 3, sdmmc1_wp_n_pv3, sdmmc1, clk12, spi4, uarta), + GMUX(0x3E8, V, 2, sdmmc3_cd_n_pv2, sdmmc3, owr, rsvd3, rsvd4), + GMUX(0x3EC, W, 2, gpio_w2_aud_pw2, spi6, rsvd2, spi2, i2c1), + GMUX(0x3F0, W, 3, gpio_w3_aud_pw3, spi6, spi1, spi2, i2c1), + GMUX(0x3F4, N, 4, usb_vbus_en0_pn4, usb, rsvd2, rsvd3, rsvd4), + GMUX(0x3F8, N, 5, usb_vbus_en1_pn5, usb, rsvd2, rsvd3, rsvd4), + GMUX(0x3FC, EE, 5, sdmmc3_clk_lb_in_pee5, sdmmc3, rsvd2, rsvd3, rsvd4), + GMUX(0x400, EE, 4, sdmmc3_clk_lb_out_pee4, sdmmc3, rsvd2, rsvd3, rsvd4), + FMUX(0x404, gmi_clk_lb, sdmmc2, rsvd2, gmi, rsvd4), + FMUX(0x408, reset_out_n, rsvd1, rsvd2, rsvd3, reset_out_n), + GMUX(0x40C, T, 0, kb_row16_pt0, kbc, rsvd2, rsvd3, uartc), + GMUX(0x410, T, 1, kb_row17_pt1, kbc, rsvd2, rsvd3, uartc), + GMUX(0x414, FF, 1, usb_vbus_en2_pff1, usb, rsvd2, rsvd3, rsvd4), + GMUX(0x418, FF, 2, pff2, sata, rsvd2, rsvd3, rsvd4), + GMUX(0x430, FF, 0, dp_hpd_pff0, dp, rsvd2, rsvd3, rsvd4), +}; + +struct tegra_grp { + char *name; + bus_size_t reg; + int drvdn_shift; + int drvdn_mask; + int drvup_shift; + int drvup_mask; +}; + +#define GRP(r, nm, dn_s, dn_w, up_s, up_w) \ +{ \ + .name = #nm, \ + .reg = r - 0x868, \ + .drvdn_shift = dn_s, \ + .drvdn_mask = (1 << dn_w) - 1, \ + .drvup_shift = up_s, \ + .drvup_mask = (1 << dn_w) - 1, \ +} + +/* Use register offsets from TRM */ +static const struct tegra_grp pin_grp_tbl[] = { + GRP(0x868, ao1, 12, 5, 20, 5), + GRP(0x86C, ao2, 12, 5, 20, 5), + GRP(0x870, at1, 12, 7, 20, 7), + GRP(0x874, at2, 12, 7, 20, 7), + GRP(0x878, at3, 12, 7, 20, 7), + GRP(0x87C, at4, 12, 7, 20, 7), + GRP(0x880, at5, 14, 5, 19, 5), + GRP(0x884, cdev1, 12, 5, 20, 5), + GRP(0x888, cdev2, 12, 5, 20, 5), + GRP(0x890, dap1, 12, 5, 20, 5), + GRP(0x894, dap2, 12, 5, 20, 5), + GRP(0x898, dap3, 12, 5, 20, 5), + GRP(0x89C, dap4, 12, 5, 20, 5), + GRP(0x8A0, dbg, 12, 5, 20, 5), + GRP(0x8B0, sdio3, 12, 7, 20, 7), + GRP(0x8B4, spi, 12, 5, 20, 5), + GRP(0x8B8, uaa, 12, 5, 20, 5), + GRP(0x8BC, uab, 12, 5, 20, 5), + GRP(0x8C0, uart2, 12, 5, 20, 5), + GRP(0x8C4, uart3, 12, 5, 20, 5), + GRP(0x8EC, sdio1, 12, 7, 20, 7), + GRP(0x8FC, ddc, 12, 5, 20, 5), + GRP(0x900, gma, 14, 5, 20, 5), + GRP(0x910, gme, 14, 5, 19, 5), + GRP(0x914, gmf, 14, 5, 19, 5), + GRP(0x918, gmg, 14, 5, 19, 5), + GRP(0x91C, gmh, 14, 5, 19, 5), + GRP(0x920, owr, 12, 5, 20, 5), + GRP(0x924, uda, 12, 5, 20, 5), + GRP(0x928, gpv, 12, 5, 20, 5), + GRP(0x92C, dev3, 12, 5, 20, 5), + GRP(0x938, cec, 12, 5, 20, 5), + GRP(0x994, at6, 12, 7, 20, 7), + GRP(0x998, dap5, 12, 5, 20, 5), + GRP(0x99C, usb_vbus_en, 12, 5, 20, 5), + GRP(0x9A8, ao3, 12, 5, -1, 0), + GRP(0x9B0, ao0, 12, 5, 20, 5), + GRP(0x9B4, hv0, 12, 5, -1, 0), + GRP(0x9C4, sdio4, 12, 5, 20, 5), + GRP(0x9C8, ao4, 12, 7, 20, 7), +}; + +static const struct tegra_grp * +pinmux_search_grp(char *grp_name) +{ + int i; + + for (i = 0; i < nitems(pin_grp_tbl); i++) { + if (strcmp(grp_name, pin_grp_tbl[i].name) == 0) + return (&pin_grp_tbl[i]); + } + return (NULL); +} + +static const struct tegra_mux * +pinmux_search_mux(char *pin_name) +{ + int i; + + for (i = 0; i < nitems(pin_mux_tbl); i++) { + if (strcmp(pin_name, pin_mux_tbl[i].name) == 0) + return (&pin_mux_tbl[i]); + } + return (NULL); +} + +static int +pinmux_mux_function(const struct tegra_mux *mux, char *fnc_name) +{ + int i; + + for (i = 0; i < 4; i++) { + if (strcmp(fnc_name, mux->functions[i]) == 0) + return (i); + } + return (-1); +} + +static int +pinmux_config_mux(struct pinmux_softc *sc, char *pin_name, + const struct tegra_mux *mux, struct pincfg *cfg) +{ + int tmp; + uint32_t reg; + + reg = bus_read_4(sc->mux_mem_res, mux->reg); + + if (cfg->function != NULL) { + tmp = pinmux_mux_function(mux, cfg->function); + if (tmp == -1) { + device_printf(sc->dev, + "Unknown function %s for pin %s\n", cfg->function, + pin_name); + return (ENXIO); + } + reg &= ~(TEGRA_MUX_FUNCTION_MASK << TEGRA_MUX_FUNCTION_SHIFT); + reg |= (tmp & TEGRA_MUX_FUNCTION_MASK) << + TEGRA_MUX_FUNCTION_SHIFT; + } + if (cfg->params[PROP_ID_PULL] != -1) { + reg &= ~(TEGRA_MUX_PUPD_MASK << TEGRA_MUX_PUPD_SHIFT); + reg |= (cfg->params[PROP_ID_PULL] & TEGRA_MUX_PUPD_MASK) << + TEGRA_MUX_PUPD_SHIFT; + } + if (cfg->params[PROP_ID_TRISTATE] != -1) { + reg &= ~(1 << TEGRA_MUX_TRISTATE_SHIFT); + reg |= (cfg->params[PROP_ID_TRISTATE] & 1) << + TEGRA_MUX_TRISTATE_SHIFT; + } + if (cfg->params[TEGRA_MUX_ENABLE_INPUT_SHIFT] != -1) { + reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT); + reg |= (cfg->params[TEGRA_MUX_ENABLE_INPUT_SHIFT] & 1) << + TEGRA_MUX_ENABLE_INPUT_SHIFT; + } + if (cfg->params[PROP_ID_ENABLE_INPUT] != -1) { + reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT); + reg |= (cfg->params[PROP_ID_ENABLE_INPUT] & 1) << + TEGRA_MUX_ENABLE_INPUT_SHIFT; + } + if (cfg->params[PROP_ID_ENABLE_INPUT] != -1) { + reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT); + reg |= (cfg->params[PROP_ID_OPEN_DRAIN] & 1) << + TEGRA_MUX_ENABLE_INPUT_SHIFT; + } + if (cfg->params[PROP_ID_LOCK] != -1) { + reg &= ~(1 << TEGRA_MUX_LOCK_SHIFT); + reg |= (cfg->params[PROP_ID_LOCK] & 1) << + TEGRA_MUX_LOCK_SHIFT; + } + if (cfg->params[PROP_ID_IORESET] != -1) { + reg &= ~(1 << TEGRA_MUX_IORESET_SHIFT); + reg |= (cfg->params[PROP_ID_IORESET] & 1) << + TEGRA_MUX_IORESET_SHIFT; + } + if (cfg->params[PROP_ID_RCV_SEL] != -1) { + reg &= ~(1 << TEGRA_MUX_RCV_SEL_SHIFT); + reg |= (cfg->params[PROP_ID_RCV_SEL] & 1) << + TEGRA_MUX_RCV_SEL_SHIFT; + } + bus_write_4(sc->mux_mem_res, mux->reg, reg); + return (0); +} + +static int +pinmux_config_grp(struct pinmux_softc *sc, char *grp_name, + const struct tegra_grp *grp, struct pincfg *cfg) +{ + uint32_t reg; + + reg = bus_read_4(sc->pad_mem_res, grp->reg); + + if (cfg->params[PROP_ID_HIGH_SPEED_MODE] != -1) { + reg &= ~(1 << TEGRA_GRP_HSM_SHIFT); + reg |= (cfg->params[PROP_ID_HIGH_SPEED_MODE] & 1) << + TEGRA_GRP_HSM_SHIFT; + } + if (cfg->params[PROP_ID_SCHMITT] != -1) { + reg &= ~(1 << TEGRA_GRP_SCHMT_SHIFT); + reg |= (cfg->params[PROP_ID_SCHMITT] & 1) << + TEGRA_GRP_SCHMT_SHIFT; + } + if (cfg->params[PROP_ID_DRIVE_TYPE] != -1) { + reg &= ~(TEGRA_GRP_DRV_TYPE_MASK << TEGRA_GRP_DRV_TYPE_SHIFT); + reg |= (cfg->params[PROP_ID_DRIVE_TYPE] & + TEGRA_GRP_DRV_TYPE_MASK) << TEGRA_GRP_DRV_TYPE_SHIFT; + } + if (cfg->params[PROP_ID_SLEW_RATE_RISING] != -1) { + reg &= ~(TEGRA_GRP_DRV_DRVDN_SLWR_MASK << + TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT); + reg |= (cfg->params[PROP_ID_SLEW_RATE_RISING] & + TEGRA_GRP_DRV_DRVDN_SLWR_MASK) << + TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT; + } + if (cfg->params[PROP_ID_SLEW_RATE_FALLING] != -1) { + reg &= ~(TEGRA_GRP_DRV_DRVUP_SLWF_MASK << + TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT); + reg |= (cfg->params[PROP_ID_SLEW_RATE_FALLING] & + TEGRA_GRP_DRV_DRVUP_SLWF_MASK) << + TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT; + } + if ((cfg->params[PROP_ID_DRIVE_DOWN_STRENGTH] != -1) && + (grp->drvdn_mask != -1)) { + reg &= ~(grp->drvdn_shift << grp->drvdn_mask); + reg |= (cfg->params[PROP_ID_DRIVE_DOWN_STRENGTH] & + grp->drvdn_mask) << grp->drvdn_shift; + } + if ((cfg->params[PROP_ID_DRIVE_UP_STRENGTH] != -1) && + (grp->drvup_mask != -1)) { + reg &= ~(grp->drvup_shift << grp->drvup_mask); + reg |= (cfg->params[PROP_ID_DRIVE_UP_STRENGTH] & + grp->drvup_mask) << grp->drvup_shift; + } + bus_write_4(sc->pad_mem_res, grp->reg, reg); + return (0); +} + +static int +pinmux_config_node(struct pinmux_softc *sc, char *pin_name, struct pincfg *cfg) +{ + const struct tegra_mux *mux; + const struct tegra_grp *grp; + uint32_t reg; + int rv; + + /* Handle MIPI special case first */ + if (strcmp(pin_name, "dsi_b") == 0) { + if (cfg->function == NULL) { + /* nothing to set */ + return (0); + } + reg = bus_read_4(sc->mipi_mem_res, 0); /* register 0x820 */ + if (strcmp(cfg->function, "csi") == 0) + reg &= ~(1 << 1); + else if (strcmp(cfg->function, "dsi_b") == 0) + reg |= (1 << 1); + bus_write_4(sc->mipi_mem_res, 0, reg); /* register 0x820 */ + } + + /* Handle pin muxes */ + mux = pinmux_search_mux(pin_name); + if (mux != NULL) { + if (mux->gpio_num != -1) { + /* XXXX TODO: Reserve gpio here */ + } + rv = pinmux_config_mux(sc, pin_name, mux, cfg); + return (rv); + } + + /* Handle pin groups */ + grp = pinmux_search_grp(pin_name); + if (grp != NULL) { + rv = pinmux_config_grp(sc, pin_name, grp, cfg); + return (rv); + } + + device_printf(sc->dev, "Unknown pin: %s\n", pin_name); + return (ENXIO); +} + +static int +pinmux_read_node(struct pinmux_softc *sc, phandle_t node, struct pincfg *cfg, + char **pins, int *lpins) +{ + int rv, i; + + *lpins = OF_getprop_alloc(node, "nvidia,pins", 1, (void **)pins); + if (*lpins <= 0) + return (ENOENT); + + /* Read function (mux) settings. */ + rv = OF_getprop_alloc(node, "nvidia,function", 1, + (void **)&cfg->function); + if (rv <= 0) + cfg->function = NULL; + + /* Read numeric properties. */ + for (i = 0; i < PROP_ID_MAX_ID; i++) { + rv = OF_getencprop(node, prop_names[i].name, &cfg->params[i], + sizeof(cfg->params[i])); + if (rv <= 0) + cfg->params[i] = -1; + } + return (0); +} + +static int +pinmux_process_node(struct pinmux_softc *sc, phandle_t node) +{ + struct pincfg cfg; + char *pins, *pname; + int i, len, lpins, rv; + + rv = pinmux_read_node(sc, node, &cfg, &pins, &lpins); + if (rv != 0) + return (rv); + + len = 0; + pname = pins; + do { + i = strlen(pname) + 1; + rv = pinmux_config_node(sc, pname, &cfg); + if (rv != 0) + device_printf(sc->dev, + "Cannot configure pin: %s: %d\n", pname, rv); + + len += i; + pname += i; + } while (len < lpins); + + if (pins != NULL) + free(pins, M_OFWPROP); + if (cfg.function != NULL) + free(cfg.function, M_OFWPROP); + return (rv); +} + +static int pinmux_configure(device_t dev, phandle_t cfgxref) +{ + struct pinmux_softc *sc; + phandle_t node, cfgnode; + int rv; + + sc = device_get_softc(dev); + cfgnode = OF_node_from_xref(cfgxref); + + + for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) { + if (!fdt_is_enabled(node)) + continue; + rv = pinmux_process_node(sc, node); + } + return (0); +} + +static int +pinmux_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + + device_set_desc(dev, "Tegra pin configuration"); + return (BUS_PROBE_DEFAULT); +} + +static int +pinmux_detach(device_t dev) +{ + + /* This device is always present. */ + return (EBUSY); +} + +static int +pinmux_attach(device_t dev) +{ + struct pinmux_softc * sc; + int rid; + + sc = device_get_softc(dev); + sc->dev = dev; + + rid = 0; + sc->pad_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->pad_mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + rid = 1; + sc->mux_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mux_mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + rid = 2; + sc->mipi_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mipi_mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + /* Register as a pinctrl device and process default configuration */ + fdt_pinctrl_register(dev, NULL); + fdt_pinctrl_configure_by_name(dev, "boot"); + + return (0); +} + + +static device_method_t tegra_pinmux_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pinmux_probe), + DEVMETHOD(device_attach, pinmux_attach), + DEVMETHOD(device_detach, pinmux_detach), + + /* fdt_pinctrl interface */ + DEVMETHOD(fdt_pinctrl_configure,pinmux_configure), + + DEVMETHOD_END +}; + +static driver_t tegra_pinmux_driver = { + "tegra_pinmux", + tegra_pinmux_methods, + sizeof(struct pinmux_softc), +}; + +static devclass_t tegra_pinmux_devclass; + +EARLY_DRIVER_MODULE(tegra_pinmux, simplebus, tegra_pinmux_driver, + tegra_pinmux_devclass, 0, 0, 71); diff --git a/sys/arm/nvidia/tegra_pmc.h b/sys/arm/nvidia/tegra_pmc.h new file mode 100644 index 0000000..933c408 --- /dev/null +++ b/sys/arm/nvidia/tegra_pmc.h @@ -0,0 +1,115 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#ifndef _TEGRA_PMC_H_ +#define _TEGRA_PMC_H_ + +enum tegra_suspend_mode { + TEGRA_SUSPEND_NONE = 0, + TEGRA_SUSPEND_LP2, /* CPU voltage off */ + TEGRA_SUSPEND_LP1, /* CPU voltage off, DRAM self-refresh */ + TEGRA_SUSPEND_LP0, /* CPU + core voltage off, DRAM self-refresh */ +}; + +/* PARTIDs for powergate */ +enum tegra_powergate_id { + TEGRA_POWERGATE_CRAIL = 0, + TEGRA_POWERGATE_TD = 1, + TEGRA_POWERGATE_VE = 2, + TEGRA_POWERGATE_PCX = 3, + TEGRA_POWERGATE_VDE = 4, + TEGRA_POWERGATE_L2C = 5, + TEGRA_POWERGATE_MPE = 6, + TEGRA_POWERGATE_HEG = 7, + TEGRA_POWERGATE_SAX = 8, + TEGRA_POWERGATE_CE1 = 9, + TEGRA_POWERGATE_CE2 = 10, + TEGRA_POWERGATE_CE3 = 11, + TEGRA_POWERGATE_CELP = 12, + /* */ + TEGRA_POWERGATE_CE0 = 14, + TEGRA_POWERGATE_C0NC = 15, + TEGRA_POWERGATE_C1NC = 16, + TEGRA_POWERGATE_SOR = 17, + TEGRA_POWERGATE_DIS = 18, + TEGRA_POWERGATE_DISB = 19, + TEGRA_POWERGATE_XUSBA = 20, + TEGRA_POWERGATE_XUSBB = 21, + TEGRA_POWERGATE_XUSBC = 22, + TEGRA_POWERGATE_VIC = 23, + TEGRA_POWERGATE_IRAM = 24, + /* */ + TEGRA_POWERGATE_3D = 32 + +}; + +/* PARTIDs for power rails */ +enum tegra_powerrail_id { + TEGRA_IO_RAIL_CSIA = 0, + TEGRA_IO_RAIL_CSIB = 1, + TEGRA_IO_RAIL_DSI = 2, + TEGRA_IO_RAIL_MIPI_BIAS = 3, + TEGRA_IO_RAIL_PEX_BIAS = 4, + TEGRA_IO_RAIL_PEX_CLK1 = 5, + TEGRA_IO_RAIL_PEX_CLK2 = 6, + TEGRA_IO_RAIL_USB0 = 9, + TEGRA_IO_RAIL_USB1 = 10, + TEGRA_IO_RAIL_USB2 = 11, + TEGRA_IO_RAIL_USB_BIAS = 12, + TEGRA_IO_RAIL_NAND = 13, + TEGRA_IO_RAIL_UART = 14, + TEGRA_IO_RAIL_BB = 15, + TEGRA_IO_RAIL_AUDIO = 17, + TEGRA_IO_RAIL_HSIC = 19, + TEGRA_IO_RAIL_COMP = 22, + TEGRA_IO_RAIL_HDMI = 28, + TEGRA_IO_RAIL_PEX_CNTRL = 32, + TEGRA_IO_RAIL_SDMMC1 = 33, + TEGRA_IO_RAIL_SDMMC3 = 34, + TEGRA_IO_RAIL_SDMMC4 = 35, + TEGRA_IO_RAIL_CAM = 36, + TEGRA_IO_RAIL_RES = 37, + TEGRA_IO_RAIL_HV = 38, + TEGRA_IO_RAIL_DSIB = 39, + TEGRA_IO_RAIL_DSIC = 40, + TEGRA_IO_RAIL_DSID = 41, + TEGRA_IO_RAIL_CSIE = 44, + TEGRA_IO_RAIL_LVDS = 57, + TEGRA_IO_RAIL_SYS_DDC = 58, +}; + +int tegra_powergate_is_powered(enum tegra_powergate_id id); +int tegra_powergate_power_on(enum tegra_powergate_id id); +int tegra_powergate_power_off(enum tegra_powergate_id id); +int tegra_powergate_remove_clamping(enum tegra_powergate_id id); +int tegra_powergate_sequence_power_up(enum tegra_powergate_id id, + clk_t clk, hwreset_t rst); +int tegra_io_rail_power_on(int tegra_powerrail_id); +int tegra_io_rail_power_off(int tegra_powerrail_id); + +#endif /*_TEGRA_PMC_H_*/
\ No newline at end of file diff --git a/sys/arm/nvidia/tegra_rtc.c b/sys/arm/nvidia/tegra_rtc.c new file mode 100644 index 0000000..c15ded3 --- /dev/null +++ b/sys/arm/nvidia/tegra_rtc.c @@ -0,0 +1,303 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * RTC driver for Tegra SoCs. + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/clock.h> +#include <sys/kernel.h> +#include <sys/limits.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/module.h> +#include <sys/resource.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <dev/extres/clk/clk.h> +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include "clock_if.h" + +#define RTC_CONTROL 0x00 +#define RTC_BUSY 0x04 +#define RTC_BUSY_STATUS (1 << 0) +#define RTC_SECONDS 0x08 +#define RTC_SHADOW_SECONDS 0x0c +#define RTC_MILLI_SECONDS 0x10 +#define RTC_SECONDS_ALARM0 0x14 +#define RTC_SECONDS_ALARM1 0x18 +#define RTC_MILLI_SECONDS_ALARM 0x1c +#define RTC_SECONDS_COUNTDOWN_ALARM 0x20 +#define RTC_MILLI_SECONDS_COUNTDOW_ALARM 0x24 +#define RTC_INTR_MASK 0x28 +#define RTC_INTR_MSEC_CDN_ALARM (1 << 4) +#define RTC_INTR_SEC_CDN_ALARM (1 << 3) +#define RTC_INTR_MSEC_ALARM (1 << 2) +#define RTC_INTR_SEC_ALARM1 (1 << 1) +#define RTC_INTR_SEC_ALARM0 (1 << 0) + +#define RTC_INTR_STATUS 0x2c +#define RTC_INTR_SOURCE 0x30 +#define RTC_INTR_SET 0x34 +#define RTC_CORRECTION_FACTOR 0x38 + +#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) + +#define LOCK(_sc) mtx_lock(&(_sc)->mtx) +#define UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) +#define SLEEP(_sc, timeout) \ + mtx_sleep(sc, &sc->mtx, 0, "rtcwait", timeout); +#define LOCK_INIT(_sc) \ + mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_rtc", MTX_DEF) +#define LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx) +#define ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED) +#define ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED) + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-rtc", 1}, + {NULL, 0} +}; + +struct tegra_rtc_softc { + device_t dev; + struct mtx mtx; + + struct resource *mem_res; + struct resource *irq_res; + void *irq_h; + + clk_t clk; + uint32_t core_freq; +}; + +static void +tegra_rtc_wait(struct tegra_rtc_softc *sc) +{ + int timeout; + + for (timeout = 500; timeout >0; timeout--) { + if ((RD4(sc, RTC_BUSY) & RTC_BUSY_STATUS) == 0) + break; + DELAY(1); + } + if (timeout <= 0) + device_printf(sc->dev, "Device busy timeouted\n"); + +} + +/* + * Get the time of day clock and return it in ts. + * Return 0 on success, an error number otherwise. + */ +static int +tegra_rtc_gettime(device_t dev, struct timespec *ts) +{ + struct tegra_rtc_softc *sc; + struct timeval tv; + uint32_t msec, sec; + + sc = device_get_softc(dev); + + LOCK(sc); + msec = RD4(sc, RTC_MILLI_SECONDS); + sec = RD4(sc, RTC_SHADOW_SECONDS); + UNLOCK(sc); + tv.tv_sec = sec; + tv.tv_usec = msec * 1000; + TIMEVAL_TO_TIMESPEC(&tv, ts); + return (0); +} + + +static int +tegra_rtc_settime(device_t dev, struct timespec *ts) +{ + struct tegra_rtc_softc *sc; + struct timeval tv; + + sc = device_get_softc(dev); + + LOCK(sc); + TIMESPEC_TO_TIMEVAL(&tv, ts); + tegra_rtc_wait(sc); + WR4(sc, RTC_SECONDS, tv.tv_sec); + UNLOCK(sc); + + return (0); +} + + +static void +tegra_rtc_intr(void *arg) +{ + struct tegra_rtc_softc *sc; + uint32_t status; + + sc = (struct tegra_rtc_softc *)arg; + LOCK(sc); + status = RD4(sc, RTC_INTR_STATUS); + WR4(sc, RTC_INTR_STATUS, status); + UNLOCK(sc); +} + +static int +tegra_rtc_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + return (BUS_PROBE_DEFAULT); +} + +static int +tegra_rtc_attach(device_t dev) +{ + int rv, rid; + struct tegra_rtc_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + LOCK_INIT(sc); + + /* Get the memory resource for the register mapping. */ + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot map registers.\n"); + rv = ENXIO; + goto fail; + } + + /* Allocate our IRQ resource. */ + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "Cannot allocate interrupt.\n"); + rv = ENXIO; + goto fail; + } + + /* OFW resources. */ + rv = clk_get_by_ofw_index(dev, 0, &sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot get i2c clock: %d\n", rv); + goto fail; + } + rv = clk_enable(sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot enable clock: %d\n", rv); + goto fail; + } + + /* Init hardware. */ + WR4(sc, RTC_SECONDS_ALARM0, 0); + WR4(sc, RTC_SECONDS_ALARM1, 0); + WR4(sc, RTC_INTR_STATUS, 0xFFFFFFFF); + WR4(sc, RTC_INTR_MASK, 0); + + /* Setup interrupt */ + rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, tegra_rtc_intr, sc, &sc->irq_h); + if (rv) { + device_printf(dev, "Cannot setup interrupt.\n"); + goto fail; + } + + /* + * Register as a time of day clock with 1-second resolution. + * + * XXXX Not yet, we don't have support for multiple RTCs + */ + /* clock_register(dev, 1000000); */ + + return (bus_generic_attach(dev)); + +fail: + if (sc->clk != NULL) + clk_release(sc->clk); + if (sc->irq_h != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_h); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + LOCK_DESTROY(sc); + + return (rv); +} + +static int +tegra_rtc_detach(device_t dev) +{ + struct tegra_rtc_softc *sc; + + sc = device_get_softc(dev); + if (sc->irq_h != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_h); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + LOCK_DESTROY(sc); + return (bus_generic_detach(dev)); +} + +static device_method_t tegra_rtc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_rtc_probe), + DEVMETHOD(device_attach, tegra_rtc_attach), + DEVMETHOD(device_detach, tegra_rtc_detach), + + /* clock interface */ + DEVMETHOD(clock_gettime, tegra_rtc_gettime), + DEVMETHOD(clock_settime, tegra_rtc_settime), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(tegra_rtc, tegra_rtc_driver, tegra_rtc_methods, + sizeof(struct tegra_rtc_softc)); +static devclass_t tegra_rtc_devclass; +DRIVER_MODULE(tegra_rtc, simplebus, tegra_rtc_driver, tegra_rtc_devclass, 0, 0); diff --git a/sys/arm/nvidia/tegra_sdhci.c b/sys/arm/nvidia/tegra_sdhci.c new file mode 100644 index 0000000..e2ec063 --- /dev/null +++ b/sys/arm/nvidia/tegra_sdhci.c @@ -0,0 +1,465 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * SDHCI driver glue for NVIDIA Tegra family + * + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/types.h> +#include <sys/bus.h> +#include <sys/callout.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/resource.h> +#include <sys/rman.h> +#include <sys/sysctl.h> +#include <sys/taskqueue.h> +#include <sys/time.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <machine/intr.h> + +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> +#include <dev/gpio/gpiobusvar.h> +#include <dev/mmc/bridge.h> +#include <dev/mmc/mmcreg.h> +#include <dev/mmc/mmcbrvar.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/sdhci/sdhci.h> + +#include "sdhci_if.h" + +/* Tegra SDHOST controller vendor register definitions */ +#define SDMMC_VENDOR_CLOCK_CNTRL 0x100 +#define VENDOR_CLOCK_CNTRL_CLK_SHIFT 8 +#define VENDOR_CLOCK_CNTRL_CLK_MASK 0xFF +#define SDMMC_VENDOR_SYS_SW_CNTRL 0x104 +#define SDMMC_VENDOR_CAP_OVERRIDES 0x10C +#define SDMMC_VENDOR_BOOT_CNTRL 0x110 +#define SDMMC_VENDOR_BOOT_ACK_TIMEOUT 0x114 +#define SDMMC_VENDOR_BOOT_DAT_TIMEOUT 0x118 +#define SDMMC_VENDOR_DEBOUNCE_COUNT 0x11C +#define SDMMC_VENDOR_MISC_CNTRL 0x120 +#define VENDOR_MISC_CTRL_ENABLE_SDR104 0x8 +#define VENDOR_MISC_CTRL_ENABLE_SDR50 0x10 +#define VENDOR_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20 +#define VENDOR_MISC_CTRL_ENABLE_DDR50 0x200 +#define SDMMC_MAX_CURRENT_OVERRIDE 0x124 +#define SDMMC_MAX_CURRENT_OVERRIDE_HI 0x128 +#define SDMMC_VENDOR_CLK_GATE_HYSTERESIS_COUNT 0x1D0 +#define SDMMC_VENDOR_PHWRESET_VAL0 0x1D4 +#define SDMMC_VENDOR_PHWRESET_VAL1 0x1D8 +#define SDMMC_VENDOR_PHWRESET_VAL2 0x1DC +#define SDMMC_SDMEMCOMPPADCTRL_0 0x1E0 +#define SDMMC_AUTO_CAL_CONFIG 0x1E4 +#define SDMMC_AUTO_CAL_INTERVAL 0x1E8 +#define SDMMC_AUTO_CAL_STATUS 0x1EC +#define SDMMC_SDMMC_MCCIF_FIFOCTRL 0x1F4 +#define SDMMC_TIMEOUT_WCOAL_SDMMC 0x1F8 + +/* Compatible devices. */ +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-sdhci", 1}, + {NULL, 0}, +}; + +struct tegra_sdhci_softc { + device_t dev; + struct resource * mem_res; + struct resource * irq_res; + void * intr_cookie; + u_int quirks; /* Chip specific quirks */ + u_int caps; /* If we override SDHCI_CAPABILITIES */ + uint32_t max_clk; /* Max possible freq */ + clk_t clk; + hwreset_t reset; + gpio_pin_t gpio_cd; + gpio_pin_t gpio_wp; + gpio_pin_t gpio_power; + + int force_card_present; + struct sdhci_slot slot; + +}; + +static inline uint32_t +RD4(struct tegra_sdhci_softc *sc, bus_size_t off) +{ + + return (bus_read_4(sc->mem_res, off)); +} + +static uint8_t +tegra_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off) +{ + struct tegra_sdhci_softc *sc; + + sc = device_get_softc(dev); + return (bus_read_1(sc->mem_res, off)); +} + +static uint16_t +tegra_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off) +{ + struct tegra_sdhci_softc *sc; + + sc = device_get_softc(dev); + return (bus_read_2(sc->mem_res, off)); +} + +static uint32_t +tegra_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off) +{ + struct tegra_sdhci_softc *sc; + uint32_t val32; + + sc = device_get_softc(dev); + val32 = bus_read_4(sc->mem_res, off); + /* Force the card-present state if necessary. */ + if (off == SDHCI_PRESENT_STATE && sc->force_card_present) + val32 |= SDHCI_CARD_PRESENT; + return (val32); +} + +static void +tegra_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, + uint32_t *data, bus_size_t count) +{ + struct tegra_sdhci_softc *sc; + + sc = device_get_softc(dev); + bus_read_multi_4(sc->mem_res, off, data, count); +} + +static void +tegra_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, + uint8_t val) +{ + struct tegra_sdhci_softc *sc; + + sc = device_get_softc(dev); + bus_write_1(sc->mem_res, off, val); +} + +static void +tegra_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, + uint16_t val) +{ + struct tegra_sdhci_softc *sc; + + sc = device_get_softc(dev); + bus_write_2(sc->mem_res, off, val); +} + +static void +tegra_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, + uint32_t val) +{ + struct tegra_sdhci_softc *sc; + + sc = device_get_softc(dev); + bus_write_4(sc->mem_res, off, val); +} + +static void +tegra_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, + uint32_t *data, bus_size_t count) +{ + struct tegra_sdhci_softc *sc; + + sc = device_get_softc(dev); + bus_write_multi_4(sc->mem_res, off, data, count); +} + +static void +tegra_sdhci_intr(void *arg) +{ + struct tegra_sdhci_softc *sc = arg; + + sdhci_generic_intr(&sc->slot); + RD4(sc, SDHCI_INT_STATUS); +} + +static int +tegra_generic_get_ro(device_t brdev, device_t reqdev) +{ + + return (0); +} + +static int +tegra_sdhci_probe(device_t dev) +{ + struct tegra_sdhci_softc *sc; + phandle_t node; + pcell_t cid; + const struct ofw_compat_data *cd; + + sc = device_get_softc(dev); + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_is_compatible(dev, "nvidia,tegra124-sdhci")) { + device_set_desc(dev, "Tegra SDHCI controller"); + } else + return (ENXIO); + cd = ofw_bus_search_compatible(dev, compat_data); + if (cd->ocd_data == 0) + return (ENXIO); + + node = ofw_bus_get_node(dev); + + /* Allow dts to patch quirks, slots, and max-frequency. */ + if ((OF_getencprop(node, "quirks", &cid, sizeof(cid))) > 0) + sc->quirks = cid; + if ((OF_getencprop(node, "max-frequency", &cid, sizeof(cid))) > 0) + sc->max_clk = cid; + + return (BUS_PROBE_DEFAULT); +} + +static int +tegra_sdhci_attach(device_t dev) +{ + struct tegra_sdhci_softc *sc; + int rid, rv; + uint64_t freq; + phandle_t node, prop; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (!sc->mem_res) { + device_printf(dev, "cannot allocate memory window\n"); + rv = ENXIO; + goto fail; + } + + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (!sc->irq_res) { + device_printf(dev, "cannot allocate interrupt\n"); + rv = ENXIO; + goto fail; + } + + if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, tegra_sdhci_intr, sc, &sc->intr_cookie)) { + device_printf(dev, "cannot setup interrupt handler\n"); + rv = ENXIO; + goto fail; + } + + rv = hwreset_get_by_ofw_name(sc->dev, "sdhci", &sc->reset); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'sdhci' reset\n"); + goto fail; + } + rv = hwreset_deassert(sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot unreset 'sdhci' reset\n"); + goto fail; + } + + gpio_pin_get_by_ofw_property(sc->dev, "cd-gpios", &sc->gpio_cd); + gpio_pin_get_by_ofw_property(sc->dev, "power-gpios", &sc->gpio_power); + gpio_pin_get_by_ofw_property(sc->dev, "wp-gpios", &sc->gpio_wp); + + rv = clk_get_by_ofw_index(dev, 0, &sc->clk); + if (rv != 0) { + + device_printf(dev, "Cannot get clock\n"); + goto fail; + } + + rv = clk_get_by_ofw_index(dev, 0, &sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot get clock\n"); + goto fail; + } + rv = clk_enable(sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot enable clock\n"); + goto fail; + } + rv = clk_set_freq(sc->clk, 48000000, CLK_SET_ROUND_DOWN); + if (rv != 0) { + device_printf(dev, "Cannot set clock\n"); + } + rv = clk_get_freq(sc->clk, &freq); + if (rv != 0) { + device_printf(dev, "Cannot get clock frequency\n"); + goto fail; + } + if (bootverbose) + device_printf(dev, " Base MMC clock: %lld\n", freq); + + /* Fill slot information. */ + sc->max_clk = (int)freq; + sc->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | + SDHCI_QUIRK_MISSING_CAPS; + + /* Limit real slot capabilities. */ + sc->caps = RD4(sc, SDHCI_CAPABILITIES); + if (OF_getencprop(node, "bus-width", &prop, sizeof(prop)) > 0) { + sc->caps &= ~(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA); + switch (prop) { + case 8: + sc->caps |= MMC_CAP_8_BIT_DATA; + /* FALLTHROUGH */ + case 4: + sc->caps |= MMC_CAP_4_BIT_DATA; + break; + case 1: + break; + default: + device_printf(dev, "Bad bus-width value %u\n", prop); + break; + } + } + if (OF_hasprop(node, "non-removable")) + sc->force_card_present = 1; + /* + * Clear clock field, so SDHCI driver uses supplied frequency. + * in sc->slot.max_clk + */ + sc->caps &= ~SDHCI_CLOCK_V3_BASE_MASK; + + sc->slot.quirks = sc->quirks; + sc->slot.max_clk = sc->max_clk; + sc->slot.caps = sc->caps; + + rv = sdhci_init_slot(dev, &sc->slot, 0); + if (rv != 0) { + goto fail; + } + + bus_generic_probe(dev); + bus_generic_attach(dev); + + sdhci_start_slot(&sc->slot); + + return (0); + +fail: + if (sc->gpio_cd != NULL) + gpio_pin_release(sc->gpio_cd); + if (sc->gpio_wp != NULL) + gpio_pin_release(sc->gpio_wp); + if (sc->gpio_power != NULL) + gpio_pin_release(sc->gpio_power); + if (sc->clk != NULL) + clk_release(sc->clk); + if (sc->reset != NULL) + hwreset_release(sc->reset); + if (sc->intr_cookie != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + return (rv); +} + +static int +tegra_sdhci_detach(device_t dev) +{ + struct tegra_sdhci_softc *sc = device_get_softc(dev); + struct sdhci_slot *slot = &sc->slot; + + bus_generic_detach(dev); + clk_release(sc->clk); + bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie); + bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq_res), + sc->irq_res); + + sdhci_cleanup_slot(slot); + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(sc->mem_res), + sc->mem_res); + return (0); +} + +static device_method_t tegra_sdhci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_sdhci_probe), + DEVMETHOD(device_attach, tegra_sdhci_attach), + DEVMETHOD(device_detach, tegra_sdhci_detach), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar), + DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar), + + /* MMC bridge interface */ + DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios), + DEVMETHOD(mmcbr_request, sdhci_generic_request), + DEVMETHOD(mmcbr_get_ro, tegra_generic_get_ro), + DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host), + DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host), + + /* SDHCI registers accessors */ + DEVMETHOD(sdhci_read_1, tegra_sdhci_read_1), + DEVMETHOD(sdhci_read_2, tegra_sdhci_read_2), + DEVMETHOD(sdhci_read_4, tegra_sdhci_read_4), + DEVMETHOD(sdhci_read_multi_4, tegra_sdhci_read_multi_4), + DEVMETHOD(sdhci_write_1, tegra_sdhci_write_1), + DEVMETHOD(sdhci_write_2, tegra_sdhci_write_2), + DEVMETHOD(sdhci_write_4, tegra_sdhci_write_4), + DEVMETHOD(sdhci_write_multi_4, tegra_sdhci_write_multi_4), + + { 0, 0 } +}; + +static devclass_t tegra_sdhci_devclass; + +static driver_t tegra_sdhci_driver = { + "sdhci_tegra", + tegra_sdhci_methods, + sizeof(struct tegra_sdhci_softc), +}; + +DRIVER_MODULE(sdhci_tegra, simplebus, tegra_sdhci_driver, tegra_sdhci_devclass, + 0, 0); +MODULE_DEPEND(sdhci_tegra, sdhci, 1, 1, 1); +DRIVER_MODULE(mmc, sdhci_tegra, mmc_driver, mmc_devclass, NULL, NULL); diff --git a/sys/arm/nvidia/tegra_soctherm.c b/sys/arm/nvidia/tegra_soctherm.c new file mode 100644 index 0000000..3102173 --- /dev/null +++ b/sys/arm/nvidia/tegra_soctherm.c @@ -0,0 +1,696 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Thermometer and thermal zones driver for Tegra SoCs. + * Calibration data and algo are taken from Linux, because this part of SoC + * is undocumented in TRM. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/gpio.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/malloc.h> +#include <sys/rman.h> +#include <sys/sysctl.h> + +#include <machine/bus.h> + +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <arm/nvidia/tegra_efuse.h> +#include <gnu/dts/include/dt-bindings/thermal/tegra124-soctherm.h> +#include "tegra_soctherm_if.h" + +/* Per sensors registers - base is 0x0c0*/ +#define TSENSOR_CONFIG0 0x000 +#define TSENSOR_CONFIG0_TALL(x) (((x) & 0xFFFFF) << 8) +#define TSENSOR_CONFIG0_STATUS_CLR (1 << 5) +#define TSENSOR_CONFIG0_TCALC_OVERFLOW (1 << 4) +#define TSENSOR_CONFIG0_OVERFLOW (1 << 3) +#define TSENSOR_CONFIG0_CPTR_OVERFLOW (1 << 2) +#define TSENSOR_CONFIG0_RO_SEL (1 << 1) +#define TSENSOR_CONFIG0_STOP (1 << 0) + +#define TSENSOR_CONFIG1 0x004 +#define TSENSOR_CONFIG1_TEMP_ENABLE (1U << 31) +#define TSENSOR_CONFIG1_TEN_COUNT(x) (((x) & 0x3F) << 24) +#define TSENSOR_CONFIG1_TIDDQ_EN(x) (((x) & 0x3F) << 15) +#define TSENSOR_CONFIG1_TSAMPLE(x) (((x) & 0x3FF) << 0) + +#define TSENSOR_CONFIG2 0x008 +#define TSENSOR_CONFIG2_THERMA(x) (((x) & 0xFFFF) << 16) +#define TSENSOR_CONFIG2_THERMB(x) (((x) & 0xFFFF) << 0) + +#define TSENSOR_STATUS0 0x00c +#define TSENSOR_STATUS0_CAPTURE_VALID (1U << 31) +#define TSENSOR_STATUS0_CAPTURE(x) (((x) >> 0) & 0xffff) + +#define TSENSOR_STATUS1 0x010 +#define TSENSOR_STATUS1_TEMP_VALID (1U << 31) +#define TSENSOR_STATUS1_TEMP(x) (((x) >> 0) & 0xffff) + +#define TSENSOR_STATUS2 0x014 +#define TSENSOR_STATUS2_TEMP_MAX(x) (((x) >> 16) & 0xffff) +#define TSENSOR_STATUS2_TEMP_MIN(x) (((x) >> 0) & 0xffff) + +/* Global registers */ +#define TSENSOR_PDIV 0x1c0 +#define TSENSOR_PDIV_T124 0x8888 +#define TSENSOR_HOTSPOT_OFF 0x1c4 +#define TSENSOR_HOTSPOT_OFF_T124 0x00060600 +#define TSENSOR_TEMP1 0x1c8 +#define TSENSOR_TEMP2 0x1cc + +/* Readbacks */ +#define READBACK_VALUE_MASK 0xff00 +#define READBACK_VALUE_SHIFT 8 +#define READBACK_ADD_HALF (1 << 7) +#define READBACK_NEGATE (1 << 0) + + +/* Fuses */ +#define FUSE_TSENSOR_CALIB_CP_TS_BASE_SHIFT 0 +#define FUSE_TSENSOR_CALIB_CP_TS_BASE_BITS 13 +#define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13 +#define FUSE_TSENSOR_CALIB_FT_TS_BASE_BITS 13 + +#define FUSE_TSENSOR8_CALIB 0x180 +#define FUSE_TSENSOR8_CALIB_CP_TS_BASE(x) (((x) >> 0) & 0x3ff) +#define FUSE_TSENSOR8_CALIB_FT_TS_BASE(x) (((x) >> 10) & 0x7ff) + +#define FUSE_SPARE_REALIGNMENT_REG 0x1fc +#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_SHIFT 0 +#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_BITS 6 +#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT 21 +#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_BITS 5 +#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP(x) (((x) >> 0) & 0x3f) +#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT(x) (((x) >> 21) & 0x1f) + + +#define NOMINAL_CALIB_FT_T124 105 +#define NOMINAL_CALIB_CP_T124 25 + +#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) + +static struct sysctl_ctx_list soctherm_sysctl_ctx; + +struct soctherm_shared_cal { + uint32_t base_cp; + uint32_t base_ft; + int32_t actual_temp_cp; + int32_t actual_temp_ft; +}; +struct tsensor_cfg { + uint32_t tall; + uint32_t tsample; + uint32_t tiddq_en; + uint32_t ten_count; + uint32_t pdiv; + uint32_t tsample_ate; + uint32_t pdiv_ate; +}; + +struct tsensor { + char *name; + int id; + struct tsensor_cfg *cfg; + bus_addr_t sensor_base; + bus_addr_t calib_fuse; + int fuse_corr_alpha; + int fuse_corr_beta; + + int16_t therm_a; + int16_t therm_b; +}; + +struct soctherm_softc { + device_t dev; + struct resource *mem_res; + struct resource *irq_res; + void *irq_ih; + + clk_t tsensor_clk; + clk_t soctherm_clk; + hwreset_t reset; + + int ntsensors; + struct tsensor *tsensors; +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-soctherm", 1}, + {NULL, 0}, +}; + +static struct tsensor_cfg t124_tsensor_config = { + .tall = 16300, + .tsample = 120, + .tiddq_en = 1, + .ten_count = 1, + .pdiv = 8, + .tsample_ate = 480, + .pdiv_ate = 8 +}; + + +static struct tsensor t124_tsensors[] = { + { + .name = "cpu0", + .id = TEGRA124_SOCTHERM_SENSOR_CPU, + .cfg = &t124_tsensor_config, + .sensor_base = 0x0c0, + .calib_fuse = 0x098, + .fuse_corr_alpha = 1135400, + .fuse_corr_beta = -6266900, + }, + { + .name = "cpu1", + .id = -1, + .cfg = &t124_tsensor_config, + .sensor_base = 0x0e0, + .calib_fuse = 0x084, + .fuse_corr_alpha = 1122220, + .fuse_corr_beta = -5700700, + }, + { + .name = "cpu2", + .id = -1, + .cfg = &t124_tsensor_config, + .sensor_base = 0x100, + .calib_fuse = 0x088, + .fuse_corr_alpha = 1127000, + .fuse_corr_beta = -6768200, + }, + { + .name = "cpu3", + .id = -1, + .cfg = &t124_tsensor_config, + .sensor_base = 0x120, + .calib_fuse = 0x12c, + .fuse_corr_alpha = 1110900, + .fuse_corr_beta = -6232000, + }, + { + .name = "mem0", + .id = TEGRA124_SOCTHERM_SENSOR_MEM, + .cfg = &t124_tsensor_config, + .sensor_base = 0x140, + .calib_fuse = 0x158, + .fuse_corr_alpha = 1122300, + .fuse_corr_beta = -5936400, + }, + { + .name = "mem1", + .id = -1, + .cfg = &t124_tsensor_config, + .sensor_base = 0x160, + .calib_fuse = 0x15c, + .fuse_corr_alpha = 1145700, + .fuse_corr_beta = -7124600, + }, + { + .name = "gpu", + .id = TEGRA124_SOCTHERM_SENSOR_GPU, + .cfg = &t124_tsensor_config, + .sensor_base = 0x180, + .calib_fuse = 0x154, + .fuse_corr_alpha = 1120100, + .fuse_corr_beta = -6000500, + }, + { + .name = "pllX", + .id = TEGRA124_SOCTHERM_SENSOR_PLLX, + .cfg = &t124_tsensor_config, + .sensor_base = 0x1a0, + .calib_fuse = 0x160, + .fuse_corr_alpha = 1106500, + .fuse_corr_beta = -6729300, + }, +}; + +/* Extract signed integer bitfield from register */ +static int +extract_signed(uint32_t reg, int shift, int bits) +{ + int32_t val; + uint32_t mask; + + mask = (1 << bits) - 1; + val = ((reg >> shift) & mask) << (32 - bits); + val >>= 32 - bits; + return ((int32_t)val); +} + +static inline int64_t div64_s64_precise(int64_t a, int64_t b) +{ + int64_t r, al; + + al = a << 16; + r = (al * 2 + 1) / (2 * b); + return r >> 16; +} + +static void +get_shared_cal(struct soctherm_softc *sc, struct soctherm_shared_cal *cal) +{ + uint32_t val; + int calib_cp, calib_ft; + + val = tegra_fuse_read_4(FUSE_TSENSOR8_CALIB); + cal->base_cp = FUSE_TSENSOR8_CALIB_CP_TS_BASE(val); + cal->base_ft = FUSE_TSENSOR8_CALIB_FT_TS_BASE(val); + + val = tegra_fuse_read_4(FUSE_SPARE_REALIGNMENT_REG); + calib_ft = extract_signed(val, + FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT, + FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_BITS); + calib_cp = extract_signed(val, + FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_SHIFT, + FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_BITS); + + cal->actual_temp_cp = 2 * NOMINAL_CALIB_CP_T124 + calib_cp; + cal->actual_temp_ft = 2 * NOMINAL_CALIB_FT_T124 + calib_ft; +#ifdef DEBUG + printf("%s: base_cp: %u, base_ft: %d," + " actual_temp_cp: %d, actual_temp_ft: %d\n", + __func__, cal->base_cp, cal->base_ft, + cal->actual_temp_cp, cal->actual_temp_ft); +#endif +} + + +static void +tsensor_calibration(struct tsensor *sensor, struct soctherm_shared_cal *shared) +{ + uint32_t val; + int mult, div, calib_cp, calib_ft; + int actual_tsensor_ft, actual_tsensor_cp, delta_sens, delta_temp; + int temp_a, temp_b; + int64_t tmp; + + val = tegra_fuse_read_4(sensor->calib_fuse); + calib_cp = extract_signed(val, + FUSE_TSENSOR_CALIB_CP_TS_BASE_SHIFT, + FUSE_TSENSOR_CALIB_CP_TS_BASE_BITS); + actual_tsensor_cp = shared->base_cp * 64 + calib_cp; + + calib_ft = extract_signed(val, + FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT, + FUSE_TSENSOR_CALIB_FT_TS_BASE_BITS); + actual_tsensor_ft = shared->base_ft * 32 + calib_ft; + + delta_sens = actual_tsensor_ft - actual_tsensor_cp; + delta_temp = shared->actual_temp_ft - shared->actual_temp_cp; + mult = sensor->cfg->pdiv * sensor->cfg->tsample_ate; + div = sensor->cfg->tsample * sensor->cfg->pdiv_ate; + + + temp_a = div64_s64_precise((int64_t) delta_temp * (1LL << 13) * mult, + (int64_t) delta_sens * div); + + tmp = (int64_t)actual_tsensor_ft * shared->actual_temp_cp - + (int64_t)actual_tsensor_cp * shared->actual_temp_ft; + temp_b = div64_s64_precise(tmp, (int64_t)delta_sens); + + temp_a = div64_s64_precise((int64_t)temp_a * sensor->fuse_corr_alpha, + 1000000); + temp_b = div64_s64_precise((int64_t)temp_b * sensor->fuse_corr_alpha + + sensor->fuse_corr_beta, 1000000); + sensor->therm_a = (int16_t)temp_a; + sensor->therm_b = (int16_t)temp_b; +#ifdef DEBUG + printf("%s: sensor %s fuse: 0x%08X (0x%04X, 0x%04X)" + " calib_cp: %d(0x%04X), calib_ft: %d(0x%04X)\n", + __func__, sensor->name, val, val & 0x1FFF, (val >> 13) & 0x1FFF, + calib_cp, calib_cp, calib_ft, calib_ft); + printf("therma: 0x%04X(%d), thermb: 0x%04X(%d)\n", + (uint16_t)sensor->therm_a, temp_a, + (uint16_t)sensor->therm_b, sensor->therm_b); +#endif +} + +static void +soctherm_init_tsensor(struct soctherm_softc *sc, struct tsensor *sensor, + struct soctherm_shared_cal *shared_cal) +{ + uint32_t val; + + tsensor_calibration(sensor, shared_cal); + + val = RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0); + val |= TSENSOR_CONFIG0_STOP; + val |= TSENSOR_CONFIG0_STATUS_CLR; + WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val); + + val = TSENSOR_CONFIG0_TALL(sensor->cfg->tall); + val |= TSENSOR_CONFIG0_STOP; + WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val); + + val = TSENSOR_CONFIG1_TSAMPLE(sensor->cfg->tsample - 1); + val |= TSENSOR_CONFIG1_TIDDQ_EN(sensor->cfg->tiddq_en); + val |= TSENSOR_CONFIG1_TEN_COUNT(sensor->cfg->ten_count); + val |= TSENSOR_CONFIG1_TEMP_ENABLE; + WR4(sc, sensor->sensor_base + TSENSOR_CONFIG1, val); + + val = TSENSOR_CONFIG2_THERMA((uint16_t)sensor->therm_a) | + TSENSOR_CONFIG2_THERMB((uint16_t)sensor->therm_b); + WR4(sc, sensor->sensor_base + TSENSOR_CONFIG2, val); + + val = RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0); + val &= ~TSENSOR_CONFIG0_STOP; + WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val); +#ifdef DEBUG + printf(" Sensor: %s cfg:0x%08X, 0x%08X, 0x%08X," + " sts:0x%08X, 0x%08X, 0x%08X\n", sensor->name, + RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0), + RD4(sc, sensor->sensor_base + TSENSOR_CONFIG1), + RD4(sc, sensor->sensor_base + TSENSOR_CONFIG2), + RD4(sc, sensor->sensor_base + TSENSOR_STATUS0), + RD4(sc, sensor->sensor_base + TSENSOR_STATUS1), + RD4(sc, sensor->sensor_base + TSENSOR_STATUS2) + ); +#endif +} + +static int +soctherm_convert_raw(uint32_t val) +{ + int32_t t; + + t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000; + if (val & READBACK_ADD_HALF) + t += 500; + if (val & READBACK_NEGATE) + t *= -1; + + return t; +} + +static int +soctherm_read_temp(struct soctherm_softc *sc, struct tsensor *sensor, int *temp) +{ + int timeout; + uint32_t val; + + + /* wait for valid sample */ + for (timeout = 1000; timeout > 0; timeout--) { + val = RD4(sc, sensor->sensor_base + TSENSOR_STATUS1); + if ((val & TSENSOR_STATUS1_TEMP_VALID) != 0) + break; + DELAY(100); + } + if (timeout <= 0) + device_printf(sc->dev, "Sensor %s timeouted\n", sensor->name); + *temp = soctherm_convert_raw(val); +#ifdef DEBUG + printf("%s: Raw: 0x%08X, temp: %d\n", __func__, val, *temp); + printf(" Sensor: %s cfg:0x%08X, 0x%08X, 0x%08X," + " sts:0x%08X, 0x%08X, 0x%08X\n", sensor->name, + RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0), + RD4(sc, sensor->sensor_base + TSENSOR_CONFIG1), + RD4(sc, sensor->sensor_base + TSENSOR_CONFIG2), + RD4(sc, sensor->sensor_base + TSENSOR_STATUS0), + RD4(sc, sensor->sensor_base + TSENSOR_STATUS1), + RD4(sc, sensor->sensor_base + TSENSOR_STATUS2) + ); +#endif + return 0; +} + +static int +soctherm_get_temp(device_t dev, device_t cdev, uintptr_t id, int *val) +{ + struct soctherm_softc *sc; + int i; + + sc = device_get_softc(dev); + /* The direct sensor map starts at 0x100 */ + if (id >= 0x100) { + id -= 0x100; + if (id >= sc->ntsensors) + return (ERANGE); + return(soctherm_read_temp(sc, sc->tsensors + id, val)); + } + /* Linux (DT) compatible thermal zones */ + for (i = 0; i < sc->ntsensors; i++) { + if (sc->tsensors->id == id) + return(soctherm_read_temp(sc, sc->tsensors + id, val)); + } + return (ERANGE); +} + +static int +soctherm_sysctl_temperature(SYSCTL_HANDLER_ARGS) +{ + struct soctherm_softc *sc; + int val; + int rv; + int id; + + /* Write request */ + if (req->newptr != NULL) + return (EINVAL); + + sc = arg1; + id = arg2; + + if (id >= sc->ntsensors) + return (ERANGE); + rv = soctherm_read_temp(sc, sc->tsensors + id, &val); + if (rv != 0) + return (rv); + + val = val / 100; + val += 2731; + rv = sysctl_handle_int(oidp, &val, 0, req); + return (rv); +} + +static int +soctherm_init_sysctl(struct soctherm_softc *sc) +{ + int i; + struct sysctl_oid *oid, *tmp; + + sysctl_ctx_init(&soctherm_sysctl_ctx); + /* create node for hw.temp */ + oid = SYSCTL_ADD_NODE(&soctherm_sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "temperature", + CTLFLAG_RD, NULL, ""); + if (oid == NULL) + return (ENXIO); + + /* Add sensors */ + for (i = sc->ntsensors - 1; i >= 0; i--) { + tmp = SYSCTL_ADD_PROC(&soctherm_sysctl_ctx, + SYSCTL_CHILDREN(oid), OID_AUTO, sc->tsensors[i].name, + CTLTYPE_INT | CTLFLAG_RD, sc, i, + soctherm_sysctl_temperature, "IK", "SoC Temperature"); + if (tmp == NULL) + return (ENXIO); + } + + return (0); +} + +static int +soctherm_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "Tegra temperature sensors"); + return (BUS_PROBE_DEFAULT); +} + +static int +soctherm_attach(device_t dev) +{ + struct soctherm_softc *sc; + phandle_t node; + int i, rid, rv; + struct soctherm_shared_cal shared_calib; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(sc->dev); + + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + goto fail; + } + + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "Cannot allocate IRQ resources\n"); + goto fail; + } + +/* + if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC, + soctherm_intr, NULL, sc, &sc->irq_ih))) { + device_printf(dev, + "WARNING: unable to register interrupt handler\n"); + goto fail; + } +*/ + + /* OWF resources */ + rv = hwreset_get_by_ofw_name(dev, "soctherm", &sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot get fuse reset\n"); + goto fail; + } + rv = clk_get_by_ofw_name(dev, "tsensor", &sc->tsensor_clk); + if (rv != 0) { + device_printf(dev, "Cannot get 'tsensor' clock: %d\n", rv); + goto fail; + } + rv = clk_get_by_ofw_name(dev, "soctherm", &sc->soctherm_clk); + if (rv != 0) { + device_printf(dev, "Cannot get 'soctherm' clock: %d\n", rv); + goto fail; + } + + rv = hwreset_assert(sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot assert reset\n"); + goto fail; + } + rv = clk_enable(sc->tsensor_clk); + if (rv != 0) { + device_printf(dev, "Cannot enable 'tsensor' clock: %d\n", rv); + goto fail; + } + rv = clk_enable(sc->soctherm_clk); + if (rv != 0) { + device_printf(dev, "Cannot enable 'soctherm' clock: %d\n", rv); + goto fail; + } + rv = hwreset_deassert(sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot clear reset\n"); + goto fail; + } + + /* Tegra 124 */ + sc->tsensors = t124_tsensors; + sc->ntsensors = nitems(t124_tsensors); + get_shared_cal(sc, &shared_calib); + + WR4(sc, TSENSOR_PDIV, TSENSOR_PDIV_T124); + WR4(sc, TSENSOR_HOTSPOT_OFF, TSENSOR_HOTSPOT_OFF_T124); + + for (i = 0; i < sc->ntsensors; i++) + soctherm_init_tsensor(sc, sc->tsensors + i, &shared_calib); + + rv = soctherm_init_sysctl(sc); + if (rv != 0) { + device_printf(sc->dev, "Cannot initialize sysctls\n"); + goto fail; + } + + OF_device_register_xref(OF_xref_from_node(node), dev); + return (bus_generic_attach(dev)); + +fail: + if (sc->irq_ih != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); + sysctl_ctx_free(&soctherm_sysctl_ctx); + if (sc->tsensor_clk != NULL) + clk_release(sc->tsensor_clk); + if (sc->soctherm_clk != NULL) + clk_release(sc->soctherm_clk); + if (sc->reset != NULL) + hwreset_release(sc->reset); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + return (ENXIO); +} + +static int +soctherm_detach(device_t dev) +{ + struct soctherm_softc *sc; + sc = device_get_softc(dev); + + if (sc->irq_ih != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); + sysctl_ctx_free(&soctherm_sysctl_ctx); + if (sc->tsensor_clk != NULL) + clk_release(sc->tsensor_clk); + if (sc->soctherm_clk != NULL) + clk_release(sc->soctherm_clk); + if (sc->reset != NULL) + hwreset_release(sc->reset); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + return (ENXIO); +} + +static device_method_t tegra_soctherm_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, soctherm_probe), + DEVMETHOD(device_attach, soctherm_attach), + DEVMETHOD(device_detach, soctherm_detach), + + /* SOCTHERM interface */ + DEVMETHOD(tegra_soctherm_get_temperature, soctherm_get_temp), + + DEVMETHOD_END +}; + +static devclass_t tegra_soctherm_devclass; +DEFINE_CLASS_0(tegra_soctherm, tegra_soctherm_driver, tegra_soctherm_methods, + sizeof(struct soctherm_softc)); +EARLY_DRIVER_MODULE(tegra_soctherm, simplebus, tegra_soctherm_driver, +tegra_soctherm_devclass, 0, 0, 79); diff --git a/sys/arm/nvidia/tegra_soctherm_if.m b/sys/arm/nvidia/tegra_soctherm_if.m new file mode 100644 index 0000000..55ae044 --- /dev/null +++ b/sys/arm/nvidia/tegra_soctherm_if.m @@ -0,0 +1,42 @@ +#- +# Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> +# 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. +# +# $FreeBSD$ +# + +#include <machine/bus.h> + +INTERFACE tegra_soctherm; + + +/** + * Read temperature + */ +METHOD int get_temperature{ + device_t dev; + device_t consumer; + uintptr_t id; + int *val; +}; diff --git a/sys/arm/nvidia/tegra_uart.c b/sys/arm/nvidia/tegra_uart.c new file mode 100644 index 0000000..86972f0 --- /dev/null +++ b/sys/arm/nvidia/tegra_uart.c @@ -0,0 +1,251 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + + +/* + * UART driver for Tegra SoCs. + */ +#include "opt_platform.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/sysctl.h> +#include <machine/bus.h> + +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/uart/uart.h> +#include <dev/uart/uart_cpu.h> +#include <dev/uart/uart_cpu_fdt.h> +#include <dev/uart/uart_bus.h> +#include <dev/uart/uart_dev_ns8250.h> +#include <dev/ic/ns16550.h> + +#include "uart_if.h" + +/* + * High-level UART interface. + */ +struct tegra_softc { + struct ns8250_softc ns8250_base; + clk_t clk; + hwreset_t reset; +}; + +/* + * UART class interface. + */ +static int +tegra_uart_attach(struct uart_softc *sc) +{ + int rv; + struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; + struct uart_bas *bas = &sc->sc_bas; + + rv = ns8250_bus_attach(sc); + if (rv != 0) + return (rv); + + ns8250->ier_rxbits = 0x1d; + ns8250->ier_mask = 0xc0; + ns8250->ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask; + ns8250->ier |= ns8250->ier_rxbits; + uart_setreg(bas, REG_IER, ns8250->ier); + uart_barrier(bas); + return (0); +} + +static void +tegra_uart_grab(struct uart_softc *sc) +{ + struct uart_bas *bas = &sc->sc_bas; + struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; + u_char ier; + + /* + * turn off all interrupts to enter polling mode. Leave the + * saved mask alone. We'll restore whatever it was in ungrab. + * All pending interrupt signals are reset when IER is set to 0. + */ + uart_lock(sc->sc_hwmtx); + ier = uart_getreg(bas, REG_IER); + uart_setreg(bas, REG_IER, ier & ns8250->ier_mask); + uart_setreg(bas, REG_FCR, 0); + uart_barrier(bas); + uart_unlock(sc->sc_hwmtx); +} + +static void +tegra_uart_ungrab(struct uart_softc *sc) +{ + struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; + struct uart_bas *bas = &sc->sc_bas; + + /* + * Restore previous interrupt mask + */ + uart_lock(sc->sc_hwmtx); + uart_setreg(bas, REG_FCR, ns8250->fcr); + uart_setreg(bas, REG_IER, ns8250->ier); + uart_barrier(bas); + uart_unlock(sc->sc_hwmtx); +} + +static kobj_method_t tegra_methods[] = { + KOBJMETHOD(uart_probe, ns8250_bus_probe), + KOBJMETHOD(uart_attach, tegra_uart_attach), + KOBJMETHOD(uart_detach, ns8250_bus_detach), + KOBJMETHOD(uart_flush, ns8250_bus_flush), + KOBJMETHOD(uart_getsig, ns8250_bus_getsig), + KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl), + KOBJMETHOD(uart_ipend, ns8250_bus_ipend), + KOBJMETHOD(uart_param, ns8250_bus_param), + KOBJMETHOD(uart_receive, ns8250_bus_receive), + KOBJMETHOD(uart_setsig, ns8250_bus_setsig), + KOBJMETHOD(uart_transmit, ns8250_bus_transmit), + KOBJMETHOD(uart_grab, tegra_uart_grab), + KOBJMETHOD(uart_ungrab, tegra_uart_ungrab), + KOBJMETHOD_END +}; + +static struct uart_class tegra_uart_class = { + "tegra class", + tegra_methods, + sizeof(struct tegra_softc), + .uc_ops = &uart_ns8250_ops, + .uc_range = 8, + .uc_rclk = 0, +}; + +/* Compatible devices. */ +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-uart", (uintptr_t)&tegra_uart_class}, + {NULL, (uintptr_t)NULL}, +}; + +UART_FDT_CLASS(compat_data); + +/* + * UART Driver interface. + */ +static int +uart_fdt_get_shift1(phandle_t node) +{ + pcell_t shift; + + if ((OF_getencprop(node, "reg-shift", &shift, sizeof(shift))) <= 0) + shift = 2; + return ((int)shift); +} + +static int +tegra_uart_probe(device_t dev) +{ + struct tegra_softc *sc; + phandle_t node; + uint64_t freq; + int shift; + int rv; + const struct ofw_compat_data *cd; + + sc = device_get_softc(dev); + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + cd = ofw_bus_search_compatible(dev, compat_data); + if (cd->ocd_data == 0) + return (ENXIO); + sc->ns8250_base.base.sc_class = (struct uart_class *)cd->ocd_data; + + rv = hwreset_get_by_ofw_name(dev, "serial", &sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot get 'serial' reset\n"); + return (ENXIO); + } + rv = hwreset_deassert(sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot unreset 'serial' reset\n"); + return (ENXIO); + } + + node = ofw_bus_get_node(dev); + shift = uart_fdt_get_shift1(node); + rv = clk_get_by_ofw_index(dev, 0, &sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot get UART clock: %d\n", rv); + return (ENXIO); + } + rv = clk_enable(sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot enable UART clock: %d\n", rv); + return (ENXIO); + } + rv = clk_get_freq(sc->clk, &freq); + if (rv != 0) { + device_printf(dev, "Cannot enable UART clock: %d\n", rv); + return (ENXIO); + } + return (uart_bus_probe(dev, shift, (int)freq, 0, 0)); +} + +static int +tegra_uart_detach(device_t dev) +{ + struct tegra_softc *sc; + + sc = device_get_softc(dev); + if (sc->clk != NULL) { + clk_release(sc->clk); + } + + return (uart_bus_detach(dev)); +} + +static device_method_t tegra_uart_bus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_uart_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, tegra_uart_detach), + { 0, 0 } +}; + +static driver_t tegra_uart_driver = { + uart_driver_name, + tegra_uart_bus_methods, + sizeof(struct tegra_softc), +}; + +DRIVER_MODULE(tegra_uart, simplebus, tegra_uart_driver, uart_devclass, + 0, 0);
\ No newline at end of file diff --git a/sys/arm/nvidia/tegra_usbphy.c b/sys/arm/nvidia/tegra_usbphy.c new file mode 100644 index 0000000..40c0714 --- /dev/null +++ b/sys/arm/nvidia/tegra_usbphy.c @@ -0,0 +1,839 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + + +/* + * USB phy driver for Tegra SoCs. + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/malloc.h> +#include <sys/rman.h> + +#include <machine/bus.h> +#include <machine/fdt.h> + +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> +#include <dev/extres/phy/phy.h> +#include <dev/extres/regulator/regulator.h> +#include <dev/fdt/fdt_common.h> +#include <dev/fdt/fdt_pinctrl.h> +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include "phy_if.h" + +#define CTRL_ICUSB_CTRL 0x15c +#define ICUSB_CTR_IC_ENB1 (1 << 3) + +#define CTRL_USB_USBMODE 0x1f8 +#define USB_USBMODE_MASK (3 << 0) +#define USB_USBMODE_HOST (3 << 0) +#define USB_USBMODE_DEVICE (2 << 0) + +#define CTRL_USB_HOSTPC1_DEVLC 0x1b4 +#define USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) +#define USB_HOSTPC1_DEVLC_STS (1 << 28) +#define USB_HOSTPC1_DEVLC_PHCD (1 << 22) + + +#define IF_USB_SUSP_CTRL 0x400 +#define FAST_WAKEUP_RESP (1 << 26) +#define UTMIP_SUSPL1_SET (1 << 25) +#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16) +#define USB_SUSP_SET (1 << 14) +#define UTMIP_PHY_ENB (1 << 12) +#define UTMIP_RESET (1 << 11) +#define USB_SUSP_POL (1 << 10) +#define USB_PHY_CLK_VALID_INT_ENB (1 << 9) +#define USB_PHY_CLK_VALID_INT_STS (1 << 8) +#define USB_PHY_CLK_VALID (1 << 7) +#define USB_CLKEN (1 << 6) +#define USB_SUSP_CLR (1 << 5) +#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4) +#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3) +#define USB_WAKE_ON_RESUME_EN (1 << 2) +#define USB_WAKEUP_INT_ENB (1 << 1) +#define USB_WAKEUP_INT_STS (1 << 0) + +#define IF_USB_PHY_VBUS_SENSORS 0x404 +#define B_SESS_END_SW_VALUE (1 << 4) +#define B_SESS_END_SW_EN (1 << 3) + + +#define UTMIP_XCVR_CFG0 0x808 +#define UTMIP_XCVR_HSSLEW_MSB(x) ((((x) & 0x1fc) >> 2) << 25) +#define UTMIP_XCVR_SETUP_MSB(x) ((((x) & 0x70) >> 4) << 22) +#define UTMIP_XCVR_LSBIAS_SEL (1 << 21) +#define UTMIP_XCVR_DISCON_METHOD (1 << 20) +#define UTMIP_FORCE_PDZI_POWERUP (1 << 19) +#define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18) +#define UTMIP_FORCE_PD2_POWERUP (1 << 17) +#define UTMIP_FORCE_PD2_POWERDOWN (1 << 16) +#define UTMIP_FORCE_PD_POWERUP (1 << 15) +#define UTMIP_FORCE_PD_POWERDOWN (1 << 14) +#define UTMIP_XCVR_TERMEN (1 << 13) +#define UTMIP_XCVR_HSLOOPBACK (1 << 12) +#define UTMIP_XCVR_LSFSLEW(x) (((x) & 0x3) << 10) +#define UTMIP_XCVR_LSRSLEW(x) (((x) & 0x3) << 8) +#define UTMIP_XCVR_FSSLEW(x) (((x) & 0x3) << 6) +#define UTMIP_XCVR_HSSLEW(x) (((x) & 0x3) << 4) +#define UTMIP_XCVR_SETUP(x) (((x) & 0xf) << 0) + +#define UTMIP_BIAS_CFG0 0x80C +#define UTMIP_IDDIG_C_VAL (1 << 30) +#define UTMIP_IDDIG_C_SEL (1 << 29) +#define UTMIP_IDDIG_B_VAL (1 << 28) +#define UTMIP_IDDIG_B_SEL (1 << 27) +#define UTMIP_IDDIG_A_VAL (1 << 26) +#define UTMIP_IDDIG_A_SEL (1 << 25) +#define UTMIP_HSDISCON_LEVEL_MSB(x) ((((x) & 0x4) >> 2) << 24) +#define UTMIP_IDPD_VAL (1 << 23) +#define UTMIP_IDPD_SEL (1 << 22) +#define UTMIP_IDDIG_VAL (1 << 21) +#define UTMIP_IDDIG_SEL (1 << 20) +#define UTMIP_GPI_VAL (1 << 19) +#define UTMIP_GPI_SEL (1 << 18) +#define UTMIP_ACTIVE_TERM_OFFSET(x) (((x) & 0x7) << 15) +#define UTMIP_ACTIVE_PULLUP_OFFSET(x) (((x) & 0x7) << 12) +#define UTMIP_OTGPD (1 << 11) +#define UTMIP_BIASPD (1 << 10) +#define UTMIP_VBUS_LEVEL_LEVEL(x) (((x) & 0x3) << 8) +#define UTMIP_SESS_LEVEL_LEVEL(x) (((x) & 0x3) << 6) +#define UTMIP_HSCHIRP_LEVEL(x) (((x) & 0x3) << 4) +#define UTMIP_HSDISCON_LEVEL(x) (((x) & 0x3) << 2) +#define UTMIP_HSSQUELCH_LEVEL(x) (((x) & 0x3) << 0) + + +#define UTMIP_HSRX_CFG0 0x810 +#define UTMIP_KEEP_PATT_ON_ACTIVE(x) (((x) & 0x3) << 30) +#define UTMIP_ALLOW_CONSEC_UPDN (1 << 29) +#define UTMIP_REALIGN_ON_NEW_PKT (1 << 28) +#define UTMIP_PCOUNT_UPDN_DIV(x) (((x) & 0xf) << 24) +#define UTMIP_SQUELCH_EOP_DLY(x) (((x) & 0x7) << 21) +#define UTMIP_NO_STRIPPING (1 << 20) +#define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15) +#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10) +#define UTMIP_ELASTIC_OVERRUN_DISABLE (1 << 9) +#define UTMIP_ELASTIC_UNDERRUN_DISABLE (1 << 8) +#define UTMIP_PASS_CHIRP (1 << 7) +#define UTMIP_PASS_FEEDBACK (1 << 6) +#define UTMIP_PCOUNT_INERTIA(x) (((x) & 0x3) << 4) +#define UTMIP_PHASE_ADJUST(x) (((x) & 0x3) << 2) +#define UTMIP_THREE_SYNCBITS (1 << 1) +#define UTMIP_USE4SYNC_TRAN (1 << 0) + +#define UTMIP_HSRX_CFG1 0x814 +#define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1F) << 1) +#define UTMIP_HS_ALLOW_KEEP_ALIVE (1 << 0) + +#define UTMIP_TX_CFG0 0x820 +#define UTMIP_FS_PREAMBLE_J (1 << 19) +#define UTMIP_FS_POSTAMBLE_OUTPUT_ENABLE (1 << 18) +#define UTMIP_FS_PREAMBLE_OUTPUT_ENABLE (1 << 17) +#define UTMIP_FSLS_ALLOW_SOP_TX_STUFF_ERR (1 << 16) +#define UTMIP_HS_READY_WAIT_FOR_VALID (1 << 15) +#define UTMIP_HS_TX_IPG_DLY(x) (((x) & 0x1f) << 10) +#define UTMIP_HS_DISCON_EOP_ONLY (1 << 9) +#define UTMIP_HS_DISCON_DISABLE (1 << 8) +#define UTMIP_HS_POSTAMBLE_OUTPUT_ENABLE (1 << 7) +#define UTMIP_HS_PREAMBLE_OUTPUT_ENABLE (1 << 6) +#define UTMIP_SIE_RESUME_ON_LINESTATE (1 << 5) +#define UTMIP_SOF_ON_NO_STUFF (1 << 4) +#define UTMIP_SOF_ON_NO_ENCODE (1 << 3) +#define UTMIP_NO_STUFFING (1 << 2) +#define UTMIP_NO_ENCODING (1 << 1) +#define UTMIP_NO_SYNC_NO_EOP (1 << 0) + +#define UTMIP_MISC_CFG0 0x824 +#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27) +#define UTMIP_DPDM_OBSERVE (1 << 26) +#define UTMIP_KEEP_XCVR_PD_ON_SOFT_DISCON (1 << 25) +#define UTMIP_ALLOW_LS_ON_SOFT_DISCON (1 << 24) +#define UTMIP_FORCE_FS_DISABLE_ON_DEV_CHIRP (1 << 23) +#define UTMIP_SUSPEND_EXIT_ON_EDGE (1 << 22) +#define UTMIP_LS_TO_FS_SKIP_4MS (1 << 21) +#define UTMIP_INJECT_ERROR_TYPE(x) (((x) & 0x3) << 19) +#define UTMIP_FORCE_HS_CLOCK_ON (1 << 18) +#define UTMIP_DISABLE_HS_TERM (1 << 17) +#define UTMIP_FORCE_HS_TERM (1 << 16) +#define UTMIP_DISABLE_PULLUP_DP (1 << 15) +#define UTMIP_DISABLE_PULLUP_DM (1 << 14) +#define UTMIP_DISABLE_PULLDN_DP (1 << 13) +#define UTMIP_DISABLE_PULLDN_DM (1 << 12) +#define UTMIP_FORCE_PULLUP_DP (1 << 11) +#define UTMIP_FORCE_PULLUP_DM (1 << 10) +#define UTMIP_FORCE_PULLDN_DP (1 << 9) +#define UTMIP_FORCE_PULLDN_DM (1 << 8) +#define UTMIP_STABLE_COUNT(x) (((x) & 0x7) << 5) +#define UTMIP_STABLE_ALL (1 << 4) +#define UTMIP_NO_FREE_ON_SUSPEND (1 << 3) +#define UTMIP_NEVER_FREE_RUNNING_TERMS (1 << 2) +#define UTMIP_ALWAYS_FREE_RUNNING_TERMS (1 << 1) +#define UTMIP_COMB_TERMS (1 << 0) + +#define UTMIP_MISC_CFG1 0x828 +#define UTMIP_PHY_XTAL_CLOCKEN (1 << 30) + +#define UTMIP_DEBOUNCE_CFG0 0x82C +#define UTMIP_BIAS_DEBOUNCE_B(x) (((x) & 0xffff) << 16) +#define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0) + +#define UTMIP_BAT_CHRG_CFG0 0x830 +#define UTMIP_CHRG_DEBOUNCE_TIMESCALE(x) (((x) & 0x1f) << 8) +#define UTMIP_OP_I_SRC_ENG (1 << 5) +#define UTMIP_ON_SRC_ENG (1 << 4) +#define UTMIP_OP_SRC_ENG (1 << 3) +#define UTMIP_ON_SINK_ENG (1 << 2) +#define UTMIP_OP_SINK_ENG (1 << 1) +#define UTMIP_PD_CHRG (1 << 0) + +#define UTMIP_SPARE_CFG0 0x834 +#define FUSE_HS_IREF_CAP_CFG (1 << 7) +#define FUSE_HS_SQUELCH_LEVEL (1 << 6) +#define FUSE_SPARE (1 << 5) +#define FUSE_TERM_RANGE_ADJ_SEL (1 << 4) +#define FUSE_SETUP_SEL (1 << 3) +#define HS_RX_LATE_SQUELCH (1 << 2) +#define HS_RX_FLUSH_ALAP (1 << 1) +#define HS_RX_IPG_ERROR_ENABLE (1 << 0) + +#define UTMIP_XCVR_CFG1 0x838 +#define UTMIP_XCVR_RPU_RANGE_ADJ(x) (((x) & 0x3) << 26) +#define UTMIP_XCVR_HS_IREF_CAP(x) (((x) & 0x3) << 24) +#define UTMIP_XCVR_SPARE(x) (((x) & 0x3) << 22) +#define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18) +#define UTMIP_RCTRL_SW_SET (1 << 17) +#define UTMIP_RCTRL_SW_VAL(x) (((x) & 0x1f) << 12) +#define UTMIP_TCTRL_SW_SET (1 << 11) +#define UTMIP_TCTRL_SW_VAL(x) (((x) & 0x1f) << 6) +#define UTMIP_FORCE_PDDR_POWERUP (1 << 5) +#define UTMIP_FORCE_PDDR_POWERDOWN (1 << 4) +#define UTMIP_FORCE_PDCHRP_POWERUP (1 << 3) +#define UTMIP_FORCE_PDCHRP_POWERDOWN (1 << 2) +#define UTMIP_FORCE_PDDISC_POWERUP (1 << 1) +#define UTMIP_FORCE_PDDISC_POWERDOWN (1 << 0) + +#define UTMIP_BIAS_CFG1 0x83c +#define UTMIP_BIAS_DEBOUNCE_TIMESCALE(x) (((x) & 0x3f) << 8) +#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3) +#define UTMIP_VBUS_WAKEUP_POWERDOWN (1 << 2) +#define UTMIP_FORCE_PDTRK_POWERUP (1 << 1) +#define UTMIP_FORCE_PDTRK_POWERDOWN (1 << 0) + +static int usbpby_enable_cnt; + +enum usb_ifc_type { + USB_IFC_TYPE_UNKNOWN = 0, + USB_IFC_TYPE_UTMI, + USB_IFC_TYPE_ULPI +}; + +enum usb_dr_mode { + USB_DR_MODE_UNKNOWN = 0, + USB_DR_MODE_DEVICE, + USB_DR_MODE_HOST, + USB_DR_MODE_OTG +}; + +struct usbphy_softc { + device_t dev; + struct resource *mem_res; + struct resource *pads_res; + clk_t clk_reg; + clk_t clk_pads; + clk_t clk_pllu; + regulator_t supply_vbus; + hwreset_t reset_usb; + hwreset_t reset_pads; + enum usb_ifc_type ifc_type; + enum usb_dr_mode dr_mode; + bool have_utmi_regs; + + /* UTMI params */ + int hssync_start_delay; + int elastic_limit; + int idle_wait_delay; + int term_range_adj; + int xcvr_lsfslew; + int xcvr_lsrslew; + int xcvr_hsslew; + int hssquelch_level; + int hsdiscon_level; + int xcvr_setup; + int xcvr_setup_use_fuses; +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra30-usb-phy", 1}, + {NULL, 0}, +}; + +#define RD4(sc, offs) \ + bus_read_4(sc->mem_res, offs) + +#define WR4(sc, offs, val) \ + bus_write_4(sc->mem_res, offs, val) + +static int +reg_wait(struct usbphy_softc *sc, uint32_t reg, uint32_t mask, uint32_t val) +{ + int i; + + for (i = 0; i < 1000; i++) { + if ((RD4(sc, reg) & mask) == val) + return (0); + DELAY(10); + } + return (ETIMEDOUT); +} + +static int +usbphy_utmi_phy_clk(struct usbphy_softc *sc, bool enable) +{ + uint32_t val; + int rv; + + val = RD4(sc, CTRL_USB_HOSTPC1_DEVLC); + if (enable) + val &= ~USB_HOSTPC1_DEVLC_PHCD; + else + val |= USB_HOSTPC1_DEVLC_PHCD; + WR4(sc, CTRL_USB_HOSTPC1_DEVLC, val); + + rv = reg_wait(sc, IF_USB_SUSP_CTRL, USB_PHY_CLK_VALID, + enable ? USB_PHY_CLK_VALID: 0); + if (rv != 0) { + device_printf(sc->dev, "USB phy clock timeout.\n"); + return (ETIMEDOUT); + } + return (0); +} + +static int +usbphy_utmi_enable(struct usbphy_softc *sc) +{ + int rv; + uint32_t val; + + /* Reset phy */ + val = RD4(sc, IF_USB_SUSP_CTRL); + val |= UTMIP_RESET; + WR4(sc, IF_USB_SUSP_CTRL, val); + + + val = RD4(sc, UTMIP_TX_CFG0); + val |= UTMIP_FS_PREAMBLE_J; + WR4(sc, UTMIP_TX_CFG0, val); + + val = RD4(sc, UTMIP_HSRX_CFG0); + val &= ~UTMIP_IDLE_WAIT(~0); + val &= ~UTMIP_ELASTIC_LIMIT(~0); + val |= UTMIP_IDLE_WAIT(sc->idle_wait_delay); + val |= UTMIP_ELASTIC_LIMIT(sc->elastic_limit); + WR4(sc, UTMIP_HSRX_CFG0, val); + + val = RD4(sc, UTMIP_HSRX_CFG1); + val &= ~UTMIP_HS_SYNC_START_DLY(~0); + val |= UTMIP_HS_SYNC_START_DLY(sc->hssync_start_delay); + WR4(sc, UTMIP_HSRX_CFG1, val); + + val = RD4(sc, UTMIP_DEBOUNCE_CFG0); + val &= ~UTMIP_BIAS_DEBOUNCE_A(~0); + val |= UTMIP_BIAS_DEBOUNCE_A(0x7530); /* For 12MHz */ + WR4(sc, UTMIP_DEBOUNCE_CFG0, val); + + val = RD4(sc, UTMIP_MISC_CFG0); + val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE; + WR4(sc, UTMIP_MISC_CFG0, val); + + if (sc->dr_mode == USB_DR_MODE_DEVICE) { + val = RD4(sc,IF_USB_SUSP_CTRL); + val &= ~USB_WAKE_ON_CNNT_EN_DEV; + val &= ~USB_WAKE_ON_DISCON_EN_DEV; + WR4(sc, IF_USB_SUSP_CTRL, val); + + val = RD4(sc, UTMIP_BAT_CHRG_CFG0); + val &= ~UTMIP_PD_CHRG; + WR4(sc, UTMIP_BAT_CHRG_CFG0, val); + } else { + val = RD4(sc, UTMIP_BAT_CHRG_CFG0); + val |= UTMIP_PD_CHRG; + WR4(sc, UTMIP_BAT_CHRG_CFG0, val); + } + + usbpby_enable_cnt++; + if (usbpby_enable_cnt == 1) { + rv = hwreset_deassert(sc->reset_pads); + if (rv != 0) { + device_printf(sc->dev, + "Cannot unreset 'utmi-pads' reset\n"); + return (rv); + } + rv = clk_enable(sc->clk_pads); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'utmi-pads' clock\n"); + return (rv); + } + + val = bus_read_4(sc->pads_res, UTMIP_BIAS_CFG0); + val &= ~UTMIP_OTGPD; + val &= ~UTMIP_BIASPD; + val &= ~UTMIP_HSSQUELCH_LEVEL(~0); + val &= ~UTMIP_HSDISCON_LEVEL(~0); + val &= ~UTMIP_HSDISCON_LEVEL_MSB(~0); + val |= UTMIP_HSSQUELCH_LEVEL(sc->hssquelch_level); + val |= UTMIP_HSDISCON_LEVEL(sc->hsdiscon_level); + val |= UTMIP_HSDISCON_LEVEL_MSB(sc->hsdiscon_level); + bus_write_4(sc->pads_res, UTMIP_BIAS_CFG0, val); + + rv = clk_disable(sc->clk_pads); + if (rv != 0) { + device_printf(sc->dev, + "Cannot disable 'utmi-pads' clock\n"); + return (rv); + } + } + + val = RD4(sc, UTMIP_XCVR_CFG0); + val &= ~UTMIP_FORCE_PD_POWERDOWN; + val &= ~UTMIP_FORCE_PD2_POWERDOWN ; + val &= ~UTMIP_FORCE_PDZI_POWERDOWN; + val &= ~UTMIP_XCVR_LSBIAS_SEL; + val &= ~UTMIP_XCVR_LSFSLEW(~0); + val &= ~UTMIP_XCVR_LSRSLEW(~0); + val &= ~UTMIP_XCVR_HSSLEW(~0); + val &= ~UTMIP_XCVR_HSSLEW_MSB(~0); + val |= UTMIP_XCVR_LSFSLEW(sc->xcvr_lsfslew); + val |= UTMIP_XCVR_LSRSLEW(sc->xcvr_lsrslew); + val |= UTMIP_XCVR_HSSLEW(sc->xcvr_hsslew); + val |= UTMIP_XCVR_HSSLEW_MSB(sc->xcvr_hsslew); + if (!sc->xcvr_setup_use_fuses) { + val &= ~UTMIP_XCVR_SETUP(~0); + val &= ~UTMIP_XCVR_SETUP_MSB(~0); + val |= UTMIP_XCVR_SETUP(sc->xcvr_setup); + val |= UTMIP_XCVR_SETUP_MSB(sc->xcvr_setup); + } + WR4(sc, UTMIP_XCVR_CFG0, val); + + val = RD4(sc, UTMIP_XCVR_CFG1); + val &= ~UTMIP_FORCE_PDDISC_POWERDOWN; + val &= ~UTMIP_FORCE_PDCHRP_POWERDOWN; + val &= ~UTMIP_FORCE_PDDR_POWERDOWN; + val &= ~UTMIP_XCVR_TERM_RANGE_ADJ(~0); + val |= UTMIP_XCVR_TERM_RANGE_ADJ(sc->term_range_adj); + WR4(sc, UTMIP_XCVR_CFG1, val); + + + val = RD4(sc, UTMIP_BIAS_CFG1); + val &= ~UTMIP_BIAS_PDTRK_COUNT(~0); + val |= UTMIP_BIAS_PDTRK_COUNT(0x5); + WR4(sc, UTMIP_BIAS_CFG1, val); + + val = RD4(sc, UTMIP_SPARE_CFG0); + if (sc->xcvr_setup_use_fuses) + val |= FUSE_SETUP_SEL; + else + val &= ~FUSE_SETUP_SEL; + WR4(sc, UTMIP_SPARE_CFG0, val); + + val = RD4(sc, IF_USB_SUSP_CTRL); + val |= UTMIP_PHY_ENB; + WR4(sc, IF_USB_SUSP_CTRL, val); + + val = RD4(sc, IF_USB_SUSP_CTRL); + val &= ~UTMIP_RESET; + WR4(sc, IF_USB_SUSP_CTRL, val); + + usbphy_utmi_phy_clk(sc, true); + + val = RD4(sc, CTRL_USB_USBMODE); + val &= ~USB_USBMODE_MASK; + if (sc->dr_mode == USB_DR_MODE_HOST) + val |= USB_USBMODE_HOST; + else + val |= USB_USBMODE_DEVICE; + WR4(sc, CTRL_USB_USBMODE, val); + + val = RD4(sc, CTRL_USB_HOSTPC1_DEVLC); + val &= ~USB_HOSTPC1_DEVLC_PTS(~0); + val |= USB_HOSTPC1_DEVLC_PTS(0); + WR4(sc, CTRL_USB_HOSTPC1_DEVLC, val); + + return (0); +} + +static int +usbphy_utmi_disable(struct usbphy_softc *sc) +{ + int rv; + uint32_t val; + + usbphy_utmi_phy_clk(sc, false); + + if (sc->dr_mode == USB_DR_MODE_DEVICE) { + val = RD4(sc, IF_USB_SUSP_CTRL); + val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0); + val |= USB_WAKE_ON_CNNT_EN_DEV; + val |= USB_WAKEUP_DEBOUNCE_COUNT(5); + WR4(sc, IF_USB_SUSP_CTRL, val); + } + + val = RD4(sc, IF_USB_SUSP_CTRL); + val |= UTMIP_RESET; + WR4(sc, IF_USB_SUSP_CTRL, val); + + val = RD4(sc, UTMIP_BAT_CHRG_CFG0); + val |= UTMIP_PD_CHRG; + WR4(sc, UTMIP_BAT_CHRG_CFG0, val); + + val = RD4(sc, UTMIP_XCVR_CFG0); + val |= UTMIP_FORCE_PD_POWERDOWN; + val |= UTMIP_FORCE_PD2_POWERDOWN; + val |= UTMIP_FORCE_PDZI_POWERDOWN; + WR4(sc, UTMIP_XCVR_CFG0, val); + + val = RD4(sc, UTMIP_XCVR_CFG1); + val |= UTMIP_FORCE_PDDISC_POWERDOWN; + val |= UTMIP_FORCE_PDCHRP_POWERDOWN; + val |= UTMIP_FORCE_PDDR_POWERDOWN; + WR4(sc, UTMIP_XCVR_CFG1, val); + + usbpby_enable_cnt--; + if (usbpby_enable_cnt <= 0) { + rv = clk_enable(sc->clk_pads); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'utmi-pads' clock\n"); + return (rv); + } + val =bus_read_4(sc->pads_res, UTMIP_BIAS_CFG0); + val |= UTMIP_OTGPD; + val |= UTMIP_BIASPD; + bus_write_4(sc->pads_res, UTMIP_BIAS_CFG0, val); + + rv = clk_disable(sc->clk_pads); + if (rv != 0) { + device_printf(sc->dev, + "Cannot disable 'utmi-pads' clock\n"); + return (rv); + } + } + return (0); +} + +static int +usbphy_phy_enable(device_t dev, int id, bool enable) +{ + struct usbphy_softc *sc; + int rv = 0; + + sc = device_get_softc(dev); + + if (sc->ifc_type != USB_IFC_TYPE_UTMI) { + device_printf(sc->dev, + "Only UTMI interface is supported.\n"); + return (ENXIO); + } + if (enable) + rv = usbphy_utmi_enable(sc); + else + rv = usbphy_utmi_disable(sc); + + return (rv); +} + +static enum usb_ifc_type +usb_get_ifc_mode(device_t dev, phandle_t node, char *name) +{ + char *tmpstr; + int rv; + enum usb_ifc_type ret; + + rv = OF_getprop_alloc(node, name, 1, (void **)&tmpstr); + if (rv <= 0) + return (USB_IFC_TYPE_UNKNOWN); + + ret = USB_IFC_TYPE_UNKNOWN; + if (strcmp(tmpstr, "utmi") == 0) + ret = USB_IFC_TYPE_UTMI; + else if (strcmp(tmpstr, "ulpi") == 0) + ret = USB_IFC_TYPE_ULPI; + else + device_printf(dev, "Unsupported phy type: %s\n", tmpstr); + free(tmpstr, M_OFWPROP); + return (ret); +} + +static enum usb_dr_mode +usb_get_dr_mode(device_t dev, phandle_t node, char *name) +{ + char *tmpstr; + int rv; + enum usb_dr_mode ret; + + rv = OF_getprop_alloc(node, name, 1, (void **)&tmpstr); + if (rv <= 0) + return (USB_DR_MODE_UNKNOWN); + + ret = USB_DR_MODE_UNKNOWN; + if (strcmp(tmpstr, "device") == 0) + ret = USB_DR_MODE_DEVICE; + else if (strcmp(tmpstr, "host") == 0) + ret = USB_DR_MODE_HOST; + else if (strcmp(tmpstr, "otg") == 0) + ret = USB_DR_MODE_OTG; + else + device_printf(dev, "Unknown dr mode: %s\n", tmpstr); + free(tmpstr, M_OFWPROP); + return (ret); +} + +static int +usbphy_utmi_read_params(struct usbphy_softc *sc, phandle_t node) +{ + int rv; + + rv = OF_getencprop(node, "nvidia,hssync-start-delay", + &sc->hssync_start_delay, sizeof (sc->hssync_start_delay)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getencprop(node, "nvidia,elastic-limit", + &sc->elastic_limit, sizeof (sc->elastic_limit)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getencprop(node, "nvidia,idle-wait-delay", + &sc->idle_wait_delay, sizeof (sc->idle_wait_delay)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getencprop(node, "nvidia,term-range-adj", + &sc->term_range_adj, sizeof (sc->term_range_adj)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getencprop(node, "nvidia,xcvr-lsfslew", + &sc->xcvr_lsfslew, sizeof (sc->xcvr_lsfslew)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getencprop(node, "nvidia,xcvr-lsrslew", + &sc->xcvr_lsrslew, sizeof (sc->xcvr_lsrslew)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getencprop(node, "nvidia,xcvr-hsslew", + &sc->xcvr_hsslew, sizeof (sc->xcvr_hsslew)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getencprop(node, "nvidia,hssquelch-level", + &sc->hssquelch_level, sizeof (sc->hssquelch_level)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getencprop(node, "nvidia,hsdiscon-level", + &sc->hsdiscon_level, sizeof (sc->hsdiscon_level)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getproplen(node, "nvidia,xcvr-setup-use-fuses"); + if (rv >= 1) { + sc->xcvr_setup_use_fuses = 1; + } else { + rv = OF_getencprop(node, "nvidia,xcvr-setup", + &sc->xcvr_setup, sizeof (sc->xcvr_setup)); + if (rv <= 0) + return (ENXIO); + } + + return (0); +} + +static int +usbphy_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + + device_set_desc(dev, "Tegra USB phy"); + return (BUS_PROBE_DEFAULT); +} + +static int +usbphy_attach(device_t dev) +{ + struct usbphy_softc * sc; + int rid, rv; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + rid = 1; + sc->pads_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + node = ofw_bus_get_node(dev); + + rv = hwreset_get_by_ofw_name(sc->dev, "usb", &sc->reset_usb); + if (rv != 0) { + device_printf(dev, "Cannot get 'usb' reset\n"); + return (ENXIO); + } + rv = hwreset_get_by_ofw_name(sc->dev, "utmi-pads", &sc->reset_pads); + if (rv != 0) { + device_printf(dev, "Cannot get 'utmi-pads' reset\n"); + return (ENXIO); + } + + rv = clk_get_by_ofw_name(sc->dev, "reg", &sc->clk_reg); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'reg' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, "pll_u", &sc->clk_pllu); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pll_u' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, "utmi-pads", &sc->clk_pads); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'utmi-pads' clock\n"); + return (ENXIO); + } + + rv = hwreset_deassert(sc->reset_usb); + if (rv != 0) { + device_printf(dev, "Cannot unreset 'usb' reset\n"); + return (ENXIO); + } + + rv = clk_enable(sc->clk_pllu); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'pllu' clock\n"); + return (ENXIO); + } + rv = clk_enable(sc->clk_reg); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'reg' clock\n"); + return (ENXIO); + } + if (OF_hasprop(node, "nvidia,has-utmi-pad-registers")) + sc->have_utmi_regs = true; + + sc->dr_mode = usb_get_dr_mode(dev, node, "dr_mode"); + if (sc->dr_mode == USB_DR_MODE_UNKNOWN) + sc->dr_mode = USB_DR_MODE_HOST; + + sc->ifc_type = usb_get_ifc_mode(dev, node, "phy_type"); + + /* We supports only utmi phy mode for now .... */ + if (sc->ifc_type != USB_IFC_TYPE_UTMI) { + device_printf(dev, "Unsupported phy type\n"); + return (ENXIO); + } + rv = usbphy_utmi_read_params(sc, node); + if (rv < 0) + return rv; + + if (OF_hasprop(node, "vbus-supply")) { + rv = regulator_get_by_ofw_property(sc->dev, "vbus-supply", + &sc->supply_vbus); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get \"vbus\" regulator\n"); + return (ENXIO); + } + rv = regulator_enable(sc->supply_vbus); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable \"vbus\" regulator\n"); + return (rv); + } + } + + phy_register_provider(dev); + return (0); +} + +static int +usbphy_detach(device_t dev) +{ + + /* This device is always present. */ + return (EBUSY); +} + +static device_method_t tegra_usbphy_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, usbphy_probe), + DEVMETHOD(device_attach, usbphy_attach), + DEVMETHOD(device_detach, usbphy_detach), + + /* phy interface */ + DEVMETHOD(phy_enable, usbphy_phy_enable), + + DEVMETHOD_END +}; + +static driver_t tegra_usbphy_driver = { + "tegra_usbphy", + tegra_usbphy_methods, + sizeof(struct usbphy_softc), +}; + +static devclass_t tegra_usbphy_devclass; + +EARLY_DRIVER_MODULE(tegra_usbphy, simplebus, tegra_usbphy_driver, + tegra_usbphy_devclass, 0, 0, 79); diff --git a/sys/arm/ti/aintc.c b/sys/arm/ti/aintc.c index 19f4544..9a0a313 100644 --- a/sys/arm/ti/aintc.c +++ b/sys/arm/ti/aintc.c @@ -30,12 +30,15 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/bus.h> #include <sys/kernel.h> #include <sys/ktr.h> #include <sys/module.h> +#include <sys/proc.h> #include <sys/rman.h> #include <machine/bus.h> #include <machine/intr.h> @@ -45,6 +48,10 @@ __FBSDID("$FreeBSD$"); #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> +#ifdef ARM_INTRNG +#include "pic_if.h" +#endif + #define INTC_REVISION 0x00 #define INTC_SYSCONFIG 0x10 #define INTC_SYSSTATUS 0x14 @@ -56,12 +63,27 @@ __FBSDID("$FreeBSD$"); #define INTC_ISR_SET(x) (0x90 + ((x) * 0x20)) #define INTC_ISR_CLEAR(x) (0x94 + ((x) * 0x20)) +#define INTC_SIR_SPURIOUS_MASK 0xffffff80 +#define INTS_SIR_ACTIVE_MASK 0x7f + +#define INTC_NIRQS 128 + +#ifdef ARM_INTRNG +struct ti_aintc_irqsrc { + struct intr_irqsrc tai_isrc; + u_int tai_irq; +}; +#endif + struct ti_aintc_softc { device_t sc_dev; struct resource * aintc_res[3]; bus_space_tag_t aintc_bst; bus_space_handle_t aintc_bsh; uint8_t ver; +#ifdef ARM_INTRNG + struct ti_aintc_irqsrc aintc_isrcs[INTC_NIRQS]; +#endif }; static struct resource_spec ti_aintc_spec[] = { @@ -83,6 +105,141 @@ static struct ofw_compat_data compat_data[] = { {NULL, 0}, }; +#ifdef ARM_INTRNG +static inline void +ti_aintc_irq_eoi(struct ti_aintc_softc *sc) +{ + + aintc_write_4(sc, INTC_CONTROL, 1); +} + +static inline void +ti_aintc_irq_mask(struct ti_aintc_softc *sc, u_int irq) +{ + + aintc_write_4(sc, INTC_MIR_SET(irq >> 5), (1UL << (irq & 0x1F))); +} + +static inline void +ti_aintc_irq_unmask(struct ti_aintc_softc *sc, u_int irq) +{ + + aintc_write_4(sc, INTC_MIR_CLEAR(irq >> 5), (1UL << (irq & 0x1F))); +} + +static int +ti_aintc_intr(void *arg) +{ + uint32_t irq; + struct ti_aintc_softc *sc = arg; + + /* Get active interrupt */ + irq = aintc_read_4(sc, INTC_SIR_IRQ); + if ((irq & INTC_SIR_SPURIOUS_MASK) != 0) { + device_printf(sc->sc_dev, + "Spurious interrupt detected (0x%08x)\n", irq); + ti_aintc_irq_eoi(sc); + return (FILTER_HANDLED); + } + + /* Only level-sensitive interrupts detection is supported. */ + irq &= INTS_SIR_ACTIVE_MASK; + if (intr_isrc_dispatch(&sc->aintc_isrcs[irq].tai_isrc, + curthread->td_intr_frame) != 0) { + ti_aintc_irq_mask(sc, irq); + ti_aintc_irq_eoi(sc); + device_printf(sc->sc_dev, "Stray irq %u disabled\n", irq); + } + + arm_irq_memory_barrier(irq); /* XXX */ + return (FILTER_HANDLED); +} + +static void +ti_aintc_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + u_int irq = ((struct ti_aintc_irqsrc *)isrc)->tai_irq; + struct ti_aintc_softc *sc = device_get_softc(dev); + + arm_irq_memory_barrier(irq); + ti_aintc_irq_unmask(sc, irq); +} + +static void +ti_aintc_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + u_int irq = ((struct ti_aintc_irqsrc *)isrc)->tai_irq; + struct ti_aintc_softc *sc = device_get_softc(dev); + + ti_aintc_irq_mask(sc, irq); +} + +static int +ti_aintc_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct ti_aintc_softc *sc; + + if (data->type != INTR_MAP_DATA_FDT || data->fdt.ncells != 1 || + data->fdt.cells[0] >= INTC_NIRQS) + return (EINVAL); + + sc = device_get_softc(dev); + *isrcp = &sc->aintc_isrcs[data->fdt.cells[0]].tai_isrc; + return (0); +} + +static void +ti_aintc_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + u_int irq = ((struct ti_aintc_irqsrc *)isrc)->tai_irq; + struct ti_aintc_softc *sc = device_get_softc(dev); + + ti_aintc_irq_mask(sc, irq); + ti_aintc_irq_eoi(sc); +} + +static void +ti_aintc_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + + ti_aintc_enable_intr(dev, isrc); +} + +static void +ti_aintc_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + + ti_aintc_irq_eoi(device_get_softc(dev)); +} + +static int +ti_aintc_pic_attach(struct ti_aintc_softc *sc) +{ + int error; + uint32_t irq; + const char *name; + intptr_t xref; + + name = device_get_nameunit(sc->sc_dev); + for (irq = 0; irq < INTC_NIRQS; irq++) { + sc->aintc_isrcs[irq].tai_irq = irq; + + error = intr_isrc_register(&sc->aintc_isrcs[irq].tai_isrc, + sc->sc_dev, 0, "%s,%u", name, irq); + if (error != 0) + return (error); + } + + xref = OF_xref_from_node(ofw_bus_get_node(sc->sc_dev)); + error = intr_pic_register(sc->sc_dev, xref); + if (error != 0) + return (error); + + return (intr_pic_claim_root(sc->sc_dev, xref, ti_aintc_intr, sc, 0)); +} + +#else static void aintc_post_filter(void *arg) { @@ -90,6 +247,7 @@ aintc_post_filter(void *arg) arm_irq_memory_barrier(0); aintc_write_4(ti_aintc_sc, INTC_CONTROL, 1); /* EOI */ } +#endif static int ti_aintc_probe(device_t dev) @@ -137,14 +295,30 @@ ti_aintc_attach(device_t dev) /*Set Priority Threshold */ aintc_write_4(sc, INTC_THRESHOLD, 0xFF); +#ifndef ARM_INTRNG arm_post_filter = aintc_post_filter; - +#else + if (ti_aintc_pic_attach(sc) != 0) { + device_printf(dev, "could not attach PIC\n"); + return (ENXIO); + } +#endif return (0); } static device_method_t ti_aintc_methods[] = { DEVMETHOD(device_probe, ti_aintc_probe), DEVMETHOD(device_attach, ti_aintc_attach), + +#ifdef ARM_INTRNG + DEVMETHOD(pic_disable_intr, ti_aintc_disable_intr), + DEVMETHOD(pic_enable_intr, ti_aintc_enable_intr), + DEVMETHOD(pic_map_intr, ti_aintc_map_intr), + DEVMETHOD(pic_post_filter, ti_aintc_post_filter), + DEVMETHOD(pic_post_ithread, ti_aintc_post_ithread), + DEVMETHOD(pic_pre_ithread, ti_aintc_pre_ithread), +#endif + { 0, 0 } }; @@ -160,6 +334,7 @@ EARLY_DRIVER_MODULE(aintc, simplebus, ti_aintc_driver, ti_aintc_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); SIMPLEBUS_PNP_INFO(compat_data); +#ifndef ARM_INTRNG int arm_get_next_irq(int last_irq) { @@ -200,3 +375,4 @@ arm_unmask_irq(uintptr_t nb) arm_irq_memory_barrier(nb); aintc_write_4(sc, INTC_MIR_CLEAR(nb >> 5), (1UL << (nb & 0x1F))); } +#endif diff --git a/sys/arm/ti/am335x/am335x_prcm.c b/sys/arm/ti/am335x/am335x_prcm.c index 8a6476d..f72bb54 100644 --- a/sys/arm/ti/am335x/am335x_prcm.c +++ b/sys/arm/ti/am335x/am335x_prcm.c @@ -64,6 +64,8 @@ __FBSDID("$FreeBSD$"); #define CM_PER_MMC0_CLKCTRL (CM_PER + 0x03C) #define CM_PER_I2C2_CLKCTRL (CM_PER + 0x044) #define CM_PER_I2C1_CLKCTRL (CM_PER + 0x048) +#define CM_PER_SPI0_CLKCTRL (CM_PER + 0x04C) +#define CM_PER_SPI1_CLKCTRL (CM_PER + 0x050) #define CM_PER_UART1_CLKCTRL (CM_PER + 0x06C) #define CM_PER_UART2_CLKCTRL (CM_PER + 0x070) #define CM_PER_UART3_CLKCTRL (CM_PER + 0x074) @@ -274,6 +276,10 @@ struct ti_clock_dev ti_am335x_clk_devmap[] = { AM335X_GENERIC_CLOCK_DEV(I2C2_CLK), AM335X_GENERIC_CLOCK_DEV(I2C3_CLK), + /* McSPI we use hwmods as reference, not units in spec */ + AM335X_GENERIC_CLOCK_DEV(SPI0_CLK), + AM335X_GENERIC_CLOCK_DEV(SPI1_CLK), + /* TSC_ADC */ AM335X_GENERIC_CLOCK_DEV(TSC_ADC_CLK), @@ -356,6 +362,10 @@ static struct am335x_clk_details g_am335x_clk_details[] = { _CLK_DETAIL(I2C2_CLK, CM_PER_I2C1_CLKCTRL, 0), _CLK_DETAIL(I2C3_CLK, CM_PER_I2C2_CLKCTRL, 0), + /* McSPI modules, hwmods start with spi0 */ + _CLK_DETAIL(SPI0_CLK, CM_PER_SPI0_CLKCTRL, 0), + _CLK_DETAIL(SPI1_CLK, CM_PER_SPI1_CLKCTRL, 0), + /* TSC_ADC module */ _CLK_DETAIL(TSC_ADC_CLK, CM_WKUP_ADC_TSC_CLKCTRL, 0), diff --git a/sys/arm/ti/cpsw/if_cpsw.c b/sys/arm/ti/cpsw/if_cpsw.c index a27393d..4486e0f 100644 --- a/sys/arm/ti/cpsw/if_cpsw.c +++ b/sys/arm/ti/cpsw/if_cpsw.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org> + * Copyright (c) 2016 Rubicon Communications, LLC (Netgate) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -93,50 +94,55 @@ __FBSDID("$FreeBSD$"); /* Device probe/attach/detach. */ static int cpsw_probe(device_t); -static void cpsw_init_slots(struct cpsw_softc *); static int cpsw_attach(device_t); -static void cpsw_free_slot(struct cpsw_softc *, struct cpsw_slot *); 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 void cpsw_init(void *); -static void cpsw_init_locked(void *); static int cpsw_shutdown(device_t); -static void cpsw_shutdown_locked(struct cpsw_softc *); +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 cpsw_ioctl(struct ifnet *, u_long command, caddr_t data); +static int cpswp_ioctl(struct ifnet *, u_long command, caddr_t data); -static int cpsw_miibus_readreg(device_t, int phy, int reg); -static int cpsw_miibus_writereg(device_t, int phy, int reg, int value); -static void cpsw_miibus_statchg(device_t); +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 cpsw_start(struct ifnet *); -static void cpsw_tx_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 cpsw_tick(void *); -static void cpsw_ifmedia_sts(struct ifnet *, struct ifmediareq *); -static int cpsw_ifmedia_upd(struct ifnet *); -static void cpsw_tx_watchdog(struct cpsw_softc *); +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 idx, uint32_t *ale_entry); -static void cpsw_ale_write_entry(struct cpsw_softc *, uint16_t idx, uint32_t *ale_entry); -static int cpsw_ale_mc_entry_set(struct cpsw_softc *, uint8_t portmap, uint8_t *mac); -static int cpsw_ale_update_addresses(struct cpsw_softc *, int purge); +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 *); @@ -148,27 +154,9 @@ static int cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS); * Packets with more segments than this will be defragmented before * they are queued. */ -#define CPSW_TXFRAGS 8 - - -/* - * TODO: The CPSW subsystem (CPSW_SS) can drive two independent PHYs - * as separate Ethernet ports. To properly support this, we should - * break this into two separate devices: a CPSW_SS device that owns - * the interrupts and actually talks to the CPSW hardware, and a - * separate CPSW Ethernet child device for each Ethernet port. The RX - * interrupt, for example, would be part of CPSW_SS; it would receive - * a packet, note the input port, and then dispatch it to the child - * device's interface queue. Similarly for transmit. - * - * It's not clear to me whether the device tree should be restructured - * with a cpsw_ss node and two child nodes. That would allow specifying - * MAC addresses for each port, for example, but might be overkill. - * - * Unfortunately, I don't have hardware right now that supports two - * Ethernet ports via CPSW. - */ +#define CPSW_TXFRAGS 16 +/* Shared resources. */ static device_method_t cpsw_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cpsw_probe), @@ -177,26 +165,49 @@ static device_method_t cpsw_methods[] = { DEVMETHOD(device_shutdown, cpsw_shutdown), DEVMETHOD(device_suspend, cpsw_suspend), DEVMETHOD(device_resume, cpsw_resume), - /* MII interface */ - DEVMETHOD(miibus_readreg, cpsw_miibus_readreg), - DEVMETHOD(miibus_writereg, cpsw_miibus_writereg), - DEVMETHOD(miibus_statchg, cpsw_miibus_statchg), - { 0, 0 } + /* OFW methods */ + DEVMETHOD(ofw_bus_get_node, cpsw_get_node), + DEVMETHOD_END }; static driver_t cpsw_driver = { - "cpsw", + "cpswss", cpsw_methods, sizeof(struct cpsw_softc), }; static devclass_t cpsw_devclass; -DRIVER_MODULE(cpsw, simplebus, cpsw_driver, cpsw_devclass, 0, 0); +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 }, @@ -206,7 +217,7 @@ static struct resource_spec irq_res_spec[] = { }; /* Number of entries here must match size of stats - * array in struct cpsw_softc. */ + * array in struct cpswp_softc. */ static struct cpsw_stat { int reg; char *oid; @@ -251,7 +262,7 @@ static struct cpsw_stat { * Basic debug support. */ -#define IF_DEBUG(sc) if (sc->cpsw_if_flags & IFF_DEBUG) +#define IF_DEBUG(_sc) if ((_sc)->if_flags & IFF_DEBUG) static void cpsw_debugf_head(const char *funcname) @@ -274,35 +285,42 @@ cpsw_debugf(const char *fmt, ...) } -#define CPSW_DEBUGF(a) do { \ - IF_DEBUG(sc) { \ - cpsw_debugf_head(__func__); \ - cpsw_debugf a; \ - } \ +#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 { \ +#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_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 { \ +#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_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) != \ +#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!"); \ } \ @@ -310,25 +328,34 @@ cpsw_debugf(const char *fmt, ...) mtx_lock(&(sc)->rx.lock); \ } while (0) -#define CPSW_GLOBAL_UNLOCK(sc) do { \ - CPSW_RX_UNLOCK(sc); \ - CPSW_TX_UNLOCK(sc); \ +#define CPSW_GLOBAL_UNLOCK(sc) do { \ + CPSW_RX_UNLOCK(sc); \ + CPSW_TX_UNLOCK(sc); \ } while (0) -#define CPSW_GLOBAL_LOCK_ASSERT(sc) do { \ +#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_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) \ +#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) @@ -336,16 +363,16 @@ cpsw_debugf(const char *fmt, ...) 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) \ +#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) \ +#define cpsw_read_cp(sc, queue) \ cpsw_read_4(sc, (queue)->hdp_offset + CP_OFFSET) -#define cpsw_write_cp(sc, queue, val) \ +#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) \ +#define cpsw_write_cp_slot(sc, queue, slot) \ cpsw_write_cp(sc, queue, cpsw_cpdma_bd_paddr(sc, slot)) #if 0 @@ -403,13 +430,12 @@ cpsw_dump_slot(struct cpsw_softc *sc, struct cpsw_slot *slot) } } -#define CPSW_DUMP_SLOT(cs, slot) do { \ +#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) { @@ -435,28 +461,6 @@ cpsw_dump_queue(struct cpsw_softc *sc, struct cpsw_slots *q) } \ } while (0) - -/* - * - * 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 void cpsw_init_slots(struct cpsw_softc *sc) { @@ -473,50 +477,6 @@ cpsw_init_slots(struct cpsw_softc *sc) } } -/* - * bind an interrupt, add the relevant info to sc->interrupts - */ -static int -cpsw_attach_interrupt(struct cpsw_softc *sc, struct resource *res, driver_intr_t *handler, const char *description) -{ - void **pcookie; - int error; - - sc->interrupts[sc->interrupt_count].res = res; - sc->interrupts[sc->interrupt_count].description = description; - pcookie = &sc->interrupts[sc->interrupt_count].ih_cookie; - - error = bus_setup_intr(sc->dev, res, INTR_TYPE_NET | INTR_MPSAFE, - NULL, *handler, sc, pcookie); - if (error) - device_printf(sc->dev, - "could not setup %s\n", description); - else - ++sc->interrupt_count; - return (error); -} - -/* - * teardown everything in sc->interrupts. - */ -static void -cpsw_detach_interrupts(struct cpsw_softc *sc) -{ - int error; - int i; - - for (i = 0; i < sizeof(sc->interrupts) / sizeof(sc->interrupts[0]); ++i) { - if (!sc->interrupts[i].ih_cookie) - continue; - error = bus_teardown_intr(sc->dev, - sc->interrupts[i].res, sc->interrupts[i].ih_cookie); - if (error) - device_printf(sc->dev, "could not release %s\n", - sc->interrupts[i].description); - sc->interrupts[i].ih_cookie = NULL; - } -} - static int cpsw_add_slots(struct cpsw_softc *sc, struct cpsw_queue *queue, int requested) { @@ -532,7 +492,7 @@ cpsw_add_slots(struct cpsw_softc *sc, struct cpsw_queue *queue, int requested) if (slot == NULL) return (0); if (bus_dmamap_create(sc->mbuf_dtag, 0, &slot->dmamap)) { - if_printf(sc->ifp, "failed to create dmamap\n"); + device_printf(sc->dev, "failed to create dmamap\n"); return (ENOMEM); } STAILQ_REMOVE_HEAD(&sc->avail, next); @@ -543,56 +503,294 @@ cpsw_add_slots(struct cpsw_softc *sc, struct cpsw_queue *queue, int requested) return (0); } -static int -cpsw_attach(device_t dev) +static void +cpsw_free_slot(struct cpsw_softc *sc, struct cpsw_slot *slot) { - bus_dma_segment_t segs[1]; - struct cpsw_softc *sc = device_get_softc(dev); - struct mii_softc *miisc; - struct ifnet *ifp; - int phy, nsegs, error; + 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; - pcell_t phy_id[3]; - u_long mem_base, mem_size; - phandle_t child; - int len; - CPSW_DEBUGF(("")); + /* Clear ALE */ + cpsw_write_4(sc, CPSW_ALE_CONTROL, CPSW_ALE_CTL_CLEAR_TBL); - getbinuptime(&sc->attach_uptime); - sc->dev = dev; - sc->node = ofw_bus_get_node(dev); + /* Enable ALE */ + reg = CPSW_ALE_CTL_ENABLE; + if (sc->dualemac) + reg |= CPSW_ALE_CTL_VLAN_AWARE; + cpsw_write_4(sc, CPSW_ALE_CONTROL, reg); - /* TODO: handle multiple slaves */ - phy = -1; + /* 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)) { - len = OF_getproplen(child, "phy_id"); - if (len <= 0) + if (OF_getprop_alloc(child, "name", 1, (void **)&name) < 0) continue; - - /* Get phy address from fdt */ - if (OF_getencprop(child, "phy_id", phy_id, len) <= 0) + 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]; + } - phy = phy_id[1]; - /* TODO: get memory window for MDIO */ + 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) { - device_printf(dev, "failed to get PHY address from FDT\n"); + 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; - mem_base = 0; - mem_size = 0; + if (OF_hasprop(sc->node, "dual_emac")) + sc->dualemac = 1; - if (fdt_regsize(sc->node, &mem_base, &mem_size) != 0) { - device_printf(sc->dev, "no regs property in cpsw node\n"); - return (ENXIO); + 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 */ @@ -610,9 +808,8 @@ cpsw_attach(device_t dev) } sc->mem_rid = 0; - sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, - &sc->mem_rid, mem_base, mem_base + CPSW_MEMWINDOW_SIZE -1, - CPSW_MEMWINDOW_SIZE, RF_ACTIVE); + 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); @@ -642,14 +839,6 @@ cpsw_attach(device_t dev) return (error); } - /* Allocate network interface */ - ifp = sc->ifp = if_alloc(IFT_ETHER); - if (ifp == NULL) { - device_printf(dev, "if_alloc() failed\n"); - cpsw_detach(dev); - return (ENOMEM); - } - /* 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); @@ -660,16 +849,6 @@ cpsw_attach(device_t dev) BUS_DMASYNC_PREWRITE); sc->null_mbuf_paddr = segs[0].ds_addr; - if_initname(ifp, device_get_name(dev), device_get_unit(dev)); - 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 = cpsw_init; - ifp->if_start = cpsw_start; - ifp->if_ioctl = cpsw_ioctl; - cpsw_init_slots(sc); /* Allocate slots to TX and RX queues. */ @@ -679,7 +858,8 @@ cpsw_attach(device_t dev) 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)) { + 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); @@ -687,127 +867,78 @@ cpsw_attach(device_t dev) device_printf(dev, "Initial queue size TX=%d RX=%d\n", sc->tx.queue_slots, sc->rx.queue_slots); - ifp->if_snd.ifq_drv_maxlen = sc->tx.queue_slots; - IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); - IFQ_SET_READY(&ifp->if_snd); - sc->tx.hdp_offset = CPSW_CPDMA_TX_HDP(0); sc->rx.hdp_offset = CPSW_CPDMA_RX_HDP(0); - /* Get high part of MAC address from control module (mac_id0_hi) */ - /* TODO: Get MAC ID1 as well as MAC ID0. */ - ti_scm_reg_read_4(0x634, ®); - sc->mac_addr[0] = reg & 0xFF; - sc->mac_addr[1] = (reg >> 8) & 0xFF; - sc->mac_addr[2] = (reg >> 16) & 0xFF; - sc->mac_addr[3] = (reg >> 24) & 0xFF; - - /* Get low part of MAC address from control module (mac_id0_lo) */ - ti_scm_reg_read_4(0x630, ®); - sc->mac_addr[4] = reg & 0xFF; - sc->mac_addr[5] = (reg >> 8) & 0xFF; - - /* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */ - /* TODO Calculate MDCLK=CLK/(CLKDIV+1) */ - cpsw_write_4(sc, MDIOCONTROL, 1 << 30 | 1 << 18 | 0xFF); - - /* Clear ALE */ - cpsw_write_4(sc, CPSW_ALE_CONTROL, 1 << 30); - - /* Attach PHY(s) */ - error = mii_attach(dev, &sc->miibus, ifp, cpsw_ifmedia_upd, - cpsw_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); - if (error) { - device_printf(dev, "attaching PHYs failed\n"); + if (cpsw_intr_attach(sc) == -1) { + device_printf(dev, "failed to setup interrupts\n"); cpsw_detach(dev); - return (error); + return (ENXIO); } - sc->mii = device_get_softc(sc->miibus); - /* Tell the MAC where to find the PHY so autoneg works */ - miisc = LIST_FIRST(&sc->mii->mii_phys); + /* Reset the controller. */ + cpsw_reset(sc); + cpsw_init(sc); - /* Select PHY and enable interrupts */ - cpsw_write_4(sc, MDIOUSERPHYSEL0, 1 << 6 | (miisc->mii_phy & 0x1F)); - - /* Note: We don't use sc->res[3] (TX interrupt) */ - if (cpsw_attach_interrupt(sc, sc->irq_res[0], - cpsw_intr_rx_thresh, "CPSW RX threshold interrupt") || - cpsw_attach_interrupt(sc, sc->irq_res[1], - cpsw_intr_rx, "CPSW RX interrupt") || - cpsw_attach_interrupt(sc, sc->irq_res[3], - cpsw_intr_misc, "CPSW misc interrupt")) { - cpsw_detach(dev); - return (ENXIO); + 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); + } } - - ether_ifattach(ifp, sc->mac_addr); - callout_init(&sc->watchdog.callout, 0); + bus_generic_attach(dev); return (0); } -static void -cpsw_free_slot(struct cpsw_softc *sc, struct cpsw_slot *slot) -{ - int error; - - if (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 int cpsw_detach(device_t dev) { - struct cpsw_softc *sc = device_get_softc(dev); + struct cpsw_softc *sc; int error, i; - CPSW_DEBUGF(("")); + 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); + } - /* Stop controller and free TX queue */ if (device_is_attached(dev)) { - ether_ifdetach(sc->ifp); - CPSW_GLOBAL_LOCK(sc); - cpsw_shutdown_locked(sc); - CPSW_GLOBAL_UNLOCK(sc); + callout_stop(&sc->watchdog.callout); callout_drain(&sc->watchdog.callout); } - bus_generic_detach(dev); - if (sc->miibus) - device_delete_child(dev, sc->miibus); - /* Stop and release all interrupts */ - cpsw_detach_interrupts(sc); + cpsw_intr_detach(sc); /* Free dmamaps and mbufs */ for (i = 0; i < sizeof(sc->_slots) / sizeof(sc->_slots[0]); ++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")); - } - if (sc->null_mbuf) m_freem(sc->null_mbuf); + } /* Free DMA tag */ - error = bus_dma_tag_destroy(sc->mbuf_dtag); - KASSERT(error == 0, ("Unable to destroy 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 */ - bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); + 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); - if (sc->ifp != NULL) - if_free(sc->ifp); - /* Destroy mutexes */ mtx_destroy(&sc->rx.lock); mtx_destroy(&sc->tx.lock); @@ -815,196 +946,249 @@ cpsw_detach(device_t dev) return (0); } -/* - * - * Init/Shutdown. - * - */ - -static void -cpsw_reset(struct cpsw_softc *sc) +static phandle_t +cpsw_get_node(device_t bus, device_t dev) { - int i; - - /* 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); - } + /* Share controller node with port device. */ + return (ofw_bus_get_node(bus)); +} - /* Reset CPSW subsystem. */ - cpsw_write_4(sc, CPSW_SS_SOFT_RESET, 1); - while (cpsw_read_4(sc, CPSW_SS_SOFT_RESET) & 1) - ; +static int +cpswp_probe(device_t dev) +{ - /* 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) - ; + if (device_get_unit(dev) > 1) { + device_printf(dev, "Only two ports are supported.\n"); + return (ENXIO); } + device_set_desc(dev, "Ethernet Switch Port"); - /* Reset DMA controller. */ - cpsw_write_4(sc, CPSW_CPDMA_SOFT_RESET, 1); - while (cpsw_read_4(sc, CPSW_CPDMA_SOFT_RESET) & 1) - ; + return (BUS_PROBE_DEFAULT); +} - /* Disable TX & RX DMA */ - cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 0); - cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 0); +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]; - /* 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); + 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; } - /* Clear all interrupt Masks */ - cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_CLEAR, 0xFFFFFFFF); - cpsw_write_4(sc, CPSW_CPDMA_TX_INTMASK_CLEAR, 0xFFFFFFFF); -} + mtx_init(&sc->lock, device_get_nameunit(dev), "cpsw port lock", + MTX_DEF); -static void -cpsw_init(void *arg) -{ - struct cpsw_softc *sc = arg; + /* Allocate network interface */ + ifp = sc->ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + cpswp_detach(dev); + return (ENXIO); + } - CPSW_DEBUGF(("")); - CPSW_GLOBAL_LOCK(sc); - cpsw_init_locked(arg); - CPSW_GLOBAL_UNLOCK(sc); -} + 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; -static void -cpsw_init_locked(void *arg) -{ - struct ifnet *ifp; - struct cpsw_softc *sc = arg; - struct cpsw_slot *slot; - uint32_t i; + ifp->if_init = cpswp_init; + ifp->if_start = cpswp_start; + ifp->if_ioctl = cpswp_ioctl; - CPSW_DEBUGF(("")); - ifp = sc->ifp; - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) - return; + 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); - getbinuptime(&sc->init_uptime); + /* 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; - /* Reset the controller. */ - cpsw_reset(sc); + /* 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; - /* Enable ALE */ - cpsw_write_4(sc, CPSW_ALE_CONTROL, 1 << 31 | 1 << 4); + 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); - /* Init Sliver port 1 and 2 */ - for (i = 0; i < 2; i++) { - /* Set Slave Mapping */ - cpsw_write_4(sc, CPSW_SL_RX_PRI_MAP(i), 0x76543210); - cpsw_write_4(sc, CPSW_PORT_P_TX_PRI_MAP(i + 1), 0x33221100); - cpsw_write_4(sc, CPSW_SL_RX_MAXLEN(i), 0x5f2); - /* Set MACCONTROL for ports 0,1: IFCTL_B(16), IFCTL_A(15), - GMII_EN(5), FULLDUPLEX(1) */ - /* TODO: Docs claim that IFCTL_B and IFCTL_A do the same thing? */ - /* Huh? Docs call bit 0 "Loopback" some places, "FullDuplex" others. */ - cpsw_write_4(sc, CPSW_SL_MACCONTROL(i), 1 << 15 | 1 << 5 | 1); - } - - /* 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); + /* Select PHY and enable interrupts */ + cpsw_write_4(sc->swsc, sc->physel, + MDIO_PHYSEL_LINKINTENB | (sc->phy & 0x1F)); - /* Initialize ALE: all ports set to forwarding(3), initialize addrs */ - for (i = 0; i < 3; i++) - cpsw_write_4(sc, CPSW_ALE_PORTCTL(i), 3); - cpsw_ale_update_addresses(sc, 1); + ether_ifattach(sc->ifp, mac_addr); + callout_init(&sc->mii_callout, 0); - cpsw_write_4(sc, CPSW_SS_PTYPE, 0); + return (0); +} - /* Enable statistics for ports 0, 1 and 2 */ - cpsw_write_4(sc, CPSW_SS_STAT_PORT_EN, 7); +static int +cpswp_detach(device_t dev) +{ + struct cpswp_softc *sc; - /* 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); + 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); + } - /* Make IP hdr aligned with 4 */ - cpsw_write_4(sc, CPSW_CPDMA_RX_BUFFER_OFFSET, 2); + bus_generic_detach(dev); - /* Initialize RX Buffer Descriptors */ - cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), 0); + if_free(sc->ifp); + mtx_destroy(&sc->lock); - /* Enable TX & RX DMA */ - cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 1); - cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 1); + return (0); +} - /* 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), 0x3F); +/* + * + * Init/Shutdown. + * + */ - /* Enable host Error Interrupt */ - cpsw_write_4(sc, CPSW_CPDMA_DMA_INTMASK_SET, 3); +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); - /* Enable interrupts for RX Channel 0 */ - cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_SET, 1); + return (0); +} - /* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */ - /* TODO Calculate MDCLK=CLK/(CLKDIV+1) */ - cpsw_write_4(sc, MDIOCONTROL, 1 << 30 | 1 << 18 | 0xFF); +static void +cpswp_init(void *arg) +{ + struct cpswp_softc *sc = arg; - /* Select MII in GMII_SEL, Internal Delay mode */ - //ti_scm_reg_write_4(0x650, 0); + CPSWP_DEBUGF(sc, ("")); + CPSW_PORT_LOCK(sc); + cpswp_init_locked(arg); + CPSW_PORT_UNLOCK(sc); +} - /* 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); +static void +cpswp_init_locked(void *arg) +{ + struct cpswp_softc *sc = arg; + struct ifnet *ifp; + uint32_t reg; - /* Activate network interface */ - sc->rx.running = 1; - sc->tx.running = 1; - sc->watchdog.timer = 0; - callout_reset(&sc->watchdog.callout, hz, cpsw_tick, sc); - sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; - sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + 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 = device_get_softc(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); + } - CPSW_DEBUGF(("")); - CPSW_GLOBAL_LOCK(sc); - cpsw_shutdown_locked(sc); - CPSW_GLOBAL_UNLOCK(sc); return (0); } static void cpsw_rx_teardown_locked(struct cpsw_softc *sc) { + struct ifnet *ifp; struct mbuf *received, *next; int i = 0; - CPSW_DEBUGF(("starting RX teardown")); + CPSW_DEBUGF(sc, ("starting RX teardown")); cpsw_write_4(sc, CPSW_CPDMA_RX_TEARDOWN, 0); for (;;) { received = cpsw_rx_dequeue(sc); @@ -1012,16 +1196,20 @@ cpsw_rx_teardown_locked(struct cpsw_softc *sc) while (received != NULL) { next = received->m_nextpkt; received->m_nextpkt = NULL; - (*sc->ifp->if_input)(sc->ifp, received); + 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(("finished RX teardown (%d retries)", i)); + CPSW_DEBUGF(sc, + ("finished RX teardown (%d retries)", i)); return; } if (++i > 10) { - if_printf(sc->ifp, "Unable to cleanly shutdown receiver\n"); + device_printf(sc->dev, + "Unable to cleanly shutdown receiver\n"); return; } DELAY(10); @@ -1033,27 +1221,30 @@ cpsw_tx_teardown_locked(struct cpsw_softc *sc) { int i = 0; - CPSW_DEBUGF(("starting TX teardown")); + 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) - if_printf(sc->ifp, "Unable to cleanly shutdown transmitter\n"); - CPSW_DEBUGF(("finished TX teardown (%d retries, %d idle buffers)", + 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 -cpsw_shutdown_locked(struct cpsw_softc *sc) +cpswp_stop_locked(struct cpswp_softc *sc) { struct ifnet *ifp; + uint32_t reg; - CPSW_DEBUGF(("")); - CPSW_GLOBAL_LOCK_ASSERT(sc); ifp = sc->ifp; + CPSWP_DEBUGF(sc, ("")); + CPSW_PORT_LOCK_ASSERT(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; @@ -1063,16 +1254,28 @@ cpsw_shutdown_locked(struct cpsw_softc *sc) ifp->if_drv_flags |= IFF_DRV_OACTIVE; /* Stop ticker */ - callout_stop(&sc->watchdog.callout); + callout_stop(&sc->mii_callout); /* Tear down the RX/TX queues. */ - cpsw_rx_teardown_locked(sc); - cpsw_tx_teardown_locked(sc); + 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); + } - /* Capture stats before we reset controller. */ - cpsw_stats_collect(sc); + /* 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); - cpsw_reset(sc); + 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); + } } /* @@ -1082,21 +1285,32 @@ cpsw_shutdown_locked(struct cpsw_softc *sc) static int cpsw_suspend(device_t dev) { - struct cpsw_softc *sc = device_get_softc(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); + } - CPSW_DEBUGF(("")); - CPSW_GLOBAL_LOCK(sc); - cpsw_shutdown_locked(sc); - CPSW_GLOBAL_UNLOCK(sc); return (0); } static int cpsw_resume(device_t dev) { - struct cpsw_softc *sc = device_get_softc(dev); + struct cpsw_softc *sc; + + sc = device_get_softc(dev); + CPSW_DEBUGF(sc, ("UNIMPLEMENTED")); - CPSW_DEBUGF(("UNIMPLEMENTED")); return (0); } @@ -1107,32 +1321,26 @@ cpsw_resume(device_t dev) */ static void -cpsw_set_promisc(struct cpsw_softc *sc, int set) +cpsw_set_promisc(struct cpswp_softc *sc, int set) { + uint32_t reg; + /* - * Enabling promiscuous mode requires two bits of work: First, - * ALE_BYPASS needs to be enabled. That disables the ALE - * forwarding logic and causes every packet to be sent to the - * host port. That makes us promiscuous wrt received packets. - * - * With ALE forwarding disabled, the transmitter needs to set - * an explicit output port on every packet to route it to the - * correct egress. This should be doable for systems such as - * BeagleBone where only one egress port is actually wired to - * a PHY. If you have both egress ports wired up, life gets a - * lot more interesting. - * - * Hmmm.... NetBSD driver uses ALE_BYPASS always and doesn't - * seem to set explicit egress ports. Does that mean they - * are always promiscuous? + * 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. */ - if (set) { - printf("Promiscuous mode unimplemented\n"); - } + 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 cpsw_softc *sc, int set) +cpsw_set_allmulti(struct cpswp_softc *sc, int set) { if (set) { printf("All-multicast mode unimplemented\n"); @@ -1140,22 +1348,26 @@ cpsw_set_allmulti(struct cpsw_softc *sc, int set) } static int -cpsw_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +cpswp_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { - struct cpsw_softc *sc = ifp->if_softc; - struct ifreq *ifr = (struct ifreq *)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_GLOBAL_LOCK(sc); + CPSW_PORT_LOCK(sc); if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - changed = ifp->if_flags ^ sc->cpsw_if_flags; - CPSW_DEBUGF(("SIOCSIFFLAGS: UP & RUNNING (changed=0x%x)", changed)); + 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); @@ -1163,25 +1375,27 @@ cpsw_ioctl(struct ifnet *ifp, u_long command, caddr_t data) cpsw_set_allmulti(sc, ifp->if_flags & IFF_ALLMULTI); } else { - CPSW_DEBUGF(("SIOCSIFFLAGS: UP but not RUNNING; starting up")); - cpsw_init_locked(sc); + CPSWP_DEBUGF(sc, + ("SIOCSIFFLAGS: UP but not RUNNING; starting up")); + cpswp_init_locked(sc); } } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - CPSW_DEBUGF(("SIOCSIFFLAGS: not UP but RUNNING; shutting down")); - cpsw_shutdown_locked(sc); + CPSWP_DEBUGF(sc, + ("SIOCSIFFLAGS: not UP but RUNNING; shutting down")); + cpswp_stop_locked(sc); } - sc->cpsw_if_flags = ifp->if_flags; - CPSW_GLOBAL_UNLOCK(sc); + sc->if_flags = ifp->if_flags; + CPSW_PORT_UNLOCK(sc); break; case SIOCADDMULTI: - cpsw_ale_update_addresses(sc, 0); + 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. */ - cpsw_ale_update_addresses(sc, 1); + cpswp_ale_update_addresses(sc, 1); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: @@ -1199,41 +1413,43 @@ cpsw_ioctl(struct ifnet *ifp, u_long command, caddr_t data) * */ static int -cpsw_miibus_ready(struct cpsw_softc *sc) +cpswp_miibus_ready(struct cpsw_softc *sc, uint32_t reg) { uint32_t r, retries = CPSW_MIIBUS_RETRIES; while (--retries) { - r = cpsw_read_4(sc, MDIOUSERACCESS0); - if ((r & 1 << 31) == 0) - return 1; + r = cpsw_read_4(sc, reg); + if ((r & MDIO_PHYACCESS_GO) == 0) + return (1); DELAY(CPSW_MIIBUS_DELAY); } - return 0; + + return (0); } static int -cpsw_miibus_readreg(device_t dev, int phy, int reg) +cpswp_miibus_readreg(device_t dev, int phy, int reg) { - struct cpsw_softc *sc = device_get_softc(dev); + struct cpswp_softc *sc; uint32_t cmd, r; - if (!cpsw_miibus_ready(sc)) { + sc = device_get_softc(dev); + if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) { device_printf(dev, "MDIO not ready to read\n"); - return 0; + return (0); } /* Set GO, reg, phy */ - cmd = 1 << 31 | (reg & 0x1F) << 21 | (phy & 0x1F) << 16; - cpsw_write_4(sc, MDIOUSERACCESS0, cmd); + cmd = MDIO_PHYACCESS_GO | (reg & 0x1F) << 21 | (phy & 0x1F) << 16; + cpsw_write_4(sc->swsc, sc->phyaccess, cmd); - if (!cpsw_miibus_ready(sc)) { + if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) { device_printf(dev, "MDIO timed out during read\n"); - return 0; + return (0); } - r = cpsw_read_4(sc, MDIOUSERACCESS0); - if((r & 1 << 29) == 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; } @@ -1241,60 +1457,63 @@ cpsw_miibus_readreg(device_t dev, int phy, int reg) } static int -cpsw_miibus_writereg(device_t dev, int phy, int reg, int value) +cpswp_miibus_writereg(device_t dev, int phy, int reg, int value) { - struct cpsw_softc *sc = device_get_softc(dev); + struct cpswp_softc *sc; uint32_t cmd; - if (!cpsw_miibus_ready(sc)) { + sc = device_get_softc(dev); + if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) { device_printf(dev, "MDIO not ready to write\n"); - return 0; + return (0); } /* Set GO, WRITE, reg, phy, and value */ - cmd = 3 << 30 | (reg & 0x1F) << 21 | (phy & 0x1F) << 16 - | (value & 0xFFFF); - cpsw_write_4(sc, MDIOUSERACCESS0, cmd); + cmd = MDIO_PHYACCESS_GO | MDIO_PHYACCESS_WRITE | + (reg & 0x1F) << 21 | (phy & 0x1F) << 16 | (value & 0xFFFF); + cpsw_write_4(sc->swsc, sc->phyaccess, cmd); - if (!cpsw_miibus_ready(sc)) { + if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) { device_printf(dev, "MDIO timed out during write\n"); - return 0; + return (0); } - if((cpsw_read_4(sc, MDIOUSERACCESS0) & (1 << 29)) == 0) + if ((cpsw_read_4(sc->swsc, sc->phyaccess) & MDIO_PHYACCESS_ACK) == 0) device_printf(dev, "Failed to write to PHY.\n"); - return 0; + return (0); } static void -cpsw_miibus_statchg(device_t dev) +cpswp_miibus_statchg(device_t dev) { - struct cpsw_softc *sc = device_get_softc(dev); - uint32_t mac_control; - int i; - - CPSW_DEBUGF(("")); - - for (i = 0; i < 2; i++) { - mac_control = cpsw_read_4(sc, CPSW_SL_MACCONTROL(i)); - mac_control &= ~(1 << 15 | 1 << 7); - - 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 |= 1 << 7; - break; - - default: - mac_control |= 1 << 15; - break; - } + 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; - cpsw_write_4(sc, CPSW_SL_MACCONTROL(i), mac_control); + 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); } /* @@ -1302,12 +1521,11 @@ cpsw_miibus_statchg(device_t dev) * 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); @@ -1319,7 +1537,9 @@ cpsw_intr_rx(void *arg) while (received != NULL) { next = received->m_nextpkt; received->m_nextpkt = NULL; - (*sc->ifp->if_input)(sc->ifp, received); + ifp = received->m_pkthdr.rcvif; + (*ifp->if_input)(ifp, received); + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); received = next; } } @@ -1329,11 +1549,10 @@ cpsw_rx_dequeue(struct cpsw_softc *sc) { struct cpsw_cpdma_bd bd; struct cpsw_slot *slot; - struct ifnet *ifp; + struct cpswp_softc *psc; struct mbuf *mb_head, *mb_tail; - int removed = 0; + int port, removed = 0; - ifp = sc->ifp; mb_head = mb_tail = NULL; /* Pull completed packets off hardware RX queue. */ @@ -1342,7 +1561,7 @@ cpsw_rx_dequeue(struct cpsw_softc *sc) if (bd.flags & CPDMA_BD_OWNER) break; /* Still in use by hardware */ - CPSW_DEBUGF(("Removing received packet from RX queue")); + 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); @@ -1351,7 +1570,7 @@ cpsw_rx_dequeue(struct cpsw_softc *sc) bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); if (bd.flags & CPDMA_BD_TDOWNCMPLT) { - CPSW_DEBUGF(("RX teardown in progress")); + CPSW_DEBUGF(sc, ("RX teardown in progress")); m_freem(slot->mbuf); slot->mbuf = NULL; cpsw_write_cp(sc, &sc->rx, 0xfffffffc); @@ -1361,6 +1580,11 @@ cpsw_rx_dequeue(struct cpsw_softc *sc) 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. */ @@ -1368,10 +1592,10 @@ cpsw_rx_dequeue(struct cpsw_softc *sc) 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 = ifp; + slot->mbuf->m_pkthdr.rcvif = psc->ifp; slot->mbuf->m_nextpkt = NULL; - if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) { + 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; @@ -1405,7 +1629,6 @@ cpsw_rx_enqueue(struct cpsw_softc *sc) { bus_dma_segment_t seg[1]; struct cpsw_cpdma_bd bd; - struct ifnet *ifp = sc->ifp; struct cpsw_slots tmpqueue = STAILQ_HEAD_INITIALIZER(tmpqueue); struct cpsw_slot *slot, *prev_slot = NULL; struct cpsw_slot *last_old_slot, *first_new_slot; @@ -1416,7 +1639,8 @@ cpsw_rx_enqueue(struct cpsw_softc *sc) if (slot->mbuf == NULL) { slot->mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (slot->mbuf == NULL) { - if_printf(sc->ifp, "Unable to fill RX queue\n"); + device_printf(sc->dev, + "Unable to fill RX queue\n"); break; } slot->mbuf->m_len = @@ -1430,7 +1654,7 @@ cpsw_rx_enqueue(struct cpsw_softc *sc) KASSERT(nsegs == 1, ("More than one segment (nsegs=%d)", nsegs)); KASSERT(error == 0, ("DMA error (error=%d)", error)); if (error != 0 || nsegs != 1) { - if_printf(ifp, + 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); @@ -1462,7 +1686,7 @@ cpsw_rx_enqueue(struct cpsw_softc *sc) if (added == 0) return; - CPSW_DEBUGF(("Adding %d buffers to RX queue", added)); + 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); @@ -1489,20 +1713,20 @@ cpsw_rx_enqueue(struct cpsw_softc *sc) } static void -cpsw_start(struct ifnet *ifp) +cpswp_start(struct ifnet *ifp) { - struct cpsw_softc *sc = ifp->if_softc; + struct cpswp_softc *sc = ifp->if_softc; - CPSW_TX_LOCK(sc); - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && sc->tx.running) { - cpsw_tx_enqueue(sc); - cpsw_tx_dequeue(sc); + 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); + CPSW_TX_UNLOCK(sc->swsc); } static void -cpsw_tx_enqueue(struct cpsw_softc *sc) +cpswp_tx_enqueue(struct cpswp_softc *sc) { bus_dma_segment_t segs[CPSW_TXFRAGS]; struct cpsw_cpdma_bd bd; @@ -1510,10 +1734,15 @@ cpsw_tx_enqueue(struct cpsw_softc *sc) struct cpsw_slot *slot, *prev_slot = NULL; struct cpsw_slot *last_old_slot, *first_new_slot; struct mbuf *m0; - int error, nsegs, seg, added = 0, padlen; + 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->tx.avail)) != NULL) { + while ((slot = STAILQ_FIRST(&sc->swsc->tx.avail)) != NULL) { IF_DEQUEUE(&sc->ifp->if_snd, m0); if (m0 == NULL) break; @@ -1524,45 +1753,47 @@ cpsw_tx_enqueue(struct cpsw_softc *sc) padlen = 0; /* Create mapping in DMA memory */ - error = bus_dmamap_load_mbuf_sg(sc->mbuf_dtag, slot->dmamap, - slot->mbuf, segs, &nsegs, BUS_DMA_NOWAIT); + 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->tx.avail_queue_len)) { - bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); + 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->null_mbuf->m_data); + sc->swsc->null_mbuf->m_data); m0 = m_defrag(slot->mbuf, M_NOWAIT); if (m0 == NULL) { - if_printf(sc->ifp, + device_printf(sc->dev, "Can't defragment packet; dropping\n"); m_freem(slot->mbuf); } else { - CPSW_DEBUGF(("Requeueing defragmented packet")); + CPSWP_DEBUGF(sc, + ("Requeueing defragmented packet")); IF_PREPEND(&sc->ifp->if_snd, m0); } slot->mbuf = NULL; continue; } if (error != 0) { - if_printf(sc->ifp, + device_printf(sc->dev, "%s: Can't setup DMA (error=%d), dropping packet\n", __func__, error); - bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); + bus_dmamap_unload(sc->swsc->mbuf_dtag, slot->dmamap); m_freem(slot->mbuf); slot->mbuf = NULL; break; } - bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, + 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)); - CPSW_DEBUGF(("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. */ @@ -1572,18 +1803,20 @@ cpsw_tx_enqueue(struct cpsw_softc *sc) 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; + 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, slot, &bd); - if (prev_slot != NULL) - cpsw_cpdma_write_bd_next(sc, prev_slot, slot); + 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->tx.avail, next); - sc->tx.avail_queue_len--; + 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->tx.avail); + slot = STAILQ_FIRST(&sc->swsc->tx.avail); /* Setup next buffer (which isn't SOP) */ bd.next = 0; @@ -1591,42 +1824,42 @@ cpsw_tx_enqueue(struct cpsw_softc *sc) bd.bufoff = 0; bd.buflen = segs[seg].ds_len; bd.pktlen = 0; - bd.flags = CPDMA_BD_OWNER; + bd.flags = CPDMA_BD_OWNER | flags; } /* Save the final buffer. */ if (padlen <= 0) bd.flags |= CPDMA_BD_EOP; - cpsw_cpdma_write_bd(sc, slot, &bd); + cpsw_cpdma_write_bd(sc->swsc, slot, &bd); if (prev_slot != NULL) - cpsw_cpdma_write_bd_next(sc, prev_slot, slot); + cpsw_cpdma_write_bd_next(sc->swsc, prev_slot, slot); prev_slot = slot; - STAILQ_REMOVE_HEAD(&sc->tx.avail, next); - sc->tx.avail_queue_len--; + 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->tx.avail); - STAILQ_REMOVE_HEAD(&sc->tx.avail, next); - sc->tx.avail_queue_len--; + 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, prev_slot, slot); + cpsw_cpdma_write_bd_next(sc->swsc, prev_slot, slot); prev_slot = slot; bd.next = 0; - bd.bufptr = sc->null_mbuf_paddr; + bd.bufptr = sc->swsc->null_mbuf_paddr; bd.bufoff = 0; bd.buflen = padlen; bd.pktlen = 0; - bd.flags = CPDMA_BD_EOP | CPDMA_BD_OWNER; - cpsw_cpdma_write_bd(sc, slot, &bd); + bd.flags = CPDMA_BD_EOP | CPDMA_BD_OWNER | flags; + cpsw_cpdma_write_bd(sc->swsc, slot, &bd); ++nsegs; } - if (nsegs > sc->tx.longest_chain) - sc->tx.longest_chain = 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? @@ -1634,26 +1867,29 @@ cpsw_tx_enqueue(struct cpsw_softc *sc) } /* Attach the list of new buffers to the hardware TX queue. */ - last_old_slot = STAILQ_LAST(&sc->tx.active, cpsw_slot, next); + last_old_slot = STAILQ_LAST(&sc->swsc->tx.active, cpsw_slot, next); first_new_slot = STAILQ_FIRST(&tmpqueue); - STAILQ_CONCAT(&sc->tx.active, &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, &sc->tx, first_new_slot); + 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, last_old_slot, first_new_slot); + cpsw_cpdma_write_bd_next(sc->swsc, 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->tx, first_new_slot); + 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->tx.queue_adds += added; - sc->tx.active_queue_len += added; - if (sc->tx.active_queue_len > sc->tx.max_active_queue_len) { - sc->tx.max_active_queue_len = sc->tx.active_queue_len; + 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; } } @@ -1665,7 +1901,7 @@ cpsw_tx_dequeue(struct cpsw_softc *sc) slot = STAILQ_FIRST(&sc->tx.active); if (slot == NULL && cpsw_read_cp(sc, &sc->tx) == 0xfffffffc) { - CPSW_DEBUGF(("TX teardown of an empty queue")); + CPSW_DEBUGF(sc, ("TX teardown of an empty queue")); cpsw_write_cp(sc, &sc->tx, 0xfffffffc); sc->tx.running = 0; return (0); @@ -1677,11 +1913,13 @@ cpsw_tx_dequeue(struct cpsw_softc *sc) if (flags & CPDMA_BD_OWNER) break; /* Hardware is still using this packet. */ - CPSW_DEBUGF(("TX removing completed 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) { @@ -1694,7 +1932,7 @@ cpsw_tx_dequeue(struct cpsw_softc *sc) /* TearDown complete is only marked on the SOP for the packet. */ if (flags & CPDMA_BD_TDOWNCMPLT) { - CPSW_DEBUGF(("TX teardown in progress")); + 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; @@ -1725,7 +1963,7 @@ 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(("stat=%x", stat)); + CPSW_DEBUGF(sc, ("stat=%x", stat)); cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 0); } @@ -1817,16 +2055,20 @@ 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 & 16) - CPSW_DEBUGF(("Time sync event interrupt unimplemented")); - if (stat & 8) + 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 & 4) + if (stat & CPSW_WR_C_MISC_HOST_PEND) cpsw_intr_misc_host_error(sc); - if (stat & 2) - CPSW_DEBUGF(("MDIO link change interrupt unimplemented")); - if (stat & 1) - CPSW_DEBUGF(("MDIO operation completed interrupt unimplemented")); + 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); } @@ -1837,56 +2079,51 @@ cpsw_intr_misc(void *arg) */ static void -cpsw_tick(void *msc) +cpswp_tick(void *msc) { - struct cpsw_softc *sc = msc; - - /* Check for TX timeout */ - cpsw_tx_watchdog(sc); + struct cpswp_softc *sc = msc; /* Check for media type change */ mii_tick(sc->mii); - if(sc->cpsw_media_status != sc->mii->mii_media.ifm_media) { + 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); - cpsw_ifmedia_upd(sc->ifp); + cpswp_ifmedia_upd(sc->ifp); } /* Schedule another timeout one second from now */ - callout_reset(&sc->watchdog.callout, hz, cpsw_tick, sc); + callout_reset(&sc->mii_callout, hz, cpswp_tick, sc); } static void -cpsw_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +cpswp_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { - struct cpsw_softc *sc = ifp->if_softc; + struct cpswp_softc *sc; struct mii_data *mii; - CPSW_DEBUGF(("")); - CPSW_TX_LOCK(sc); + 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_TX_UNLOCK(sc); + CPSW_PORT_UNLOCK(sc); } static int -cpsw_ifmedia_upd(struct ifnet *ifp) +cpswp_ifmedia_upd(struct ifnet *ifp) { - struct cpsw_softc *sc = ifp->if_softc; + struct cpswp_softc *sc; - CPSW_DEBUGF(("")); - if (ifp->if_flags & IFF_UP) { - CPSW_GLOBAL_LOCK(sc); - sc->cpsw_media_status = sc->mii->mii_media.ifm_media; - mii_mediachg(sc->mii); - cpsw_init_locked(sc); - CPSW_GLOBAL_UNLOCK(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); } @@ -1894,20 +2131,29 @@ cpsw_ifmedia_upd(struct ifnet *ifp) static void cpsw_tx_watchdog_full_reset(struct cpsw_softc *sc) { + struct cpswp_softc *psc; + int i; + cpsw_debugf_head("CPSW watchdog"); - if_printf(sc->ifp, "watchdog timeout\n"); - cpsw_shutdown_locked(sc); - cpsw_init_locked(sc); + 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(struct cpsw_softc *sc) +cpsw_tx_watchdog(void *msc) { - struct ifnet *ifp = sc->ifp; + struct cpsw_softc *sc; + sc = msc; CPSW_GLOBAL_LOCK(sc); - if (sc->tx.active_queue_len == 0 || (ifp->if_flags & IFF_UP) == 0 || - (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || !sc->tx.running) { + 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. */ @@ -1916,15 +2162,17 @@ cpsw_tx_watchdog(struct cpsw_softc *sc) } else { /* There was something to do but it didn't get done. */ ++sc->watchdog.timer; - if (sc->watchdog.timer > 2) { + if (sc->watchdog.timer > 5) { sc->watchdog.timer = 0; - if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ++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); } /* @@ -1951,38 +2199,38 @@ cpsw_ale_write_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry) cpsw_write_4(sc, CPSW_ALE_TBLCTL, 1 << 31 | (idx & 1023)); } -static int +static void cpsw_ale_remove_all_mc_entries(struct cpsw_softc *sc) { int i; uint32_t ale_entry[3]; - /* First two entries are link address and broadcast. */ - for (i = 2; i < CPSW_MAX_ALE_ENTRIES; i++) { + /* 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_entry[1] >> 28) & 3) == 1 && /* Address entry */ - ((ale_entry[1] >> 8) & 1) == 1) { /* MCast link addr */ + 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); } } - return CPSW_MAX_ALE_ENTRIES; } static int -cpsw_ale_mc_entry_set(struct cpsw_softc *sc, uint8_t portmap, uint8_t *mac) +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]; + uint32_t ale_entry[3], ale_type; /* Find a matching entry or a free entry. */ - for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) { + 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_entry[1] >> 28) & 3) == 0) { + 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]) && @@ -2001,12 +2249,17 @@ cpsw_ale_mc_entry_set(struct cpsw_softc *sc, uint8_t portmap, uint8_t *mac) 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] is addr entry(1), Mcast fwd state[63:62] is fw(3)*/ - ale_entry[1] |= 0xd0 << 24; + /* 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; @@ -2022,9 +2275,23 @@ cpsw_ale_dump_table(struct cpsw_softc *sc) { uint32_t ale_entry[3]; for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) { cpsw_ale_read_entry(sc, i, ale_entry); - if (ale_entry[0] || ale_entry[1] || ale_entry[2]) { - printf("ALE[%4u] %08x %08x %08x ", i, ale_entry[0], - ale_entry[1], ale_entry[2]); + 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, @@ -2032,62 +2299,109 @@ cpsw_ale_dump_table(struct cpsw_softc *sc) { (ale_entry[0] >>16) & 0xFF, (ale_entry[0] >> 8) & 0xFF, (ale_entry[0] >> 0) & 0xFF); - printf(((ale_entry[1] >> 8) & 1) ? "mcast " : "ucast "); - printf("type: %u ", (ale_entry[1] >> 28) & 3); - printf("port: %u ", (ale_entry[2] >> 2) & 7); + 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 -cpsw_ale_update_addresses(struct cpsw_softc *sc, int purge) +cpswp_ale_update_addresses(struct cpswp_softc *sc, int purge) { uint8_t *mac; - uint32_t ale_entry[3]; - struct ifnet *ifp = sc->ifp; + uint32_t ale_entry[3], ale_type, portmask; struct ifmultiaddr *ifma; - int i; - /* Route incoming packets for our MAC address to Port 0 (host). */ - /* For simplicity, keep this entry at table index 0 in the ALE. */ - if_addr_rlock(ifp); - mac = LLADDR((struct sockaddr_dl *)ifp->if_addr->ifa_addr); + 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] = 0x10 << 24 | mac[0] << 8 | mac[1]; /* addr entry + mac */ + ale_entry[1] = ale_type | mac[0] << 8 | mac[1]; /* addr entry + mac */ ale_entry[2] = 0; /* port = 0 */ - cpsw_ale_write_entry(sc, 0, ale_entry); + cpsw_ale_write_entry(sc->swsc, 0 + 2 * sc->unit, ale_entry); - /* Set outgoing MAC Address for Ports 1 and 2. */ - for (i = 1; i < 3; ++i) { - cpsw_write_4(sc, CPSW_PORT_P_SA_HI(i), - mac[3] << 24 | mac[2] << 16 | mac[1] << 8 | mac[0]); - cpsw_write_4(sc, CPSW_PORT_P_SA_LO(i), - mac[5] << 8 | mac[4]); - } - if_addr_runlock(ifp); + /* 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. */ + /* Keep the broadcast address at table entry 1 (or 3). */ ale_entry[0] = 0xffffffff; /* Lower 32 bits of MAC */ - ale_entry[1] = 0xd000ffff; /* FW (3 << 30), Addr entry (1 << 24), upper 16 bits of Mac */ - ale_entry[2] = 0x0000001c; /* Forward to all ports */ - cpsw_ale_write_entry(sc, 1, ale_entry); + /* 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); + cpsw_ale_remove_all_mc_entries(sc->swsc); /* Set other multicast addrs desired. */ - if_maddr_rlock(ifp); - TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + 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, 7, + cpsw_ale_mc_entry_set(sc->swsc, portmask, sc->vlan, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); } - if_maddr_runlock(ifp); + 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); } @@ -2108,9 +2422,9 @@ cpsw_stats_dump(struct cpsw_softc *sc) for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) { r = cpsw_read_4(sc, CPSW_STATS_OFFSET + cpsw_stat_sysctls[i].reg); - CPSW_DEBUGF(("%s: %ju + %u = %ju", cpsw_stat_sysctls[i].oid, - (intmax_t)sc->shadow_stats[i], r, - (intmax_t)sc->shadow_stats[i] + r)); + 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 @@ -2121,13 +2435,14 @@ cpsw_stats_collect(struct cpsw_softc *sc) int i; uint32_t r; - CPSW_DEBUGF(("Controller shadow statistics updated.")); + 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); + cpsw_write_4(sc, CPSW_STATS_OFFSET + cpsw_stat_sysctls[i].reg, + r); } } @@ -2162,11 +2477,13 @@ cpsw_stat_attached(SYSCTL_HANDLER_ARGS) static int cpsw_stat_uptime(SYSCTL_HANDLER_ARGS) { - struct cpsw_softc *sc; + struct cpsw_softc *swsc; + struct cpswp_softc *sc; struct bintime t; unsigned result; - sc = (struct cpsw_softc *)arg1; + 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); @@ -2177,7 +2494,8 @@ cpsw_stat_uptime(SYSCTL_HANDLER_ARGS) } static void -cpsw_add_queue_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node, struct cpsw_queue *queue) +cpsw_add_queue_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node, + struct cpsw_queue *queue) { struct sysctl_oid_list *parent; @@ -2210,7 +2528,8 @@ cpsw_add_queue_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node, str } static void -cpsw_add_watchdog_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node, struct cpsw_softc *sc) +cpsw_add_watchdog_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node, + struct cpsw_softc *sc) { struct sysctl_oid_list *parent; @@ -2226,18 +2545,35 @@ 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"); - SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "uptime", - CTLTYPE_UINT | CTLFLAG_RD, sc, 0, cpsw_stat_uptime, "IU", - "Seconds since driver init"); + 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"); @@ -2266,4 +2602,3 @@ cpsw_add_sysctls(struct cpsw_softc *sc) CTLFLAG_RD, NULL, "Watchdog Statistics"); cpsw_add_watchdog_sysctls(ctx, node, sc); } - diff --git a/sys/arm/ti/cpsw/if_cpswreg.h b/sys/arm/ti/cpsw/if_cpswreg.h index 98d6033..bea6f19 100644 --- a/sys/arm/ti/cpsw/if_cpswreg.h +++ b/sys/arm/ti/cpsw/if_cpswreg.h @@ -29,103 +29,142 @@ #ifndef _IF_CPSWREG_H #define _IF_CPSWREG_H -#define CPSW_SS_OFFSET 0x0000 -#define CPSW_SS_IDVER (CPSW_SS_OFFSET + 0x00) -#define CPSW_SS_SOFT_RESET (CPSW_SS_OFFSET + 0x08) -#define CPSW_SS_STAT_PORT_EN (CPSW_SS_OFFSET + 0x0C) -#define CPSW_SS_PTYPE (CPSW_SS_OFFSET + 0x10) +#define CPSW_SS_OFFSET 0x0000 +#define CPSW_SS_IDVER (CPSW_SS_OFFSET + 0x00) +#define CPSW_SS_SOFT_RESET (CPSW_SS_OFFSET + 0x08) +#define CPSW_SS_STAT_PORT_EN (CPSW_SS_OFFSET + 0x0C) +#define CPSW_SS_PTYPE (CPSW_SS_OFFSET + 0x10) #define CPSW_SS_FLOW_CONTROL (CPSW_SS_OFFSET + 0x24) -#define CPSW_PORT_OFFSET 0x0100 +#define CPSW_PORT_OFFSET 0x0100 #define CPSW_PORT_P_MAX_BLKS(p) (CPSW_PORT_OFFSET + 0x08 + ((p) * 0x100)) #define CPSW_PORT_P_BLK_CNT(p) (CPSW_PORT_OFFSET + 0x0C + ((p) * 0x100)) -#define CPSW_PORT_P_TX_PRI_MAP(p) (CPSW_PORT_OFFSET + 0x118 + ((p-1) * 0x100)) -#define CPSW_PORT_P0_CPDMA_TX_PRI_MAP (CPSW_PORT_OFFSET + 0x01C) -#define CPSW_PORT_P0_CPDMA_RX_CH_MAP (CPSW_PORT_OFFSET + 0x020) -#define CPSW_PORT_P_SA_LO(p) (CPSW_PORT_OFFSET + 0x120 + ((p-1) * 0x100)) -#define CPSW_PORT_P_SA_HI(p) (CPSW_PORT_OFFSET + 0x124 + ((p-1) * 0x100)) - -#define CPSW_CPDMA_OFFSET 0x0800 -#define CPSW_CPDMA_TX_CONTROL (CPSW_CPDMA_OFFSET + 0x04) -#define CPSW_CPDMA_TX_TEARDOWN (CPSW_CPDMA_OFFSET + 0x08) -#define CPSW_CPDMA_RX_CONTROL (CPSW_CPDMA_OFFSET + 0x14) -#define CPSW_CPDMA_RX_TEARDOWN (CPSW_CPDMA_OFFSET + 0x18) -#define CPSW_CPDMA_SOFT_RESET (CPSW_CPDMA_OFFSET + 0x1c) -#define CPSW_CPDMA_DMACONTROL (CPSW_CPDMA_OFFSET + 0x20) -#define CPSW_CPDMA_DMASTATUS (CPSW_CPDMA_OFFSET + 0x24) -#define CPSW_CPDMA_RX_BUFFER_OFFSET (CPSW_CPDMA_OFFSET + 0x28) -#define CPSW_CPDMA_TX_INTSTAT_RAW (CPSW_CPDMA_OFFSET + 0x80) -#define CPSW_CPDMA_TX_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0x84) -#define CPSW_CPDMA_TX_INTMASK_SET (CPSW_CPDMA_OFFSET + 0x88) -#define CPSW_CPDMA_TX_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0x8C) -#define CPSW_CPDMA_CPDMA_EOI_VECTOR (CPSW_CPDMA_OFFSET + 0x94) -#define CPSW_CPDMA_RX_INTSTAT_RAW (CPSW_CPDMA_OFFSET + 0xA0) -#define CPSW_CPDMA_RX_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0xA4) -#define CPSW_CPDMA_RX_INTMASK_SET (CPSW_CPDMA_OFFSET + 0xA8) -#define CPSW_CPDMA_RX_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0xAc) -#define CPSW_CPDMA_DMA_INTSTAT_RAW (CPSW_CPDMA_OFFSET + 0xB0) -#define CPSW_CPDMA_DMA_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0xB4) -#define CPSW_CPDMA_DMA_INTMASK_SET (CPSW_CPDMA_OFFSET + 0xB8) -#define CPSW_CPDMA_DMA_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0xBC) -#define CPSW_CPDMA_RX_FREEBUFFER(p) (CPSW_CPDMA_OFFSET + 0x0e0 + ((p) * 0x04)) - -#define CPSW_STATS_OFFSET 0x0900 - -#define CPSW_STATERAM_OFFSET 0x0A00 -#define CPSW_CPDMA_TX_HDP(p) (CPSW_STATERAM_OFFSET + 0x00 + ((p) * 0x04)) -#define CPSW_CPDMA_RX_HDP(p) (CPSW_STATERAM_OFFSET + 0x20 + ((p) * 0x04)) -#define CPSW_CPDMA_TX_CP(p) (CPSW_STATERAM_OFFSET + 0x40 + ((p) * 0x04)) -#define CPSW_CPDMA_RX_CP(p) (CPSW_STATERAM_OFFSET + 0x60 + ((p) * 0x04)) - -#define CPSW_CPTS_OFFSET 0x0C00 - -#define CPSW_ALE_OFFSET 0x0D00 -#define CPSW_ALE_CONTROL (CPSW_ALE_OFFSET + 0x08) -#define CPSW_ALE_TBLCTL (CPSW_ALE_OFFSET + 0x20) -#define CPSW_ALE_TBLW2 (CPSW_ALE_OFFSET + 0x34) -#define CPSW_ALE_TBLW1 (CPSW_ALE_OFFSET + 0x38) -#define CPSW_ALE_TBLW0 (CPSW_ALE_OFFSET + 0x3C) -#define CPSW_ALE_PORTCTL(p) (CPSW_ALE_OFFSET + 0x40 + ((p) * 0x04)) +#define CPSW_PORT_P_VLAN(p) (CPSW_PORT_OFFSET + 0x14 + ((p) * 0x100)) +#define CPSW_PORT_P_TX_PRI_MAP(p) (CPSW_PORT_OFFSET + 0x118 + ((p-1) * 0x100)) +#define CPSW_PORT_P0_CPDMA_TX_PRI_MAP (CPSW_PORT_OFFSET + 0x01C) +#define CPSW_PORT_P0_CPDMA_RX_CH_MAP (CPSW_PORT_OFFSET + 0x020) +#define CPSW_PORT_P_SA_LO(p) (CPSW_PORT_OFFSET + 0x120 + ((p-1) * 0x100)) +#define CPSW_PORT_P_SA_HI(p) (CPSW_PORT_OFFSET + 0x124 + ((p-1) * 0x100)) + +#define CPSW_CPDMA_OFFSET 0x0800 +#define CPSW_CPDMA_TX_CONTROL (CPSW_CPDMA_OFFSET + 0x04) +#define CPSW_CPDMA_TX_TEARDOWN (CPSW_CPDMA_OFFSET + 0x08) +#define CPSW_CPDMA_RX_CONTROL (CPSW_CPDMA_OFFSET + 0x14) +#define CPSW_CPDMA_RX_TEARDOWN (CPSW_CPDMA_OFFSET + 0x18) +#define CPSW_CPDMA_SOFT_RESET (CPSW_CPDMA_OFFSET + 0x1c) +#define CPSW_CPDMA_DMACONTROL (CPSW_CPDMA_OFFSET + 0x20) +#define CPSW_CPDMA_DMASTATUS (CPSW_CPDMA_OFFSET + 0x24) +#define CPSW_CPDMA_RX_BUFFER_OFFSET (CPSW_CPDMA_OFFSET + 0x28) +#define CPSW_CPDMA_TX_INTSTAT_RAW (CPSW_CPDMA_OFFSET + 0x80) +#define CPSW_CPDMA_TX_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0x84) +#define CPSW_CPDMA_TX_INTMASK_SET (CPSW_CPDMA_OFFSET + 0x88) +#define CPSW_CPDMA_TX_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0x8C) +#define CPSW_CPDMA_CPDMA_EOI_VECTOR (CPSW_CPDMA_OFFSET + 0x94) +#define CPSW_CPDMA_RX_INTSTAT_RAW (CPSW_CPDMA_OFFSET + 0xA0) +#define CPSW_CPDMA_RX_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0xA4) +#define CPSW_CPDMA_RX_INTMASK_SET (CPSW_CPDMA_OFFSET + 0xA8) +#define CPSW_CPDMA_RX_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0xAc) +#define CPSW_CPDMA_DMA_INTSTAT_RAW (CPSW_CPDMA_OFFSET + 0xB0) +#define CPSW_CPDMA_DMA_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0xB4) +#define CPSW_CPDMA_DMA_INTMASK_SET (CPSW_CPDMA_OFFSET + 0xB8) +#define CPSW_CPDMA_DMA_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0xBC) +#define CPSW_CPDMA_RX_FREEBUFFER(p) (CPSW_CPDMA_OFFSET + 0x0e0 + ((p) * 0x04)) + +#define CPSW_STATS_OFFSET 0x0900 + +#define CPSW_STATERAM_OFFSET 0x0A00 +#define CPSW_CPDMA_TX_HDP(p) (CPSW_STATERAM_OFFSET + 0x00 + ((p) * 0x04)) +#define CPSW_CPDMA_RX_HDP(p) (CPSW_STATERAM_OFFSET + 0x20 + ((p) * 0x04)) +#define CPSW_CPDMA_TX_CP(p) (CPSW_STATERAM_OFFSET + 0x40 + ((p) * 0x04)) +#define CPSW_CPDMA_RX_CP(p) (CPSW_STATERAM_OFFSET + 0x60 + ((p) * 0x04)) + +#define CPSW_CPTS_OFFSET 0x0C00 + +#define CPSW_ALE_OFFSET 0x0D00 +#define CPSW_ALE_CONTROL (CPSW_ALE_OFFSET + 0x08) +#define CPSW_ALE_CTL_ENABLE (1U << 31) +#define CPSW_ALE_CTL_CLEAR_TBL (1 << 30) +#define CPSW_ALE_CTL_BYPASS (1 << 4) +#define CPSW_ALE_CTL_VLAN_AWARE (1 << 2) +#define CPSW_ALE_TBLCTL (CPSW_ALE_OFFSET + 0x20) +#define CPSW_ALE_TBLW2 (CPSW_ALE_OFFSET + 0x34) +#define CPSW_ALE_TBLW1 (CPSW_ALE_OFFSET + 0x38) +#define CPSW_ALE_TBLW0 (CPSW_ALE_OFFSET + 0x3C) +#define ALE_MCAST(_a) ((_a[1] >> 8) & 1) +#define ALE_MCAST_FWD (3 << 30) +#define ALE_PORTS(_a) ((_a[2] >> 2) & 7) +#define ALE_TYPE(_a) ((_a[1] >> 28) & 3) +#define ALE_TYPE_ADDR 1 +#define ALE_TYPE_VLAN 2 +#define ALE_TYPE_VLAN_ADDR 3 +#define ALE_VLAN(_a) ((_a[1] >> 16) & 0xfff) +#define ALE_VLAN_UNREGFLOOD(_a) ((_a[0] >> 8) & 7) +#define ALE_VLAN_REGFLOOD(_a) ((_a[0] >> 16) & 7) +#define ALE_VLAN_UNTAG(_a) ((_a[0] >> 24) & 7) +#define ALE_VLAN_MEMBERS(_a) (_a[0] & 7) +#define CPSW_ALE_PORTCTL(p) (CPSW_ALE_OFFSET + 0x40 + ((p) * 0x04)) /* SL1 is at 0x0D80, SL2 is at 0x0DC0 */ -#define CPSW_SL_OFFSET 0x0D80 -#define CPSW_SL_MACCONTROL(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x04) -#define CPSW_SL_MACSTATUS(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x08) -#define CPSW_SL_SOFT_RESET(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x0C) -#define CPSW_SL_RX_MAXLEN(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x10) -#define CPSW_SL_RX_PAUSE(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x18) -#define CPSW_SL_TX_PAUSE(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x1C) -#define CPSW_SL_RX_PRI_MAP(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x24) - -#define MDIO_OFFSET 0x1000 -#define MDIOCONTROL (MDIO_OFFSET + 0x04) -#define MDIOUSERACCESS0 (MDIO_OFFSET + 0x80) -#define MDIOUSERPHYSEL0 (MDIO_OFFSET + 0x84) - -#define CPSW_WR_OFFSET 0x1200 -#define CPSW_WR_SOFT_RESET (CPSW_WR_OFFSET + 0x04) -#define CPSW_WR_CONTROL (CPSW_WR_OFFSET + 0x08) -#define CPSW_WR_INT_CONTROL (CPSW_WR_OFFSET + 0x0c) -#define CPSW_WR_C_RX_THRESH_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x10) -#define CPSW_WR_C_RX_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x14) -#define CPSW_WR_C_TX_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x18) -#define CPSW_WR_C_MISC_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x1C) -#define CPSW_WR_C_RX_THRESH_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x40) -#define CPSW_WR_C_RX_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x44) -#define CPSW_WR_C_TX_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x48) -#define CPSW_WR_C_MISC_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x4C) - -#define CPSW_CPPI_RAM_OFFSET 0x2000 +#define CPSW_SL_OFFSET 0x0D80 +#define CPSW_SL_MACCONTROL(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x04) +#define CPSW_SL_MACTL_IFCTL_B (1 << 16) +#define CPSW_SL_MACTL_IFCTL_A (1 << 15) +#define CPSW_SL_MACTL_GIG (1 << 7) +#define CPSW_SL_MACTL_GMII_ENABLE (1 << 5) +#define CPSW_SL_MACTL_FULLDUPLEX (1 << 0) +#define CPSW_SL_MACSTATUS(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x08) +#define CPSW_SL_SOFT_RESET(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x0C) +#define CPSW_SL_RX_MAXLEN(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x10) +#define CPSW_SL_RX_PAUSE(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x18) +#define CPSW_SL_TX_PAUSE(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x1C) +#define CPSW_SL_RX_PRI_MAP(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x24) + +#define MDIO_OFFSET 0x1000 +#define MDIOCONTROL (MDIO_OFFSET + 0x04) +#define MDIOCTL_ENABLE (1 << 30) +#define MDIOCTL_FAULTENB (1 << 18) +#define MDIOLINKINTRAW (MDIO_OFFSET + 0x10) +#define MDIOLINKINTMASKED (MDIO_OFFSET + 0x14) +#define MDIOUSERACCESS0 (MDIO_OFFSET + 0x80) +#define MDIOUSERPHYSEL0 (MDIO_OFFSET + 0x84) +#define MDIOUSERACCESS1 (MDIO_OFFSET + 0x88) +#define MDIOUSERPHYSEL1 (MDIO_OFFSET + 0x8C) +#define MDIO_PHYSEL_LINKINTENB (1 << 6) +#define MDIO_PHYACCESS_GO (1U << 31) +#define MDIO_PHYACCESS_WRITE (1 << 30) +#define MDIO_PHYACCESS_ACK (1 << 29) + +#define CPSW_WR_OFFSET 0x1200 +#define CPSW_WR_SOFT_RESET (CPSW_WR_OFFSET + 0x04) +#define CPSW_WR_CONTROL (CPSW_WR_OFFSET + 0x08) +#define CPSW_WR_INT_CONTROL (CPSW_WR_OFFSET + 0x0c) +#define CPSW_WR_C_RX_THRESH_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x10) +#define CPSW_WR_C_RX_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x14) +#define CPSW_WR_C_TX_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x18) +#define CPSW_WR_C_MISC_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x1C) +#define CPSW_WR_C_RX_THRESH_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x40) +#define CPSW_WR_C_RX_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x44) +#define CPSW_WR_C_TX_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x48) +#define CPSW_WR_C_MISC_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x4C) +#define CPSW_WR_C_MISC_EVNT_PEND (1 << 4) +#define CPSW_WR_C_MISC_STAT_PEND (1 << 3) +#define CPSW_WR_C_MISC_HOST_PEND (1 << 2) +#define CPSW_WR_C_MISC_MDIOLINK (1 << 1) +#define CPSW_WR_C_MISC_MDIOUSER (1 << 0) + +#define CPSW_CPPI_RAM_OFFSET 0x2000 #define CPSW_CPPI_RAM_SIZE 0x2000 #define CPSW_MEMWINDOW_SIZE 0x4000 -#define CPDMA_BD_SOP (1<<15) -#define CPDMA_BD_EOP (1<<14) -#define CPDMA_BD_OWNER (1<<13) -#define CPDMA_BD_EOQ (1<<12) -#define CPDMA_BD_TDOWNCMPLT (1<<11) -#define CPDMA_BD_PKT_ERR_MASK (3<< 4) +#define CPDMA_BD_SOP (1 << 15) +#define CPDMA_BD_EOP (1 << 14) +#define CPDMA_BD_OWNER (1 << 13) +#define CPDMA_BD_EOQ (1 << 12) +#define CPDMA_BD_TDOWNCMPLT (1 << 11) +#define CPDMA_BD_PKT_ERR_MASK (3 << 4) +#define CPDMA_BD_TO_PORT (1 << 4) +#define CPDMA_BD_PORT_MASK 3 struct cpsw_cpdma_bd { volatile uint32_t next; diff --git a/sys/arm/ti/cpsw/if_cpswvar.h b/sys/arm/ti/cpsw/if_cpswvar.h index fbd7de5..adf2a37 100644 --- a/sys/arm/ti/cpsw/if_cpswvar.h +++ b/sys/arm/ti/cpsw/if_cpswvar.h @@ -29,19 +29,21 @@ #ifndef _IF_CPSWVAR_H #define _IF_CPSWVAR_H -#define CPSW_INTR_COUNT 4 +#define CPSW_PORTS 2 +#define CPSW_INTR_COUNT 4 /* MII BUS */ -#define CPSW_MIIBUS_RETRIES 5 -#define CPSW_MIIBUS_DELAY 1000 +#define CPSW_MIIBUS_RETRIES 5 +#define CPSW_MIIBUS_DELAY 1000 -#define CPSW_MAX_ALE_ENTRIES 1024 +#define CPSW_MAX_ALE_ENTRIES 1024 -#define CPSW_SYSCTL_COUNT 34 +#define CPSW_SYSCTL_COUNT 34 struct cpsw_slot { uint32_t bd_offset; /* Offset of corresponding BD within CPPI RAM. */ bus_dmamap_t dmamap; + struct ifnet *ifp; struct mbuf *mbuf; STAILQ_ENTRY(cpsw_slot) next; }; @@ -64,52 +66,43 @@ struct cpsw_queue { int hdp_offset; }; +struct cpsw_port { + device_t dev; + int phy; + int vlan; +}; + struct cpsw_softc { - struct ifnet *ifp; - phandle_t node; device_t dev; + int active_slave; + int debug; + int dualemac; + phandle_t node; struct bintime attach_uptime; /* system uptime when attach happened. */ - struct bintime init_uptime; /* system uptime when init happened. */ + struct cpsw_port port[2]; - /* TODO: We should set up a child structure for each port; - store mac, phy information, etc, in that structure. */ - uint8_t mac_addr[ETHER_ADDR_LEN]; + /* RX and TX buffer tracking */ + struct cpsw_queue rx, tx; - device_t miibus; - struct mii_data *mii; /* We expect 1 memory resource and 4 interrupts from the device tree. */ - struct resource *mem_res; int mem_rid; + struct resource *mem_res; struct resource *irq_res[CPSW_INTR_COUNT]; + void *ih_cookie[CPSW_INTR_COUNT]; - /* Interrupts get recorded here as we initialize them. */ - /* Interrupt teardown just walks this list. */ - struct { - struct resource *res; - void *ih_cookie; - const char *description; - } interrupts[CPSW_INTR_COUNT]; - int interrupt_count; + /* An mbuf full of nulls for TX padding. */ + bus_dmamap_t null_mbuf_dmamap; + struct mbuf *null_mbuf; + bus_addr_t null_mbuf_paddr; - uint32_t cpsw_if_flags; - int cpsw_media_status; + bus_dma_tag_t mbuf_dtag; struct { int resets; int timer; - struct callout callout; + struct callout callout; } watchdog; - bus_dma_tag_t mbuf_dtag; - - /* An mbuf full of nulls for TX padding. */ - bus_dmamap_t null_mbuf_dmamap; - struct mbuf *null_mbuf; - bus_addr_t null_mbuf_paddr; - - /* RX and TX buffer tracking */ - struct cpsw_queue rx, tx; - /* 64-bit versions of 32-bit hardware statistics counters */ uint64_t shadow_stats[CPSW_SYSCTL_COUNT]; @@ -123,4 +116,23 @@ struct cpsw_softc { struct cpsw_slots avail; }; +struct cpswp_softc { + device_t dev; + device_t miibus; + device_t pdev; + int media_status; + int unit; + int vlan; + struct bintime init_uptime; /* system uptime when init happened. */ + struct callout mii_callout; + struct cpsw_softc *swsc; + struct ifnet *ifp; + struct mii_data *mii; + struct mtx lock; + uint32_t if_flags; + uint32_t phy; + uint32_t phyaccess; + uint32_t physel; +}; + #endif /*_IF_CPSWVAR_H */ diff --git a/sys/arm/ti/files.ti b/sys/arm/ti/files.ti index d4daab1..6f571c6 100644 --- a/sys/arm/ti/files.ti +++ b/sys/arm/ti/files.ti @@ -18,6 +18,7 @@ arm/ti/ti_gpio.c optional gpio arm/ti/ti_gpio_if.m optional gpio arm/ti/ti_i2c.c optional ti_i2c arm/ti/ti_sdhci.c optional sdhci +arm/ti/ti_spi.c optional ti_spi dev/uart/uart_dev_ti8250.c optional uart dev/uart/uart_dev_ns8250.c optional uart diff --git a/sys/arm/ti/omap4/omap4_gpio.c b/sys/arm/ti/omap4/omap4_gpio.c index a261116..dd38f4a 100644 --- a/sys/arm/ti/omap4/omap4_gpio.c +++ b/sys/arm/ti/omap4/omap4_gpio.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$"); #include <sys/gpio.h> #include <machine/bus.h> +#include <machine/intr.h> #include <dev/fdt/fdt_common.h> #include <dev/ofw/ofw_bus.h> diff --git a/sys/arm/ti/omap4/omap4_wugen.c b/sys/arm/ti/omap4/omap4_wugen.c index 2b24eaf..f2bed61 100644 --- a/sys/arm/ti/omap4/omap4_wugen.c +++ b/sys/arm/ti/omap4/omap4_wugen.c @@ -57,44 +57,64 @@ struct omap4_wugen_sc { }; static int -omap4_wugen_register(device_t dev, struct intr_irqsrc *isrc, - boolean_t *is_percpu) +omap4_wugen_alloc_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) { struct omap4_wugen_sc *sc = device_get_softc(dev); - return (PIC_REGISTER(sc->sc_parent, isrc, is_percpu)); + return (PIC_ALLOC_INTR(sc->sc_parent, isrc, res, data)); } -static int -omap4_wugen_unregister(device_t dev, struct intr_irqsrc *isrc) +static void +omap4_wugen_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct omap4_wugen_sc *sc = device_get_softc(dev); - return (PIC_UNREGISTER(sc->sc_parent, isrc)); + PIC_DISABLE_INTR(sc->sc_parent, isrc); } static void -omap4_wugen_enable_source(device_t dev, struct intr_irqsrc *isrc) +omap4_wugen_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct omap4_wugen_sc *sc = device_get_softc(dev); - PIC_ENABLE_SOURCE(sc->sc_parent, isrc); + PIC_ENABLE_INTR(sc->sc_parent, isrc); } -static void -omap4_wugen_disable_source(device_t dev, struct intr_irqsrc *isrc) +static int +omap4_wugen_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) { struct omap4_wugen_sc *sc = device_get_softc(dev); - PIC_DISABLE_SOURCE(sc->sc_parent, isrc); + return (PIC_MAP_INTR(sc->sc_parent, data, isrcp)); } -static void -omap4_wugen_enable_intr(device_t dev, struct intr_irqsrc *isrc) +static int +omap4_wugen_release_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) { struct omap4_wugen_sc *sc = device_get_softc(dev); - PIC_ENABLE_INTR(sc->sc_parent, isrc); + return (PIC_RELEASE_INTR(sc->sc_parent, isrc, res, data)); +} + +static int +omap4_wugen_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct omap4_wugen_sc *sc = device_get_softc(dev); + + return (PIC_SETUP_INTR(sc->sc_parent, isrc, res, data)); +} + +static int +omap4_wugen_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct omap4_wugen_sc *sc = device_get_softc(dev); + + return (PIC_TEARDOWN_INTR(sc->sc_parent, isrc, res, data)); } static void @@ -124,11 +144,11 @@ omap4_wugen_post_filter(device_t dev, struct intr_irqsrc *isrc) #ifdef SMP static int -omap4_wugen_bind(device_t dev, struct intr_irqsrc *isrc) +omap4_wugen_bind_intr(device_t dev, struct intr_irqsrc *isrc) { struct omap4_wugen_sc *sc = device_get_softc(dev); - return (PIC_BIND(sc->sc_parent, isrc)); + return (PIC_BIND_INTR(sc->sc_parent, isrc)); } #endif @@ -207,16 +227,18 @@ static device_method_t omap4_wugen_methods[] = { DEVMETHOD(device_detach, omap4_wugen_detach), /* Interrupt controller interface */ - DEVMETHOD(pic_register, omap4_wugen_register), - DEVMETHOD(pic_unregister, omap4_wugen_unregister), - DEVMETHOD(pic_enable_source, omap4_wugen_enable_source), - DEVMETHOD(pic_disable_source, omap4_wugen_disable_source), + DEVMETHOD(pic_alloc_intr, omap4_wugen_alloc_intr), + DEVMETHOD(pic_disable_intr, omap4_wugen_disable_intr), DEVMETHOD(pic_enable_intr, omap4_wugen_enable_intr), + DEVMETHOD(pic_map_intr, omap4_wugen_map_intr), + DEVMETHOD(pic_release_intr, omap4_wugen_release_intr), + DEVMETHOD(pic_setup_intr, omap4_wugen_setup_intr), + DEVMETHOD(pic_teardown_intr, omap4_wugen_teardown_intr), DEVMETHOD(pic_pre_ithread, omap4_wugen_pre_ithread), DEVMETHOD(pic_post_ithread, omap4_wugen_post_ithread), DEVMETHOD(pic_post_filter, omap4_wugen_post_filter), #ifdef SMP - DEVMETHOD(pic_bind, omap4_wugen_bind), + DEVMETHOD(pic_bind_intr, omap4_wugen_bind_intr), #endif DEVMETHOD_END }; diff --git a/sys/arm/ti/ti_adc.c b/sys/arm/ti/ti_adc.c index d65afc6..f11091f 100644 --- a/sys/arm/ti/ti_adc.c +++ b/sys/arm/ti/ti_adc.c @@ -161,11 +161,9 @@ ti_adc_input_setup(struct ti_adc_softc *sc, int32_t ain) /* Set the negative voltage reference. */ val &= ~ADC_STEP_RFM_MSK; - val |= ADC_STEP_RFM_VREFN << ADC_STEP_RFM_SHIFT; /* Set the positive voltage reference. */ val &= ~ADC_STEP_RFP_MSK; - val |= ADC_STEP_RFP_VREFP << ADC_STEP_RFP_SHIFT; /* Set the samples average. */ val &= ~ADC_STEP_AVG_MSK; @@ -450,11 +448,9 @@ ti_adc_idlestep_init(struct ti_adc_softc *sc) /* Set the negative voltage reference. */ val &= ~ADC_STEP_RFM_MSK; - val |= ADC_STEP_RFM_VREFN << ADC_STEP_RFM_SHIFT; /* Set the positive voltage reference. */ val &= ~ADC_STEP_RFP_MSK; - val |= ADC_STEP_RFP_VREFP << ADC_STEP_RFP_SHIFT; /* Connect the input to VREFN. */ val &= ~ADC_STEP_INP_MSK; @@ -484,6 +480,11 @@ ti_adc_attach(device_t dev) sc = device_get_softc(dev); sc->sc_dev = dev; + /* Activate the ADC_TSC module. */ + err = ti_prcm_clk_enable(TSC_ADC_CLK); + if (err) + return (err); + rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); @@ -509,11 +510,6 @@ ti_adc_attach(device_t dev) return (ENXIO); } - /* Activate the ADC_TSC module. */ - err = ti_prcm_clk_enable(TSC_ADC_CLK); - if (err) - return (err); - /* Check the ADC revision. */ rev = ADC_READ4(sc, ADC_REVISION); device_printf(dev, diff --git a/sys/arm/ti/ti_gpio.c b/sys/arm/ti/ti_gpio.c index 2818bab..633fbf0 100644 --- a/sys/arm/ti/ti_gpio.c +++ b/sys/arm/ti/ti_gpio.c @@ -33,12 +33,15 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/bus.h> #include <sys/kernel.h> #include <sys/module.h> +#include <sys/proc.h> #include <sys/rman.h> #include <sys/lock.h> #include <sys/mutex.h> @@ -46,6 +49,7 @@ __FBSDID("$FreeBSD$"); #include <sys/interrupt.h> #include <machine/bus.h> +#include <machine/intr.h> #include <machine/resource.h> #include <arm/ti/ti_cpuid.h> @@ -62,6 +66,9 @@ __FBSDID("$FreeBSD$"); #include "gpio_if.h" #include "ti_gpio_if.h" +#ifdef ARM_INTRNG +#include "pic_if.h" +#endif #if !defined(SOC_OMAP4) && !defined(SOC_TI_AM335X) #error "Unknown SoC" @@ -72,12 +79,12 @@ __FBSDID("$FreeBSD$"); #define TI_GPIO_SYSCONFIG 0x0010 #define TI_GPIO_IRQSTATUS_RAW_0 0x0024 #define TI_GPIO_IRQSTATUS_RAW_1 0x0028 -#define TI_GPIO_IRQSTATUS_0 0x002C -#define TI_GPIO_IRQSTATUS_1 0x0030 -#define TI_GPIO_IRQSTATUS_SET_0 0x0034 -#define TI_GPIO_IRQSTATUS_SET_1 0x0038 -#define TI_GPIO_IRQSTATUS_CLR_0 0x003C -#define TI_GPIO_IRQSTATUS_CLR_1 0x0040 +#define TI_GPIO_IRQSTATUS_0 0x002C /* writing a 0 has no effect */ +#define TI_GPIO_IRQSTATUS_1 0x0030 /* writing a 0 has no effect */ +#define TI_GPIO_IRQSTATUS_SET_0 0x0034 /* writing a 0 has no effect */ +#define TI_GPIO_IRQSTATUS_SET_1 0x0038 /* writing a 0 has no effect */ +#define TI_GPIO_IRQSTATUS_CLR_0 0x003C /* writing a 0 has no effect */ +#define TI_GPIO_IRQSTATUS_CLR_1 0x0040 /* writing a 0 has no effect */ #define TI_GPIO_IRQWAKEN_0 0x0044 #define TI_GPIO_IRQWAKEN_1 0x0048 #define TI_GPIO_SYSSTATUS 0x0114 @@ -90,10 +97,10 @@ __FBSDID("$FreeBSD$"); #define TI_GPIO_OE 0x0134 #define TI_GPIO_DATAIN 0x0138 #define TI_GPIO_DATAOUT 0x013C -#define TI_GPIO_LEVELDETECT0 0x0140 -#define TI_GPIO_LEVELDETECT1 0x0144 -#define TI_GPIO_RISINGDETECT 0x0148 -#define TI_GPIO_FALLINGDETECT 0x014C +#define TI_GPIO_LEVELDETECT0 0x0140 /* RW register */ +#define TI_GPIO_LEVELDETECT1 0x0144 /* RW register */ +#define TI_GPIO_RISINGDETECT 0x0148 /* RW register */ +#define TI_GPIO_FALLINGDETECT 0x014C /* RW register */ #define TI_GPIO_DEBOUNCENABLE 0x0150 #define TI_GPIO_DEBOUNCINGTIME 0x0154 #define TI_GPIO_CLEARWKUPENA 0x0180 @@ -111,8 +118,14 @@ __FBSDID("$FreeBSD$"); #define PINS_PER_BANK 32 #define TI_GPIO_MASK(p) (1U << ((p) % PINS_PER_BANK)) +static int ti_gpio_intr(void *arg); static int ti_gpio_detach(device_t); +#ifdef ARM_INTRNG +static int ti_gpio_pic_attach(struct ti_gpio_softc *sc); +static int ti_gpio_pic_detach(struct ti_gpio_softc *sc); +#endif + static u_int ti_first_gpio_bank(void) { @@ -533,6 +546,7 @@ ti_gpio_pin_toggle(device_t dev, uint32_t pin) return (0); } +#ifndef ARM_INTRNG /** * ti_gpio_intr - ISR for all GPIO modules * @arg: the soft context pointer @@ -567,6 +581,7 @@ ti_gpio_intr(void *arg) return (FILTER_HANDLED); } +#endif static int ti_gpio_bank_init(device_t dev) @@ -640,7 +655,9 @@ static int ti_gpio_attach(device_t dev) { struct ti_gpio_softc *sc; +#ifndef ARM_INTRNG unsigned int i; +#endif int err; sc = device_get_softc(dev); @@ -679,6 +696,13 @@ ti_gpio_attach(device_t dev) return (ENXIO); } +#ifdef ARM_INTRNG + if (ti_gpio_pic_attach(sc) != 0) { + device_printf(dev, "WARNING: unable to attach PIC\n"); + ti_gpio_detach(dev); + return (ENXIO); + } +#else /* * Initialize the interrupt settings. The default is active-low * interrupts. @@ -699,7 +723,7 @@ ti_gpio_attach(device_t dev) sc->sc_mask_args = malloc(sizeof(struct ti_gpio_mask_arg) * sc->sc_maxpin, M_DEVBUF, M_WAITOK | M_ZERO); - +#endif /* We need to go through each block and ensure the clocks are running and * the module is enabled. It might be better to do this only when the * pins are configured which would result in less power used if the GPIO @@ -747,6 +771,10 @@ ti_gpio_detach(device_t dev) if (sc->sc_mem_res != NULL) ti_gpio_intr_clr(sc, 0xffffffff); gpiobus_detach_bus(dev); +#ifdef ARM_INTRNG + if (sc->sc_isrcs != NULL) + ti_gpio_pic_detach(sc); +#else if (sc->sc_events) free(sc->sc_events, M_DEVBUF); if (sc->sc_mask_args) @@ -755,6 +783,7 @@ ti_gpio_detach(device_t dev) free(sc->sc_irq_polarity, M_DEVBUF); if (sc->sc_irq_trigger) free(sc->sc_irq_trigger, M_DEVBUF); +#endif /* Release the memory and IRQ resources. */ if (sc->sc_irq_hdl) { bus_teardown_intr(dev, sc->sc_irq_res, @@ -769,6 +798,282 @@ ti_gpio_detach(device_t dev) return (0); } +#ifdef ARM_INTRNG +static inline void +ti_gpio_rwreg_set(struct ti_gpio_softc *sc, uint32_t reg, uint32_t mask) +{ + + ti_gpio_write_4(sc, reg, ti_gpio_read_4(sc, reg) | mask); +} + +static inline void +ti_gpio_rwreg_clr(struct ti_gpio_softc *sc, uint32_t reg, uint32_t mask) +{ + + ti_gpio_write_4(sc, reg, ti_gpio_read_4(sc, reg) & ~mask); +} + +static inline void +ti_gpio_isrc_mask(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi) +{ + + /* Writing a 0 has no effect. */ + ti_gpio_intr_clr(sc, tgi->tgi_mask); +} + +static inline void +ti_gpio_isrc_unmask(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi) +{ + + /* Writing a 0 has no effect. */ + ti_gpio_intr_set(sc, tgi->tgi_mask); +} + +static inline void +ti_gpio_isrc_eoi(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi) +{ + + /* Writing a 0 has no effect. */ + ti_gpio_intr_ack(sc, tgi->tgi_mask); +} + +static inline bool +ti_gpio_isrc_is_level(struct ti_gpio_irqsrc *tgi) +{ + + return (tgi->tgi_cfgreg == TI_GPIO_LEVELDETECT0 || + tgi->tgi_cfgreg == TI_GPIO_LEVELDETECT1); +} + +static int +ti_gpio_intr(void *arg) +{ + u_int irq; + uint32_t reg; + struct ti_gpio_softc *sc; + struct trapframe *tf; + struct ti_gpio_irqsrc *tgi; + + sc = (struct ti_gpio_softc *)arg; + tf = curthread->td_intr_frame; + + reg = ti_gpio_intr_status(sc); + for (irq = 0; irq < sc->sc_maxpin; irq++) { + tgi = &sc->sc_isrcs[irq]; + if ((reg & tgi->tgi_mask) == 0) + continue; + if (!ti_gpio_isrc_is_level(tgi)) + ti_gpio_isrc_eoi(sc, tgi); + if (intr_isrc_dispatch(&tgi->tgi_isrc, tf) != 0) { + ti_gpio_isrc_mask(sc, tgi); + if (ti_gpio_isrc_is_level(tgi)) + ti_gpio_isrc_eoi(sc, tgi); + device_printf(sc->sc_dev, "Stray irq %u disabled\n", + irq); + } + } + return (FILTER_HANDLED); +} + +static int +ti_gpio_pic_attach(struct ti_gpio_softc *sc) +{ + int error; + uint32_t irq; + const char *name; + + sc->sc_isrcs = malloc(sizeof(*sc->sc_isrcs) * sc->sc_maxpin, M_DEVBUF, + M_WAITOK | M_ZERO); + + name = device_get_nameunit(sc->sc_dev); + for (irq = 0; irq < sc->sc_maxpin; irq++) { + sc->sc_isrcs[irq].tgi_irq = irq; + sc->sc_isrcs[irq].tgi_mask = TI_GPIO_MASK(irq); + sc->sc_isrcs[irq].tgi_cfgreg = 0; + + error = intr_isrc_register(&sc->sc_isrcs[irq].tgi_isrc, + sc->sc_dev, 0, "%s,%u", name, irq); + if (error != 0) + return (error); /* XXX deregister ISRCs */ + } + return (intr_pic_register(sc->sc_dev, + OF_xref_from_node(ofw_bus_get_node(sc->sc_dev)))); +} + +static int +ti_gpio_pic_detach(struct ti_gpio_softc *sc) +{ + + /* + * There has not been established any procedure yet + * how to detach PIC from living system correctly. + */ + device_printf(sc->sc_dev, "%s: not implemented yet\n", __func__); + return (EBUSY); +} + +static void +ti_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct ti_gpio_softc *sc = device_get_softc(dev); + struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc; + + ti_gpio_isrc_mask(sc, tgi); +} + +static void +ti_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct ti_gpio_softc *sc = device_get_softc(dev); + struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc; + + arm_irq_memory_barrier(tgi->tgi_irq); + ti_gpio_isrc_unmask(sc, tgi); +} + +static int +ti_gpio_pic_map_fdt(struct ti_gpio_softc *sc, u_int ncells, pcell_t *cells, + u_int *irqp, uint32_t *regp) +{ + uint32_t reg; + + /* + * The first cell is the interrupt number. + * The second cell is used to specify flags: + * bits[3:0] trigger type and level flags: + * 1 = low-to-high edge triggered. + * 2 = high-to-low edge triggered. + * 4 = active high level-sensitive. + * 8 = active low level-sensitive. + */ + if (ncells != 2 || cells[0] >= sc->sc_maxpin) + return (EINVAL); + + /* + * All interrupt types could be set for an interrupt at one moment. + * At least, the combination of 'low-to-high' and 'high-to-low' edge + * triggered interrupt types can make a sense. However, no combo is + * supported now. + */ + if (cells[1] == 1) + reg = TI_GPIO_RISINGDETECT; + else if (cells[1] == 2) + reg = TI_GPIO_FALLINGDETECT; + else if (cells[1] == 4) + reg = TI_GPIO_LEVELDETECT1; + else if (cells[1] == 8) + reg = TI_GPIO_LEVELDETECT0; + else + return (EINVAL); + + *irqp = cells[0]; + if (regp != NULL) + *regp = reg; + return (0); +} + +static int +ti_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + int error; + u_int irq; + struct ti_gpio_softc *sc; + + if (data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + + sc = device_get_softc(dev); + error = ti_gpio_pic_map_fdt(sc, data->fdt.ncells, data->fdt.cells, &irq, + NULL); + if (error == 0) + *isrcp = &sc->sc_isrcs[irq].tgi_isrc; + return (error); +} + +static void +ti_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct ti_gpio_softc *sc = device_get_softc(dev); + struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc; + + if (ti_gpio_isrc_is_level(tgi)) + ti_gpio_isrc_eoi(sc, tgi); +} + +static void +ti_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + + ti_gpio_pic_enable_intr(dev, isrc); +} + +static void +ti_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct ti_gpio_softc *sc = device_get_softc(dev); + struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc; + + ti_gpio_isrc_mask(sc, tgi); + if (ti_gpio_isrc_is_level(tgi)) + ti_gpio_isrc_eoi(sc, tgi); +} + +static int +ti_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + u_int irq; + uint32_t cfgreg; + struct ti_gpio_softc *sc; + struct ti_gpio_irqsrc *tgi; + + if (data == NULL || data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + + sc = device_get_softc(dev); + tgi = (struct ti_gpio_irqsrc *)isrc; + + /* Get and check config for an interrupt. */ + if (ti_gpio_pic_map_fdt(sc, data->fdt.ncells, data->fdt.cells, &irq, + &cfgreg) != 0 || tgi->tgi_irq != irq) + return (EINVAL); + + /* + * If this is a setup for another handler, + * only check that its configuration match. + */ + if (isrc->isrc_handlers != 0) + return (tgi->tgi_cfgreg == cfgreg ? 0 : EINVAL); + + TI_GPIO_LOCK(sc); + ti_gpio_rwreg_clr(sc, TI_GPIO_RISINGDETECT, tgi->tgi_mask); + ti_gpio_rwreg_clr(sc, TI_GPIO_FALLINGDETECT, tgi->tgi_mask); + ti_gpio_rwreg_clr(sc, TI_GPIO_LEVELDETECT1, tgi->tgi_mask); + ti_gpio_rwreg_clr(sc, TI_GPIO_LEVELDETECT0, tgi->tgi_mask); + tgi->tgi_cfgreg = cfgreg; + ti_gpio_rwreg_set(sc, cfgreg, tgi->tgi_mask); + TI_GPIO_UNLOCK(sc); + return (0); +} + +static int +ti_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct ti_gpio_softc *sc = device_get_softc(dev); + struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc; + + if (isrc->isrc_handlers == 0) { + TI_GPIO_LOCK(sc); + ti_gpio_rwreg_clr(sc, tgi->tgi_cfgreg, tgi->tgi_mask); + tgi->tgi_cfgreg = 0; + TI_GPIO_UNLOCK(sc); + } + return (0); +} + +#else static uint32_t ti_gpio_intr_reg(struct ti_gpio_softc *sc, int irq) { @@ -970,6 +1275,7 @@ ti_gpio_teardown_intr(device_t dev, device_t child, struct resource *ires, return (err); } +#endif static phandle_t ti_gpio_get_node(device_t bus, device_t dev) @@ -994,12 +1300,24 @@ static device_method_t ti_gpio_methods[] = { DEVMETHOD(gpio_pin_set, ti_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, ti_gpio_pin_toggle), +#ifdef ARM_INTRNG + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, ti_gpio_pic_disable_intr), + DEVMETHOD(pic_enable_intr, ti_gpio_pic_enable_intr), + DEVMETHOD(pic_map_intr, ti_gpio_pic_map_intr), + DEVMETHOD(pic_setup_intr, ti_gpio_pic_setup_intr), + DEVMETHOD(pic_teardown_intr, ti_gpio_pic_teardown_intr), + DEVMETHOD(pic_post_filter, ti_gpio_pic_post_filter), + DEVMETHOD(pic_post_ithread, ti_gpio_pic_post_ithread), + DEVMETHOD(pic_pre_ithread, ti_gpio_pic_pre_ithread), +#else /* Bus interface */ DEVMETHOD(bus_activate_resource, ti_gpio_activate_resource), DEVMETHOD(bus_deactivate_resource, ti_gpio_deactivate_resource), DEVMETHOD(bus_config_intr, ti_gpio_config_intr), DEVMETHOD(bus_setup_intr, ti_gpio_setup_intr), DEVMETHOD(bus_teardown_intr, ti_gpio_teardown_intr), +#endif /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, ti_gpio_get_node), diff --git a/sys/arm/ti/ti_gpio.h b/sys/arm/ti/ti_gpio.h index 6bd53c8..f16728b 100644 --- a/sys/arm/ti/ti_gpio.h +++ b/sys/arm/ti/ti_gpio.h @@ -39,10 +39,19 @@ */ #define MAX_GPIO_INTRS 8 +#ifndef ARM_INTRNG struct ti_gpio_mask_arg { void *softc; int pin; }; +#else +struct ti_gpio_irqsrc { + struct intr_irqsrc tgi_isrc; + u_int tgi_irq; + uint32_t tgi_mask; + uint32_t tgi_cfgreg; +}; +#endif /** * Structure that stores the driver context. @@ -52,11 +61,11 @@ struct ti_gpio_mask_arg { struct ti_gpio_softc { device_t sc_dev; device_t sc_busdev; - +#ifndef ARM_INTRNG /* Interrupt trigger type and level. */ enum intr_trigger *sc_irq_trigger; enum intr_polarity *sc_irq_polarity; - +#endif int sc_bank; int sc_maxpin; struct mtx sc_mtx; @@ -65,11 +74,13 @@ struct ti_gpio_softc { struct resource *sc_mem_res; int sc_irq_rid; struct resource *sc_irq_res; - +#ifndef ARM_INTRNG /* Interrupt events. */ struct intr_event **sc_events; struct ti_gpio_mask_arg *sc_mask_args; - +#else + struct ti_gpio_irqsrc *sc_isrcs; +#endif /* The handle for the register IRQ handlers. */ void *sc_irq_hdl; }; diff --git a/sys/arm/ti/ti_hwmods.c b/sys/arm/ti/ti_hwmods.c index 1488e55..db96235 100644 --- a/sys/arm/ti/ti_hwmods.c +++ b/sys/arm/ti/ti_hwmods.c @@ -76,6 +76,9 @@ struct hwmod ti_hwmods[] = { {"epwmss1", PWMSS1_CLK}, {"epwmss2", PWMSS2_CLK}, + {"spi0", SPI0_CLK}, + {"spi1", SPI1_CLK}, + {"timer1", TIMER1_CLK}, {"timer2", TIMER2_CLK}, {"timer3", TIMER3_CLK}, diff --git a/sys/arm/ti/ti_prcm.h b/sys/arm/ti/ti_prcm.h index c40439a..61b6960 100644 --- a/sys/arm/ti/ti_prcm.h +++ b/sys/arm/ti/ti_prcm.h @@ -158,6 +158,10 @@ typedef enum { /* RTC module */ RTC_CLK = 1900, + + /* McSPI */ + SPI0_CLK = 2000, + SPI1_CLK, } clk_ident_t; /* diff --git a/sys/arm/ti/ti_sdhci.c b/sys/arm/ti/ti_sdhci.c index 6c310b6..a24e693 100644 --- a/sys/arm/ti/ti_sdhci.c +++ b/sys/arm/ti/ti_sdhci.c @@ -722,3 +722,4 @@ static driver_t ti_sdhci_driver = { DRIVER_MODULE(sdhci_ti, simplebus, ti_sdhci_driver, ti_sdhci_devclass, 0, 0); MODULE_DEPEND(sdhci_ti, sdhci, 1, 1, 1); DRIVER_MODULE(mmc, sdhci_ti, mmc_driver, mmc_devclass, NULL, NULL); +MODULE_DEPEND(sdhci_ti, mmc, 1, 1, 1); diff --git a/sys/arm/ti/ti_spi.c b/sys/arm/ti/ti_spi.c new file mode 100644 index 0000000..e35f365 --- /dev/null +++ b/sys/arm/ti/ti_spi.c @@ -0,0 +1,582 @@ +/*- + * 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. + * + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> + +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/sysctl.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <machine/intr.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/spibus/spi.h> +#include <dev/spibus/spibusvar.h> + +#include <arm/ti/ti_prcm.h> +#include <arm/ti/ti_hwmods.h> +#include <arm/ti/ti_spireg.h> +#include <arm/ti/ti_spivar.h> + +#include "spibus_if.h" + +static void ti_spi_intr(void *); +static int ti_spi_detach(device_t); + +#undef TI_SPI_DEBUG +#ifdef TI_SPI_DEBUG +#define IRQSTATUSBITS \ + "\020\1TX0_EMPTY\2TX0_UNDERFLOW\3RX0_FULL\4RX0_OVERFLOW" \ + "\5TX1_EMPTY\6TX1_UNDERFLOW\7RX1_FULL\11TX2_EMPTY" \ + "\12TX1_UNDERFLOW\13RX2_FULL\15TX3_EMPTY\16TX3_UNDERFLOW" \ + "\17RX3_FULL\22EOW" +#define CONFBITS \ + "\020\1PHA\2POL\7EPOL\17DMAW\20DMAR\21DPE0\22DPE1\23IS" \ + "\24TURBO\25FORCE\30SBE\31SBPOL\34FFEW\35FFER\36CLKG" +#define STATBITS \ + "\020\1RXS\2TXS\3EOT\4TXFFE\5TXFFF\6RXFFE\7RXFFFF" +#define MODULCTRLBITS \ + "\020\1SINGLE\2NOSPIEN\3SLAVE\4SYST\10MOA\11FDAA" +#define CTRLBITS \ + "\020\1ENABLED" + +static void +ti_spi_printr(device_t dev) +{ + int clk, conf, ctrl, div, i, j, wl; + struct ti_spi_softc *sc; + uint32_t reg; + + sc = device_get_softc(dev); + reg = TI_SPI_READ(sc, MCSPI_SYSCONFIG); + device_printf(dev, "SYSCONFIG: %#x\n", reg); + reg = TI_SPI_READ(sc, MCSPI_SYSSTATUS); + device_printf(dev, "SYSSTATUS: %#x\n", reg); + reg = TI_SPI_READ(sc, MCSPI_IRQSTATUS); + device_printf(dev, "IRQSTATUS: 0x%b\n", reg, IRQSTATUSBITS); + reg = TI_SPI_READ(sc, MCSPI_IRQENABLE); + device_printf(dev, "IRQENABLE: 0x%b\n", reg, IRQSTATUSBITS); + reg = TI_SPI_READ(sc, MCSPI_MODULCTRL); + device_printf(dev, "MODULCTRL: 0x%b\n", reg, MODULCTRLBITS); + for (i = 0; i < sc->sc_numcs; i++) { + ctrl = TI_SPI_READ(sc, MCSPI_CTRL_CH(i)); + conf = TI_SPI_READ(sc, MCSPI_CONF_CH(i)); + device_printf(dev, "CH%dCONF: 0x%b\n", i, conf, CONFBITS); + if (conf & MCSPI_CONF_CLKG) { + div = (conf >> MCSPI_CONF_CLK_SHIFT) & MCSPI_CONF_CLK_MSK; + div |= ((ctrl >> MCSPI_CTRL_EXTCLK_SHIFT) & MCSPI_CTRL_EXTCLK_MSK) << 4; + } else { + div = 1; + j = (conf >> MCSPI_CONF_CLK_SHIFT) & MCSPI_CONF_CLK_MSK; + while (j-- > 0) + div <<= 1; + } + clk = TI_SPI_GCLK / div; + wl = ((conf >> MCSPI_CONF_WL_SHIFT) & MCSPI_CONF_WL_MSK) + 1; + device_printf(dev, "wordlen: %-2d clock: %d\n", wl, clk); + reg = TI_SPI_READ(sc, MCSPI_STAT_CH(i)); + device_printf(dev, "CH%dSTAT: 0x%b\n", i, reg, STATBITS); + device_printf(dev, "CH%dCTRL: 0x%b\n", i, ctrl, CTRLBITS); + } + reg = TI_SPI_READ(sc, MCSPI_XFERLEVEL); + device_printf(dev, "XFERLEVEL: %#x\n", reg); +} +#endif + +static void +ti_spi_set_clock(struct ti_spi_softc *sc, int ch, int freq) +{ + uint32_t clkdiv, conf, div, extclk, reg; + + clkdiv = TI_SPI_GCLK / freq; + if (clkdiv > MCSPI_EXTCLK_MSK) { + extclk = 0; + clkdiv = 0; + div = 1; + while (TI_SPI_GCLK / div > freq && clkdiv <= 0xf) { + clkdiv++; + div <<= 1; + } + conf = clkdiv << MCSPI_CONF_CLK_SHIFT; + } else { + extclk = clkdiv >> 4; + clkdiv &= MCSPI_CONF_CLK_MSK; + conf = MCSPI_CONF_CLKG | clkdiv << MCSPI_CONF_CLK_SHIFT; + } + + reg = TI_SPI_READ(sc, MCSPI_CTRL_CH(ch)); + reg &= ~(MCSPI_CTRL_EXTCLK_MSK << MCSPI_CTRL_EXTCLK_SHIFT); + reg |= extclk << MCSPI_CTRL_EXTCLK_SHIFT; + TI_SPI_WRITE(sc, MCSPI_CTRL_CH(ch), reg); + + reg = TI_SPI_READ(sc, MCSPI_CONF_CH(ch)); + reg &= ~(MCSPI_CONF_CLKG | MCSPI_CONF_CLK_MSK << MCSPI_CONF_CLK_SHIFT); + TI_SPI_WRITE(sc, MCSPI_CONF_CH(ch), reg | conf); +} + +static int +ti_spi_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + if (!ofw_bus_is_compatible(dev, "ti,omap4-mcspi")) + return (ENXIO); + + device_set_desc(dev, "TI McSPI controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +ti_spi_attach(device_t dev) +{ + int clk_id, err, i, rid, timeout; + struct ti_spi_softc *sc; + uint32_t rev; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + /* + * Get the MMCHS device id from FDT. If it's not there use the newbus + * unit number (which will work as long as the devices are in order and + * none are skipped in the fdt). Note that this is a property we made + * up and added in freebsd, it doesn't exist in the published bindings. + */ + clk_id = ti_hwmods_get_clock(dev); + if (clk_id == INVALID_CLK_IDENT) { + device_printf(dev, + "failed to get clock based on hwmods property\n"); + return (EINVAL); + } + + /* Activate the McSPI module. */ + err = ti_prcm_clk_enable(clk_id); + if (err) { + device_printf(dev, "Error: failed to activate source clock\n"); + return (err); + } + + /* Get the number of available channels. */ + if ((OF_getencprop(ofw_bus_get_node(dev), "ti,spi-num-cs", + &sc->sc_numcs, sizeof(sc->sc_numcs))) <= 0) { + sc->sc_numcs = 2; + } + + rid = 0; + sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (!sc->sc_mem_res) { + device_printf(dev, "cannot allocate memory window\n"); + return (ENXIO); + } + + sc->sc_bst = rman_get_bustag(sc->sc_mem_res); + sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); + + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (!sc->sc_irq_res) { + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); + device_printf(dev, "cannot allocate interrupt\n"); + return (ENXIO); + } + + /* Hook up our interrupt handler. */ + if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, ti_spi_intr, sc, &sc->sc_intrhand)) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); + device_printf(dev, "cannot setup the interrupt handler\n"); + return (ENXIO); + } + + mtx_init(&sc->sc_mtx, "ti_spi", NULL, MTX_DEF); + + /* Issue a softreset to the controller */ + TI_SPI_WRITE(sc, MCSPI_SYSCONFIG, MCSPI_SYSCONFIG_SOFTRESET); + timeout = 1000; + while (!(TI_SPI_READ(sc, MCSPI_SYSSTATUS) & + MCSPI_SYSSTATUS_RESETDONE)) { + if (--timeout == 0) { + device_printf(dev, + "Error: Controller reset operation timed out\n"); + ti_spi_detach(dev); + return (ENXIO); + } + DELAY(100); + } + + /* Print the McSPI module revision. */ + rev = TI_SPI_READ(sc, MCSPI_REVISION); + device_printf(dev, + "scheme: %#x func: %#x rtl: %d rev: %d.%d custom rev: %d\n", + (rev >> MCSPI_REVISION_SCHEME_SHIFT) & MCSPI_REVISION_SCHEME_MSK, + (rev >> MCSPI_REVISION_FUNC_SHIFT) & MCSPI_REVISION_FUNC_MSK, + (rev >> MCSPI_REVISION_RTL_SHIFT) & MCSPI_REVISION_RTL_MSK, + (rev >> MCSPI_REVISION_MAJOR_SHIFT) & MCSPI_REVISION_MAJOR_MSK, + (rev >> MCSPI_REVISION_MINOR_SHIFT) & MCSPI_REVISION_MINOR_MSK, + (rev >> MCSPI_REVISION_CUSTOM_SHIFT) & MCSPI_REVISION_CUSTOM_MSK); + + /* Set Master mode, single channel. */ + TI_SPI_WRITE(sc, MCSPI_MODULCTRL, MCSPI_MODULCTRL_SINGLE); + + /* Clear pending interrupts and disable interrupts. */ + TI_SPI_WRITE(sc, MCSPI_IRQENABLE, 0x0); + TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, 0xffff); + + for (i = 0; i < sc->sc_numcs; i++) { + /* + * Default to SPI mode 0, CS active low, 8 bits word length and + * 500kHz clock. + */ + TI_SPI_WRITE(sc, MCSPI_CONF_CH(i), + MCSPI_CONF_DPE0 | MCSPI_CONF_EPOL | + (8 - 1) << MCSPI_CONF_WL_SHIFT); + /* Set initial clock - 500kHz. */ + ti_spi_set_clock(sc, i, 500000); + } + +#ifdef TI_SPI_DEBUG + ti_spi_printr(dev); +#endif + + device_add_child(dev, "spibus", -1); + + return (bus_generic_attach(dev)); +} + +static int +ti_spi_detach(device_t dev) +{ + struct ti_spi_softc *sc; + + sc = device_get_softc(dev); + + /* Clear pending interrupts and disable interrupts. */ + TI_SPI_WRITE(sc, MCSPI_IRQENABLE, 0); + TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, 0xffff); + + /* Reset controller. */ + TI_SPI_WRITE(sc, MCSPI_SYSCONFIG, MCSPI_SYSCONFIG_SOFTRESET); + + bus_generic_detach(dev); + + mtx_destroy(&sc->sc_mtx); + if (sc->sc_intrhand) + bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); + if (sc->sc_irq_res) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); + if (sc->sc_mem_res) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); + + return (0); +} + +static int +ti_spi_fill_fifo(struct ti_spi_softc *sc) +{ + int bytes, timeout; + struct spi_command *cmd; + uint32_t written; + uint8_t *data; + + cmd = sc->sc_cmd; + bytes = min(sc->sc_len - sc->sc_written, sc->sc_fifolvl); + while (bytes-- > 0) { + data = (uint8_t *)cmd->tx_cmd; + written = sc->sc_written++; + if (written >= cmd->tx_cmd_sz) { + data = (uint8_t *)cmd->tx_data; + written -= cmd->tx_cmd_sz; + } + if (sc->sc_fifolvl == 1) { + /* FIFO disabled. */ + timeout = 1000; + while (--timeout > 0 && (TI_SPI_READ(sc, + MCSPI_STAT_CH(sc->sc_cs)) & MCSPI_STAT_TXS) == 0) { + DELAY(100); + } + if (timeout == 0) + return (-1); + } + TI_SPI_WRITE(sc, MCSPI_TX_CH(sc->sc_cs), data[written]); + } + + return (0); +} + +static int +ti_spi_drain_fifo(struct ti_spi_softc *sc) +{ + int bytes, timeout; + struct spi_command *cmd; + uint32_t read; + uint8_t *data; + + cmd = sc->sc_cmd; + bytes = min(sc->sc_len - sc->sc_read, sc->sc_fifolvl); + while (bytes-- > 0) { + data = (uint8_t *)cmd->rx_cmd; + read = sc->sc_read++; + if (read >= cmd->rx_cmd_sz) { + data = (uint8_t *)cmd->rx_data; + read -= cmd->rx_cmd_sz; + } + if (sc->sc_fifolvl == 1) { + /* FIFO disabled. */ + timeout = 1000; + while (--timeout > 0 && (TI_SPI_READ(sc, + MCSPI_STAT_CH(sc->sc_cs)) & MCSPI_STAT_RXS) == 0) { + DELAY(100); + } + if (timeout == 0) + return (-1); + } + data[read] = TI_SPI_READ(sc, MCSPI_RX_CH(sc->sc_cs)); + } + + return (0); +} + +static void +ti_spi_intr(void *arg) +{ + int eow; + struct ti_spi_softc *sc; + uint32_t status; + + eow = 0; + sc = (struct ti_spi_softc *)arg; + TI_SPI_LOCK(sc); + status = TI_SPI_READ(sc, MCSPI_IRQSTATUS); + + /* + * No new TX_empty or RX_full event will be asserted while the CPU has + * not performed the number of writes or reads defined by + * MCSPI_XFERLEVEL[AEL] and MCSPI_XFERLEVEL[AFL]. It is responsibility + * of CPU perform the right number of writes and reads. + */ + if (status & MCSPI_IRQ_TX0_EMPTY) + ti_spi_fill_fifo(sc); + if (status & MCSPI_IRQ_RX0_FULL) + ti_spi_drain_fifo(sc); + + if (status & MCSPI_IRQ_EOW) + eow = 1; + + /* Clear interrupt status. */ + TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, status); + + /* Check for end of transfer. */ + if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len) { + sc->sc_flags |= TI_SPI_DONE; + wakeup(sc->sc_dev); + } + + TI_SPI_UNLOCK(sc); +} + +static int +ti_spi_pio_transfer(struct ti_spi_softc *sc) +{ + + while (sc->sc_len - sc->sc_written > 0) { + if (ti_spi_fill_fifo(sc) == -1) + return (EIO); + if (ti_spi_drain_fifo(sc) == -1) + return (EIO); + } + + return (0); +} + +static int +ti_spi_gcd(int a, int b) +{ + int m; + + while ((m = a % b) != 0) { + a = b; + b = m; + } + + return (b); +} + +static int +ti_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) +{ + int cs, err; + struct ti_spi_softc *sc; + uint32_t reg; + + sc = device_get_softc(dev); + + KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, + ("TX/RX command sizes should be equal")); + KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, + ("TX/RX data sizes should be equal")); + + /* Get the proper chip select for this child. */ + spibus_get_cs(child, &cs); + if (cs < 0 || cs > sc->sc_numcs) { + device_printf(dev, "Invalid chip select %d requested by %s\n", + cs, device_get_nameunit(child)); + return (EINVAL); + } + + TI_SPI_LOCK(sc); + + /* If the controller is in use wait until it is available. */ + while (sc->sc_flags & TI_SPI_BUSY) + mtx_sleep(dev, &sc->sc_mtx, 0, "ti_spi", 0); + + /* Now we have control over SPI controller. */ + sc->sc_flags = TI_SPI_BUSY; + + /* Save the SPI command data. */ + sc->sc_cs = cs; + sc->sc_cmd = cmd; + sc->sc_read = 0; + sc->sc_written = 0; + sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz; + sc->sc_fifolvl = ti_spi_gcd(sc->sc_len, TI_SPI_FIFOSZ); + if (sc->sc_fifolvl < 2 || sc->sc_len > 0xffff) + sc->sc_fifolvl = 1; /* FIFO disabled. */ + /* Disable FIFO for now. */ + sc->sc_fifolvl = 1; + + /* Use a safe clock - 500kHz. */ + ti_spi_set_clock(sc, sc->sc_cs, 500000); + + /* Disable the FIFO. */ + TI_SPI_WRITE(sc, MCSPI_XFERLEVEL, 0); + + /* 8 bits word, d0 miso, d1 mosi, mode 0 and CS active low. */ + reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs)); + reg &= ~(MCSPI_CONF_FFER | MCSPI_CONF_FFEW | MCSPI_CONF_SBPOL | + MCSPI_CONF_SBE | MCSPI_CONF_TURBO | MCSPI_CONF_IS | + MCSPI_CONF_DPE1 | MCSPI_CONF_DPE0 | MCSPI_CONF_DMAR | + MCSPI_CONF_DMAW | MCSPI_CONF_EPOL); + reg |= MCSPI_CONF_DPE0 | MCSPI_CONF_EPOL | MCSPI_CONF_WL8BITS; + TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg); + +#if 0 + /* Enable channel interrupts. */ + reg = TI_SPI_READ(sc, MCSPI_IRQENABLE); + reg |= 0xf; + TI_SPI_WRITE(sc, MCSPI_IRQENABLE, reg); +#endif + + /* Start the transfer. */ + reg = TI_SPI_READ(sc, MCSPI_CTRL_CH(sc->sc_cs)); + TI_SPI_WRITE(sc, MCSPI_CTRL_CH(sc->sc_cs), reg | MCSPI_CTRL_ENABLE); + + /* Force CS on. */ + reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs)); + TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg |= MCSPI_CONF_FORCE); + + err = 0; + if (sc->sc_fifolvl == 1) + err = ti_spi_pio_transfer(sc); + + /* Force CS off. */ + reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs)); + reg &= ~MCSPI_CONF_FORCE; + TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg); + + /* Disable IRQs. */ + reg = TI_SPI_READ(sc, MCSPI_IRQENABLE); + reg &= ~0xf; + TI_SPI_WRITE(sc, MCSPI_IRQENABLE, reg); + TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, 0xf); + + /* Disable the SPI channel. */ + reg = TI_SPI_READ(sc, MCSPI_CTRL_CH(sc->sc_cs)); + reg &= ~MCSPI_CTRL_ENABLE; + TI_SPI_WRITE(sc, MCSPI_CTRL_CH(sc->sc_cs), reg); + + /* Disable FIFO. */ + reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs)); + reg &= ~(MCSPI_CONF_FFER | MCSPI_CONF_FFEW); + TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg); + + /* Release the controller and wakeup the next thread waiting for it. */ + sc->sc_flags = 0; + wakeup_one(dev); + TI_SPI_UNLOCK(sc); + + return (err); +} + +static phandle_t +ti_spi_get_node(device_t bus, device_t dev) +{ + + /* Share controller node with spibus. */ + return (ofw_bus_get_node(bus)); +} + +static device_method_t ti_spi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ti_spi_probe), + DEVMETHOD(device_attach, ti_spi_attach), + DEVMETHOD(device_detach, ti_spi_detach), + + /* SPI interface */ + DEVMETHOD(spibus_transfer, ti_spi_transfer), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, ti_spi_get_node), + + DEVMETHOD_END +}; + +static devclass_t ti_spi_devclass; + +static driver_t ti_spi_driver = { + "spi", + ti_spi_methods, + sizeof(struct ti_spi_softc), +}; + +DRIVER_MODULE(ti_spi, simplebus, ti_spi_driver, ti_spi_devclass, 0, 0); diff --git a/sys/arm/ti/ti_spireg.h b/sys/arm/ti/ti_spireg.h new file mode 100644 index 0000000..f31f55e --- /dev/null +++ b/sys/arm/ti/ti_spireg.h @@ -0,0 +1,97 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#ifndef _TI_SPIREG_H_ +#define _TI_SPIREG_H_ + +#define TI_SPI_GCLK 48000000U +#define TI_SPI_FIFOSZ 32 +#define MCSPI_REVISION 0x0 +#define MCSPI_REVISION_SCHEME_SHIFT 30 +#define MCSPI_REVISION_SCHEME_MSK 0x3 +#define MCSPI_REVISION_FUNC_SHIFT 16 +#define MCSPI_REVISION_FUNC_MSK 0xfff +#define MCSPI_REVISION_RTL_SHIFT 11 +#define MCSPI_REVISION_RTL_MSK 0x1f +#define MCSPI_REVISION_MAJOR_SHIFT 8 +#define MCSPI_REVISION_MAJOR_MSK 0x7 +#define MCSPI_REVISION_CUSTOM_SHIFT 6 +#define MCSPI_REVISION_CUSTOM_MSK 0x3 +#define MCSPI_REVISION_MINOR_SHIFT 0 +#define MCSPI_REVISION_MINOR_MSK 0x3f +#define MCSPI_SYSCONFIG 0x110 +#define MCSPI_SYSCONFIG_SOFTRESET (1 << 1) +#define MCSPI_SYSSTATUS 0x114 +#define MCSPI_SYSSTATUS_RESETDONE (1 << 0) +#define MCSPI_MODULCTRL 0x128 +#define MCSPI_MODULCTRL_SLAVE (1 << 2) +#define MCSPI_MODULCTRL_SINGLE (1 << 0) +#define MCSPI_IRQSTATUS 0x118 +#define MCSPI_IRQENABLE 0x11c +#define MCSPI_IRQ_EOW (1 << 17) +#define MCSPI_IRQ_RX0_OVERFLOW (1 << 3) +#define MCSPI_IRQ_RX0_FULL (1 << 2) +#define MCSPI_IRQ_TX0_UNDERFLOW (1 << 1) +#define MCSPI_IRQ_TX0_EMPTY (1 << 0) +#define MCSPI_CONF_CH(_c) (0x12c + 0x14 * (_c)) +#define MCSPI_CONF_CLKG (1 << 29) +#define MCSPI_CONF_FFER (1 << 28) +#define MCSPI_CONF_FFEW (1 << 27) +#define MCSPI_CONF_SBPOL (1 << 24) +#define MCSPI_CONF_SBE (1 << 23) +#define MCSPI_CONF_FORCE (1 << 20) +#define MCSPI_CONF_TURBO (1 << 19) +#define MCSPI_CONF_IS (1 << 18) +#define MCSPI_CONF_DPE1 (1 << 17) +#define MCSPI_CONF_DPE0 (1 << 16) +#define MCSPI_CONF_DMAR (1 << 15) +#define MCSPI_CONF_DMAW (1 << 14) +#define MCSPI_CONF_WL_MSK 0x1f +#define MCSPI_CONF_WL_SHIFT 7 +#define MCSPI_CONF_WL8BITS (7 << MCSPI_CONF_WL_SHIFT) +#define MCSPI_CONF_EPOL (1 << 6) +#define MCSPI_CONF_CLK_MSK 0xf +#define MCSPI_CONF_CLK_SHIFT 2 +#define MCSPI_CONF_POL (1 << 1) +#define MCSPI_CONF_PHA (1 << 0) +#define MCSPI_STAT_CH(_c) (0x130 + 0x14 * (_c)) +#define MCSPI_STAT_TXFFF (1 << 4) +#define MCSPI_STAT_TXS (1 << 1) +#define MCSPI_STAT_RXS (1 << 0) +#define MCSPI_CTRL_CH(_c) (0x134 + 0x14 * (_c)) +#define MCSPI_EXTCLK_MSK 0xfff +#define MCSPI_CTRL_EXTCLK_MSK 0xff +#define MCSPI_CTRL_EXTCLK_SHIFT 8 +#define MCSPI_CTRL_ENABLE (1 << 0) +#define MCSPI_TX_CH(_c) (0x138 + 0x14 * (_c)) +#define MCSPI_RX_CH(_c) (0x13c + 0x14 * (_c)) +#define MCSPI_XFERLEVEL 0x17c +#define MCSPI_XFERLEVEL_AFL(_a) (((_a) >> 8) & 0xff) +#define MCSPI_XFERLEVEL_AEL(_a) (((_a) >> 0) & 0xff) + +#endif /* _TI_SPIREG_H_ */ diff --git a/sys/arm/ti/ti_spivar.h b/sys/arm/ti/ti_spivar.h new file mode 100644 index 0000000..89731f3 --- /dev/null +++ b/sys/arm/ti/ti_spivar.h @@ -0,0 +1,71 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#ifndef _TI_SPIVAR_H_ +#define _TI_SPIVAR_H_ + +struct ti_spi_softc { + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + device_t sc_dev; + int sc_numcs; + struct mtx sc_mtx; + struct resource *sc_mem_res; + struct resource *sc_irq_res; + struct { + int cs; + int fifolvl; + struct spi_command *cmd; + uint32_t len; + uint32_t read; + uint32_t written; + } xfer; + uint32_t sc_flags; + void *sc_intrhand; +#define sc_cs xfer.cs +#define sc_fifolvl xfer.fifolvl +#define sc_cmd xfer.cmd +#define sc_len xfer.len +#define sc_read xfer.read +#define sc_written xfer.written +}; + +#define TI_SPI_BUSY 0x1 +#define TI_SPI_DONE 0x2 + +#define TI_SPI_WRITE(_sc, _off, _val) \ + bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off), (_val)) +#define TI_SPI_READ(_sc, _off) \ + bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off)) + +#define TI_SPI_LOCK(_sc) \ + mtx_lock(&(_sc)->sc_mtx) +#define TI_SPI_UNLOCK(_sc) \ + mtx_unlock(&(_sc)->sc_mtx) + +#endif /* _TI_SPIVAR_H_ */ diff --git a/sys/arm/xscale/ixp425/avila_ata.c b/sys/arm/xscale/ixp425/avila_ata.c index e3a9a52..f6dfaa6 100644 --- a/sys/arm/xscale/ixp425/avila_ata.c +++ b/sys/arm/xscale/ixp425/avila_ata.c @@ -287,7 +287,7 @@ ata_avila_alloc_resource(device_t dev, device_t child, int type, int *rid, struct ata_avila_softc *sc = device_get_softc(dev); KASSERT(type == SYS_RES_IRQ && *rid == ATA_IRQ_RID, - ("type %u rid %u start %lu end %lu count %lu flags %u", + ("type %u rid %u start %ju end %ju count %ju flags %u", type, *rid, start, end, count, flags)); /* doesn't matter what we return so reuse the real thing */ diff --git a/sys/arm/xscale/ixp425/ixp425.c b/sys/arm/xscale/ixp425/ixp425.c index 017d567..20120e6 100644 --- a/sys/arm/xscale/ixp425/ixp425.c +++ b/sys/arm/xscale/ixp425/ixp425.c @@ -533,7 +533,7 @@ ixp425_alloc_resource(device_t dev, device_t child, int type, int *rid, (start - vtrans->hwbase); if (bootverbose) device_printf(child, - "%s: assign 0x%lx:0x%lx%s\n", + "%s: assign 0x%jx:0x%jx%s\n", __func__, start, end - start, vtrans->isa4x ? " A4X" : vtrans->isslow ? " SLOW" : ""); @@ -542,14 +542,14 @@ ixp425_alloc_resource(device_t dev, device_t child, int type, int *rid, vtrans = gethwvtrans(start, end - start); if (vtrans == NULL) { /* likely means above table needs to be updated */ - device_printf(child, "%s: no mapping for 0x%lx:0x%lx\n", + 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%lx:0x%lx\n", + device_printf(child, "%s: cannot reserve 0x%jx:0x%jx\n", __func__, start, end - start); return NULL; } @@ -586,7 +586,7 @@ ixp425_activate_resource(device_t dev, device_t child, int type, int rid, 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%lx:0x%lx\n", + device_printf(child, "%s: no mapping for 0x%jx:0x%jx\n", __func__, rman_get_start(r), rman_get_size(r)); return (ENOENT); } diff --git a/sys/arm/xscale/pxa/pxa_obio.c b/sys/arm/xscale/pxa/pxa_obio.c index 6761ca3..6081588 100644 --- a/sys/arm/xscale/pxa/pxa_obio.c +++ b/sys/arm/xscale/pxa/pxa_obio.c @@ -159,9 +159,9 @@ pxa_print_child(device_t dev, device_t child) retval += bus_print_child_header(dev, child); retval += resource_list_print_type(&od->od_resources, "at mem", - SYS_RES_MEMORY, "0x%08lx"); + SYS_RES_MEMORY, "0x%08jx"); retval += resource_list_print_type(&od->od_resources, "irq", - SYS_RES_IRQ, "%ld"); + SYS_RES_IRQ, "%jd"); retval += bus_print_child_footer(dev, child); @@ -390,7 +390,7 @@ pxa_alloc_gpio_irq(device_t dev, device_t child, int type, int *rid, } if (bootverbose) - device_printf(dev, "lazy allocation of irq %ld for %s\n", + device_printf(dev, "lazy allocation of irq %jd for %s\n", start, device_get_nameunit(child)); return (rv); diff --git a/sys/arm/xscale/pxa/pxa_smi.c b/sys/arm/xscale/pxa/pxa_smi.c index 4c49e75..b08a7c7 100644 --- a/sys/arm/xscale/pxa/pxa_smi.c +++ b/sys/arm/xscale/pxa/pxa_smi.c @@ -144,9 +144,9 @@ pxa_smi_print_child(device_t dev, device_t child) retval += bus_print_child_header(dev, child); retval += resource_list_print_type(&smid->smid_resources, "at mem", - SYS_RES_MEMORY, "%#lx"); + SYS_RES_MEMORY, "%#jx"); retval += resource_list_print_type(&smid->smid_resources, "irq", - SYS_RES_IRQ, "%ld"); + SYS_RES_IRQ, "%jd"); retval += bus_print_child_footer(dev, child); diff --git a/sys/arm64/arm64/bzero.S b/sys/arm64/arm64/bzero.S new file mode 100644 index 0000000..60ac97e --- /dev/null +++ b/sys/arm64/arm64/bzero.S @@ -0,0 +1,206 @@ +/*- + * Copyright (C) 2016 Cavium Inc. + * All rights reserved. + * + * Developed by Semihalf. + * + * 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 <machine/asm.h> +__FBSDID("$FreeBSD$"); + + +#include "assym.s" + + /* + * void bzero(void *p, size_t size) + * + * x0 - p + * x1 - size + */ +ENTRY(bzero) + cbz x1, ending + + /* + * x5 is number of cache lines to zero - calculated later and + * will become non-zero if buffer is long enough to zero by + * cache lines (and if it is allowed.) + * We need to zero it before proceeding with buffers of size + * smaller than 16 bytes - otherwise the x5 will not be + * calculated and will retain random value. + * "normal" is used for buffers <= 16 bytes and to align buffer + * to cache line for buffers bigger than cache line; non-0 x5 + * after "normal" has completed indicates that it has been used + * to align buffer to cache line and now zero by cache lines will + * be performed, and x5 is amount of cache lines to loop through. + */ + mov x5, xzr + + /* No use of cache assisted zero for buffers with size <= 16 */ + cmp x1, #0x10 + b.le normal + + /* + * Load size of line that will be cleaned by dc zva call. + * 0 means that the instruction is not allowed + */ + ldr x7, =dczva_line_size + ldr x7, [x7] + cbz x7, normal + + /* + * Buffer must be larger than cache line for using cache zeroing + * (and cache line aligned but this is checked after jump) + */ + cmp x1, x7 + b.lt normal + + /* + * Calculate number of bytes to cache aligned address (x4) nad + * number of full cache lines (x5). x6 is final address to zero. + */ + sub x2, x7, #0x01 + mov x3, -1 + eor x3, x3, x2 + add x4, x0, x2 + and x4, x4, x3 + subs x4, x4, x0 + b.eq normal + + /* Calculate number of "lines" in buffer */ + sub x5, x1, x4 + rbit x2, x7 + clz x2, x2 + lsr x5, x5, x2 + + /* + * If number of cache lines is 0, we will not be able to zero + * by cache lines, so go normal way. + */ + cbz x5, normal + /* x6 is final address to zero */ + add x6, x0, x1 + + /* + * We are here because x5 is non-0 so normal will be used to + * align buffer before cache zeroing. x4 holds number of bytes + * needed for alignment. + */ + mov x1, x4 + + /* When jumping here: x0 holds pointer, x1 holds size */ +normal: + /* + * Get buffer offset into 16 byte aligned address; 0 means pointer + * is aligned. + */ + ands x2, x0, #0x0f + b.eq aligned_to_16 + /* Calculate one-byte loop runs to 8 byte aligned address. */ + ands x2, x2, #0x07 + mov x3, #0x08 + sub x2, x3, x2 + /* x2 is number of bytes missing for alignment, x1 is buffer size */ + cmp x1, x2 + csel x2, x1, x2, le + sub x1, x1, x2 + + /* + * Byte by byte copy will copy at least enough bytes to align + * pointer and at most "size". + */ +align: + strb wzr, [x0], #0x01 + subs x2, x2, #0x01 + b.ne align + + /* Now pointer is aligned to 8 bytes */ + cmp x1, #0x10 + b.lt lead_out + /* + * Check if copy of another 8 bytes is needed to align to 16 byte + * address and do it + */ + tbz x0, #0x03, aligned_to_16 + str xzr, [x0], #0x08 + sub x1, x1, #0x08 + + /* While jumping here: x0 is 16 byte alligned address, x1 is size */ +aligned_to_16: + /* If size is less than 16 bytes, use lead_out to copy what remains */ + cmp x1, #0x10 + b.lt lead_out + + lsr x2, x1, #0x04 +zero_by_16: + stp xzr, xzr, [x0], #0x10 + subs x2, x2, #0x01 + b.ne zero_by_16 + + /* + * Lead out requires addresses to be aligned to 8 bytes. It is used to + * zero buffers with sizes < 16 and what can not be zeroed by + * zero_by_16 loop. + */ + ands x1, x1, #0x0f + b.eq lead_out_end +lead_out: + tbz x1, #0x03, lead_out_dword + str xzr, [x0], #0x08 +lead_out_dword: + tbz x1, #0x02, lead_out_word + str wzr, [x0], #0x04 +lead_out_word: + tbz x1, #0x01, lead_out_byte + strh wzr, [x0], #0x02 +lead_out_byte: + tbz x1, #0x00, lead_out_end + strb wzr, [x0], #0x01 + +lead_out_end: + /* + * If x5 is non-zero, this means that normal has been used as + * a lead in to align buffer address to cache size + */ + cbz x5, ending + + /* + * Here x5 holds number of lines to zero; x6 is final address of + * buffer. x0 is cache line aligned pointer. x7 is cache line size + * in bytes + */ +cache_line_zero: + dc zva, x0 + add x0, x0, x7 + subs x5, x5, #0x01 + b.ne cache_line_zero + + /* Need to zero remaining bytes? */ + subs x1, x6, x0 + b.ne normal + +ending: + ret + +END(bzero) + diff --git a/sys/arm64/arm64/copyinout.S b/sys/arm64/arm64/copyinout.S index 1ba8106..b99dbc2 100644 --- a/sys/arm64/arm64/copyinout.S +++ b/sys/arm64/arm64/copyinout.S @@ -51,24 +51,18 @@ END(copyio_fault) * int copyout(const void *kaddr, void *udaddr, size_t len) */ ENTRY(copyout) - cbz x2, 2f /* If len == 0 then skip loop */ - add x3, x1, x2 + cbz x2, 1f + adds x3, x1, x2 + b.cs copyio_fault_nopcb ldr x4, =VM_MAXUSER_ADDRESS cmp x3, x4 b.hi copyio_fault_nopcb - adr x6, copyio_fault /* Get the handler address */ - SET_FAULT_HANDLER(x6, x7) /* Set the handler */ - -1: ldrb w4, [x0], #1 /* Load from kaddr */ - strb w4, [x1], #1 /* Store in uaddr */ - sub x2, x2, #1 /* len-- */ - cbnz x2, 1b - - SET_FAULT_HANDLER(xzr, x7) /* Clear the handler */ + b copycommon -2: mov x0, xzr /* return 0 */ +1: mov x0, xzr /* return 0 */ ret + END(copyout) /* @@ -77,24 +71,18 @@ END(copyout) * int copyin(const void *uaddr, void *kdaddr, size_t len) */ ENTRY(copyin) - cbz x2, 2f /* If len == 0 then skip loop */ - add x3, x0, x2 + cbz x2, 1f + adds x3, x0, x2 + b.cs copyio_fault_nopcb ldr x4, =VM_MAXUSER_ADDRESS cmp x3, x4 b.hi copyio_fault_nopcb - adr x6, copyio_fault /* Get the handler address */ - SET_FAULT_HANDLER(x6, x7) /* Set the handler */ - -1: ldrb w4, [x0], #1 /* Load from uaddr */ - strb w4, [x1], #1 /* Store in kaddr */ - sub x2, x2, #1 /* len-- */ - cbnz x2, 1b - - SET_FAULT_HANDLER(xzr, x7) /* Clear the handler */ + b copycommon -2: mov x0, xzr /* return 0 */ +1: mov x0, xzr /* return 0 */ ret + END(copyin) /* @@ -106,11 +94,11 @@ ENTRY(copyinstr) mov x5, xzr /* count = 0 */ mov w4, #1 /* If zero return faulure */ cbz x2, 3f /* If len == 0 then skip loop */ - ldr x7, =VM_MAXUSER_ADDRESS adr x6, copyio_fault /* Get the handler address */ SET_FAULT_HANDLER(x6, x7) /* Set the handler */ + ldr x7, =VM_MAXUSER_ADDRESS 1: cmp x0, x7 b.cs copyio_fault ldrb w4, [x0], #1 /* Load from uaddr */ @@ -130,3 +118,101 @@ ENTRY(copyinstr) csel w0, wzr, w1, eq /* If so return success, else failure */ ret END(copyinstr) + +/* + * Local helper + * + * x0 - src pointer + * x1 - dst pointer + * x2 - size + * lr - the return address, so jump here instead of calling + * + * This function is optimized to minimize concurrent memory accesses. In + * present form it is suited for cores with a single memory prefetching + * unit. + * ARM64TODO: + * Consider using separate functions for each ARM64 core. Adding memory + * access interleaving might increase a total throughput on A57 or A72. + */ + .text + .align 4 + .local copycommon + .type copycommon,@function + +copycommon: + adr x6, copyio_fault /* Get the handler address */ + SET_FAULT_HANDLER(x6, x7) /* Set the handler */ + + + /* Check alignment */ + orr x3, x0, x1 + ands x3, x3, 0x07 + b.eq aligned + + /* Unaligned is byte by byte copy */ +byte_by_byte: + ldrb w3, [x0], #0x01 + strb w3, [x1], #0x01 + subs x2, x2, #0x01 + b.ne byte_by_byte + b ending + +aligned: + cmp x2, #0x10 + b.lt lead_out + cmp x2, #0x40 + b.lt by_dwords_start + + /* Block copy */ + lsr x15, x2, #0x06 +by_blocks: + ldp x3, x4, [x0], #0x10 + ldp x5, x6, [x0], #0x10 + ldp x7, x8, [x0], #0x10 + ldp x9, x10, [x0], #0x10 + stp x3, x4, [x1], #0x10 + stp x5, x6, [x1], #0x10 + stp x7, x8, [x1], #0x10 + stp x9, x10, [x1], #0x10 + + subs x15, x15, #0x01 + b.ne by_blocks + + and x2, x2, #0x3f + +by_dwords_start: + lsr x15, x2, #0x04 + cbz x15, lead_out +by_dwords: + ldp x3, x4, [x0], #0x10 + stp x3, x4, [x1], #0x10 + subs x15, x15, #0x01 + b.ne by_dwords + + /* Less than 16 bytes to copy */ +lead_out: + tbz x2, #0x03, last_word + ldr x3, [x0], #0x08 + str x3, [x1], #0x08 + +last_word: + tbz x2, #0x02, last_hword + ldr w3, [x0], #0x04 + str w3, [x1], #0x04 + +last_hword: + tbz x2, #0x01, last_byte + ldrh w3, [x0], #0x02 + strh w3, [x1], #0x02 + +last_byte: + tbz x2, #0x00, ending + ldrb w3, [x0] + strb w3, [x1] + +ending: + SET_FAULT_HANDLER(xzr, x7) /* Clear the handler */ + + mov x0, xzr /* return 0 */ + ret + .size copycommon, . - copycommon diff --git a/sys/arm64/arm64/exception.S b/sys/arm64/arm64/exception.S index c5a358b..63aa04c 100644 --- a/sys/arm64/arm64/exception.S +++ b/sys/arm64/arm64/exception.S @@ -150,7 +150,7 @@ END(handle_el1h_sync) ENTRY(handle_el1h_irq) save_registers 1 mov x0, sp - bl arm_cpu_intr + bl intr_irq_handler restore_registers 1 eret END(handle_el1h_irq) @@ -171,7 +171,7 @@ END(handle_el0_sync) ENTRY(handle_el0_irq) save_registers 0 mov x0, sp - bl arm_cpu_intr + bl intr_irq_handler do_ast restore_registers 0 eret diff --git a/sys/arm64/arm64/genassym.c b/sys/arm64/arm64/genassym.c index 67c295f..26adc6d 100644 --- a/sys/arm64/arm64/genassym.c +++ b/sys/arm64/arm64/genassym.c @@ -52,7 +52,7 @@ ASSYM(PCB_SIZE, roundup2(sizeof(struct pcb), STACKALIGNBYTES + 1)); ASSYM(PCB_SINGLE_STEP_SHIFT, PCB_SINGLE_STEP_SHIFT); ASSYM(PCB_REGS, offsetof(struct pcb, pcb_x)); ASSYM(PCB_SP, offsetof(struct pcb, pcb_sp)); -ASSYM(PCB_L1ADDR, offsetof(struct pcb, pcb_l1addr)); +ASSYM(PCB_L0ADDR, offsetof(struct pcb, pcb_l0addr)); ASSYM(PCB_ONFAULT, offsetof(struct pcb, pcb_onfault)); ASSYM(PCB_FLAGS, offsetof(struct pcb, pcb_flags)); diff --git a/sys/arm64/arm64/gic.c b/sys/arm64/arm64/gic.c index 0a13b12..2140086 100644 --- a/sys/arm64/arm64/gic.c +++ b/sys/arm64/arm64/gic.c @@ -75,6 +75,7 @@ __FBSDID("$FreeBSD$"); #define GICD_ITARGETSR(n) (0x0800 + ((n) * 4)) /* v1 ICDIPTR */ #define GICD_ICFGR(n) (0x0C00 + ((n) * 4)) /* v1 ICDICFR */ #define GICD_SGIR(n) (0x0F00 + ((n) * 4)) /* v1 ICDSGIR */ +#define GICD_SGI_TARGET_SHIFT 16 /* CPU Registers */ #define GICC_CTLR 0x0000 /* v1 ICCICR */ @@ -108,6 +109,8 @@ static struct resource_spec arm_gic_spec[] = { { -1, 0 } }; +static u_int arm_gic_map[MAXCPU]; + static struct arm_gic_softc *arm_gic_sc = NULL; #define gic_c_read_4(_sc, _reg) \ @@ -124,6 +127,29 @@ static pic_eoi_t gic_eoi; static pic_mask_t gic_mask_irq; static pic_unmask_t gic_unmask_irq; +static uint8_t +gic_cpu_mask(struct arm_gic_softc *sc) +{ + uint32_t mask; + int i; + + /* Read the current cpuid mask by reading ITARGETSR{0..7} */ + for (i = 0; i < 8; i++) { + mask = gic_d_read_4(sc, GICD_ITARGETSR(i)); + if (mask != 0) + break; + } + /* No mask found, assume we are on CPU interface 0 */ + if (mask == 0) + return (1); + + /* Collect the mask in the lower byte */ + mask |= mask >> 16; + mask |= mask >> 8; + + return (mask); +} + #ifdef SMP static void gic_init_secondary(device_t dev) @@ -131,6 +157,9 @@ gic_init_secondary(device_t dev) struct arm_gic_softc *sc = device_get_softc(dev); int i; + /* Set the mask so we can find this CPU to send it IPIs */ + arm_gic_map[PCPU_GET(cpuid)] = gic_cpu_mask(sc); + for (i = 0; i < sc->nirqs; i += 4) gic_d_write_4(sc, GICD_IPRIORITYR(i >> 2), 0); @@ -162,7 +191,7 @@ arm_gic_attach(device_t dev) { struct arm_gic_softc *sc; int i; - uint32_t icciidr; + uint32_t icciidr, mask; if (arm_gic_sc) return (ENXIO); @@ -212,10 +241,19 @@ arm_gic_attach(device_t dev) gic_d_write_4(sc, GICD_ICENABLER(i >> 5), 0xFFFFFFFF); } + /* Find the current cpu mask */ + mask = gic_cpu_mask(sc); + /* Set the mask so we can find this CPU to send it IPIs */ + arm_gic_map[PCPU_GET(cpuid)] = mask; + /* Set all four targets to this cpu */ + mask |= mask << 8; + mask |= mask << 16; + for (i = 0; i < sc->nirqs; i += 4) { gic_d_write_4(sc, GICD_IPRIORITYR(i >> 2), 0); - gic_d_write_4(sc, GICD_ITARGETSR(i >> 2), - 1 << 0 | 1 << 8 | 1 << 16 | 1 << 24); + if (i > 32) { + gic_d_write_4(sc, GICD_ITARGETSR(i >> 2), mask); + } } /* Set all the interrupts to be in Group 0 (secure) */ @@ -299,7 +337,7 @@ gic_ipi_send(device_t dev, cpuset_t cpus, u_int ipi) for (i = 0; i < MAXCPU; i++) if (CPU_ISSET(i, &cpus)) - val |= 1 << (16 + i); + val |= arm_gic_map[i] << GICD_SGI_TARGET_SHIFT; gic_d_write_4(sc, GICD_SGIR(0), val | ipi); } diff --git a/sys/arm64/arm64/intr_machdep.c b/sys/arm64/arm64/intr_machdep.c index 5994279..d99303e 100644 --- a/sys/arm64/arm64/intr_machdep.c +++ b/sys/arm64/arm64/intr_machdep.c @@ -408,7 +408,7 @@ arm_setup_intr(const char *name, driver_filter_t *filt, driver_intr_t handler, } int -arm_teardown_intr(void *cookie) +intr_irq_remove_handler(device_t dev, u_int irq, void *cookie) { struct arm64_intr_entry *intr; int error; @@ -426,7 +426,7 @@ arm_teardown_intr(void *cookie) } int -arm_config_intr(u_int hw_irq, enum intr_trigger trig, enum intr_polarity pol) +intr_irq_config(u_int hw_irq, enum intr_trigger trig, enum intr_polarity pol) { struct arm64_intr_entry *intr; @@ -476,7 +476,7 @@ stray: } void -arm_cpu_intr(struct trapframe *tf) +intr_irq_handler(struct trapframe *tf) { critical_enter(); @@ -512,7 +512,7 @@ SYSINIT(arm_intr_smp_init, SI_SUB_SMP, SI_ORDER_ANY, arm_intr_smp_init, NULL); /* Attempt to bind the specified IRQ to the specified CPU. */ int -arm_intr_bind(u_int hw_irq, int cpu) +intr_irq_bind(u_int hw_irq, int cpu) { struct arm64_intr_entry *intr; diff --git a/sys/arm64/arm64/locore.S b/sys/arm64/arm64/locore.S index b1fd3ae..676c1d5 100644 --- a/sys/arm64/arm64/locore.S +++ b/sys/arm64/arm64/locore.S @@ -35,7 +35,7 @@ #include <machine/param.h> #include <machine/pte.h> -#define VIRT_BITS 39 +#define VIRT_BITS 48 .globl kernbase .set kernbase, KERNBASE @@ -89,7 +89,8 @@ _start: /* * At this point: * x27 = TTBR0 table - * x26 = TTBR1 table + * x26 = Kernel L1 table + * x24 = TTBR1 table */ /* Enable the mmu */ @@ -100,16 +101,6 @@ _start: br x15 virtdone: - /* - * Now that we are in virtual address space, - * we don't need the identity mapping in TTBR0 and - * can set the TCR to a more useful value. - */ - ldr x2, tcr - mrs x3, id_aa64mmfr0_el1 - bfi x2, x3, #32, #3 - msr tcr_el1, x2 - /* Set up the stack */ adr x25, initstack_end mov sp, x25 @@ -128,6 +119,7 @@ virtdone: /* Make the page table base a virtual address */ sub x26, x26, x29 + sub x24, x24, x29 sub sp, sp, #(64 * 4) mov x0, sp @@ -139,6 +131,7 @@ virtdone: str x26, [x0, 8] /* kern_l1pt */ str x29, [x0, 16] /* kern_delta */ str x25, [x0, 24] /* kern_stack */ + str x24, [x0, 32] /* kern_l0pt */ /* trace back starts here */ mov fp, #0 @@ -175,7 +168,7 @@ ENTRY(mpentry) msr contextidr_el1, x1 /* Load the kernel page table */ - adr x26, pagetable_l1_ttbr1 + adr x24, pagetable_l0_ttbr1 /* Load the identity page table */ adr x27, pagetable_l0_ttbr0 @@ -187,16 +180,6 @@ ENTRY(mpentry) br x15 mp_virtdone: - /* - * Now that we are in virtual address space, - * we don't need the identity mapping in TTBR0 and - * can set the TCR to a more useful value. - */ - ldr x2, tcr - mrs x3, id_aa64mmfr0_el1 - bfi x2, x3, #32, #3 - msr tcr_el1, x2 - ldr x4, =secondary_stacks mov x5, #(PAGE_SIZE * KSTACK_PAGES) mul x5, x0, x5 @@ -388,11 +371,18 @@ create_pagetables: mov x6, x26 bl link_l1_pagetable + /* Move to the l0 table */ + add x24, x26, #PAGE_SIZE + + /* Link the l0 -> l1 table */ + mov x9, x6 + mov x6, x24 + bl link_l0_pagetable /* * Build the TTBR0 maps. */ - add x27, x26, #PAGE_SIZE + add x27, x24, #PAGE_SIZE mov x6, x27 /* The initial page table */ #if defined(SOCDEV_PA) && defined(SOCDEV_VA) @@ -440,7 +430,7 @@ link_l0_pagetable: */ /* Find the table index */ lsr x11, x8, #L0_SHIFT - and x11, x11, #Ln_ADDR_MASK + and x11, x11, #L0_ADDR_MASK /* Build the L0 block entry */ mov x12, #L0_TABLE @@ -582,7 +572,7 @@ start_mmu: /* Load ttbr0 and ttbr1 */ msr ttbr0_el1, x27 - msr ttbr1_el1, x26 + msr ttbr1_el1, x24 isb /* Clear the Monitor Debug System control register */ @@ -596,11 +586,8 @@ start_mmu: /* * Setup TCR according to PARange bits from ID_AA64MMFR0_EL1. - * Some machines have physical memory mapped >512GiB, which can not - * be identity-mapped using the default 39 VA bits. Thus, use - * 48 VA bits for now and switch back to 39 after the VA jump. */ - ldr x2, tcr_early + ldr x2, tcr mrs x3, id_aa64mmfr0_el1 bfi x2, x3, #32, #3 msr tcr_el1, x2 @@ -623,9 +610,6 @@ mair: tcr: .quad (TCR_TxSZ(64 - VIRT_BITS) | TCR_ASID_16 | TCR_TG1_4K | \ TCR_CACHE_ATTRS | TCR_SMP_ATTRS) -tcr_early: - .quad (TCR_T1SZ(64 - VIRT_BITS) | TCR_T0SZ(64 - 48) | \ - TCR_ASID_16 | TCR_TG1_4K | TCR_CACHE_ATTRS | TCR_SMP_ATTRS) sctlr_set: /* Bits to set */ .quad (SCTLR_UCI | SCTLR_nTWE | SCTLR_nTWI | SCTLR_UCT | SCTLR_DZE | \ @@ -651,6 +635,8 @@ pagetable: .space PAGE_SIZE pagetable_l1_ttbr1: .space PAGE_SIZE +pagetable_l0_ttbr1: + .space PAGE_SIZE pagetable_l1_ttbr0: .space PAGE_SIZE pagetable_l0_ttbr0: diff --git a/sys/arm64/arm64/machdep.c b/sys/arm64/arm64/machdep.c index 49c9610..2bf108f 100644 --- a/sys/arm64/arm64/machdep.c +++ b/sys/arm64/arm64/machdep.c @@ -108,6 +108,14 @@ struct kva_md_info kmi; int64_t dcache_line_size; /* The minimum D cache line size */ int64_t icache_line_size; /* The minimum I cache line size */ int64_t idcache_line_size; /* The minimum cache line size */ +int64_t dczva_line_size; /* The size of cache line the dc zva zeroes */ + +/* pagezero_* implementations are provided in support.S */ +void pagezero_simple(void *); +void pagezero_cache(void *); + +/* pagezero_simple is default pagezero */ +void (*pagezero)(void *p) = pagezero_simple; static void cpu_startup(void *dummy) @@ -129,16 +137,6 @@ cpu_idle_wakeup(int cpu) return (0); } -void -bzero(void *buf, size_t len) -{ - uint8_t *p; - - p = buf; - while(len-- > 0) - *p++ = 0; -} - int fill_regs(struct thread *td, struct reg *regs) { @@ -800,8 +798,9 @@ try_load_dtb(caddr_t kmdp) static void cache_setup(void) { - int dcache_line_shift, icache_line_shift; + int dcache_line_shift, icache_line_shift, dczva_line_shift; uint32_t ctr_el0; + uint32_t dczid_el0; ctr_el0 = READ_SPECIALREG(ctr_el0); @@ -815,6 +814,20 @@ cache_setup(void) icache_line_size = sizeof(int) << icache_line_shift; idcache_line_size = MIN(dcache_line_size, icache_line_size); + + dczid_el0 = READ_SPECIALREG(dczid_el0); + + /* Check if dc zva is not prohibited */ + if (dczid_el0 & DCZID_DZP) + dczva_line_size = 0; + else { + /* Same as with above calculations */ + dczva_line_shift = DCZID_BS_SIZE(dczid_el0); + dczva_line_size = sizeof(int) << dczva_line_shift; + + /* Change pagezero function */ + pagezero = pagezero_cache; + } } void @@ -896,8 +909,8 @@ initarm(struct arm64_bootparams *abp) cache_setup(); /* Bootstrap enough of pmap to enter the kernel proper */ - pmap_bootstrap(abp->kern_l1pt, KERNBASE - abp->kern_delta, - lastaddr - KERNBASE); + pmap_bootstrap(abp->kern_l0pt, abp->kern_l1pt, + KERNBASE - abp->kern_delta, lastaddr - KERNBASE); arm_devmap_bootstrap(0, NULL); diff --git a/sys/arm64/arm64/minidump_machdep.c b/sys/arm64/arm64/minidump_machdep.c index 56d1713..27c2081 100644 --- a/sys/arm64/arm64/minidump_machdep.c +++ b/sys/arm64/arm64/minidump_machdep.c @@ -218,7 +218,7 @@ blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz) int minidumpsys(struct dumperinfo *di) { - pd_entry_t *l1, *l2; + pd_entry_t *l0, *l1, *l2; pt_entry_t *l3; uint32_t pmapsize; vm_offset_t va; @@ -236,7 +236,7 @@ minidumpsys(struct dumperinfo *di) pmapsize = 0; for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += L2_SIZE) { pmapsize += PAGE_SIZE; - if (!pmap_get_tables(pmap_kernel(), va, &l1, &l2, &l3)) + if (!pmap_get_tables(pmap_kernel(), va, &l0, &l1, &l2, &l3)) continue; /* We should always be using the l2 table for kvm */ @@ -335,7 +335,7 @@ minidumpsys(struct dumperinfo *di) /* Dump kernel page directory pages */ bzero(&tmpbuffer, sizeof(tmpbuffer)); for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += L2_SIZE) { - if (!pmap_get_tables(pmap_kernel(), va, &l1, &l2, &l3)) { + if (!pmap_get_tables(pmap_kernel(), va, &l0, &l1, &l2, &l3)) { /* We always write a page, even if it is zero */ error = blk_write(di, (char *)&tmpbuffer, 0, PAGE_SIZE); if (error) diff --git a/sys/arm64/arm64/mp_machdep.c b/sys/arm64/arm64/mp_machdep.c index b89982d..22c99ba 100644 --- a/sys/arm64/arm64/mp_machdep.c +++ b/sys/arm64/arm64/mp_machdep.c @@ -80,6 +80,12 @@ static device_identify_t arm64_cpu_identify; static device_probe_t arm64_cpu_probe; static device_attach_t arm64_cpu_attach; +static void ipi_ast(void *); +static void ipi_hardclock(void *); +static void ipi_preempt(void *); +static void ipi_rendezvous(void *); +static void ipi_stop(void *); + static int ipi_handler(void *arg); struct mtx ap_boot_mtx; @@ -179,7 +185,7 @@ release_aps(void *dummy __unused) int cpu, i; /* Setup the IPI handler */ - for (i = 0; i < COUNT_IPI; i++) + for (i = 0; i < INTR_IPI_COUNT; i++) arm_setup_ipihandler(ipi_handler, i); atomic_store_rel_int(&aps_ready, 1); @@ -238,7 +244,7 @@ init_secondary(uint64_t cpu) /* Configure the interrupt controller */ arm_init_secondary(); - for (i = 0; i < COUNT_IPI; i++) + for (i = 0; i < INTR_IPI_COUNT; i++) arm_unmask_ipi(i); /* Start per-CPU event timers. */ @@ -271,13 +277,65 @@ init_secondary(uint64_t cpu) /* NOTREACHED */ } +static void +ipi_ast(void *dummy __unused) +{ + + CTR0(KTR_SMP, "IPI_AST"); +} + +static void +ipi_hardclock(void *dummy __unused) +{ + + CTR1(KTR_SMP, "%s: IPI_HARDCLOCK", __func__); + hardclockintr(); +} + +static void +ipi_preempt(void *dummy __unused) +{ + CTR1(KTR_SMP, "%s: IPI_PREEMPT", __func__); + sched_preempt(curthread); +} + +static void +ipi_rendezvous(void *dummy __unused) +{ + + CTR0(KTR_SMP, "IPI_RENDEZVOUS"); + smp_rendezvous_action(); +} + +static void +ipi_stop(void *dummy __unused) +{ + u_int cpu; + + CTR0(KTR_SMP, "IPI_STOP"); + + cpu = PCPU_GET(cpuid); + savectx(&stoppcbs[cpu]); + + /* Indicate we are stopped */ + CPU_SET_ATOMIC(cpu, &stopped_cpus); + + /* Wait for restart */ + while (!CPU_ISSET(cpu, &started_cpus)) + cpu_spinwait(); + + CPU_CLR_ATOMIC(cpu, &started_cpus); + CPU_CLR_ATOMIC(cpu, &stopped_cpus); + CTR0(KTR_SMP, "IPI_STOP (restart)"); +} + static int ipi_handler(void *arg) { u_int cpu, ipi; arg = (void *)((uintptr_t)arg & ~(1 << 16)); - KASSERT((uintptr_t)arg < COUNT_IPI, + KASSERT((uintptr_t)arg < INTR_IPI_COUNT, ("Invalid IPI %ju", (uintptr_t)arg)); cpu = PCPU_GET(cpuid); @@ -285,35 +343,20 @@ ipi_handler(void *arg) switch(ipi) { case IPI_AST: - CTR0(KTR_SMP, "IPI_AST"); + ipi_ast(NULL); break; case IPI_PREEMPT: - CTR1(KTR_SMP, "%s: IPI_PREEMPT", __func__); - sched_preempt(curthread); + ipi_preempt(NULL); break; case IPI_RENDEZVOUS: - CTR0(KTR_SMP, "IPI_RENDEZVOUS"); - smp_rendezvous_action(); + ipi_rendezvous(NULL); break; case IPI_STOP: case IPI_STOP_HARD: - CTR0(KTR_SMP, (ipi == IPI_STOP) ? "IPI_STOP" : "IPI_STOP_HARD"); - savectx(&stoppcbs[cpu]); - - /* Indicate we are stopped */ - CPU_SET_ATOMIC(cpu, &stopped_cpus); - - /* Wait for restart */ - while (!CPU_ISSET(cpu, &started_cpus)) - cpu_spinwait(); - - CPU_CLR_ATOMIC(cpu, &started_cpus); - CPU_CLR_ATOMIC(cpu, &stopped_cpus); - CTR0(KTR_SMP, "IPI_STOP (restart)"); + ipi_stop(NULL); break; case IPI_HARDCLOCK: - CTR1(KTR_SMP, "%s: IPI_HARDCLOCK", __func__); - hardclockintr(); + ipi_hardclock(NULL); break; default: panic("Unknown IPI %#0x on cpu %d", ipi, curcpu); diff --git a/sys/arm64/arm64/nexus.c b/sys/arm64/arm64/nexus.c index cdc5fc8..f48a7aa 100644 --- a/sys/arm64/arm64/nexus.c +++ b/sys/arm64/arm64/nexus.c @@ -39,6 +39,9 @@ * and I/O memory address space. */ +#include "opt_acpi.h" +#include "opt_platform.h" + #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); @@ -60,9 +63,6 @@ __FBSDID("$FreeBSD$"); #include <machine/resource.h> #include <machine/intr.h> -#include "opt_acpi.h" -#include "opt_platform.h" - #ifdef FDT #include <dev/ofw/openfirm.h> #include "ofw_bus_if.h" @@ -271,7 +271,7 @@ nexus_config_intr(device_t dev, int irq, enum intr_trigger trig, enum intr_polarity pol) { - return (arm_config_intr(irq, trig, pol)); + return (intr_irq_config(irq, trig, pol)); } static int @@ -298,7 +298,7 @@ static int nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) { - return (arm_teardown_intr(ih)); + return (intr_irq_remove_handler(child, rman_get_start(r), ih)); } #ifdef SMP @@ -306,7 +306,7 @@ static int nexus_bind_intr(device_t dev, device_t child, struct resource *irq, int cpu) { - return (arm_intr_bind(rman_get_start(irq), cpu)); + return (intr_irq_bind(rman_get_start(irq), cpu)); } #endif diff --git a/sys/arm64/arm64/pmap.c b/sys/arm64/arm64/pmap.c index 5db731d..5eeccdd 100644 --- a/sys/arm64/arm64/pmap.c +++ b/sys/arm64/arm64/pmap.c @@ -11,7 +11,7 @@ * All rights reserved. * Copyright (c) 2014 Andrew Turner * All rights reserved. - * Copyright (c) 2014 The FreeBSD Foundation + * Copyright (c) 2014-2016 The FreeBSD Foundation * All rights reserved. * * This code is derived from software contributed to Berkeley by @@ -142,9 +142,14 @@ __FBSDID("$FreeBSD$"); #include <machine/md_var.h> #include <machine/pcb.h> -#define NPDEPG (PAGE_SIZE/(sizeof (pd_entry_t))) -#define NUPDE (NPDEPG * NPDEPG) -#define NUSERPGTBLS (NUPDE + NPDEPG) +#define NL0PG (PAGE_SIZE/(sizeof (pd_entry_t))) +#define NL1PG (PAGE_SIZE/(sizeof (pd_entry_t))) +#define NL2PG (PAGE_SIZE/(sizeof (pd_entry_t))) +#define NL3PG (PAGE_SIZE/(sizeof (pt_entry_t))) + +#define NUL0E L0_ENTRIES +#define NUL1E (NUL0E * NL1PG) +#define NUL2E (NUL1E * NL2PG) #if !defined(DIAGNOSTIC) #ifdef __GNUC_GNU_INLINE__ @@ -266,22 +271,37 @@ pagecopy(void *s, void *d) memcpy(d, s, PAGE_SIZE); } -static __inline void -pagezero(void *p) +#define pmap_l0_index(va) (((va) >> L0_SHIFT) & L0_ADDR_MASK) +#define pmap_l1_index(va) (((va) >> L1_SHIFT) & Ln_ADDR_MASK) +#define pmap_l2_index(va) (((va) >> L2_SHIFT) & Ln_ADDR_MASK) +#define pmap_l3_index(va) (((va) >> L3_SHIFT) & Ln_ADDR_MASK) + +static __inline pd_entry_t * +pmap_l0(pmap_t pmap, vm_offset_t va) { - bzero(p, PAGE_SIZE); + return (&pmap->pm_l0[pmap_l0_index(va)]); } -#define pmap_l1_index(va) (((va) >> L1_SHIFT) & Ln_ADDR_MASK) -#define pmap_l2_index(va) (((va) >> L2_SHIFT) & Ln_ADDR_MASK) -#define pmap_l3_index(va) (((va) >> L3_SHIFT) & Ln_ADDR_MASK) +static __inline pd_entry_t * +pmap_l0_to_l1(pd_entry_t *l0, vm_offset_t va) +{ + pd_entry_t *l1; + + l1 = (pd_entry_t *)PHYS_TO_DMAP(pmap_load(l0) & ~ATTR_MASK); + return (&l1[pmap_l1_index(va)]); +} static __inline pd_entry_t * pmap_l1(pmap_t pmap, vm_offset_t va) { + pd_entry_t *l0; - return (&pmap->pm_l1[pmap_l1_index(va)]); + l0 = pmap_l0(pmap, va); + if ((pmap_load(l0) & ATTR_DESCR_MASK) != L0_TABLE) + return (NULL); + + return (pmap_l0_to_l1(l0, va)); } static __inline pd_entry_t * @@ -314,28 +334,103 @@ pmap_l2_to_l3(pd_entry_t *l2, vm_offset_t va) return (&l3[pmap_l3_index(va)]); } +/* + * Returns the lowest valid pde for a given virtual address. + * The next level may or may not point to a valid page or block. + */ +static __inline pd_entry_t * +pmap_pde(pmap_t pmap, vm_offset_t va, int *level) +{ + pd_entry_t *l0, *l1, *l2, desc; + + l0 = pmap_l0(pmap, va); + desc = pmap_load(l0) & ATTR_DESCR_MASK; + if (desc != L0_TABLE) { + *level = -1; + return (NULL); + } + + l1 = pmap_l0_to_l1(l0, va); + desc = pmap_load(l1) & ATTR_DESCR_MASK; + if (desc != L1_TABLE) { + *level = 0; + return (l0); + } + + l2 = pmap_l1_to_l2(l1, va); + desc = pmap_load(l2) & ATTR_DESCR_MASK; + if (desc != L2_TABLE) { + *level = 1; + return (l1); + } + + *level = 2; + return (l2); +} + +/* + * Returns the lowest valid pte block or table entry for a given virtual + * address. If there are no valid entries return NULL and set the level to + * the first invalid level. + */ static __inline pt_entry_t * -pmap_l3(pmap_t pmap, vm_offset_t va) +pmap_pte(pmap_t pmap, vm_offset_t va, int *level) { - pd_entry_t *l2; + pd_entry_t *l1, *l2, desc; + pt_entry_t *l3; - l2 = pmap_l2(pmap, va); - if (l2 == NULL || (pmap_load(l2) & ATTR_DESCR_MASK) != L2_TABLE) + l1 = pmap_l1(pmap, va); + if (l1 == NULL) { + *level = 0; return (NULL); + } + desc = pmap_load(l1) & ATTR_DESCR_MASK; + if (desc == L1_BLOCK) { + *level = 1; + return (l1); + } + + if (desc != L1_TABLE) { + *level = 1; + return (NULL); + } + + l2 = pmap_l1_to_l2(l1, va); + desc = pmap_load(l2) & ATTR_DESCR_MASK; + if (desc == L2_BLOCK) { + *level = 2; + return (l2); + } - return (pmap_l2_to_l3(l2, va)); + if (desc != L2_TABLE) { + *level = 2; + return (NULL); + } + + *level = 3; + l3 = pmap_l2_to_l3(l2, va); + if ((pmap_load(l3) & ATTR_DESCR_MASK) != L3_PAGE) + return (NULL); + + return (l3); } bool -pmap_get_tables(pmap_t pmap, vm_offset_t va, pd_entry_t **l1, pd_entry_t **l2, - pt_entry_t **l3) +pmap_get_tables(pmap_t pmap, vm_offset_t va, pd_entry_t **l0, pd_entry_t **l1, + pd_entry_t **l2, pt_entry_t **l3) { - pd_entry_t *l1p, *l2p; + pd_entry_t *l0p, *l1p, *l2p; - if (pmap->pm_l1 == NULL) + if (pmap->pm_l0 == NULL) return (false); - l1p = pmap_l1(pmap, va); + l0p = pmap_l0(pmap, va); + *l0 = l0p; + + if ((pmap_load(l0p) & ATTR_DESCR_MASK) != L0_TABLE) + return (false); + + l1p = pmap_l0_to_l1(l0p, va); *l1 = l1p; if ((pmap_load(l1p) & ATTR_DESCR_MASK) == L1_BLOCK) { @@ -544,7 +639,8 @@ pmap_bootstrap_l3(vm_offset_t l1pt, vm_offset_t va, vm_offset_t l3_start) * Bootstrap the system enough to run with virtual memory. */ void -pmap_bootstrap(vm_offset_t l1pt, vm_paddr_t kernstart, vm_size_t kernlen) +pmap_bootstrap(vm_offset_t l0pt, vm_offset_t l1pt, vm_paddr_t kernstart, + vm_size_t kernlen) { u_int l1_slot, l2_slot, avail_slot, map_slot, used_map_slot; uint64_t kern_delta; @@ -562,7 +658,7 @@ pmap_bootstrap(vm_offset_t l1pt, vm_paddr_t kernstart, vm_size_t kernlen) printf("%lx\n", (KERNBASE >> L1_SHIFT) & Ln_ADDR_MASK); /* Set this early so we can use the pagetable walking functions */ - kernel_pmap_store.pm_l1 = (pd_entry_t *)l1pt; + kernel_pmap_store.pm_l0 = (pd_entry_t *)l0pt; PMAP_LOCK_INIT(kernel_pmap); /* @@ -805,30 +901,40 @@ pmap_invalidate_all(pmap_t pmap) vm_paddr_t pmap_extract(pmap_t pmap, vm_offset_t va) { - pd_entry_t *l2p, l2; - pt_entry_t *l3p, l3; + pt_entry_t *pte, tpte; vm_paddr_t pa; + int lvl; pa = 0; PMAP_LOCK(pmap); /* - * Start with the l2 tabel. We are unable to allocate - * pages in the l1 table. + * Find the block or page map for this virtual address. pmap_pte + * will return either a valid block/page entry, or NULL. */ - l2p = pmap_l2(pmap, va); - if (l2p != NULL) { - l2 = pmap_load(l2p); - if ((l2 & ATTR_DESCR_MASK) == L2_TABLE) { - l3p = pmap_l2_to_l3(l2p, va); - if (l3p != NULL) { - l3 = pmap_load(l3p); - - if ((l3 & ATTR_DESCR_MASK) == L3_PAGE) - pa = (l3 & ~ATTR_MASK) | - (va & L3_OFFSET); - } - } else if ((l2 & ATTR_DESCR_MASK) == L2_BLOCK) - pa = (l2 & ~ATTR_MASK) | (va & L2_OFFSET); + pte = pmap_pte(pmap, va, &lvl); + if (pte != NULL) { + tpte = pmap_load(pte); + pa = tpte & ~ATTR_MASK; + switch(lvl) { + case 1: + KASSERT((tpte & ATTR_DESCR_MASK) == L1_BLOCK, + ("pmap_extract: Invalid L1 pte found: %lx", + tpte & ATTR_DESCR_MASK)); + pa |= (va & L1_OFFSET); + break; + case 2: + KASSERT((tpte & ATTR_DESCR_MASK) == L2_BLOCK, + ("pmap_extract: Invalid L2 pte found: %lx", + tpte & ATTR_DESCR_MASK)); + pa |= (va & L2_OFFSET); + break; + case 3: + KASSERT((tpte & ATTR_DESCR_MASK) == L3_PAGE, + ("pmap_extract: Invalid L3 pte found: %lx", + tpte & ATTR_DESCR_MASK)); + pa |= (va & L3_OFFSET); + break; + } } PMAP_UNLOCK(pmap); return (pa); @@ -844,21 +950,31 @@ pmap_extract(pmap_t pmap, vm_offset_t va) vm_page_t pmap_extract_and_hold(pmap_t pmap, vm_offset_t va, vm_prot_t prot) { - pt_entry_t *l3p, l3; + pt_entry_t *pte, tpte; vm_paddr_t pa; vm_page_t m; + int lvl; pa = 0; m = NULL; PMAP_LOCK(pmap); retry: - l3p = pmap_l3(pmap, va); - if (l3p != NULL && (l3 = pmap_load(l3p)) != 0) { - if (((l3 & ATTR_AP_RW_BIT) == ATTR_AP(ATTR_AP_RW)) || + pte = pmap_pte(pmap, va, &lvl); + if (pte != NULL) { + tpte = pmap_load(pte); + + KASSERT(lvl > 0 && lvl <= 3, + ("pmap_extract_and_hold: Invalid level %d", lvl)); + CTASSERT(L1_BLOCK == L2_BLOCK); + KASSERT((lvl == 3 && (tpte & ATTR_DESCR_MASK) == L3_PAGE) || + (lvl < 3 && (tpte & ATTR_DESCR_MASK) == L1_BLOCK), + ("pmap_extract_and_hold: Invalid pte at L%d: %lx", lvl, + tpte & ATTR_DESCR_MASK)); + if (((tpte & ATTR_AP_RW_BIT) == ATTR_AP(ATTR_AP_RW)) || ((prot & VM_PROT_WRITE) == 0)) { - if (vm_page_pa_tryrelock(pmap, l3 & ~ATTR_MASK, &pa)) + if (vm_page_pa_tryrelock(pmap, tpte & ~ATTR_MASK, &pa)) goto retry; - m = PHYS_TO_VM_PAGE(l3 & ~ATTR_MASK); + m = PHYS_TO_VM_PAGE(tpte & ~ATTR_MASK); vm_page_hold(m); } } @@ -870,25 +986,39 @@ retry: vm_paddr_t pmap_kextract(vm_offset_t va) { - pd_entry_t *l2p, l2; - pt_entry_t *l3; + pt_entry_t *pte, tpte; vm_paddr_t pa; + int lvl; if (va >= DMAP_MIN_ADDRESS && va < DMAP_MAX_ADDRESS) { pa = DMAP_TO_PHYS(va); } else { - l2p = pmap_l2(kernel_pmap, va); - if (l2p == NULL) - panic("pmap_kextract: No l2"); - l2 = pmap_load(l2p); - if ((l2 & ATTR_DESCR_MASK) == L2_BLOCK) - return ((l2 & ~ATTR_MASK) | - (va & L2_OFFSET)); - - l3 = pmap_l2_to_l3(l2p, va); - if (l3 == NULL) - panic("pmap_kextract: No l3..."); - pa = (pmap_load(l3) & ~ATTR_MASK) | (va & PAGE_MASK); + pa = 0; + pte = pmap_pte(kernel_pmap, va, &lvl); + if (pte != NULL) { + tpte = pmap_load(pte); + pa = tpte & ~ATTR_MASK; + switch(lvl) { + case 1: + KASSERT((tpte & ATTR_DESCR_MASK) == L1_BLOCK, + ("pmap_kextract: Invalid L1 pte found: %lx", + tpte & ATTR_DESCR_MASK)); + pa |= (va & L1_OFFSET); + break; + case 2: + KASSERT((tpte & ATTR_DESCR_MASK) == L2_BLOCK, + ("pmap_kextract: Invalid L2 pte found: %lx", + tpte & ATTR_DESCR_MASK)); + pa |= (va & L2_OFFSET); + break; + case 3: + KASSERT((tpte & ATTR_DESCR_MASK) == L3_PAGE, + ("pmap_kextract: Invalid L3 pte found: %lx", + tpte & ATTR_DESCR_MASK)); + pa |= (va & L3_OFFSET); + break; + } + } } return (pa); } @@ -900,8 +1030,10 @@ pmap_kextract(vm_offset_t va) void pmap_kenter_device(vm_offset_t sva, vm_size_t size, vm_paddr_t pa) { - pt_entry_t *l3; + pd_entry_t *pde; + pt_entry_t *pte; vm_offset_t va; + int lvl; KASSERT((pa & L3_OFFSET) == 0, ("pmap_kenter_device: Invalid physical address")); @@ -912,11 +1044,16 @@ pmap_kenter_device(vm_offset_t sva, vm_size_t size, vm_paddr_t pa) va = sva; while (size != 0) { - l3 = pmap_l3(kernel_pmap, va); - KASSERT(l3 != NULL, ("Invalid page table, va: 0x%lx", va)); - pmap_load_store(l3, (pa & ~L3_OFFSET) | ATTR_DEFAULT | + pde = pmap_pde(kernel_pmap, va, &lvl); + KASSERT(pde != NULL, + ("pmap_kenter_device: Invalid page entry, va: 0x%lx", va)); + KASSERT(lvl == 2, + ("pmap_kenter_device: Invalid level %d", lvl)); + + pte = pmap_l2_to_l3(pde, va); + pmap_load_store(pte, (pa & ~L3_OFFSET) | ATTR_DEFAULT | ATTR_IDX(DEVICE_MEMORY) | L3_PAGE); - PTE_SYNC(l3); + PTE_SYNC(pte); va += PAGE_SIZE; pa += PAGE_SIZE; @@ -927,28 +1064,30 @@ pmap_kenter_device(vm_offset_t sva, vm_size_t size, vm_paddr_t pa) /* * Remove a page from the kernel pagetables. - * Note: not SMP coherent. */ PMAP_INLINE void pmap_kremove(vm_offset_t va) { - pt_entry_t *l3; + pt_entry_t *pte; + int lvl; - l3 = pmap_l3(kernel_pmap, va); - KASSERT(l3 != NULL, ("pmap_kremove: Invalid address")); + pte = pmap_pte(kernel_pmap, va, &lvl); + KASSERT(pte != NULL, ("pmap_kremove: Invalid address")); + KASSERT(lvl == 3, ("pmap_kremove: Invalid pte level %d", lvl)); - if (pmap_l3_valid_cacheable(pmap_load(l3))) + if (pmap_l3_valid_cacheable(pmap_load(pte))) cpu_dcache_wb_range(va, L3_SIZE); - pmap_load_clear(l3); - PTE_SYNC(l3); + pmap_load_clear(pte); + PTE_SYNC(pte); pmap_invalidate_page(kernel_pmap, va); } void pmap_kremove_device(vm_offset_t sva, vm_size_t size) { - pt_entry_t *l3; + pt_entry_t *pte; vm_offset_t va; + int lvl; KASSERT((sva & L3_OFFSET) == 0, ("pmap_kremove_device: Invalid virtual address")); @@ -957,10 +1096,12 @@ pmap_kremove_device(vm_offset_t sva, vm_size_t size) va = sva; while (size != 0) { - l3 = pmap_l3(kernel_pmap, va); - KASSERT(l3 != NULL, ("Invalid page table, va: 0x%lx", va)); - pmap_load_clear(l3); - PTE_SYNC(l3); + pte = pmap_pte(kernel_pmap, va, &lvl); + KASSERT(pte != NULL, ("Invalid page table, va: 0x%lx", va)); + KASSERT(lvl == 3, + ("Invalid device pagetable level: %d != 3", lvl)); + pmap_load_clear(pte); + PTE_SYNC(pte); va += PAGE_SIZE; size -= PAGE_SIZE; @@ -999,19 +1140,26 @@ pmap_map(vm_offset_t *virt, vm_paddr_t start, vm_paddr_t end, int prot) void pmap_qenter(vm_offset_t sva, vm_page_t *ma, int count) { - pt_entry_t *l3, pa; + pd_entry_t *pde; + pt_entry_t *pte, pa; vm_offset_t va; vm_page_t m; - int i; + int i, lvl; va = sva; for (i = 0; i < count; i++) { + pde = pmap_pde(kernel_pmap, va, &lvl); + KASSERT(pde != NULL, + ("pmap_qenter: Invalid page entry, va: 0x%lx", va)); + KASSERT(lvl == 2, + ("pmap_qenter: Invalid level %d", lvl)); + m = ma[i]; pa = VM_PAGE_TO_PHYS(m) | ATTR_DEFAULT | ATTR_AP(ATTR_AP_RW) | ATTR_IDX(m->md.pv_memattr) | L3_PAGE; - l3 = pmap_l3(kernel_pmap, va); - pmap_load_store(l3, pa); - PTE_SYNC(l3); + pte = pmap_l2_to_l3(pde, va); + pmap_load_store(pte, pa); + PTE_SYNC(pte); va += L3_SIZE; } @@ -1021,25 +1169,27 @@ pmap_qenter(vm_offset_t sva, vm_page_t *ma, int count) /* * This routine tears out page mappings from the * kernel -- it is meant only for temporary mappings. - * Note: SMP coherent. Uses a ranged shootdown IPI. */ void pmap_qremove(vm_offset_t sva, int count) { - pt_entry_t *l3; + pt_entry_t *pte; vm_offset_t va; + int lvl; KASSERT(sva >= VM_MIN_KERNEL_ADDRESS, ("usermode va %lx", sva)); va = sva; while (count-- > 0) { - l3 = pmap_l3(kernel_pmap, va); - KASSERT(l3 != NULL, ("pmap_kremove: Invalid address")); - - if (pmap_l3_valid_cacheable(pmap_load(l3))) - cpu_dcache_wb_range(va, L3_SIZE); - pmap_load_clear(l3); - PTE_SYNC(l3); + pte = pmap_pte(kernel_pmap, va, &lvl); + KASSERT(lvl == 3, + ("Invalid device pagetable level: %d != 3", lvl)); + if (pte != NULL) { + if (pmap_l3_valid_cacheable(pmap_load(pte))) + cpu_dcache_wb_range(va, L3_SIZE); + pmap_load_clear(pte); + PTE_SYNC(pte); + } va += PAGE_SIZE; } @@ -1104,26 +1254,47 @@ _pmap_unwire_l3(pmap_t pmap, vm_offset_t va, vm_page_t m, struct spglist *free) /* * unmap the page table page */ - if (m->pindex >= NUPDE) { - /* PD page */ + if (m->pindex >= (NUL2E + NUL1E)) { + /* l1 page */ + pd_entry_t *l0; + + l0 = pmap_l0(pmap, va); + pmap_load_clear(l0); + PTE_SYNC(l0); + } else if (m->pindex >= NUL2E) { + /* l2 page */ pd_entry_t *l1; + l1 = pmap_l1(pmap, va); pmap_load_clear(l1); PTE_SYNC(l1); } else { - /* PTE page */ + /* l3 page */ pd_entry_t *l2; + l2 = pmap_l2(pmap, va); pmap_load_clear(l2); PTE_SYNC(l2); } pmap_resident_count_dec(pmap, 1); - if (m->pindex < NUPDE) { - /* We just released a PT, unhold the matching PD */ - vm_page_t pdpg; + if (m->pindex < NUL2E) { + /* We just released an l3, unhold the matching l2 */ + pd_entry_t *l1, tl1; + vm_page_t l2pg; - pdpg = PHYS_TO_VM_PAGE(*pmap_l1(pmap, va) & ~ATTR_MASK); - pmap_unwire_l3(pmap, va, pdpg, free); + l1 = pmap_l1(pmap, va); + tl1 = pmap_load(l1); + l2pg = PHYS_TO_VM_PAGE(tl1 & ~ATTR_MASK); + pmap_unwire_l3(pmap, va, l2pg, free); + } else if (m->pindex < (NUL2E + NUL1E)) { + /* We just released an l2, unhold the matching l1 */ + pd_entry_t *l0, tl0; + vm_page_t l1pg; + + l0 = pmap_l0(pmap, va); + tl0 = pmap_load(l0); + l1pg = PHYS_TO_VM_PAGE(tl0 & ~ATTR_MASK); + pmap_unwire_l3(pmap, va, l1pg, free); } pmap_invalidate_page(pmap, va); @@ -1164,27 +1335,27 @@ pmap_pinit0(pmap_t pmap) PMAP_LOCK_INIT(pmap); bzero(&pmap->pm_stats, sizeof(pmap->pm_stats)); - pmap->pm_l1 = kernel_pmap->pm_l1; + pmap->pm_l0 = kernel_pmap->pm_l0; } int pmap_pinit(pmap_t pmap) { - vm_paddr_t l1phys; - vm_page_t l1pt; + vm_paddr_t l0phys; + vm_page_t l0pt; /* - * allocate the l1 page + * allocate the l0 page */ - while ((l1pt = vm_page_alloc(NULL, 0xdeadbeef, VM_ALLOC_NORMAL | + while ((l0pt = vm_page_alloc(NULL, 0, VM_ALLOC_NORMAL | VM_ALLOC_NOOBJ | VM_ALLOC_WIRED | VM_ALLOC_ZERO)) == NULL) VM_WAIT; - l1phys = VM_PAGE_TO_PHYS(l1pt); - pmap->pm_l1 = (pd_entry_t *)PHYS_TO_DMAP(l1phys); + l0phys = VM_PAGE_TO_PHYS(l0pt); + pmap->pm_l0 = (pd_entry_t *)PHYS_TO_DMAP(l0phys); - if ((l1pt->flags & PG_ZERO) == 0) - pagezero(pmap->pm_l1); + if ((l0pt->flags & PG_ZERO) == 0) + pagezero(pmap->pm_l0); bzero(&pmap->pm_stats, sizeof(pmap->pm_stats)); @@ -1205,7 +1376,7 @@ pmap_pinit(pmap_t pmap) static vm_page_t _pmap_alloc_l3(pmap_t pmap, vm_pindex_t ptepindex, struct rwlock **lockp) { - vm_page_t m, /*pdppg, */pdpg; + vm_page_t m, l1pg, l2pg; PMAP_LOCK_ASSERT(pmap, MA_OWNED); @@ -1237,33 +1408,84 @@ _pmap_alloc_l3(pmap_t pmap, vm_pindex_t ptepindex, struct rwlock **lockp) * it isn't already there. */ - if (ptepindex >= NUPDE) { - pd_entry_t *l1; - vm_pindex_t l1index; + if (ptepindex >= (NUL2E + NUL1E)) { + pd_entry_t *l0; + vm_pindex_t l0index; + + l0index = ptepindex - (NUL2E + NUL1E); + l0 = &pmap->pm_l0[l0index]; + pmap_load_store(l0, VM_PAGE_TO_PHYS(m) | L0_TABLE); + PTE_SYNC(l0); + } else if (ptepindex >= NUL2E) { + vm_pindex_t l0index, l1index; + pd_entry_t *l0, *l1; + pd_entry_t tl0; + + l1index = ptepindex - NUL2E; + l0index = l1index >> L0_ENTRIES_SHIFT; + + l0 = &pmap->pm_l0[l0index]; + tl0 = pmap_load(l0); + if (tl0 == 0) { + /* recurse for allocating page dir */ + if (_pmap_alloc_l3(pmap, NUL2E + NUL1E + l0index, + lockp) == NULL) { + --m->wire_count; + /* XXX: release mem barrier? */ + atomic_subtract_int(&vm_cnt.v_wire_count, 1); + vm_page_free_zero(m); + return (NULL); + } + } else { + l1pg = PHYS_TO_VM_PAGE(tl0 & ~ATTR_MASK); + l1pg->wire_count++; + } - l1index = ptepindex - NUPDE; - l1 = &pmap->pm_l1[l1index]; + l1 = (pd_entry_t *)PHYS_TO_DMAP(pmap_load(l0) & ~ATTR_MASK); + l1 = &l1[ptepindex & Ln_ADDR_MASK]; pmap_load_store(l1, VM_PAGE_TO_PHYS(m) | L1_TABLE); PTE_SYNC(l1); - } else { - vm_pindex_t l1index; - pd_entry_t *l1, *l2; + vm_pindex_t l0index, l1index; + pd_entry_t *l0, *l1, *l2; + pd_entry_t tl0, tl1; - l1index = ptepindex >> (L1_SHIFT - L2_SHIFT); - l1 = &pmap->pm_l1[l1index]; - if (pmap_load(l1) == 0) { + l1index = ptepindex >> Ln_ENTRIES_SHIFT; + l0index = l1index >> L0_ENTRIES_SHIFT; + + l0 = &pmap->pm_l0[l0index]; + tl0 = pmap_load(l0); + if (tl0 == 0) { /* recurse for allocating page dir */ - if (_pmap_alloc_l3(pmap, NUPDE + l1index, + if (_pmap_alloc_l3(pmap, NUL2E + l1index, lockp) == NULL) { --m->wire_count; atomic_subtract_int(&vm_cnt.v_wire_count, 1); vm_page_free_zero(m); return (NULL); } + tl0 = pmap_load(l0); + l1 = (pd_entry_t *)PHYS_TO_DMAP(tl0 & ~ATTR_MASK); + l1 = &l1[l1index & Ln_ADDR_MASK]; } else { - pdpg = PHYS_TO_VM_PAGE(pmap_load(l1) & ~ATTR_MASK); - pdpg->wire_count++; + l1 = (pd_entry_t *)PHYS_TO_DMAP(tl0 & ~ATTR_MASK); + l1 = &l1[l1index & Ln_ADDR_MASK]; + tl1 = pmap_load(l1); + if (tl1 == 0) { + /* recurse for allocating page dir */ + if (_pmap_alloc_l3(pmap, NUL2E + l1index, + lockp) == NULL) { + --m->wire_count; + /* XXX: release mem barrier? */ + atomic_subtract_int( + &vm_cnt.v_wire_count, 1); + vm_page_free_zero(m); + return (NULL); + } + } else { + l2pg = PHYS_TO_VM_PAGE(tl1 & ~ATTR_MASK); + l2pg->wire_count++; + } } l2 = (pd_entry_t *)PHYS_TO_DMAP(pmap_load(l1) & ~ATTR_MASK); @@ -1281,8 +1503,9 @@ static vm_page_t pmap_alloc_l3(pmap_t pmap, vm_offset_t va, struct rwlock **lockp) { vm_pindex_t ptepindex; - pd_entry_t *l2; + pd_entry_t *pde, tpde; vm_page_t m; + int lvl; /* * Calculate pagetable page index @@ -1292,24 +1515,29 @@ retry: /* * Get the page directory entry */ - l2 = pmap_l2(pmap, va); + pde = pmap_pde(pmap, va, &lvl); /* - * If the page table page is mapped, we just increment the - * hold count, and activate it. + * If the page table page is mapped, we just increment the hold count, + * and activate it. If we get a level 2 pde it will point to a level 3 + * table. */ - if (l2 != NULL && pmap_load(l2) != 0) { - m = PHYS_TO_VM_PAGE(pmap_load(l2) & ~ATTR_MASK); - m->wire_count++; - } else { - /* - * Here if the pte page isn't mapped, or if it has been - * deallocated. - */ - m = _pmap_alloc_l3(pmap, ptepindex, lockp); - if (m == NULL && lockp != NULL) - goto retry; + if (lvl == 2) { + tpde = pmap_load(pde); + if (tpde != 0) { + m = PHYS_TO_VM_PAGE(tpde & ~ATTR_MASK); + m->wire_count++; + return (m); + } } + + /* + * Here if the pte page isn't mapped, or if it has been deallocated. + */ + m = _pmap_alloc_l3(pmap, ptepindex, lockp); + if (m == NULL && lockp != NULL) + goto retry; + return (m); } @@ -1332,7 +1560,7 @@ pmap_release(pmap_t pmap) ("pmap_release: pmap resident count %ld != 0", pmap->pm_stats.resident_count)); - m = PHYS_TO_VM_PAGE(DMAP_TO_PHYS((vm_offset_t)pmap->pm_l1)); + m = PHYS_TO_VM_PAGE(DMAP_TO_PHYS((vm_offset_t)pmap->pm_l0)); m->wire_count--; atomic_subtract_int(&vm_cnt.v_wire_count, 1); @@ -1369,7 +1597,7 @@ pmap_growkernel(vm_offset_t addr) { vm_paddr_t paddr; vm_page_t nkpg; - pd_entry_t *l1, *l2; + pd_entry_t *l0, *l1, *l2; mtx_assert(&kernel_map->system_mtx, MA_OWNED); @@ -1377,7 +1605,11 @@ pmap_growkernel(vm_offset_t addr) if (addr - 1 >= kernel_map->max_offset) addr = kernel_map->max_offset; while (kernel_vm_end < addr) { - l1 = pmap_l1(kernel_pmap, kernel_vm_end); + l0 = pmap_l0(kernel_pmap, kernel_vm_end); + KASSERT(pmap_load(l0) != 0, + ("pmap_growkernel: No level 0 kernel entry")); + + l1 = pmap_l0_to_l1(l0, kernel_vm_end); if (pmap_load(l1) == 0) { /* We need a new PDP entry */ nkpg = vm_page_alloc(NULL, kernel_vm_end >> L1_SHIFT, @@ -1716,7 +1948,7 @@ pmap_remove(pmap_t pmap, vm_offset_t sva, vm_offset_t eva) { struct rwlock *lock; vm_offset_t va, va_next; - pd_entry_t *l1, *l2; + pd_entry_t *l0, *l1, *l2; pt_entry_t l3_paddr, *l3; struct spglist free; int anyvalid; @@ -1739,7 +1971,15 @@ pmap_remove(pmap_t pmap, vm_offset_t sva, vm_offset_t eva) if (pmap->pm_stats.resident_count == 0) break; - l1 = pmap_l1(pmap, sva); + l0 = pmap_l0(pmap, sva); + if (pmap_load(l0) == 0) { + va_next = (sva + L0_SIZE) & ~L0_OFFSET; + if (va_next < sva) + va_next = eva; + continue; + } + + l1 = pmap_l0_to_l1(l0, sva); if (pmap_load(l1) == 0) { va_next = (sva + L1_SIZE) & ~L1_OFFSET; if (va_next < sva) @@ -1824,9 +2064,10 @@ pmap_remove_all(vm_page_t m) { pv_entry_t pv; pmap_t pmap; - pt_entry_t *l3, tl3; - pd_entry_t *l2, tl2; + pd_entry_t *pde, tpde; + pt_entry_t *pte, tpte; struct spglist free; + int lvl; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_remove_all: page %p is not managed", m)); @@ -1836,30 +2077,33 @@ pmap_remove_all(vm_page_t m) pmap = PV_PMAP(pv); PMAP_LOCK(pmap); pmap_resident_count_dec(pmap, 1); - l2 = pmap_l2(pmap, pv->pv_va); - KASSERT(l2 != NULL, ("pmap_remove_all: no l2 table found")); - tl2 = pmap_load(l2); - KASSERT((tl2 & ATTR_DESCR_MASK) == L2_TABLE, - ("pmap_remove_all: found a table when expecting " - "a block in %p's pv list", m)); - l3 = pmap_l2_to_l3(l2, pv->pv_va); + + pde = pmap_pde(pmap, pv->pv_va, &lvl); + KASSERT(pde != NULL, + ("pmap_remove_all: no page directory entry found")); + KASSERT(lvl == 2, + ("pmap_remove_all: invalid pde level %d", lvl)); + tpde = pmap_load(pde); + + pte = pmap_l2_to_l3(pde, pv->pv_va); + tpte = pmap_load(pte); if (pmap_is_current(pmap) && - pmap_l3_valid_cacheable(pmap_load(l3))) + pmap_l3_valid_cacheable(tpte)) cpu_dcache_wb_range(pv->pv_va, L3_SIZE); - tl3 = pmap_load_clear(l3); - PTE_SYNC(l3); + pmap_load_clear(pte); + PTE_SYNC(pte); pmap_invalidate_page(pmap, pv->pv_va); - if (tl3 & ATTR_SW_WIRED) + if (tpte & ATTR_SW_WIRED) pmap->pm_stats.wired_count--; - if ((tl3 & ATTR_AF) != 0) + if ((tpte & ATTR_AF) != 0) vm_page_aflag_set(m, PGA_REFERENCED); /* * Update the vm_page_t clean and reference bits. */ - if (pmap_page_dirty(tl3)) + if (pmap_page_dirty(tpte)) vm_page_dirty(m); - pmap_unuse_l3(pmap, pv->pv_va, tl2, &free); + pmap_unuse_l3(pmap, pv->pv_va, tpde, &free); TAILQ_REMOVE(&m->md.pv_list, pv, pv_next); m->md.pv_gen++; free_pv_entry(pmap, pv); @@ -1878,7 +2122,7 @@ void pmap_protect(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot) { vm_offset_t va, va_next; - pd_entry_t *l1, *l2; + pd_entry_t *l0, *l1, *l2; pt_entry_t *l3p, l3; if ((prot & VM_PROT_READ) == VM_PROT_NONE) { @@ -1892,7 +2136,15 @@ pmap_protect(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot) PMAP_LOCK(pmap); for (; sva < eva; sva = va_next) { - l1 = pmap_l1(pmap, sva); + l0 = pmap_l0(pmap, sva); + if (pmap_load(l0) == 0) { + va_next = (sva + L0_SIZE) & ~L0_OFFSET; + if (va_next < sva) + va_next = eva; + continue; + } + + l1 = pmap_l0_to_l1(l0, sva); if (pmap_load(l1) == 0) { va_next = (sva + L1_SIZE) & ~L1_OFFSET; if (va_next < sva) @@ -1946,13 +2198,14 @@ pmap_enter(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, u_int flags, int8_t psind __unused) { struct rwlock *lock; - pd_entry_t *l1, *l2; + pd_entry_t *pde; pt_entry_t new_l3, orig_l3; pt_entry_t *l3; pv_entry_t pv; - vm_paddr_t opa, pa, l2_pa, l3_pa; - vm_page_t mpte, om, l2_m, l3_m; + vm_paddr_t opa, pa, l1_pa, l2_pa, l3_pa; + vm_page_t mpte, om, l1_m, l2_m, l3_m; boolean_t nosleep; + int lvl; va = trunc_page(va); if ((m->oflags & VPO_UNMANAGED) == 0 && !vm_page_xbusied(m)) @@ -1986,14 +2239,44 @@ pmap_enter(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, PMAP_UNLOCK(pmap); return (KERN_RESOURCE_SHORTAGE); } - l3 = pmap_l3(pmap, va); + pde = pmap_pde(pmap, va, &lvl); + KASSERT(pde != NULL, + ("pmap_enter: Invalid page entry, va: 0x%lx", va)); + KASSERT(lvl == 2, + ("pmap_enter: Invalid level %d", lvl)); + + l3 = pmap_l2_to_l3(pde, va); } else { - l3 = pmap_l3(pmap, va); - /* TODO: This is not optimal, but should mostly work */ - if (l3 == NULL) { - l2 = pmap_l2(pmap, va); + pde = pmap_pde(pmap, va, &lvl); + /* + * If we get a level 2 pde it must point to a level 3 entry + * otherwise we will need to create the intermediate tables + */ + if (lvl < 2) { + switch(lvl) { + default: + case -1: + /* Get the l0 pde to update */ + pde = pmap_l0(pmap, va); + KASSERT(pde != NULL, ("...")); + + l1_m = vm_page_alloc(NULL, 0, VM_ALLOC_NORMAL | + VM_ALLOC_NOOBJ | VM_ALLOC_WIRED | + VM_ALLOC_ZERO); + if (l1_m == NULL) + panic("pmap_enter: l1 pte_m == NULL"); + if ((l1_m->flags & PG_ZERO) == 0) + pmap_zero_page(l1_m); + + l1_pa = VM_PAGE_TO_PHYS(l1_m); + pmap_load_store(pde, l1_pa | L0_TABLE); + PTE_SYNC(pde); + /* FALLTHROUGH */ + case 0: + /* Get the l1 pde to update */ + pde = pmap_l1_to_l2(pde, va); + KASSERT(pde != NULL, ("...")); - if (l2 == NULL) { l2_m = vm_page_alloc(NULL, 0, VM_ALLOC_NORMAL | VM_ALLOC_NOOBJ | VM_ALLOC_WIRED | VM_ALLOC_ZERO); @@ -2003,27 +2286,28 @@ pmap_enter(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, pmap_zero_page(l2_m); l2_pa = VM_PAGE_TO_PHYS(l2_m); - l1 = pmap_l1(pmap, va); - pmap_load_store(l1, l2_pa | L1_TABLE); - PTE_SYNC(l1); - l2 = pmap_l1_to_l2(l1, va); + pmap_load_store(pde, l2_pa | L1_TABLE); + PTE_SYNC(pde); + /* FALLTHROUGH */ + case 1: + /* Get the l2 pde to update */ + pde = pmap_l1_to_l2(pde, va); + + l3_m = vm_page_alloc(NULL, 0, VM_ALLOC_NORMAL | + VM_ALLOC_NOOBJ | VM_ALLOC_WIRED | + VM_ALLOC_ZERO); + if (l3_m == NULL) + panic("pmap_enter: l3 pte_m == NULL"); + if ((l3_m->flags & PG_ZERO) == 0) + pmap_zero_page(l3_m); + + l3_pa = VM_PAGE_TO_PHYS(l3_m); + pmap_load_store(pde, l3_pa | L2_TABLE); + PTE_SYNC(pde); + break; } - - KASSERT(l2 != NULL, - ("No l2 table after allocating one")); - - l3_m = vm_page_alloc(NULL, 0, VM_ALLOC_NORMAL | - VM_ALLOC_NOOBJ | VM_ALLOC_WIRED | VM_ALLOC_ZERO); - if (l3_m == NULL) - panic("pmap_enter: l3 pte_m == NULL"); - if ((l3_m->flags & PG_ZERO) == 0) - pmap_zero_page(l3_m); - - l3_pa = VM_PAGE_TO_PHYS(l3_m); - pmap_load_store(l2, l3_pa | L2_TABLE); - PTE_SYNC(l2); - l3 = pmap_l2_to_l3(l2, va); } + l3 = pmap_l2_to_l3(pde, va); pmap_invalidate_page(pmap, va); } @@ -2207,9 +2491,10 @@ pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, vm_page_t mpte, struct rwlock **lockp) { struct spglist free; - pd_entry_t *l2; + pd_entry_t *pde; pt_entry_t *l3; vm_paddr_t pa; + int lvl; KASSERT(va < kmi.clean_sva || va >= kmi.clean_eva || (m->oflags & VPO_UNMANAGED) != 0, @@ -2235,7 +2520,7 @@ pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, /* * Get the l2 entry */ - l2 = pmap_l2(pmap, va); + pde = pmap_pde(pmap, va, &lvl); /* * If the page table page is mapped, we just increment @@ -2243,9 +2528,9 @@ pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, * attempt to allocate a page table page. If this * attempt fails, we don't retry. Instead, we give up. */ - if (l2 != NULL && pmap_load(l2) != 0) { + if (lvl == 2 && pmap_load(pde) != 0) { mpte = - PHYS_TO_VM_PAGE(pmap_load(l2) & ~ATTR_MASK); + PHYS_TO_VM_PAGE(pmap_load(pde) & ~ATTR_MASK); mpte->wire_count++; } else { /* @@ -2261,10 +2546,15 @@ pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, l3 = &l3[pmap_l3_index(va)]; } else { mpte = NULL; - l3 = pmap_l3(kernel_pmap, va); + pde = pmap_pde(kernel_pmap, va, &lvl); + KASSERT(pde != NULL, + ("pmap_enter_quick_locked: Invalid page entry, va: 0x%lx", + va)); + KASSERT(lvl == 2, + ("pmap_enter_quick_locked: Invalid level %d", lvl)); + l3 = pmap_l2_to_l3(pde, va); } - if (l3 == NULL) - panic("pmap_enter_quick_locked: No l3"); + if (pmap_load(l3) != 0) { if (mpte != NULL) { mpte->wire_count--; @@ -2336,14 +2626,22 @@ void pmap_unwire(pmap_t pmap, vm_offset_t sva, vm_offset_t eva) { vm_offset_t va_next; - pd_entry_t *l1, *l2; + pd_entry_t *l0, *l1, *l2; pt_entry_t *l3; boolean_t pv_lists_locked; pv_lists_locked = FALSE; PMAP_LOCK(pmap); for (; sva < eva; sva = va_next) { - l1 = pmap_l1(pmap, sva); + l0 = pmap_l0(pmap, sva); + if (pmap_load(l0) == 0) { + va_next = (sva + L0_SIZE) & ~L0_OFFSET; + if (va_next < sva) + va_next = eva; + continue; + } + + l1 = pmap_l0_to_l1(l0, sva); if (pmap_load(l1) == 0) { va_next = (sva + L1_SIZE) & ~L1_OFFSET; if (va_next < sva) @@ -2551,9 +2849,9 @@ pmap_page_wired_mappings(vm_page_t m) { struct rwlock *lock; pmap_t pmap; - pt_entry_t *l3; + pt_entry_t *pte; pv_entry_t pv; - int count, md_gen; + int count, lvl, md_gen; if ((m->oflags & VPO_UNMANAGED) != 0) return (0); @@ -2574,8 +2872,8 @@ restart: goto restart; } } - l3 = pmap_l3(pmap, pv->pv_va); - if (l3 != NULL && (pmap_load(l3) & ATTR_SW_WIRED) != 0) + pte = pmap_pte(pmap, pv->pv_va, &lvl); + if (pte != NULL && (pmap_load(pte) & ATTR_SW_WIRED) != 0) count++; PMAP_UNLOCK(pmap); } @@ -2603,8 +2901,8 @@ restart: void pmap_remove_pages(pmap_t pmap) { - pd_entry_t ptepde, *l2; - pt_entry_t *l3, tl3; + pd_entry_t *pde; + pt_entry_t *pte, tpte; struct spglist free; vm_page_t m; pv_entry_t pv; @@ -2612,7 +2910,7 @@ pmap_remove_pages(pmap_t pmap) struct rwlock *lock; int64_t bit; uint64_t inuse, bitmask; - int allfree, field, freed, idx; + int allfree, field, freed, idx, lvl; vm_paddr_t pa; lock = NULL; @@ -2632,44 +2930,51 @@ pmap_remove_pages(pmap_t pmap) pv = &pc->pc_pventry[idx]; inuse &= ~bitmask; - l2 = pmap_l2(pmap, pv->pv_va); - ptepde = pmap_load(l2); - l3 = pmap_l2_to_l3(l2, pv->pv_va); - tl3 = pmap_load(l3); + pde = pmap_pde(pmap, pv->pv_va, &lvl); + KASSERT(pde != NULL, + ("Attempting to remove an unmapped page")); + KASSERT(lvl == 2, + ("Invalid page directory level: %d", lvl)); + + pte = pmap_l2_to_l3(pde, pv->pv_va); + KASSERT(pte != NULL, + ("Attempting to remove an unmapped page")); + + tpte = pmap_load(pte); /* * We cannot remove wired pages from a process' mapping at this time */ - if (tl3 & ATTR_SW_WIRED) { + if (tpte & ATTR_SW_WIRED) { allfree = 0; continue; } - pa = tl3 & ~ATTR_MASK; + pa = tpte & ~ATTR_MASK; m = PHYS_TO_VM_PAGE(pa); KASSERT(m->phys_addr == pa, ("vm_page_t %p phys_addr mismatch %016jx %016jx", m, (uintmax_t)m->phys_addr, - (uintmax_t)tl3)); + (uintmax_t)tpte)); KASSERT((m->flags & PG_FICTITIOUS) != 0 || m < &vm_page_array[vm_page_array_size], - ("pmap_remove_pages: bad l3 %#jx", - (uintmax_t)tl3)); + ("pmap_remove_pages: bad pte %#jx", + (uintmax_t)tpte)); + /* XXX: assumes tpte is level 3 */ if (pmap_is_current(pmap) && - pmap_l3_valid_cacheable(pmap_load(l3))) + pmap_l3_valid_cacheable(tpte)) cpu_dcache_wb_range(pv->pv_va, L3_SIZE); - pmap_load_clear(l3); - PTE_SYNC(l3); + pmap_load_clear(pte); + PTE_SYNC(pte); pmap_invalidate_page(pmap, pv->pv_va); /* * Update the vm_page_t clean/reference bits. */ - if ((tl3 & ATTR_AP_RW_BIT) == - ATTR_AP(ATTR_AP_RW)) + if ((tpte & ATTR_AP_RW_BIT) == ATTR_AP(ATTR_AP_RW)) vm_page_dirty(m); CHANGE_PV_LIST_LOCK_TO_VM_PAGE(&lock, m); @@ -2681,7 +2986,8 @@ pmap_remove_pages(pmap_t pmap) TAILQ_REMOVE(&m->md.pv_list, pv, pv_next); m->md.pv_gen++; - pmap_unuse_l3(pmap, pv->pv_va, ptepde, &free); + pmap_unuse_l3(pmap, pv->pv_va, pmap_load(pde), + &free); freed++; } } @@ -2711,9 +3017,9 @@ pmap_page_test_mappings(vm_page_t m, boolean_t accessed, boolean_t modified) { struct rwlock *lock; pv_entry_t pv; - pt_entry_t *l3, mask, value; + pt_entry_t *pte, mask, value; pmap_t pmap; - int md_gen; + int lvl, md_gen; boolean_t rv; rv = FALSE; @@ -2733,7 +3039,9 @@ restart: goto restart; } } - l3 = pmap_l3(pmap, pv->pv_va); + pte = pmap_pte(pmap, pv->pv_va, &lvl); + KASSERT(lvl == 3, + ("pmap_page_test_mappings: Invalid level %d", lvl)); mask = 0; value = 0; if (modified) { @@ -2744,7 +3052,7 @@ restart: mask |= ATTR_AF | ATTR_DESCR_MASK; value |= ATTR_AF | L3_PAGE; } - rv = (pmap_load(l3) & mask) == value; + rv = (pmap_load(pte) & mask) == value; PMAP_UNLOCK(pmap); if (rv) goto out; @@ -2788,13 +3096,14 @@ pmap_is_modified(vm_page_t m) boolean_t pmap_is_prefaultable(pmap_t pmap, vm_offset_t addr) { - pt_entry_t *l3; + pt_entry_t *pte; boolean_t rv; + int lvl; rv = FALSE; PMAP_LOCK(pmap); - l3 = pmap_l3(pmap, addr); - if (l3 != NULL && pmap_load(l3) != 0) { + pte = pmap_pte(pmap, addr, &lvl); + if (pte != NULL && pmap_load(pte) != 0) { rv = TRUE; } PMAP_UNLOCK(pmap); @@ -2825,8 +3134,8 @@ pmap_remove_write(vm_page_t m) pmap_t pmap; struct rwlock *lock; pv_entry_t pv; - pt_entry_t *l3, oldl3; - int md_gen; + pt_entry_t oldpte, *pte; + int lvl, md_gen; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_remove_write: page %p is not managed", m)); @@ -2856,14 +3165,14 @@ retry_pv_loop: goto retry_pv_loop; } } - l3 = pmap_l3(pmap, pv->pv_va); + pte = pmap_pte(pmap, pv->pv_va, &lvl); retry: - oldl3 = pmap_load(l3); - if ((oldl3 & ATTR_AP_RW_BIT) == ATTR_AP(ATTR_AP_RW)) { - if (!atomic_cmpset_long(l3, oldl3, - oldl3 | ATTR_AP(ATTR_AP_RO))) + oldpte = pmap_load(pte); + if ((oldpte & ATTR_AP_RW_BIT) == ATTR_AP(ATTR_AP_RW)) { + if (!atomic_cmpset_long(pte, oldpte, + oldpte | ATTR_AP(ATTR_AP_RO))) goto retry; - if ((oldl3 & ATTR_AF) != 0) + if ((oldpte & ATTR_AF) != 0) vm_page_dirty(m); pmap_invalidate_page(pmap, pv->pv_va); } @@ -2901,10 +3210,10 @@ pmap_ts_referenced(vm_page_t m) pv_entry_t pv, pvf; pmap_t pmap; struct rwlock *lock; - pd_entry_t *l2p, l2; - pt_entry_t *l3; + pd_entry_t *pde, tpde; + pt_entry_t *pte, tpte; vm_paddr_t pa; - int cleared, md_gen, not_cleared; + int cleared, md_gen, not_cleared, lvl; struct spglist free; KASSERT((m->oflags & VPO_UNMANAGED) == 0, @@ -2934,28 +3243,31 @@ retry: goto retry; } } - l2p = pmap_l2(pmap, pv->pv_va); - KASSERT(l2p != NULL, ("pmap_ts_referenced: no l2 table found")); - l2 = pmap_load(l2p); - KASSERT((l2 & ATTR_DESCR_MASK) == L2_TABLE, + pde = pmap_pde(pmap, pv->pv_va, &lvl); + KASSERT(pde != NULL, ("pmap_ts_referenced: no l2 table found")); + KASSERT(lvl == 2, + ("pmap_ts_referenced: invalid pde level %d", lvl)); + tpde = pmap_load(pde); + KASSERT((tpde & ATTR_DESCR_MASK) == L2_TABLE, ("pmap_ts_referenced: found an invalid l2 table")); - l3 = pmap_l2_to_l3(l2p, pv->pv_va); - if ((pmap_load(l3) & ATTR_AF) != 0) { - if (safe_to_clear_referenced(pmap, pmap_load(l3))) { + pte = pmap_l2_to_l3(pde, pv->pv_va); + tpte = pmap_load(pte); + if ((tpte & ATTR_AF) != 0) { + if (safe_to_clear_referenced(pmap, tpte)) { /* * TODO: We don't handle the access flag * at all. We need to be able to set it in * the exception handler. */ panic("ARM64TODO: safe_to_clear_referenced\n"); - } else if ((pmap_load(l3) & ATTR_SW_WIRED) == 0) { + } else if ((tpte & ATTR_SW_WIRED) == 0) { /* * Wired pages cannot be paged out so * doing accessed bit emulation for * them is wasted effort. We do the * hard work for unwired pages only. */ - pmap_remove_l3(pmap, l3, pv->pv_va, l2, + pmap_remove_l3(pmap, pte, pv->pv_va, tpde, &free, &lock); pmap_invalidate_page(pmap, pv->pv_va); cleared++; @@ -3145,8 +3457,8 @@ pmap_activate(struct thread *td) critical_enter(); pmap = vmspace_pmap(td->td_proc->p_vmspace); - td->td_pcb->pcb_l1addr = vtophys(pmap->pm_l1); - __asm __volatile("msr ttbr0_el1, %0" : : "r"(td->td_pcb->pcb_l1addr)); + td->td_pcb->pcb_l0addr = vtophys(pmap->pm_l0); + __asm __volatile("msr ttbr0_el1, %0" : : "r"(td->td_pcb->pcb_l0addr)); pmap_invalidate_all(pmap); critical_exit(); } diff --git a/sys/arm64/arm64/support.S b/sys/arm64/arm64/support.S index 2f42a84..4938240 100644 --- a/sys/arm64/arm64/support.S +++ b/sys/arm64/arm64/support.S @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include <machine/setjmp.h> +#include <machine/param.h> #include "assym.s" @@ -290,3 +291,38 @@ ENTRY(longjmp) mov x0, x1 ret END(longjmp) + +/* + * pagezero, simple implementation + */ +ENTRY(pagezero_simple) + add x1, x0, #PAGE_SIZE + +1: + stp xzr, xzr, [x0], #0x10 + stp xzr, xzr, [x0], #0x10 + stp xzr, xzr, [x0], #0x10 + stp xzr, xzr, [x0], #0x10 + cmp x0, x1 + b.ne 1b + ret + +END(pagezero_simple) + +/* + * pagezero, cache assisted + */ +ENTRY(pagezero_cache) + add x1, x0, #PAGE_SIZE + + ldr x2, =dczva_line_size + ldr x2, [x2] + +1: + dc zva, x0 + add x0, x0, x2 + cmp x0, x1 + b.ne 1b + ret + +END(pagezero_cache) diff --git a/sys/arm64/arm64/swtch.S b/sys/arm64/arm64/swtch.S index 35153c2..1b501a49 100644 --- a/sys/arm64/arm64/swtch.S +++ b/sys/arm64/arm64/swtch.S @@ -86,7 +86,7 @@ ENTRY(cpu_throw) */ /* Switch to the new pmap */ - ldr x5, [x4, #PCB_L1ADDR] + ldr x5, [x4, #PCB_L0ADDR] msr ttbr0_el1, x5 isb @@ -183,7 +183,7 @@ ENTRY(cpu_switch) */ /* Switch to the new pmap */ - ldr x5, [x4, #PCB_L1ADDR] + ldr x5, [x4, #PCB_L0ADDR] msr ttbr0_el1, x5 isb diff --git a/sys/arm64/arm64/vm_machdep.c b/sys/arm64/arm64/vm_machdep.c index 6794b1b4..4950fe8 100644 --- a/sys/arm64/arm64/vm_machdep.c +++ b/sys/arm64/arm64/vm_machdep.c @@ -84,8 +84,8 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags) td2->td_pcb = pcb2; bcopy(td1->td_pcb, pcb2, sizeof(*pcb2)); - td2->td_pcb->pcb_l1addr = - vtophys(vmspace_pmap(td2->td_proc->p_vmspace)->pm_l1); + td2->td_pcb->pcb_l0addr = + vtophys(vmspace_pmap(td2->td_proc->p_vmspace)->pm_l0); tf = (struct trapframe *)STACKALIGN((struct trapframe *)pcb2 - 1); bcopy(td1->td_frame, tf, sizeof(*tf)); diff --git a/sys/arm64/cloudabi64/cloudabi64_sysvec.c b/sys/arm64/cloudabi64/cloudabi64_sysvec.c index cb569cd..a26007a 100644 --- a/sys/arm64/cloudabi64/cloudabi64_sysvec.c +++ b/sys/arm64/cloudabi64/cloudabi64_sysvec.c @@ -157,5 +157,6 @@ Elf64_Brandinfo cloudabi64_brand = { .brand = ELFOSABI_CLOUDABI, .machine = EM_AARCH64, .sysvec = &cloudabi64_elf_sysvec, + .flags = BI_CAN_EXEC_DYN, .compat_3_brand = "CloudABI", }; diff --git a/sys/arm64/include/armreg.h b/sys/arm64/include/armreg.h index 8b2bc92..40d07f0 100644 --- a/sys/arm64/include/armreg.h +++ b/sys/arm64/include/armreg.h @@ -66,6 +66,12 @@ #define CTR_ILINE_MASK (0xf << CTR_ILINE_SHIFT) #define CTR_ILINE_SIZE(reg) (((reg) & CTR_ILINE_MASK) >> CTR_ILINE_SHIFT) +/* DCZID_EL0 - Data Cache Zero ID register */ +#define DCZID_DZP (1 << 4) /* DC ZVA prohibited if non-0 */ +#define DCZID_BS_SHIFT 0 +#define DCZID_BS_MASK (0xf << DCZID_BS_SHIFT) +#define DCZID_BS_SIZE(reg) (((reg) & DCZID_BS_MASK) >> DCZID_BS_SHIFT) + /* ESR_ELx */ #define ESR_ELx_ISS_MASK 0x00ffffff #define ISS_INSN_FnV (0x01 << 10) diff --git a/sys/arm64/include/cpu.h b/sys/arm64/include/cpu.h index 8f14e82..520729c 100644 --- a/sys/arm64/include/cpu.h +++ b/sys/arm64/include/cpu.h @@ -1,6 +1,6 @@ /*- * Copyright (c) 1990 The Regents of the University of California. - * Copyright (c) 2014 The FreeBSD Foundation + * Copyright (c) 2014-2016 The FreeBSD Foundation * All rights reserved. * * This code is derived from software contributed to Berkeley by @@ -46,7 +46,7 @@ #include <machine/armreg.h> #define TRAPF_PC(tfp) ((tfp)->tf_lr) -#define TRAPF_USERMODE(tfp) (((tfp)->tf_elr & (1ul << 63)) == 0) +#define TRAPF_USERMODE(tfp) (((tfp)->tf_spsr & PSR_M_MASK) == PSR_M_EL0t) #define cpu_getstack(td) ((td)->td_frame->tf_sp) #define cpu_setstack(td, sp) ((td)->td_frame->tf_sp = (sp)) diff --git a/sys/arm64/include/intr.h b/sys/arm64/include/intr.h index 2d7da21..327b249 100644 --- a/sys/arm64/include/intr.h +++ b/sys/arm64/include/intr.h @@ -29,8 +29,10 @@ #ifndef _MACHINE_INTR_H_ #define _MACHINE_INTR_H_ -int arm_config_intr(u_int, enum intr_trigger, enum intr_polarity); -void arm_cpu_intr(struct trapframe *); +int intr_irq_config(u_int, enum intr_trigger, enum intr_polarity); +void intr_irq_handler(struct trapframe *); +int intr_irq_remove_handler(device_t, u_int, void *); + void arm_dispatch_intr(u_int, struct trapframe *); int arm_enable_intr(void); void arm_mask_irq(u_int); @@ -44,12 +46,12 @@ int arm_map_msi(device_t, device_t, int, uint64_t *, uint32_t *); int arm_map_msix(device_t, device_t, int, uint64_t *, uint32_t *); int arm_setup_intr(const char *, driver_filter_t *, driver_intr_t, void *, u_int, enum intr_type, void **); -int arm_teardown_intr(void *); void arm_unmask_irq(u_int); #ifdef SMP +int intr_irq_bind(u_int, int); + void arm_init_secondary(void); -int arm_intr_bind(u_int, int); void arm_setup_ipihandler(driver_filter_t *, u_int); void arm_unmask_ipi(u_int); #endif diff --git a/sys/arm64/include/machdep.h b/sys/arm64/include/machdep.h index 92c735b..0b1feae 100644 --- a/sys/arm64/include/machdep.h +++ b/sys/arm64/include/machdep.h @@ -34,11 +34,13 @@ struct arm64_bootparams { vm_offset_t kern_l1pt; /* L1 page table for the kernel */ uint64_t kern_delta; vm_offset_t kern_stack; + vm_offset_t kern_l0pt; /* L1 page table for the kernel */ }; extern vm_paddr_t physmap[]; extern u_int physmap_idx; void initarm(struct arm64_bootparams *); +extern void (*pagezero)(void *); #endif /* _MACHINE_MACHDEP_H_ */ diff --git a/sys/arm64/include/pcb.h b/sys/arm64/include/pcb.h index 55dd6e9..4426226 100644 --- a/sys/arm64/include/pcb.h +++ b/sys/arm64/include/pcb.h @@ -40,7 +40,7 @@ struct pcb { /* These two need to be in order as we access them together */ uint64_t pcb_sp; uint64_t pcb_tpidr_el0; - vm_offset_t pcb_l1addr; + vm_offset_t pcb_l0addr; /* Fault handler, the error value is passed in x0 */ vm_offset_t pcb_onfault; diff --git a/sys/arm64/include/pmap.h b/sys/arm64/include/pmap.h index 0faf2e8..578eb46 100644 --- a/sys/arm64/include/pmap.h +++ b/sys/arm64/include/pmap.h @@ -78,7 +78,7 @@ struct pv_addr { struct pmap { struct mtx pm_mtx; struct pmap_statistics pm_stats; /* pmap statictics */ - pd_entry_t *pm_l1; + pd_entry_t *pm_l0; TAILQ_HEAD(,pv_chunk) pm_pvchunk; /* list of mappings in pmap */ }; @@ -134,7 +134,7 @@ extern vm_offset_t virtual_end; #define L1_MAPPABLE_P(va, pa, size) \ ((((va) | (pa)) & L1_OFFSET) == 0 && (size) >= L1_SIZE) -void pmap_bootstrap(vm_offset_t, vm_paddr_t, vm_size_t); +void pmap_bootstrap(vm_offset_t, vm_offset_t, vm_paddr_t, vm_size_t); void pmap_kenter_device(vm_offset_t, vm_size_t, vm_paddr_t); vm_paddr_t pmap_kextract(vm_offset_t va); void pmap_kremove(vm_offset_t); @@ -149,7 +149,7 @@ boolean_t pmap_map_io_transient(vm_page_t *, vm_offset_t *, int, boolean_t); void pmap_unmap_io_transient(vm_page_t *, vm_offset_t *, int, boolean_t); bool pmap_get_tables(pmap_t, vm_offset_t, pd_entry_t **, pd_entry_t **, - pt_entry_t **); + pd_entry_t **, pt_entry_t **); #define pmap_page_is_mapped(m) (!TAILQ_EMPTY(&(m)->md.pv_list)) diff --git a/sys/arm64/include/pte.h b/sys/arm64/include/pte.h index 645cf31..da70dc4 100644 --- a/sys/arm64/include/pte.h +++ b/sys/arm64/include/pte.h @@ -73,8 +73,10 @@ typedef uint64_t pt_entry_t; /* page table entry */ /* Level 0 table, 512GiB per entry */ #define L0_SHIFT 39 +#define L0_SIZE (1ul << L0_SHIFT) +#define L0_OFFSET (L0_SIZE - 1ul) #define L0_INVAL 0x0 /* An invalid address */ -#define L0_BLOCK 0x1 /* A block */ + /* 0x1 Level 0 doesn't support block translation */ /* 0x2 also marks an invalid address */ #define L0_TABLE 0x3 /* A next-level table */ @@ -83,16 +85,16 @@ typedef uint64_t pt_entry_t; /* page table entry */ #define L1_SIZE (1 << L1_SHIFT) #define L1_OFFSET (L1_SIZE - 1) #define L1_INVAL L0_INVAL -#define L1_BLOCK L0_BLOCK +#define L1_BLOCK 0x1 #define L1_TABLE L0_TABLE /* Level 2 table, 2MiB per entry */ #define L2_SHIFT 21 #define L2_SIZE (1 << L2_SHIFT) #define L2_OFFSET (L2_SIZE - 1) -#define L2_INVAL L0_INVAL -#define L2_BLOCK L0_BLOCK -#define L2_TABLE L0_TABLE +#define L2_INVAL L1_INVAL +#define L2_BLOCK L1_BLOCK +#define L2_TABLE L1_TABLE #define L2_BLOCK_MASK UINT64_C(0xffffffe00000) @@ -106,7 +108,12 @@ typedef uint64_t pt_entry_t; /* page table entry */ /* 0x2 also marks an invalid address */ #define L3_PAGE 0x3 -#define Ln_ENTRIES (1 << 9) +#define L0_ENTRIES_SHIFT 9 +#define L0_ENTRIES (1 << L0_ENTRIES_SHIFT) +#define L0_ADDR_MASK (L0_ENTRIES - 1) + +#define Ln_ENTRIES_SHIFT 9 +#define Ln_ENTRIES (1 << Ln_ENTRIES_SHIFT) #define Ln_ADDR_MASK (Ln_ENTRIES - 1) #define Ln_TABLE_MASK ((1 << 12) - 1) diff --git a/sys/arm64/include/smp.h b/sys/arm64/include/smp.h index 0f56396..538981a 100644 --- a/sys/arm64/include/smp.h +++ b/sys/arm64/include/smp.h @@ -42,7 +42,7 @@ enum { IPI_STOP, IPI_STOP_HARD, IPI_HARDCLOCK, - COUNT_IPI, + INTR_IPI_COUNT, }; void ipi_all_but_self(u_int ipi); diff --git a/sys/arm64/include/vmparam.h b/sys/arm64/include/vmparam.h index 2752ee1..cd9198e 100644 --- a/sys/arm64/include/vmparam.h +++ b/sys/arm64/include/vmparam.h @@ -188,7 +188,7 @@ extern vm_paddr_t dmap_phys_base; }) #define VM_MIN_USER_ADDRESS (0x0000000000000000UL) -#define VM_MAX_USER_ADDRESS (0x0000008000000000UL) +#define VM_MAX_USER_ADDRESS (0x0001000000000000UL) #define VM_MINUSER_ADDRESS (VM_MIN_USER_ADDRESS) #define VM_MAXUSER_ADDRESS (VM_MAX_USER_ADDRESS) diff --git a/sys/boot/Makefile.amd64 b/sys/boot/Makefile.amd64 index 384cf7a..f2ccbad 100644 --- a/sys/boot/Makefile.amd64 +++ b/sys/boot/Makefile.amd64 @@ -3,6 +3,7 @@ SUBDIR+= efi SUBDIR+= libstand32 SUBDIR+= zfs +SUBDIR+= geli SUBDIR+= userboot .if ${MK_FORTH} != "no" diff --git a/sys/boot/Makefile.i386 b/sys/boot/Makefile.i386 index e7de353..8244da5 100644 --- a/sys/boot/Makefile.i386 +++ b/sys/boot/Makefile.i386 @@ -3,3 +3,4 @@ SUBDIR+= efi SUBDIR+= libstand32 SUBDIR+= zfs +SUBDIR+= geli diff --git a/sys/boot/common/dev_net.c b/sys/boot/common/dev_net.c index 873b28d..ed07a2c 100644 --- a/sys/boot/common/dev_net.c +++ b/sys/boot/common/dev_net.c @@ -169,6 +169,12 @@ net_open(struct open_file *f, ...) setenv("boot.netif.gateway", inet_ntoa(gateip), 1); setenv("boot.nfsroot.server", inet_ntoa(rootip), 1); setenv("boot.nfsroot.path", rootpath, 1); + if (intf_mtu != 0) { + char mtu[16]; + sprintf(mtu, "%u", intf_mtu); + setenv("boot.netif.mtu", mtu, 1); + } + } netdev_opens++; f->f_devdata = &netdev_sock; diff --git a/sys/boot/common/disk.c b/sys/boot/common/disk.c index a8801e1..c862d30 100644 --- a/sys/boot/common/disk.c +++ b/sys/boot/common/disk.c @@ -170,7 +170,7 @@ display_size(uint64_t size, u_int sectorsize) return (buf); } -static int +int ptblread(void *d, void *buf, size_t blocks, off_t offset) { struct disk_devdesc *dev; diff --git a/sys/boot/common/disk.h b/sys/boot/common/disk.h index e95256d..d24fb1a 100644 --- a/sys/boot/common/disk.h +++ b/sys/boot/common/disk.h @@ -107,6 +107,7 @@ extern int disk_read(struct disk_devdesc *dev, void *buf, off_t offset, u_int blocks); extern int disk_write(struct disk_devdesc *dev, void *buf, off_t offset, u_int blocks); +extern int ptblread(void *d, void *buf, size_t blocks, off_t offset); /* * Print information about slices on a disk. diff --git a/sys/boot/common/gpt.c b/sys/boot/common/gpt.c index 8baa64c..7ab3fc6 100644 --- a/sys/boot/common/gpt.c +++ b/sys/boot/common/gpt.c @@ -39,8 +39,6 @@ __FBSDID("$FreeBSD$"); #include "util.h" #include "gpt.h" -#define MAXTBLENTS 128 - static struct gpt_hdr hdr_primary, hdr_backup, *gpthdr; static uint64_t hdr_primary_lba, hdr_backup_lba; static struct gpt_ent table_primary[MAXTBLENTS], table_backup[MAXTBLENTS]; diff --git a/sys/boot/common/gpt.h b/sys/boot/common/gpt.h index c42b40d..9d48564 100644 --- a/sys/boot/common/gpt.h +++ b/sys/boot/common/gpt.h @@ -32,6 +32,8 @@ #include <uuid.h> #include <drv.h> +#define MAXTBLENTS 128 + int gptread(const uuid_t *uuid, struct dsk *dskp, char *buf); int gptfind(const uuid_t *uuid, struct dsk *dskp, int part); void gptbootfailed(struct dsk *dskp); diff --git a/sys/boot/efi/Makefile b/sys/boot/efi/Makefile index 94a975a..21da86f 100644 --- a/sys/boot/efi/Makefile +++ b/sys/boot/efi/Makefile @@ -2,8 +2,9 @@ .include <src.opts.mk> -# In-tree GCC does not support __attribute__((ms_abi)). -.if ${COMPILER_TYPE} != "gcc" +# In-tree GCC does not support __attribute__((ms_abi)), but gcc newer +# than 4.5 supports it. +.if ${COMPILER_TYPE} != "gcc" || ${COMPILER_VERSION} >= 404500 .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "arm" .if ${MK_FDT} != "no" @@ -17,7 +18,6 @@ SUBDIR+= fdt SUBDIR+= libefi loader boot1 .endif -.endif # ${COMPILER_TYPE} != "gcc" +.endif # ${COMPILER_TYPE} != "gcc" || ${COMPILER_VERSION} >= 404500 .include <bsd.subdir.mk> - diff --git a/sys/boot/fdt/dts/arm/bcm2836.dtsi b/sys/boot/fdt/dts/arm/bcm2836.dtsi index ce967df..5229d6a 100644 --- a/sys/boot/fdt/dts/arm/bcm2836.dtsi +++ b/sys/boot/fdt/dts/arm/bcm2836.dtsi @@ -32,8 +32,8 @@ timer { compatible = "arm,armv7-timer"; clock-frequency = <19200000>; - interrupts = <72 73 75 74>; - interrupt-parent = <&intc>; + interrupts = <0 1 3 2>; + interrupt-parent = <&local_intc>; }; SOC: axi { @@ -41,12 +41,23 @@ #address-cells = <1>; #size-cells = <1>; reg = <0x3f000000 0x01000000>; - ranges = <0 0x3f000000 0x01000000>; + ranges = <0 0x3f000000 0x01000000>, + <0x40000000 0x40000000 0x00001000>; + + local_intc: local_intc { + compatible = "brcm,bcm2836-l1-intc"; + reg = <0x40000000 0x100>; + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&local_intc>; + }; intc: interrupt-controller { compatible = "broadcom,bcm2835-armctrl-ic", "broadcom,bcm2708-armctrl-ic"; reg = <0xB200 0x200>; + interrupt-parent = <&local_intc>; + interrupts = <8>; interrupt-controller; #interrupt-cells = <1>; diff --git a/sys/boot/fdt/dts/arm/beaglebone-black.dts b/sys/boot/fdt/dts/arm/beaglebone-black.dts index d4f19b2..6605505 100644 --- a/sys/boot/fdt/dts/arm/beaglebone-black.dts +++ b/sys/boot/fdt/dts/arm/beaglebone-black.dts @@ -33,15 +33,18 @@ &am33xx_pinmux { i2c1_pins: pinmux_i2c1_pins { pinctrl-single,pins = < - 0x158 (PIN_INPUT_PULLUP | MUX_MODE2) /* spi0_d1.i2c1_sda */ - 0x15c (PIN_INPUT_PULLUP | MUX_MODE2) /* spi0_cs0.i2c1_scl */ + AM33XX_IOPAD(0x958, PIN_INPUT_PULLUP | MUX_MODE2) /* spi0_d1.i2c1_sda */ + AM33XX_IOPAD(0x95c, PIN_INPUT_PULLUP | MUX_MODE2) /* spi0_cs0.i2c1_scl */ >; }; - i2c2_pins: pinmux_i2c2_pins { + spi1_pins: pinmux_spi1_pins { pinctrl-single,pins = < - 0x178 (PIN_INPUT_PULLUP | MUX_MODE3) /* uart1_ctsn.i2c2_sda */ - 0x17c (PIN_INPUT_PULLUP | MUX_MODE3) /* uart1_rtsn.i2c2_scl */ + AM33XX_IOPAD(0x964, PIN_INPUT_PULLUP | MUX_MODE2) /* eCAP0_in_PWM0_out.spi1_cs1 */ + AM33XX_IOPAD(0x990, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcasp0_aclkx.spi1_sclk */ + AM33XX_IOPAD(0x994, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcasp0_fsx.spi1_d0 - miso */ + AM33XX_IOPAD(0x998, PIN_INPUT_PULLUP | MUX_MODE3) /* mcasp0_axr0.spi1_d1 - mosi */ + AM33XX_IOPAD(0x99c, PIN_INPUT_PULLUP | MUX_MODE3) /* mcasp0_ahclkr.spi1_cs0 */ >; }; }; @@ -72,6 +75,13 @@ status = "okay"; }; +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spi1_pins>; + + status = "okay"; +}; + &lcdc { hdmi = <&tda998x>; }; diff --git a/sys/dev/filemon/filemon_lock.c b/sys/boot/fdt/dts/arm/tegra124-jetson-tk1-fbsd.dts index 5cac47c..902616b 100644 --- a/sys/dev/filemon/filemon_lock.c +++ b/sys/boot/fdt/dts/arm/tegra124-jetson-tk1-fbsd.dts @@ -1,8 +1,11 @@ /*- - * Copyright (c) 2009-2011, Juniper Networks, Inc. - * Copyright (c) 2015, EMC Corp. + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> * All rights reserved. * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) + * ("CTSRD"), as part of the DARPA CRASH research programme. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -12,10 +15,10 @@ * 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 JUNIPER NETWORKS AND CONTRIBUTORS ``AS IS'' AND + * 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 JUNIPER NETWORKS OR CONTRIBUTORS BE LIABLE + * 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) @@ -23,35 +26,21 @@ * 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 <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -static __inline void -filemon_lock_read(void) -{ - - sx_slock(&access_lock); -} - -static __inline void -filemon_unlock_read(void) -{ - - sx_sunlock(&access_lock); -} - -static __inline void -filemon_lock_write(void) -{ +#include "tegra124-jetson-tk1.dts" - sx_xlock(&access_lock); -} +/ { + chosen { + stdin = &uartd; + stdout = &uartd; + }; -static __inline void -filemon_unlock_write(void) -{ + memory { +/* reg = <0x0 0x80000000 0x0 0x80000000>; */ + reg = <0x0 0x80000000 0x0 0x70000000>; + }; - sx_xunlock(&access_lock); -} +}; diff --git a/sys/boot/geli/Makefile b/sys/boot/geli/Makefile new file mode 100644 index 0000000..f5ab243 --- /dev/null +++ b/sys/boot/geli/Makefile @@ -0,0 +1,52 @@ +# $FreeBSD$ +# libgeliboot + +MAN= + +.include <src.opts.mk> +MK_SSP= no + +LIB= geliboot +INTERNALLIB= +MK_PROFILE= no +NO_PIC= + +.if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" +CFLAGS+= -march=i386 +.endif +.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "powerpc64" +CFLAGS+= -m32 +.endif + +WARNS?= 0 + +# string functions from libc +.PATH: ${.CURDIR}/../../../lib/libc/string +SRCS+= bcmp.c bcopy.c bzero.c + +# Our password input method +SRCS+= pwgets.c + +# sha256 and sha512 from sys/crypto +.PATH: ${.CURDIR}/../../crypto/sha2 +CFLAGS+= -DWEAK_REFS +SRCS+= sha256c.c sha512c.c + +# md5 from libmd +.PATH: ${.CURDIR}/../../../lib/libmd +SRCS+= md5c.c + +# AES implementation from sys/crypto +.PATH: ${.CURDIR}/../../crypto/rijndael +CFLAGS+= -I${.CURDIR}/../../ +# Remove asserts +CFLAGS+= -DNDEBUG +SRCS+= rijndael-alg-fst.c rijndael-api-fst.c rijndael-api.c + +# local GELI Implementation +.PATH: ${.CURDIR}/../../geom/eli +CFLAGS+= -D_STAND +SRCS+= geliboot_crypto.c g_eli_hmac.c g_eli_key.c g_eli_key_cache.c pkcs5v2.c + +.include <bsd.stand.mk> +.include <bsd.lib.mk> diff --git a/sys/boot/geli/Makefile.depend b/sys/boot/geli/Makefile.depend new file mode 100644 index 0000000..7b57224 --- /dev/null +++ b/sys/boot/geli/Makefile.depend @@ -0,0 +1,16 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/xlocale \ + lib/libmd \ + lib/libstand \ + secure/lib/libcrypto \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/sys/boot/geli/geliboot.c b/sys/boot/geli/geliboot.c new file mode 100644 index 0000000..becbc5f --- /dev/null +++ b/sys/boot/geli/geliboot.c @@ -0,0 +1,292 @@ +/*- + * Copyright (c) 2015 Allan Jude <allanjude@FreeBSD.org> + * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel@dawidek.net> + * 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 AUTHORS 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 AUTHORS 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. + * + * $FreeBSD$ + */ + +#include "geliboot.h" + +SLIST_HEAD(geli_list, geli_entry) geli_head = SLIST_HEAD_INITIALIZER(geli_head); +struct geli_list *geli_headp; + +static int +geli_same_device(struct geli_entry *ge, struct dsk *dskp) +{ + + if (geli_e->dsk->drive == dskp->drive && + dskp->part == 255 && geli_e->dsk->part == dskp->slice) { + /* + * Sometimes slice = slice, and sometimes part = slice + * If the incoming struct dsk has part=255, it means look at + * the slice instead of the part number + */ + return (0); + } + + /* Is this the same device? */ + if (geli_e->dsk->drive != dskp->drive || + geli_e->dsk->slice != dskp->slice || + geli_e->dsk->part != dskp->part) { + return (1); + } + + return (0); +} + +void +geli_init(void) +{ + + geli_count = 0; + SLIST_INIT(&geli_head); +} + +/* + * Read the last sector of the drive or partition pointed to by dsk and see + * if it is GELI encrypted + */ +int +geli_taste(int read_func(void *vdev, void *priv, off_t off, void *buf, + size_t bytes), struct dsk *dskp, daddr_t lastsector) +{ + struct g_eli_metadata md; + u_char buf[DEV_BSIZE]; + int error; + + error = read_func(NULL, dskp, (off_t) lastsector * DEV_BSIZE, &buf, + (size_t) DEV_BSIZE); + if (error != 0) { + return (error); + } + error = eli_metadata_decode(buf, &md); + if (error != 0) { + return (error); + } + + if ((md.md_flags & G_ELI_FLAG_ONETIME)) { + /* Swap device, skip it */ + return (1); + } + if (!(md.md_flags & G_ELI_FLAG_BOOT)) { + /* Disk is not GELI boot device, skip it */ + return (1); + } + if (md.md_iterations < 0) { + /* XXX TODO: Support loading key files */ + /* Disk does not have a passphrase, skip it */ + return (1); + } + geli_e = malloc(sizeof(struct geli_entry)); + if (geli_e == NULL) + return (2); + + geli_e->dsk = malloc(sizeof(struct dsk)); + if (geli_e->dsk == NULL) + return (2); + memcpy(geli_e->dsk, dskp, sizeof(struct dsk)); + geli_e->part_end = lastsector; + if (dskp->part == 255) { + geli_e->dsk->part = dskp->slice; + } + + geli_e->md = md; + eli_metadata_softc(&geli_e->sc, &md, DEV_BSIZE, + (lastsector + DEV_BSIZE) * DEV_BSIZE); + + SLIST_INSERT_HEAD(&geli_head, geli_e, entries); + geli_count++; + + return (0); +} + +/* + * Attempt to decrypt the device + */ +int +geli_attach(struct dsk *dskp, const char *passphrase) +{ + u_char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN], *mkp; + u_int keynum; + struct hmac_ctx ctx; + int error; + + SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) { + if (geli_same_device(geli_e, dskp) != 0) { + continue; + } + + g_eli_crypto_hmac_init(&ctx, NULL, 0); + /* + * Prepare Derived-Key from the user passphrase. + */ + if (geli_e->md.md_iterations < 0) { + /* XXX TODO: Support loading key files */ + return (1); + } else if (geli_e->md.md_iterations == 0) { + g_eli_crypto_hmac_update(&ctx, geli_e->md.md_salt, + sizeof(geli_e->md.md_salt)); + g_eli_crypto_hmac_update(&ctx, passphrase, + strlen(passphrase)); + } else if (geli_e->md.md_iterations > 0) { + printf("Calculating GELI Decryption Key disk%dp%d @ %lu " + "iterations...\n", dskp->unit, + (dskp->slice > 0 ? dskp->slice : dskp->part), + geli_e->md.md_iterations); + u_char dkey[G_ELI_USERKEYLEN]; + + pkcs5v2_genkey(dkey, sizeof(dkey), geli_e->md.md_salt, + sizeof(geli_e->md.md_salt), passphrase, + geli_e->md.md_iterations); + g_eli_crypto_hmac_update(&ctx, dkey, sizeof(dkey)); + bzero(&dkey, sizeof(dkey)); + } + + g_eli_crypto_hmac_final(&ctx, key, 0); + + error = g_eli_mkey_decrypt(&geli_e->md, key, mkey, &keynum); + bzero(&key, sizeof(key)); + if (error == -1) { + bzero(&mkey, sizeof(mkey)); + printf("Bad GELI key: %d\n", error); + return (error); + } else if (error != 0) { + bzero(&mkey, sizeof(mkey)); + printf("Failed to decrypt GELI master key: %d\n", error); + return (error); + } + + /* Store the keys */ + bcopy(mkey, geli_e->sc.sc_mkey, sizeof(geli_e->sc.sc_mkey)); + bcopy(mkey, geli_e->sc.sc_ivkey, sizeof(geli_e->sc.sc_ivkey)); + mkp = mkey + sizeof(geli_e->sc.sc_ivkey); + if ((geli_e->sc.sc_flags & G_ELI_FLAG_AUTH) == 0) { + bcopy(mkp, geli_e->sc.sc_ekey, G_ELI_DATAKEYLEN); + } else { + /* + * The encryption key is: ekey = HMAC_SHA512(Data-Key, 0x10) + */ + g_eli_crypto_hmac(mkp, G_ELI_MAXKEYLEN, "\x10", 1, + geli_e->sc.sc_ekey, 0); + } + bzero(&mkey, sizeof(mkey)); + + /* Initialize the per-sector IV */ + switch (geli_e->sc.sc_ealgo) { + case CRYPTO_AES_XTS: + break; + default: + SHA256_Init(&geli_e->sc.sc_ivctx); + SHA256_Update(&geli_e->sc.sc_ivctx, geli_e->sc.sc_ivkey, + sizeof(geli_e->sc.sc_ivkey)); + break; + } + + return (0); + } + + /* Disk not found */ + return (2); +} + +int +is_geli(struct dsk *dskp) +{ + SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) { + if (geli_same_device(geli_e, dskp) == 0) { + return (0); + } + } + + return (1); +} + +int +geli_read(struct dsk *dskp, off_t offset, u_char *buf, size_t bytes) +{ + u_char iv[G_ELI_IVKEYLEN]; + u_char *pbuf; + int error; + off_t os; + uint64_t keyno; + size_t n, nb; + struct g_eli_key gkey; + + SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) { + if (geli_same_device(geli_e, dskp) != 0) { + continue; + } + + nb = bytes / DEV_BSIZE; + for (n = 0; n < nb; n++) { + os = offset + (n * DEV_BSIZE); + pbuf = buf + (n * DEV_BSIZE); + + g_eli_crypto_ivgen(&geli_e->sc, os, iv, G_ELI_IVKEYLEN); + + /* Get the key that corresponds to this offset */ + keyno = (os >> G_ELI_KEY_SHIFT) / DEV_BSIZE; + g_eli_key_fill(&geli_e->sc, &gkey, keyno); + + error = geliboot_crypt(geli_e->sc.sc_ealgo, 0, pbuf, + DEV_BSIZE, gkey.gek_key, geli_e->sc.sc_ekeylen, iv); + + if (error != 0) { + bzero(&gkey, sizeof(gkey)); + printf("Failed to decrypt in geli_read()!"); + return (error); + } + } + bzero(&gkey, sizeof(gkey)); + return (0); + } + + printf("GELI provider not found\n"); + return (1); +} + +int +geli_passphrase(char *pw, int disk, int parttype, int part, struct dsk *dskp) +{ + int i; + + /* TODO: Implement GELI keyfile(s) support */ + for (i = 0; i < 3; i++) { + /* Try cached passphrase */ + if (i == 0 && pw[0] != '\0') { + if (geli_attach(dskp, pw) == 0) { + return (0); + } + } + printf("GELI Passphrase for disk%d%c%d: ", disk, parttype, part); + pwgets(pw, GELI_PW_MAXLEN); + printf("\n"); + if (geli_attach(dskp, pw) == 0) { + return (0); + } + } + + return (1); +} diff --git a/sys/boot/geli/geliboot.h b/sys/boot/geli/geliboot.h new file mode 100644 index 0000000..36bebcc --- /dev/null +++ b/sys/boot/geli/geliboot.h @@ -0,0 +1,86 @@ +/*- + * Copyright (c) 2015 Allan Jude <allanjude@FreeBSD.org> + * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel@dawidek.net> + * 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 AUTHORS 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 AUTHORS 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. + * + * $FreeBSD$ + */ + +#include <sys/endian.h> +#include <sys/queue.h> + +#ifndef _GELIBOOT_H_ +#define _GELIBOOT_H_ + +#define _STRING_H_ +#define _STRINGS_H_ +#define _STDIO_H_ +#include <geom/eli/g_eli.h> +#include <geom/eli/pkcs5v2.h> + +/* Pull in the md5, sha256, and sha512 implementations */ +#include <md5.h> +#include <crypto/sha2/sha256.h> +#include <crypto/sha2/sha512.h> + +/* Pull in AES implementation */ +#include <crypto/rijndael/rijndael-api-fst.h> + +/* AES-XTS implementation */ +#define _STAND +#define STAND_H /* We don't want stand.h in {gpt,zfs,gptzfs}boot */ +#include <opencrypto/xform_enc.h> + +#ifndef DEV_BSIZE +#define DEV_BSIZE 512 +#endif + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define GELI_PW_MAXLEN 256 +extern void pwgets(char *buf, int n); + +struct geli_entry { + struct dsk *dsk; + off_t part_end; + struct g_eli_softc sc; + struct g_eli_metadata md; + SLIST_ENTRY(geli_entry) entries; +} *geli_e, *geli_e_tmp; + +int geli_count; + +void geli_init(void); +int geli_taste(int read_func(void *vdev, void *priv, off_t off, + void *buf, size_t bytes), struct dsk *dsk, daddr_t lastsector); +int geli_attach(struct dsk *dskp, const char *passphrase); +int is_geli(struct dsk *dsk); +int geli_read(struct dsk *dsk, off_t offset, u_char *buf, size_t bytes); +int geli_decrypt(u_int algo, u_char *data, size_t datasize, + const u_char *key, size_t keysize, const uint8_t* iv); +int geli_passphrase(char *pw, int disk, int parttype, int part, struct dsk *dskp); + +#endif /* _GELIBOOT_H_ */ diff --git a/sys/boot/geli/geliboot_crypto.c b/sys/boot/geli/geliboot_crypto.c new file mode 100644 index 0000000..1596236 --- /dev/null +++ b/sys/boot/geli/geliboot_crypto.c @@ -0,0 +1,135 @@ +/*- + * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> + * Copyright (c) 2015 Allan Jude <allanjude@FreeBSD.org> + * 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 AUTHORS 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 AUTHORS 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. + * + * $FreeBSD$ + */ + +#include "geliboot.h" + +int +geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize, + const u_char *key, size_t keysize, u_char *iv) +{ + keyInstance aeskey; + cipherInstance cipher; + struct aes_xts_ctx xtsctx, *ctxp; + size_t xts_len; + int err, blks, i; + + switch (algo) { + case CRYPTO_AES_CBC: + err = rijndael_makeKey(&aeskey, !enc, keysize, + (const char *)key); + if (err < 0) { + printf("Failed to setup decryption keys: %d\n", err); + return (err); + } + + err = rijndael_cipherInit(&cipher, MODE_CBC, iv); + if (err < 0) { + printf("Failed to setup IV: %d\n", err); + return (err); + } + + switch (enc) { + case 0: /* decrypt */ + blks = rijndael_blockDecrypt(&cipher, &aeskey, data, + datasize * 8, data); + break; + case 1: /* encrypt */ + blks = rijndael_blockEncrypt(&cipher, &aeskey, data, + datasize * 8, data); + break; + } + if (datasize != (blks / 8)) { + printf("Failed to decrypt the entire input: " + "%u != %u\n", blks, datasize); + return (1); + } + break; + case CRYPTO_AES_XTS: + xts_len = keysize << 1; + ctxp = &xtsctx; + + rijndael_set_key(&ctxp->key1, key, xts_len / 2); + rijndael_set_key(&ctxp->key2, key + (xts_len / 16), xts_len / 2); + + enc_xform_aes_xts.reinit(ctxp, iv); + + switch (enc) { + case 0: /* decrypt */ + for (i = 0; i < datasize; i += AES_XTS_BLOCKSIZE) { + enc_xform_aes_xts.decrypt(ctxp, data + i); + } + break; + case 1: /* encrypt */ + for (i = 0; i < datasize; i += AES_XTS_BLOCKSIZE) { + enc_xform_aes_xts.encrypt(ctxp, data + i); + } + break; + } + break; + default: + printf("Unsupported crypto algorithm #%d\n", algo); + return (1); + } + + return (0); +} + +static int +g_eli_crypto_cipher(u_int algo, int enc, u_char *data, size_t datasize, + const u_char *key, size_t keysize) +{ + u_char iv[keysize]; + + bzero(iv, sizeof(iv)); + return (geliboot_crypt(algo, enc, data, datasize, key, keysize, iv)); +} + +int +g_eli_crypto_encrypt(u_int algo, u_char *data, size_t datasize, + const u_char *key, size_t keysize) +{ + + /* We prefer AES-CBC for metadata protection. */ + if (algo == CRYPTO_AES_XTS) + algo = CRYPTO_AES_CBC; + + return (g_eli_crypto_cipher(algo, 1, data, datasize, key, keysize)); +} + +int +g_eli_crypto_decrypt(u_int algo, u_char *data, size_t datasize, + const u_char *key, size_t keysize) +{ + + /* We prefer AES-CBC for metadata protection. */ + if (algo == CRYPTO_AES_XTS) + algo = CRYPTO_AES_CBC; + + return (g_eli_crypto_cipher(algo, 0, data, datasize, key, keysize)); +} diff --git a/sys/boot/geli/pwgets.c b/sys/boot/geli/pwgets.c new file mode 100644 index 0000000..aca6c3c --- /dev/null +++ b/sys/boot/geli/pwgets.c @@ -0,0 +1,83 @@ +/* $NetBSD: gets.c,v 1.6 1995/10/11 21:16:57 pk Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. 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. + * 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 REGENTS 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 REGENTS 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. + * + * @(#)gets.c 8.1 (Berkeley) 6/11/93 + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "stand.h" + +/* gets() with constrained input length, for passwords */ + +void +pwgets(char *buf, int n) +{ + int c; + char *lp; + + for (lp = buf;;) + switch (c = getchar() & 0177) { + case '\n': + case '\r': + *lp = '\0'; + putchar('\n'); + return; + case '\b': + case '\177': + if (lp > buf) { + lp--; + putchar('\b'); + putchar(' '); + putchar('\b'); + } + break; + case 'r'&037: { + char *p; + + putchar('\n'); + for (p = buf; p < lp; ++p) + putchar(*p); + break; + } + case 'u'&037: + case 'w'&037: + lp = buf; + putchar('\n'); + break; + default: + if ((n < 1) || ((lp - buf) < n - 1)) { + *lp++ = c; + putchar('*'); + } + } + /*NOTREACHED*/ +} diff --git a/sys/boot/i386/boot0/Makefile b/sys/boot/i386/boot0/Makefile index 733fc68..f5faef3 100644 --- a/sys/boot/i386/boot0/Makefile +++ b/sys/boot/i386/boot0/Makefile @@ -80,4 +80,3 @@ LDFLAGS=-e start -Ttext ${BOOT_BOOT0_ORG} -Wl,-N,-S,--oformat,binary # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.boot0.S= ${CLANG_NO_IAS} -CFLAGS+= ${CFLAGS.${.IMPSRC:T}} diff --git a/sys/boot/i386/boot2/Makefile b/sys/boot/i386/boot2/Makefile index 195206f..16ed404 100644 --- a/sys/boot/i386/boot2/Makefile +++ b/sys/boot/i386/boot2/Makefile @@ -115,4 +115,3 @@ machine: ${.CURDIR}/../../../i386/include .NOMETA # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.boot1.S= ${CLANG_NO_IAS} -CFLAGS+= ${CFLAGS.${.IMPSRC:T}} diff --git a/sys/boot/i386/btx/btx/Makefile b/sys/boot/i386/btx/btx/Makefile index 0f5a468..a07e000 100644 --- a/sys/boot/i386/btx/btx/Makefile +++ b/sys/boot/i386/btx/btx/Makefile @@ -31,4 +31,3 @@ LDFLAGS=-e start -Ttext ${ORG} -Wl,-N,-S,--oformat,binary # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.btx.S= ${CLANG_NO_IAS} -CFLAGS+= ${CFLAGS.${.IMPSRC:T}} diff --git a/sys/boot/i386/btx/btxldr/Makefile b/sys/boot/i386/btx/btxldr/Makefile index 7e57ca3..bf7833d 100644 --- a/sys/boot/i386/btx/btxldr/Makefile +++ b/sys/boot/i386/btx/btxldr/Makefile @@ -18,4 +18,3 @@ LDFLAGS=-e start -Ttext ${LOADER_ADDRESS} -Wl,-N,-S,--oformat,binary # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.btxldr.S= ${CLANG_NO_IAS} -CFLAGS+= ${CFLAGS.${.IMPSRC:T}} diff --git a/sys/boot/i386/cdboot/Makefile b/sys/boot/i386/cdboot/Makefile index a3dc251..e9e046b 100644 --- a/sys/boot/i386/cdboot/Makefile +++ b/sys/boot/i386/cdboot/Makefile @@ -16,4 +16,3 @@ LDFLAGS=-e start -Ttext ${ORG} -Wl,-N,-S,--oformat,binary # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.cdboot.S= ${CLANG_NO_IAS} -CFLAGS+= ${CFLAGS.${.IMPSRC:T}} diff --git a/sys/boot/i386/common/bootargs.h b/sys/boot/i386/common/bootargs.h index 4768d68..e6674e1 100644 --- a/sys/boot/i386/common/bootargs.h +++ b/sys/boot/i386/common/bootargs.h @@ -64,6 +64,12 @@ struct bootargs */ }; +struct geli_boot_args +{ + uint32_t size; + char gelipw[256]; +}; + #endif /*__ASSEMBLER__*/ #endif /* !_BOOT_I386_ARGS_H_ */ diff --git a/sys/boot/i386/common/cons.c b/sys/boot/i386/common/cons.c index 97019c6..b967d9b 100644 --- a/sys/boot/i386/common/cons.c +++ b/sys/boot/i386/common/cons.c @@ -97,6 +97,13 @@ xgetc(int fn) } int +getchar(void) +{ + + return (xgetc(0)); +} + +int keyhit(unsigned int secs) { uint32_t t0, t1; diff --git a/sys/boot/i386/common/drv.c b/sys/boot/i386/common/drv.c index 52933d5..f5133de 100644 --- a/sys/boot/i386/common/drv.c +++ b/sys/boot/i386/common/drv.c @@ -29,7 +29,6 @@ __FBSDID("$FreeBSD$"); #include "xreadorg.h" #endif -#ifdef GPT static struct edd_params params; uint64_t @@ -50,7 +49,6 @@ drvsize(struct dsk *dskp) } return (params.sectors); } -#endif /* GPT */ #ifndef USE_XREAD static struct edd_packet packet; diff --git a/sys/boot/i386/common/drv.h b/sys/boot/i386/common/drv.h index 1ecfbc3..8ad3c9c 100644 --- a/sys/boot/i386/common/drv.h +++ b/sys/boot/i386/common/drv.h @@ -42,7 +42,7 @@ struct dsk { int drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk); #ifdef GPT int drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk); -uint64_t drvsize(struct dsk *dskp); #endif /* GPT */ +uint64_t drvsize(struct dsk *dskp); #endif /* !_DRV_H_ */ diff --git a/sys/boot/i386/gptboot/Makefile b/sys/boot/i386/gptboot/Makefile index 1aafe0b..cc8251c 100644 --- a/sys/boot/i386/gptboot/Makefile +++ b/sys/boot/i386/gptboot/Makefile @@ -39,6 +39,14 @@ CFLAGS= -DBOOTPROG=\"gptboot\" \ CFLAGS.gcc+= --param max-inline-insns-single=100 +.if !defined(LOADER_NO_GELI_SUPPORT) +CFLAGS+= -DLOADER_GELI_SUPPORT +CFLAGS+= -I${.CURDIR}/../../geli +LIBGELIBOOT= ${.OBJDIR}/../../geli/libgeliboot.a +.PATH: ${.CURDIR}/../../../opencrypto +OPENCRYPTO_XTS= xform_aes_xts.o +.endif + LD_FLAGS=-static -N --gc-sections LIBSTAND= ${.OBJDIR}/../../libstand32/libstand.a @@ -60,14 +68,14 @@ gptldr.bin: gptldr.out gptldr.out: gptldr.o ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o -CLEANFILES+= gptboot.bin gptboot.out gptboot.o sio.o gpt.o crc32.o drv.o \ - cons.o util.o +CLEANFILES+= gptboot.bin gptboot.out gptboot.o sio.o crc32.o drv.o \ + cons.o util.o ${OPENCRYPTO_XTS} gptboot.bin: gptboot.out ${OBJCOPY} -S -O binary gptboot.out ${.TARGET} -gptboot.out: ${BTXCRT} gptboot.o sio.o gpt.o crc32.o drv.o cons.o util.o - ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} +gptboot.out: ${BTXCRT} gptboot.o sio.o crc32.o drv.o cons.o util.o ${OPENCRYPTO_XTS} + ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} ${LIBGELIBOOT} gptboot.o: ${.CURDIR}/../../common/ufsread.c @@ -82,4 +90,3 @@ machine: .NOMETA # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.gptldr.S= ${CLANG_NO_IAS} -CFLAGS+= ${CFLAGS.${.IMPSRC:T}} diff --git a/sys/boot/i386/gptboot/Makefile.depend b/sys/boot/i386/gptboot/Makefile.depend index c798603..295be1a 100644 --- a/sys/boot/i386/gptboot/Makefile.depend +++ b/sys/boot/i386/gptboot/Makefile.depend @@ -3,6 +3,10 @@ DIRDEPS = \ include \ + include/xlocale \ + lib/libmd \ + lib/libstand \ + sys/boot/geli \ sys/boot/i386/btx/btx \ sys/boot/i386/btx/lib \ sys/boot/libstand32 \ diff --git a/sys/boot/i386/gptboot/gptboot.c b/sys/boot/i386/gptboot/gptboot.c index a1f46eb..14438e6 100644 --- a/sys/boot/i386/gptboot/gptboot.c +++ b/sys/boot/i386/gptboot/gptboot.c @@ -23,6 +23,7 @@ __FBSDID("$FreeBSD$"); #include <machine/bootinfo.h> #include <machine/elf.h> +#include <machine/pc/bios.h> #include <machine/psl.h> #include <stdarg.h> @@ -31,6 +32,7 @@ __FBSDID("$FreeBSD$"); #include <btxv86.h> +#include "bootargs.h" #include "lib.h" #include "rbx.h" #include "drv.h" @@ -82,14 +84,60 @@ static struct dsk dsk; static char kname[1024]; static int comspeed = SIOSPD; static struct bootinfo bootinfo; +static struct geli_boot_args geliargs; + +static vm_offset_t high_heap_base; +static uint32_t bios_basemem, bios_extmem, high_heap_size; + +static struct bios_smap smap; + +/* + * The minimum amount of memory to reserve in bios_extmem for the heap. + */ +#define HEAP_MIN (3 * 1024 * 1024) + +static char *heap_next; +static char *heap_end; void exit(int); static void load(void); static int parse(char *, int *); static int dskread(void *, daddr_t, unsigned); -static uint32_t memsize(void); +void *malloc(size_t n); +void free(void *ptr); +#ifdef LOADER_GELI_SUPPORT +static int vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, + size_t bytes); +#endif + +void * +malloc(size_t n) +{ + char *p = heap_next; + if (p + n > heap_end) { + printf("malloc failure\n"); + for (;;) + ; + /* NOTREACHED */ + return (0); + } + heap_next += n; + return (p); +} + +void +free(void *ptr) +{ + + return; +} #include "ufsread.c" +#include "gpt.c" +#ifdef LOADER_GELI_SUPPORT +#include "geliboot.c" +static char gelipw[GELI_PW_MAXLEN]; +#endif static inline int xfsread(ufs_ino_t inode, void *buf, size_t nbyte) @@ -102,14 +150,90 @@ xfsread(ufs_ino_t inode, void *buf, size_t nbyte) return (0); } -static inline uint32_t -memsize(void) +static void +bios_getmem(void) { + uint64_t size; - v86.addr = MEM_EXT; + /* Parse system memory map */ + v86.ebx = 0; + do { + v86.ctl = V86_FLAGS; + v86.addr = MEM_EXT; /* int 0x15 function 0xe820*/ + v86.eax = 0xe820; + v86.ecx = sizeof(struct bios_smap); + v86.edx = SMAP_SIG; + v86.es = VTOPSEG(&smap); + v86.edi = VTOPOFF(&smap); + v86int(); + if ((v86.efl & 1) || (v86.eax != SMAP_SIG)) + break; + /* look for a low-memory segment that's large enough */ + if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) && + (smap.length >= (512 * 1024))) + bios_basemem = smap.length; + /* look for the first segment in 'extended' memory */ + if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) { + bios_extmem = smap.length; + } + + /* + * Look for the largest segment in 'extended' memory beyond + * 1MB but below 4GB. + */ + if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base > 0x100000) && + (smap.base < 0x100000000ull)) { + size = smap.length; + + /* + * If this segment crosses the 4GB boundary, truncate it. + */ + if (smap.base + size > 0x100000000ull) + size = 0x100000000ull - smap.base; + + if (size > high_heap_size) { + high_heap_size = size; + high_heap_base = smap.base; + } + } + } while (v86.ebx != 0); + + /* Fall back to the old compatibility function for base memory */ + if (bios_basemem == 0) { + v86.ctl = 0; + v86.addr = 0x12; /* int 0x12 */ + v86int(); + + bios_basemem = (v86.eax & 0xffff) * 1024; + } + + /* Fall back through several compatibility functions for extended memory */ + if (bios_extmem == 0) { + v86.ctl = V86_FLAGS; + v86.addr = 0x15; /* int 0x15 function 0xe801*/ + v86.eax = 0xe801; + v86int(); + if (!(v86.efl & 1)) { + bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024; + } + } + if (bios_extmem == 0) { + v86.ctl = 0; + v86.addr = 0x15; /* int 0x15 function 0x88*/ v86.eax = 0x8800; v86int(); - return (v86.eax); + bios_extmem = (v86.eax & 0xffff) * 1024; + } + + /* + * If we have extended memory and did not find a suitable heap + * region in the SMAP, use the last 3MB of 'extended' memory as a + * high heap candidate. + */ + if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) { + high_heap_size = HEAP_MIN; + high_heap_base = bios_extmem + 0x100000 - HEAP_MIN; + } } static int @@ -124,6 +248,16 @@ gptinit(void) printf("%s: no UFS partition was found\n", BOOTPROG); return (-1); } +#ifdef LOADER_GELI_SUPPORT + if (geli_taste(vdev_read, &dsk, (gpttable[curent].ent_lba_end - + gpttable[curent].ent_lba_start)) == 0) { + if (geli_passphrase(&gelipw, dsk.unit, 'p', curent + 1, &dsk) != 0) { + printf("%s: unable to decrypt GELI key\n", BOOTPROG); + return (-1); + } + } +#endif + dsk_meta = 0; return (0); } @@ -137,6 +271,17 @@ main(void) ufs_ino_t ino; dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); + + bios_getmem(); + + if (high_heap_size > 0) { + heap_end = PTOV(high_heap_base + high_heap_size); + heap_next = PTOV(high_heap_base); + } else { + heap_next = (char *)dmadat + sizeof(*dmadat); + heap_end = (char *)PTOV(bios_basemem); + } + v86.ctl = V86_FLAGS; v86.efl = PSL_RESERVED_DEFAULT | PSL_I; dsk.drive = *(uint8_t *)PTOV(ARGS); @@ -146,10 +291,14 @@ main(void) dsk.start = 0; bootinfo.bi_version = BOOTINFO_VERSION; bootinfo.bi_size = sizeof(bootinfo); - bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */ - bootinfo.bi_extmem = memsize(); + bootinfo.bi_basemem = bios_basemem / 1024; + bootinfo.bi_extmem = bios_extmem / 1024; bootinfo.bi_memsizes_valid++; + bootinfo.bi_bios_dev = dsk.drive; +#ifdef LOADER_GELI_SUPPORT + geli_init(); +#endif /* Process configuration file */ if (gptinit() != 0) @@ -327,9 +476,16 @@ load(void) bootinfo.bi_esymtab = VTOP(p); bootinfo.bi_kernelname = VTOP(kname); bootinfo.bi_bios_dev = dsk.drive; + geliargs.size = sizeof(geliargs); +#ifdef LOADER_GELI_SUPPORT + bcopy(gelipw, geliargs.gelipw, sizeof(geliargs.gelipw)); + bzero(gelipw, sizeof(gelipw)); +#else + geliargs.gelipw[0] = '\0'; +#endif __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff), - 0, 0, 0, VTOP(&bootinfo)); + KARGS_FLAGS_EXTARG, 0, 0, VTOP(&bootinfo), geliargs); } static int @@ -430,6 +586,53 @@ parse(char *cmdstr, int *dskupdated) static int dskread(void *buf, daddr_t lba, unsigned nblk) { + int err; + + err = drvread(&dsk, buf, lba + dsk.start, nblk); - return drvread(&dsk, buf, lba + dsk.start, nblk); +#ifdef LOADER_GELI_SUPPORT + if (err == 0 && is_geli(&dsk) == 0) { + /* Decrypt */ + if (geli_read(&dsk, lba * DEV_BSIZE, buf, nblk * DEV_BSIZE)) + return (err); + } +#endif + + return (err); +} + +#ifdef LOADER_GELI_SUPPORT +/* + * Read function compartible with the ZFS callback, required to keep the GELI + * Implementation the same for both UFS and ZFS + */ +static int +vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, size_t bytes) +{ + char *p; + daddr_t lba; + unsigned int nb; + struct dsk *dskp = (struct dsk *) priv; + + if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1))) + return (-1); + + p = buf; + lba = off / DEV_BSIZE; + lba += dskp->start; + + while (bytes > 0) { + nb = bytes / DEV_BSIZE; + if (nb > VBLKSIZE / DEV_BSIZE) + nb = VBLKSIZE / DEV_BSIZE; + if (drvread(dskp, dmadat->blkbuf, lba, nb)) + return (-1); + memcpy(p, dmadat->blkbuf, nb * DEV_BSIZE); + p += nb * DEV_BSIZE; + lba += nb; + bytes -= nb * DEV_BSIZE; + } + + return (0); } +#endif /* LOADER_GELI_SUPPORT */ diff --git a/sys/boot/i386/gptzfsboot/Makefile b/sys/boot/i386/gptzfsboot/Makefile index 1406333..97ddd39 100644 --- a/sys/boot/i386/gptzfsboot/Makefile +++ b/sys/boot/i386/gptzfsboot/Makefile @@ -35,6 +35,14 @@ CFLAGS= -DBOOTPROG=\"gptzfsboot\" \ -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \ -Winline +.if !defined(LOADER_NO_GELI_SUPPORT) +CFLAGS+= -DLOADER_GELI_SUPPORT +CFLAGS+= -I${.CURDIR}/../../geli +LIBGELIBOOT= ${.OBJDIR}/../../geli/libgeliboot.a +.PATH: ${.CURDIR}/../../../opencrypto +OPENCRYPTO_XTS= xform_aes_xts.o +.endif + CFLAGS.gcc+= --param max-inline-insns-single=100 LD_FLAGS=-static -N --gc-sections @@ -59,13 +67,13 @@ gptldr.out: gptldr.o ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o CLEANFILES+= gptzfsboot.bin gptzfsboot.out zfsboot.o sio.o cons.o \ - drv.o gpt.o util.o + drv.o gpt.o util.o ${OPENCRYPTO_XTS} gptzfsboot.bin: gptzfsboot.out ${OBJCOPY} -S -O binary gptzfsboot.out ${.TARGET} -gptzfsboot.out: ${BTXCRT} zfsboot.o sio.o gpt.o drv.o cons.o util.o - ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} +gptzfsboot.out: ${BTXCRT} zfsboot.o sio.o gpt.o drv.o cons.o util.o ${OPENCRYPTO_XTS} + ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} ${LIBGELIBOOT} zfsboot.o: ${.CURDIR}/../../zfs/zfsimpl.c @@ -80,4 +88,3 @@ machine: .NOMETA # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.gptldr.S= ${CLANG_NO_IAS} -CFLAGS+= ${CFLAGS.${.IMPSRC:T}} diff --git a/sys/boot/i386/gptzfsboot/Makefile.depend b/sys/boot/i386/gptzfsboot/Makefile.depend index 63a43d8..295be1a 100644 --- a/sys/boot/i386/gptzfsboot/Makefile.depend +++ b/sys/boot/i386/gptzfsboot/Makefile.depend @@ -4,6 +4,9 @@ DIRDEPS = \ include \ include/xlocale \ + lib/libmd \ + lib/libstand \ + sys/boot/geli \ sys/boot/i386/btx/btx \ sys/boot/i386/btx/lib \ sys/boot/libstand32 \ diff --git a/sys/boot/i386/libi386/Makefile b/sys/boot/i386/libi386/Makefile index f3c1d8d..f1c55f1 100644 --- a/sys/boot/i386/libi386/Makefile +++ b/sys/boot/i386/libi386/Makefile @@ -30,6 +30,12 @@ CFLAGS+= -DCOMSPEED=${BOOT_COMCONSOLE_SPEED} CFLAGS+= -DDISK_DEBUG .endif +.if !defined(LOADER_NO_GELI_SUPPORT) +# Decrypt encrypted drives +CFLAGS+= -DLOADER_GELI_SUPPORT +CFLAGS+= -I${.CURDIR}/../../geli +.endif + .if !defined(BOOT_HIDE_SERIAL_NUMBERS) # Export serial numbers, UUID, and asset tag from loader. CFLAGS+= -DSMBIOS_SERIAL_NUMBERS @@ -69,7 +75,6 @@ machine: .NOMETA # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.amd64_tramp.S= ${CLANG_NO_IAS} CFLAGS.multiboot_tramp.S= ${CLANG_NO_IAS} -CFLAGS+= ${CFLAGS.${.IMPSRC:T}} .if ${MACHINE_CPUARCH} == "amd64" beforedepend ${OBJS}: machine diff --git a/sys/boot/i386/libi386/Makefile.depend b/sys/boot/i386/libi386/Makefile.depend index 18be76b..df20c96 100644 --- a/sys/boot/i386/libi386/Makefile.depend +++ b/sys/boot/i386/libi386/Makefile.depend @@ -4,6 +4,7 @@ DIRDEPS = \ include \ include/xlocale \ + lib/libmd \ .include <dirdeps.mk> diff --git a/sys/boot/i386/libi386/biosdisk.c b/sys/boot/i386/libi386/biosdisk.c index 2a71d64..1c54769 100644 --- a/sys/boot/i386/libi386/biosdisk.c +++ b/sys/boot/i386/libi386/biosdisk.c @@ -49,6 +49,34 @@ __FBSDID("$FreeBSD$"); #include "disk.h" #include "libi386.h" +#ifdef LOADER_GELI_SUPPORT +#include "cons.h" +#include "drv.h" +#include "gpt.h" +#include "part.h" +#include <uuid.h> +struct pentry { + struct ptable_entry part; + uint64_t flags; + union { + uint8_t bsd; + uint8_t mbr; + uuid_t gpt; + uint16_t vtoc8; + } type; + STAILQ_ENTRY(pentry) entry; +}; +struct ptable { + enum ptable_type type; + uint16_t sectorsize; + uint64_t sectors; + + STAILQ_HEAD(, pentry) entries; +}; + +#include "geliboot.c" +#endif /* LOADER_GELI_SUPPORT */ + CTASSERT(sizeof(struct i386_devdesc) >= sizeof(struct disk_devdesc)); #define BIOS_NUMDRIVES 0x475 @@ -108,6 +136,18 @@ static int bd_ioctl(struct open_file *f, u_long cmd, void *data); static void bd_print(int verbose); static void bd_cleanup(void); +#ifdef LOADER_GELI_SUPPORT +static enum isgeli { + ISGELI_UNKNOWN, + ISGELI_NO, + ISGELI_YES +}; +static enum isgeli geli_status[MAXBDDEV][MAXTBLENTS]; + +int bios_read(void *vdev __unused, struct dsk *priv, off_t off, char *buf, + size_t bytes); +#endif /* LOADER_GELI_SUPPORT */ + struct devsw biosdisk = { "disk", DEVT_DISK, @@ -154,6 +194,9 @@ bd_init(void) { int base, unit, nfd = 0; +#ifdef LOADER_GELI_SUPPORT + geli_init(); +#endif /* sequence 0, 0x80 */ for (base = 0; base <= 0x80; base += 0x80) { for (unit = base; (nbdinfo < MAXBDDEV); unit++) { @@ -299,7 +342,8 @@ bd_print(int verbose) static int bd_open(struct open_file *f, ...) { - struct disk_devdesc *dev; + struct disk_devdesc *dev, rdev; + int err, g_err; va_list ap; va_start(ap, f); @@ -309,9 +353,83 @@ bd_open(struct open_file *f, ...) if (dev->d_unit < 0 || dev->d_unit >= nbdinfo) return (EIO); - return (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize, + err = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize, BD(dev).bd_sectorsize, (BD(dev).bd_flags & BD_FLOPPY) ? - DISK_F_NOCACHE: 0)); + DISK_F_NOCACHE: 0); + +#ifdef LOADER_GELI_SUPPORT + static char gelipw[GELI_PW_MAXLEN]; + char *passphrase; + + if (err) + return (err); + + /* if we already know there is no GELI, skip the rest */ + if (geli_status[dev->d_unit][dev->d_slice] != ISGELI_UNKNOWN) + return (err); + + struct dsk dskp; + struct ptable *table = NULL; + struct ptable_entry part; + struct pentry *entry; + int geli_part = 0; + + dskp.drive = bd_unit2bios(dev->d_unit); + dskp.type = dev->d_type; + dskp.unit = dev->d_unit; + dskp.slice = dev->d_slice; + dskp.part = dev->d_partition; + dskp.start = dev->d_offset; + + memcpy(&rdev, dev, sizeof(rdev)); + /* to read the GPT table, we need to read the first sector */ + rdev.d_offset = 0; + /* We need the LBA of the end of the partition */ + table = ptable_open(&rdev, BD(dev).bd_sectors, + BD(dev).bd_sectorsize, ptblread); + if (table == NULL) { + DEBUG("Can't read partition table"); + /* soft failure, return the exit status of disk_open */ + return (err); + } + + if (table->type == PTABLE_GPT) + dskp.part = 255; + + STAILQ_FOREACH(entry, &table->entries, entry) { + dskp.slice = entry->part.index; + dskp.start = entry->part.start; + if (is_geli(&dskp) == 0) { + geli_status[dev->d_unit][dskp.slice] = ISGELI_YES; + return (0); + } + if (geli_taste(bios_read, &dskp, + entry->part.end - entry->part.start) == 0) { + if ((passphrase = getenv("kern.geom.eli.passphrase")) + != NULL) { + /* Use the cached passphrase */ + bcopy(passphrase, &gelipw, GELI_PW_MAXLEN); + } + if (geli_passphrase(&gelipw, dskp.unit, 'p', + (dskp.slice > 0 ? dskp.slice : dskp.part), + &dskp) == 0) { + setenv("kern.geom.eli.passphrase", &gelipw, 1); + bzero(gelipw, sizeof(gelipw)); + geli_status[dev->d_unit][dskp.slice] = ISGELI_YES; + geli_part++; + } + } else + geli_status[dev->d_unit][dskp.slice] = ISGELI_NO; + } + + /* none of the partitions on this disk have GELI */ + if (geli_part == 0) { + /* found no GELI */ + geli_status[dev->d_unit][dev->d_slice] = ISGELI_NO; + } +#endif /* LOADER_GELI_SUPPORT */ + + return (err); } static int @@ -586,6 +704,38 @@ bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, int write) static int bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest) { +#ifdef LOADER_GELI_SUPPORT + struct dsk dskp; + off_t p_off; + int err, n; + + /* if we already know there is no GELI, skip the rest */ + if (geli_status[dev->d_unit][dev->d_slice] != ISGELI_YES) + return (bd_io(dev, dblk, blks, dest, 0)); + + if (geli_status[dev->d_unit][dev->d_slice] == ISGELI_YES) { + err = bd_io(dev, dblk, blks, dest, 0); + if (err) + return (err); + + dskp.drive = bd_unit2bios(dev->d_unit); + dskp.type = dev->d_type; + dskp.unit = dev->d_unit; + dskp.slice = dev->d_slice; + dskp.part = dev->d_partition; + dskp.start = dev->d_offset; + + /* GELI needs the offset relative to the partition start */ + p_off = dblk - dskp.start; + + err = geli_read(&dskp, p_off * BIOSDISK_SECSIZE, dest, + blks * BIOSDISK_SECSIZE); + if (err) + return (err); + + return (0); + } +#endif /* LOADER_GELI_SUPPORT */ return (bd_io(dev, dblk, blks, dest, 0)); } @@ -682,3 +832,25 @@ bd_getdev(struct i386_devdesc *d) DEBUG("dev is 0x%x\n", rootdev); return(rootdev); } + +#ifdef LOADER_GELI_SUPPORT +int +bios_read(void *vdev __unused, struct dsk *priv, off_t off, char *buf, size_t bytes) +{ + struct disk_devdesc dev; + + dev.d_dev = &biosdisk; + dev.d_type = priv->type; + dev.d_unit = priv->unit; + dev.d_slice = priv->slice; + dev.d_partition = priv->part; + dev.d_offset = priv->start; + + off = off / BIOSDISK_SECSIZE; + /* GELI gives us the offset relative to the partition start */ + off += dev.d_offset; + bytes = bytes / BIOSDISK_SECSIZE; + + return (bd_io(&dev, off, bytes, buf, 0)); +} +#endif /* LOADER_GELI_SUPPORT */ diff --git a/sys/boot/i386/libi386/pxe.c b/sys/boot/i386/libi386/pxe.c index efa04fc..d67665e 100644 --- a/sys/boot/i386/libi386/pxe.c +++ b/sys/boot/i386/libi386/pxe.c @@ -310,6 +310,11 @@ pxe_open(struct open_file *f, ...) sprintf(temp, "%6D", bootplayer.CAddr, ":"); setenv("boot.netif.hwaddr", temp, 1); } + if (intf_mtu != 0) { + char mtu[16]; + sprintf(mtu, "%u", intf_mtu); + setenv("boot.netif.mtu", mtu, 1); + } #ifdef LOADER_NFS_SUPPORT printf("pxe_open: server addr: %s\n", inet_ntoa(rootip)); printf("pxe_open: server path: %s\n", rootpath); diff --git a/sys/boot/i386/loader/Makefile b/sys/boot/i386/loader/Makefile index 776ba92..233876a 100644 --- a/sys/boot/i386/loader/Makefile +++ b/sys/boot/i386/loader/Makefile @@ -58,6 +58,13 @@ CFLAGS+= -DLOADER_GZIP_SUPPORT .if defined(LOADER_NANDFS_SUPPORT) CFLAGS+= -DLOADER_NANDFS_SUPPORT .endif +.if !defined(LOADER_NO_GELI_SUPPORT) +CFLAGS+= -DLOADER_GELI_SUPPORT +LIBGELIBOOT= ${.OBJDIR}/../../geli/libgeliboot.a +.PATH: ${.CURDIR}/../../../opencrypto +SRCS+= xform_aes_xts.c +CFLAGS+= -I${.CURDIR}/../../.. -D_STAND +.endif # Always add MI sources .PATH: ${.CURDIR}/../../common @@ -116,8 +123,8 @@ FILES+= loader.rc menu.rc # XXX crt0.o needs to be first for pxeboot(8) to work OBJS= ${BTXCRT} -DPADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBSTAND} -LDADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBSTAND} +DPADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBSTAND} ${LIBGELIBOOT} +LDADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBSTAND} ${LIBGELIBOOT} .include <bsd.prog.mk> diff --git a/sys/boot/i386/loader/Makefile.depend b/sys/boot/i386/loader/Makefile.depend index 9650fc0..89d5422 100644 --- a/sys/boot/i386/loader/Makefile.depend +++ b/sys/boot/i386/loader/Makefile.depend @@ -6,6 +6,7 @@ DIRDEPS = \ include/xlocale \ lib/libstand \ sys/boot/ficl32 \ + sys/boot/geli \ sys/boot/i386/btx/btx \ sys/boot/i386/btx/btxldr \ sys/boot/i386/btx/lib \ diff --git a/sys/boot/i386/loader/main.c b/sys/boot/i386/loader/main.c index c0782fc..b3e7b41 100644 --- a/sys/boot/i386/loader/main.c +++ b/sys/boot/i386/loader/main.c @@ -68,7 +68,11 @@ static void extract_currdev(void); static int isa_inb(int port); static void isa_outb(int port, int value); void exit(int code); +#ifdef LOADER_GELI_SUPPORT +struct geli_boot_args *gargs; +#endif #ifdef LOADER_ZFS_SUPPORT +struct zfs_boot_args *zargs; static void i386_zfs_probe(void); #endif @@ -164,7 +168,31 @@ main(void) archsw.arch_isaoutb = isa_outb; #ifdef LOADER_ZFS_SUPPORT archsw.arch_zfs_probe = i386_zfs_probe; -#endif + +#ifdef LOADER_GELI_SUPPORT + if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) { + zargs = (struct zfs_boot_args *)(kargs + 1); + if (zargs != NULL && zargs->size >= offsetof(struct zfs_boot_args, gelipw)) { + if (zargs->gelipw[0] != '\0') { + setenv("kern.geom.eli.passphrase", zargs->gelipw, 1); + bzero(zargs->gelipw, sizeof(zargs->gelipw)); + } + } + } +#endif /* LOADER_GELI_SUPPORT */ +#else /* !LOADER_ZFS_SUPPORT */ +#ifdef LOADER_GELI_SUPPORT + if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) { + gargs = (struct geli_boot_args *)(kargs + 1); + if (gargs != NULL && gargs->size >= offsetof(struct geli_boot_args, gelipw)) { + if (gargs->gelipw[0] != '\0') { + setenv("kern.geom.eli.passphrase", gargs->gelipw, 1); + bzero(gargs->gelipw, sizeof(gargs->gelipw)); + } + } + } +#endif /* LOADER_GELI_SUPPORT */ +#endif /* LOADER_ZFS_SUPPORT */ /* * March through the device switch probing for things. @@ -214,7 +242,6 @@ extract_currdev(void) struct i386_devdesc new_currdev; #ifdef LOADER_ZFS_SUPPORT char buf[20]; - struct zfs_boot_args *zargs; #endif int biosdev = -1; diff --git a/sys/boot/i386/pxeldr/Makefile b/sys/boot/i386/pxeldr/Makefile index c4e008f..8de2139 100644 --- a/sys/boot/i386/pxeldr/Makefile +++ b/sys/boot/i386/pxeldr/Makefile @@ -46,4 +46,3 @@ ${LOADER}: ${LOADERBIN} ${BTXLDR} ${BTXKERN} # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.pxeldr.S= ${CLANG_NO_IAS} -CFLAGS+= ${CFLAGS.${.IMPSRC:T}} diff --git a/sys/boot/i386/zfsboot/Makefile b/sys/boot/i386/zfsboot/Makefile index f65e0ad..7f434a8 100644 --- a/sys/boot/i386/zfsboot/Makefile +++ b/sys/boot/i386/zfsboot/Makefile @@ -93,4 +93,3 @@ machine: .NOMETA # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.zfsldr.S= ${CLANG_NO_IAS} -CFLAGS+= ${CFLAGS.${.IMPSRC:T}} diff --git a/sys/boot/i386/zfsboot/zfsboot.c b/sys/boot/i386/zfsboot/zfsboot.c index 6fbbc6f..4b371dc 100644 --- a/sys/boot/i386/zfsboot/zfsboot.c +++ b/sys/boot/i386/zfsboot/zfsboot.c @@ -121,8 +121,10 @@ void exit(int); static void load(void); static int parse(void); static void bios_getmem(void); +void *malloc(size_t n); +void free(void *ptr); -static void * +void * malloc(size_t n) { char *p = heap_next; @@ -130,10 +132,18 @@ malloc(size_t n) printf("malloc failure\n"); for (;;) ; - return 0; + /* NOTREACHED */ + return (0); } heap_next += n; - return p; + return (p); +} + +void +free(void *ptr) +{ + + return; } static char * @@ -141,9 +151,14 @@ strdup(const char *s) { char *p = malloc(strlen(s) + 1); strcpy(p, s); - return p; + return (p); } +#ifdef LOADER_GELI_SUPPORT +#include "geliboot.c" +static char gelipw[GELI_PW_MAXLEN]; +#endif + #include "zfsimpl.c" /* @@ -199,6 +214,14 @@ vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes) nb = READ_BUF_SIZE / DEV_BSIZE; if (drvread(dsk, dmadat->rdbuf, lba, nb)) return -1; +#ifdef LOADER_GELI_SUPPORT + /* decrypt */ + if (is_geli(dsk) == 0) { + if (geli_read(dsk, ((lba - dsk->start) * DEV_BSIZE), + dmadat->rdbuf, nb * DEV_BSIZE)) + return (-1); + } +#endif memcpy(p, dmadat->rdbuf, nb * DEV_BSIZE); p += nb * DEV_BSIZE; lba += nb; @@ -302,7 +325,7 @@ bios_getmem(void) high_heap_size = HEAP_MIN; high_heap_base = bios_extmem + 0x100000 - HEAP_MIN; } -} +} /* * Try to detect a device supported by the legacy int13 BIOS @@ -346,21 +369,39 @@ probe_drive(struct dsk *dsk) #ifdef GPT struct gpt_hdr hdr; struct gpt_ent *ent; - daddr_t slba, elba; unsigned part, entries_per_sec; #endif + daddr_t slba, elba; struct dos_partition *dp; char *sec; unsigned i; /* - * If we find a vdev on the whole disk, stop here. Otherwise dig - * out the partition table and probe each slice/partition - * in turn for a vdev. + * If we find a vdev on the whole disk, stop here. */ if (vdev_probe(vdev_read, dsk, NULL) == 0) return; +#ifdef LOADER_GELI_SUPPORT + /* + * Taste the disk, if it is GELI encrypted, decrypt it and check to see if + * it is a usable vdev then. Otherwise dig + * out the partition table and probe each slice/partition + * in turn for a vdev or GELI encrypted vdev. + */ + elba = drvsize(dsk); + if (elba > 0) { + elba--; + } + if (geli_taste(vdev_read, dsk, elba) == 0) { + if (geli_passphrase(&gelipw, dsk->unit, ':', 0, dsk) == 0) { + if (vdev_probe(vdev_read, dsk, NULL) == 0) { + return; + } + } + } +#endif /* LOADER_GELI_SUPPORT */ + sec = dmadat->secbuf; dsk->start = 0; @@ -383,6 +424,8 @@ probe_drive(struct dsk *dsk) * return the spa_t for the first we find (if requested). This * will have the effect of booting from the first pool on the * disk. + * + * If no vdev is found, GELI decrypting the device and try again */ entries_per_sec = DEV_BSIZE / hdr.hdr_entsz; slba = hdr.hdr_lba_table; @@ -396,6 +439,8 @@ probe_drive(struct dsk *dsk) if (memcmp(&ent->ent_type, &freebsd_zfs_uuid, sizeof(uuid_t)) == 0) { dsk->start = ent->ent_lba_start; + dsk->slice = part + 1; + dsk->part = 255; if (vdev_probe(vdev_read, dsk, NULL) == 0) { /* * This slice had a vdev. We need a new dsk @@ -403,13 +448,31 @@ probe_drive(struct dsk *dsk) */ dsk = copy_dsk(dsk); } +#ifdef LOADER_GELI_SUPPORT + else if (geli_taste(vdev_read, dsk, ent->ent_lba_end - + ent->ent_lba_start) == 0) { + if (geli_passphrase(&gelipw, dsk->unit, 'p', dsk->slice, dsk) == 0) { + /* + * This slice has GELI, check it for ZFS. + */ + if (vdev_probe(vdev_read, dsk, NULL) == 0) { + /* + * This slice had a vdev. We need a new dsk + * structure now since the vdev now owns this one. + */ + dsk = copy_dsk(dsk); + } + break; + } + } +#endif /* LOADER_GELI_SUPPORT */ } } slba++; } return; trymbr: -#endif +#endif /* GPT */ if (drvread(dsk, sec, DOSBBSECTOR, 1)) return; @@ -419,13 +482,28 @@ trymbr: if (!dp[i].dp_typ) continue; dsk->start = dp[i].dp_start; + dsk->slice = i + 1; if (vdev_probe(vdev_read, dsk, NULL) == 0) { - /* - * This slice had a vdev. We need a new dsk structure now - * since the vdev now owns this one. - */ dsk = copy_dsk(dsk); } +#ifdef LOADER_GELI_SUPPORT + else if (geli_taste(vdev_read, dsk, dp[i].dp_size - + dp[i].dp_start) == 0) { + if (geli_passphrase(&gelipw, dsk->unit, 's', i, dsk) == 0) { + /* + * This slice has GELI, check it for ZFS. + */ + if (vdev_probe(vdev_read, dsk, NULL) == 0) { + /* + * This slice had a vdev. We need a new dsk + * structure now since the vdev now owns this one. + */ + dsk = copy_dsk(dsk); + } + break; + } + } +#endif /* LOADER_GELI_SUPPORT */ } } @@ -445,8 +523,8 @@ main(void) heap_end = PTOV(high_heap_base + high_heap_size); heap_next = PTOV(high_heap_base); } else { - heap_next = (char *) dmadat + sizeof(*dmadat); - heap_end = (char *) PTOV(bios_basemem); + heap_next = (char *)dmadat + sizeof(*dmadat); + heap_end = (char *)PTOV(bios_basemem); } dsk = malloc(sizeof(struct dsk)); @@ -472,6 +550,9 @@ main(void) autoboot = 1; +#ifdef LOADER_GELI_SUPPORT + geli_init(); +#endif zfs_init(); /* @@ -690,6 +771,12 @@ load(void) zfsargs.pool = zfsmount.spa->spa_guid; zfsargs.root = zfsmount.rootobj; zfsargs.primary_pool = primary_spa->spa_guid; +#ifdef LOADER_GELI_SUPPORT + bcopy(gelipw, zfsargs.gelipw, sizeof(zfsargs.gelipw)); + bzero(gelipw, sizeof(gelipw)); +#else + zfsargs.gelipw[0] = '\0'; +#endif if (primary_vdev != NULL) zfsargs.primary_vdev = primary_vdev->v_guid; else diff --git a/sys/boot/i386/zfsloader/Makefile.depend b/sys/boot/i386/zfsloader/Makefile.depend index b3810a2..15b0c98 100644 --- a/sys/boot/i386/zfsloader/Makefile.depend +++ b/sys/boot/i386/zfsloader/Makefile.depend @@ -6,6 +6,7 @@ DIRDEPS = \ include/xlocale \ lib/libstand \ sys/boot/ficl32 \ + sys/boot/geli \ sys/boot/i386/btx/btx \ sys/boot/i386/btx/btxldr \ sys/boot/i386/btx/lib \ diff --git a/sys/boot/pc98/boot2/Makefile b/sys/boot/pc98/boot2/Makefile index 6af4f90..8d5e791 100644 --- a/sys/boot/pc98/boot2/Makefile +++ b/sys/boot/pc98/boot2/Makefile @@ -114,4 +114,3 @@ boot2.h: boot1.out # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.boot1.S= ${CLANG_NO_IAS} -CFLAGS+= ${CFLAGS.${.IMPSRC:T}} diff --git a/sys/boot/pc98/btx/btx/Makefile b/sys/boot/pc98/btx/btx/Makefile index 905908f..9046d35 100644 --- a/sys/boot/pc98/btx/btx/Makefile +++ b/sys/boot/pc98/btx/btx/Makefile @@ -31,4 +31,3 @@ LDFLAGS=-e start -Ttext ${ORG} -Wl,-N,-S,--oformat,binary # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.btx.S= ${CLANG_NO_IAS} -CFLAGS+= ${CFLAGS.${.IMPSRC:T}} diff --git a/sys/boot/pc98/btx/btxldr/Makefile b/sys/boot/pc98/btx/btxldr/Makefile index 7d34e9e..b0c8996 100644 --- a/sys/boot/pc98/btx/btxldr/Makefile +++ b/sys/boot/pc98/btx/btxldr/Makefile @@ -18,4 +18,3 @@ LDFLAGS=-e start -Ttext ${LOADER_ADDRESS} -Wl,-N,-S,--oformat,binary # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.btxldr.S= ${CLANG_NO_IAS} -CFLAGS+= ${CFLAGS.${.IMPSRC:T}} diff --git a/sys/boot/pc98/cdboot/Makefile b/sys/boot/pc98/cdboot/Makefile index 23543d4..3b91c12 100644 --- a/sys/boot/pc98/cdboot/Makefile +++ b/sys/boot/pc98/cdboot/Makefile @@ -16,4 +16,3 @@ LDFLAGS=-e start -Ttext ${ORG} -Wl,-N,-S,--oformat,binary # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.cdboot.S= ${CLANG_NO_IAS} -CFLAGS+= ${CFLAGS.${.IMPSRC:T}} diff --git a/sys/boot/zfs/libzfs.h b/sys/boot/zfs/libzfs.h index ee64d1c..58bfd26 100644 --- a/sys/boot/zfs/libzfs.h +++ b/sys/boot/zfs/libzfs.h @@ -55,6 +55,7 @@ struct zfs_boot_args uint64_t root; uint64_t primary_pool; uint64_t primary_vdev; + char gelipw[256]; }; int zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, diff --git a/sys/cam/ata/ata_da.c b/sys/cam/ata/ata_da.c index 6ea25c2..38cf46e 100644 --- a/sys/cam/ata/ata_da.c +++ b/sys/cam/ata/ata_da.c @@ -466,14 +466,6 @@ static struct ada_quirk_entry ada_quirk_table[] = }, { /* - * Samsung 843T Series SSDs - * 4k optimised - */ - { T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG MZ7WD*", "*" }, - /*quirks*/ADA_Q_4K - }, - { - /* * Samsung 850 SSDs * 4k optimised */ @@ -482,10 +474,13 @@ static struct ada_quirk_entry ada_quirk_table[] = }, { /* - * Samsung PM853T Series SSDs + * Samsung 843T Series SSDs (MZ7WD*) + * Samsung PM851 Series SSDs (MZ7TE*) + * Samsung PM853T Series SSDs (MZ7GE*) + * Samsung SM863 Series SSDs (MZ7KM*) * 4k optimised */ - { T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG MZ7GE*", "*" }, + { T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG MZ7*", "*" }, /*quirks*/ADA_Q_4K }, { diff --git a/sys/cam/cam_ccb.h b/sys/cam/cam_ccb.h index 4e1e6a2..28415ed 100644 --- a/sys/cam/cam_ccb.h +++ b/sys/cam/cam_ccb.h @@ -725,6 +725,13 @@ struct ccb_scsiio { u_int init_id; /* initiator id of who selected */ }; +static __inline uint8_t * +scsiio_cdb_ptr(struct ccb_scsiio *ccb) +{ + return ((ccb->ccb_h.flags & CAM_CDB_POINTER) ? + ccb->cdb_io.cdb_ptr : ccb->cdb_io.cdb_bytes); +} + /* * ATA I/O Request CCB used for the XPT_ATA_IO function code. */ diff --git a/sys/cam/scsi/scsi_ch.c b/sys/cam/scsi/scsi_ch.c index 137128d..8e069db 100644 --- a/sys/cam/scsi/scsi_ch.c +++ b/sys/cam/scsi/scsi_ch.c @@ -651,6 +651,7 @@ chdone(struct cam_periph *periph, union ccb *done_ccb) } else { int error; + announce_buf[0] = '\0'; error = cherror(done_ccb, CAM_RETRY_SELTO, SF_RETRY_UA | SF_NO_PRINT); /* @@ -659,7 +660,7 @@ chdone(struct cam_periph *periph, union ccb *done_ccb) */ if (error == ERESTART) { /* - * A retry was scheuled, so + * A retry was scheduled, so * just return. */ return; diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c index 9d39182..07a6435 100644 --- a/sys/cam/scsi/scsi_da.c +++ b/sys/cam/scsi/scsi_da.c @@ -1138,14 +1138,6 @@ static struct da_quirk_entry da_quirk_table[] = }, { /* - * Samsung 843T Series SSDs - * 4k optimised - */ - { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "SAMSUNG MZ7WD*", "*" }, - /*quirks*/DA_Q_4K - }, - { - /* * Samsung 850 SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ @@ -1154,10 +1146,13 @@ static struct da_quirk_entry da_quirk_table[] = }, { /* - * Samsung PM853T Series SSDs + * Samsung 843T Series SSDs (MZ7WD*) + * Samsung PM851 Series SSDs (MZ7TE*) + * Samsung PM853T Series SSDs (MZ7GE*) + * Samsung SM863 Series SSDs (MZ7KM*) * 4k optimised */ - { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "SAMSUNG MZ7GE*", "*" }, + { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "SAMSUNG MZ7*", "*" }, /*quirks*/DA_Q_4K }, { diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c b/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c index a2532f8..49becbc 100644 --- a/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c +++ b/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c @@ -196,6 +196,7 @@ mount_snapshot(kthread_t *td, vnode_t **vpp, const char *fstype, char *fspath, VI_UNLOCK(vp); vrele(vp); vfs_unbusy(mp); + vfs_freeopts(mp->mnt_optnew); vfs_mount_destroy(mp); *vpp = NULL; return (error); diff --git a/sys/cddl/compat/opensolaris/sys/vfs.h b/sys/cddl/compat/opensolaris/sys/vfs.h index e1e49ed..c6e21cc 100644 --- a/sys/cddl/compat/opensolaris/sys/vfs.h +++ b/sys/cddl/compat/opensolaris/sys/vfs.h @@ -54,17 +54,6 @@ typedef struct mount vfs_t; #define VFS_NOSETUID MNT_NOSUID #define VFS_NOEXEC MNT_NOEXEC -#define VFS_HOLD(vfsp) do { \ - MNT_ILOCK(vfsp); \ - MNT_REF(vfsp); \ - MNT_IUNLOCK(vfsp); \ -} while (0) -#define VFS_RELE(vfsp) do { \ - MNT_ILOCK(vfsp); \ - MNT_REL(vfsp); \ - MNT_IUNLOCK(vfsp); \ -} while (0) - #define fs_vscan(vp, cr, async) (0) #define VROOT VV_ROOT diff --git a/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c b/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c index d3bce0e..7ec7dfd 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c +++ b/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c @@ -246,10 +246,6 @@ static int dtrace_dynvar_failclean; /* dynvars failed to clean */ #ifndef illumos static struct mtx dtrace_unr_mtx; MTX_SYSINIT(dtrace_unr_mtx, &dtrace_unr_mtx, "Unique resource identifier", MTX_DEF); -int dtrace_in_probe; /* non-zero if executing a probe */ -#if defined(__i386__) || defined(__amd64__) || defined(__mips__) || defined(__powerpc__) -uintptr_t dtrace_in_probe_addr; /* Address of invop when already in probe */ -#endif static eventhandler_tag dtrace_kld_load_tag; static eventhandler_tag dtrace_kld_unload_try_tag; #endif diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/gfs.c b/sys/cddl/contrib/opensolaris/uts/common/fs/gfs.c index 89882f4..c5c5c00 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/gfs.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/gfs.c @@ -589,7 +589,9 @@ gfs_root_create(size_t size, vfs_t *vfsp, vnodeops_t *ops, ino64_t ino, { vnode_t *vp; +#ifdef illumos VFS_HOLD(vfsp); +#endif vp = gfs_dir_create(size, NULL, vfsp, ops, entries, inode_cb, maxlen, readdir_cb, lookup_cb); /* Manually set the inode */ @@ -700,7 +702,9 @@ found: vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); } else { ASSERT(vp->v_vfsp != NULL); +#ifdef illumos VFS_RELE(vp->v_vfsp); +#endif } #ifdef TODO if (vp->v_flag & V_XATTRDIR) diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c index 188810b..9e8d86e 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c @@ -107,6 +107,19 @@ dump_bytes(dmu_sendarg_t *dsp, void *buf, int len) dsl_dataset_t *ds = dmu_objset_ds(dsp->dsa_os); struct uio auio; struct iovec aiov; + + /* + * The code does not rely on this (len being a multiple of 8). We keep + * this assertion because of the corresponding assertion in + * receive_read(). Keeping this assertion ensures that we do not + * inadvertently break backwards compatibility (causing the assertion + * in receive_read() to trigger on old software). + * + * Removing the assertions could be rolled into a new feature that uses + * data that isn't 8-byte aligned; if the assertions were removed, a + * feature flag would have to be added. + */ + ASSERT0(len % 8); aiov.iov_base = buf; @@ -1824,7 +1837,10 @@ receive_read(struct receive_arg *ra, int len, void *buf) { int done = 0; - /* some things will require 8-byte alignment, so everything must */ + /* + * The code doesn't rely on this (lengths being multiples of 8). See + * comment in dump_bytes. + */ ASSERT0(len % 8); while (done < len) { diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_prop.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_prop.c index 12e2771..8586cb6 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_prop.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_prop.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2014 by Delphix. All rights reserved. + * Copyright 2015, Joyent, Inc. */ #include <sys/zfs_context.h> @@ -41,16 +42,14 @@ #define ZPROP_RECVD_SUFFIX "$recvd" static int -dodefault(const char *propname, int intsz, int numints, void *buf) +dodefault(zfs_prop_t prop, int intsz, int numints, void *buf) { - zfs_prop_t prop; - /* * The setonce properties are read-only, BUT they still * have a default value that can be used as the initial * value. */ - if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL || + if (prop == ZPROP_INVAL || (zfs_prop_readonly(prop) && !zfs_prop_setonce(prop))) return (SET_ERROR(ENOENT)); @@ -148,7 +147,7 @@ dsl_prop_get_dd(dsl_dir_t *dd, const char *propname, } if (err == ENOENT) - err = dodefault(propname, intsz, numints, buf); + err = dodefault(prop, intsz, numints, buf); strfree(inheritstr); strfree(recvdstr); @@ -622,7 +621,7 @@ dsl_prop_set_sync_impl(dsl_dataset_t *ds, const char *propname, int err; uint64_t version = spa_version(ds->ds_dir->dd_pool->dp_spa); - isint = (dodefault(propname, 8, 1, &intval) == 0); + isint = (dodefault(zfs_name_to_prop(propname), 8, 1, &intval) == 0); if (ds->ds_is_snapshot) { ASSERT(version >= SPA_VERSION_SNAP_PROPS); @@ -1180,7 +1179,7 @@ dsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value) VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0); /* Indicate the default source if we can. */ - if (dodefault(propname, 8, 1, &default_value) == 0 && + if (dodefault(prop, 8, 1, &default_value) == 0 && value == default_value) { VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, "") == 0); } diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c index c24836e..430ed34 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c @@ -1443,7 +1443,14 @@ getzfsvfs(const char *dsname, zfsvfs_t **zfvp) mutex_enter(&os->os_user_ptr_lock); *zfvp = dmu_objset_get_user(os); if (*zfvp) { +#ifdef illumos VFS_HOLD((*zfvp)->z_vfs); +#else + if (vfs_busy((*zfvp)->z_vfs, 0) != 0) { + *zfvp = NULL; + error = SET_ERROR(ESRCH); + } +#endif } else { error = SET_ERROR(ESRCH); } @@ -1487,7 +1494,11 @@ zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag) rrm_exit(&zfsvfs->z_teardown_lock, tag); if (zfsvfs->z_vfs) { +#ifdef illumos VFS_RELE(zfsvfs->z_vfs); +#else + vfs_unbusy(zfsvfs->z_vfs); +#endif } else { dmu_objset_disown(zfsvfs->z_os, zfsvfs); zfsvfs_free(zfsvfs); @@ -3018,11 +3029,13 @@ zfs_get_vfs(const char *resource) mtx_lock(&mountlist_mtx); TAILQ_FOREACH(vfsp, &mountlist, mnt_list) { if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) { - VFS_HOLD(vfsp); + if (vfs_busy(vfsp, MBF_MNTLSTLOCK) != 0) + vfsp = NULL; break; } } - mtx_unlock(&mountlist_mtx); + if (vfsp == NULL) + mtx_unlock(&mountlist_mtx); return (vfsp); } @@ -3475,7 +3488,11 @@ zfs_unmount_snap(const char *snapname) ASSERT(!dsl_pool_config_held(dmu_objset_pool(zfsvfs->z_os))); err = vn_vfswlock(vfsp->vfs_vnodecovered); +#ifdef illumos VFS_RELE(vfsp); +#else + vfs_unbusy(vfsp); +#endif if (err != 0) return (SET_ERROR(err)); @@ -3721,7 +3738,11 @@ zfs_ioc_rollback(const char *fsname, nvlist_t *args, nvlist_t *outnvl) resume_err = zfs_resume_fs(zfsvfs, fsname); error = error ? error : resume_err; } +#ifdef illumos VFS_RELE(zfsvfs->z_vfs); +#else + vfs_unbusy(zfsvfs->z_vfs); +#endif } else { error = dsl_dataset_rollback(fsname, NULL, outnvl); } @@ -4376,7 +4397,11 @@ zfs_ioc_recv(zfs_cmd_t *zc) if (error == 0) error = zfs_resume_fs(zfsvfs, tofs); error = error ? error : end_err; +#ifdef illumos VFS_RELE(zfsvfs->z_vfs); +#else + vfs_unbusy(zfsvfs->z_vfs); +#endif } else { error = dmu_recv_end(&drc, NULL); } @@ -4925,7 +4950,11 @@ zfs_ioc_userspace_upgrade(zfs_cmd_t *zc) } if (error == 0) error = dmu_objset_userspace_upgrade(zfsvfs->z_os); +#ifdef illumos VFS_RELE(zfsvfs->z_vfs); +#else + vfs_unbusy(zfsvfs->z_vfs); +#endif } else { /* XXX kind of reading contents without owning */ error = dmu_objset_hold(zc->zc_name, FTAG, &os); diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c index 5c2b66d..96b1cec 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c @@ -5773,7 +5773,7 @@ zfs_getpages(struct vnode *vp, vm_page_t *m, int count, int *rbehind, off_t startoff, endoff; int i, error; vm_pindex_t reqstart, reqend; - int lsize, reqsize, size; + int lsize, size; object = m[0]->object; error = 0; @@ -5797,7 +5797,7 @@ zfs_getpages(struct vnode *vp, vm_page_t *m, int count, int *rbehind, } PCPU_INC(cnt.v_vnodein); - PCPU_ADD(cnt.v_vnodepgsin, reqsize); + PCPU_ADD(cnt.v_vnodepgsin, count); lsize = PAGE_SIZE; if (IDX_TO_OFF(mlast->pindex) + lsize > object->un_pager.vnp.vnp_size) diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c index 04f0b6c..964b453 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c @@ -743,7 +743,9 @@ zfs_znode_alloc(zfsvfs_t *zfsvfs, dmu_buf_t *db, int blksz, if (vp->v_type != VFIFO) VN_LOCK_ASHARE(vp); +#ifdef illumos VFS_HOLD(zfsvfs->z_vfs); +#endif return (zp); } @@ -1428,7 +1430,9 @@ zfs_znode_free(znode_t *zp) kmem_cache_free(znode_cache, zp); +#ifdef illumos VFS_RELE(zfsvfs->z_vfs); +#endif } void diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio.c index 25fba19..05e16ba 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio.c @@ -2775,10 +2775,19 @@ zio_vdev_io_start(zio_t *zio) (void) atomic_cas_64(&spa->spa_last_io, old, new); } +#ifdef illumos align = 1ULL << vd->vdev_top->vdev_ashift; if (!(zio->io_flags & ZIO_FLAG_PHYSICAL) && P2PHASE(zio->io_size, align) != 0) { +#else + if (zio->io_flags & ZIO_FLAG_PHYSICAL) + align = 1ULL << vd->vdev_top->vdev_logical_ashift; + else + align = 1ULL << vd->vdev_top->vdev_ashift; + + if (P2PHASE(zio->io_size, align) != 0) { +#endif /* Transform logical writes to be a full physical block size. */ uint64_t asize = P2ROUNDUP(zio->io_size, align); char *abuf = NULL; @@ -2794,6 +2803,7 @@ zio_vdev_io_start(zio_t *zio) zio_subblock); } +#ifdef illumos /* * If this is not a physical io, make sure that it is properly aligned * before proceeding. @@ -2809,6 +2819,10 @@ zio_vdev_io_start(zio_t *zio) ASSERT0(P2PHASE(zio->io_offset, SPA_MINBLOCKSIZE)); ASSERT0(P2PHASE(zio->io_size, SPA_MINBLOCKSIZE)); } +#else + ASSERT0(P2PHASE(zio->io_offset, align)); + ASSERT0(P2PHASE(zio->io_size, align)); +#endif VERIFY(zio->io_type == ZIO_TYPE_READ || spa_writeable(spa)); diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c index 0c3cfce..d0c7a74 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c @@ -589,7 +589,6 @@ zvol_create_minor(const char *name) minor_t minor = 0; char chrbuf[30], blkbuf[30]; #else - struct cdev *dev; struct g_provider *pp; struct g_geom *gp; uint64_t volsize, mode; @@ -688,17 +687,25 @@ zvol_create_minor(const char *name) bioq_init(&zv->zv_queue); mtx_init(&zv->zv_queue_mtx, "zvol", NULL, MTX_DEF); } else if (zv->zv_volmode == ZFS_VOLMODE_DEV) { - if (make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK, - &dev, &zvol_cdevsw, NULL, UID_ROOT, GID_OPERATOR, - 0640, "%s/%s", ZVOL_DRIVER, name) != 0) { + struct make_dev_args args; + + make_dev_args_init(&args); + args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK; + args.mda_devsw = &zvol_cdevsw; + args.mda_cr = NULL; + args.mda_uid = UID_ROOT; + args.mda_gid = GID_OPERATOR; + args.mda_mode = 0640; + args.mda_si_drv2 = zv; + error = make_dev_s(&args, &zv->zv_dev, + "%s/%s", ZVOL_DRIVER, name); + if (error != 0) { kmem_free(zv, sizeof(*zv)); dmu_objset_disown(os, FTAG); mutex_exit(&zfsdev_state_lock); - return (SET_ERROR(ENXIO)); + return (error); } - zv->zv_dev = dev; - dev->si_iosize_max = MAXPHYS; - dev->si_drv2 = zv; + zv->zv_dev->si_iosize_max = MAXPHYS; } LIST_INSERT_HEAD(&all_zvols, zv, zv_links); #endif /* illumos */ @@ -2854,7 +2861,8 @@ zvol_create_snapshots(objset_t *os, const char *name) break; } - if ((error = zvol_create_minor(sname)) != 0) { + error = zvol_create_minor(sname); + if (error != 0 && error != EEXIST) { printf("ZFS WARNING: Unable to create ZVOL %s (error=%d).\n", sname, error); break; @@ -2963,18 +2971,29 @@ zvol_rename_minor(zvol_state_t *zv, const char *newname) g_error_provider(pp, 0); g_topology_unlock(); } else if (zv->zv_volmode == ZFS_VOLMODE_DEV) { + struct make_dev_args args; + dev = zv->zv_dev; ASSERT(dev != NULL); zv->zv_dev = NULL; destroy_dev(dev); - - if (make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK, - &dev, &zvol_cdevsw, NULL, UID_ROOT, GID_OPERATOR, - 0640, "%s/%s", ZVOL_DRIVER, newname) == 0) { - zv->zv_dev = dev; - dev->si_iosize_max = MAXPHYS; - dev->si_drv2 = zv; + if (zv->zv_total_opens > 0) { + zv->zv_flags &= ~ZVOL_EXCL; + zv->zv_total_opens = 0; + zvol_last_close(zv); } + + make_dev_args_init(&args); + args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK; + args.mda_devsw = &zvol_cdevsw; + args.mda_cr = NULL; + args.mda_uid = UID_ROOT; + args.mda_gid = GID_OPERATOR; + args.mda_mode = 0640; + args.mda_si_drv2 = zv; + if (make_dev_s(&args, &zv->zv_dev, + "%s/%s", ZVOL_DRIVER, newname) == 0) + zv->zv_dev->si_iosize_max = MAXPHYS; } strlcpy(zv->zv_name, newname, sizeof(zv->zv_name)); } @@ -3021,16 +3040,10 @@ zvol_rename_minors(const char *oldname, const char *newname) static int zvol_d_open(struct cdev *dev, int flags, int fmt, struct thread *td) { - zvol_state_t *zv; + zvol_state_t *zv = dev->si_drv2; int err = 0; mutex_enter(&zfsdev_state_lock); - zv = dev->si_drv2; - if (zv == NULL) { - mutex_exit(&zfsdev_state_lock); - return(ENXIO); /* zvol_create_minor() not done yet */ - } - if (zv->zv_total_opens == 0) err = zvol_first_open(zv); if (err) { @@ -3068,16 +3081,9 @@ out: static int zvol_d_close(struct cdev *dev, int flags, int fmt, struct thread *td) { - zvol_state_t *zv; - int err = 0; + zvol_state_t *zv = dev->si_drv2; mutex_enter(&zfsdev_state_lock); - zv = dev->si_drv2; - if (zv == NULL) { - mutex_exit(&zfsdev_state_lock); - return(ENXIO); - } - if (zv->zv_flags & ZVOL_EXCL) { ASSERT(zv->zv_total_opens == 1); zv->zv_flags &= ~ZVOL_EXCL; diff --git a/sys/cddl/dev/dtrace/aarch64/dtrace_subr.c b/sys/cddl/dev/dtrace/aarch64/dtrace_subr.c index 9cf5bc4..78b5637 100644 --- a/sys/cddl/dev/dtrace/aarch64/dtrace_subr.c +++ b/sys/cddl/dev/dtrace/aarch64/dtrace_subr.c @@ -45,8 +45,6 @@ __FBSDID("$FreeBSD$"); #include <machine/trap.h> #include <vm/pmap.h> -extern uintptr_t dtrace_in_probe_addr; -extern int dtrace_in_probe; extern dtrace_id_t dtrace_probeid_error; extern int (*dtrace_invop_jump_addr)(struct trapframe *); extern void dtrace_getnanotime(struct timespec *tsp); diff --git a/sys/cddl/dev/dtrace/amd64/dtrace_subr.c b/sys/cddl/dev/dtrace/amd64/dtrace_subr.c index 7f2ec7e..f6577d5 100644 --- a/sys/cddl/dev/dtrace/amd64/dtrace_subr.c +++ b/sys/cddl/dev/dtrace/amd64/dtrace_subr.c @@ -44,9 +44,6 @@ #include <machine/frame.h> #include <vm/pmap.h> -extern uintptr_t dtrace_in_probe_addr; -extern int dtrace_in_probe; - extern void dtrace_getnanotime(struct timespec *tsp); int dtrace_invop(uintptr_t, uintptr_t *, uintptr_t); diff --git a/sys/cddl/dev/dtrace/arm/dtrace_subr.c b/sys/cddl/dev/dtrace/arm/dtrace_subr.c index 2aea13e..44dde29 100644 --- a/sys/cddl/dev/dtrace/arm/dtrace_subr.c +++ b/sys/cddl/dev/dtrace/arm/dtrace_subr.c @@ -51,8 +51,6 @@ __FBSDID("$FreeBSD$"); #define BIT_LR 14 #define BIT_SP 13 -extern uintptr_t dtrace_in_probe_addr; -extern int dtrace_in_probe; extern dtrace_id_t dtrace_probeid_error; extern int (*dtrace_invop_jump_addr)(struct trapframe *); extern void dtrace_getnanotime(struct timespec *tsp); diff --git a/sys/cddl/dev/dtrace/i386/dtrace_subr.c b/sys/cddl/dev/dtrace/i386/dtrace_subr.c index 814019e..be5bd4b 100644 --- a/sys/cddl/dev/dtrace/i386/dtrace_subr.c +++ b/sys/cddl/dev/dtrace/i386/dtrace_subr.c @@ -46,8 +46,6 @@ #include <vm/pmap.h> extern uintptr_t kernelbase; -extern uintptr_t dtrace_in_probe_addr; -extern int dtrace_in_probe; extern void dtrace_getnanotime(struct timespec *tsp); diff --git a/sys/cddl/dev/dtrace/mips/dtrace_subr.c b/sys/cddl/dev/dtrace/mips/dtrace_subr.c index 4f13b98..a2aa8c7 100644 --- a/sys/cddl/dev/dtrace/mips/dtrace_subr.c +++ b/sys/cddl/dev/dtrace/mips/dtrace_subr.c @@ -46,8 +46,6 @@ __FBSDID("$FreeBSD$"); #define DELAYBRANCH(x) ((int)(x) < 0) -extern uintptr_t dtrace_in_probe_addr; -extern int dtrace_in_probe; extern dtrace_id_t dtrace_probeid_error; int dtrace_invop(uintptr_t, uintptr_t *, uintptr_t); diff --git a/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c b/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c index c0360fd..33b9e71 100644 --- a/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c +++ b/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c @@ -46,8 +46,6 @@ __FBSDID("$FreeBSD$"); #define DELAYBRANCH(x) ((int)(x) < 0) -extern uintptr_t dtrace_in_probe_addr; -extern int dtrace_in_probe; extern dtrace_id_t dtrace_probeid_error; extern int (*dtrace_invop_jump_addr)(struct trapframe *); diff --git a/sys/compat/cloudabi/cloudabi_clock.c b/sys/compat/cloudabi/cloudabi_clock.c index ed32cf6..b26d98e 100644 --- a/sys/compat/cloudabi/cloudabi_clock.c +++ b/sys/compat/cloudabi/cloudabi_clock.c @@ -31,8 +31,9 @@ __FBSDID("$FreeBSD$"); #include <sys/stdint.h> #include <sys/syscallsubr.h> +#include <contrib/cloudabi/cloudabi_types_common.h> + #include <compat/cloudabi/cloudabi_proto.h> -#include <compat/cloudabi/cloudabi_syscalldefs.h> #include <compat/cloudabi/cloudabi_util.h> /* Converts a CloudABI clock ID to a FreeBSD clock ID. */ diff --git a/sys/compat/cloudabi/cloudabi_errno.c b/sys/compat/cloudabi/cloudabi_errno.c index 5193cfc..38520b9 100644 --- a/sys/compat/cloudabi/cloudabi_errno.c +++ b/sys/compat/cloudabi/cloudabi_errno.c @@ -28,7 +28,8 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> -#include <compat/cloudabi/cloudabi_syscalldefs.h> +#include <contrib/cloudabi/cloudabi_types_common.h> + #include <compat/cloudabi/cloudabi_util.h> /* Converts a FreeBSD errno to a CloudABI errno. */ diff --git a/sys/compat/cloudabi/cloudabi_fd.c b/sys/compat/cloudabi/cloudabi_fd.c index 17177d2..c044adc 100644 --- a/sys/compat/cloudabi/cloudabi_fd.c +++ b/sys/compat/cloudabi/cloudabi_fd.c @@ -38,8 +38,9 @@ __FBSDID("$FreeBSD$"); #include <sys/unistd.h> #include <sys/vnode.h> +#include <contrib/cloudabi/cloudabi_types_common.h> + #include <compat/cloudabi/cloudabi_proto.h> -#include <compat/cloudabi/cloudabi_syscalldefs.h> #include <compat/cloudabi/cloudabi_util.h> /* Translation between CloudABI and Capsicum rights. */ diff --git a/sys/compat/cloudabi/cloudabi_file.c b/sys/compat/cloudabi/cloudabi_file.c index cdf0585..f48b754 100644 --- a/sys/compat/cloudabi/cloudabi_file.c +++ b/sys/compat/cloudabi/cloudabi_file.c @@ -39,8 +39,9 @@ __FBSDID("$FreeBSD$"); #include <sys/uio.h> #include <sys/vnode.h> +#include <contrib/cloudabi/cloudabi_types_common.h> + #include <compat/cloudabi/cloudabi_proto.h> -#include <compat/cloudabi/cloudabi_syscalldefs.h> #include <compat/cloudabi/cloudabi_util.h> #include <security/mac/mac_framework.h> @@ -185,8 +186,8 @@ cloudabi_sys_file_link(struct thread *td, return (error); } - error = kern_linkat(td, uap->fd1, uap->fd2, path1, path2, - UIO_SYSSPACE, (uap->fd1 & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? + error = kern_linkat(td, uap->fd1.fd, uap->fd2, path1, path2, + UIO_SYSSPACE, (uap->fd1.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) ? FOLLOW : NOFOLLOW); cloudabi_freestr(path1); cloudabi_freestr(path2); @@ -248,7 +249,7 @@ cloudabi_sys_file_open(struct thread *td, fflags |= O_SYNC; cap_rights_set(&rights, CAP_FSYNC); } - if ((uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) == 0) + if ((uap->dirfd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) == 0) fflags |= O_NOFOLLOW; if (write && (fflags & (O_APPEND | O_TRUNC)) == 0) cap_rights_set(&rights, CAP_SEEK); @@ -265,7 +266,7 @@ cloudabi_sys_file_open(struct thread *td, fdrop(fp, td); return (error); } - NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, uap->fd, + NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, uap->dirfd.fd, &rights, td); error = vn_open(&nd, &fflags, 0777 & ~td->td_proc->p_fd->fd_cmask, fp); cloudabi_freestr(path); @@ -657,8 +658,8 @@ cloudabi_sys_file_stat_get(struct thread *td, return (error); error = kern_statat(td, - (uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 0 : - AT_SYMLINK_NOFOLLOW, uap->fd, path, UIO_SYSSPACE, &sb, NULL); + (uap->fd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 0 : + AT_SYMLINK_NOFOLLOW, uap->fd.fd, path, UIO_SYSSPACE, &sb, NULL); cloudabi_freestr(path); if (error != 0) return (error); @@ -711,8 +712,8 @@ cloudabi_sys_file_stat_put(struct thread *td, return (error); convert_utimens_arguments(&fs, uap->flags, ts); - error = kern_utimensat(td, uap->fd, path, UIO_SYSSPACE, ts, - UIO_SYSSPACE, (uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? + error = kern_utimensat(td, uap->fd.fd, path, UIO_SYSSPACE, ts, + UIO_SYSSPACE, (uap->fd.flags & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) ? 0 : AT_SYMLINK_NOFOLLOW); cloudabi_freestr(path); return (error); @@ -751,7 +752,7 @@ cloudabi_sys_file_unlink(struct thread *td, if (error != 0) return (error); - if (uap->flag & CLOUDABI_UNLINK_REMOVEDIR) + if (uap->flags & CLOUDABI_UNLINK_REMOVEDIR) error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE); else error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0); diff --git a/sys/compat/cloudabi/cloudabi_futex.c b/sys/compat/cloudabi/cloudabi_futex.c index aec2f33..d12b331 100644 --- a/sys/compat/cloudabi/cloudabi_futex.c +++ b/sys/compat/cloudabi/cloudabi_futex.c @@ -37,8 +37,9 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/umtx.h> +#include <contrib/cloudabi/cloudabi_types_common.h> + #include <compat/cloudabi/cloudabi_proto.h> -#include <compat/cloudabi/cloudabi_syscalldefs.h> #include <compat/cloudabi/cloudabi_util.h> /* @@ -211,16 +212,16 @@ static int futex_user_cmpxchg(uint32_t *, uint32_t, uint32_t *, uint32_t); static int futex_address_create(struct futex_address *fa, struct thread *td, - const void *object, cloudabi_mflags_t scope) + const void *object, cloudabi_scope_t scope) { KASSERT(td == curthread, ("Can only create umtx keys for the current thread")); switch (scope) { - case CLOUDABI_MAP_PRIVATE: + case CLOUDABI_SCOPE_PRIVATE: return (umtx_key_get(object, TYPE_FUTEX, THREAD_SHARE, &fa->fa_key)); - case CLOUDABI_MAP_SHARED: + case CLOUDABI_SCOPE_SHARED: return (umtx_key_get(object, TYPE_FUTEX, AUTO_SHARE, &fa->fa_key)); default: @@ -258,7 +259,7 @@ futex_condvar_assert(const struct futex_condvar *fc) static int futex_condvar_lookup(struct thread *td, const cloudabi_condvar_t *address, - cloudabi_mflags_t scope, struct futex_condvar **fcret) + cloudabi_scope_t scope, struct futex_condvar **fcret) { struct futex_address fa_condvar; struct futex_condvar *fc; @@ -285,8 +286,8 @@ futex_condvar_lookup(struct thread *td, const cloudabi_condvar_t *address, static int futex_condvar_lookup_or_create(struct thread *td, - const cloudabi_condvar_t *condvar, cloudabi_mflags_t condvar_scope, - const cloudabi_lock_t *lock, cloudabi_mflags_t lock_scope, + const cloudabi_condvar_t *condvar, cloudabi_scope_t condvar_scope, + const cloudabi_lock_t *lock, cloudabi_scope_t lock_scope, struct futex_condvar **fcret) { struct futex_address fa_condvar, fa_lock; @@ -384,7 +385,7 @@ futex_lock_assert(const struct futex_lock *fl) static int futex_lock_lookup(struct thread *td, const cloudabi_lock_t *address, - cloudabi_mflags_t scope, struct futex_lock **flret) + cloudabi_scope_t scope, struct futex_lock **flret) { struct futex_address fa; int error; @@ -973,8 +974,8 @@ futex_user_cmpxchg(uint32_t *obj, uint32_t cmp, uint32_t *old, uint32_t new) int cloudabi_futex_condvar_wait(struct thread *td, cloudabi_condvar_t *condvar, - cloudabi_mflags_t condvar_scope, cloudabi_lock_t *lock, - cloudabi_mflags_t lock_scope, cloudabi_clockid_t clock_id, + cloudabi_scope_t condvar_scope, cloudabi_lock_t *lock, + cloudabi_scope_t lock_scope, cloudabi_clockid_t clock_id, cloudabi_timestamp_t timeout, cloudabi_timestamp_t precision) { struct futex_condvar *fc; @@ -1046,7 +1047,7 @@ cloudabi_futex_condvar_wait(struct thread *td, cloudabi_condvar_t *condvar, int cloudabi_futex_lock_rdlock(struct thread *td, cloudabi_lock_t *lock, - cloudabi_mflags_t scope, cloudabi_clockid_t clock_id, + cloudabi_scope_t scope, cloudabi_clockid_t clock_id, cloudabi_timestamp_t timeout, cloudabi_timestamp_t precision) { struct futex_lock *fl; @@ -1065,7 +1066,7 @@ cloudabi_futex_lock_rdlock(struct thread *td, cloudabi_lock_t *lock, int cloudabi_futex_lock_wrlock(struct thread *td, cloudabi_lock_t *lock, - cloudabi_mflags_t scope, cloudabi_clockid_t clock_id, + cloudabi_scope_t scope, cloudabi_clockid_t clock_id, cloudabi_timestamp_t timeout, cloudabi_timestamp_t precision) { struct futex_lock *fl; diff --git a/sys/compat/cloudabi/cloudabi_mem.c b/sys/compat/cloudabi/cloudabi_mem.c index 9d82673..34ee14a 100644 --- a/sys/compat/cloudabi/cloudabi_mem.c +++ b/sys/compat/cloudabi/cloudabi_mem.c @@ -30,8 +30,9 @@ __FBSDID("$FreeBSD$"); #include <sys/mman.h> #include <sys/sysproto.h> +#include <contrib/cloudabi/cloudabi_types_common.h> + #include <compat/cloudabi/cloudabi_proto.h> -#include <compat/cloudabi/cloudabi_syscalldefs.h> /* Converts CloudABI's memory protection flags to FreeBSD's. */ static int diff --git a/sys/compat/cloudabi/cloudabi_proc.c b/sys/compat/cloudabi/cloudabi_proc.c index 8d0b6e7..139af2c 100644 --- a/sys/compat/cloudabi/cloudabi_proc.c +++ b/sys/compat/cloudabi/cloudabi_proc.c @@ -38,8 +38,9 @@ __FBSDID("$FreeBSD$"); #include <sys/syscallsubr.h> #include <sys/unistd.h> +#include <contrib/cloudabi/cloudabi_types_common.h> + #include <compat/cloudabi/cloudabi_proto.h> -#include <compat/cloudabi/cloudabi_syscalldefs.h> int cloudabi_sys_proc_exec(struct thread *td, diff --git a/sys/compat/cloudabi/cloudabi_proto.h b/sys/compat/cloudabi/cloudabi_proto.h index e4baffd..95271fd 100644 --- a/sys/compat/cloudabi/cloudabi_proto.h +++ b/sys/compat/cloudabi/cloudabi_proto.h @@ -30,5 +30,7 @@ * calls. Unfortunately, we don't have a separate system call table for * those, so rely on the system call table from COMPAT_CLOUDABI64. */ -#include <compat/cloudabi64/cloudabi64_syscalldefs.h> + +#include <contrib/cloudabi/cloudabi64_types.h> + #include <compat/cloudabi64/cloudabi64_proto.h> diff --git a/sys/compat/cloudabi/cloudabi_sock.c b/sys/compat/cloudabi/cloudabi_sock.c index cdccdff..b66f0ab 100644 --- a/sys/compat/cloudabi/cloudabi_sock.c +++ b/sys/compat/cloudabi/cloudabi_sock.c @@ -43,8 +43,9 @@ __FBSDID("$FreeBSD$"); #include <netinet/in.h> +#include <contrib/cloudabi/cloudabi_types_common.h> + #include <compat/cloudabi/cloudabi_proto.h> -#include <compat/cloudabi/cloudabi_syscalldefs.h> #include <compat/cloudabi/cloudabi_util.h> /* Converts FreeBSD's struct sockaddr to CloudABI's cloudabi_sockaddr_t. */ @@ -117,12 +118,12 @@ cloudabi_sys_sock_accept(struct thread *td, if (uap->buf == NULL) { /* Only return the new file descriptor number. */ - return (kern_accept(td, uap->s, NULL, NULL, NULL)); + return (kern_accept(td, uap->sock, NULL, NULL, NULL)); } else { /* Also return properties of the new socket descriptor. */ sal = MAX(sizeof(struct sockaddr_in), sizeof(struct sockaddr_in6)); - error = kern_accept(td, uap->s, (void *)&sa, &sal, NULL); + error = kern_accept(td, uap->sock, (void *)&sa, &sal, NULL); if (error != 0) return (error); @@ -143,7 +144,7 @@ cloudabi_sys_sock_bind(struct thread *td, error = copyin_sockaddr_un(uap->path, uap->pathlen, &sun); if (error != 0) return (error); - return (kern_bindat(td, uap->fd, uap->s, (struct sockaddr *)&sun)); + return (kern_bindat(td, uap->fd, uap->sock, (struct sockaddr *)&sun)); } int @@ -156,7 +157,8 @@ cloudabi_sys_sock_connect(struct thread *td, error = copyin_sockaddr_un(uap->path, uap->pathlen, &sun); if (error != 0) return (error); - return (kern_connectat(td, uap->fd, uap->s, (struct sockaddr *)&sun)); + return (kern_connectat(td, uap->fd, uap->sock, + (struct sockaddr *)&sun)); } int @@ -164,7 +166,7 @@ cloudabi_sys_sock_listen(struct thread *td, struct cloudabi_sys_sock_listen_args *uap) { struct listen_args listen_args = { - .s = uap->s, + .s = uap->sock, .backlog = uap->backlog, }; @@ -176,7 +178,7 @@ cloudabi_sys_sock_shutdown(struct thread *td, struct cloudabi_sys_sock_shutdown_args *uap) { struct shutdown_args shutdown_args = { - .s = uap->fd, + .s = uap->sock, }; switch (uap->how) { @@ -207,7 +209,7 @@ cloudabi_sys_sock_stat_get(struct thread *td, struct socket *so; int error; - error = getsock_cap(td, uap->fd, cap_rights_init(&rights, + error = getsock_cap(td, uap->sock, cap_rights_init(&rights, CAP_GETSOCKOPT, CAP_GETPEERNAME, CAP_GETSOCKNAME), &fp, NULL); if (error != 0) return (error); @@ -243,7 +245,7 @@ cloudabi_sys_sock_stat_get(struct thread *td, /* Set ss_state. */ if ((so->so_options & SO_ACCEPTCONN) != 0) - ss.ss_state |= CLOUDABI_SOCKSTAT_ACCEPTCONN; + ss.ss_state |= CLOUDABI_SOCKSTATE_ACCEPTCONN; fdrop(fp, td); return (copyout(&ss, uap->buf, sizeof(ss))); diff --git a/sys/compat/cloudabi/cloudabi_thread.c b/sys/compat/cloudabi/cloudabi_thread.c index 8aee708..37dc794 100644 --- a/sys/compat/cloudabi/cloudabi_thread.c +++ b/sys/compat/cloudabi/cloudabi_thread.c @@ -31,8 +31,9 @@ __FBSDID("$FreeBSD$"); #include <sys/sched.h> #include <sys/syscallsubr.h> +#include <contrib/cloudabi/cloudabi_types_common.h> + #include <compat/cloudabi/cloudabi_proto.h> -#include <compat/cloudabi/cloudabi_syscalldefs.h> int cloudabi_sys_thread_exit(struct thread *td, diff --git a/sys/compat/cloudabi/cloudabi_util.h b/sys/compat/cloudabi/cloudabi_util.h index 10da229..c0a02aa 100644 --- a/sys/compat/cloudabi/cloudabi_util.h +++ b/sys/compat/cloudabi/cloudabi_util.h @@ -30,7 +30,7 @@ #include <sys/socket.h> -#include <compat/cloudabi/cloudabi_syscalldefs.h> +#include <contrib/cloudabi/cloudabi_types_common.h> struct file; struct thread; @@ -67,13 +67,13 @@ int cloudabi_convert_timespec(const struct timespec *, cloudabi_timestamp_t *); * sleep on a lock or condition variable. */ int cloudabi_futex_condvar_wait(struct thread *, cloudabi_condvar_t *, - cloudabi_mflags_t, cloudabi_lock_t *, cloudabi_mflags_t, cloudabi_clockid_t, + cloudabi_scope_t, cloudabi_lock_t *, cloudabi_scope_t, cloudabi_clockid_t, cloudabi_timestamp_t, cloudabi_timestamp_t); int cloudabi_futex_lock_rdlock(struct thread *, cloudabi_lock_t *, - cloudabi_mflags_t, cloudabi_clockid_t, cloudabi_timestamp_t, + cloudabi_scope_t, cloudabi_clockid_t, cloudabi_timestamp_t, cloudabi_timestamp_t); int cloudabi_futex_lock_wrlock(struct thread *, cloudabi_lock_t *, - cloudabi_mflags_t, cloudabi_clockid_t, cloudabi_timestamp_t, + cloudabi_scope_t, cloudabi_clockid_t, cloudabi_timestamp_t, cloudabi_timestamp_t); #endif diff --git a/sys/compat/cloudabi64/Makefile b/sys/compat/cloudabi64/Makefile index 3fbef57..83d27a3 100644 --- a/sys/compat/cloudabi64/Makefile +++ b/sys/compat/cloudabi64/Makefile @@ -8,5 +8,7 @@ sysent: cloudabi64_sysent.c cloudabi64_syscall.h cloudabi64_proto.h \ cloudabi64_sysent.c cloudabi64_syscall.h cloudabi64_proto.h \ cloudabi64_syscalls.c cloudabi64_systrace_args.c: \ - ../../kern/makesyscalls.sh syscalls.master syscalls.conf - sh ../../kern/makesyscalls.sh syscalls.master syscalls.conf + ../../kern/makesyscalls.sh ../../contrib/cloudabi/syscalls.master \ + syscalls.conf + sh ../../kern/makesyscalls.sh ../../contrib/cloudabi/syscalls.master \ + syscalls.conf diff --git a/sys/compat/cloudabi64/cloudabi64_fd.c b/sys/compat/cloudabi64/cloudabi64_fd.c index b2fd67d..7d0c69a 100644 --- a/sys/compat/cloudabi64/cloudabi64_fd.c +++ b/sys/compat/cloudabi64/cloudabi64_fd.c @@ -34,7 +34,8 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/uio.h> -#include <compat/cloudabi64/cloudabi64_syscalldefs.h> +#include <contrib/cloudabi/cloudabi64_types.h> + #include <compat/cloudabi64/cloudabi64_proto.h> /* Copies in 64-bit iovec structures from userspace. */ diff --git a/sys/compat/cloudabi64/cloudabi64_module.c b/sys/compat/cloudabi64/cloudabi64_module.c index ca8ecf4..de890bc 100644 --- a/sys/compat/cloudabi64/cloudabi64_module.c +++ b/sys/compat/cloudabi64/cloudabi64_module.c @@ -36,7 +36,8 @@ __FBSDID("$FreeBSD$"); #include <sys/sysent.h> #include <sys/systm.h> -#include <compat/cloudabi64/cloudabi64_syscalldefs.h> +#include <contrib/cloudabi/cloudabi64_types.h> + #include <compat/cloudabi64/cloudabi64_util.h> register_t * @@ -98,6 +99,7 @@ cloudabi64_fixup(register_t **stack_base, struct image_params *imgp) #define PTR(type, ptr) { .a_type = (type), .a_ptr = (uintptr_t)(ptr) } PTR(CLOUDABI_AT_ARGDATA, argdata), VAL(CLOUDABI_AT_ARGDATALEN, argdatalen), + VAL(CLOUDABI_AT_BASE, args->base), PTR(CLOUDABI_AT_CANARY, canary), VAL(CLOUDABI_AT_CANARYLEN, sizeof(canarybuf)), VAL(CLOUDABI_AT_NCPUS, mp_ncpus), diff --git a/sys/compat/cloudabi64/cloudabi64_poll.c b/sys/compat/cloudabi64/cloudabi64_poll.c index c8f1811..e44d69f 100644 --- a/sys/compat/cloudabi64/cloudabi64_poll.c +++ b/sys/compat/cloudabi64/cloudabi64_poll.c @@ -30,9 +30,10 @@ __FBSDID("$FreeBSD$"); #include <sys/proc.h> #include <sys/syscallsubr.h> +#include <contrib/cloudabi/cloudabi64_types.h> + #include <compat/cloudabi/cloudabi_util.h> -#include <compat/cloudabi64/cloudabi64_syscalldefs.h> #include <compat/cloudabi64/cloudabi64_proto.h> /* Converts a FreeBSD signal number to a CloudABI signal number. */ @@ -248,7 +249,7 @@ cloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap) * Bandaid to support CloudABI futex constructs that are not * implemented through FreeBSD's kqueue(). */ - if (uap->nevents == 1) { + if (uap->nsubscriptions == 1) { cloudabi64_subscription_t sub; cloudabi64_event_t ev = {}; int error; @@ -291,7 +292,7 @@ cloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap) td->td_retval[0] = 1; return (copyout(&ev, uap->out, sizeof(ev))); } - } else if (uap->nevents == 2) { + } else if (uap->nsubscriptions == 2) { cloudabi64_subscription_t sub[2]; cloudabi64_event_t ev[2] = {}; int error; @@ -365,7 +366,7 @@ cloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap) } } - return (kern_kevent_anonymous(td, uap->nevents, ©ops)); + return (kern_kevent_anonymous(td, uap->nsubscriptions, ©ops)); } int diff --git a/sys/compat/cloudabi64/cloudabi64_proto.h b/sys/compat/cloudabi64/cloudabi64_proto.h index 8c65fe3..79a0f60 100644 --- a/sys/compat/cloudabi64/cloudabi64_proto.h +++ b/sys/compat/cloudabi64/cloudabi64_proto.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/compat/cloudabi64/syscalls.master 286318 2015-08-05 13:09:46Z ed + * created from FreeBSD: head/sys/contrib/cloudabi/syscalls.master 297468 2016-03-31 18:50:06Z ed */ #ifndef _CLOUDABI64_SYSPROTO_H_ @@ -43,7 +43,7 @@ struct cloudabi_sys_clock_time_get_args { }; struct cloudabi_sys_condvar_signal_args { char condvar_l_[PADL_(cloudabi_condvar_t *)]; cloudabi_condvar_t * condvar; char condvar_r_[PADR_(cloudabi_condvar_t *)]; - char scope_l_[PADL_(cloudabi_mflags_t)]; cloudabi_mflags_t scope; char scope_r_[PADR_(cloudabi_mflags_t)]; + char scope_l_[PADL_(cloudabi_scope_t)]; cloudabi_scope_t scope; char scope_r_[PADR_(cloudabi_scope_t)]; char nwaiters_l_[PADL_(cloudabi_nthreads_t)]; cloudabi_nthreads_t nwaiters; char nwaiters_r_[PADR_(cloudabi_nthreads_t)]; }; struct cloudabi_sys_fd_close_args { @@ -64,19 +64,19 @@ struct cloudabi_sys_fd_dup_args { struct cloudabi64_sys_fd_pread_args { char fd_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t fd; char fd_r_[PADR_(cloudabi_fd_t)]; char iov_l_[PADL_(const cloudabi64_iovec_t *)]; const cloudabi64_iovec_t * iov; char iov_r_[PADR_(const cloudabi64_iovec_t *)]; - char iovcnt_l_[PADL_(cloudabi64_size_t)]; cloudabi64_size_t iovcnt; char iovcnt_r_[PADR_(cloudabi64_size_t)]; + char iovcnt_l_[PADL_(size_t)]; size_t iovcnt; char iovcnt_r_[PADR_(size_t)]; char offset_l_[PADL_(cloudabi_filesize_t)]; cloudabi_filesize_t offset; char offset_r_[PADR_(cloudabi_filesize_t)]; }; struct cloudabi64_sys_fd_pwrite_args { char fd_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t fd; char fd_r_[PADR_(cloudabi_fd_t)]; char iov_l_[PADL_(const cloudabi64_ciovec_t *)]; const cloudabi64_ciovec_t * iov; char iov_r_[PADR_(const cloudabi64_ciovec_t *)]; - char iovcnt_l_[PADL_(cloudabi64_size_t)]; cloudabi64_size_t iovcnt; char iovcnt_r_[PADR_(cloudabi64_size_t)]; + char iovcnt_l_[PADL_(size_t)]; size_t iovcnt; char iovcnt_r_[PADR_(size_t)]; char offset_l_[PADL_(cloudabi_filesize_t)]; cloudabi_filesize_t offset; char offset_r_[PADR_(cloudabi_filesize_t)]; }; struct cloudabi64_sys_fd_read_args { char fd_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t fd; char fd_r_[PADR_(cloudabi_fd_t)]; char iov_l_[PADL_(const cloudabi64_iovec_t *)]; const cloudabi64_iovec_t * iov; char iov_r_[PADR_(const cloudabi64_iovec_t *)]; - char iovcnt_l_[PADL_(cloudabi64_size_t)]; cloudabi64_size_t iovcnt; char iovcnt_r_[PADR_(cloudabi64_size_t)]; + char iovcnt_l_[PADL_(size_t)]; size_t iovcnt; char iovcnt_r_[PADR_(size_t)]; }; struct cloudabi_sys_fd_replace_args { char from_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t from; char from_r_[PADR_(cloudabi_fd_t)]; @@ -102,7 +102,7 @@ struct cloudabi_sys_fd_sync_args { struct cloudabi64_sys_fd_write_args { char fd_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t fd; char fd_r_[PADR_(cloudabi_fd_t)]; char iov_l_[PADL_(const cloudabi64_ciovec_t *)]; const cloudabi64_ciovec_t * iov; char iov_r_[PADR_(const cloudabi64_ciovec_t *)]; - char iovcnt_l_[PADL_(cloudabi64_size_t)]; cloudabi64_size_t iovcnt; char iovcnt_r_[PADR_(cloudabi64_size_t)]; + char iovcnt_l_[PADL_(size_t)]; size_t iovcnt; char iovcnt_r_[PADR_(size_t)]; }; struct cloudabi_sys_file_advise_args { char fd_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t fd; char fd_r_[PADR_(cloudabi_fd_t)]; @@ -130,7 +130,7 @@ struct cloudabi_sys_file_link_args { char path2len_l_[PADL_(size_t)]; size_t path2len; char path2len_r_[PADR_(size_t)]; }; struct cloudabi_sys_file_open_args { - char fd_l_[PADL_(cloudabi_lookup_t)]; cloudabi_lookup_t fd; char fd_r_[PADR_(cloudabi_lookup_t)]; + char dirfd_l_[PADL_(cloudabi_lookup_t)]; cloudabi_lookup_t dirfd; char dirfd_r_[PADR_(cloudabi_lookup_t)]; char path_l_[PADL_(const char *)]; const char * path; char path_r_[PADR_(const char *)]; char pathlen_l_[PADL_(size_t)]; size_t pathlen; char pathlen_r_[PADR_(size_t)]; char oflags_l_[PADL_(cloudabi_oflags_t)]; cloudabi_oflags_t oflags; char oflags_r_[PADR_(cloudabi_oflags_t)]; @@ -146,7 +146,7 @@ struct cloudabi_sys_file_readlink_args { char fd_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t fd; char fd_r_[PADR_(cloudabi_fd_t)]; char path_l_[PADL_(const char *)]; const char * path; char path_r_[PADR_(const char *)]; char pathlen_l_[PADL_(size_t)]; size_t pathlen; char pathlen_r_[PADR_(size_t)]; - char buf_l_[PADL_(void *)]; void * buf; char buf_r_[PADR_(void *)]; + char buf_l_[PADL_(char *)]; char * buf; char buf_r_[PADR_(char *)]; char bufsize_l_[PADL_(size_t)]; size_t bufsize; char bufsize_r_[PADR_(size_t)]; }; struct cloudabi_sys_file_rename_args { @@ -190,11 +190,11 @@ struct cloudabi_sys_file_unlink_args { char fd_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t fd; char fd_r_[PADR_(cloudabi_fd_t)]; char path_l_[PADL_(const char *)]; const char * path; char path_r_[PADR_(const char *)]; char pathlen_l_[PADL_(size_t)]; size_t pathlen; char pathlen_r_[PADR_(size_t)]; - char flag_l_[PADL_(cloudabi_ulflags_t)]; cloudabi_ulflags_t flag; char flag_r_[PADR_(cloudabi_ulflags_t)]; + char flags_l_[PADL_(cloudabi_ulflags_t)]; cloudabi_ulflags_t flags; char flags_r_[PADR_(cloudabi_ulflags_t)]; }; struct cloudabi_sys_lock_unlock_args { char lock_l_[PADL_(cloudabi_lock_t *)]; cloudabi_lock_t * lock; char lock_r_[PADR_(cloudabi_lock_t *)]; - char scope_l_[PADL_(cloudabi_mflags_t)]; cloudabi_mflags_t scope; char scope_r_[PADR_(cloudabi_mflags_t)]; + char scope_l_[PADL_(cloudabi_scope_t)]; cloudabi_scope_t scope; char scope_r_[PADR_(cloudabi_scope_t)]; }; struct cloudabi_sys_mem_advise_args { char addr_l_[PADL_(void *)]; void * addr; char addr_r_[PADR_(void *)]; @@ -234,7 +234,7 @@ struct cloudabi_sys_mem_unmap_args { struct cloudabi64_sys_poll_args { char in_l_[PADL_(const cloudabi64_subscription_t *)]; const cloudabi64_subscription_t * in; char in_r_[PADR_(const cloudabi64_subscription_t *)]; char out_l_[PADL_(cloudabi64_event_t *)]; cloudabi64_event_t * out; char out_r_[PADR_(cloudabi64_event_t *)]; - char nevents_l_[PADL_(cloudabi64_size_t)]; cloudabi64_size_t nevents; char nevents_r_[PADR_(cloudabi64_size_t)]; + char nsubscriptions_l_[PADL_(size_t)]; size_t nsubscriptions; char nsubscriptions_r_[PADR_(size_t)]; }; struct cloudabi_sys_proc_exec_args { char fd_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t fd; char fd_r_[PADR_(cloudabi_fd_t)]; @@ -257,41 +257,41 @@ struct cloudabi_sys_random_get_args { char nbyte_l_[PADL_(size_t)]; size_t nbyte; char nbyte_r_[PADR_(size_t)]; }; struct cloudabi_sys_sock_accept_args { - char s_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t s; char s_r_[PADR_(cloudabi_fd_t)]; + char sock_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t sock; char sock_r_[PADR_(cloudabi_fd_t)]; char buf_l_[PADL_(cloudabi_sockstat_t *)]; cloudabi_sockstat_t * buf; char buf_r_[PADR_(cloudabi_sockstat_t *)]; }; struct cloudabi_sys_sock_bind_args { - char s_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t s; char s_r_[PADR_(cloudabi_fd_t)]; + char sock_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t sock; char sock_r_[PADR_(cloudabi_fd_t)]; char fd_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t fd; char fd_r_[PADR_(cloudabi_fd_t)]; char path_l_[PADL_(const char *)]; const char * path; char path_r_[PADR_(const char *)]; char pathlen_l_[PADL_(size_t)]; size_t pathlen; char pathlen_r_[PADR_(size_t)]; }; struct cloudabi_sys_sock_connect_args { - char s_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t s; char s_r_[PADR_(cloudabi_fd_t)]; + char sock_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t sock; char sock_r_[PADR_(cloudabi_fd_t)]; char fd_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t fd; char fd_r_[PADR_(cloudabi_fd_t)]; char path_l_[PADL_(const char *)]; const char * path; char path_r_[PADR_(const char *)]; char pathlen_l_[PADL_(size_t)]; size_t pathlen; char pathlen_r_[PADR_(size_t)]; }; struct cloudabi_sys_sock_listen_args { - char s_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t s; char s_r_[PADR_(cloudabi_fd_t)]; + char sock_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t sock; char sock_r_[PADR_(cloudabi_fd_t)]; char backlog_l_[PADL_(cloudabi_backlog_t)]; cloudabi_backlog_t backlog; char backlog_r_[PADR_(cloudabi_backlog_t)]; }; struct cloudabi64_sys_sock_recv_args { - char s_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t s; char s_r_[PADR_(cloudabi_fd_t)]; + char sock_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t sock; char sock_r_[PADR_(cloudabi_fd_t)]; char in_l_[PADL_(const cloudabi64_recv_in_t *)]; const cloudabi64_recv_in_t * in; char in_r_[PADR_(const cloudabi64_recv_in_t *)]; char out_l_[PADL_(cloudabi64_recv_out_t *)]; cloudabi64_recv_out_t * out; char out_r_[PADR_(cloudabi64_recv_out_t *)]; }; struct cloudabi64_sys_sock_send_args { - char s_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t s; char s_r_[PADR_(cloudabi_fd_t)]; + char sock_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t sock; char sock_r_[PADR_(cloudabi_fd_t)]; char in_l_[PADL_(const cloudabi64_send_in_t *)]; const cloudabi64_send_in_t * in; char in_r_[PADR_(const cloudabi64_send_in_t *)]; char out_l_[PADL_(cloudabi64_send_out_t *)]; cloudabi64_send_out_t * out; char out_r_[PADR_(cloudabi64_send_out_t *)]; }; struct cloudabi_sys_sock_shutdown_args { - char fd_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t fd; char fd_r_[PADR_(cloudabi_fd_t)]; + char sock_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t sock; char sock_r_[PADR_(cloudabi_fd_t)]; char how_l_[PADL_(cloudabi_sdflags_t)]; cloudabi_sdflags_t how; char how_r_[PADR_(cloudabi_sdflags_t)]; }; struct cloudabi_sys_sock_stat_get_args { - char fd_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t fd; char fd_r_[PADR_(cloudabi_fd_t)]; + char sock_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t sock; char sock_r_[PADR_(cloudabi_fd_t)]; char buf_l_[PADL_(cloudabi_sockstat_t *)]; cloudabi_sockstat_t * buf; char buf_r_[PADR_(cloudabi_sockstat_t *)]; char flags_l_[PADL_(cloudabi_ssflags_t)]; cloudabi_ssflags_t flags; char flags_r_[PADR_(cloudabi_ssflags_t)]; }; @@ -300,7 +300,7 @@ struct cloudabi64_sys_thread_create_args { }; struct cloudabi_sys_thread_exit_args { char lock_l_[PADL_(cloudabi_lock_t *)]; cloudabi_lock_t * lock; char lock_r_[PADR_(cloudabi_lock_t *)]; - char scope_l_[PADL_(cloudabi_mflags_t)]; cloudabi_mflags_t scope; char scope_r_[PADR_(cloudabi_mflags_t)]; + char scope_l_[PADL_(cloudabi_scope_t)]; cloudabi_scope_t scope; char scope_r_[PADR_(cloudabi_scope_t)]; }; struct cloudabi_sys_thread_tcb_set_args { char tcb_l_[PADL_(void *)]; void * tcb; char tcb_r_[PADR_(void *)]; @@ -311,9 +311,9 @@ struct cloudabi_sys_thread_yield_args { struct cloudabi64_sys_poll_fd_args { char fd_l_[PADL_(cloudabi_fd_t)]; cloudabi_fd_t fd; char fd_r_[PADR_(cloudabi_fd_t)]; char in_l_[PADL_(const cloudabi64_subscription_t *)]; const cloudabi64_subscription_t * in; char in_r_[PADR_(const cloudabi64_subscription_t *)]; - char nin_l_[PADL_(cloudabi64_size_t)]; cloudabi64_size_t nin; char nin_r_[PADR_(cloudabi64_size_t)]; + char nin_l_[PADL_(size_t)]; size_t nin; char nin_r_[PADR_(size_t)]; char out_l_[PADL_(cloudabi64_event_t *)]; cloudabi64_event_t * out; char out_r_[PADR_(cloudabi64_event_t *)]; - char nout_l_[PADL_(cloudabi64_size_t)]; cloudabi64_size_t nout; char nout_r_[PADR_(cloudabi64_size_t)]; + char nout_l_[PADL_(size_t)]; size_t nout; char nout_r_[PADR_(size_t)]; char timeout_l_[PADL_(const cloudabi64_subscription_t *)]; const cloudabi64_subscription_t * timeout; char timeout_r_[PADR_(const cloudabi64_subscription_t *)]; }; int cloudabi_sys_clock_res_get(struct thread *, struct cloudabi_sys_clock_res_get_args *); diff --git a/sys/compat/cloudabi64/cloudabi64_sock.c b/sys/compat/cloudabi64/cloudabi64_sock.c index c612a32..e6b9c94 100644 --- a/sys/compat/cloudabi64/cloudabi64_sock.c +++ b/sys/compat/cloudabi64/cloudabi64_sock.c @@ -35,9 +35,10 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/uio.h> +#include <contrib/cloudabi/cloudabi64_types.h> + #include <compat/cloudabi/cloudabi_util.h> -#include <compat/cloudabi64/cloudabi64_syscalldefs.h> #include <compat/cloudabi64/cloudabi64_proto.h> static MALLOC_DEFINE(M_SOCKET, "socket", "CloudABI socket"); @@ -82,7 +83,7 @@ cloudabi64_sys_sock_recv(struct thread *td, msghdr.msg_flags |= MSG_WAITALL; /* TODO(ed): Add file descriptor passing. */ - error = kern_recvit(td, uap->s, &msghdr, UIO_SYSSPACE, NULL); + error = kern_recvit(td, uap->sock, &msghdr, UIO_SYSSPACE, NULL); free(msghdr.msg_iov, M_SOCKET); if (error != 0) return (error); @@ -132,7 +133,7 @@ cloudabi64_sys_sock_send(struct thread *td, flags |= MSG_EOR; /* TODO(ed): Add file descriptor passing. */ - error = kern_sendit(td, uap->s, &msghdr, flags, NULL, UIO_USERSPACE); + error = kern_sendit(td, uap->sock, &msghdr, flags, NULL, UIO_USERSPACE); free(msghdr.msg_iov, M_SOCKET); if (error != 0) return (error); diff --git a/sys/compat/cloudabi64/cloudabi64_syscall.h b/sys/compat/cloudabi64/cloudabi64_syscall.h index 40c017a..b5694bf 100644 --- a/sys/compat/cloudabi64/cloudabi64_syscall.h +++ b/sys/compat/cloudabi64/cloudabi64_syscall.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/compat/cloudabi64/syscalls.master 286318 2015-08-05 13:09:46Z ed + * created from FreeBSD: head/sys/contrib/cloudabi/syscalls.master 297468 2016-03-31 18:50:06Z ed */ #define CLOUDABI64_SYS_cloudabi_sys_clock_res_get 0 diff --git a/sys/compat/cloudabi64/cloudabi64_syscalls.c b/sys/compat/cloudabi64/cloudabi64_syscalls.c index 5c0732b..03407ff 100644 --- a/sys/compat/cloudabi64/cloudabi64_syscalls.c +++ b/sys/compat/cloudabi64/cloudabi64_syscalls.c @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/compat/cloudabi64/syscalls.master 286318 2015-08-05 13:09:46Z ed + * created from FreeBSD: head/sys/contrib/cloudabi/syscalls.master 297468 2016-03-31 18:50:06Z ed */ const char *cloudabi64_syscallnames[] = { diff --git a/sys/compat/cloudabi64/cloudabi64_sysent.c b/sys/compat/cloudabi64/cloudabi64_sysent.c index 2ed5042..50f4a65 100644 --- a/sys/compat/cloudabi64/cloudabi64_sysent.c +++ b/sys/compat/cloudabi64/cloudabi64_sysent.c @@ -3,12 +3,12 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/compat/cloudabi64/syscalls.master 286318 2015-08-05 13:09:46Z ed + * created from FreeBSD: head/sys/contrib/cloudabi/syscalls.master 297468 2016-03-31 18:50:06Z ed */ #include <sys/sysent.h> #include <sys/sysproto.h> -#include <compat/cloudabi64/cloudabi64_syscalldefs.h> +#include <contrib/cloudabi/cloudabi64_types.h> #include <compat/cloudabi64/cloudabi64_proto.h> #define AS(name) (sizeof(struct name) / sizeof(register_t)) diff --git a/sys/compat/cloudabi64/cloudabi64_systrace_args.c b/sys/compat/cloudabi64/cloudabi64_systrace_args.c index b3176aa..9429e8e 100644 --- a/sys/compat/cloudabi64/cloudabi64_systrace_args.c +++ b/sys/compat/cloudabi64/cloudabi64_systrace_args.c @@ -30,7 +30,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) case 2: { struct cloudabi_sys_condvar_signal_args *p = params; uarg[0] = (intptr_t) p->condvar; /* cloudabi_condvar_t * */ - iarg[1] = p->scope; /* cloudabi_mflags_t */ + iarg[1] = p->scope; /* cloudabi_scope_t */ iarg[2] = p->nwaiters; /* cloudabi_nthreads_t */ *n_args = 3; break; @@ -75,7 +75,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) struct cloudabi64_sys_fd_pread_args *p = params; iarg[0] = p->fd; /* cloudabi_fd_t */ uarg[1] = (intptr_t) p->iov; /* const cloudabi64_iovec_t * */ - iarg[2] = p->iovcnt; /* cloudabi64_size_t */ + uarg[2] = p->iovcnt; /* size_t */ iarg[3] = p->offset; /* cloudabi_filesize_t */ *n_args = 4; break; @@ -85,7 +85,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) struct cloudabi64_sys_fd_pwrite_args *p = params; iarg[0] = p->fd; /* cloudabi_fd_t */ uarg[1] = (intptr_t) p->iov; /* const cloudabi64_ciovec_t * */ - iarg[2] = p->iovcnt; /* cloudabi64_size_t */ + uarg[2] = p->iovcnt; /* size_t */ iarg[3] = p->offset; /* cloudabi_filesize_t */ *n_args = 4; break; @@ -95,7 +95,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) struct cloudabi64_sys_fd_read_args *p = params; iarg[0] = p->fd; /* cloudabi_fd_t */ uarg[1] = (intptr_t) p->iov; /* const cloudabi64_iovec_t * */ - iarg[2] = p->iovcnt; /* cloudabi64_size_t */ + uarg[2] = p->iovcnt; /* size_t */ *n_args = 3; break; } @@ -145,7 +145,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) struct cloudabi64_sys_fd_write_args *p = params; iarg[0] = p->fd; /* cloudabi_fd_t */ uarg[1] = (intptr_t) p->iov; /* const cloudabi64_ciovec_t * */ - iarg[2] = p->iovcnt; /* cloudabi64_size_t */ + uarg[2] = p->iovcnt; /* size_t */ *n_args = 3; break; } @@ -193,7 +193,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) /* cloudabi_sys_file_open */ case 21: { struct cloudabi_sys_file_open_args *p = params; - iarg[0] = p->fd; /* cloudabi_lookup_t */ + iarg[0] = p->dirfd; /* cloudabi_lookup_t */ uarg[1] = (intptr_t) p->path; /* const char * */ uarg[2] = p->pathlen; /* size_t */ iarg[3] = p->oflags; /* cloudabi_oflags_t */ @@ -217,7 +217,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) iarg[0] = p->fd; /* cloudabi_fd_t */ uarg[1] = (intptr_t) p->path; /* const char * */ uarg[2] = p->pathlen; /* size_t */ - uarg[3] = (intptr_t) p->buf; /* void * */ + uarg[3] = (intptr_t) p->buf; /* char * */ uarg[4] = p->bufsize; /* size_t */ *n_args = 5; break; @@ -289,7 +289,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) iarg[0] = p->fd; /* cloudabi_fd_t */ uarg[1] = (intptr_t) p->path; /* const char * */ uarg[2] = p->pathlen; /* size_t */ - iarg[3] = p->flag; /* cloudabi_ulflags_t */ + iarg[3] = p->flags; /* cloudabi_ulflags_t */ *n_args = 4; break; } @@ -297,7 +297,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) case 31: { struct cloudabi_sys_lock_unlock_args *p = params; uarg[0] = (intptr_t) p->lock; /* cloudabi_lock_t * */ - iarg[1] = p->scope; /* cloudabi_mflags_t */ + iarg[1] = p->scope; /* cloudabi_scope_t */ *n_args = 2; break; } @@ -369,7 +369,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) struct cloudabi64_sys_poll_args *p = params; uarg[0] = (intptr_t) p->in; /* const cloudabi64_subscription_t * */ uarg[1] = (intptr_t) p->out; /* cloudabi64_event_t * */ - iarg[2] = p->nevents; /* cloudabi64_size_t */ + uarg[2] = p->nsubscriptions; /* size_t */ *n_args = 3; break; } @@ -414,7 +414,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) /* cloudabi_sys_sock_accept */ case 45: { struct cloudabi_sys_sock_accept_args *p = params; - iarg[0] = p->s; /* cloudabi_fd_t */ + iarg[0] = p->sock; /* cloudabi_fd_t */ uarg[1] = (intptr_t) p->buf; /* cloudabi_sockstat_t * */ *n_args = 2; break; @@ -422,7 +422,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) /* cloudabi_sys_sock_bind */ case 46: { struct cloudabi_sys_sock_bind_args *p = params; - iarg[0] = p->s; /* cloudabi_fd_t */ + iarg[0] = p->sock; /* cloudabi_fd_t */ iarg[1] = p->fd; /* cloudabi_fd_t */ uarg[2] = (intptr_t) p->path; /* const char * */ uarg[3] = p->pathlen; /* size_t */ @@ -432,7 +432,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) /* cloudabi_sys_sock_connect */ case 47: { struct cloudabi_sys_sock_connect_args *p = params; - iarg[0] = p->s; /* cloudabi_fd_t */ + iarg[0] = p->sock; /* cloudabi_fd_t */ iarg[1] = p->fd; /* cloudabi_fd_t */ uarg[2] = (intptr_t) p->path; /* const char * */ uarg[3] = p->pathlen; /* size_t */ @@ -442,7 +442,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) /* cloudabi_sys_sock_listen */ case 48: { struct cloudabi_sys_sock_listen_args *p = params; - iarg[0] = p->s; /* cloudabi_fd_t */ + iarg[0] = p->sock; /* cloudabi_fd_t */ iarg[1] = p->backlog; /* cloudabi_backlog_t */ *n_args = 2; break; @@ -450,7 +450,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) /* cloudabi64_sys_sock_recv */ case 49: { struct cloudabi64_sys_sock_recv_args *p = params; - iarg[0] = p->s; /* cloudabi_fd_t */ + iarg[0] = p->sock; /* cloudabi_fd_t */ uarg[1] = (intptr_t) p->in; /* const cloudabi64_recv_in_t * */ uarg[2] = (intptr_t) p->out; /* cloudabi64_recv_out_t * */ *n_args = 3; @@ -459,7 +459,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) /* cloudabi64_sys_sock_send */ case 50: { struct cloudabi64_sys_sock_send_args *p = params; - iarg[0] = p->s; /* cloudabi_fd_t */ + iarg[0] = p->sock; /* cloudabi_fd_t */ uarg[1] = (intptr_t) p->in; /* const cloudabi64_send_in_t * */ uarg[2] = (intptr_t) p->out; /* cloudabi64_send_out_t * */ *n_args = 3; @@ -468,7 +468,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) /* cloudabi_sys_sock_shutdown */ case 51: { struct cloudabi_sys_sock_shutdown_args *p = params; - iarg[0] = p->fd; /* cloudabi_fd_t */ + iarg[0] = p->sock; /* cloudabi_fd_t */ iarg[1] = p->how; /* cloudabi_sdflags_t */ *n_args = 2; break; @@ -476,7 +476,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) /* cloudabi_sys_sock_stat_get */ case 52: { struct cloudabi_sys_sock_stat_get_args *p = params; - iarg[0] = p->fd; /* cloudabi_fd_t */ + iarg[0] = p->sock; /* cloudabi_fd_t */ uarg[1] = (intptr_t) p->buf; /* cloudabi_sockstat_t * */ iarg[2] = p->flags; /* cloudabi_ssflags_t */ *n_args = 3; @@ -493,7 +493,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) case 54: { struct cloudabi_sys_thread_exit_args *p = params; uarg[0] = (intptr_t) p->lock; /* cloudabi_lock_t * */ - iarg[1] = p->scope; /* cloudabi_mflags_t */ + iarg[1] = p->scope; /* cloudabi_scope_t */ *n_args = 2; break; } @@ -514,9 +514,9 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) struct cloudabi64_sys_poll_fd_args *p = params; iarg[0] = p->fd; /* cloudabi_fd_t */ uarg[1] = (intptr_t) p->in; /* const cloudabi64_subscription_t * */ - iarg[2] = p->nin; /* cloudabi64_size_t */ + uarg[2] = p->nin; /* size_t */ uarg[3] = (intptr_t) p->out; /* cloudabi64_event_t * */ - iarg[4] = p->nout; /* cloudabi64_size_t */ + uarg[4] = p->nout; /* size_t */ uarg[5] = (intptr_t) p->timeout; /* const cloudabi64_subscription_t * */ *n_args = 6; break; @@ -561,7 +561,7 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) p = "cloudabi_condvar_t *"; break; case 1: - p = "cloudabi_mflags_t"; + p = "cloudabi_scope_t"; break; case 2: p = "cloudabi_nthreads_t"; @@ -630,7 +630,7 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) p = "const cloudabi64_iovec_t *"; break; case 2: - p = "cloudabi64_size_t"; + p = "size_t"; break; case 3: p = "cloudabi_filesize_t"; @@ -649,7 +649,7 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) p = "const cloudabi64_ciovec_t *"; break; case 2: - p = "cloudabi64_size_t"; + p = "size_t"; break; case 3: p = "cloudabi_filesize_t"; @@ -668,7 +668,7 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) p = "const cloudabi64_iovec_t *"; break; case 2: - p = "cloudabi64_size_t"; + p = "size_t"; break; default: break; @@ -752,7 +752,7 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) p = "const cloudabi64_ciovec_t *"; break; case 2: - p = "cloudabi64_size_t"; + p = "size_t"; break; default: break; @@ -891,7 +891,7 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) p = "size_t"; break; case 3: - p = "void *"; + p = "char *"; break; case 4: p = "size_t"; @@ -1043,7 +1043,7 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) p = "cloudabi_lock_t *"; break; case 1: - p = "cloudabi_mflags_t"; + p = "cloudabi_scope_t"; break; default: break; @@ -1171,7 +1171,7 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) p = "cloudabi64_event_t *"; break; case 2: - p = "cloudabi64_size_t"; + p = "size_t"; break; default: break; @@ -1377,7 +1377,7 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) p = "cloudabi_lock_t *"; break; case 1: - p = "cloudabi_mflags_t"; + p = "cloudabi_scope_t"; break; default: break; @@ -1406,13 +1406,13 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) p = "const cloudabi64_subscription_t *"; break; case 2: - p = "cloudabi64_size_t"; + p = "size_t"; break; case 3: p = "cloudabi64_event_t *"; break; case 4: - p = "cloudabi64_size_t"; + p = "size_t"; break; case 5: p = "const cloudabi64_subscription_t *"; @@ -1475,17 +1475,17 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) /* cloudabi64_sys_fd_pread */ case 8: if (ndx == 0 || ndx == 1) - p = "cloudabi64_size_t"; + p = "size_t"; break; /* cloudabi64_sys_fd_pwrite */ case 9: if (ndx == 0 || ndx == 1) - p = "cloudabi64_size_t"; + p = "size_t"; break; /* cloudabi64_sys_fd_read */ case 10: if (ndx == 0 || ndx == 1) - p = "cloudabi64_size_t"; + p = "size_t"; break; /* cloudabi_sys_fd_replace */ case 11: @@ -1515,7 +1515,7 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) /* cloudabi64_sys_fd_write */ case 16: if (ndx == 0 || ndx == 1) - p = "cloudabi64_size_t"; + p = "size_t"; break; /* cloudabi_sys_file_advise */ case 17: @@ -1630,7 +1630,7 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) /* cloudabi64_sys_poll */ case 39: if (ndx == 0 || ndx == 1) - p = "cloudabi64_size_t"; + p = "size_t"; break; /* cloudabi_sys_proc_exec */ case 40: @@ -1677,12 +1677,12 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) /* cloudabi64_sys_sock_recv */ case 49: if (ndx == 0 || ndx == 1) - p = "cloudabi64_size_t"; + p = "void"; break; /* cloudabi64_sys_sock_send */ case 50: if (ndx == 0 || ndx == 1) - p = "cloudabi64_size_t"; + p = "void"; break; /* cloudabi_sys_sock_shutdown */ case 51: @@ -1714,7 +1714,7 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) /* cloudabi64_sys_poll_fd */ case 57: if (ndx == 0 || ndx == 1) - p = "cloudabi64_size_t"; + p = "size_t"; break; default: break; diff --git a/sys/compat/cloudabi64/cloudabi64_thread.c b/sys/compat/cloudabi64/cloudabi64_thread.c index 04b1782..51961f8 100644 --- a/sys/compat/cloudabi64/cloudabi64_thread.c +++ b/sys/compat/cloudabi64/cloudabi64_thread.c @@ -30,7 +30,8 @@ __FBSDID("$FreeBSD$"); #include <sys/proc.h> #include <sys/systm.h> -#include <compat/cloudabi64/cloudabi64_syscalldefs.h> +#include <contrib/cloudabi/cloudabi64_types.h> + #include <compat/cloudabi64/cloudabi64_proto.h> #include <compat/cloudabi64/cloudabi64_util.h> diff --git a/sys/compat/cloudabi64/cloudabi64_util.h b/sys/compat/cloudabi64/cloudabi64_util.h index 180a01e..dcec70e 100644 --- a/sys/compat/cloudabi64/cloudabi64_util.h +++ b/sys/compat/cloudabi64/cloudabi64_util.h @@ -31,7 +31,7 @@ #include <sys/types.h> #include <sys/imgact_elf.h> -#include <compat/cloudabi64/cloudabi64_syscalldefs.h> +#include <contrib/cloudabi/cloudabi64_types.h> struct image_params; struct thread; diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index bdcdf6f..598bdc5 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -1653,6 +1653,19 @@ freebsd32_do_sendfile(struct thread *td, hdtr32.hdr_cnt, &hdr_uio); if (error) goto out; +#ifdef COMPAT_FREEBSD4 + /* + * In FreeBSD < 5.0 the nbytes to send also included + * the header. If compat is specified subtract the + * header size from nbytes. + */ + if (compat) { + if (uap->nbytes > hdr_uio->uio_resid) + uap->nbytes -= hdr_uio->uio_resid; + else + uap->nbytes = 0; + } +#endif } if (hdtr.trailers != NULL) { iov32 = PTRIN(hdtr32.trailers); @@ -1670,7 +1683,7 @@ freebsd32_do_sendfile(struct thread *td, goto out; error = fo_sendfile(fp, uap->s, hdr_uio, trl_uio, offset, - uap->nbytes, &sbytes, uap->flags, compat ? SFK_COMPAT : 0, td); + uap->nbytes, &sbytes, uap->flags, td); fdrop(fp, td); if (uap->sbytes != NULL) diff --git a/sys/compat/linux/linux_event.c b/sys/compat/linux/linux_event.c index b1ceadf..3e5a15c 100644 --- a/sys/compat/linux/linux_event.c +++ b/sys/compat/linux/linux_event.c @@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$"); #include <sys/user.h> #include <sys/file.h> #include <sys/filedesc.h> +#include <sys/filio.h> #include <sys/errno.h> #include <sys/event.h> #include <sys/poll.h> @@ -750,6 +751,8 @@ retry: if (UINT64_MAX - efd->efd_count <= count) { if ((efd->efd_flags & LINUX_O_NONBLOCK) != 0) { mtx_unlock(&efd->efd_lock); + /* Do not not return the number of bytes written */ + uio->uio_resid += sizeof(eventfd_t); return (EAGAIN); } error = mtx_sleep(&efd->efd_count, &efd->efd_lock, @@ -871,8 +874,24 @@ static int eventfd_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *active_cred, struct thread *td) { + struct eventfd *efd; - return (ENXIO); + efd = fp->f_data; + if (fp->f_type != DTYPE_LINUXEFD || efd == NULL) + return (EINVAL); + + switch (cmd) + { + case FIONBIO: + if (*(int *)data) + efd->efd_flags |= LINUX_O_NONBLOCK; + else + efd->efd_flags &= ~LINUX_O_NONBLOCK; + case FIOASYNC: + return (0); + default: + return (ENXIO); + } } /*ARGSUSED*/ diff --git a/sys/compat/linux/linux_mib.c b/sys/compat/linux/linux_mib.c index 396344b..a66ed80 100644 --- a/sys/compat/linux/linux_mib.c +++ b/sys/compat/linux/linux_mib.c @@ -168,9 +168,6 @@ linux_find_prison(struct prison *spr, struct prison **prp) struct prison *pr; struct linux_prison *lpr; - if (!linux_osd_jail_slot) - /* In case osd_register failed. */ - spr = &prison0; for (pr = spr;; pr = pr->pr_parent) { mtx_lock(&pr->pr_mtx); lpr = (pr == &prison0) @@ -189,15 +186,14 @@ linux_find_prison(struct prison *spr, struct prison **prp) * Ensure a prison has its own Linux info. If lprp is non-null, point it to * the Linux info and lock the prison. */ -static int +static void linux_alloc_prison(struct prison *pr, struct linux_prison **lprp) { struct prison *ppr; struct linux_prison *lpr, *nlpr; - int error; + void *rsv; /* If this prison already has Linux info, return that. */ - error = 0; lpr = linux_find_prison(pr, &ppr); if (ppr == pr) goto done; @@ -207,29 +203,24 @@ linux_alloc_prison(struct prison *pr, struct linux_prison **lprp) */ mtx_unlock(&ppr->pr_mtx); nlpr = malloc(sizeof(struct linux_prison), M_PRISON, M_WAITOK); + rsv = osd_reserve(linux_osd_jail_slot); lpr = linux_find_prison(pr, &ppr); if (ppr == pr) { free(nlpr, M_PRISON); + osd_free_reserved(rsv); goto done; } /* Inherit the initial values from the ancestor. */ mtx_lock(&pr->pr_mtx); - error = osd_jail_set(pr, linux_osd_jail_slot, nlpr); - if (error == 0) { - bcopy(lpr, nlpr, sizeof(*lpr)); - lpr = nlpr; - } else { - free(nlpr, M_PRISON); - lpr = NULL; - } + (void)osd_jail_set_reserved(pr, linux_osd_jail_slot, rsv, nlpr); + bcopy(lpr, nlpr, sizeof(*lpr)); + lpr = nlpr; mtx_unlock(&ppr->pr_mtx); done: if (lprp != NULL) *lprp = lpr; else mtx_unlock(&pr->pr_mtx); - - return (error); } /* @@ -249,7 +240,8 @@ linux_prison_create(void *obj, void *data) * Inherit a prison's initial values from its parent * (different from JAIL_SYS_INHERIT which also inherits changes). */ - return (linux_alloc_prison(pr, NULL)); + linux_alloc_prison(pr, NULL); + return (0); } static int @@ -345,11 +337,7 @@ linux_prison_set(void *obj, void *data) * "linux=new" or "linux.*": * the prison gets its own Linux info. */ - error = linux_alloc_prison(pr, &lpr); - if (error) { - mtx_unlock(&pr->pr_mtx); - return (error); - } + linux_alloc_prison(pr, &lpr); if (osrelease) { error = linux_map_osrel(osrelease, &lpr->pr_osrel); if (error) { @@ -449,21 +437,18 @@ linux_osd_jail_register(void) linux_osd_jail_slot = osd_jail_register(linux_prison_destructor, methods); - if (linux_osd_jail_slot > 0) { - /* Copy the system linux info to any current prisons. */ - sx_xlock(&allprison_lock); - TAILQ_FOREACH(pr, &allprison, pr_list) - (void)linux_alloc_prison(pr, NULL); - sx_xunlock(&allprison_lock); - } + /* Copy the system linux info to any current prisons. */ + sx_slock(&allprison_lock); + TAILQ_FOREACH(pr, &allprison, pr_list) + linux_alloc_prison(pr, NULL); + sx_sunlock(&allprison_lock); } void linux_osd_jail_deregister(void) { - if (linux_osd_jail_slot) - osd_jail_deregister(linux_osd_jail_slot); + osd_jail_deregister(linux_osd_jail_slot); } void diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c index 7e9a0d5..98a842c 100644 --- a/sys/compat/linux/linux_misc.c +++ b/sys/compat/linux/linux_misc.c @@ -191,32 +191,33 @@ linux_alarm(struct thread *td, struct linux_alarm_args *args) { struct itimerval it, old_it; u_int secs; + int error; #ifdef DEBUG if (ldebug(alarm)) printf(ARGS(alarm, "%u"), args->secs); #endif - secs = args->secs; - - if (secs > INT_MAX) - secs = INT_MAX; - - it.it_value.tv_sec = (long) secs; - it.it_value.tv_usec = 0; - it.it_interval.tv_sec = 0; - it.it_interval.tv_usec = 0; /* - * According to POSIX and Linux implementation - * the alarm() system call is always successfull. - * Ignore errors and return 0 as a Linux does. + * Linux alarm() is always successfull. Limit secs to INT32_MAX / 2 + * to match kern_setitimer()'s limit to avoid error from it. + * + * XXX. Linux limit secs to INT_MAX on 32 and does not limit on 64-bit + * platforms. */ - kern_setitimer(td, ITIMER_REAL, &it, &old_it); - if (timevalisset(&old_it.it_value)) { - if (old_it.it_value.tv_usec != 0) - old_it.it_value.tv_sec++; - td->td_retval[0] = old_it.it_value.tv_sec; - } + if (secs > INT32_MAX / 2) + secs = INT32_MAX / 2; + + it.it_value.tv_sec = secs; + it.it_value.tv_usec = 0; + timevalclear(&it.it_interval); + error = kern_setitimer(td, ITIMER_REAL, &it, &old_it); + KASSERT(error == 0, ("kern_setitimer returns %d", error)); + + if ((old_it.it_value.tv_sec == 0 && old_it.it_value.tv_usec > 0) || + old_it.it_value.tv_usec >= 500000) + old_it.it_value.tv_sec++; + td->td_retval[0] = old_it.it_value.tv_sec; return (0); } @@ -894,13 +895,14 @@ linux_utimensat(struct thread *td, struct linux_utimensat_args *args) break; } timesp = times; - } - if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT) /* This breaks POSIX, but is what the Linux kernel does * _on purpose_ (documented in the man page for utimensat(2)), * so we must follow that behaviour. */ - return (0); + if (times[0].tv_nsec == UTIME_OMIT && + times[1].tv_nsec == UTIME_OMIT) + return (0); + } if (args->pathname != NULL) LCONVPATHEXIST_AT(td, args->pathname, &path, dfd); diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c index 01b007c..741a45b 100644 --- a/sys/compat/linux/linux_socket.c +++ b/sys/compat/linux/linux_socket.c @@ -448,7 +448,7 @@ linux_to_bsd_msg_flags(int flags) if (flags & LINUX_MSG_ERRQUEUE) ; #endif - return ret_flags; + return (ret_flags); } /* @@ -463,15 +463,12 @@ bsd_to_linux_sockaddr(struct sockaddr *arg) struct sockaddr sa; size_t sa_len = sizeof(struct sockaddr); int error; - + if ((error = copyin(arg, &sa, sa_len))) return (error); - + *(u_short *)&sa = sa.sa_family; - - error = copyout(&sa, arg, sa_len); - - return (error); + return (copyout(&sa, arg, sa_len)); } static int @@ -486,10 +483,7 @@ linux_to_bsd_sockaddr(struct sockaddr *arg, int len) sa.sa_family = *(sa_family_t *)&sa; sa.sa_len = len; - - error = copyout(&sa, arg, sa_len); - - return (error); + return (copyout(&sa, arg, sa_len)); } static int @@ -511,11 +505,7 @@ linux_sa_put(struct osockaddr *osa) return (EINVAL); sa.sa_family = bdom; - error = copyout(&sa, osa, sizeof(sa.sa_family)); - if (error) - return (error); - - return (0); + return (copyout(&sa, osa, sizeof(sa.sa_family))); } static int @@ -912,10 +902,7 @@ linux_getsockname(struct thread *td, struct linux_getsockname_args *args) bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.asa); if (error) return (error); - error = linux_sa_put(PTRIN(args->addr)); - if (error) - return (error); - return (0); + return (linux_sa_put(PTRIN(args->addr))); } int @@ -935,10 +922,7 @@ linux_getpeername(struct thread *td, struct linux_getpeername_args *args) bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.asa); if (error) return (error); - error = linux_sa_put(PTRIN(args->addr)); - if (error) - return (error); - return (0); + return (linux_sa_put(PTRIN(args->addr))); } int @@ -1003,7 +987,7 @@ linux_send(struct thread *td, struct linux_send_args *args) bsd_args.flags = args->flags; bsd_args.to = NULL; bsd_args.tolen = 0; - return sys_sendto(td, &bsd_args); + return (sys_sendto(td, &bsd_args)); } struct linux_recv_args { @@ -1040,7 +1024,6 @@ linux_sendto(struct thread *td, struct linux_sendto_args *args) { struct msghdr msg; struct iovec aiov; - int error; if (linux_check_hdrincl(td, args->s) == 0) /* IP_HDRINCL set, tweak the packet before sending */ @@ -1054,9 +1037,8 @@ linux_sendto(struct thread *td, struct linux_sendto_args *args) msg.msg_flags = 0; aiov.iov_base = PTRIN(args->msg); aiov.iov_len = args->len; - error = linux_sendit(td, args->s, &msg, args->flags, NULL, - UIO_USERSPACE); - return (error); + return (linux_sendit(td, args->s, &msg, args->flags, NULL, + UIO_USERSPACE)); } int diff --git a/sys/compat/linux/linux_stats.c b/sys/compat/linux/linux_stats.c index 84ade7b..3638c6b 100644 --- a/sys/compat/linux/linux_stats.c +++ b/sys/compat/linux/linux_stats.c @@ -257,7 +257,7 @@ static int stat_copyout(struct stat *buf, void *ubuf) { struct l_stat lbuf; - + bzero(&lbuf, sizeof(lbuf)); lbuf.st_dev = buf->st_dev; lbuf.st_ino = buf->st_ino; @@ -303,7 +303,7 @@ linux_stat(struct thread *td, struct linux_stat_args *args) return (error); } LFREEPATH(path); - return(stat_copyout(&buf, args->up)); + return (stat_copyout(&buf, args->up)); } int @@ -325,7 +325,7 @@ linux_lstat(struct thread *td, struct linux_lstat_args *args) return (error); } LFREEPATH(path); - return(stat_copyout(&buf, args->up)); + return (stat_copyout(&buf, args->up)); } #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */ @@ -381,10 +381,22 @@ bsd_to_linux_ftype(const char *fstypename) return (0L); } -static void +static int bsd_to_linux_statfs(struct statfs *bsd_statfs, struct l_statfs *linux_statfs) { +#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32)) + uint64_t tmp; +#define LINUX_HIBITS 0xffffffff00000000ULL + + tmp = bsd_statfs->f_blocks | bsd_statfs->f_bfree | bsd_statfs->f_files | + bsd_statfs->f_bsize; + if ((bsd_statfs->f_bavail != -1 && (bsd_statfs->f_bavail & LINUX_HIBITS)) || + (bsd_statfs->f_ffree != -1 && (bsd_statfs->f_ffree & LINUX_HIBITS)) || + (tmp & LINUX_HIBITS)) + return (EOVERFLOW); +#undef LINUX_HIBITS +#endif linux_statfs->f_type = bsd_to_linux_ftype(bsd_statfs->f_fstypename); linux_statfs->f_bsize = bsd_statfs->f_bsize; linux_statfs->f_blocks = bsd_statfs->f_blocks; @@ -395,6 +407,8 @@ bsd_to_linux_statfs(struct statfs *bsd_statfs, struct l_statfs *linux_statfs) linux_statfs->f_fsid.val[0] = bsd_statfs->f_fsid.val[0]; linux_statfs->f_fsid.val[1] = bsd_statfs->f_fsid.val[1]; linux_statfs->f_namelen = MAXNAMLEN; + + return (0); } int @@ -415,8 +429,10 @@ linux_statfs(struct thread *td, struct linux_statfs_args *args) LFREEPATH(path); if (error) return (error); - bsd_to_linux_statfs(&bsd_statfs, &linux_statfs); - return copyout(&linux_statfs, args->buf, sizeof(linux_statfs)); + error = bsd_to_linux_statfs(&bsd_statfs, &linux_statfs); + if (error) + return (error); + return (copyout(&linux_statfs, args->buf, sizeof(linux_statfs))); } #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32)) @@ -458,7 +474,28 @@ linux_statfs64(struct thread *td, struct linux_statfs64_args *args) if (error) return (error); bsd_to_linux_statfs64(&bsd_statfs, &linux_statfs); - return copyout(&linux_statfs, args->buf, sizeof(linux_statfs)); + return (copyout(&linux_statfs, args->buf, sizeof(linux_statfs))); +} + +int +linux_fstatfs64(struct thread *td, struct linux_fstatfs64_args *args) +{ + struct l_statfs64 linux_statfs; + struct statfs bsd_statfs; + int error; + +#ifdef DEBUG + if (ldebug(fstatfs64)) + printf(ARGS(fstatfs64, "%d, *"), args->fd); +#endif + if (args->bufsize != sizeof(struct l_statfs64)) + return (EINVAL); + + error = kern_fstatfs(td, args->fd, &bsd_statfs); + if (error) + return error; + bsd_to_linux_statfs64(&bsd_statfs, &linux_statfs); + return (copyout(&linux_statfs, args->buf, sizeof(linux_statfs))); } #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */ @@ -475,9 +512,11 @@ linux_fstatfs(struct thread *td, struct linux_fstatfs_args *args) #endif error = kern_fstatfs(td, args->fd, &bsd_statfs); if (error) - return error; - bsd_to_linux_statfs(&bsd_statfs, &linux_statfs); - return copyout(&linux_statfs, args->buf, sizeof(linux_statfs)); + return (error); + error = bsd_to_linux_statfs(&bsd_statfs, &linux_statfs); + if (error) + return (error); + return (copyout(&linux_statfs, args->buf, sizeof(linux_statfs))); } struct l_ustat diff --git a/sys/compat/linuxkpi/common/include/linux/bitops.h b/sys/compat/linuxkpi/common/include/linux/bitops.h index 0dcdfe0..b12050a 100644 --- a/sys/compat/linuxkpi/common/include/linux/bitops.h +++ b/sys/compat/linuxkpi/common/include/linux/bitops.h @@ -147,11 +147,11 @@ find_last_bit(unsigned long *addr, unsigned long size) if (mask) return (bit + __flsl(mask)); } - while (--pos) { + while (pos--) { addr--; bit -= BITS_PER_LONG; if (*addr) - return (bit + __flsl(mask)); + return (bit + __flsl(*addr)); } return (size); } diff --git a/sys/compat/linuxkpi/common/include/linux/jiffies.h b/sys/compat/linuxkpi/common/include/linux/jiffies.h index f7bc529..33629ec 100644 --- a/sys/compat/linuxkpi/common/include/linux/jiffies.h +++ b/sys/compat/linuxkpi/common/include/linux/jiffies.h @@ -95,4 +95,14 @@ get_jiffies_64(void) return ((u64)(unsigned)ticks); } +static inline int +linux_timer_jiffies_until(unsigned long expires) +{ + int delta = expires - jiffies; + /* guard against already expired values */ + if (delta < 1) + delta = 1; + return (delta); +} + #endif /* _LINUX_JIFFIES_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/list.h b/sys/compat/linuxkpi/common/include/linux/list.h index 63e8af5..1357555 100644 --- a/sys/compat/linuxkpi/common/include/linux/list.h +++ b/sys/compat/linuxkpi/common/include/linux/list.h @@ -61,6 +61,7 @@ #include <netinet/in.h> #include <netinet/in_pcb.h> #include <netinet/in_var.h> +#include <netinet/tcp_lro.h> #include <netinet6/in6_var.h> #include <netinet6/nd6.h> diff --git a/sys/compat/linuxkpi/common/include/linux/wait.h b/sys/compat/linuxkpi/common/include/linux/wait.h index c62f735..8afea08 100644 --- a/sys/compat/linuxkpi/common/include/linux/wait.h +++ b/sys/compat/linuxkpi/common/include/linux/wait.h @@ -34,6 +34,7 @@ #include <linux/spinlock.h> #include <linux/sched.h> #include <linux/list.h> +#include <linux/jiffies.h> #include <sys/param.h> #include <sys/systm.h> @@ -113,6 +114,52 @@ do { \ -_error; \ }) +#define wait_event_interruptible_timeout(q, cond, timeout) \ +({ \ + void *c = &(q).wchan; \ + long end = jiffies + timeout; \ + int __ret = 0; \ + int __rc = 0; \ + \ + if (!(cond)) { \ + for (; __rc == 0;) { \ + sleepq_lock(c); \ + if (cond) { \ + sleepq_release(c); \ + __ret = 1; \ + break; \ + } \ + sleepq_add(c, NULL, "completion", \ + SLEEPQ_SLEEP | SLEEPQ_INTERRUPTIBLE, 0); \ + sleepq_set_timeout(c, linux_timer_jiffies_until(end));\ + __rc = sleepq_timedwait_sig (c, 0); \ + if (__rc != 0) { \ + /* check for timeout or signal. \ + * 0 if the condition evaluated to false\ + * after the timeout elapsed, 1 if the \ + * condition evaluated to true after the\ + * timeout elapsed. \ + */ \ + if (__rc == EWOULDBLOCK) \ + __ret = (cond); \ + else \ + __ret = -ERESTARTSYS; \ + } \ + \ + } \ + } else { \ + /* return remaining jiffies (at least 1) if the \ + * condition evaluated to true before the timeout \ + * elapsed. \ + */ \ + __ret = (end - jiffies); \ + if( __ret < 1 ) \ + __ret = 1; \ + } \ + __ret; \ +}) + + static inline int waitqueue_active(wait_queue_head_t *q) { diff --git a/sys/compat/linuxkpi/common/src/linux_compat.c b/sys/compat/linuxkpi/common/src/linux_compat.c index b8a0801..d85d4ad 100644 --- a/sys/compat/linuxkpi/common/src/linux_compat.c +++ b/sys/compat/linuxkpi/common/src/linux_compat.c @@ -894,16 +894,6 @@ kasprintf(gfp_t gfp, const char *fmt, ...) return (p); } -static int -linux_timer_jiffies_until(unsigned long expires) -{ - int delta = expires - jiffies; - /* guard against already expired values */ - if (delta < 1) - delta = 1; - return (delta); -} - static void linux_timer_callback_wrapper(void *context) { diff --git a/sys/compat/ndis/kern_ndis.c b/sys/compat/ndis/kern_ndis.c index 2ad1683..90e79b0 100644 --- a/sys/compat/ndis/kern_ndis.c +++ b/sys/compat/ndis/kern_ndis.c @@ -348,13 +348,13 @@ ndis_create_sysctls(arg) ndis_add_sysctl(sc, "BusType", "Bus Type", buf, NDIS_FLAG_RDONLY); if (sc->ndis_res_io != NULL) { - sprintf(buf, "0x%lx", rman_get_start(sc->ndis_res_io)); + sprintf(buf, "0x%jx", rman_get_start(sc->ndis_res_io)); ndis_add_sysctl(sc, "IOBaseAddress", "Base I/O Address", buf, NDIS_FLAG_RDONLY); } if (sc->ndis_irq != NULL) { - sprintf(buf, "%lu", rman_get_start(sc->ndis_irq)); + sprintf(buf, "%ju", rman_get_start(sc->ndis_irq)); ndis_add_sysctl(sc, "InterruptNumber", "Interrupt Number", buf, NDIS_FLAG_RDONLY); } diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 5a8014c..c9fce2e 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -2792,9 +2792,6 @@ device urndis # Realtek RTL8187B/L wireless driver device urtw # -# Realtek RTL8188CU/RTL8192CU wireless driver -device urtwn -# # ZyDas ZD1211/ZD1211B wireless driver device zyd # diff --git a/sys/conf/files b/sys/conf/files index 47b68b9..e2dbc67 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1413,12 +1413,20 @@ dev/exca/exca.c optional cbb dev/extres/clk/clk.c optional ext_resources clk dev/extres/clk/clkdev_if.m optional ext_resources clk dev/extres/clk/clknode_if.m optional ext_resources clk +dev/extres/clk/clk_bus.c optional ext_resources clk fdt dev/extres/clk/clk_div.c optional ext_resources clk dev/extres/clk/clk_fixed.c optional ext_resources clk dev/extres/clk/clk_gate.c optional ext_resources clk dev/extres/clk/clk_mux.c optional ext_resources clk +dev/extres/phy/phy.c optional ext_resources phy +dev/extres/phy/phy_if.m optional ext_resources phy dev/extres/hwreset/hwreset.c optional ext_resources hwreset dev/extres/hwreset/hwreset_if.m optional ext_resources hwreset +dev/extres/regulator/regdev_if.m optional ext_resources regulator +dev/extres/regulator/regnode_if.m optional ext_resources regulator +dev/extres/regulator/regulator.c optional ext_resources regulator +dev/extres/regulator/regulator_bus.c optional ext_resources regulator fdt +dev/extres/regulator/regulator_fixed.c optional ext_resources regulator dev/fatm/if_fatm.c optional fatm pci dev/fb/fbd.c optional fbd | vt dev/fb/fb_if.m standard @@ -1560,10 +1568,10 @@ ipw_monitor.fw optional ipwmonitorfw | ipwfw \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "ipw_monitor.fw" -dev/iscsi/icl.c optional iscsi | ctl -dev/iscsi/icl_conn_if.m optional iscsi | ctl +dev/iscsi/icl.c optional iscsi | ctl +dev/iscsi/icl_conn_if.m optional iscsi | ctl dev/iscsi/icl_proxy.c optional iscsi | ctl -dev/iscsi/icl_soft.c optional iscsi | ctl +dev/iscsi/icl_soft.c optional iscsi | ctl dev/iscsi/iscsi.c optional iscsi scbus dev/iscsi_initiator/iscsi.c optional iscsi_initiator scbus dev/iscsi_initiator/iscsi_subr.c optional iscsi_initiator scbus @@ -2547,11 +2555,12 @@ dev/uart/uart_bus_puc.c optional uart puc dev/uart/uart_bus_scc.c optional uart scc dev/uart/uart_core.c optional uart dev/uart/uart_dbg.c optional uart gdb -dev/uart/uart_dev_ns8250.c optional uart uart_ns8250 +dev/uart/uart_dev_ns8250.c optional uart uart_ns8250 | uart uart_snps dev/uart/uart_dev_pl011.c optional uart pl011 dev/uart/uart_dev_quicc.c optional uart quicc dev/uart/uart_dev_sab82532.c optional uart uart_sab82532 dev/uart/uart_dev_sab82532.c optional uart scc +dev/uart/uart_dev_snps.c optional uart uart_snps dev/uart/uart_dev_z8530.c optional uart uart_z8530 dev/uart/uart_dev_z8530.c optional uart scc dev/uart/uart_if.m optional uart @@ -2670,50 +2679,6 @@ dev/usb/wlan/if_uath.c optional uath dev/usb/wlan/if_upgt.c optional upgt dev/usb/wlan/if_ural.c optional ural dev/usb/wlan/if_urtw.c optional urtw -dev/usb/wlan/if_urtwn.c optional urtwn -urtwn-rtl8188eufw.c optional urtwn-rtl8188eufw | urtwnfw \ - compile-with "${AWK} -f $S/tools/fw_stub.awk urtwn-rtl8188eufw.fw:urtwn-rtl8188eufw:111 -murtwn-rtl8188eufw -c${.TARGET}" \ - no-implicit-rule before-depend local \ - clean "urtwn-rtl8188eufw.c" -urtwn-rtl8188eufw.fwo optional urtwn-rtl8188eufw | urtwnfw \ - dependency "urtwn-rtl8188eufw.fw" \ - compile-with "${NORMAL_FWO}" \ - no-implicit-rule \ - clean "urtwn-rtl8188eufw.fwo" -urtwn-rtl8188eufw.fw optional urtwn-rtl8188eufw | urtwnfw \ - dependency "$S/contrib/dev/urtwn/urtwn-rtl8188eufw.fw.uu" \ - compile-with "${NORMAL_FW}" \ - no-obj no-implicit-rule \ - clean "urtwn-rtl8188eufw.fw" -urtwn-rtl8192cfwT.c optional urtwn-rtl8192cfwT | urtwnfw \ - compile-with "${AWK} -f $S/tools/fw_stub.awk urtwn-rtl8192cfwT.fw:urtwn-rtl8192cfwT:111 -murtwn-rtl8192cfwT -c${.TARGET}" \ - no-implicit-rule before-depend local \ - clean "urtwn-rtl8192cfwT.c" -urtwn-rtl8192cfwT.fwo optional urtwn-rtl8192cfwT | urtwnfw \ - dependency "urtwn-rtl8192cfwT.fw" \ - compile-with "${NORMAL_FWO}" \ - no-implicit-rule \ - clean "urtwn-rtl8192cfwT.fwo" -urtwn-rtl8192cfwT.fw optional urtwn-rtl8192cfwT | urtwnfw \ - dependency "$S/contrib/dev/urtwn/urtwn-rtl8192cfwT.fw.uu" \ - compile-with "${NORMAL_FW}" \ - no-obj no-implicit-rule \ - clean "urtwn-rtl8192cfwT.fw" -urtwn-rtl8192cfwU.c optional urtwn-rtl8192cfwU | urtwnfw \ - compile-with "${AWK} -f $S/tools/fw_stub.awk urtwn-rtl8192cfwU.fw:urtwn-rtl8192cfwU:111 -murtwn-rtl8192cfwU -c${.TARGET}" \ - no-implicit-rule before-depend local \ - clean "urtwn-rtl8192cfwU.c" -urtwn-rtl8192cfwU.fwo optional urtwn-rtl8192cfwU | urtwnfw \ - dependency "urtwn-rtl8192cfwU.fw" \ - compile-with "${NORMAL_FWO}" \ - no-implicit-rule \ - clean "urtwn-rtl8192cfwU.fwo" -urtwn-rtl8192cfwU.fw optional urtwn-rtl8192cfwU | urtwnfw \ - dependency "$S/contrib/dev/urtwn/urtwn-rtl8192cfwU.fw.uu" \ - compile-with "${NORMAL_FW}" \ - no-obj no-implicit-rule \ - clean "urtwn-rtl8192cfwU.fw" - dev/usb/wlan/if_zyd.c optional zyd # # USB serial and parallel port drivers diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64 index 97f2e37..85366fa 100644 --- a/sys/conf/files.amd64 +++ b/sys/conf/files.amd64 @@ -50,12 +50,12 @@ font.h optional sc_dflt_font \ clean "font.h ${SC_DFLT_FONT}-8x14 ${SC_DFLT_FONT}-8x16 ${SC_DFLT_FONT}-8x8" # atkbdmap.h optional atkbd_dflt_keymap \ - compile-with "/usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ + compile-with "kbdcontrol -P ${S:S/sys$/share/}/vt/keymaps -P ${S:S/sys$/share/}/syscons/keymaps -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # ukbdmap.h optional ukbd_dflt_keymap \ - compile-with "/usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ + compile-with "kbdcontrol -P ${S:S/sys$/share/}/vt/keymaps -P ${S:S/sys$/share/}/syscons/keymaps -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # @@ -104,7 +104,6 @@ acpi_wakedata.h optional acpi \ amd64/amd64/amd64_mem.c optional mem #amd64/amd64/apic_vector.S standard amd64/amd64/atomic.c standard -amd64/amd64/autoconf.c standard amd64/amd64/bios.c standard amd64/amd64/bpf_jit_machdep.c optional bpf_jitter amd64/amd64/cpu_switch.S standard @@ -596,6 +595,7 @@ x86/isa/nmi.c standard x86/isa/orm.c optional isa x86/pci/pci_bus.c optional pci x86/pci/qpi.c optional pci +x86/x86/autoconf.c standard x86/x86/bus_machdep.c standard x86/x86/busdma_bounce.c standard x86/x86/busdma_machdep.c standard diff --git a/sys/conf/files.arm b/sys/conf/files.arm index 2407616..a9da11c 100644 --- a/sys/conf/files.arm +++ b/sys/conf/files.arm @@ -103,6 +103,8 @@ dev/hwpmc/hwpmc_arm.c optional hwpmc dev/hwpmc/hwpmc_armv7.c optional hwpmc armv6 dev/iicbus/twsi/twsi.c optional twsi dev/ofw/ofw_cpu.c optional fdt +dev/ofw/ofwpci.c optional fdt pci +dev/pci/pci_host_generic.c optional pci_host_generic pci fdt dev/psci/psci.c optional psci dev/psci/psci_arm.S optional psci dev/syscons/scgfbrndr.c optional sc diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 index 8f5f10a..7bbe458 100644 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -12,6 +12,7 @@ arm64/arm64/bus_machdep.c standard arm64/arm64/bus_space_asm.S standard arm64/arm64/busdma_bounce.c standard arm64/arm64/busdma_machdep.c standard +arm64/arm64/bzero.S standard arm64/arm64/clock.c standard arm64/arm64/copyinout.S standard arm64/arm64/copystr.c standard @@ -66,6 +67,7 @@ dev/hwpmc/hwpmc_arm64_md.c optional hwpmc dev/mmc/host/dwmmc.c optional dwmmc dev/mmc/host/dwmmc_hisi.c optional dwmmc soc_hisi_hi6220 dev/ofw/ofw_cpu.c optional fdt +dev/ofw/ofwpci.c optional fdt pci dev/pci/pci_host_generic.c optional pci fdt dev/psci/psci.c optional psci dev/psci/psci_arm64.S optional psci diff --git a/sys/conf/files.i386 b/sys/conf/files.i386 index b065165..4b6f158 100644 --- a/sys/conf/files.i386 +++ b/sys/conf/files.i386 @@ -49,12 +49,12 @@ font.h optional sc_dflt_font \ clean "font.h ${SC_DFLT_FONT}-8x14 ${SC_DFLT_FONT}-8x16 ${SC_DFLT_FONT}-8x8" # atkbdmap.h optional atkbd_dflt_keymap \ - compile-with "/usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ + compile-with "kbdcontrol -P ${S:S/sys$/share/}/vt/keymaps -P ${S:S/sys$/share/}/syscons/keymaps -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # ukbdmap.h optional ukbd_dflt_keymap \ - compile-with "/usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ + compile-with "kbdcontrol -P ${S:S/sys$/share/}/vt/keymaps -P ${S:S/sys$/share/}/syscons/keymaps -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # @@ -285,6 +285,7 @@ dev/nvme/nvme_sysctl.c optional nvme dev/nvme/nvme_test.c optional nvme dev/nvme/nvme_util.c optional nvme dev/nvram/nvram.c optional nvram isa +dev/ofw/ofwpci.c optional fdt pci dev/pcf/pcf_isa.c optional pcf dev/random/ivy.c optional rdrand_rng dev/random/nehemiah.c optional padlock_rng @@ -451,7 +452,6 @@ i386/bios/smapi_bios.S optional smapi #i386/i386/apic_vector.s optional apic i386/i386/atomic.c standard \ compile-with "${CC} -c ${CFLAGS} ${DEFINED_PROF:S/^$/-fomit-frame-pointer/} ${.IMPSRC}" -i386/i386/autoconf.c standard i386/i386/bios.c standard i386/i386/bioscall.s standard i386/i386/bpf_jit_machdep.c optional bpf_jitter @@ -590,6 +590,7 @@ x86/isa/nmi.c standard x86/isa/orm.c optional isa x86/pci/pci_bus.c optional pci x86/pci/qpi.c optional pci +x86/x86/autoconf.c standard x86/x86/bus_machdep.c standard x86/x86/busdma_bounce.c standard x86/x86/busdma_machdep.c standard diff --git a/sys/conf/files.mips b/sys/conf/files.mips index 91d53aa..4cacac4 100644 --- a/sys/conf/files.mips +++ b/sys/conf/files.mips @@ -92,3 +92,6 @@ dev/nvram2env/nvram2env.c optional nvram2env dev/hwpmc/hwpmc_mips.c optional hwpmc dev/hwpmc/hwpmc_mips24k.c optional hwpmc_mips24k dev/hwpmc/hwpmc_mips74k.c optional hwpmc_mips74k + +# ofw support +dev/ofw/ofwpci.c optional fdt pci diff --git a/sys/conf/files.pc98 b/sys/conf/files.pc98 index 75269b4..9f0ef00 100644 --- a/sys/conf/files.pc98 +++ b/sys/conf/files.pc98 @@ -46,7 +46,7 @@ svr4_assym.h optional compat_svr4 \ clean "svr4_assym.h" # ukbdmap.h optional ukbd_dflt_keymap \ - compile-with "/usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ + compile-with "kbdcontrol -P ${S:S/sys$/share/}/vt/keymaps -P ${S:S/sys$/share/}/syscons/keymaps -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # @@ -147,7 +147,6 @@ i386/bios/apm.c optional apm #i386/i386/apic_vector.s optional apic i386/i386/atomic.c standard \ compile-with "${CC} -c ${CFLAGS} ${DEFINED_PROF:S/^$/-fomit-frame-pointer/} ${.IMPSRC}" -i386/i386/autoconf.c standard i386/i386/bios.c standard i386/i386/bioscall.s standard i386/i386/bpf_jit_machdep.c optional bpf_jitter @@ -258,6 +257,7 @@ x86/isa/atpic.c optional atpic x86/isa/clock.c standard x86/isa/isa.c optional isa x86/pci/pci_bus.c optional pci +x86/x86/autoconf.c standard x86/x86/busdma_bounce.c standard x86/x86/busdma_machdep.c standard x86/x86/cpu_machdep.c standard diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc index 0a1e7c1..d2e8e3b 100644 --- a/sys/conf/files.powerpc +++ b/sys/conf/files.powerpc @@ -57,6 +57,7 @@ dev/ofw/ofw_console.c optional aim dev/ofw/ofw_disk.c optional ofwd aim dev/ofw/ofw_iicbus.c optional iicbus aim dev/ofw/ofwbus.c optional aim | fdt +dev/ofw/ofwpci.c optional pci dev/ofw/ofw_standard.c optional aim powerpc dev/ofw/ofw_subr.c optional aim powerpc dev/powermac_nvram/powermac_nvram.c optional powermac_nvram powermac @@ -145,7 +146,6 @@ powerpc/mpc85xx/pci_mpc85xx.c optional pci mpc85xx | pci qoriq_dpaa powerpc/mpc85xx/pci_mpc85xx_pcib.c optional pci mpc85xx | pci qoriq_dpaa powerpc/mpc85xx/qoriq_gpio.c optional mpc85xx gpio | qoriq_dpaa gpio powerpc/ofw/ofw_machdep.c standard -powerpc/ofw/ofw_pci.c optional pci powerpc/ofw/ofw_pcibus.c optional pci powerpc/ofw/ofw_pcib_pci.c optional pci powerpc/ofw/ofw_real.c optional aim diff --git a/sys/conf/files.sparc64 b/sys/conf/files.sparc64 index 84f23ff..a9643bd 100644 --- a/sys/conf/files.sparc64 +++ b/sys/conf/files.sparc64 @@ -8,17 +8,17 @@ # dependency lines other than the first are silently ignored. # atkbdmap.h optional atkbd_dflt_keymap \ - compile-with "/usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ + compile-with "kbdcontrol -P ${S:S/sys$/share/}/vt/keymaps -P ${S:S/sys$/share/}/syscons/keymaps -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # sunkbdmap.h optional sunkbd_dflt_keymap \ - compile-with "/usr/sbin/kbdcontrol -L ${SUNKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > sunkbdmap.h" \ + compile-with "kbdcontrol -P ${S:S/sys$/share/}/vt/keymaps -P ${S:S/sys$/share/}/syscons/keymaps -L ${SUNKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > sunkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "sunkbdmap.h" # ukbdmap.h optional ukbd_dflt_keymap \ - compile-with "/usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ + compile-with "kbdcontrol -P ${S:S/sys$/share/}/vt/keymaps -P ${S:S/sys$/share/}/syscons/keymaps -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # diff --git a/sys/conf/kern.opts.mk b/sys/conf/kern.opts.mk index d1d2d04..ad6ee72 100644 --- a/sys/conf/kern.opts.mk +++ b/sys/conf/kern.opts.mk @@ -30,7 +30,6 @@ __DEFAULT_YES_OPTIONS = \ CDDL \ CRYPT \ CUSE \ - FAST_DEPEND \ FORMAT_EXTENSIONS \ INET \ INET6 \ @@ -49,12 +48,6 @@ __DEFAULT_NO_OPTIONS = \ NAND \ OFED -# Enable FAST_DEPEND by default for the meta build. -.if !empty(.MAKE.MODE:Unormal:Mmeta) -__DEFAULT_YES_OPTIONS+= FAST_DEPEND -__DEFAULT_NO_OPTIONS:= ${__DEFAULT_NO_OPTIONS:NFAST_DEPEND} -.endif - # Some options are totally broken on some architectures. We disable # them. If you need to enable them on an experimental basis, you # must change this code. @@ -65,9 +58,8 @@ __DEFAULT_NO_OPTIONS:= ${__DEFAULT_NO_OPTIONS:NFAST_DEPEND} # Things that don't work based on the CPU .if ${MACHINE_CPUARCH} == "arm" -BROKEN_OPTIONS+= ZFS . if ${MACHINE_ARCH:Marmv6*} == "" -BROKEN_OPTIONS+= CDDL +BROKEN_OPTIONS+= CDDL ZFS . endif .endif diff --git a/sys/conf/kern.post.mk b/sys/conf/kern.post.mk index d5047a8..d28e87b 100644 --- a/sys/conf/kern.post.mk +++ b/sys/conf/kern.post.mk @@ -145,10 +145,6 @@ ${FULLKERNEL}: ${SYSTEM_DEP} vers.o OBJS_DEPEND_GUESS+= assym.s vnode_if.h ${BEFORE_DEPEND:M*.h} \ ${MFILES:T:S/.m$/.h/} -.if ${MK_FAST_DEPEND} == "no" && !exists(${.OBJDIR}/.depend) -${SYSTEM_OBJS}: ${OBJS_DEPEND_GUESS} -.endif - LNFILES= ${CFILES:T:S/.c$/.ln/} .for mfile in ${MFILES} @@ -189,23 +185,6 @@ genassym.o: $S/$M/$M/genassym.c ${SYSTEM_OBJS} genassym.o vers.o: opt_global.h -# Normal files first -CFILES_NORMAL= ${CFILES:N*/cddl/*:N*fs/nfsclient/nfs_clkdtrace*:N*/compat/linuxkpi/common/*:N*/ofed/*:N*/dev/mlx5/*} -SFILES_NORMAL= ${SFILES:N*/cddl/*} - -# We have "special" -I include paths for zfs/dtrace files in 'depend'. -CFILES_CDDL= ${CFILES:M*/cddl/*} -SFILES_CDDL= ${SFILES:M*/cddl/*} - -# We have "special" -I include paths for LinuxKPI. -CFILES_LINUXKPI=${CFILES:M*/compat/linuxkpi/common/*} - -# We have "special" -I include paths for OFED. -CFILES_OFED=${CFILES:M*/ofed/*} - -# We have "special" -I include paths for MLX5. -CFILES_MLX5=${CFILES:M*/dev/mlx5/*} - # Skip reading .depend when not needed to speed up tree-walks # and simple lookups. .if !empty(.MAKEFLAGS:M-V${_V_READ_DEPEND}) || make(obj) || make(clean*) || \ @@ -216,9 +195,6 @@ _SKIP_READ_DEPEND= 1 .endif kernel-depend: .depend -# The argument list can be very long, so use make -V and xargs to -# pass it to mkdep. -_MKDEPCC:= ${CC:N${CCACHE_BIN}} SRCS= assym.s vnode_if.h ${BEFORE_DEPEND} ${CFILES} \ ${SYSTEM_CFILES} ${GEN_CFILES} ${SFILES} \ ${MFILES:T:S/.m$/.h/} @@ -228,7 +204,6 @@ DEPENDFILES= .depend .depend.* .if !empty(.MAKE.MODE:Unormal:Mmeta) && empty(.MAKE.MODE:Unormal:Mnofilemon) _meta_filemon= 1 .endif -.if ${MK_FAST_DEPEND} == "yes" DEPENDOBJS+= ${SYSTEM_OBJS} genassym.o DEPENDFILES_OBJS= ${DEPENDOBJS:O:u:C/^/.depend./} .if ${MAKE_VERSION} < 20160220 @@ -261,12 +236,10 @@ CFLAGS+= ${DEPEND_CFLAGS} # all dependencies are correctly added or accounted for. This is mostly to # ensure downstream uses of kernel-depend are handled. beforebuild: kernel-depend -.endif # ${MK_FAST_DEPEND} == "yes" # Guess some dependencies for when no ${DEPENDFILE}.OBJ is generated yet. # For meta+filemon the .meta file is checked for since it is the dependency # file used. -.if ${MK_FAST_DEPEND} == "yes" .for __obj in ${DEPENDOBJS:O:u} .if (defined(_meta_filemon) && !exists(${.OBJDIR}/${__obj}.meta)) || \ (!defined(_meta_filemon) && !exists(${.OBJDIR}/.depend.${__obj})) @@ -276,32 +249,10 @@ ${__obj}: ${OBJS_DEPEND_GUESS} ${__obj}: ${OBJS_DEPEND_GUESS.${__obj}} .endif .endfor -.endif .NOPATH: .depend ${DEPENDFILES_OBJS} .depend: .PRECIOUS ${SRCS} -.if ${MK_FAST_DEPEND} == "no" - rm -f ${.TARGET}.tmp -# C files - ${MAKE} -V CFILES_NORMAL -V SYSTEM_CFILES -V GEN_CFILES | \ - CC="${_MKDEPCC}" xargs mkdep -a -f ${.TARGET}.tmp ${CFLAGS} - ${MAKE} -V CFILES_CDDL | \ - CC="${_MKDEPCC}" xargs mkdep -a -f ${.TARGET}.tmp ${ZFS_CFLAGS} \ - ${FBT_CFLAGS} ${DTRACE_CFLAGS} - ${MAKE} -V CFILES_LINUXKPI | \ - CC="${_MKDEPCC}" xargs mkdep -a -f ${.TARGET}.tmp \ - ${CFLAGS} ${LINUXKPI_INCLUDES} - ${MAKE} -V CFILES_OFED -V CFILES_MLX5 | \ - CC="${_MKDEPCC}" xargs mkdep -a -f ${.TARGET}.tmp \ - ${CFLAGS} ${OFEDINCLUDES} -# Assembly files - ${MAKE} -V SFILES_NORMAL | \ - CC="${_MKDEPCC}" xargs mkdep -a -f ${.TARGET}.tmp ${ASM_CFLAGS} - ${MAKE} -V SFILES_CDDL | \ - CC="${_MKDEPCC}" xargs mkdep -a -f ${.TARGET}.tmp ${ZFS_ASM_CFLAGS} - mv ${.TARGET}.tmp ${.TARGET} -.endif _ILINKS= machine .if ${MACHINE} != ${MACHINE_CPUARCH} && ${MACHINE} != "arm64" diff --git a/sys/conf/kern.pre.mk b/sys/conf/kern.pre.mk index cf6ec10..591c988 100644 --- a/sys/conf/kern.pre.mk +++ b/sys/conf/kern.pre.mk @@ -64,29 +64,6 @@ NOSTDINC= -nostdinc INCLUDES= ${NOSTDINC} ${INCLMAGIC} -I. -I$S -.if ${MK_FAST_DEPEND} == "no" && (make(depend) || make(kernel-depend)) - -# This hack lets us use the ipfilter code without spamming a new -# include path into contrib'ed source files. -INCLUDES+= -I$S/contrib/ipfilter - -# ... and the same for ath -INCLUDES+= -I$S/dev/ath -I$S/dev/ath/ath_hal -I$S/contrib/dev/ath/ath_hal - -# ... and the same for the NgATM stuff -INCLUDES+= -I$S/contrib/ngatm - -# ... and the same for vchiq -INCLUDES+= -I$S/contrib/vchiq - -# ... and the same for twa -INCLUDES+= -I$S/dev/twa - -# ... and the same for cxgb and cxgbe -INCLUDES+= -I$S/dev/cxgb -I$S/dev/cxgbe - -.endif - CFLAGS= ${COPTFLAGS} ${DEBUG} CFLAGS+= ${INCLUDES} -D_KERNEL -DHAVE_KERNEL_OPTION_HEADERS -include opt_global.h CFLAGS_PARAM_INLINE_UNIT_GROWTH?=100 diff --git a/sys/conf/kmod.mk b/sys/conf/kmod.mk index 6a3e1eb..9e6b06c 100644 --- a/sys/conf/kmod.mk +++ b/sys/conf/kmod.mk @@ -458,11 +458,6 @@ cleanilinks: OBJS_DEPEND_GUESS+= ${SRCS:M*.h} .include <bsd.dep.mk> - -.if ${MK_FAST_DEPEND} == "no" && !exists(${.OBJDIR}/${DEPENDFILE}) -${OBJS}: ${OBJS_DEPEND_GUESS} -.endif - .include <bsd.clang-analyze.mk> .include <bsd.obj.mk> .include "kern.mk" diff --git a/sys/contrib/cloudabi/cloudabi64_types.h b/sys/contrib/cloudabi/cloudabi64_types.h new file mode 100644 index 0000000..88babaa --- /dev/null +++ b/sys/contrib/cloudabi/cloudabi64_types.h @@ -0,0 +1,225 @@ +// Copyright (c) 2016 Nuxi (https://nuxi.nl/) and contributors. +// +// 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. +// +// This file is automatically generated. Do not edit. +// +// Source: https://github.com/NuxiNL/cloudabi + +#ifndef CLOUDABI64_TYPES_H +#define CLOUDABI64_TYPES_H + +#include "cloudabi_types_common.h" + +typedef struct { + _Alignas(4) cloudabi_auxtype_t a_type; + union { + _Alignas(8) uint64_t a_val; + _Alignas(8) uint64_t a_ptr; + }; +} cloudabi64_auxv_t; +_Static_assert(offsetof(cloudabi64_auxv_t, a_type) == 0, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_auxv_t, a_val) == 8, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_auxv_t, a_ptr) == 8, "Incorrect layout"); +_Static_assert(sizeof(cloudabi64_auxv_t) == 16, "Incorrect layout"); +_Static_assert(_Alignof(cloudabi64_auxv_t) == 8, "Incorrect layout"); + +typedef struct { + _Alignas(8) uint64_t iov_base; + _Alignas(8) uint64_t iov_len; +} cloudabi64_ciovec_t; +_Static_assert(offsetof(cloudabi64_ciovec_t, iov_base) == 0, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_ciovec_t, iov_len) == 8, "Incorrect layout"); +_Static_assert(sizeof(cloudabi64_ciovec_t) == 16, "Incorrect layout"); +_Static_assert(_Alignof(cloudabi64_ciovec_t) == 8, "Incorrect layout"); + +typedef struct { + _Alignas(8) cloudabi_userdata_t userdata; + _Alignas(2) cloudabi_errno_t error; + _Alignas(1) cloudabi_eventtype_t type; + union { + struct { + _Alignas(8) cloudabi_userdata_t identifier; + } clock; + struct { + _Alignas(8) uint64_t condvar; + } condvar; + struct { + _Alignas(8) cloudabi_filesize_t nbytes; + _Alignas(4) cloudabi_fd_t fd; + _Alignas(2) cloudabi_eventrwflags_t flags; + } fd_readwrite; + struct { + _Alignas(8) uint64_t lock; + } lock; + struct { + _Alignas(4) cloudabi_fd_t fd; + _Alignas(1) cloudabi_signal_t signal; + _Alignas(4) cloudabi_exitcode_t exitcode; + } proc_terminate; + }; +} cloudabi64_event_t; +_Static_assert(offsetof(cloudabi64_event_t, userdata) == 0, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_event_t, error) == 8, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_event_t, type) == 10, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_event_t, clock.identifier) == 16, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_event_t, condvar.condvar) == 16, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_event_t, fd_readwrite.nbytes) == 16, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_event_t, fd_readwrite.fd) == 24, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_event_t, fd_readwrite.flags) == 28, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_event_t, lock.lock) == 16, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_event_t, proc_terminate.fd) == 16, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_event_t, proc_terminate.signal) == 20, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_event_t, proc_terminate.exitcode) == 24, "Incorrect layout"); +_Static_assert(sizeof(cloudabi64_event_t) == 32, "Incorrect layout"); +_Static_assert(_Alignof(cloudabi64_event_t) == 8, "Incorrect layout"); + +typedef struct { + _Alignas(8) uint64_t iov_base; + _Alignas(8) uint64_t iov_len; +} cloudabi64_iovec_t; +_Static_assert(offsetof(cloudabi64_iovec_t, iov_base) == 0, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_iovec_t, iov_len) == 8, "Incorrect layout"); +_Static_assert(sizeof(cloudabi64_iovec_t) == 16, "Incorrect layout"); +_Static_assert(_Alignof(cloudabi64_iovec_t) == 8, "Incorrect layout"); + +typedef void cloudabi64_processentry_t(uint64_t auxv); + +typedef struct { + _Alignas(8) uint64_t ri_data; + _Alignas(8) uint64_t ri_datalen; + _Alignas(8) uint64_t ri_fds; + _Alignas(8) uint64_t ri_fdslen; + _Alignas(2) cloudabi_msgflags_t ri_flags; +} cloudabi64_recv_in_t; +_Static_assert(offsetof(cloudabi64_recv_in_t, ri_data) == 0, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_recv_in_t, ri_datalen) == 8, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_recv_in_t, ri_fds) == 16, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_recv_in_t, ri_fdslen) == 24, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_recv_in_t, ri_flags) == 32, "Incorrect layout"); +_Static_assert(sizeof(cloudabi64_recv_in_t) == 40, "Incorrect layout"); +_Static_assert(_Alignof(cloudabi64_recv_in_t) == 8, "Incorrect layout"); + +typedef struct { + _Alignas(8) uint64_t si_data; + _Alignas(8) uint64_t si_datalen; + _Alignas(8) uint64_t si_fds; + _Alignas(8) uint64_t si_fdslen; + _Alignas(2) cloudabi_msgflags_t si_flags; +} cloudabi64_send_in_t; +_Static_assert(offsetof(cloudabi64_send_in_t, si_data) == 0, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_send_in_t, si_datalen) == 8, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_send_in_t, si_fds) == 16, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_send_in_t, si_fdslen) == 24, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_send_in_t, si_flags) == 32, "Incorrect layout"); +_Static_assert(sizeof(cloudabi64_send_in_t) == 40, "Incorrect layout"); +_Static_assert(_Alignof(cloudabi64_send_in_t) == 8, "Incorrect layout"); + +typedef struct { + _Alignas(8) uint64_t so_datalen; +} cloudabi64_send_out_t; +_Static_assert(offsetof(cloudabi64_send_out_t, so_datalen) == 0, "Incorrect layout"); +_Static_assert(sizeof(cloudabi64_send_out_t) == 8, "Incorrect layout"); +_Static_assert(_Alignof(cloudabi64_send_out_t) == 8, "Incorrect layout"); + +typedef struct { + _Alignas(8) cloudabi_userdata_t userdata; + _Alignas(2) cloudabi_subflags_t flags; + _Alignas(1) cloudabi_eventtype_t type; + union { + struct { + _Alignas(8) cloudabi_userdata_t identifier; + _Alignas(4) cloudabi_clockid_t clock_id; + _Alignas(8) cloudabi_timestamp_t timeout; + _Alignas(8) cloudabi_timestamp_t precision; + _Alignas(2) cloudabi_subclockflags_t flags; + } clock; + struct { + _Alignas(8) uint64_t condvar; + _Alignas(8) uint64_t lock; + _Alignas(1) cloudabi_scope_t condvar_scope; + _Alignas(1) cloudabi_scope_t lock_scope; + } condvar; + struct { + _Alignas(4) cloudabi_fd_t fd; + _Alignas(2) cloudabi_subrwflags_t flags; + } fd_readwrite; + struct { + _Alignas(8) uint64_t lock; + _Alignas(1) cloudabi_scope_t lock_scope; + } lock; + struct { + _Alignas(4) cloudabi_fd_t fd; + } proc_terminate; + }; +} cloudabi64_subscription_t; +_Static_assert(offsetof(cloudabi64_subscription_t, userdata) == 0, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_subscription_t, flags) == 8, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_subscription_t, type) == 10, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_subscription_t, clock.identifier) == 16, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_subscription_t, clock.clock_id) == 24, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_subscription_t, clock.timeout) == 32, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_subscription_t, clock.precision) == 40, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_subscription_t, clock.flags) == 48, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_subscription_t, condvar.condvar) == 16, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_subscription_t, condvar.lock) == 24, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_subscription_t, condvar.condvar_scope) == 32, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_subscription_t, condvar.lock_scope) == 33, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_subscription_t, fd_readwrite.fd) == 16, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_subscription_t, fd_readwrite.flags) == 20, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_subscription_t, lock.lock) == 16, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_subscription_t, lock.lock_scope) == 24, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_subscription_t, proc_terminate.fd) == 16, "Incorrect layout"); +_Static_assert(sizeof(cloudabi64_subscription_t) == 56, "Incorrect layout"); +_Static_assert(_Alignof(cloudabi64_subscription_t) == 8, "Incorrect layout"); + +typedef void cloudabi64_threadentry_t(cloudabi_tid_t tid, uint64_t aux); + +typedef struct { + _Alignas(8) uint64_t ro_datalen; + _Alignas(8) uint64_t ro_fdslen; + _Alignas(2) cloudabi_sockaddr_t ro_sockname; + _Alignas(2) cloudabi_sockaddr_t ro_peername; + _Alignas(2) cloudabi_msgflags_t ro_flags; +} cloudabi64_recv_out_t; +_Static_assert(offsetof(cloudabi64_recv_out_t, ro_datalen) == 0, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_recv_out_t, ro_fdslen) == 8, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_recv_out_t, ro_sockname) == 16, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_recv_out_t, ro_peername) == 36, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_recv_out_t, ro_flags) == 56, "Incorrect layout"); +_Static_assert(sizeof(cloudabi64_recv_out_t) == 64, "Incorrect layout"); +_Static_assert(_Alignof(cloudabi64_recv_out_t) == 8, "Incorrect layout"); + +typedef struct { + _Alignas(8) uint64_t entry_point; + _Alignas(8) uint64_t stack; + _Alignas(8) uint64_t stack_size; + _Alignas(8) uint64_t argument; +} cloudabi64_threadattr_t; +_Static_assert(offsetof(cloudabi64_threadattr_t, entry_point) == 0, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_threadattr_t, stack) == 8, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_threadattr_t, stack_size) == 16, "Incorrect layout"); +_Static_assert(offsetof(cloudabi64_threadattr_t, argument) == 24, "Incorrect layout"); +_Static_assert(sizeof(cloudabi64_threadattr_t) == 32, "Incorrect layout"); +_Static_assert(_Alignof(cloudabi64_threadattr_t) == 8, "Incorrect layout"); + +#endif diff --git a/sys/contrib/cloudabi/cloudabi_types_common.h b/sys/contrib/cloudabi/cloudabi_types_common.h new file mode 100644 index 0000000..83c61b9 --- /dev/null +++ b/sys/contrib/cloudabi/cloudabi_types_common.h @@ -0,0 +1,463 @@ +// Copyright (c) 2016 Nuxi (https://nuxi.nl/) and contributors. +// +// 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. +// +// This file is automatically generated. Do not edit. +// +// Source: https://github.com/NuxiNL/cloudabi + +#ifndef CLOUDABI_TYPES_COMMON_H +#define CLOUDABI_TYPES_COMMON_H + +#if defined(__FreeBSD__) && defined(_KERNEL) +#include <sys/stdint.h> +#elif defined(__linux__) && defined(__KERNEL__) +#include <linux/types.h> +#else +#include <stddef.h> +#include <stdint.h> +#endif + +typedef uint8_t cloudabi_advice_t; +#define CLOUDABI_ADVICE_DONTNEED 1 +#define CLOUDABI_ADVICE_NOREUSE 2 +#define CLOUDABI_ADVICE_NORMAL 3 +#define CLOUDABI_ADVICE_RANDOM 4 +#define CLOUDABI_ADVICE_SEQUENTIAL 5 +#define CLOUDABI_ADVICE_WILLNEED 6 + +typedef uint32_t cloudabi_auxtype_t; +#define CLOUDABI_AT_ARGDATA 256 +#define CLOUDABI_AT_ARGDATALEN 257 +#define CLOUDABI_AT_BASE 7 +#define CLOUDABI_AT_CANARY 258 +#define CLOUDABI_AT_CANARYLEN 259 +#define CLOUDABI_AT_NCPUS 260 +#define CLOUDABI_AT_NULL 0 +#define CLOUDABI_AT_PAGESZ 6 +#define CLOUDABI_AT_PHDR 3 +#define CLOUDABI_AT_PHNUM 4 +#define CLOUDABI_AT_TID 261 + +typedef uint32_t cloudabi_backlog_t; + +typedef uint32_t cloudabi_clockid_t; +#define CLOUDABI_CLOCK_MONOTONIC 1 +#define CLOUDABI_CLOCK_PROCESS_CPUTIME_ID 2 +#define CLOUDABI_CLOCK_REALTIME 3 +#define CLOUDABI_CLOCK_THREAD_CPUTIME_ID 4 + +typedef uint32_t cloudabi_condvar_t; +#define CLOUDABI_CONDVAR_HAS_NO_WAITERS 0 + +typedef uint64_t cloudabi_device_t; + +typedef uint64_t cloudabi_dircookie_t; +#define CLOUDABI_DIRCOOKIE_START 0 + +typedef uint16_t cloudabi_errno_t; +#define CLOUDABI_E2BIG 1 +#define CLOUDABI_EACCES 2 +#define CLOUDABI_EADDRINUSE 3 +#define CLOUDABI_EADDRNOTAVAIL 4 +#define CLOUDABI_EAFNOSUPPORT 5 +#define CLOUDABI_EAGAIN 6 +#define CLOUDABI_EALREADY 7 +#define CLOUDABI_EBADF 8 +#define CLOUDABI_EBADMSG 9 +#define CLOUDABI_EBUSY 10 +#define CLOUDABI_ECANCELED 11 +#define CLOUDABI_ECHILD 12 +#define CLOUDABI_ECONNABORTED 13 +#define CLOUDABI_ECONNREFUSED 14 +#define CLOUDABI_ECONNRESET 15 +#define CLOUDABI_EDEADLK 16 +#define CLOUDABI_EDESTADDRREQ 17 +#define CLOUDABI_EDOM 18 +#define CLOUDABI_EDQUOT 19 +#define CLOUDABI_EEXIST 20 +#define CLOUDABI_EFAULT 21 +#define CLOUDABI_EFBIG 22 +#define CLOUDABI_EHOSTUNREACH 23 +#define CLOUDABI_EIDRM 24 +#define CLOUDABI_EILSEQ 25 +#define CLOUDABI_EINPROGRESS 26 +#define CLOUDABI_EINTR 27 +#define CLOUDABI_EINVAL 28 +#define CLOUDABI_EIO 29 +#define CLOUDABI_EISCONN 30 +#define CLOUDABI_EISDIR 31 +#define CLOUDABI_ELOOP 32 +#define CLOUDABI_EMFILE 33 +#define CLOUDABI_EMLINK 34 +#define CLOUDABI_EMSGSIZE 35 +#define CLOUDABI_EMULTIHOP 36 +#define CLOUDABI_ENAMETOOLONG 37 +#define CLOUDABI_ENETDOWN 38 +#define CLOUDABI_ENETRESET 39 +#define CLOUDABI_ENETUNREACH 40 +#define CLOUDABI_ENFILE 41 +#define CLOUDABI_ENOBUFS 42 +#define CLOUDABI_ENODEV 43 +#define CLOUDABI_ENOENT 44 +#define CLOUDABI_ENOEXEC 45 +#define CLOUDABI_ENOLCK 46 +#define CLOUDABI_ENOLINK 47 +#define CLOUDABI_ENOMEM 48 +#define CLOUDABI_ENOMSG 49 +#define CLOUDABI_ENOPROTOOPT 50 +#define CLOUDABI_ENOSPC 51 +#define CLOUDABI_ENOSYS 52 +#define CLOUDABI_ENOTCONN 53 +#define CLOUDABI_ENOTDIR 54 +#define CLOUDABI_ENOTEMPTY 55 +#define CLOUDABI_ENOTRECOVERABLE 56 +#define CLOUDABI_ENOTSOCK 57 +#define CLOUDABI_ENOTSUP 58 +#define CLOUDABI_ENOTTY 59 +#define CLOUDABI_ENXIO 60 +#define CLOUDABI_EOVERFLOW 61 +#define CLOUDABI_EOWNERDEAD 62 +#define CLOUDABI_EPERM 63 +#define CLOUDABI_EPIPE 64 +#define CLOUDABI_EPROTO 65 +#define CLOUDABI_EPROTONOSUPPORT 66 +#define CLOUDABI_EPROTOTYPE 67 +#define CLOUDABI_ERANGE 68 +#define CLOUDABI_EROFS 69 +#define CLOUDABI_ESPIPE 70 +#define CLOUDABI_ESRCH 71 +#define CLOUDABI_ESTALE 72 +#define CLOUDABI_ETIMEDOUT 73 +#define CLOUDABI_ETXTBSY 74 +#define CLOUDABI_EXDEV 75 +#define CLOUDABI_ENOTCAPABLE 76 + +typedef uint16_t cloudabi_eventrwflags_t; +#define CLOUDABI_EVENT_FD_READWRITE_HANGUP 0x0001 + +typedef uint8_t cloudabi_eventtype_t; +#define CLOUDABI_EVENTTYPE_CLOCK 1 +#define CLOUDABI_EVENTTYPE_CONDVAR 2 +#define CLOUDABI_EVENTTYPE_FD_READ 3 +#define CLOUDABI_EVENTTYPE_FD_WRITE 4 +#define CLOUDABI_EVENTTYPE_LOCK_RDLOCK 5 +#define CLOUDABI_EVENTTYPE_LOCK_WRLOCK 6 +#define CLOUDABI_EVENTTYPE_PROC_TERMINATE 7 + +typedef uint32_t cloudabi_exitcode_t; + +typedef uint32_t cloudabi_fd_t; +#define CLOUDABI_PROCESS_CHILD 0xffffffff +#define CLOUDABI_MAP_ANON_FD 0xffffffff + +typedef uint16_t cloudabi_fdflags_t; +#define CLOUDABI_FDFLAG_APPEND 0x0001 +#define CLOUDABI_FDFLAG_DSYNC 0x0002 +#define CLOUDABI_FDFLAG_NONBLOCK 0x0004 +#define CLOUDABI_FDFLAG_RSYNC 0x0008 +#define CLOUDABI_FDFLAG_SYNC 0x0010 + +typedef uint16_t cloudabi_fdsflags_t; +#define CLOUDABI_FDSTAT_FLAGS 0x0001 +#define CLOUDABI_FDSTAT_RIGHTS 0x0002 + +typedef int64_t cloudabi_filedelta_t; + +typedef uint64_t cloudabi_filesize_t; + +typedef uint8_t cloudabi_filetype_t; +#define CLOUDABI_FILETYPE_UNKNOWN 0 +#define CLOUDABI_FILETYPE_BLOCK_DEVICE 16 +#define CLOUDABI_FILETYPE_CHARACTER_DEVICE 17 +#define CLOUDABI_FILETYPE_DIRECTORY 32 +#define CLOUDABI_FILETYPE_FIFO 48 +#define CLOUDABI_FILETYPE_POLL 64 +#define CLOUDABI_FILETYPE_PROCESS 80 +#define CLOUDABI_FILETYPE_REGULAR_FILE 96 +#define CLOUDABI_FILETYPE_SHARED_MEMORY 112 +#define CLOUDABI_FILETYPE_SOCKET_DGRAM 128 +#define CLOUDABI_FILETYPE_SOCKET_SEQPACKET 129 +#define CLOUDABI_FILETYPE_SOCKET_STREAM 130 +#define CLOUDABI_FILETYPE_SYMBOLIC_LINK 144 + +typedef uint16_t cloudabi_fsflags_t; +#define CLOUDABI_FILESTAT_ATIM 0x0001 +#define CLOUDABI_FILESTAT_ATIM_NOW 0x0002 +#define CLOUDABI_FILESTAT_MTIM 0x0004 +#define CLOUDABI_FILESTAT_MTIM_NOW 0x0008 +#define CLOUDABI_FILESTAT_SIZE 0x0010 + +typedef uint64_t cloudabi_inode_t; + +typedef uint32_t cloudabi_linkcount_t; + +typedef uint32_t cloudabi_lock_t; +#define CLOUDABI_LOCK_UNLOCKED 0x00000000 +#define CLOUDABI_LOCK_WRLOCKED 0x40000000 +#define CLOUDABI_LOCK_KERNEL_MANAGED 0x80000000 +#define CLOUDABI_LOCK_BOGUS 0x80000000 + +typedef uint32_t cloudabi_lookupflags_t; +#define CLOUDABI_LOOKUP_SYMLINK_FOLLOW 0x00000001 + +typedef uint8_t cloudabi_mflags_t; +#define CLOUDABI_MAP_ANON 0x01 +#define CLOUDABI_MAP_FIXED 0x02 +#define CLOUDABI_MAP_PRIVATE 0x04 +#define CLOUDABI_MAP_SHARED 0x08 + +typedef uint8_t cloudabi_mprot_t; +#define CLOUDABI_PROT_EXEC 0x01 +#define CLOUDABI_PROT_WRITE 0x02 +#define CLOUDABI_PROT_READ 0x04 + +typedef uint8_t cloudabi_msflags_t; +#define CLOUDABI_MS_ASYNC 0x01 +#define CLOUDABI_MS_INVALIDATE 0x02 +#define CLOUDABI_MS_SYNC 0x04 + +typedef uint16_t cloudabi_msgflags_t; +#define CLOUDABI_MSG_CTRUNC 0x0001 +#define CLOUDABI_MSG_EOR 0x0002 +#define CLOUDABI_MSG_PEEK 0x0004 +#define CLOUDABI_MSG_TRUNC 0x0008 +#define CLOUDABI_MSG_WAITALL 0x0010 + +typedef uint32_t cloudabi_nthreads_t; + +typedef uint16_t cloudabi_oflags_t; +#define CLOUDABI_O_CREAT 0x0001 +#define CLOUDABI_O_DIRECTORY 0x0002 +#define CLOUDABI_O_EXCL 0x0004 +#define CLOUDABI_O_TRUNC 0x0008 + +typedef uint64_t cloudabi_rights_t; +#define CLOUDABI_RIGHT_FD_DATASYNC 0x0000000000000001 +#define CLOUDABI_RIGHT_FD_READ 0x0000000000000002 +#define CLOUDABI_RIGHT_FD_SEEK 0x0000000000000004 +#define CLOUDABI_RIGHT_FD_STAT_PUT_FLAGS 0x0000000000000008 +#define CLOUDABI_RIGHT_FD_SYNC 0x0000000000000010 +#define CLOUDABI_RIGHT_FD_TELL 0x0000000000000020 +#define CLOUDABI_RIGHT_FD_WRITE 0x0000000000000040 +#define CLOUDABI_RIGHT_FILE_ADVISE 0x0000000000000080 +#define CLOUDABI_RIGHT_FILE_ALLOCATE 0x0000000000000100 +#define CLOUDABI_RIGHT_FILE_CREATE_DIRECTORY 0x0000000000000200 +#define CLOUDABI_RIGHT_FILE_CREATE_FILE 0x0000000000000400 +#define CLOUDABI_RIGHT_FILE_CREATE_FIFO 0x0000000000000800 +#define CLOUDABI_RIGHT_FILE_LINK_SOURCE 0x0000000000001000 +#define CLOUDABI_RIGHT_FILE_LINK_TARGET 0x0000000000002000 +#define CLOUDABI_RIGHT_FILE_OPEN 0x0000000000004000 +#define CLOUDABI_RIGHT_FILE_READDIR 0x0000000000008000 +#define CLOUDABI_RIGHT_FILE_READLINK 0x0000000000010000 +#define CLOUDABI_RIGHT_FILE_RENAME_SOURCE 0x0000000000020000 +#define CLOUDABI_RIGHT_FILE_RENAME_TARGET 0x0000000000040000 +#define CLOUDABI_RIGHT_FILE_STAT_FGET 0x0000000000080000 +#define CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE 0x0000000000100000 +#define CLOUDABI_RIGHT_FILE_STAT_FPUT_TIMES 0x0000000000200000 +#define CLOUDABI_RIGHT_FILE_STAT_GET 0x0000000000400000 +#define CLOUDABI_RIGHT_FILE_STAT_PUT_TIMES 0x0000000000800000 +#define CLOUDABI_RIGHT_FILE_SYMLINK 0x0000000001000000 +#define CLOUDABI_RIGHT_FILE_UNLINK 0x0000000002000000 +#define CLOUDABI_RIGHT_MEM_MAP 0x0000000004000000 +#define CLOUDABI_RIGHT_MEM_MAP_EXEC 0x0000000008000000 +#define CLOUDABI_RIGHT_POLL_FD_READWRITE 0x0000000010000000 +#define CLOUDABI_RIGHT_POLL_MODIFY 0x0000000020000000 +#define CLOUDABI_RIGHT_POLL_PROC_TERMINATE 0x0000000040000000 +#define CLOUDABI_RIGHT_POLL_WAIT 0x0000000080000000 +#define CLOUDABI_RIGHT_PROC_EXEC 0x0000000100000000 +#define CLOUDABI_RIGHT_SOCK_ACCEPT 0x0000000200000000 +#define CLOUDABI_RIGHT_SOCK_BIND_DIRECTORY 0x0000000400000000 +#define CLOUDABI_RIGHT_SOCK_BIND_SOCKET 0x0000000800000000 +#define CLOUDABI_RIGHT_SOCK_CONNECT_DIRECTORY 0x0000001000000000 +#define CLOUDABI_RIGHT_SOCK_CONNECT_SOCKET 0x0000002000000000 +#define CLOUDABI_RIGHT_SOCK_LISTEN 0x0000004000000000 +#define CLOUDABI_RIGHT_SOCK_SHUTDOWN 0x0000008000000000 +#define CLOUDABI_RIGHT_SOCK_STAT_GET 0x0000010000000000 + +typedef uint8_t cloudabi_sa_family_t; +#define CLOUDABI_AF_UNSPEC 0 +#define CLOUDABI_AF_INET 1 +#define CLOUDABI_AF_INET6 2 +#define CLOUDABI_AF_UNIX 3 + +typedef uint8_t cloudabi_scope_t; +#define CLOUDABI_SCOPE_PRIVATE 4 +#define CLOUDABI_SCOPE_SHARED 8 + +typedef uint8_t cloudabi_sdflags_t; +#define CLOUDABI_SHUT_RD 0x01 +#define CLOUDABI_SHUT_WR 0x02 + +typedef uint8_t cloudabi_signal_t; +#define CLOUDABI_SIGABRT 1 +#define CLOUDABI_SIGALRM 2 +#define CLOUDABI_SIGBUS 3 +#define CLOUDABI_SIGCHLD 4 +#define CLOUDABI_SIGCONT 5 +#define CLOUDABI_SIGFPE 6 +#define CLOUDABI_SIGHUP 7 +#define CLOUDABI_SIGILL 8 +#define CLOUDABI_SIGINT 9 +#define CLOUDABI_SIGKILL 10 +#define CLOUDABI_SIGPIPE 11 +#define CLOUDABI_SIGQUIT 12 +#define CLOUDABI_SIGSEGV 13 +#define CLOUDABI_SIGSTOP 14 +#define CLOUDABI_SIGSYS 15 +#define CLOUDABI_SIGTERM 16 +#define CLOUDABI_SIGTRAP 17 +#define CLOUDABI_SIGTSTP 18 +#define CLOUDABI_SIGTTIN 19 +#define CLOUDABI_SIGTTOU 20 +#define CLOUDABI_SIGURG 21 +#define CLOUDABI_SIGUSR1 22 +#define CLOUDABI_SIGUSR2 23 +#define CLOUDABI_SIGVTALRM 24 +#define CLOUDABI_SIGXCPU 25 +#define CLOUDABI_SIGXFSZ 26 + +typedef uint8_t cloudabi_ssflags_t; +#define CLOUDABI_SOCKSTAT_CLEAR_ERROR 0x01 + +typedef uint32_t cloudabi_sstate_t; +#define CLOUDABI_SOCKSTATE_ACCEPTCONN 0x00000001 + +typedef uint16_t cloudabi_subclockflags_t; +#define CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME 0x0001 + +typedef uint16_t cloudabi_subflags_t; +#define CLOUDABI_SUBSCRIPTION_ADD 0x0001 +#define CLOUDABI_SUBSCRIPTION_CLEAR 0x0002 +#define CLOUDABI_SUBSCRIPTION_DELETE 0x0004 +#define CLOUDABI_SUBSCRIPTION_DISABLE 0x0008 +#define CLOUDABI_SUBSCRIPTION_ENABLE 0x0010 +#define CLOUDABI_SUBSCRIPTION_ONESHOT 0x0020 + +typedef uint16_t cloudabi_subrwflags_t; +#define CLOUDABI_SUBSCRIPTION_FD_READWRITE_POLL 0x0001 + +typedef uint32_t cloudabi_tid_t; + +typedef uint64_t cloudabi_timestamp_t; + +typedef uint8_t cloudabi_ulflags_t; +#define CLOUDABI_UNLINK_REMOVEDIR 0x01 + +typedef uint64_t cloudabi_userdata_t; + +typedef uint8_t cloudabi_whence_t; +#define CLOUDABI_WHENCE_CUR 1 +#define CLOUDABI_WHENCE_END 2 +#define CLOUDABI_WHENCE_SET 3 + +typedef struct { + _Alignas(8) cloudabi_dircookie_t d_next; + _Alignas(8) cloudabi_inode_t d_ino; + _Alignas(4) uint32_t d_namlen; + _Alignas(1) cloudabi_filetype_t d_type; +} cloudabi_dirent_t; +_Static_assert(offsetof(cloudabi_dirent_t, d_next) == 0, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_dirent_t, d_ino) == 8, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_dirent_t, d_namlen) == 16, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_dirent_t, d_type) == 20, "Incorrect layout"); +_Static_assert(sizeof(cloudabi_dirent_t) == 24, "Incorrect layout"); +_Static_assert(_Alignof(cloudabi_dirent_t) == 8, "Incorrect layout"); + +typedef struct { + _Alignas(1) cloudabi_filetype_t fs_filetype; + _Alignas(2) cloudabi_fdflags_t fs_flags; + _Alignas(8) cloudabi_rights_t fs_rights_base; + _Alignas(8) cloudabi_rights_t fs_rights_inheriting; +} cloudabi_fdstat_t; +_Static_assert(offsetof(cloudabi_fdstat_t, fs_filetype) == 0, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_fdstat_t, fs_flags) == 2, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_fdstat_t, fs_rights_base) == 8, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_fdstat_t, fs_rights_inheriting) == 16, "Incorrect layout"); +_Static_assert(sizeof(cloudabi_fdstat_t) == 24, "Incorrect layout"); +_Static_assert(_Alignof(cloudabi_fdstat_t) == 8, "Incorrect layout"); + +typedef struct { + _Alignas(8) cloudabi_device_t st_dev; + _Alignas(8) cloudabi_inode_t st_ino; + _Alignas(1) cloudabi_filetype_t st_filetype; + _Alignas(4) cloudabi_linkcount_t st_nlink; + _Alignas(8) cloudabi_filesize_t st_size; + _Alignas(8) cloudabi_timestamp_t st_atim; + _Alignas(8) cloudabi_timestamp_t st_mtim; + _Alignas(8) cloudabi_timestamp_t st_ctim; +} cloudabi_filestat_t; +_Static_assert(offsetof(cloudabi_filestat_t, st_dev) == 0, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_filestat_t, st_ino) == 8, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_filestat_t, st_filetype) == 16, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_filestat_t, st_nlink) == 20, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_filestat_t, st_size) == 24, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_filestat_t, st_atim) == 32, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_filestat_t, st_mtim) == 40, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_filestat_t, st_ctim) == 48, "Incorrect layout"); +_Static_assert(sizeof(cloudabi_filestat_t) == 56, "Incorrect layout"); +_Static_assert(_Alignof(cloudabi_filestat_t) == 8, "Incorrect layout"); + +typedef struct { + _Alignas(4) cloudabi_fd_t fd; + _Alignas(4) cloudabi_lookupflags_t flags; +} cloudabi_lookup_t; +_Static_assert(offsetof(cloudabi_lookup_t, fd) == 0, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_lookup_t, flags) == 4, "Incorrect layout"); +_Static_assert(sizeof(cloudabi_lookup_t) == 8, "Incorrect layout"); +_Static_assert(_Alignof(cloudabi_lookup_t) == 4, "Incorrect layout"); + +typedef struct { + _Alignas(1) cloudabi_sa_family_t sa_family; + union { + struct { + _Alignas(1) uint8_t addr[4]; + _Alignas(2) uint16_t port; + } sa_inet; + struct { + _Alignas(1) uint8_t addr[16]; + _Alignas(2) uint16_t port; + } sa_inet6; + }; +} cloudabi_sockaddr_t; +_Static_assert(offsetof(cloudabi_sockaddr_t, sa_family) == 0, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_sockaddr_t, sa_inet.addr) == 2, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_sockaddr_t, sa_inet.port) == 6, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_sockaddr_t, sa_inet6.addr) == 2, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_sockaddr_t, sa_inet6.port) == 18, "Incorrect layout"); +_Static_assert(sizeof(cloudabi_sockaddr_t) == 20, "Incorrect layout"); +_Static_assert(_Alignof(cloudabi_sockaddr_t) == 2, "Incorrect layout"); + +typedef struct { + _Alignas(2) cloudabi_sockaddr_t ss_sockname; + _Alignas(2) cloudabi_sockaddr_t ss_peername; + _Alignas(2) cloudabi_errno_t ss_error; + _Alignas(4) cloudabi_sstate_t ss_state; +} cloudabi_sockstat_t; +_Static_assert(offsetof(cloudabi_sockstat_t, ss_sockname) == 0, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_sockstat_t, ss_peername) == 20, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_sockstat_t, ss_error) == 40, "Incorrect layout"); +_Static_assert(offsetof(cloudabi_sockstat_t, ss_state) == 44, "Incorrect layout"); +_Static_assert(sizeof(cloudabi_sockstat_t) == 48, "Incorrect layout"); +_Static_assert(_Alignof(cloudabi_sockstat_t) == 4, "Incorrect layout"); + +#endif diff --git a/sys/contrib/cloudabi/syscalldefs_md.h b/sys/contrib/cloudabi/syscalldefs_md.h deleted file mode 100644 index f2e3705..0000000 --- a/sys/contrib/cloudabi/syscalldefs_md.h +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (c) 2015 Nuxi, https://nuxi.nl/ -// -// 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. - -// Machine dependent definitions. - -// Macro to force sane alignment rules. -// -// On x86-32 it is the case that 64-bit integers are 4-byte aligned when -// embedded in structs, even though they are 8-byte aligned when not -// embedded. Force 8-byte alignment explicitly. -#define MEMBER(type) alignas(alignof(type)) type -#define ASSERT_OFFSET(type, field, offset32, offset64) \ - static_assert((sizeof(PTR(void)) == 4 && \ - offsetof(IDENT(type), field) == (offset32)) || \ - (sizeof(PTR(void)) == 8 && \ - offsetof(IDENT(type), field) == (offset64)), \ - "Offset incorrect") -#define ASSERT_SIZE(type, size32, size64) \ - static_assert( \ - (sizeof(PTR(void)) == 4 && sizeof(IDENT(type)) == (size32)) || \ - (sizeof(PTR(void)) == 8 && sizeof(IDENT(type)) == (size64)), \ - "Size incorrect") - -typedef void IDENT(threadentry_t)(cloudabi_tid_t, PTR(void)); - -// Auxiliary vector entry, used to provide paramters on startup. -typedef struct { - uint32_t a_type; - union { - MEMBER(IDENT(size_t)) a_val; - MEMBER(PTR(void)) a_ptr; - }; -} IDENT(auxv_t); -ASSERT_OFFSET(auxv_t, a_type, 0, 0); -ASSERT_OFFSET(auxv_t, a_val, 4, 8); -ASSERT_OFFSET(auxv_t, a_ptr, 4, 8); -ASSERT_SIZE(auxv_t, 8, 16); - -typedef struct { - MEMBER(PTR(const void)) iov_base; - MEMBER(IDENT(size_t)) iov_len; -} IDENT(ciovec_t); -ASSERT_OFFSET(ciovec_t, iov_base, 0, 0); -ASSERT_OFFSET(ciovec_t, iov_len, 4, 8); -ASSERT_SIZE(ciovec_t, 8, 16); - -typedef struct { - MEMBER(cloudabi_userdata_t) userdata; - MEMBER(cloudabi_errno_t) error; - MEMBER(cloudabi_eventtype_t) type; - union { - // CLOUDABI_EVENTTYPE_CLOCK: Wait until the value of a clock - // exceeds a value. - struct { - MEMBER(cloudabi_userdata_t) identifier; - } clock; - - // CLOUDABI_EVENTTYPE_CONDVAR: Release a lock and wait on a - // condition variable. - struct { - MEMBER(PTR(_Atomic(cloudabi_condvar_t))) condvar; - } condvar; - - // CLOUDABI_EVENTTYPE_FD_READ and CLOUDABI_EVENTTYPE_FD_WRITE: - // Wait for a file descriptor to allow read() and write() to be - // called without blocking. - struct { - MEMBER(cloudabi_filesize_t) nbytes; - MEMBER(cloudabi_fd_t) fd; - MEMBER(uint16_t) flags; - } fd_readwrite; - - // CLOUDABI_EVENT_LOCK_RDLOCK and CLOUDABI_EVENT_LOCK_WRLOCK: Wait - // and acquire a read or write lock. - struct { - MEMBER(PTR(_Atomic(cloudabi_lock_t))) lock; - } lock; - - // CLOUDABI_EVENTTYPE_PROC_TERMINATE: Wait for a process to terminate. - struct { - MEMBER(cloudabi_fd_t) fd; - MEMBER(cloudabi_signal_t) signal; // Non-zero if process got killed. - MEMBER(cloudabi_exitcode_t) exitcode; // Exit code. - } proc_terminate; - }; -} IDENT(event_t); -ASSERT_OFFSET(event_t, userdata, 0, 0); -ASSERT_OFFSET(event_t, error, 8, 8); -ASSERT_OFFSET(event_t, type, 10, 10); -ASSERT_OFFSET(event_t, clock.identifier, 16, 16); -ASSERT_OFFSET(event_t, condvar.condvar, 16, 16); -ASSERT_OFFSET(event_t, fd_readwrite.nbytes, 16, 16); -ASSERT_OFFSET(event_t, fd_readwrite.fd, 24, 24); -ASSERT_OFFSET(event_t, fd_readwrite.flags, 28, 28); -ASSERT_OFFSET(event_t, lock.lock, 16, 16); -ASSERT_OFFSET(event_t, proc_terminate.fd, 16, 16); -ASSERT_OFFSET(event_t, proc_terminate.signal, 20, 20); -ASSERT_OFFSET(event_t, proc_terminate.exitcode, 24, 24); -ASSERT_SIZE(event_t, 32, 32); - -typedef struct { - MEMBER(PTR(void)) iov_base; - MEMBER(IDENT(size_t)) iov_len; -} IDENT(iovec_t); -ASSERT_OFFSET(iovec_t, iov_base, 0, 0); -ASSERT_OFFSET(iovec_t, iov_len, 4, 8); -ASSERT_SIZE(iovec_t, 8, 16); - -typedef struct { - MEMBER(PTR(const IDENT(iovec_t))) ri_data; // Data I/O vectors. - MEMBER(IDENT(size_t)) ri_datalen; // Number of data I/O vectors. - MEMBER(PTR(cloudabi_fd_t)) ri_fds; // File descriptors. - MEMBER(IDENT(size_t)) ri_fdslen; // Number of file descriptors. - MEMBER(cloudabi_msgflags_t) ri_flags; // Input flags. -} IDENT(recv_in_t); -ASSERT_OFFSET(recv_in_t, ri_data, 0, 0); -ASSERT_OFFSET(recv_in_t, ri_datalen, 4, 8); -ASSERT_OFFSET(recv_in_t, ri_fds, 8, 16); -ASSERT_OFFSET(recv_in_t, ri_fdslen, 12, 24); -ASSERT_OFFSET(recv_in_t, ri_flags, 16, 32); -ASSERT_SIZE(recv_in_t, 20, 40); - -typedef struct { - MEMBER(IDENT(size_t)) ro_datalen; // Bytes of data received. - MEMBER(IDENT(size_t)) ro_fdslen; // Number of file descriptors received. - MEMBER(cloudabi_sockaddr_t) ro_sockname; // Address of receiver. - MEMBER(cloudabi_sockaddr_t) ro_peername; // Address of sender. - MEMBER(cloudabi_msgflags_t) ro_flags; // Output flags. -} IDENT(recv_out_t); -ASSERT_OFFSET(recv_out_t, ro_datalen, 0, 0); -ASSERT_OFFSET(recv_out_t, ro_fdslen, 4, 8); -ASSERT_OFFSET(recv_out_t, ro_sockname, 8, 16); -ASSERT_OFFSET(recv_out_t, ro_peername, 28, 36); -ASSERT_OFFSET(recv_out_t, ro_flags, 48, 56); -ASSERT_SIZE(recv_out_t, 52, 64); - -typedef struct { - MEMBER(PTR(const IDENT(ciovec_t))) si_data; // Data I/O vectors. - MEMBER(IDENT(size_t)) si_datalen; // Number of data I/O vectors. - MEMBER(PTR(const cloudabi_fd_t)) si_fds; // File descriptors. - MEMBER(IDENT(size_t)) si_fdslen; // Number of file descriptors. - MEMBER(cloudabi_msgflags_t) si_flags; // Input flags. -} IDENT(send_in_t); -ASSERT_OFFSET(send_in_t, si_data, 0, 0); -ASSERT_OFFSET(send_in_t, si_datalen, 4, 8); -ASSERT_OFFSET(send_in_t, si_fds, 8, 16); -ASSERT_OFFSET(send_in_t, si_fdslen, 12, 24); -ASSERT_OFFSET(send_in_t, si_flags, 16, 32); -ASSERT_SIZE(send_in_t, 20, 40); - -typedef struct { - MEMBER(IDENT(size_t)) so_datalen; // Bytes of data sent. -} IDENT(send_out_t); -ASSERT_OFFSET(send_out_t, so_datalen, 0, 0); -ASSERT_SIZE(send_out_t, 4, 8); - -typedef struct { - MEMBER(cloudabi_userdata_t) userdata; - MEMBER(uint16_t) flags; - MEMBER(cloudabi_eventtype_t) type; - union { - // CLOUDABI_EVENTTYPE_CLOCK: Wait until the value of a clock - // exceeds a value. - struct { - MEMBER(cloudabi_userdata_t) identifier; - MEMBER(cloudabi_clockid_t) clock_id; - MEMBER(cloudabi_timestamp_t) timeout; - MEMBER(cloudabi_timestamp_t) precision; - MEMBER(uint16_t) flags; - } clock; - - // CLOUDABI_EVENTTYPE_CONDVAR: Release a lock and wait on a - // condition variable. - struct { - MEMBER(PTR(_Atomic(cloudabi_condvar_t))) condvar; - MEMBER(PTR(_Atomic(cloudabi_lock_t))) lock; - MEMBER(cloudabi_mflags_t) condvar_scope; - MEMBER(cloudabi_mflags_t) lock_scope; - } condvar; - - // CLOUDABI_EVENTTYPE_FD_READ and CLOUDABI_EVENTTYPE_FD_WRITE: - // Wait for a file descriptor to allow read() and write() to be - // called without blocking. - struct { - MEMBER(cloudabi_fd_t) fd; - MEMBER(uint16_t) flags; - } fd_readwrite; - - // CLOUDABI_EVENT_LOCK_RDLOCK and CLOUDABI_EVENT_LOCK_WRLOCK: Wait - // and acquire a read or write lock. - struct { - MEMBER(PTR(_Atomic(cloudabi_lock_t))) lock; - MEMBER(cloudabi_mflags_t) lock_scope; - } lock; - - // CLOUDABI_EVENTTYPE_PROC_TERMINATE: Wait for a process to terminate. - struct { - MEMBER(cloudabi_fd_t) fd; - } proc_terminate; - }; -} IDENT(subscription_t); -ASSERT_OFFSET(subscription_t, userdata, 0, 0); -ASSERT_OFFSET(subscription_t, flags, 8, 8); -ASSERT_OFFSET(subscription_t, type, 10, 10); -ASSERT_OFFSET(subscription_t, clock.identifier, 16, 16); -ASSERT_OFFSET(subscription_t, clock.clock_id, 24, 24); -ASSERT_OFFSET(subscription_t, clock.timeout, 32, 32); -ASSERT_OFFSET(subscription_t, clock.precision, 40, 40); -ASSERT_OFFSET(subscription_t, clock.flags, 48, 48); -ASSERT_OFFSET(subscription_t, condvar.condvar, 16, 16); -ASSERT_OFFSET(subscription_t, condvar.lock, 20, 24); -ASSERT_OFFSET(subscription_t, condvar.condvar_scope, 24, 32); -ASSERT_OFFSET(subscription_t, condvar.lock_scope, 25, 33); -ASSERT_OFFSET(subscription_t, fd_readwrite.fd, 16, 16); -ASSERT_OFFSET(subscription_t, fd_readwrite.flags, 20, 20); -ASSERT_OFFSET(subscription_t, lock.lock, 16, 16); -ASSERT_OFFSET(subscription_t, lock.lock_scope, 20, 24); -ASSERT_OFFSET(subscription_t, proc_terminate.fd, 16, 16); -ASSERT_SIZE(subscription_t, 56, 56); - -typedef struct { - MEMBER(PTR(IDENT(threadentry_t))) entry_point; // Entry point. - MEMBER(PTR(void)) stack; // Pointer to stack buffer. - MEMBER(IDENT(size_t)) stack_size; // Size of stack buffer. - MEMBER(PTR(void)) argument; // Argument to be passed to entry point. -} IDENT(threadattr_t); -ASSERT_OFFSET(threadattr_t, entry_point, 0, 0); -ASSERT_OFFSET(threadattr_t, stack, 4, 8); -ASSERT_OFFSET(threadattr_t, stack_size, 8, 16); -ASSERT_OFFSET(threadattr_t, argument, 12, 24); -ASSERT_SIZE(threadattr_t, 16, 32); - -#undef MEMBER -#undef ASSERT_OFFSET -#undef ASSERT_SIZE diff --git a/sys/contrib/cloudabi/syscalldefs_mi.h b/sys/contrib/cloudabi/syscalldefs_mi.h deleted file mode 100644 index adce208..0000000 --- a/sys/contrib/cloudabi/syscalldefs_mi.h +++ /dev/null @@ -1,476 +0,0 @@ -// Copyright (c) 2015 Nuxi, https://nuxi.nl/ -// -// 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. - -#ifndef COMMON_SYSCALLDEFS_MI_H -#define COMMON_SYSCALLDEFS_MI_H - -// Machine independent definitions. - -// Socket address families. -#define CLOUDABI_AF_UNSPEC 0 -#define CLOUDABI_AF_INET 1 -#define CLOUDABI_AF_INET6 2 -#define CLOUDABI_AF_UNIX 3 - -// File and memory I/O advice. -#define CLOUDABI_ADVICE_DONTNEED 1 -#define CLOUDABI_ADVICE_NOREUSE 2 -#define CLOUDABI_ADVICE_NORMAL 3 -#define CLOUDABI_ADVICE_RANDOM 4 -#define CLOUDABI_ADVICE_SEQUENTIAL 5 -#define CLOUDABI_ADVICE_WILLNEED 6 - -// Auxiliary vector entries. All entries that are also part of the -// x86-64 ABI use the same number. All extensions start at 256. -#define CLOUDABI_AT_ARGDATA 256 -#define CLOUDABI_AT_ARGDATALEN 257 -#define CLOUDABI_AT_CANARY 258 -#define CLOUDABI_AT_CANARYLEN 259 -#define CLOUDABI_AT_NCPUS 260 -#define CLOUDABI_AT_NULL 0 -#define CLOUDABI_AT_PAGESZ 6 -#define CLOUDABI_AT_PHDR 3 -#define CLOUDABI_AT_PHNUM 4 -#define CLOUDABI_AT_TID 261 - -// Clocks. -#define CLOUDABI_CLOCK_MONOTONIC 1 -#define CLOUDABI_CLOCK_PROCESS_CPUTIME_ID 2 -#define CLOUDABI_CLOCK_REALTIME 3 -#define CLOUDABI_CLOCK_THREAD_CPUTIME_ID 4 - -// Condition variables. -#define CLOUDABI_CONDVAR_HAS_NO_WAITERS 0 - -// The start of a directory, to be passed to readdir(). -#define CLOUDABI_DIRCOOKIE_START 0 - -// POSIX standard error numbers. -#define CLOUDABI_E2BIG 1 -#define CLOUDABI_EACCES 2 -#define CLOUDABI_EADDRINUSE 3 -#define CLOUDABI_EADDRNOTAVAIL 4 -#define CLOUDABI_EAFNOSUPPORT 5 -#define CLOUDABI_EAGAIN 6 -#define CLOUDABI_EALREADY 7 -#define CLOUDABI_EBADF 8 -#define CLOUDABI_EBADMSG 9 -#define CLOUDABI_EBUSY 10 -#define CLOUDABI_ECANCELED 11 -#define CLOUDABI_ECHILD 12 -#define CLOUDABI_ECONNABORTED 13 -#define CLOUDABI_ECONNREFUSED 14 -#define CLOUDABI_ECONNRESET 15 -#define CLOUDABI_EDEADLK 16 -#define CLOUDABI_EDESTADDRREQ 17 -#define CLOUDABI_EDOM 18 -#define CLOUDABI_EDQUOT 19 -#define CLOUDABI_EEXIST 20 -#define CLOUDABI_EFAULT 21 -#define CLOUDABI_EFBIG 22 -#define CLOUDABI_EHOSTUNREACH 23 -#define CLOUDABI_EIDRM 24 -#define CLOUDABI_EILSEQ 25 -#define CLOUDABI_EINPROGRESS 26 -#define CLOUDABI_EINTR 27 -#define CLOUDABI_EINVAL 28 -#define CLOUDABI_EIO 29 -#define CLOUDABI_EISCONN 30 -#define CLOUDABI_EISDIR 31 -#define CLOUDABI_ELOOP 32 -#define CLOUDABI_EMFILE 33 -#define CLOUDABI_EMLINK 34 -#define CLOUDABI_EMSGSIZE 35 -#define CLOUDABI_EMULTIHOP 36 -#define CLOUDABI_ENAMETOOLONG 37 -#define CLOUDABI_ENETDOWN 38 -#define CLOUDABI_ENETRESET 39 -#define CLOUDABI_ENETUNREACH 40 -#define CLOUDABI_ENFILE 41 -#define CLOUDABI_ENOBUFS 42 -#define CLOUDABI_ENODEV 43 -#define CLOUDABI_ENOENT 44 -#define CLOUDABI_ENOEXEC 45 -#define CLOUDABI_ENOLCK 46 -#define CLOUDABI_ENOLINK 47 -#define CLOUDABI_ENOMEM 48 -#define CLOUDABI_ENOMSG 49 -#define CLOUDABI_ENOPROTOOPT 50 -#define CLOUDABI_ENOSPC 51 -#define CLOUDABI_ENOSYS 52 -#define CLOUDABI_ENOTCONN 53 -#define CLOUDABI_ENOTDIR 54 -#define CLOUDABI_ENOTEMPTY 55 -#define CLOUDABI_ENOTRECOVERABLE 56 -#define CLOUDABI_ENOTSOCK 57 -#define CLOUDABI_ENOTSUP 58 -#define CLOUDABI_ENOTTY 59 -#define CLOUDABI_ENXIO 60 -#define CLOUDABI_EOVERFLOW 61 -#define CLOUDABI_EOWNERDEAD 62 -#define CLOUDABI_EPERM 63 -#define CLOUDABI_EPIPE 64 -#define CLOUDABI_EPROTO 65 -#define CLOUDABI_EPROTONOSUPPORT 66 -#define CLOUDABI_EPROTOTYPE 67 -#define CLOUDABI_ERANGE 68 -#define CLOUDABI_EROFS 69 -#define CLOUDABI_ESPIPE 70 -#define CLOUDABI_ESRCH 71 -#define CLOUDABI_ESTALE 72 -#define CLOUDABI_ETIMEDOUT 73 -#define CLOUDABI_ETXTBSY 74 -#define CLOUDABI_EXDEV 75 - -// Non-standard error numbers. -#define CLOUDABI_ENOTCAPABLE 76 - -#define CLOUDABI_EVENT_FD_READWRITE_HANGUP 0x1 - -// Filter types for cloudabi_eventtype_t. -#define CLOUDABI_EVENTTYPE_CLOCK 1 -#define CLOUDABI_EVENTTYPE_CONDVAR 2 -#define CLOUDABI_EVENTTYPE_FD_READ 3 -#define CLOUDABI_EVENTTYPE_FD_WRITE 4 -#define CLOUDABI_EVENTTYPE_LOCK_RDLOCK 5 -#define CLOUDABI_EVENTTYPE_LOCK_WRLOCK 6 -#define CLOUDABI_EVENTTYPE_PROC_TERMINATE 7 - -// File descriptor behavior flags. -#define CLOUDABI_FDFLAG_APPEND 0x1 -#define CLOUDABI_FDFLAG_DSYNC 0x2 -#define CLOUDABI_FDFLAG_NONBLOCK 0x4 -#define CLOUDABI_FDFLAG_RSYNC 0x8 -#define CLOUDABI_FDFLAG_SYNC 0x10 - -// fdstat_put() flags. -#define CLOUDABI_FDSTAT_FLAGS 0x1 -#define CLOUDABI_FDSTAT_RIGHTS 0x2 - -// filestat_put() flags. -#define CLOUDABI_FILESTAT_ATIM 0x1 -#define CLOUDABI_FILESTAT_ATIM_NOW 0x2 -#define CLOUDABI_FILESTAT_MTIM 0x4 -#define CLOUDABI_FILESTAT_MTIM_NOW 0x8 -#define CLOUDABI_FILESTAT_SIZE 0x10 - -// File types returned through struct stat::st_mode. -#define CLOUDABI_FILETYPE_UNKNOWN 0 -#define CLOUDABI_FILETYPE_BLOCK_DEVICE 0x10 -#define CLOUDABI_FILETYPE_CHARACTER_DEVICE 0x11 -#define CLOUDABI_FILETYPE_DIRECTORY 0x20 -#define CLOUDABI_FILETYPE_FIFO 0x30 -#define CLOUDABI_FILETYPE_POLL 0x40 -#define CLOUDABI_FILETYPE_PROCESS 0x50 -#define CLOUDABI_FILETYPE_REGULAR_FILE 0x60 -#define CLOUDABI_FILETYPE_SHARED_MEMORY 0x70 -#define CLOUDABI_FILETYPE_SOCKET_DGRAM 0x80 -#define CLOUDABI_FILETYPE_SOCKET_SEQPACKET 0x81 -#define CLOUDABI_FILETYPE_SOCKET_STREAM 0x82 -#define CLOUDABI_FILETYPE_SYMBOLIC_LINK 0x90 - -// Read-write lock related constants. -#define CLOUDABI_LOCK_UNLOCKED 0 // Lock is unlocked. -#define CLOUDABI_LOCK_WRLOCKED 0x40000000 // Lock is write locked. -#define CLOUDABI_LOCK_KERNEL_MANAGED 0x80000000 // Lock has waiters. -#define CLOUDABI_LOCK_BOGUS 0x80000000 // Lock is broken. - -// Lookup properties for *at() functions. -#define CLOUDABI_LOOKUP_SYMLINK_FOLLOW (UINT64_C(0x1) << 32) - -// Open flags for openat(), etc. -#define CLOUDABI_O_CREAT 0x1 -#define CLOUDABI_O_DIRECTORY 0x2 -#define CLOUDABI_O_EXCL 0x4 -#define CLOUDABI_O_TRUNC 0x8 - -// File descriptor returned to pdfork()'s child process. -#define CLOUDABI_PROCESS_CHILD 0xffffffff - -// mmap() map flags. -#define CLOUDABI_MAP_ANON 0x1 -#define CLOUDABI_MAP_FIXED 0x2 -#define CLOUDABI_MAP_PRIVATE 0x4 -#define CLOUDABI_MAP_SHARED 0x8 - -// File descriptor that must be passed in when using CLOUDABI_MAP_ANON. -#define CLOUDABI_MAP_ANON_FD 0xffffffff - -// msync() flags. -#define CLOUDABI_MS_ASYNC 0x1 -#define CLOUDABI_MS_INVALIDATE 0x2 -#define CLOUDABI_MS_SYNC 0x4 - -// send() and recv() flags. -#define CLOUDABI_MSG_CTRUNC 0x1 // Control data truncated. -#define CLOUDABI_MSG_EOR 0x2 // Terminates a record. -#define CLOUDABI_MSG_PEEK 0x4 // Leave received data in queue. -#define CLOUDABI_MSG_TRUNC 0x8 // Normal data truncated. -#define CLOUDABI_MSG_WAITALL 0x10 // Attempt to fill the read buffer. - -// mmap()/mprotect() protection flags. -#define CLOUDABI_PROT_EXEC 0x1 -#define CLOUDABI_PROT_WRITE 0x2 -#define CLOUDABI_PROT_READ 0x4 - -// File descriptor capabilities/rights. -#define CLOUDABI_RIGHT_BIT(bit) (UINT64_C(1) << (bit)) -#define CLOUDABI_RIGHT_FD_DATASYNC CLOUDABI_RIGHT_BIT(0) -#define CLOUDABI_RIGHT_FD_READ CLOUDABI_RIGHT_BIT(1) -#define CLOUDABI_RIGHT_FD_SEEK CLOUDABI_RIGHT_BIT(2) -#define CLOUDABI_RIGHT_FD_STAT_PUT_FLAGS CLOUDABI_RIGHT_BIT(3) -#define CLOUDABI_RIGHT_FD_SYNC CLOUDABI_RIGHT_BIT(4) -#define CLOUDABI_RIGHT_FD_TELL CLOUDABI_RIGHT_BIT(5) -#define CLOUDABI_RIGHT_FD_WRITE CLOUDABI_RIGHT_BIT(6) -#define CLOUDABI_RIGHT_FILE_ADVISE CLOUDABI_RIGHT_BIT(7) -#define CLOUDABI_RIGHT_FILE_ALLOCATE CLOUDABI_RIGHT_BIT(8) -#define CLOUDABI_RIGHT_FILE_CREATE_DIRECTORY CLOUDABI_RIGHT_BIT(9) -#define CLOUDABI_RIGHT_FILE_CREATE_FILE CLOUDABI_RIGHT_BIT(10) -#define CLOUDABI_RIGHT_FILE_CREATE_FIFO CLOUDABI_RIGHT_BIT(11) -#define CLOUDABI_RIGHT_FILE_LINK_SOURCE CLOUDABI_RIGHT_BIT(12) -#define CLOUDABI_RIGHT_FILE_LINK_TARGET CLOUDABI_RIGHT_BIT(13) -#define CLOUDABI_RIGHT_FILE_OPEN CLOUDABI_RIGHT_BIT(14) -#define CLOUDABI_RIGHT_FILE_READDIR CLOUDABI_RIGHT_BIT(15) -#define CLOUDABI_RIGHT_FILE_READLINK CLOUDABI_RIGHT_BIT(16) -#define CLOUDABI_RIGHT_FILE_RENAME_SOURCE CLOUDABI_RIGHT_BIT(17) -#define CLOUDABI_RIGHT_FILE_RENAME_TARGET CLOUDABI_RIGHT_BIT(18) -#define CLOUDABI_RIGHT_FILE_STAT_FGET CLOUDABI_RIGHT_BIT(19) -#define CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE CLOUDABI_RIGHT_BIT(20) -#define CLOUDABI_RIGHT_FILE_STAT_FPUT_TIMES CLOUDABI_RIGHT_BIT(21) -#define CLOUDABI_RIGHT_FILE_STAT_GET CLOUDABI_RIGHT_BIT(22) -#define CLOUDABI_RIGHT_FILE_STAT_PUT_TIMES CLOUDABI_RIGHT_BIT(23) -#define CLOUDABI_RIGHT_FILE_SYMLINK CLOUDABI_RIGHT_BIT(24) -#define CLOUDABI_RIGHT_FILE_UNLINK CLOUDABI_RIGHT_BIT(25) -#define CLOUDABI_RIGHT_MEM_MAP CLOUDABI_RIGHT_BIT(26) -#define CLOUDABI_RIGHT_MEM_MAP_EXEC CLOUDABI_RIGHT_BIT(27) -#define CLOUDABI_RIGHT_POLL_FD_READWRITE CLOUDABI_RIGHT_BIT(28) -#define CLOUDABI_RIGHT_POLL_MODIFY CLOUDABI_RIGHT_BIT(29) -#define CLOUDABI_RIGHT_POLL_PROC_TERMINATE CLOUDABI_RIGHT_BIT(30) -#define CLOUDABI_RIGHT_POLL_WAIT CLOUDABI_RIGHT_BIT(31) -#define CLOUDABI_RIGHT_PROC_EXEC CLOUDABI_RIGHT_BIT(32) -#define CLOUDABI_RIGHT_SOCK_ACCEPT CLOUDABI_RIGHT_BIT(33) -#define CLOUDABI_RIGHT_SOCK_BIND_DIRECTORY CLOUDABI_RIGHT_BIT(34) -#define CLOUDABI_RIGHT_SOCK_BIND_SOCKET CLOUDABI_RIGHT_BIT(35) -#define CLOUDABI_RIGHT_SOCK_CONNECT_DIRECTORY CLOUDABI_RIGHT_BIT(36) -#define CLOUDABI_RIGHT_SOCK_CONNECT_SOCKET CLOUDABI_RIGHT_BIT(37) -#define CLOUDABI_RIGHT_SOCK_LISTEN CLOUDABI_RIGHT_BIT(38) -#define CLOUDABI_RIGHT_SOCK_SHUTDOWN CLOUDABI_RIGHT_BIT(39) -#define CLOUDABI_RIGHT_SOCK_STAT_GET CLOUDABI_RIGHT_BIT(40) - -// Socket shutdown flags. -#define CLOUDABI_SHUT_RD 0x1 -#define CLOUDABI_SHUT_WR 0x2 - -// Signals. -#define CLOUDABI_SIGABRT 1 -#define CLOUDABI_SIGALRM 2 -#define CLOUDABI_SIGBUS 3 -#define CLOUDABI_SIGCHLD 4 -#define CLOUDABI_SIGCONT 5 -#define CLOUDABI_SIGFPE 6 -#define CLOUDABI_SIGHUP 7 -#define CLOUDABI_SIGILL 8 -#define CLOUDABI_SIGINT 9 -#define CLOUDABI_SIGKILL 10 -#define CLOUDABI_SIGPIPE 11 -#define CLOUDABI_SIGQUIT 12 -#define CLOUDABI_SIGSEGV 13 -#define CLOUDABI_SIGSTOP 14 -#define CLOUDABI_SIGSYS 15 -#define CLOUDABI_SIGTERM 16 -#define CLOUDABI_SIGTRAP 17 -#define CLOUDABI_SIGTSTP 18 -#define CLOUDABI_SIGTTIN 19 -#define CLOUDABI_SIGTTOU 20 -#define CLOUDABI_SIGURG 21 -#define CLOUDABI_SIGUSR1 22 -#define CLOUDABI_SIGUSR2 23 -#define CLOUDABI_SIGVTALRM 24 -#define CLOUDABI_SIGXCPU 25 -#define CLOUDABI_SIGXFSZ 26 - -// sockstat() flags. -#define CLOUDABI_SOCKSTAT_CLEAR_ERROR 0x1 - -// sockstat() state. -#define CLOUDABI_SOCKSTAT_ACCEPTCONN 0x1 - -// cloudabi_subscription_t flags. -#define CLOUDABI_SUBSCRIPTION_ADD 0x1 -#define CLOUDABI_SUBSCRIPTION_CLEAR 0x2 -#define CLOUDABI_SUBSCRIPTION_DELETE 0x4 -#define CLOUDABI_SUBSCRIPTION_DISABLE 0x8 -#define CLOUDABI_SUBSCRIPTION_ENABLE 0x10 -#define CLOUDABI_SUBSCRIPTION_ONESHOT 0x20 - -// cloudabi_subscription_t::clock.flags. -#define CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME 0x1 - -// cloudabi_subscription_t::fd_readwrite.flags. -#define CLOUDABI_SUBSCRIPTION_FD_READWRITE_POLL 0x1 - -// unlinkat(). -#define CLOUDABI_UNLINK_REMOVEDIR 0x1 - -// Seeking. -#define CLOUDABI_WHENCE_CUR 1 -#define CLOUDABI_WHENCE_END 2 -#define CLOUDABI_WHENCE_SET 3 - -typedef uint8_t cloudabi_advice_t; // posix_fadvise() and posix_madvise(). -typedef uint32_t cloudabi_backlog_t; // listen(). -typedef uint32_t cloudabi_clockid_t; // clock_*(). -typedef uint32_t cloudabi_condvar_t; // pthread_cond_*(). -typedef uint64_t cloudabi_device_t; // struct stat::st_dev. -typedef uint64_t cloudabi_dircookie_t; // readdir(). -typedef uint16_t cloudabi_errno_t; // errno. -typedef uint8_t cloudabi_eventtype_t; // poll(). -typedef uint32_t cloudabi_exitcode_t; // _exit() and _Exit(). -typedef uint32_t cloudabi_fd_t; // File descriptors. -typedef uint16_t cloudabi_fdflags_t; // cloudabi_fdstat_t. -typedef uint16_t cloudabi_fdsflags_t; // fd_stat_put(). -typedef int64_t cloudabi_filedelta_t; // lseek(). -typedef uint64_t cloudabi_filesize_t; // ftruncate(), struct stat::st_size. -typedef uint8_t cloudabi_filetype_t; // struct stat::st_mode. -typedef uint16_t cloudabi_fsflags_t; // file_stat_put(). -typedef uint64_t cloudabi_inode_t; // struct stat::st_ino. -typedef uint32_t cloudabi_linkcount_t; // struct stat::st_nlink. -typedef uint32_t cloudabi_lock_t; // pthread_{mutex,rwlock}_*(). -typedef uint64_t cloudabi_lookup_t; // openat(), linkat(), etc. -typedef uint8_t cloudabi_mflags_t; // mmap(). -typedef uint8_t cloudabi_mprot_t; // mmap(). -typedef uint8_t cloudabi_msflags_t; // msync(). -typedef uint16_t cloudabi_msgflags_t; // send() and recv(). -typedef uint32_t cloudabi_nthreads_t; // pthread_cond_*(). -typedef uint16_t cloudabi_oflags_t; // openat(), etc. -typedef uint64_t cloudabi_rights_t; // File descriptor rights. -typedef uint8_t cloudabi_sa_family_t; // Socket address family. -typedef uint8_t cloudabi_sdflags_t; // shutdown(). -typedef uint8_t cloudabi_ssflags_t; // sockstat(). -typedef uint8_t cloudabi_signal_t; // raise(). -typedef uint32_t cloudabi_tid_t; // Thread ID. -typedef uint64_t cloudabi_timestamp_t; // clock_*(), struct stat::st_*tim. -typedef uint8_t cloudabi_ulflags_t; // unlinkat(). -typedef uint64_t cloudabi_userdata_t; // User-supplied data for callbacks. -typedef uint8_t cloudabi_whence_t; // lseek(). - -// Macro to force sane alignment rules. -// -// On x86-32 it is the case that 64-bit integers are 4-byte aligned when -// embedded in structs, even though they are 8-byte aligned when not -// embedded. Force 8-byte alignment explicitly. -#define MEMBER(type) alignas(alignof(type)) type -#define ASSERT_OFFSET(type, field, offset) \ - static_assert(offsetof(cloudabi_##type, field) == (offset), \ - "Offset incorrect") -#define ASSERT_SIZE(type, size) \ - static_assert(sizeof(cloudabi_##type) == (size), "Size incorrect") - -// Directory entries. -typedef struct { - MEMBER(cloudabi_dircookie_t) d_next; // Cookie of the next entry. - MEMBER(cloudabi_inode_t) d_ino; // Inode number of the current entry. - MEMBER(uint32_t) d_namlen; // Length of the name of the current entry. - MEMBER(cloudabi_filetype_t) d_type; // File type of the current entry. -} cloudabi_dirent_t; -ASSERT_OFFSET(dirent_t, d_next, 0); -ASSERT_OFFSET(dirent_t, d_ino, 8); -ASSERT_OFFSET(dirent_t, d_namlen, 16); -ASSERT_OFFSET(dirent_t, d_type, 20); -ASSERT_SIZE(dirent_t, 24); - -// File descriptor status. -typedef struct { - MEMBER(cloudabi_filetype_t) fs_filetype; // File descriptor type. - MEMBER(cloudabi_fdflags_t) fs_flags; // Non-blocking mode, etc. - MEMBER(cloudabi_rights_t) fs_rights_base; // Base rights. - MEMBER(cloudabi_rights_t) fs_rights_inheriting; // Inheriting rights. -} cloudabi_fdstat_t; -ASSERT_OFFSET(fdstat_t, fs_filetype, 0); -ASSERT_OFFSET(fdstat_t, fs_flags, 2); -ASSERT_OFFSET(fdstat_t, fs_rights_base, 8); -ASSERT_OFFSET(fdstat_t, fs_rights_inheriting, 16); -ASSERT_SIZE(fdstat_t, 24); - -// File status. -typedef struct { - MEMBER(cloudabi_device_t) st_dev; // Device storing the file. - MEMBER(cloudabi_inode_t) st_ino; // Inode of the file. - MEMBER(cloudabi_filetype_t) st_filetype; // File type. - MEMBER(cloudabi_linkcount_t) st_nlink; // Number of hardlinks. - MEMBER(cloudabi_filesize_t) st_size; // Size of the file. - MEMBER(cloudabi_timestamp_t) st_atim; // Access time. - MEMBER(cloudabi_timestamp_t) st_mtim; // Modification time. - MEMBER(cloudabi_timestamp_t) st_ctim; // Change time. -} cloudabi_filestat_t; -ASSERT_OFFSET(filestat_t, st_dev, 0); -ASSERT_OFFSET(filestat_t, st_ino, 8); -ASSERT_OFFSET(filestat_t, st_filetype, 16); -ASSERT_OFFSET(filestat_t, st_nlink, 20); -ASSERT_OFFSET(filestat_t, st_size, 24); -ASSERT_OFFSET(filestat_t, st_atim, 32); -ASSERT_OFFSET(filestat_t, st_mtim, 40); -ASSERT_OFFSET(filestat_t, st_ctim, 48); -ASSERT_SIZE(filestat_t, 56); - -typedef struct { - MEMBER(cloudabi_sa_family_t) sa_family; - union { - struct { - // IPv4 address and port number. - MEMBER(uint8_t) addr[4]; - MEMBER(uint16_t) port; - } sa_inet; - struct { - // IPv6 address and port number. - // TODO(ed): What about the flow info and scope ID? - MEMBER(uint8_t) addr[16]; - MEMBER(uint16_t) port; - } sa_inet6; - }; -} cloudabi_sockaddr_t; -ASSERT_OFFSET(sockaddr_t, sa_family, 0); -ASSERT_OFFSET(sockaddr_t, sa_inet.addr, 2); -ASSERT_OFFSET(sockaddr_t, sa_inet.port, 6); -ASSERT_OFFSET(sockaddr_t, sa_inet6.addr, 2); -ASSERT_OFFSET(sockaddr_t, sa_inet6.port, 18); -ASSERT_SIZE(sockaddr_t, 20); - -// Socket status. -typedef struct { - MEMBER(cloudabi_sockaddr_t) ss_sockname; // Socket address. - MEMBER(cloudabi_sockaddr_t) ss_peername; // Peer address. - MEMBER(cloudabi_errno_t) ss_error; // Current error state. - MEMBER(uint32_t) ss_state; // State flags. -} cloudabi_sockstat_t; -ASSERT_OFFSET(sockstat_t, ss_sockname, 0); -ASSERT_OFFSET(sockstat_t, ss_peername, 20); -ASSERT_OFFSET(sockstat_t, ss_error, 40); -ASSERT_OFFSET(sockstat_t, ss_state, 44); -ASSERT_SIZE(sockstat_t, 48); - -#undef MEMBER -#undef ASSERT_OFFSET -#undef ASSERT_SIZE - -#endif diff --git a/sys/compat/cloudabi64/syscalls.master b/sys/contrib/cloudabi/syscalls.master index 5fd6f7e..7da55c5 100644 --- a/sys/compat/cloudabi64/syscalls.master +++ b/sys/contrib/cloudabi/syscalls.master @@ -1,19 +1,43 @@ $FreeBSD$ -; System call table for CloudABI. +; Copyright (c) 2016 Nuxi (https://nuxi.nl/) and contributors. ; -; All system calls that do not use any machine-dependent data types are -; prefixed with cloudabi_sys_. The others are called cloudabi64_sys_. +; 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. +; +; This file is automatically generated. Do not edit. +; +; Source: https://github.com/NuxiNL/cloudabi #include <sys/sysent.h> #include <sys/sysproto.h> -#include <compat/cloudabi64/cloudabi64_syscalldefs.h> +#include <contrib/cloudabi/cloudabi64_types.h> + #include <compat/cloudabi64/cloudabi64_proto.h> 0 AUE_NULL STD { cloudabi_timestamp_t \ cloudabi_sys_clock_res_get( \ cloudabi_clockid_t clock_id); } + 1 AUE_NULL STD { cloudabi_timestamp_t \ cloudabi_sys_clock_time_get( \ cloudabi_clockid_t clock_id, \ @@ -21,200 +45,274 @@ 2 AUE_NULL STD { void cloudabi_sys_condvar_signal( \ cloudabi_condvar_t *condvar, \ - cloudabi_mflags_t scope, \ + cloudabi_scope_t scope, \ cloudabi_nthreads_t nwaiters); } 3 AUE_NULL STD { void cloudabi_sys_fd_close( \ cloudabi_fd_t fd); } + 4 AUE_NULL STD { cloudabi_fd_t cloudabi_sys_fd_create1( \ cloudabi_filetype_t type); } + 5 AUE_NULL STD { void cloudabi_sys_fd_create2( \ cloudabi_filetype_t type); } + 6 AUE_NULL STD { void cloudabi_sys_fd_datasync( \ cloudabi_fd_t fd); } + 7 AUE_NULL STD { cloudabi_fd_t cloudabi_sys_fd_dup( \ cloudabi_fd_t from); } -8 AUE_NULL STD { cloudabi64_size_t cloudabi64_sys_fd_pread( \ + +8 AUE_NULL STD { size_t cloudabi64_sys_fd_pread( \ cloudabi_fd_t fd, \ const cloudabi64_iovec_t *iov, \ - cloudabi64_size_t iovcnt, \ + size_t iovcnt, \ cloudabi_filesize_t offset); } -9 AUE_NULL STD { cloudabi64_size_t cloudabi64_sys_fd_pwrite( \ + +9 AUE_NULL STD { size_t cloudabi64_sys_fd_pwrite( \ cloudabi_fd_t fd, \ const cloudabi64_ciovec_t *iov, \ - cloudabi64_size_t iovcnt, \ + size_t iovcnt, \ cloudabi_filesize_t offset); } -10 AUE_NULL STD { cloudabi64_size_t cloudabi64_sys_fd_read( \ + +10 AUE_NULL STD { size_t cloudabi64_sys_fd_read( \ cloudabi_fd_t fd, \ const cloudabi64_iovec_t *iov, \ - cloudabi64_size_t iovcnt); } + size_t iovcnt); } + 11 AUE_NULL STD { void cloudabi_sys_fd_replace( \ cloudabi_fd_t from, \ cloudabi_fd_t to); } -12 AUE_NULL STD { cloudabi_filesize_t cloudabi_sys_fd_seek( \ + +12 AUE_NULL STD { cloudabi_filesize_t \ + cloudabi_sys_fd_seek( \ cloudabi_fd_t fd, \ cloudabi_filedelta_t offset, \ cloudabi_whence_t whence); } + 13 AUE_NULL STD { void cloudabi_sys_fd_stat_get( \ cloudabi_fd_t fd, \ cloudabi_fdstat_t *buf); } + 14 AUE_NULL STD { void cloudabi_sys_fd_stat_put( \ cloudabi_fd_t fd, \ const cloudabi_fdstat_t *buf, \ cloudabi_fdsflags_t flags); } + 15 AUE_NULL STD { void cloudabi_sys_fd_sync( \ cloudabi_fd_t fd); } -16 AUE_NULL STD { cloudabi64_size_t cloudabi64_sys_fd_write( \ + +16 AUE_NULL STD { size_t cloudabi64_sys_fd_write( \ cloudabi_fd_t fd, \ const cloudabi64_ciovec_t *iov, \ - cloudabi64_size_t iovcnt); } + size_t iovcnt); } 17 AUE_NULL STD { void cloudabi_sys_file_advise( \ cloudabi_fd_t fd, \ cloudabi_filesize_t offset, \ cloudabi_filesize_t len, \ cloudabi_advice_t advice); } + 18 AUE_NULL STD { void cloudabi_sys_file_allocate( \ cloudabi_fd_t fd, \ cloudabi_filesize_t offset, \ cloudabi_filesize_t len); } + 19 AUE_NULL STD { void cloudabi_sys_file_create( \ cloudabi_fd_t fd, \ - const char *path, size_t pathlen, \ + const char *path, \ + size_t pathlen, \ cloudabi_filetype_t type); } + 20 AUE_NULL STD { void cloudabi_sys_file_link( \ cloudabi_lookup_t fd1, \ - const char *path1, size_t path1len, \ + const char *path1, \ + size_t path1len, \ cloudabi_fd_t fd2, \ - const char *path2, size_t path2len); } + const char *path2, \ + size_t path2len); } + 21 AUE_NULL STD { cloudabi_fd_t cloudabi_sys_file_open( \ - cloudabi_lookup_t fd, \ - const char *path, size_t pathlen, \ + cloudabi_lookup_t dirfd, \ + const char *path, \ + size_t pathlen, \ cloudabi_oflags_t oflags, \ const cloudabi_fdstat_t *fds); } + 22 AUE_NULL STD { size_t cloudabi_sys_file_readdir( \ cloudabi_fd_t fd, \ - void *buf, size_t nbyte, \ + void *buf, \ + size_t nbyte, \ cloudabi_dircookie_t cookie); } + 23 AUE_NULL STD { size_t cloudabi_sys_file_readlink( \ cloudabi_fd_t fd, \ - const char *path, size_t pathlen, \ - void *buf, size_t bufsize); } + const char *path, \ + size_t pathlen, \ + char *buf, \ + size_t bufsize); } + 24 AUE_NULL STD { void cloudabi_sys_file_rename( \ cloudabi_fd_t oldfd, \ - const char *old, size_t oldlen, \ + const char *old, \ + size_t oldlen, \ cloudabi_fd_t newfd, \ - const char *new, size_t newlen); } + const char *new, \ + size_t newlen); } + 25 AUE_NULL STD { void cloudabi_sys_file_stat_fget( \ cloudabi_fd_t fd, \ cloudabi_filestat_t *buf); } + 26 AUE_NULL STD { void cloudabi_sys_file_stat_fput( \ cloudabi_fd_t fd, \ const cloudabi_filestat_t *buf, \ cloudabi_fsflags_t flags); } + 27 AUE_NULL STD { void cloudabi_sys_file_stat_get( \ cloudabi_lookup_t fd, \ - const char *path, size_t pathlen, \ + const char *path, \ + size_t pathlen, \ cloudabi_filestat_t *buf); } + 28 AUE_NULL STD { void cloudabi_sys_file_stat_put( \ cloudabi_lookup_t fd, \ - const char *path, size_t pathlen, \ + const char *path, \ + size_t pathlen, \ const cloudabi_filestat_t *buf, \ cloudabi_fsflags_t flags); } + 29 AUE_NULL STD { void cloudabi_sys_file_symlink( \ - const char *path1, size_t path1len, \ + const char *path1, \ + size_t path1len, \ cloudabi_fd_t fd, \ - const char *path2, size_t path2len); } + const char *path2, \ + size_t path2len); } + 30 AUE_NULL STD { void cloudabi_sys_file_unlink( \ cloudabi_fd_t fd, \ - const char *path, size_t pathlen, \ - cloudabi_ulflags_t flag); } + const char *path, \ + size_t pathlen, \ + cloudabi_ulflags_t flags); } 31 AUE_NULL STD { void cloudabi_sys_lock_unlock( \ cloudabi_lock_t *lock, \ - cloudabi_mflags_t scope); } + cloudabi_scope_t scope); } 32 AUE_NULL STD { void cloudabi_sys_mem_advise( \ - void *addr, size_t len, \ + void *addr, \ + size_t len, \ cloudabi_advice_t advice); } + 33 AUE_NULL STD { void cloudabi_sys_mem_lock( \ - const void *addr, size_t len); } + const void *addr, \ + size_t len); } + 34 AUE_NULL STD { void cloudabi_sys_mem_map( \ - void *addr, size_t len, \ + void *addr, \ + size_t len, \ cloudabi_mprot_t prot, \ cloudabi_mflags_t flags, \ cloudabi_fd_t fd, \ cloudabi_filesize_t off); } + 35 AUE_NULL STD { void cloudabi_sys_mem_protect( \ - void *addr, size_t len, \ + void *addr, \ + size_t len, \ cloudabi_mprot_t prot); } + 36 AUE_NULL STD { void cloudabi_sys_mem_sync( \ - void *addr, size_t len, \ + void *addr, \ + size_t len, \ cloudabi_msflags_t flags); } + 37 AUE_NULL STD { void cloudabi_sys_mem_unlock( \ - const void *addr, size_t len); } + const void *addr, \ + size_t len); } + 38 AUE_NULL STD { void cloudabi_sys_mem_unmap( \ - void * addr, size_t len); } + void *addr, \ + size_t len); } -39 AUE_NULL STD { cloudabi64_size_t cloudabi64_sys_poll( \ +39 AUE_NULL STD { size_t cloudabi64_sys_poll( \ const cloudabi64_subscription_t *in, \ cloudabi64_event_t *out, \ - cloudabi64_size_t nevents); } + size_t nsubscriptions); } 40 AUE_NULL STD { void cloudabi_sys_proc_exec( \ - cloudabi_fd_t fd, const void *data, \ + cloudabi_fd_t fd, \ + const void *data, \ size_t datalen, \ const cloudabi_fd_t *fds, \ size_t fdslen); } + 41 AUE_NULL STD { void cloudabi_sys_proc_exit( \ cloudabi_exitcode_t rval); } -42 AUE_NULL STD { cloudabi_fd_t cloudabi_sys_proc_fork(); } + +42 AUE_NULL STD { void cloudabi_sys_proc_fork(); } + 43 AUE_NULL STD { void cloudabi_sys_proc_raise( \ cloudabi_signal_t sig); } 44 AUE_NULL STD { void cloudabi_sys_random_get( \ - void *buf, size_t nbyte); } + void *buf, \ + size_t nbyte); } 45 AUE_NULL STD { cloudabi_fd_t cloudabi_sys_sock_accept( \ - cloudabi_fd_t s, \ + cloudabi_fd_t sock, \ cloudabi_sockstat_t *buf); } + 46 AUE_NULL STD { void cloudabi_sys_sock_bind( \ - cloudabi_fd_t s, cloudabi_fd_t fd, \ - const char *path, size_t pathlen); } + cloudabi_fd_t sock, \ + cloudabi_fd_t fd, \ + const char *path, \ + size_t pathlen); } + 47 AUE_NULL STD { void cloudabi_sys_sock_connect( \ - cloudabi_fd_t s, cloudabi_fd_t fd, \ - const char *path, size_t pathlen); } + cloudabi_fd_t sock, \ + cloudabi_fd_t fd, \ + const char *path, \ + size_t pathlen); } + 48 AUE_NULL STD { void cloudabi_sys_sock_listen( \ - cloudabi_fd_t s, \ + cloudabi_fd_t sock, \ cloudabi_backlog_t backlog); } -49 AUE_NULL STD { cloudabi64_size_t cloudabi64_sys_sock_recv( \ - cloudabi_fd_t s, \ + +49 AUE_NULL STD { void cloudabi64_sys_sock_recv( \ + cloudabi_fd_t sock, \ const cloudabi64_recv_in_t *in, \ cloudabi64_recv_out_t *out); } -50 AUE_NULL STD { cloudabi64_size_t cloudabi64_sys_sock_send( \ - cloudabi_fd_t s, \ + +50 AUE_NULL STD { void cloudabi64_sys_sock_send( \ + cloudabi_fd_t sock, \ const cloudabi64_send_in_t *in, \ cloudabi64_send_out_t *out); } + 51 AUE_NULL STD { void cloudabi_sys_sock_shutdown( \ - cloudabi_fd_t fd, \ + cloudabi_fd_t sock, \ cloudabi_sdflags_t how); } + 52 AUE_NULL STD { void cloudabi_sys_sock_stat_get( \ - cloudabi_fd_t fd, \ + cloudabi_fd_t sock, \ cloudabi_sockstat_t *buf, \ cloudabi_ssflags_t flags); } 53 AUE_NULL STD { cloudabi_tid_t cloudabi64_sys_thread_create( \ cloudabi64_threadattr_t *attr); } + 54 AUE_NULL STD { void cloudabi_sys_thread_exit( \ cloudabi_lock_t *lock, \ - cloudabi_mflags_t scope); } -55 AUE_NULL STD { void cloudabi_sys_thread_tcb_set(void *tcb); } + cloudabi_scope_t scope); } + +55 AUE_NULL STD { void cloudabi_sys_thread_tcb_set( \ + void *tcb); } + 56 AUE_NULL STD { void cloudabi_sys_thread_yield(); } -57 AUE_NULL STD { cloudabi64_size_t cloudabi64_sys_poll_fd( \ +57 AUE_NULL STD { size_t cloudabi64_sys_poll_fd( \ cloudabi_fd_t fd, \ const cloudabi64_subscription_t *in, \ - cloudabi64_size_t nin, \ + size_t nin, \ cloudabi64_event_t *out, \ - cloudabi64_size_t nout, \ + size_t nout, \ const cloudabi64_subscription_t *timeout); } diff --git a/sys/contrib/rdma/krping/krping.c b/sys/contrib/rdma/krping/krping.c index 667af40..3ad881e 100644 --- a/sys/contrib/rdma/krping/krping.c +++ b/sys/contrib/rdma/krping/krping.c @@ -261,12 +261,18 @@ static int krping_cma_event_handler(struct rdma_cm_id *cma_id, case RDMA_CM_EVENT_ROUTE_RESOLVED: cb->state = ROUTE_RESOLVED; + cb->child_cm_id = cma_id; wake_up_interruptible(&cb->sem); break; case RDMA_CM_EVENT_CONNECT_REQUEST: - cb->state = CONNECT_REQUEST; - cb->child_cm_id = cma_id; + if (cb->state == IDLE) { + cb->state = CONNECT_REQUEST; + cb->child_cm_id = cma_id; + } else { + PRINTF(cb, "Received connection request in wrong state" + " (%d)\n", cb->state); + } DEBUG_LOG(cb, "child cma %p\n", cb->child_cm_id); wake_up_interruptible(&cb->sem); break; diff --git a/sys/ddb/db_ps.c b/sys/ddb/db_ps.c index f38c89f..76ab2c5 100644 --- a/sys/ddb/db_ps.c +++ b/sys/ddb/db_ps.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include <sys/cons.h> #include <sys/jail.h> #include <sys/kdb.h> +#include <sys/kernel.h> #include <sys/proc.h> #include <sys/sysent.h> #include <sys/systm.h> @@ -302,6 +303,7 @@ DB_SHOW_COMMAND(thread, db_show_thread) struct thread *td; struct lock_object *lock; bool comma; + int delta; /* Determine which thread to examine. */ if (have_addr) @@ -376,6 +378,16 @@ DB_SHOW_COMMAND(thread, db_show_thread) td->td_wchan); db_printf(" priority: %d\n", td->td_priority); db_printf(" container lock: %s (%p)\n", lock->lo_name, lock); + if (td->td_swvoltick != 0) { + delta = (u_int)ticks - (u_int)td->td_swvoltick; + db_printf(" last voluntary switch: %d ms ago\n", + 1000 * delta / hz); + } + if (td->td_swinvoltick != 0) { + delta = (u_int)ticks - (u_int)td->td_swinvoltick; + db_printf(" last involuntary switch: %d ms ago\n", + 1000 * delta / hz); + } } DB_SHOW_COMMAND(proc, db_show_proc) diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c index 70d4bce..6f5a11f 100644 --- a/sys/dev/acpica/acpi.c +++ b/sys/dev/acpica/acpi.c @@ -795,10 +795,10 @@ acpi_print_child(device_t bus, device_t child) int retval = 0; retval += bus_print_child_header(bus, child); - retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx"); - retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#lx"); - retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); - retval += resource_list_print_type(rl, "drq", SYS_RES_DRQ, "%ld"); + retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#jx"); + retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#jx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); + retval += resource_list_print_type(rl, "drq", SYS_RES_DRQ, "%jd"); if (device_get_flags(child)) retval += printf(" flags %#x", device_get_flags(child)); retval += bus_print_child_domain(bus, child); @@ -1156,7 +1156,7 @@ acpi_sysres_alloc(device_t dev) rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev); STAILQ_FOREACH(rle, rl, link) { if (rle->res != NULL) { - device_printf(dev, "duplicate resource for %lx\n", rle->start); + device_printf(dev, "duplicate resource for %jx\n", rle->start); continue; } @@ -1179,7 +1179,7 @@ acpi_sysres_alloc(device_t dev) rman_manage_region(rm, rman_get_start(res), rman_get_end(res)); rle->res = res; } else if (bootverbose) - device_printf(dev, "reservation of %lx, %lx (%d) failed\n", + device_printf(dev, "reservation of %jx, %jx (%d) failed\n", rle->start, rle->count, rle->type); } return (0); diff --git a/sys/dev/acpica/acpi_hpet.c b/sys/dev/acpica/acpi_hpet.c index 76fbd5a..da1e160e 100644 --- a/sys/dev/acpica/acpi_hpet.c +++ b/sys/dev/acpica/acpi_hpet.c @@ -453,7 +453,7 @@ hpet_attach(device_t dev) /* Validate that we can access the whole region. */ if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH) { - device_printf(dev, "memory region width %ld too small\n", + device_printf(dev, "memory region width %jd too small\n", rman_get_size(sc->mem_res)); bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res); return (ENXIO); diff --git a/sys/dev/acpica/acpi_timer.c b/sys/dev/acpica/acpi_timer.c index 2cdf908..34a8320 100644 --- a/sys/dev/acpica/acpi_timer.c +++ b/sys/dev/acpica/acpi_timer.c @@ -152,7 +152,7 @@ acpi_timer_identify(driver_t *driver, device_t parent) rlen = AcpiGbl_FADT.PmTimerLength; rstart = AcpiGbl_FADT.XPmTimerBlock.Address; if (bus_set_resource(dev, rtype, rid, rstart, rlen)) - device_printf(dev, "couldn't set resource (%s 0x%lx+0x%lx)\n", + device_printf(dev, "couldn't set resource (%s 0x%jx+0x%jx)\n", (rtype == SYS_RES_IOPORT) ? "port" : "mem", rstart, rlen); return_VOID; } diff --git a/sys/dev/advansys/adv_isa.c b/sys/dev/advansys/adv_isa.c index 00381a3..3b3a45b 100644 --- a/sys/dev/advansys/adv_isa.c +++ b/sys/dev/advansys/adv_isa.c @@ -136,7 +136,7 @@ adv_isa_probe(device_t dev) || (iobase != adv_isa_ioports[port_index])) { if (bootverbose) device_printf(dev, - "Invalid baseport of 0x%lx specified. " + "Invalid baseport of 0x%jx specified. " "Nearest valid baseport is 0x%x. Failing " "probe.\n", iobase, (port_index <= max_port_index) ? diff --git a/sys/dev/ahci/ahci.c b/sys/dev/ahci/ahci.c index 25ebbf7..8341f66 100644 --- a/sys/dev/ahci/ahci.c +++ b/sys/dev/ahci/ahci.c @@ -527,7 +527,7 @@ ahci_alloc_resource(device_t dev, device_t child, int type, int *rid, { struct ahci_controller *ctlr = device_get_softc(dev); struct resource *res; - long st; + rman_res_t st; int offset, size, unit; unit = (intptr_t)device_get_ivars(child); diff --git a/sys/dev/ahci/ahci.h b/sys/dev/ahci/ahci.h index 86bdf2b..868a376 100644 --- a/sys/dev/ahci/ahci.h +++ b/sys/dev/ahci/ahci.h @@ -597,6 +597,7 @@ enum ahci_err_type { #define AHCI_Q_1MSI 0x00020000 #define AHCI_Q_FORCE_PI 0x00040000 #define AHCI_Q_RESTORE_CAP 0x00080000 +#define AHCI_Q_NOMSIX 0x00100000 #define AHCI_Q_BIT_STRING \ "\020" \ @@ -619,7 +620,8 @@ enum ahci_err_type { "\021ABAR0" \ "\0221MSI" \ "\023FORCE_PI" \ - "\024RESTORE_CAP" + "\024RESTORE_CAP" \ + "\025NOMSIX" int ahci_attach(device_t dev); int ahci_detach(device_t dev); diff --git a/sys/dev/ahci/ahci_pci.c b/sys/dev/ahci/ahci_pci.c index 5c236ce..57f7ea9 100644 --- a/sys/dev/ahci/ahci_pci.c +++ b/sys/dev/ahci/ahci_pci.c @@ -293,7 +293,7 @@ static const struct { {0x11851039, 0x00, "SiS 968", 0}, {0x01861039, 0x00, "SiS 968", 0}, {0xa01c177d, 0x00, "ThunderX", AHCI_Q_ABAR0|AHCI_Q_1MSI}, - {0x00311c36, 0x00, "Annapurna", AHCI_Q_FORCE_PI|AHCI_Q_RESTORE_CAP}, + {0x00311c36, 0x00, "Annapurna", AHCI_Q_FORCE_PI|AHCI_Q_RESTORE_CAP|AHCI_Q_NOMSIX}, {0x00000000, 0x00, NULL, 0} }; @@ -437,6 +437,9 @@ ahci_pci_attach(device_t dev) &ctlr->r_rid, RF_ACTIVE))) return ENXIO; + if (ctlr->quirks & AHCI_Q_NOMSIX) + msix_count = 0; + /* Read MSI-x BAR IDs if supported */ if (msix_count > 0) { error = ahci_pci_read_msix_bars(dev, &table_bar, &pba_bar); diff --git a/sys/dev/amdsbwd/amdsbwd.c b/sys/dev/amdsbwd/amdsbwd.c index 7221db4..207d0b9 100644 --- a/sys/dev/amdsbwd/amdsbwd.c +++ b/sys/dev/amdsbwd/amdsbwd.c @@ -106,6 +106,8 @@ __FBSDID("$FreeBSD$"); /* SB7xx RRG 2.3.1.1, SB600 RRG 2.3.1.1, SB8xx RRG 2.3.1. */ #define AMDSB_SMBUS_DEVID 0x43851002 #define AMDSB8_SMBUS_REVID 0x40 +#define AMDHUDSON_SMBUS_DEVID 0x780b1022 +#define AMDKERNCZ_SMBUS_DEVID 0x790b1022 #define amdsbwd_verbose_printf(dev, ...) \ do { \ @@ -279,7 +281,9 @@ amdsbwd_identify(driver_t *driver, device_t parent) smb_dev = pci_find_bsf(0, 20, 0); if (smb_dev == NULL) return; - if (pci_get_devid(smb_dev) != AMDSB_SMBUS_DEVID) + if (pci_get_devid(smb_dev) != AMDSB_SMBUS_DEVID && + pci_get_devid(smb_dev) != AMDHUDSON_SMBUS_DEVID && + pci_get_devid(smb_dev) != AMDKERNCZ_SMBUS_DEVID) return; child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "amdsbwd", -1); @@ -309,10 +313,12 @@ amdsbwd_probe_sb7xx(device_t dev, struct resource *pmres, uint32_t *addr) *addr <<= 8; *addr |= pmio_read(pmres, AMDSB_PM_WDT_BASE_MSB - i); } + *addr &= ~0x07u; + /* Set watchdog timer tick to 1s. */ val = pmio_read(pmres, AMDSB_PM_WDT_CTRL); val &= ~AMDSB_WDT_RES_MASK; - val |= AMDSB_WDT_RES_10MS; + val |= AMDSB_WDT_RES_1S; pmio_write(pmres, AMDSB_PM_WDT_CTRL, val); /* Enable watchdog device (in stopped state). */ @@ -372,7 +378,7 @@ amdsbwd_probe_sb8xx(device_t dev, struct resource *pmres, uint32_t *addr) val = pmio_read(pmres, AMDSB8_PM_WDT_EN); device_printf(dev, "AMDSB8_PM_WDT_EN value = %#02x\n", val); #endif - device_set_desc(dev, "AMD SB8xx Watchdog Timer"); + device_set_desc(dev, "AMD SB8xx/SB9xx/Axx Watchdog Timer"); } static int @@ -404,7 +410,8 @@ amdsbwd_probe(device_t dev) smb_dev = pci_find_bsf(0, 20, 0); KASSERT(smb_dev != NULL, ("can't find SMBus PCI device\n")); - if (pci_get_revid(smb_dev) < AMDSB8_SMBUS_REVID) + if (pci_get_devid(smb_dev) == AMDSB_SMBUS_DEVID && + pci_get_revid(smb_dev) < AMDSB8_SMBUS_REVID) amdsbwd_probe_sb7xx(dev, res, &addr); else amdsbwd_probe_sb8xx(dev, res, &addr); @@ -440,10 +447,7 @@ amdsbwd_attach_sb(device_t dev, struct amdsbwd_softc *sc) smb_dev = pci_find_bsf(0, 20, 0); KASSERT(smb_dev != NULL, ("can't find SMBus PCI device\n")); - if (pci_get_revid(smb_dev) < AMDSB8_SMBUS_REVID) - sc->ms_per_tick = 10; - else - sc->ms_per_tick = 1000; + sc->ms_per_tick = 1000; sc->res_ctrl = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid_ctrl, RF_ACTIVE); diff --git a/sys/dev/arcmsr/arcmsr.c b/sys/dev/arcmsr/arcmsr.c index 3cf7fce..cabb99a 100644 --- a/sys/dev/arcmsr/arcmsr.c +++ b/sys/dev/arcmsr/arcmsr.c @@ -872,7 +872,7 @@ static void arcmsr_srb_timeout(void *arg) ARCMSR_LOCK_ACQUIRE(&acb->isr_lock); if(srb->srb_state == ARCMSR_SRB_START) { - cmd = srb->pccb->csio.cdb_io.cdb_bytes[0]; + cmd = scsiio_cdb_ptr(&srb->pccb->csio)[0]; srb->srb_state = ARCMSR_SRB_TIMEOUT; srb->pccb->ccb_h.status |= CAM_CMD_TIMEOUT; arcmsr_srb_complete(srb, 1); @@ -997,7 +997,7 @@ static void arcmsr_build_srb(struct CommandControlBlock *srb, arcmsr_cdb->LUN = pccb->ccb_h.target_lun; arcmsr_cdb->Function = 1; arcmsr_cdb->CdbLength = (u_int8_t)pcsio->cdb_len; - bcopy(pcsio->cdb_io.cdb_bytes, arcmsr_cdb->Cdb, pcsio->cdb_len); + bcopy(scsiio_cdb_ptr(pcsio), arcmsr_cdb->Cdb, pcsio->cdb_len); if(nseg != 0) { struct AdapterControlBlock *acb = srb->acb; bus_dmasync_op_t op; @@ -2453,10 +2453,11 @@ static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, union ccb *p struct CMD_MESSAGE_FIELD *pcmdmessagefld; int retvalue = 0, transfer_len = 0; char *buffer; - u_int32_t controlcode = (u_int32_t ) pccb->csio.cdb_io.cdb_bytes[5] << 24 | - (u_int32_t ) pccb->csio.cdb_io.cdb_bytes[6] << 16 | - (u_int32_t ) pccb->csio.cdb_io.cdb_bytes[7] << 8 | - (u_int32_t ) pccb->csio.cdb_io.cdb_bytes[8]; + uint8_t *ptr = scsiio_cdb_ptr(&pccb->csio); + u_int32_t controlcode = (u_int32_t ) ptr[5] << 24 | + (u_int32_t ) ptr[6] << 16 | + (u_int32_t ) ptr[7] << 8 | + (u_int32_t ) ptr[8]; /* 4 bytes: Areca io control code */ if ((pccb->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) { buffer = pccb->csio.data_ptr; @@ -2683,7 +2684,7 @@ static void arcmsr_execute_srb(void *arg, bus_dma_segment_t *dm_segs, int nseg, if(acb->devstate[target][lun] == ARECA_RAID_GONE) { u_int8_t block_cmd, cmd; - cmd = pccb->csio.cdb_io.cdb_bytes[0]; + cmd = scsiio_cdb_ptr(&pccb->csio)[0]; block_cmd = cmd & 0x0f; if(block_cmd == 0x08 || block_cmd == 0x0a) { printf("arcmsr%d:block 'read/write' command " @@ -2800,7 +2801,7 @@ static void arcmsr_handle_virtual_command(struct AdapterControlBlock *acb, return; } pccb->ccb_h.status |= CAM_REQ_CMP; - switch (pccb->csio.cdb_io.cdb_bytes[0]) { + switch (scsiio_cdb_ptr(&pccb->csio)[0]) { case INQUIRY: { unsigned char inqdata[36]; char *buffer = pccb->csio.data_ptr; @@ -2853,6 +2854,12 @@ static void arcmsr_action(struct cam_sim *psim, union ccb *pccb) int target = pccb->ccb_h.target_id; int error; + if (pccb->ccb_h.flags & CAM_CDB_PHYS) { + pccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + return; + } + if(target == 16) { /* virtual device for iop message transfer */ arcmsr_handle_virtual_command(acb, pccb); diff --git a/sys/dev/ata/ata-lowlevel.c b/sys/dev/ata/ata-lowlevel.c index 6b825c0..1c81c6f 100644 --- a/sys/dev/ata/ata-lowlevel.c +++ b/sys/dev/ata/ata-lowlevel.c @@ -851,7 +851,7 @@ ata_pio_read(struct ata_request *request, int length) panic("ata_pio_read: Unsupported CAM data type %x\n", (request->ccb->ccb_h.flags & CAM_DATA_MASK)); - /* We may have extra byte already red but not stored. */ + /* We may have extra byte already read but not stored. */ if (resid) { addr[0] = buf[1]; addr++; diff --git a/sys/dev/ath/if_ath_lna_div.c b/sys/dev/ath/if_ath_lna_div.c index f0a33a5..5c770c0 100644 --- a/sys/dev/ath/if_ath_lna_div.c +++ b/sys/dev/ath/if_ath_lna_div.c @@ -766,7 +766,7 @@ ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs, /* Short scan check */ if (antcomb->scan && antcomb->alt_good) { - if (time_after(ticks, antcomb->scan_start_time + + if (ieee80211_time_after(ticks, antcomb->scan_start_time + msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR))) short_scan = AH_TRUE; else diff --git a/sys/dev/atkbdc/atkbdc_subr.c b/sys/dev/atkbdc/atkbdc_subr.c index 28730c6..436d97a 100644 --- a/sys/dev/atkbdc/atkbdc_subr.c +++ b/sys/dev/atkbdc/atkbdc_subr.c @@ -63,7 +63,7 @@ atkbdc_print_child(device_t bus, device_t dev) retval += printf(" flags 0x%x", flags); irq = bus_get_resource_start(dev, SYS_RES_IRQ, kbdcdev->rid); if (irq != 0) - retval += printf(" irq %ld", irq); + retval += printf(" irq %jd", irq); retval += bus_print_child_footer(bus, dev); return (retval); diff --git a/sys/dev/bhnd/bhnd.c b/sys/dev/bhnd/bhnd.c index 674094a..860d58d 100644 --- a/sys/dev/bhnd/bhnd.c +++ b/sys/dev/bhnd/bhnd.c @@ -451,7 +451,7 @@ bhnd_generic_print_child(device_t dev, device_t child) rl = BUS_GET_RESOURCE_LIST(dev, child); if (rl != NULL) { retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, - "%#lx"); + "%#jx"); } retval += printf(" at core %u", bhnd_get_core_index(child)); @@ -499,7 +499,7 @@ bhnd_generic_probe_nomatch(device_t dev, device_t child) rl = BUS_GET_RESOURCE_LIST(dev, child); if (rl != NULL) - resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); + resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); printf(" at core %u (no driver attached)\n", bhnd_get_core_index(child)); diff --git a/sys/dev/bhnd/bhndb/bhndb.c b/sys/dev/bhnd/bhndb/bhndb.c index 238d4ee..43580b7 100644 --- a/sys/dev/bhnd/bhndb/bhndb.c +++ b/sys/dev/bhnd/bhndb/bhndb.c @@ -143,9 +143,9 @@ bhndb_print_child(device_t dev, device_t child) rl = BUS_GET_RESOURCE_LIST(dev, child); if (rl != NULL) { retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, - "%#lx"); + "%#jx"); retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, - "%ld"); + "%jd"); } retval += bus_print_child_domain(dev, child); diff --git a/sys/dev/bwn/if_bwn.c b/sys/dev/bwn/if_bwn.c index 2f474d1..decef5f 100644 --- a/sys/dev/bwn/if_bwn.c +++ b/sys/dev/bwn/if_bwn.c @@ -2612,7 +2612,7 @@ bwn_phy_g_task_15s(struct bwn_mac *mac) BWN_GETTIME(now); if (bwn_has_hwpctl(mac)) { expire = now - BWN_LO_PWRVEC_EXPIRE; - if (time_before(lo->pwr_vec_read_time, expire)) { + if (ieee80211_time_before(lo->pwr_vec_read_time, expire)) { bwn_lo_get_powervector(mac); bwn_phy_g_dc_lookup_init(mac, 0); } @@ -2621,7 +2621,7 @@ bwn_phy_g_task_15s(struct bwn_mac *mac) expire = now - BWN_LO_CALIB_EXPIRE; TAILQ_FOREACH_SAFE(cal, &lo->calib_list, list, tmp) { - if (!time_before(cal->calib_time, expire)) + if (!ieee80211_time_before(cal->calib_time, expire)) continue; if (BWN_BBATTCMP(&cal->bbatt, &pg->pg_bbatt) && BWN_RFATTCMP(&cal->rfatt, &pg->pg_rfatt)) { @@ -6149,7 +6149,7 @@ bwn_lo_save(struct bwn_mac *mac, struct bwn_lo_g_value *sav) BWN_PHY_WRITE(mac, BWN_PHY_CCK(0x2f), 0); nanouptime(&ts); - if (time_before(lo->txctl_measured_time, + if (ieee80211_time_before(lo->txctl_measured_time, (ts.tv_nsec / 1000000 + ts.tv_sec * 1000) - BWN_LO_TXCTL_EXPIRE)) bwn_lo_measure_txctl_values(mac); @@ -9365,7 +9365,7 @@ bwn_phy_txpower_check(struct bwn_mac *mac, uint32_t flags) BWN_GETTIME(now); - if (!(flags & BWN_TXPWR_IGNORE_TIME) && time_before(now, phy->nexttime)) + if (!(flags & BWN_TXPWR_IGNORE_TIME) && ieee80211_time_before(now, phy->nexttime)) return; phy->nexttime = now + 2 * 1000; diff --git a/sys/dev/bxe/bxe.c b/sys/dev/bxe/bxe.c index d944395..53d8e97 100644 --- a/sys/dev/bxe/bxe.c +++ b/sys/dev/bxe/bxe.c @@ -3063,7 +3063,7 @@ bxe_tpa_stop(struct bxe_softc *sc, #if __FreeBSD_version >= 800000 /* specify what RSS queue was used for this flow */ m->m_pkthdr.flowid = fp->index; - M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE); + BXE_SET_FLOWID(m); #endif if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); @@ -3352,7 +3352,7 @@ bxe_rxeof(struct bxe_softc *sc, #if __FreeBSD_version >= 800000 /* specify what RSS queue was used for this flow */ m->m_pkthdr.flowid = fp->index; - M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE); + BXE_SET_FLOWID(m); #endif next_rx: @@ -4829,6 +4829,8 @@ bxe_dump_mbuf(struct bxe_softc *sc, } while (m) { + +#if __FreeBSD_version >= 1000000 BLOGD(sc, DBG_MBUF, "%02d: mbuf=%p m_len=%d m_flags=0x%b m_data=%p\n", i, m, m->m_len, m->m_flags, M_FLAG_BITS, m->m_data); @@ -4839,6 +4841,26 @@ bxe_dump_mbuf(struct bxe_softc *sc, i, m->m_pkthdr.len, m->m_flags, M_FLAG_BITS, (int)m->m_pkthdr.csum_flags, CSUM_BITS); } +#else + BLOGD(sc, DBG_MBUF, + "%02d: mbuf=%p m_len=%d m_flags=0x%b m_data=%p\n", + i, m, m->m_len, m->m_flags, + "\20\1M_EXT\2M_PKTHDR\3M_EOR\4M_RDONLY", m->m_data); + + if (m->m_flags & M_PKTHDR) { + BLOGD(sc, DBG_MBUF, + "%02d: - m_pkthdr: tot_len=%d flags=0x%b csum_flags=%b\n", + i, m->m_pkthdr.len, m->m_flags, + "\20\12M_BCAST\13M_MCAST\14M_FRAG" + "\15M_FIRSTFRAG\16M_LASTFRAG\21M_VLANTAG" + "\22M_PROMISC\23M_NOFREE", + (int)m->m_pkthdr.csum_flags, + "\20\1CSUM_IP\2CSUM_TCP\3CSUM_UDP\4CSUM_IP_FRAGS" + "\5CSUM_FRAGMENT\6CSUM_TSO\11CSUM_IP_CHECKED" + "\12CSUM_IP_VALID\13CSUM_DATA_VALID" + "\14CSUM_PSEUDO_HDR"); + } +#endif /* #if __FreeBSD_version >= 1000000 */ if (m->m_flags & M_EXT) { switch (m->m_ext.ext_type) { @@ -5222,7 +5244,9 @@ bxe_tx_encap(struct bxe_fastpath *fp, struct mbuf **m_head) sc = fp->sc; +#if __FreeBSD_version >= 800000 M_ASSERTPKTHDR(*m_head); +#endif /* #if __FreeBSD_version >= 800000 */ m0 = *m_head; rc = defragged = nbds = ovlan = vlan_off = total_pkt_size = 0; @@ -5741,7 +5765,7 @@ bxe_tx_mq_start_locked(struct bxe_softc *sc, if (!sc->link_vars.link_up || (if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) { - rc = drbr_enqueue_drv(ifp, tx_br, m); + rc = drbr_enqueue(ifp, tx_br, m); goto bxe_tx_mq_start_locked_exit; } @@ -5756,7 +5780,7 @@ bxe_tx_mq_start_locked(struct bxe_softc *sc, next = drbr_dequeue_drv(ifp, tx_br); } else if (drbr_needs_enqueue_drv(ifp, tx_br)) { /* have both new and pending work, maintain packet order */ - rc = drbr_enqueue_drv(ifp, tx_br, m); + rc = drbr_enqueue(ifp, tx_br, m); if (rc != 0) { fp->eth_q_stats.tx_soft_errors++; goto bxe_tx_mq_start_locked_exit; @@ -5785,7 +5809,7 @@ bxe_tx_mq_start_locked(struct bxe_softc *sc, /* mark the TX queue as full and save the frame */ if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); /* XXX this may reorder the frame */ - rc = drbr_enqueue_drv(ifp, tx_br, next); + rc = drbr_enqueue(ifp, tx_br, next); fp->eth_q_stats.mbuf_alloc_tx--; fp->eth_q_stats.tx_frames_deferred++; } @@ -5837,7 +5861,8 @@ bxe_tx_mq_start(struct ifnet *ifp, fp_index = 0; /* default is the first queue */ /* check if flowid is set */ - if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) + + if (BXE_VALID_FLOWID(m)) fp_index = (m->m_pkthdr.flowid % sc->num_queues); fp = &sc->fp[fp_index]; @@ -5846,7 +5871,7 @@ bxe_tx_mq_start(struct ifnet *ifp, rc = bxe_tx_mq_start_locked(sc, ifp, fp, m); BXE_FP_TX_UNLOCK(fp); } else - rc = drbr_enqueue_drv(ifp, fp->tx_br, m); + rc = drbr_enqueue(ifp, fp->tx_br, m); return (rc); } @@ -12845,7 +12870,7 @@ bxe_allocate_bars(struct bxe_softc *sc) sc->bar[i].handle = rman_get_bushandle(sc->bar[i].resource); sc->bar[i].kva = (vm_offset_t)rman_get_virtual(sc->bar[i].resource); - BLOGI(sc, "PCI BAR%d [%02x] memory allocated: %p-%p (%ld) -> %p\n", + BLOGI(sc, "PCI BAR%d [%02x] memory allocated: %p-%p (%jd) -> %p\n", i, PCIR_BAR(i), (void *)rman_get_start(sc->bar[i].resource), (void *)rman_get_end(sc->bar[i].resource), @@ -15677,18 +15702,11 @@ bxe_add_sysctls(struct bxe_softc *sc) CTLFLAG_RD, BXE_DRIVER_VERSION, 0, "version"); - SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "bc_version", - CTLFLAG_RD, sc->devinfo.bc_ver_str, 0, - "bootcode version"); - snprintf(sc->fw_ver_str, sizeof(sc->fw_ver_str), "%d.%d.%d.%d", BCM_5710_FW_MAJOR_VERSION, BCM_5710_FW_MINOR_VERSION, BCM_5710_FW_REVISION_VERSION, BCM_5710_FW_ENGINEERING_VERSION); - SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "fw_version", - CTLFLAG_RD, sc->fw_ver_str, 0, - "firmware version"); snprintf(sc->mf_mode_str, sizeof(sc->mf_mode_str), "%s", ((sc->devinfo.mf_info.mf_mode == SINGLE_FUNCTION) ? "Single" : @@ -15696,32 +15714,58 @@ bxe_add_sysctls(struct bxe_softc *sc) (sc->devinfo.mf_info.mf_mode == MULTI_FUNCTION_SI) ? "MF-SI" : (sc->devinfo.mf_info.mf_mode == MULTI_FUNCTION_AFEX) ? "MF-AFEX" : "Unknown")); - SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "mf_mode", - CTLFLAG_RD, sc->mf_mode_str, 0, - "multifunction mode"); - SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "mf_vnics", CTLFLAG_RD, &sc->devinfo.mf_info.vnics_per_port, 0, "multifunction vnics per port"); - SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "mac_addr", - CTLFLAG_RD, sc->mac_addr_str, 0, - "mac address"); - snprintf(sc->pci_link_str, sizeof(sc->pci_link_str), "%s x%d", ((sc->devinfo.pcie_link_speed == 1) ? "2.5GT/s" : (sc->devinfo.pcie_link_speed == 2) ? "5.0GT/s" : (sc->devinfo.pcie_link_speed == 4) ? "8.0GT/s" : "???GT/s"), sc->devinfo.pcie_link_width); + + sc->debug = bxe_debug; + +#if __FreeBSD_version >= 900000 + SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "bc_version", + CTLFLAG_RD, sc->devinfo.bc_ver_str, 0, + "bootcode version"); + SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "fw_version", + CTLFLAG_RD, sc->fw_ver_str, 0, + "firmware version"); + SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "mf_mode", + CTLFLAG_RD, sc->mf_mode_str, 0, + "multifunction mode"); + SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "mac_addr", + CTLFLAG_RD, sc->mac_addr_str, 0, + "mac address"); SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "pci_link", CTLFLAG_RD, sc->pci_link_str, 0, "pci link status"); - - sc->debug = bxe_debug; SYSCTL_ADD_ULONG(ctx, children, OID_AUTO, "debug", CTLFLAG_RW, &sc->debug, "debug logging mode"); +#else + SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "bc_version", + CTLFLAG_RD, &sc->devinfo.bc_ver_str, 0, + "bootcode version"); + SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "fw_version", + CTLFLAG_RD, &sc->fw_ver_str, 0, + "firmware version"); + SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "mf_mode", + CTLFLAG_RD, &sc->mf_mode_str, 0, + "multifunction mode"); + SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "mac_addr", + CTLFLAG_RD, &sc->mac_addr_str, 0, + "mac address"); + SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "pci_link", + CTLFLAG_RD, &sc->pci_link_str, 0, + "pci link status"); + SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "debug", + CTLFLAG_RW, &sc->debug, 0, + "debug logging mode"); +#endif /* #if __FreeBSD_version >= 900000 */ SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "trigger_grcdump", CTLTYPE_UINT | CTLFLAG_RW, sc, 0, diff --git a/sys/dev/bxe/bxe.h b/sys/dev/bxe/bxe.h index e87c65b..73f72ce 100644 --- a/sys/dev/bxe/bxe.h +++ b/sys/dev/bxe/bxe.h @@ -2271,6 +2271,17 @@ void bxe_dump_mem(struct bxe_softc *sc, char *tag, void bxe_dump_mbuf_data(struct bxe_softc *sc, char *pTag, struct mbuf *m, uint8_t contents); + +#if __FreeBSD_version >= 800000 +#if __FreeBSD_version >= 1000000 +#define BXE_SET_FLOWID(m) M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE) +#define BXE_VALID_FLOWID(m) (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) +#else +#define BXE_VALID_FLOWID(m) ((m->m_flags & M_FLOWID) != 0) +#define BXE_SET_FLOWID(m) m->m_flags |= M_FLOWID +#endif +#endif /* #if __FreeBSD_version >= 800000 */ + /***********/ /* INLINES */ /***********/ diff --git a/sys/dev/cardbus/cardbus_cis.c b/sys/dev/cardbus/cardbus_cis.c index 5d7704a..f9f7816 100644 --- a/sys/dev/cardbus/cardbus_cis.c +++ b/sys/dev/cardbus/cardbus_cis.c @@ -485,7 +485,8 @@ cardbus_read_tuple_init(device_t cbdev, device_t child, uint32_t *start, "to read CIS.\n"); return (NULL); } - DEVPRINTF((cbdev, "CIS Mapped to %#lx\n", rman_get_start(res))); + DEVPRINTF((cbdev, "CIS Mapped to %#jx\n", + rman_get_start(res))); /* Flip to the right ROM image if CIS is in ROM */ if (space == PCIM_CIS_ASI_ROM) { diff --git a/sys/dev/ctau/if_ct.c b/sys/dev/ctau/if_ct.c index da8d32c..a3df2a8 100644 --- a/sys/dev/ctau/if_ct.c +++ b/sys/dev/ctau/if_ct.c @@ -459,7 +459,7 @@ static int ct_probe (device_t dev) } if (!ct_probe_board (iobase, -1, -1)) { - printf ("ct%d: probing for Tau-ISA at %lx faild\n", unit, iobase); + printf ("ct%d: probing for Tau-ISA at %jx faild\n", unit, iobase); return ENXIO; } @@ -632,7 +632,7 @@ static int ct_attach (device_t dev) ct_ln[2] = '0' + unit; mtx_init (&bd->ct_mtx, ct_ln, MTX_NETWORK_LOCK, MTX_DEF|MTX_RECURSE); if (! probe_irq (b, irq)) { - printf ("ct%d: irq %ld not functional\n", unit, irq); + printf ("ct%d: irq %jd not functional\n", unit, irq); bd->board = 0; adapter [unit] = 0; free (b, M_DEVBUF); @@ -651,7 +651,7 @@ static int ct_attach (device_t dev) if (bus_setup_intr (dev, bd->irq_res, INTR_TYPE_NET|INTR_MPSAFE, NULL, ct_intr, bd, &bd->intrhand)) { - printf ("ct%d: Can't setup irq %ld\n", unit, irq); + printf ("ct%d: Can't setup irq %jd\n", unit, irq); bd->board = 0; adapter [unit] = 0; free (b, M_DEVBUF); diff --git a/sys/dev/cxgb/cxgb_sge.c b/sys/dev/cxgb/cxgb_sge.c index a622fcf..c83bd66 100644 --- a/sys/dev/cxgb/cxgb_sge.c +++ b/sys/dev/cxgb/cxgb_sge.c @@ -2976,11 +2976,7 @@ process_responses(adapter_t *adap, struct sge_qset *qs, int budget) #if defined(INET6) || defined(INET) /* Flush LRO */ - while (!SLIST_EMPTY(&lro_ctrl->lro_active)) { - struct lro_entry *queued = SLIST_FIRST(&lro_ctrl->lro_active); - SLIST_REMOVE_HEAD(&lro_ctrl->lro_active, next); - tcp_lro_flush(lro_ctrl, queued); - } + tcp_lro_flush_all(lro_ctrl); #endif if (sleeping) diff --git a/sys/dev/cxgbe/adapter.h b/sys/dev/cxgbe/adapter.h index ee14712..2193142 100644 --- a/sys/dev/cxgbe/adapter.h +++ b/sys/dev/cxgbe/adapter.h @@ -246,12 +246,10 @@ struct vi_info { int rsrv_noflowq; /* Reserve queue 0 for non-flowid packets */ int nrxq; /* # of rx queues */ int first_rxq; /* index of first rx queue */ -#ifdef TCP_OFFLOAD int nofldtxq; /* # of offload tx queues */ int first_ofld_txq; /* index of first offload tx queue */ int nofldrxq; /* # of offload rx queues */ int first_ofld_rxq; /* index of first offload rx queue */ -#endif int tmr_idx; int pktc_idx; int qsize_rxq; @@ -311,9 +309,7 @@ struct cluster_layout { struct cluster_metadata { u_int refcount; -#ifdef INVARIANTS struct fl_sdesc *sd; /* For debug only. Could easily be stale */ -#endif }; struct fl_sdesc { @@ -571,7 +567,6 @@ iq_to_rxq(struct sge_iq *iq) } -#ifdef TCP_OFFLOAD /* ofld_rxq: SGE ingress queue + SGE free list + miscellaneous items */ struct sge_ofld_rxq { struct sge_iq iq; /* MUST be first */ @@ -584,7 +579,6 @@ iq_to_ofld_rxq(struct sge_iq *iq) return (__containerof(iq, struct sge_ofld_rxq, iq)); } -#endif struct wrqe { STAILQ_ENTRY(wrqe) link; @@ -636,7 +630,6 @@ struct sge_wrq { } __aligned(CACHE_LINE_SIZE); -#ifdef DEV_NETMAP struct sge_nm_rxq { struct vi_info *vi; @@ -691,19 +684,14 @@ struct sge_nm_txq { bus_addr_t ba; int iqidx; } __aligned(CACHE_LINE_SIZE); -#endif struct sge { int nrxq; /* total # of Ethernet rx queues */ int ntxq; /* total # of Ethernet tx tx queues */ -#ifdef TCP_OFFLOAD int nofldrxq; /* total # of TOE rx queues */ int nofldtxq; /* total # of TOE tx queues */ -#endif -#ifdef DEV_NETMAP int nnmrxq; /* total # of netmap rx queues */ int nnmtxq; /* total # of netmap tx queues */ -#endif int niq; /* total # of ingress queues */ int neq; /* total # of egress queues */ @@ -712,14 +700,10 @@ struct sge { struct sge_wrq *ctrlq; /* Control queues */ struct sge_txq *txq; /* NIC tx queues */ struct sge_rxq *rxq; /* NIC rx queues */ -#ifdef TCP_OFFLOAD struct sge_wrq *ofld_txq; /* TOE tx queues */ struct sge_ofld_rxq *ofld_rxq; /* TOE rx queues */ -#endif -#ifdef DEV_NETMAP struct sge_nm_txq *nm_txq; /* netmap tx queues */ struct sge_nm_rxq *nm_rxq; /* netmap rx queues */ -#endif uint16_t iq_start; int eq_start; @@ -778,20 +762,16 @@ struct adapter { struct port_info *port[MAX_NPORTS]; uint8_t chan_map[MAX_NCHAN]; -#ifdef TCP_OFFLOAD void *tom_softc; /* (struct tom_data *) */ struct tom_tunables tt; void *iwarp_softc; /* (struct c4iw_dev *) */ void *iscsi_ulp_softc; /* (struct cxgbei_data *) */ -#endif struct l2t_data *l2t; /* L2 table */ struct tid_info tids; uint16_t doorbells; -#ifdef TCP_OFFLOAD int offload_map; /* ports with IFCAP_TOE enabled */ int active_ulds; /* ULDs activated on this adapter */ -#endif int flags; int debug_flags; @@ -840,11 +820,9 @@ struct adapter { fw_msg_handler_t fw_msg_handler[7]; /* NUM_FW6_TYPES */ cpl_handler_t cpl_handler[0xef]; /* NUM_CPL_CMDS */ -#ifdef INVARIANTS const char *last_op; const void *last_op_thr; int last_op_flags; -#endif int sc_do_rxcopy; }; diff --git a/sys/dev/cxgbe/common/t4_hw.c b/sys/dev/cxgbe/common/t4_hw.c index 92104c8..588f6fd 100644 --- a/sys/dev/cxgbe/common/t4_hw.c +++ b/sys/dev/cxgbe/common/t4_hw.c @@ -5615,6 +5615,7 @@ void t4_get_port_stats_offset(struct adapter *adap, int idx, void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p) { u32 bgmap = t4_get_mps_bg_map(adap, idx); + u32 stat_ctl; #define GET_STAT(name) \ t4_read_reg64(adap, \ @@ -5622,6 +5623,8 @@ void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p) T5_PORT_REG(idx, A_MPS_PORT_STAT_##name##_L))) #define GET_STAT_COM(name) t4_read_reg64(adap, A_MPS_STAT_##name##_L) + stat_ctl = t4_read_reg(adap, A_MPS_STAT_CTL); + p->tx_pause = GET_STAT(TX_PORT_PAUSE); p->tx_octets = GET_STAT(TX_PORT_BYTES); p->tx_frames = GET_STAT(TX_PORT_FRAMES); @@ -5646,6 +5649,12 @@ void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p) p->tx_ppp6 = GET_STAT(TX_PORT_PPP6); p->tx_ppp7 = GET_STAT(TX_PORT_PPP7); + if (stat_ctl & F_COUNTPAUSESTATTX) { + p->tx_frames -= p->tx_pause; + p->tx_octets -= p->tx_pause * 64; + p->tx_mcast_frames -= p->tx_pause; + } + p->rx_pause = GET_STAT(RX_PORT_PAUSE); p->rx_octets = GET_STAT(RX_PORT_BYTES); p->rx_frames = GET_STAT(RX_PORT_FRAMES); @@ -5674,6 +5683,12 @@ void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p) p->rx_ppp6 = GET_STAT(RX_PORT_PPP6); p->rx_ppp7 = GET_STAT(RX_PORT_PPP7); + if (stat_ctl & F_COUNTPAUSESTATRX) { + p->rx_frames -= p->rx_pause; + p->rx_octets -= p->rx_pause * 64; + p->rx_mcast_frames -= p->rx_pause; + } + p->rx_ovflow0 = (bgmap & 1) ? GET_STAT_COM(RX_BG_0_MAC_DROP_FRAME) : 0; p->rx_ovflow1 = (bgmap & 2) ? GET_STAT_COM(RX_BG_1_MAC_DROP_FRAME) : 0; p->rx_ovflow2 = (bgmap & 4) ? GET_STAT_COM(RX_BG_2_MAC_DROP_FRAME) : 0; diff --git a/sys/dev/cxgbe/firmware/t4fw_cfg.txt b/sys/dev/cxgbe/firmware/t4fw_cfg.txt index 0e13122..43820a0 100644 --- a/sys/dev/cxgbe/firmware/t4fw_cfg.txt +++ b/sys/dev/cxgbe/firmware/t4fw_cfg.txt @@ -10,7 +10,7 @@ [global] rss_glb_config_mode = basicvirtual - rss_glb_config_options = tnlmapen, hashtoeplitz, tnlalllkp + rss_glb_config_options = tnlmapen,hashtoeplitz,tnlalllkp sge_timer_value = 1, 5, 10, 50, 100, 200 # usecs @@ -20,61 +20,82 @@ # disable TP_PARA_REG3.RxFragEn reg[0x7d6c] = 0x00000000/0x00007000 - # TP_SHIFT_CNT - reg[0x7dc0] = 0x62f8849 + reg[0x7dc0] = 0x0e2f8849 # TP_SHIFT_CNT filterMode = fragmentation, mpshittype, protocol, vlan, port, fcoe filterMask = protocol, fcoe - # TP rx and tx channels (0 = auto). + tp_pmrx = 36, 512 + tp_pmrx_pagesize = 64K + + # TP number of RX channels (0 = auto) tp_nrxch = 0 - tp_ntxch = 0 - # TP rx and tx payload memory (% of the total EDRAM + DDR3). - tp_pmrx = 38, 512 - tp_pmtx = 60, 512 - tp_pmrx_pagesize = 64K + tp_pmtx = 46, 512 tp_pmtx_pagesize = 64K - # cluster, lan, or wan. - tp_tcptuning = lan + # TP number of TX channels (0 = auto) + tp_ntxch = 0 # TP OFLD MTUs tp_mtus = 88, 256, 512, 576, 808, 1024, 1280, 1488, 1500, 2002, 2048, 4096, 4352, 8192, 9000, 9600 + # cluster, lan, or wan. + tp_tcptuning = lan + # PFs 0-3. These get 8 MSI/8 MSI-X vectors each. VFs are supported by -# these 4 PFs only. Not used here at all. +# these 4 PFs only. [function "0"] - nvf = 16 - nvi = 1 - rssnvi = 0 -[function "0/*"] - nvi = 1 - rssnvi = 0 + nvf = 4 + wx_caps = all + r_caps = all + nvi = 2 + rssnvi = 2 + niqflint = 4 + nethctrl = 4 + neq = 8 + nexactf = 4 + cmask = all + pmask = 0x1 [function "1"] - nvf = 16 - nvi = 1 - rssnvi = 0 -[function "1/*"] - nvi = 1 - rssnvi = 0 + nvf = 4 + wx_caps = all + r_caps = all + nvi = 2 + rssnvi = 2 + niqflint = 4 + nethctrl = 4 + neq = 8 + nexactf = 4 + cmask = all + pmask = 0x2 [function "2"] - nvf = 16 - nvi = 1 - rssnvi = 0 -[function "2/*"] - nvi = 1 - rssnvi = 0 + nvf = 4 + wx_caps = all + r_caps = all + nvi = 2 + rssnvi = 2 + niqflint = 4 + nethctrl = 4 + neq = 8 + nexactf = 4 + cmask = all + pmask = 0x4 [function "3"] - nvf = 16 - nvi = 1 - rssnvi = 0 -[function "3/*"] - nvi = 1 - rssnvi = 0 + nvf = 4 + wx_caps = all + r_caps = all + nvi = 2 + rssnvi = 2 + niqflint = 4 + nethctrl = 4 + neq = 8 + nexactf = 4 + cmask = all + pmask = 0x8 # PF4 is the resource-rich PF that the bus/nexus driver attaches to. # It gets 32 MSI/128 MSI-X vectors. @@ -86,15 +107,19 @@ niqflint = 512 nethctrl = 1024 neq = 2048 - nexactf = 328 + nexactf = 280 cmask = all pmask = all # driver will mask off features it won't use - protocol = ofld + protocol = ofld, rddp, rdmac, iscsi_initiator_pdu, iscsi_target_pdu tp_l2t = 4096 tp_ddp = 2 + tp_ddp_iscsi = 2 + tp_stag = 2 + tp_pbl = 5 + tp_rq = 7 # TCAM has 8K cells; each region must start at a multiple of 128 cell. # Each entry in these categories takes 4 cells each. nhash will use the @@ -130,6 +155,60 @@ nexactf = 8 nfilter = 16 +# For Virtual functions, we only allow NIC functionality and we only allow +# access to one port (1 << PF). Note that because of limitations in the +# Scatter Gather Engine (SGE) hardware which checks writes to VF KDOORBELL +# and GTS registers, the number of Ingress and Egress Queues must be a power +# of 2. +# +[function "0/*"] + wx_caps = 0x82 + r_caps = 0x86 + nvi = 1 + rssnvi = 1 + niqflint = 2 + nethctrl = 2 + neq = 4 + nexactf = 2 + cmask = all + pmask = 0x1 + +[function "1/*"] + wx_caps = 0x82 + r_caps = 0x86 + nvi = 1 + rssnvi = 1 + niqflint = 2 + nethctrl = 2 + neq = 4 + nexactf = 2 + cmask = all + pmask = 0x2 + +[function "2/*"] + wx_caps = 0x82 + r_caps = 0x86 + nvi = 1 + rssnvi = 1 + niqflint = 2 + nethctrl = 2 + neq = 4 + nexactf = 2 + cmask = all + pmask = 0x4 + +[function "3/*"] + wx_caps = 0x82 + r_caps = 0x86 + nvi = 1 + rssnvi = 1 + niqflint = 2 + nethctrl = 2 + neq = 4 + nexactf = 2 + cmask = all + pmask = 0x8 + # MPS has 192K buffer space for ingress packets from the wire as well as # loopback path of the L2 switch. [port "0"] @@ -166,7 +245,7 @@ [fini] version = 0x1 - checksum = 0x98210e18 + checksum = 0xbec0621 # # $FreeBSD$ # diff --git a/sys/dev/cxgbe/firmware/t5fw_cfg.txt b/sys/dev/cxgbe/firmware/t5fw_cfg.txt index 4ae6c99..9e16da5 100644 --- a/sys/dev/cxgbe/firmware/t5fw_cfg.txt +++ b/sys/dev/cxgbe/firmware/t5fw_cfg.txt @@ -10,12 +10,33 @@ [global] rss_glb_config_mode = basicvirtual - rss_glb_config_options = tnlmapen, hashtoeplitz, tnlalllkp + rss_glb_config_options = tnlmapen,hashtoeplitz,tnlalllkp # PL_TIMEOUT register - pl_timeout_value = 200 # the timeout value in units of us + pl_timeout_value = 10000 # the timeout value in units of us - sge_timer_value = 1, 5, 10, 50, 100, 200 # usecs + # SGE_THROTTLE_CONTROL + bar2throttlecount = 500 # bar2throttlecount in us + + sge_timer_value = 1, 5, 10, 50, 100, 200 # SGE_TIMER_VALUE* in usecs + + reg[0x1124] = 0x00000400/0x00000400 # SGE_CONTROL2, enable VFIFO; if + # SGE_VFIFO_SIZE is not set, then + # firmware will set it up in function + # of number of egress queues used + + reg[0x1130] = 0x00d5ffeb # SGE_DBP_FETCH_THRESHOLD, fetch + # threshold set to queue depth + # minus 128-entries for FL and HP + # queues, and 0xfff for LP which + # prompts the firmware to set it up + # in function of egress queues + # used + + reg[0x113c] = 0x0002ffc0 # SGE_VFIFO_SIZE, set to 0x2ffc0 which + # prompts the firmware to set it up in + # function of number of egress queues + # used # enable TP_OUT_CONFIG.IPIDSPLITMODE reg[0x7d04] = 0x00010000/0x00010000 @@ -26,34 +47,38 @@ # enable TP_PARA_REG6.EnableCSnd reg[0x7d78] = 0x00000400/0x00000000 - # TP_SHIFT_CNT - reg[0x7dc0] = 0x62f8849 - - # TP_GLOBAL_CONFIG - reg[0x7d08] = 0x00000800/0x00000800 # set IssFromCplEnable - - # TP_PARA_REG0 - reg[0x7d60] = 0x06000000/0x07000000 # set InitCWND to 6 + reg[0x7dc0] = 0x0e2f8849 # TP_SHIFT_CNT filterMode = fragmentation, mpshittype, protocol, vlan, port, fcoe filterMask = protocol, fcoe - # TP rx and tx channels (0 = auto). + tp_pmrx = 36, 512 + tp_pmrx_pagesize = 64K + + # TP number of RX channels (0 = auto) tp_nrxch = 0 - tp_ntxch = 0 - # TP rx and tx payload memory (% of the total EDRAM + DDR3). - tp_pmrx = 38, 512 - tp_pmtx = 60, 512 - tp_pmrx_pagesize = 64K + tp_pmtx = 46, 512 tp_pmtx_pagesize = 64K - # cluster, lan, or wan. - tp_tcptuning = lan + # TP number of TX channels (0 = auto) + tp_ntxch = 0 # TP OFLD MTUs tp_mtus = 88, 256, 512, 576, 808, 1024, 1280, 1488, 1500, 2002, 2048, 4096, 4352, 8192, 9000, 9600 + # TP_GLOBAL_CONFIG + reg[0x7d08] = 0x00000800/0x00000800 # set IssFromCplEnable + + # TP_PC_CONFIG + reg[0x7d48] = 0x00000000/0x00000400 # clear EnableFLMError + + # TP_PARA_REG0 + reg[0x7d60] = 0x06000000/0x07000000 # set InitCWND to 6 + + # cluster, lan, or wan. + tp_tcptuning = lan + # MC configuration mc_mode_brc[0] = 1 # mc0 - 1: enable BRC, 0: enable RBC mc_mode_brc[1] = 1 # mc1 - 1: enable BRC, 0: enable RBC @@ -63,38 +88,58 @@ # TPT error. # PFs 0-3. These get 8 MSI/8 MSI-X vectors each. VFs are supported by -# these 4 PFs only. Not used here at all. +# these 4 PFs only. [function "0"] - nvf = 16 - nvi = 1 - rssnvi = 0 -[function "0/*"] - nvi = 1 - rssnvi = 0 + nvf = 4 + wx_caps = all + r_caps = all + nvi = 2 + rssnvi = 2 + niqflint = 4 + nethctrl = 4 + neq = 8 + nexactf = 4 + cmask = all + pmask = 0x1 [function "1"] - nvf = 16 - nvi = 1 - rssnvi = 0 -[function "1/*"] - nvi = 1 - rssnvi = 0 + nvf = 4 + wx_caps = all + r_caps = all + nvi = 2 + rssnvi = 2 + niqflint = 4 + nethctrl = 4 + neq = 8 + nexactf = 4 + cmask = all + pmask = 0x2 [function "2"] - nvf = 16 - nvi = 1 - rssnvi = 0 -[function "2/*"] - nvi = 1 - rssnvi = 0 + nvf = 4 + wx_caps = all + r_caps = all + nvi = 2 + rssnvi = 2 + niqflint = 4 + nethctrl = 4 + neq = 8 + nexactf = 4 + cmask = all + pmask = 0x4 [function "3"] - nvf = 16 - nvi = 1 - rssnvi = 0 -[function "3/*"] - nvi = 1 - rssnvi = 0 + nvf = 4 + wx_caps = all + r_caps = all + nvi = 2 + rssnvi = 2 + niqflint = 4 + nethctrl = 4 + neq = 8 + nexactf = 4 + cmask = all + pmask = 0x8 # PF4 is the resource-rich PF that the bus/nexus driver attaches to. # It gets 32 MSI/128 MSI-X vectors. @@ -106,15 +151,19 @@ niqflint = 512 nethctrl = 1024 neq = 2048 - nexactf = 328 + nexactf = 456 cmask = all pmask = all # driver will mask off features it won't use - protocol = ofld + protocol = ofld, rddp, rdmac, iscsi_initiator_pdu, iscsi_target_pdu, iscsi_t10dif tp_l2t = 4096 tp_ddp = 2 + tp_ddp_iscsi = 2 + tp_stag = 2 + tp_pbl = 5 + tp_rq = 7 # TCAM has 8K cells; each region must start at a multiple of 128 cell. # Each entry in these categories takes 4 cells each. nhash will use the @@ -150,6 +199,60 @@ nexactf = 8 nfilter = 16 +# For Virtual functions, we only allow NIC functionality and we only allow +# access to one port (1 << PF). Note that because of limitations in the +# Scatter Gather Engine (SGE) hardware which checks writes to VF KDOORBELL +# and GTS registers, the number of Ingress and Egress Queues must be a power +# of 2. +# +[function "0/*"] + wx_caps = 0x82 + r_caps = 0x86 + nvi = 1 + rssnvi = 1 + niqflint = 2 + nethctrl = 2 + neq = 4 + nexactf = 2 + cmask = all + pmask = 0x1 + +[function "1/*"] + wx_caps = 0x82 + r_caps = 0x86 + nvi = 1 + rssnvi = 1 + niqflint = 2 + nethctrl = 2 + neq = 4 + nexactf = 2 + cmask = all + pmask = 0x2 + +[function "2/*"] + wx_caps = 0x82 + r_caps = 0x86 + nvi = 1 + rssnvi = 1 + niqflint = 2 + nethctrl = 2 + neq = 4 + nexactf = 2 + cmask = all + pmask = 0x4 + +[function "3/*"] + wx_caps = 0x82 + r_caps = 0x86 + nvi = 1 + rssnvi = 1 + niqflint = 2 + nethctrl = 2 + neq = 4 + nexactf = 2 + cmask = all + pmask = 0x8 + # MPS has 192K buffer space for ingress packets from the wire as well as # loopback path of the L2 switch. [port "0"] @@ -186,7 +289,7 @@ [fini] version = 0x1 - checksum = 0x7044b7fd + checksum = 0x2d7417e5 # # $FreeBSD$ # diff --git a/sys/dev/cxgbe/iw_cxgbe/cm.c b/sys/dev/cxgbe/iw_cxgbe/cm.c index c884f5a..c2b72fa 100644 --- a/sys/dev/cxgbe/iw_cxgbe/cm.c +++ b/sys/dev/cxgbe/iw_cxgbe/cm.c @@ -80,7 +80,7 @@ static spinlock_t timeout_lock; static void process_req(struct work_struct *ctx); static void start_ep_timer(struct c4iw_ep *ep); -static void stop_ep_timer(struct c4iw_ep *ep); +static int stop_ep_timer(struct c4iw_ep *ep); static int set_tcpinfo(struct c4iw_ep *ep); static enum c4iw_ep_state state_read(struct c4iw_ep_common *epc); static void __state_set(struct c4iw_ep_common *epc, enum c4iw_ep_state tostate); @@ -96,14 +96,14 @@ static void send_mpa_req(struct c4iw_ep *ep); static int send_mpa_reject(struct c4iw_ep *ep, const void *pdata, u8 plen); static int send_mpa_reply(struct c4iw_ep *ep, const void *pdata, u8 plen); static void close_complete_upcall(struct c4iw_ep *ep, int status); -static int abort_connection(struct c4iw_ep *ep); +static int send_abort(struct c4iw_ep *ep); static void peer_close_upcall(struct c4iw_ep *ep); static void peer_abort_upcall(struct c4iw_ep *ep); static void connect_reply_upcall(struct c4iw_ep *ep, int status); static int connect_request_upcall(struct c4iw_ep *ep); static void established_upcall(struct c4iw_ep *ep); -static void process_mpa_reply(struct c4iw_ep *ep); -static void process_mpa_request(struct c4iw_ep *ep); +static int process_mpa_reply(struct c4iw_ep *ep); +static int process_mpa_request(struct c4iw_ep *ep); static void process_peer_close(struct c4iw_ep *ep); static void process_conn_error(struct c4iw_ep *ep); static void process_close_complete(struct c4iw_ep *ep); @@ -123,11 +123,11 @@ static void release_ep_resources(struct c4iw_ep *ep); } while (0) #define STOP_EP_TIMER(ep) \ - do { \ + ({ \ CTR3(KTR_IW_CXGBE, "stop_ep_timer (%s:%d) ep %p", \ __func__, __LINE__, (ep)); \ stop_ep_timer(ep); \ - } while (0) + }) #ifdef KTR static char *states[] = { @@ -147,6 +147,34 @@ static char *states[] = { }; #endif + +static void deref_cm_id(struct c4iw_ep_common *epc) +{ + epc->cm_id->rem_ref(epc->cm_id); + epc->cm_id = NULL; + set_bit(CM_ID_DEREFED, &epc->history); +} + +static void ref_cm_id(struct c4iw_ep_common *epc) +{ + set_bit(CM_ID_REFED, &epc->history); + epc->cm_id->add_ref(epc->cm_id); +} + +static void deref_qp(struct c4iw_ep *ep) +{ + c4iw_qp_rem_ref(&ep->com.qp->ibqp); + clear_bit(QP_REFERENCED, &ep->com.flags); + set_bit(QP_DEREFED, &ep->com.history); +} + +static void ref_qp(struct c4iw_ep *ep) +{ + set_bit(QP_REFERENCED, &ep->com.flags); + set_bit(QP_REFED, &ep->com.history); + c4iw_qp_add_ref(&ep->com.qp->ibqp); +} + static void process_req(struct work_struct *ctx) { @@ -304,9 +332,7 @@ process_peer_close(struct c4iw_ep *ep) disconnect = 0; STOP_EP_TIMER(ep); close_socket(&ep->com, 0); - ep->com.cm_id->rem_ref(ep->com.cm_id); - ep->com.cm_id = NULL; - ep->com.qp = NULL; + deref_cm_id(&ep->com); release = 1; break; @@ -490,6 +516,7 @@ process_close_complete(struct c4iw_ep *ep) /* The cm_id may be null if we failed to connect */ mutex_lock(&ep->com.mutex); + set_bit(CLOSE_CON_RPL, &ep->com.history); switch (ep->com.state) { @@ -580,13 +607,14 @@ static void process_data(struct c4iw_ep *ep) { struct sockaddr_in *local, *remote; + int disconnect = 0; CTR5(KTR_IW_CXGBE, "%s: so %p, ep %p, state %s, sbused %d", __func__, ep->com.so, ep, states[ep->com.state], sbused(&ep->com.so->so_rcv)); switch (state_read(&ep->com)) { case MPA_REQ_SENT: - process_mpa_reply(ep); + disconnect = process_mpa_reply(ep); break; case MPA_REQ_WAIT: in_getsockaddr(ep->com.so, (struct sockaddr **)&local); @@ -595,7 +623,7 @@ process_data(struct c4iw_ep *ep) ep->com.remote_addr = *remote; free(local, M_SONAME); free(remote, M_SONAME); - process_mpa_request(ep); + disconnect = process_mpa_request(ep); break; default: if (sbused(&ep->com.so->so_rcv)) @@ -605,6 +633,9 @@ process_data(struct c4iw_ep *ep) ep->com.so->so_state, sbused(&ep->com.so->so_rcv)); break; } + if (disconnect) + c4iw_ep_disconnect(ep, disconnect == 2, GFP_KERNEL); + } static void @@ -749,9 +780,9 @@ int db_delay_usecs = 1; SYSCTL_INT(_hw_iw_cxgbe, OID_AUTO, db_delay_usecs, CTLFLAG_RWTUN, &db_delay_usecs, 0, "Usecs to delay awaiting db fifo to drain"); -static int dack_mode = 1; +static int dack_mode = 0; SYSCTL_INT(_hw_iw_cxgbe, OID_AUTO, dack_mode, CTLFLAG_RWTUN, &dack_mode, 0, - "Delayed ack mode (default = 1)"); + "Delayed ack mode (default = 0)"); int c4iw_max_read_depth = 8; SYSCTL_INT(_hw_iw_cxgbe, OID_AUTO, c4iw_max_read_depth, CTLFLAG_RWTUN, &c4iw_max_read_depth, 0, @@ -773,9 +804,9 @@ int c4iw_debug = 1; SYSCTL_INT(_hw_iw_cxgbe, OID_AUTO, c4iw_debug, CTLFLAG_RWTUN, &c4iw_debug, 0, "Enable debug logging (default = 0)"); -static int peer2peer; +static int peer2peer = 1; SYSCTL_INT(_hw_iw_cxgbe, OID_AUTO, peer2peer, CTLFLAG_RWTUN, &peer2peer, 0, - "Support peer2peer ULPs (default = 0)"); + "Support peer2peer ULPs (default = 1)"); static int p2p_type = FW_RI_INIT_P2PTYPE_READ_REQ; SYSCTL_INT(_hw_iw_cxgbe, OID_AUTO, p2p_type, CTLFLAG_RWTUN, &p2p_type, 0, @@ -827,14 +858,16 @@ start_ep_timer(struct c4iw_ep *ep) add_timer(&ep->timer); } -static void +static int stop_ep_timer(struct c4iw_ep *ep) { del_timer_sync(&ep->timer); if (!test_and_set_bit(TIMEOUT, &ep->com.flags)) { c4iw_put_ep(&ep->com); + return 0; } + return 1; } static enum @@ -900,6 +933,8 @@ void _c4iw_free_ep(struct kref *kref) epc = &ep->com; KASSERT(!epc->entry.tqe_prev, ("%s epc %p still on req list", __func__, epc)); + if (test_bit(QP_REFERENCED, &ep->com.flags)) + deref_qp(ep); kfree(ep); } @@ -1175,20 +1210,17 @@ static void close_complete_upcall(struct c4iw_ep *ep, int status) CTR2(KTR_IW_CXGBE, "%s:ccu1 %1", __func__, ep); ep->com.cm_id->event_handler(ep->com.cm_id, &event); - ep->com.cm_id->rem_ref(ep->com.cm_id); - ep->com.cm_id = NULL; - ep->com.qp = NULL; + deref_cm_id(&ep->com); set_bit(CLOSE_UPCALL, &ep->com.history); } CTR2(KTR_IW_CXGBE, "%s:ccuE %p", __func__, ep); } -static int abort_connection(struct c4iw_ep *ep) +static int send_abort(struct c4iw_ep *ep) { int err; CTR2(KTR_IW_CXGBE, "%s:abB %p", __func__, ep); - state_set(&ep->com, ABORTING); abort_socket(ep); err = close_socket(&ep->com, 0); set_bit(ABORT_CONN, &ep->com.history); @@ -1226,9 +1258,7 @@ static void peer_abort_upcall(struct c4iw_ep *ep) CTR2(KTR_IW_CXGBE, "%s:pau1 %p", __func__, ep); ep->com.cm_id->event_handler(ep->com.cm_id, &event); - ep->com.cm_id->rem_ref(ep->com.cm_id); - ep->com.cm_id = NULL; - ep->com.qp = NULL; + deref_cm_id(&ep->com); set_bit(ABORT_UPCALL, &ep->com.history); } CTR2(KTR_IW_CXGBE, "%s:pauE %p", __func__, ep); @@ -1282,9 +1312,7 @@ static void connect_reply_upcall(struct c4iw_ep *ep, int status) if (status < 0) { CTR3(KTR_IW_CXGBE, "%s:cru4 %p %d", __func__, ep, status); - ep->com.cm_id->rem_ref(ep->com.cm_id); - ep->com.cm_id = NULL; - ep->com.qp = NULL; + deref_cm_id(&ep->com); } CTR2(KTR_IW_CXGBE, "%s:cruE %p", __func__, ep); @@ -1353,8 +1381,19 @@ static void established_upcall(struct c4iw_ep *ep) } - -static void process_mpa_reply(struct c4iw_ep *ep) +/* + * process_mpa_reply - process streaming mode MPA reply + * + * Returns: + * + * 0 upon success indicating a connect request was delivered to the ULP + * or the mpa request is incomplete but valid so far. + * + * 1 if a failure requires the caller to close the connection. + * + * 2 if a failure requires the caller to abort the connection. + */ +static int process_mpa_reply(struct c4iw_ep *ep) { struct mpa_message *mpa; struct mpa_v2_conn_params *mpa_v2_params; @@ -1367,17 +1406,17 @@ static void process_mpa_reply(struct c4iw_ep *ep) struct mbuf *top, *m; int flags = MSG_DONTWAIT; struct uio uio; + int disconnect = 0; CTR2(KTR_IW_CXGBE, "%s:pmrB %p", __func__, ep); /* - * Stop mpa timer. If it expired, then the state has - * changed and we bail since ep_timeout already aborted - * the connection. + * Stop mpa timer. If it expired, then + * we ignore the MPA reply. process_timeout() + * will abort the connection. */ - STOP_EP_TIMER(ep); - if (state_read(&ep->com) != MPA_REQ_SENT) - return; + if (STOP_EP_TIMER(ep)) + return 0; uio.uio_resid = 1000000; uio.uio_td = ep->com.thread; @@ -1389,7 +1428,7 @@ static void process_mpa_reply(struct c4iw_ep *ep) CTR2(KTR_IW_CXGBE, "%s:pmr1 %p", __func__, ep); START_EP_TIMER(ep); - return; + return 0; } err = -err; CTR2(KTR_IW_CXGBE, "%s:pmr2 %p", __func__, ep); @@ -1417,7 +1456,7 @@ static void process_mpa_reply(struct c4iw_ep *ep) CTR3(KTR_IW_CXGBE, "%s:pmr5 %p %d", __func__, ep, ep->mpa_pkt_len + m->m_len); err = (-EINVAL); - goto err; + goto err_stop_timer; } /* @@ -1435,8 +1474,9 @@ static void process_mpa_reply(struct c4iw_ep *ep) /* * if we don't even have the mpa message, then bail. */ - if (ep->mpa_pkt_len < sizeof(*mpa)) - return; + if (ep->mpa_pkt_len < sizeof(*mpa)) { + return 0; + } mpa = (struct mpa_message *) ep->mpa_pkt; /* Validate MPA header. */ @@ -1447,14 +1487,14 @@ static void process_mpa_reply(struct c4iw_ep *ep) printk(KERN_ERR MOD "%s MPA version mismatch. Local = %d, " " Received = %d\n", __func__, mpa_rev, mpa->revision); err = -EPROTO; - goto err; + goto err_stop_timer; } if (memcmp(mpa->key, MPA_KEY_REP, sizeof(mpa->key))) { CTR2(KTR_IW_CXGBE, "%s:pmr7 %p", __func__, ep); err = -EPROTO; - goto err; + goto err_stop_timer; } plen = ntohs(mpa->private_data_size); @@ -1466,7 +1506,7 @@ static void process_mpa_reply(struct c4iw_ep *ep) CTR2(KTR_IW_CXGBE, "%s:pmr8 %p", __func__, ep); err = -EPROTO; - goto err; + goto err_stop_timer; } /* @@ -1475,8 +1515,9 @@ static void process_mpa_reply(struct c4iw_ep *ep) if (ep->mpa_pkt_len > (sizeof(*mpa) + plen)) { CTR2(KTR_IW_CXGBE, "%s:pmr9 %p", __func__, ep); + STOP_EP_TIMER(ep); err = -EPROTO; - goto err; + goto err_stop_timer; } ep->plen = (u8) plen; @@ -1488,14 +1529,14 @@ static void process_mpa_reply(struct c4iw_ep *ep) if (ep->mpa_pkt_len < (sizeof(*mpa) + plen)) { CTR2(KTR_IW_CXGBE, "%s:pmra %p", __func__, ep); - return; + return 0; } if (mpa->flags & MPA_REJECT) { CTR2(KTR_IW_CXGBE, "%s:pmrb %p", __func__, ep); err = -ECONNREFUSED; - goto err; + goto err_stop_timer; } /* @@ -1638,6 +1679,7 @@ static void process_mpa_reply(struct c4iw_ep *ep) err = c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, C4IW_QP_ATTR_NEXT_STATE, &attrs, 0); err = -ENOMEM; + disconnect = 1; goto out; } @@ -1658,19 +1700,33 @@ static void process_mpa_reply(struct c4iw_ep *ep) err = c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, C4IW_QP_ATTR_NEXT_STATE, &attrs, 0); err = -ENOMEM; + disconnect = 1; goto out; } goto out; +err_stop_timer: + STOP_EP_TIMER(ep); err: - state_set(&ep->com, ABORTING); - abort_connection(ep); + disconnect = 2; out: connect_reply_upcall(ep, err); CTR2(KTR_IW_CXGBE, "%s:pmrE %p", __func__, ep); - return; + return disconnect; } -static void +/* + * process_mpa_request - process streaming mode MPA request + * + * Returns: + * + * 0 upon success indicating a connect request was delivered to the ULP + * or the mpa request is incomplete but valid so far. + * + * 1 if a failure requires the caller to close the connection. + * + * 2 if a failure requires the caller to abort the connection. + */ +static int process_mpa_request(struct c4iw_ep *ep) { struct mpa_message *mpa; @@ -1684,7 +1740,7 @@ process_mpa_request(struct c4iw_ep *ep) CTR3(KTR_IW_CXGBE, "%s: ep %p, state %s", __func__, ep, states[state]); if (state != MPA_REQ_WAIT) - return; + return 0; iov.iov_base = &ep->mpa_pkt[ep->mpa_pkt_len]; iov.iov_len = sizeof(ep->mpa_pkt) - ep->mpa_pkt_len; @@ -1698,13 +1754,10 @@ process_mpa_request(struct c4iw_ep *ep) rc = soreceive(ep->com.so, NULL, &uio, NULL, NULL, &flags); if (rc == EAGAIN) - return; - else if (rc) { -abort: - STOP_EP_TIMER(ep); - abort_connection(ep); - return; - } + return 0; + else if (rc) + goto err_stop_timer; + KASSERT(uio.uio_offset > 0, ("%s: sorecieve on so %p read no data", __func__, ep->com.so)); ep->mpa_pkt_len += uio.uio_offset; @@ -1718,7 +1771,7 @@ abort: /* Don't even have the MPA message. Wait for more data to arrive. */ if (ep->mpa_pkt_len < sizeof(*mpa)) - return; + return 0; mpa = (struct mpa_message *) ep->mpa_pkt; /* @@ -1727,24 +1780,24 @@ abort: if (mpa->revision > mpa_rev) { log(LOG_ERR, "%s: MPA version mismatch. Local = %d," " Received = %d\n", __func__, mpa_rev, mpa->revision); - goto abort; + goto err_stop_timer; } if (memcmp(mpa->key, MPA_KEY_REQ, sizeof(mpa->key))) - goto abort; + goto err_stop_timer; /* * Fail if there's too much private data. */ plen = ntohs(mpa->private_data_size); if (plen > MPA_MAX_PRIVATE_DATA) - goto abort; + goto err_stop_timer; /* * If plen does not account for pkt size */ if (ep->mpa_pkt_len > (sizeof(*mpa) + plen)) - goto abort; + goto err_stop_timer; ep->plen = (u8) plen; @@ -1752,7 +1805,7 @@ abort: * If we don't have all the pdata yet, then bail. */ if (ep->mpa_pkt_len < (sizeof(*mpa) + plen)) - return; + return 0; /* * If we get here we have accumulated the entire mpa @@ -1794,7 +1847,7 @@ abort: ep->mpa_attr.p2p_type = p2p_type; if (set_tcpinfo(ep)) - goto abort; + goto err_stop_timer; CTR5(KTR_IW_CXGBE, "%s: crc_enabled = %d, recv_marker_enabled = %d, " "xmit_marker_enabled = %d, version = %d", __func__, @@ -1807,12 +1860,18 @@ abort: /* drive upcall */ mutex_lock(&ep->parent_ep->com.mutex); if (ep->parent_ep->com.state != DEAD) { - if(connect_request_upcall(ep)) { - abort_connection(ep); - } - }else - abort_connection(ep); + if(connect_request_upcall(ep)) + goto err_out; + }else { + goto err_out; + } mutex_unlock(&ep->parent_ep->com.mutex); + return 0; + +err_stop_timer: + STOP_EP_TIMER(ep); +err_out: + return 2; } /* @@ -1825,6 +1884,7 @@ int c4iw_reject_cr(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len) int err; struct c4iw_ep *ep = to_ep(cm_id); CTR2(KTR_IW_CXGBE, "%s:crcB %p", __func__, ep); + int disconnect = 0; if (state_read(&ep->com) == DEAD) { @@ -1838,7 +1898,7 @@ int c4iw_reject_cr(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len) if (mpa_rev == 0) { CTR2(KTR_IW_CXGBE, "%s:crc2 %p", __func__, ep); - abort_connection(ep); + disconnect = 2; } else { @@ -1847,6 +1907,8 @@ int c4iw_reject_cr(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len) err = soshutdown(ep->com.so, 3); } c4iw_put_ep(&ep->com); + if (disconnect) + err = c4iw_ep_disconnect(ep, disconnect == 2, GFP_KERNEL); CTR2(KTR_IW_CXGBE, "%s:crc4 %p", __func__, ep); return 0; } @@ -1859,6 +1921,7 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) struct c4iw_ep *ep = to_ep(cm_id); struct c4iw_dev *h = to_c4iw_dev(cm_id->device); struct c4iw_qp *qp = get_qhp(h, conn_param->qpn); + int abort = 0; CTR2(KTR_IW_CXGBE, "%s:cacB %p", __func__, ep); @@ -1866,7 +1929,7 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) CTR2(KTR_IW_CXGBE, "%s:cac1 %p", __func__, ep); err = -ECONNRESET; - goto err; + goto err_out; } BUG_ON(state_read(&ep->com) != MPA_REQ_RCVD); @@ -1878,9 +1941,8 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) (conn_param->ird > c4iw_max_read_depth)) { CTR2(KTR_IW_CXGBE, "%s:cac2 %p", __func__, ep); - abort_connection(ep); err = -EINVAL; - goto err; + goto err_abort; } if (ep->mpa_attr.version == 2 && ep->mpa_attr.enhanced_rdma_conn) { @@ -1894,9 +1956,8 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) ep->ord = conn_param->ord; send_mpa_reject(ep, conn_param->private_data, conn_param->private_data_len); - abort_connection(ep); err = -ENOMEM; - goto err; + goto err_abort; } if (conn_param->ird > ep->ord) { @@ -1910,9 +1971,8 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) } else { CTR2(KTR_IW_CXGBE, "%s:cac7 %p", __func__, ep); - abort_connection(ep); err = -ENOMEM; - goto err; + goto err_abort; } } @@ -1932,9 +1992,10 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) } - cm_id->add_ref(cm_id); ep->com.cm_id = cm_id; + ref_cm_id(&ep->com); ep->com.qp = qp; + ref_qp(ep); //ep->ofld_txq = TOEPCB(ep->com.so)->ofld_txq; /* bind QP to EP and move to RTS */ @@ -1956,7 +2017,7 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) if (err) { CTR2(KTR_IW_CXGBE, "%s:caca %p", __func__, ep); - goto err1; + goto err_defef_cm_id; } err = send_mpa_reply(ep, conn_param->private_data, conn_param->private_data_len); @@ -1964,7 +2025,7 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) if (err) { CTR2(KTR_IW_CXGBE, "%s:caca %p", __func__, ep); - goto err1; + goto err_defef_cm_id; } state_set(&ep->com, FPDU_MODE); @@ -1972,11 +2033,13 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) c4iw_put_ep(&ep->com); CTR2(KTR_IW_CXGBE, "%s:cacE %p", __func__, ep); return 0; -err1: - ep->com.cm_id = NULL; - ep->com.qp = NULL; - cm_id->rem_ref(cm_id); -err: +err_defef_cm_id: + deref_cm_id(&ep->com); +err_abort: + abort = 1; +err_out: + if (abort) + c4iw_ep_disconnect(ep, 1, GFP_KERNEL); c4iw_put_ep(&ep->com); CTR2(KTR_IW_CXGBE, "%s:cacE err %p", __func__, ep); return err; @@ -2028,9 +2091,9 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) ep->ord = 1; } - cm_id->add_ref(cm_id); ep->com.dev = dev; ep->com.cm_id = cm_id; + ref_cm_id(&ep->com); ep->com.qp = get_qhp(dev, conn_param->qpn); if (!ep->com.qp) { @@ -2039,6 +2102,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) err = -EINVAL; goto fail2; } + ref_qp(ep); ep->com.thread = curthread; ep->com.so = cm_id->so; @@ -2096,7 +2160,7 @@ fail3: CTR2(KTR_IW_CXGBE, "%s:ccb %p", __func__, ep); fib4_free_nh_ext(RT_DEFAULT_FIB, &nh4); fail2: - cm_id->rem_ref(cm_id); + deref_cm_id(&ep->com); c4iw_put_ep(&ep->com); out: CTR2(KTR_IW_CXGBE, "%s:ccE %p", __func__, ep); @@ -2124,8 +2188,8 @@ c4iw_create_listen_ep(struct iw_cm_id *cm_id, int backlog) goto failed; } - cm_id->add_ref(cm_id); ep->com.cm_id = cm_id; + ref_cm_id(&ep->com); ep->com.dev = dev; ep->backlog = backlog; ep->com.local_addr = cm_id->local_addr; @@ -2150,7 +2214,7 @@ c4iw_destroy_listen_ep(struct iw_cm_id *cm_id) cm_id->so, states[ep->com.state]); state_set(&ep->com, DEAD); - cm_id->rem_ref(cm_id); + deref_cm_id(&ep->com); c4iw_put_ep(&ep->com); return; @@ -2232,7 +2296,8 @@ int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp) CTR2(KTR_IW_CXGBE, "%s:ced4 %p", __func__, ep); set_bit(EP_DISC_ABORT, &ep->com.history); - ret = abort_connection(ep); + close_complete_upcall(ep, -ECONNRESET); + ret = send_abort(ep); } else { CTR2(KTR_IW_CXGBE, "%s:ced5 %p", __func__, ep); @@ -2250,8 +2315,25 @@ int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp) } if (fatal) { + set_bit(EP_DISC_FAIL, &ep->com.history); + if (!abrupt) { + STOP_EP_TIMER(ep); + close_complete_upcall(ep, -EIO); + } + if (ep->com.qp) { + struct c4iw_qp_attributes attrs; + attrs.next_state = C4IW_QP_STATE_ERROR; + ret = c4iw_modify_qp(ep->com.dev, ep->com.qp, + C4IW_QP_ATTR_NEXT_STATE, + &attrs, 1); + if (ret) { + CTR2(KTR_IW_CXGBE, "%s:ced7 %p", __func__, ep); + printf("%s - qp <- error failed!\n", __func__); + } + } release_ep_resources(ep); + ep->com.state = DEAD; CTR2(KTR_IW_CXGBE, "%s:ced6 %p", __func__, ep); } CTR2(KTR_IW_CXGBE, "%s:cedE %p", __func__, ep); @@ -2290,8 +2372,13 @@ static void ep_timeout(unsigned long arg) if (!test_and_set_bit(TIMEOUT, &ep->com.flags)) { - list_add_tail(&ep->entry, &timeout_list); - kickit = 1; + /* + * Only insert if it is not already on the list. + */ + if (!ep->entry.next) { + list_add_tail(&ep->entry, &timeout_list); + kickit = 1; + } } spin_unlock(&timeout_lock); diff --git a/sys/dev/cxgbe/iw_cxgbe/cq.c b/sys/dev/cxgbe/iw_cxgbe/cq.c index 8710e03..b40ffc7 100644 --- a/sys/dev/cxgbe/iw_cxgbe/cq.c +++ b/sys/dev/cxgbe/iw_cxgbe/cq.c @@ -724,7 +724,7 @@ static int c4iw_poll_cq_one(struct c4iw_cq *chp, struct ib_wc *wc) default: printf("Unexpected cqe_status 0x%x for QPID = 0x%0x\n", CQE_STATUS(&cqe), CQE_QPID(&cqe)); - ret = -EINVAL; + wc->status = IB_WC_FATAL_ERR; } } out: @@ -861,6 +861,7 @@ c4iw_create_cq(struct ib_device *ibdev, struct ib_cq_init_attr *attr, if (!mm2) goto err4; + memset(&uresp, 0, sizeof(uresp)); uresp.qid_mask = rhp->rdev.cqmask; uresp.cqid = chp->cq.cqid; uresp.size = chp->cq.size; @@ -871,7 +872,8 @@ c4iw_create_cq(struct ib_device *ibdev, struct ib_cq_init_attr *attr, uresp.gts_key = ucontext->key; ucontext->key += PAGE_SIZE; spin_unlock(&ucontext->mmap_lock); - ret = ib_copy_to_udata(udata, &uresp, sizeof uresp); + ret = ib_copy_to_udata(udata, &uresp, + sizeof(uresp) - sizeof(uresp.reserved)); if (ret) goto err5; diff --git a/sys/dev/cxgbe/iw_cxgbe/iw_cxgbe.h b/sys/dev/cxgbe/iw_cxgbe/iw_cxgbe.h index c232f70..38a8af6 100644 --- a/sys/dev/cxgbe/iw_cxgbe/iw_cxgbe.h +++ b/sys/dev/cxgbe/iw_cxgbe/iw_cxgbe.h @@ -157,7 +157,7 @@ static inline int c4iw_fatal_error(struct c4iw_rdev *rdev) static inline int c4iw_num_stags(struct c4iw_rdev *rdev) { - return min((int)T4_MAX_NUM_STAG, (int)(rdev->adap->vres.stag.size >> 5)); + return (int)(rdev->adap->vres.stag.size >> 5); } #define C4IW_WR_TO (10*HZ) @@ -435,6 +435,7 @@ struct c4iw_qp { atomic_t refcnt; wait_queue_head_t wait; struct timer_list timer; + int sq_sig_all; }; static inline struct c4iw_qp *to_c4iw_qp(struct ib_qp *ibqp) @@ -712,7 +713,8 @@ enum c4iw_ep_flags { ABORT_REQ_IN_PROGRESS = 1, RELEASE_RESOURCES = 2, CLOSE_SENT = 3, - TIMEOUT = 4 + TIMEOUT = 4, + QP_REFERENCED = 5 }; enum c4iw_ep_history { @@ -737,7 +739,13 @@ enum c4iw_ep_history { EP_DISC_ABORT = 18, CONN_RPL_UPCALL = 19, ACT_RETRY_NOMEM = 20, - ACT_RETRY_INUSE = 21 + ACT_RETRY_INUSE = 21, + CLOSE_CON_RPL = 22, + EP_DISC_FAIL = 24, + QP_REFED = 25, + QP_DEREFED = 26, + CM_ID_REFED = 27, + CM_ID_DEREFED = 28 }; struct c4iw_ep_common { diff --git a/sys/dev/cxgbe/iw_cxgbe/mem.c b/sys/dev/cxgbe/iw_cxgbe/mem.c index f7c460a..e42aa1a 100644 --- a/sys/dev/cxgbe/iw_cxgbe/mem.c +++ b/sys/dev/cxgbe/iw_cxgbe/mem.c @@ -46,6 +46,12 @@ __FBSDID("$FreeBSD$"); #define T4_ULPTX_MIN_IO 32 #define C4IW_MAX_INLINE_SIZE 96 +static int mr_exceeds_hw_limits(struct c4iw_dev *dev, u64 length) +{ + return (is_t4(dev->rdev.adap) || + is_t5(dev->rdev.adap)) && + length >= 8*1024*1024*1024ULL; +} static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len, void *data) { @@ -144,8 +150,12 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry, if ((!reset_tpt_entry) && (*stag == T4_STAG_UNSET)) { stag_idx = c4iw_get_resource(&rdev->resource.tpt_table); - if (!stag_idx) + if (!stag_idx) { + mutex_lock(&rdev->stats.lock); + rdev->stats.stag.fail++; + mutex_unlock(&rdev->stats.lock); return -ENOMEM; + } mutex_lock(&rdev->stats.lock); rdev->stats.stag.cur += 32; if (rdev->stats.stag.cur > rdev->stats.stag.max) @@ -250,9 +260,9 @@ static int register_mem(struct c4iw_dev *rhp, struct c4iw_pd *php, int ret; ret = write_tpt_entry(&rhp->rdev, 0, &stag, 1, mhp->attr.pdid, - FW_RI_STAG_NSMR, mhp->attr.perms, + FW_RI_STAG_NSMR, mhp->attr.len ? mhp->attr.perms : 0, mhp->attr.mw_bind_enable, mhp->attr.zbva, - mhp->attr.va_fbo, mhp->attr.len, shift - 12, + mhp->attr.va_fbo, mhp->attr.len ? mhp->attr.len : -1, shift - 12, mhp->attr.pbl_size, mhp->attr.pbl_addr); if (ret) return ret; @@ -381,7 +391,7 @@ int c4iw_reregister_phys_mem(struct ib_mr *mr, int mr_rereg_mask, struct c4iw_dev *rhp; __be64 *page_list = NULL; int shift = 0; - u64 total_size; + u64 total_size = 0; int npages = 0; int ret; @@ -416,7 +426,10 @@ int c4iw_reregister_phys_mem(struct ib_mr *mr, int mr_rereg_mask, if (ret) return ret; } - + if (mr_exceeds_hw_limits(rhp, total_size)) { + kfree(page_list); + return -EINVAL; + } ret = reregister_mem(rhp, php, &mh, shift, npages); kfree(page_list); if (ret) @@ -477,10 +490,15 @@ struct ib_mr *c4iw_register_phys_mem(struct ib_pd *pd, if (ret) goto err; + if (mr_exceeds_hw_limits(rhp, total_size)) { + kfree(page_list); + ret = -EINVAL; + goto err; + } ret = alloc_pbl(mhp, npages); if (ret) { kfree(page_list); - goto err_pbl; + goto err; } ret = write_pbl(&mhp->rhp->rdev, page_list, mhp->attr.pbl_addr, @@ -580,6 +598,10 @@ struct ib_mr *c4iw_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, php = to_c4iw_pd(pd); rhp = php->rhp; + + if (mr_exceeds_hw_limits(rhp, length)) + return ERR_PTR(-EINVAL); + mhp = kzalloc(sizeof(*mhp), GFP_KERNEL); if (!mhp) return ERR_PTR(-ENOMEM); diff --git a/sys/dev/cxgbe/iw_cxgbe/qp.c b/sys/dev/cxgbe/iw_cxgbe/qp.c index 1c0381c..3ee16b1 100644 --- a/sys/dev/cxgbe/iw_cxgbe/qp.c +++ b/sys/dev/cxgbe/iw_cxgbe/qp.c @@ -615,7 +615,7 @@ int c4iw_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, fw_flags = 0; if (wr->send_flags & IB_SEND_SOLICITED) fw_flags |= FW_RI_SOLICITED_EVENT_FLAG; - if (wr->send_flags & IB_SEND_SIGNALED) + if (wr->send_flags & IB_SEND_SIGNALED || qhp->sq_sig_all) fw_flags |= FW_RI_COMPLETION_FLAG; swsqe = &qhp->wq.sq.sw_sq[qhp->wq.sq.pidx]; switch (wr->opcode) { @@ -673,7 +673,8 @@ int c4iw_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, } swsqe->idx = qhp->wq.sq.pidx; swsqe->complete = 0; - swsqe->signaled = (wr->send_flags & IB_SEND_SIGNALED); + swsqe->signaled = (wr->send_flags & IB_SEND_SIGNALED) || + qhp->sq_sig_all; swsqe->wr_id = wr->wr_id; init_wr_hdr(wqe, qhp->wq.sq.pidx, fw_opcode, fw_flags, len16); @@ -952,7 +953,7 @@ static void __flush_qp(struct c4iw_qp *qhp, struct c4iw_cq *rchp, flushed = c4iw_flush_rq(&qhp->wq, &rchp->cq, count); spin_unlock(&qhp->lock); spin_unlock_irqrestore(&rchp->lock, flag); - if (flushed) { + if (flushed && rchp->ibcq.comp_handler) { spin_lock_irqsave(&rchp->comp_handler_lock, flag); (*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context); spin_unlock_irqrestore(&rchp->comp_handler_lock, flag); @@ -966,7 +967,7 @@ static void __flush_qp(struct c4iw_qp *qhp, struct c4iw_cq *rchp, flushed = c4iw_flush_sq(&qhp->wq, &schp->cq, count); spin_unlock(&qhp->lock); spin_unlock_irqrestore(&schp->lock, flag); - if (flushed) { + if (flushed && schp->ibcq.comp_handler) { spin_lock_irqsave(&schp->comp_handler_lock, flag); (*schp->ibcq.comp_handler)(&schp->ibcq, schp->ibcq.cq_context); spin_unlock_irqrestore(&schp->comp_handler_lock, flag); @@ -1530,6 +1531,7 @@ c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs, qhp->attr.enable_bind = 1; qhp->attr.max_ord = 1; qhp->attr.max_ird = 1; + qhp->sq_sig_all = attrs->sq_sig_type == IB_SIGNAL_ALL_WR; spin_lock_init(&qhp->lock); mutex_init(&qhp->mutex); init_waitqueue_head(&qhp->wait); @@ -1702,6 +1704,12 @@ int c4iw_ib_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, memset(attr, 0, sizeof *attr); memset(init_attr, 0, sizeof *init_attr); attr->qp_state = to_ib_qp_state(qhp->attr.state); + init_attr->cap.max_send_wr = qhp->attr.sq_num_entries; + init_attr->cap.max_recv_wr = qhp->attr.rq_num_entries; + init_attr->cap.max_send_sge = qhp->attr.sq_max_sges; + init_attr->cap.max_recv_sge = qhp->attr.sq_max_sges; + init_attr->cap.max_inline_data = T4_MAX_SEND_INLINE; + init_attr->sq_sig_type = qhp->sq_sig_all ? IB_SIGNAL_ALL_WR : 0; return 0; } #endif diff --git a/sys/dev/cxgbe/iw_cxgbe/t4.h b/sys/dev/cxgbe/iw_cxgbe/t4.h index 023c607..b0118f9 100644 --- a/sys/dev/cxgbe/iw_cxgbe/t4.h +++ b/sys/dev/cxgbe/iw_cxgbe/t4.h @@ -69,7 +69,6 @@ #define T4_MAX_SQ_SIZE (T4_MAX_EQ_SIZE - 1) #define T4_MAX_QP_DEPTH (T4_MAX_RQ_SIZE - 1) #define T4_MAX_CQ_DEPTH (T4_MAX_IQ_SIZE - 1) -#define T4_MAX_NUM_STAG (1<<15) #define T4_MAX_MR_SIZE (~0ULL - 1) #define T4_PAGESIZE_MASK 0xffff000 /* 4KB-128MB */ #define T4_STAG_UNSET 0xffffffff @@ -524,7 +523,7 @@ static inline void t4_swcq_consume(struct t4_cq *cq) static inline void t4_hwcq_consume(struct t4_cq *cq) { cq->bits_type_ts = cq->queue[cq->cidx].bits_type_ts; - if (++cq->cidx_inc == (cq->size >> 4)) { + if (++cq->cidx_inc == (cq->size >> 4) || cq->cidx_inc == M_CIDXINC) { u32 val; val = SEINTARM(0) | CIDXINC(cq->cidx_inc) | TIMERREG(7) | diff --git a/sys/dev/cxgbe/iw_cxgbe/user.h b/sys/dev/cxgbe/iw_cxgbe/user.h index 59a1f43..d42f659 100644 --- a/sys/dev/cxgbe/iw_cxgbe/user.h +++ b/sys/dev/cxgbe/iw_cxgbe/user.h @@ -50,6 +50,7 @@ struct c4iw_create_cq_resp { __u32 cqid; __u32 size; __u32 qid_mask; + __u32 reserved; /* explicit padding (optional for i386) */ }; struct c4iw_create_qp_resp { diff --git a/sys/dev/cxgbe/offload.h b/sys/dev/cxgbe/offload.h index 2b5e4dc..992b4cd 100644 --- a/sys/dev/cxgbe/offload.h +++ b/sys/dev/cxgbe/offload.h @@ -125,7 +125,6 @@ struct t4_virt_res { /* virtualized HW resources */ struct t4_range l2t; }; -#ifdef TCP_OFFLOAD enum { ULD_TOM = 0, ULD_IWARP, @@ -152,6 +151,7 @@ struct tom_tunables { int tx_align; }; +#ifdef TCP_OFFLOAD int t4_register_uld(struct uld_info *); int t4_unregister_uld(struct uld_info *); int t4_activate_uld(struct adapter *, int); diff --git a/sys/dev/cxgbe/t4_main.c b/sys/dev/cxgbe/t4_main.c index 77777f8..11d4253 100644 --- a/sys/dev/cxgbe/t4_main.c +++ b/sys/dev/cxgbe/t4_main.c @@ -334,7 +334,8 @@ TUNABLE_INT("hw.cxgbe.nbmcaps_allowed", &t4_nbmcaps_allowed); static int t4_linkcaps_allowed = 0; /* No DCBX, PPP, etc. by default */ TUNABLE_INT("hw.cxgbe.linkcaps_allowed", &t4_linkcaps_allowed); -static int t4_switchcaps_allowed = 0; +static int t4_switchcaps_allowed = FW_CAPS_CONFIG_SWITCH_INGRESS | + FW_CAPS_CONFIG_SWITCH_EGRESS; TUNABLE_INT("hw.cxgbe.switchcaps_allowed", &t4_switchcaps_allowed); static int t4_niccaps_allowed = FW_CAPS_CONFIG_NIC; @@ -343,13 +344,13 @@ TUNABLE_INT("hw.cxgbe.niccaps_allowed", &t4_niccaps_allowed); static int t4_toecaps_allowed = -1; TUNABLE_INT("hw.cxgbe.toecaps_allowed", &t4_toecaps_allowed); -static int t4_rdmacaps_allowed = 0; +static int t4_rdmacaps_allowed = -1; TUNABLE_INT("hw.cxgbe.rdmacaps_allowed", &t4_rdmacaps_allowed); static int t4_tlscaps_allowed = 0; TUNABLE_INT("hw.cxgbe.tlscaps_allowed", &t4_tlscaps_allowed); -static int t4_iscsicaps_allowed = 0; +static int t4_iscsicaps_allowed = -1; TUNABLE_INT("hw.cxgbe.iscsicaps_allowed", &t4_iscsicaps_allowed); static int t4_fcoecaps_allowed = 0; @@ -1731,29 +1732,29 @@ cxgbe_get_counter(struct ifnet *ifp, ift_counter c) switch (c) { case IFCOUNTER_IPACKETS: - return (s->rx_frames - s->rx_pause); + return (s->rx_frames); case IFCOUNTER_IERRORS: return (s->rx_jabber + s->rx_runt + s->rx_too_long + s->rx_fcs_err + s->rx_len_err); case IFCOUNTER_OPACKETS: - return (s->tx_frames - s->tx_pause); + return (s->tx_frames); case IFCOUNTER_OERRORS: return (s->tx_error_frames); case IFCOUNTER_IBYTES: - return (s->rx_octets - s->rx_pause * 64); + return (s->rx_octets); case IFCOUNTER_OBYTES: - return (s->tx_octets - s->tx_pause * 64); + return (s->tx_octets); case IFCOUNTER_IMCASTS: - return (s->rx_mcast_frames - s->rx_pause); + return (s->rx_mcast_frames); case IFCOUNTER_OMCASTS: - return (s->tx_mcast_frames - s->tx_pause); + return (s->tx_mcast_frames); case IFCOUNTER_IQDROPS: return (s->rx_ovflow0 + s->rx_ovflow1 + s->rx_ovflow2 + @@ -6287,6 +6288,9 @@ mem_region_show(struct sbuf *sb, const char *name, unsigned int from, { unsigned int size; + if (from == to) + return; + size = to - from + 1; if (size == 0) return; @@ -6390,13 +6394,10 @@ sysctl_meminfo(SYSCTL_HANDLER_ARGS) md++; if (t4_read_reg(sc, A_LE_DB_CONFIG) & F_HASHEN) { - if (chip_id(sc) <= CHELSIO_T5) { - hi = t4_read_reg(sc, A_LE_DB_TID_HASHBASE) / 4; + if (chip_id(sc) <= CHELSIO_T5) md->base = t4_read_reg(sc, A_LE_DB_HASH_TID_BASE); - } else { - hi = t4_read_reg(sc, A_LE_DB_HASH_TID_BASE); + else md->base = t4_read_reg(sc, A_LE_DB_HASH_TBL_BASE_ADDR); - } md->limit = 0; } else { md->base = 0; @@ -9103,9 +9104,26 @@ tweak_tunables(void) if (t4_toecaps_allowed == -1) t4_toecaps_allowed = FW_CAPS_CONFIG_TOE; + + if (t4_rdmacaps_allowed == -1) { + t4_rdmacaps_allowed = FW_CAPS_CONFIG_RDMA_RDDP | + FW_CAPS_CONFIG_RDMA_RDMAC; + } + + if (t4_iscsicaps_allowed == -1) { + t4_iscsicaps_allowed = FW_CAPS_CONFIG_ISCSI_INITIATOR_PDU | + FW_CAPS_CONFIG_ISCSI_TARGET_PDU | + FW_CAPS_CONFIG_ISCSI_T10DIF; + } #else if (t4_toecaps_allowed == -1) t4_toecaps_allowed = 0; + + if (t4_rdmacaps_allowed == -1) + t4_rdmacaps_allowed = 0; + + if (t4_iscsicaps_allowed == -1) + t4_iscsicaps_allowed = 0; #endif #ifdef DEV_NETMAP diff --git a/sys/dev/cxgbe/t4_sge.c b/sys/dev/cxgbe/t4_sge.c index 33d8d48..daf3b8a 100644 --- a/sys/dev/cxgbe/t4_sge.c +++ b/sys/dev/cxgbe/t4_sge.c @@ -1397,13 +1397,8 @@ process_iql: #if defined(INET) || defined(INET6) if (iq->flags & IQ_LRO_ENABLED) { struct lro_ctrl *lro = &rxq->lro; - struct lro_entry *l; - while (!SLIST_EMPTY(&lro->lro_active)) { - l = SLIST_FIRST(&lro->lro_active); - SLIST_REMOVE_HEAD(&lro->lro_active, next); - tcp_lro_flush(lro, l); - } + tcp_lro_flush_all(lro); } #endif @@ -2343,8 +2338,8 @@ eth_tx(struct mp_ring *r, u_int cidx, u_int pidx) } else { total++; remaining--; - n = write_txpkt_wr(txq, (void *)wr, m0, available); ETHER_BPF_MTAP(ifp, m0); + n = write_txpkt_wr(txq, (void *)wr, m0, available); } MPASS(n >= 1 && n <= available && n <= SGE_MAX_WR_NDESC); diff --git a/sys/dev/drm2/i915/i915_gem.c b/sys/dev/drm2/i915/i915_gem.c index 7789e36..41ce74c 100644 --- a/sys/dev/drm2/i915/i915_gem.c +++ b/sys/dev/drm2/i915/i915_gem.c @@ -1481,7 +1481,7 @@ i915_gem_pager_fault(vm_object_t vm_obj, vm_ooffset_t offset, int prot, struct drm_i915_gem_object *obj = to_intel_bo(gem_obj); struct drm_device *dev = obj->base.dev; drm_i915_private_t *dev_priv = dev->dev_private; - vm_page_t page, oldpage; + vm_page_t page; int ret = 0; #ifdef FREEBSD_WIP bool write = (prot & VM_PROT_WRITE) != 0; @@ -1504,13 +1504,10 @@ i915_gem_pager_fault(vm_object_t vm_obj, vm_ooffset_t offset, int prot, * progress. */ if (*mres != NULL) { - oldpage = *mres; - vm_page_lock(oldpage); - vm_page_remove(oldpage); - vm_page_unlock(oldpage); - *mres = NULL; - } else - oldpage = NULL; + vm_page_lock(*mres); + vm_page_remove(*mres); + vm_page_unlock(*mres); + } VM_OBJECT_WUNLOCK(vm_obj); retry: ret = 0; @@ -1590,7 +1587,6 @@ retry: } page->valid = VM_PAGE_BITS_ALL; have_page: - *mres = page; vm_page_xbusy(page); CTR4(KTR_DRM, "fault %p %jx %x phys %x", gem_obj, offset, prot, @@ -1603,11 +1599,13 @@ have_page: i915_gem_object_unpin(obj); } DRM_UNLOCK(dev); - if (oldpage != NULL) { - vm_page_lock(oldpage); - vm_page_free(oldpage); - vm_page_unlock(oldpage); + if (*mres != NULL) { + KASSERT(*mres != page, ("loosing %p %p", *mres, page)); + vm_page_lock(*mres); + vm_page_free(*mres); + vm_page_unlock(*mres); } + *mres = page; vm_object_pip_wakeup(vm_obj); return (VM_PAGER_OK); diff --git a/sys/dev/drm2/i915/intel_pm.c b/sys/dev/drm2/i915/intel_pm.c index ab9eee4..ddab457 100644 --- a/sys/dev/drm2/i915/intel_pm.c +++ b/sys/dev/drm2/i915/intel_pm.c @@ -3672,9 +3672,39 @@ static void gen6_init_clock_gating(struct drm_device *dev) ILK_DPARBUNIT_CLOCK_GATE_ENABLE | ILK_DPFDUNIT_CLOCK_GATE_ENABLE); + +#ifdef FREEBSD_WIP + /* NOTE Linux<->FreeBSD: Disable GEN6_MBCTL write. + * + * This arrived in Linux 3.6 in commit + * b4ae3f22d238617ca11610b29fde16cf8c0bc6e0 and causes significantly + * increased power consumption after kldloading i915kms.ko on FreeBSD + * on (some) Sandy Bridge laptops. A Thinkpad X220 reported about 11W + * after booting while idle at the vt(4) console and about double that + * after loading the driver. + * + * There were reports in Linux of increased consumption after a suspend + * and resume cycle due to that change. + * + * Linux bug reports: + * https://bugs.freedesktop.org/show_bug.cgi?id=54089 + * https://bugzilla.kernel.org/show_bug.cgi?id=58971 + * + * This suspend and resume issue is reportedly fixed in Linux with + * commits 7dcd2677ea912573d9ed4bcd629b0023b2d11505 and + * 7dcd2677ea912573d9ed4bcd629b0023b2d11505 (Linux 3.11). However, I + * found that those changes did not help on FreeBSD, where increased + * power consumption is observed after loading i915kms.ko without + * suspending and resuming. + * + * This workaround should be removed after updating to a future Linux + * i915 version and verifying normal power consumption on Sandy Bridge. + */ + /* WaMbcDriverBootEnable */ I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) | GEN6_MBCTL_ENABLE_BOOT_FETCH); +#endif /* FREEBSD_WIP */ for_each_pipe(pipe) { I915_WRITE(DSPCNTR(pipe), diff --git a/sys/dev/drm2/ttm/ttm_bo_vm.c b/sys/dev/drm2/ttm/ttm_bo_vm.c index 5d72d97..96ebeca 100644 --- a/sys/dev/drm2/ttm/ttm_bo_vm.c +++ b/sys/dev/drm2/ttm/ttm_bo_vm.c @@ -106,21 +106,18 @@ ttm_bo_vm_fault(vm_object_t vm_obj, vm_ooffset_t offset, struct ttm_buffer_object *bo = vm_obj->handle; struct ttm_bo_device *bdev = bo->bdev; struct ttm_tt *ttm = NULL; - vm_page_t m, m1, oldm; + vm_page_t m, m1; int ret; int retval = VM_PAGER_OK; struct ttm_mem_type_manager *man = &bdev->man[bo->mem.mem_type]; vm_object_pip_add(vm_obj, 1); - oldm = *mres; - if (oldm != NULL) { - vm_page_lock(oldm); - vm_page_remove(oldm); - vm_page_unlock(oldm); - *mres = NULL; - } else - oldm = NULL; + if (*mres != NULL) { + vm_page_lock(*mres); + vm_page_remove(*mres); + vm_page_unlock(*mres); + } retry: VM_OBJECT_WUNLOCK(vm_obj); m = NULL; @@ -261,14 +258,14 @@ reserve: bo, m, m1, (uintmax_t)offset)); } m->valid = VM_PAGE_BITS_ALL; - *mres = m; vm_page_xbusy(m); - - if (oldm != NULL) { - vm_page_lock(oldm); - vm_page_free(oldm); - vm_page_unlock(oldm); + if (*mres != NULL) { + KASSERT(*mres != m, ("loosing %p %p", *mres, m)); + vm_page_lock(*mres); + vm_page_free(*mres); + vm_page_unlock(*mres); } + *mres = m; out_io_unlock1: ttm_mem_io_unlock(man); diff --git a/sys/dev/e1000/if_igb.c b/sys/dev/e1000/if_igb.c index b1b2c4a..93b33ad 100644 --- a/sys/dev/e1000/if_igb.c +++ b/sys/dev/e1000/if_igb.c @@ -1184,10 +1184,27 @@ igb_ioctl(struct ifnet *ifp, u_long command, caddr_t data) } } #endif +#if __FreeBSD_version >= 1000000 + /* HW cannot turn these on/off separately */ + if (mask & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) { + ifp->if_capenable ^= IFCAP_RXCSUM; + ifp->if_capenable ^= IFCAP_RXCSUM_IPV6; + reinit = 1; + } + if (mask & IFCAP_TXCSUM) { + ifp->if_capenable ^= IFCAP_TXCSUM; + reinit = 1; + } + if (mask & IFCAP_TXCSUM_IPV6) { + ifp->if_capenable ^= IFCAP_TXCSUM_IPV6; + reinit = 1; + } +#else if (mask & IFCAP_HWCSUM) { ifp->if_capenable ^= IFCAP_HWCSUM; reinit = 1; } +#endif if (mask & IFCAP_TSO4) { ifp->if_capenable ^= IFCAP_TSO4; reinit = 1; @@ -1266,14 +1283,26 @@ igb_init_locked(struct adapter *adapter) /* Set hardware offload abilities */ ifp->if_hwassist = 0; if (ifp->if_capenable & IFCAP_TXCSUM) { +#if __FreeBSD_version >= 1000000 + ifp->if_hwassist |= (CSUM_IP_TCP | CSUM_IP_UDP); + if (adapter->hw.mac.type != e1000_82575) + ifp->if_hwassist |= CSUM_IP_SCTP; +#else ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); #if __FreeBSD_version >= 800000 - if ((adapter->hw.mac.type == e1000_82576) || - (adapter->hw.mac.type == e1000_82580)) + if (adapter->hw.mac.type != e1000_82575) ifp->if_hwassist |= CSUM_SCTP; #endif +#endif } +#if __FreeBSD_version >= 1000000 + if (ifp->if_capenable & IFCAP_TXCSUM_IPV6) { + ifp->if_hwassist |= (CSUM_IP6_TCP | CSUM_IP6_UDP); + if (adapter->hw.mac.type != e1000_82575) + ifp->if_hwassist |= CSUM_IP6_SCTP; + } +#endif if (ifp->if_capenable & IFCAP_TSO) ifp->if_hwassist |= CSUM_TSO; @@ -3160,6 +3189,9 @@ igb_setup_interface(device_t dev, struct adapter *adapter) ifp->if_capabilities = ifp->if_capenable = 0; ifp->if_capabilities = IFCAP_HWCSUM | IFCAP_VLAN_HWCSUM; +#if __FreeBSD_version >= 1000000 + ifp->if_capabilities |= IFCAP_HWCSUM_IPV6; +#endif ifp->if_capabilities |= IFCAP_TSO; ifp->if_capabilities |= IFCAP_JUMBO_MTU; ifp->if_capenable = ifp->if_capabilities; @@ -3933,17 +3965,29 @@ igb_tx_ctx_setup(struct tx_ring *txr, struct mbuf *mp, switch (ipproto) { case IPPROTO_TCP: +#if __FreeBSD_version >= 1000000 + if (mp->m_pkthdr.csum_flags & (CSUM_IP_TCP | CSUM_IP6_TCP)) +#else if (mp->m_pkthdr.csum_flags & CSUM_TCP) +#endif type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_L4T_TCP; break; case IPPROTO_UDP: +#if __FreeBSD_version >= 1000000 + if (mp->m_pkthdr.csum_flags & (CSUM_IP_UDP | CSUM_IP6_UDP)) +#else if (mp->m_pkthdr.csum_flags & CSUM_UDP) +#endif type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_L4T_UDP; break; #if __FreeBSD_version >= 800000 case IPPROTO_SCTP: +#if __FreeBSD_version >= 1000000 + if (mp->m_pkthdr.csum_flags & (CSUM_IP_SCTP | CSUM_IP6_SCTP)) +#else if (mp->m_pkthdr.csum_flags & CSUM_SCTP) +#endif type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_L4T_SCTP; break; #endif @@ -4701,8 +4745,7 @@ igb_initialize_receive_units(struct adapter *adapter) rxcsum |= E1000_RXCSUM_PCSD; #if __FreeBSD_version >= 800000 /* For SCTP Offload */ - if (((hw->mac.type == e1000_82576) || - (hw->mac.type == e1000_82580)) && + if ((hw->mac.type != e1000_82575) && (ifp->if_capenable & IFCAP_RXCSUM)) rxcsum |= E1000_RXCSUM_CRCOFL; #endif @@ -4711,8 +4754,7 @@ igb_initialize_receive_units(struct adapter *adapter) if (ifp->if_capenable & IFCAP_RXCSUM) { rxcsum |= E1000_RXCSUM_IPPCSE; #if __FreeBSD_version >= 800000 - if ((adapter->hw.mac.type == e1000_82576) || - (adapter->hw.mac.type == e1000_82580)) + if (adapter->hw.mac.type != e1000_82575) rxcsum |= E1000_RXCSUM_CRCOFL; #endif } else @@ -4932,7 +4974,6 @@ igb_rxeof(struct igb_queue *que, int count, int *done) struct rx_ring *rxr = que->rxr; struct ifnet *ifp = adapter->ifp; struct lro_ctrl *lro = &rxr->lro; - struct lro_entry *queued; int i, processed = 0, rxdone = 0; u32 ptype, staterr = 0; union e1000_adv_rx_desc *cur; @@ -5160,10 +5201,7 @@ next_desc: /* * Flush any outstanding LRO work */ - while ((queued = SLIST_FIRST(&lro->lro_active)) != NULL) { - SLIST_REMOVE_HEAD(&lro->lro_active, next); - tcp_lro_flush(lro, queued); - } + tcp_lro_flush_all(lro); if (done != NULL) *done += rxdone; diff --git a/sys/dev/e1000/if_igb.h b/sys/dev/e1000/if_igb.h index 98df1ec..a0c35be 100644 --- a/sys/dev/e1000/if_igb.h +++ b/sys/dev/e1000/if_igb.h @@ -291,7 +291,11 @@ #define ETH_ADDR_LEN 6 /* Offload bits in mbuf flag */ -#if __FreeBSD_version >= 800000 +#if __FreeBSD_version >= 1000000 +#define CSUM_OFFLOAD_IPV4 (CSUM_IP|CSUM_IP_TCP|CSUM_IP_UDP|CSUM_IP_SCTP) +#define CSUM_OFFLOAD_IPV6 (CSUM_IP6_TCP|CSUM_IP6_UDP|CSUM_IP6_SCTP) +#define CSUM_OFFLOAD (CSUM_OFFLOAD_IPV4|CSUM_OFFLOAD_IPV6) +#elif __FreeBSD_version >= 800000 #define CSUM_OFFLOAD (CSUM_IP|CSUM_TCP|CSUM_UDP|CSUM_SCTP) #else #define CSUM_OFFLOAD (CSUM_IP|CSUM_TCP|CSUM_UDP) diff --git a/sys/dev/ed/if_ed_3c503.c b/sys/dev/ed/if_ed_3c503.c index 353fbbf..bcb9e44 100644 --- a/sys/dev/ed/if_ed_3c503.c +++ b/sys/dev/ed/if_ed_3c503.c @@ -324,7 +324,7 @@ ed_probe_3Com(device_t dev, int port_rid, int flags) ed_asic_outb(sc, ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ5); break; default: - device_printf(dev, "Invalid irq configuration (%ld) must be 3-5,9 for 3c503\n", + device_printf(dev, "Invalid irq configuration (%jd) must be 3-5,9 for 3c503\n", irq); return (ENXIO); } diff --git a/sys/dev/ed/if_ed_cbus.c b/sys/dev/ed/if_ed_cbus.c index f9672ac..7926fa8 100644 --- a/sys/dev/ed/if_ed_cbus.c +++ b/sys/dev/ed/if_ed_cbus.c @@ -1021,7 +1021,7 @@ ed_probe_CNET98(device_t dev, int port_rid, int flags) if (((rman_get_start(sc->port_res) & 0x0fff) != 0x03d0) || ((rman_get_start(sc->port_res) & 0xf000) < (u_short) 0xa000)) { #ifdef DIAGNOSTIC - device_printf(dev, "Invalid i/o port configuration (0x%lx) " + device_printf(dev, "Invalid i/o port configuration (0x%jx) " "must be %s for %s\n", rman_get_start(sc->port_res), "0x[a-f]3d0", "CNET98"); #endif @@ -1032,7 +1032,7 @@ ed_probe_CNET98(device_t dev, int port_rid, int flags) /* Check window area address */ tmp_s = rman_get_start(sc->mem_res) >> 12; if (tmp_s < 0x80) { - device_printf(dev, "Please change window address(0x%lx)\n", + device_printf(dev, "Please change window address(0x%jx)\n", rman_get_start(sc->mem_res)); return (ENXIO); } @@ -1040,8 +1040,8 @@ ed_probe_CNET98(device_t dev, int port_rid, int flags) tmp_s &= 0x0f; tmp = rman_get_start(sc->port_res) >> 12; if ((tmp_s <= tmp) && (tmp < (tmp_s + 4))) { - device_printf(dev, "Please change iobase address(0x%lx) " - "or window address(0x%lx)\n", + device_printf(dev, "Please change iobase address(0x%jx) " + "or window address(0x%jx)\n", rman_get_start(sc->port_res), rman_get_start(sc->mem_res)); return (ENXIO); @@ -1128,7 +1128,7 @@ ed_probe_CNET98(device_t dev, int port_rid, int flags) tmp = ED_CNET98_INT_IRQ13; break; default: - device_printf(dev, "Invalid irq configuration (%ld) must be " + device_printf(dev, "Invalid irq configuration (%jd) must be " "%s for %s\n", conf_irq, "3,5,6,9,12,13", "CNET98"); return (ENXIO); } @@ -1169,7 +1169,7 @@ ed_probe_CNET98EL(device_t dev, int port_rid, int flags) /* Check I/O address. 0x[0-f]3d0 are allowed. */ if ((rman_get_start(sc->port_res) & 0x0fff) != 0x03d0) { #ifdef DIAGNOSTIC - device_printf(dev, "Invalid i/o port configuration (0x%lx) " + device_printf(dev, "Invalid i/o port configuration (0x%jx) " "must be %s for %s\n", rman_get_start(sc->port_res), "0x?3d0", "CNET98E/L"); #endif @@ -1223,7 +1223,7 @@ ed_probe_CNET98EL(device_t dev, int port_rid, int flags) break; #endif default: - device_printf(dev, "Invalid irq configuration (%ld) must be " + device_printf(dev, "Invalid irq configuration (%jd) must be " "%s for %s\n", conf_irq, "3,5,6", "CNET98E/L"); return (ENXIO); } @@ -1285,7 +1285,7 @@ ed_probe_NEC77(device_t dev, int port_rid, int flags) tmp = ED_NEC77_IRQ13; break; default: - device_printf(dev, "Invalid irq configuration (%ld) must be " + device_printf(dev, "Invalid irq configuration (%jd) must be " "%s for %s\n", conf_irq, "3,5,6,12,13", "PC-9801-77"); return (ENXIO); } @@ -1337,7 +1337,7 @@ ed_probe_NW98X(device_t dev, int port_rid, int flags) tmp = ED_NW98X_IRQ13; break; default: - device_printf(dev, "Invalid irq configuration (%ld) must be " + device_printf(dev, "Invalid irq configuration (%jd) must be " "%s for %s\n", conf_irq, "3,5,6,12,13", "EC/EP-98X"); return (ENXIO); } @@ -1439,7 +1439,7 @@ ed_probe_SB98(device_t dev, int port_rid, int flags) /* Check I/O address. 00d[02468ace] are allowed. */ if ((rman_get_start(sc->port_res) & ~0x000e) != 0x00d0) { #ifdef DIAGNOSTIC - device_printf(dev, "Invalid i/o port configuration (0x%lx) " + device_printf(dev, "Invalid i/o port configuration (0x%jx) " "must be %s for %s\n", rman_get_start(sc->port_res), "0xd?", "SB9801"); #endif @@ -1475,7 +1475,7 @@ ed_probe_SB98(device_t dev, int port_rid, int flags) tmp = ED_SB98_CFG_IRQ12; break; default: - device_printf(dev, "Invalid irq configuration (%ld) must be " + device_printf(dev, "Invalid irq configuration (%jd) must be " "%s for %s\n", conf_irq, "3,5,6,12", "SB9801"); return (ENXIO); } diff --git a/sys/dev/extres/clk/clk.c b/sys/dev/extres/clk/clk.c index f1ed098..07cbd6b 100644 --- a/sys/dev/extres/clk/clk.c +++ b/sys/dev/extres/clk/clk.c @@ -487,10 +487,10 @@ clkdom_dump(struct clkdom * clkdom) CLK_TOPO_SLOCK(); TAILQ_FOREACH(clknode, &clkdom->clknode_list, clkdom_link) { rv = clknode_get_freq(clknode, &freq); - printf("Clock: %s, parent: %s(%d), freq: %llu\n", clknode->name, + printf("Clock: %s, parent: %s(%d), freq: %ju\n", clknode->name, clknode->parent == NULL ? "(NULL)" : clknode->parent->name, clknode->parent_idx, - ((rv == 0) ? freq: rv)); + (uintmax_t)((rv == 0) ? freq: rv)); } CLK_TOPO_UNLOCK(); } @@ -818,6 +818,10 @@ clknode_set_freq(struct clknode *clknode, uint64_t freq, int flags, /* We have exclusive topology lock, node lock is not needed. */ CLK_TOPO_XASSERT(); + /* Check for no change */ + if (clknode->freq == freq) + return (0); + parent_freq = 0; /* @@ -1258,4 +1262,75 @@ clk_get_by_ofw_name(device_t dev, const char *name, clk_t *clk) return (rv); return (clk_get_by_ofw_index(dev, idx, clk)); } + +/* -------------------------------------------------------------------------- + * + * Support functions for parsing various clock related OFW things. + */ + +/* + * Get "clock-output-names" and (optional) "clock-indices" lists. + * Both lists are alocated using M_OFWPROP specifier. + * + * Returns number of items or 0. + */ +int +clk_parse_ofw_out_names(device_t dev, phandle_t node, const char ***out_names, + uint32_t **indices) +{ + int name_items, rv; + + *out_names = NULL; + *indices = NULL; + if (!OF_hasprop(node, "clock-output-names")) + return (0); + rv = ofw_bus_string_list_to_array(node, "clock-output-names", + out_names); + if (rv <= 0) + return (0); + name_items = rv; + + if (!OF_hasprop(node, "clock-indices")) + return (name_items); + rv = OF_getencprop_alloc(node, "clock-indices", sizeof (uint32_t), + (void **)indices); + if (rv != name_items) { + device_printf(dev, " Size of 'clock-output-names' and " + "'clock-indices' differs\n"); + free(*out_names, M_OFWPROP); + free(*indices, M_OFWPROP); + return (0); + } + return (name_items); +} + +/* + * Get output clock name for single output clock node. + */ +int +clk_parse_ofw_clk_name(device_t dev, phandle_t node, const char **name) +{ + const char **out_names; + const char *tmp_name; + int rv; + + *name = NULL; + if (!OF_hasprop(node, "clock-output-names")) { + tmp_name = ofw_bus_get_name(dev); + if (tmp_name == NULL) + return (ENXIO); + *name = strdup(tmp_name, M_OFWPROP); + return (0); + } + rv = ofw_bus_string_list_to_array(node, "clock-output-names", + &out_names); + if (rv != 1) { + free(out_names, M_OFWPROP); + device_printf(dev, "Malformed 'clock-output-names' property\n"); + return (ENXIO); + } + *name = strdup(out_names[0], M_OFWPROP); + free(out_names, M_OFWPROP); + return (0); +} #endif diff --git a/sys/dev/extres/clk/clk.h b/sys/dev/extres/clk/clk.h index 8547bae..510f0ce 100644 --- a/sys/dev/extres/clk/clk.h +++ b/sys/dev/extres/clk/clk.h @@ -131,6 +131,9 @@ const char *clk_get_name(clk_t clk); #ifdef FDT int clk_get_by_ofw_index(device_t dev, int idx, clk_t *clk); int clk_get_by_ofw_name(device_t dev, const char *name, clk_t *clk); +int clk_parse_ofw_out_names(device_t dev, phandle_t node, + const char ***out_names, uint32_t **indices); +int clk_parse_ofw_clk_name(device_t dev, phandle_t node, const char **name); #endif #endif /* _DEV_EXTRES_CLK_H_ */ diff --git a/sys/dev/extres/clk/clk_bus.c b/sys/dev/extres/clk/clk_bus.c new file mode 100644 index 0000000..e475736 --- /dev/null +++ b/sys/dev/extres/clk/clk_bus.c @@ -0,0 +1,93 @@ +/*- + * Copyright 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> + +#include <dev/fdt/simplebus.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_bus_subr.h> + +struct ofw_clkbus_softc { + struct simplebus_softc simplebus_sc; +}; + +static int +ofw_clkbus_probe(device_t dev) +{ + const char *name; + + name = ofw_bus_get_name(dev); + + if (name == NULL || strcmp(name, "clocks") != 0) + return (ENXIO); + + device_set_desc(dev, "OFW clocks bus"); + + return (BUS_PROBE_GENERIC); +} + +static int +ofw_clkbus_attach(device_t dev) +{ + struct ofw_clkbus_softc *sc; + phandle_t node, child; + device_t cdev; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + simplebus_init(dev, node); + + for (child = OF_child(node); child > 0; child = OF_peer(child)) { + cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL); + if (cdev != NULL) + device_probe_and_attach(cdev); + } + + return (bus_generic_attach(dev)); +} + +static device_method_t ofw_clkbus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ofw_clkbus_probe), + DEVMETHOD(device_attach, ofw_clkbus_attach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(ofw_clkbus, ofw_clkbus_driver, ofw_clkbus_methods, + sizeof(struct ofw_clkbus_softc), simplebus_driver); +static devclass_t ofw_clkbus_devclass; +EARLY_DRIVER_MODULE(ofw_clkbus, simplebus, ofw_clkbus_driver, + ofw_clkbus_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(ofw_clkbus, 1); diff --git a/sys/dev/extres/clk/clk_div.c b/sys/dev/extres/clk/clk_div.c index fcb0a57..51cc838 100644 --- a/sys/dev/extres/clk/clk_div.c +++ b/sys/dev/extres/clk/clk_div.c @@ -46,6 +46,10 @@ __FBSDID("$FreeBSD$"); CLKDEV_READ_4(clknode_get_device(_clk), off, val) #define MD4(_clk, off, clr, set ) \ CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set) +#define DEVICE_LOCK(_clk) \ + CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) +#define DEVICE_UNLOCK(_clk) \ + CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) static int clknode_div_init(struct clknode *clk, device_t dev); static int clknode_div_recalc(struct clknode *clk, uint64_t *req); @@ -86,7 +90,9 @@ clknode_div_init(struct clknode *clk, device_t dev) sc = clknode_get_softc(clk); + DEVICE_LOCK(clk); rv = RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); if (rv != 0) return (rv); @@ -171,12 +177,17 @@ clknode_div_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, (*fout != (_fin / divider))) return (ERANGE); + DEVICE_LOCK(clk); rv = MD4(clk, sc->offset, (sc->i_mask << sc->i_shift) | (sc->f_mask << sc->f_shift), (i_div << sc->i_shift) | (f_div << sc->f_shift)); - if (rv != 0) + if (rv != 0) { + DEVICE_UNLOCK(clk); return (rv); + } RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + sc->divider = divider; } diff --git a/sys/dev/extres/clk/clk_fixed.c b/sys/dev/extres/clk/clk_fixed.c index 4ddceae..02721ae 100644 --- a/sys/dev/extres/clk/clk_fixed.c +++ b/sys/dev/extres/clk/clk_fixed.c @@ -27,7 +27,6 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <sys/conf.h> #include <sys/bus.h> @@ -35,20 +34,25 @@ __FBSDID("$FreeBSD$"); #include <sys/kobj.h> #include <sys/malloc.h> #include <sys/mutex.h> +#include <sys/module.h> #include <sys/rman.h> #include <sys/systm.h> #include <machine/bus.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> #include <dev/extres/clk/clk_fixed.h> -#define DEVICE_LOCK(_sc) mtx_lock((_sc)->mtx) -#define DEVICE_UNLOCK(_sc) mtx_unlock((_sc)->mtx) +#define CLK_TYPE_FIXED 1 +#define CLK_TYPE_FIXED_FACTOR 2 static int clknode_fixed_init(struct clknode *clk, device_t dev); static int clknode_fixed_recalc(struct clknode *clk, uint64_t *freq); +static int clknode_fixed_set_freq(struct clknode *clk, uint64_t fin, + uint64_t *fout, int flags, int *stop); + struct clknode_fixed_sc { - struct mtx *mtx; int fixed_flags; uint64_t freq; uint32_t mult; @@ -59,6 +63,7 @@ static clknode_method_t clknode_fixed_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, clknode_fixed_init), CLKNODEMETHOD(clknode_recalc_freq, clknode_fixed_recalc), + CLKNODEMETHOD(clknode_set_freq, clknode_fixed_set_freq), CLKNODEMETHOD_END }; DEFINE_CLASS_1(clknode_fixed, clknode_fixed_class, clknode_fixed_methods, @@ -74,36 +79,52 @@ clknode_fixed_init(struct clknode *clk, device_t dev) clknode_init_parent_idx(clk, 0); return(0); } + static int clknode_fixed_recalc(struct clknode *clk, uint64_t *freq) { struct clknode_fixed_sc *sc; sc = clknode_get_softc(clk); - if (sc->freq != 0) - *freq = sc->freq; - else if ((sc->mult != 0) && (sc->div != 0)) + + if ((sc->mult != 0) && (sc->div != 0)) *freq = (*freq / sc->div) * sc->mult; else - *freq = 0; + *freq = sc->freq; + return (0); +} + +static int +clknode_fixed_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct clknode_fixed_sc *sc; + + sc = clknode_get_softc(clk); + if (sc->mult == 0 || sc->div == 0) { + /* Fixed frequency clock. */ + *stop = 1; + if (*fout != sc->freq) + return (ERANGE); + return (0); + } + /* Fixed factor clock. */ + *stop = 0; + *fout = (*fout / sc->mult) * sc->div; return (0); } int -clknode_fixed_register(struct clkdom *clkdom, struct clk_fixed_def *clkdef, - struct mtx *dev_mtx) +clknode_fixed_register(struct clkdom *clkdom, struct clk_fixed_def *clkdef) { struct clknode *clk; struct clknode_fixed_sc *sc; - if ((clkdef->freq == 0) && (clkdef->clkdef.parent_cnt == 0)) - panic("fixed clk: Frequency is not defined for clock source"); clk = clknode_create(clkdom, &clknode_fixed_class, &clkdef->clkdef); if (clk == NULL) return (1); sc = clknode_get_softc(clk); - sc->mtx = dev_mtx; sc->fixed_flags = clkdef->fixed_flags; sc->freq = clkdef->freq; sc->mult = clkdef->mult; @@ -112,3 +133,150 @@ clknode_fixed_register(struct clkdom *clkdom, struct clk_fixed_def *clkdef, clknode_register(clkdom, clk); return (0); } + +#ifdef FDT + +static struct ofw_compat_data compat_data[] = { + {"fixed-clock", CLK_TYPE_FIXED}, + {"fixed-factor-clock", CLK_TYPE_FIXED_FACTOR}, + {NULL, 0}, +}; + +struct clk_fixed_softc { + device_t dev; + struct clkdom *clkdom; +}; + +static int +clk_fixed_probe(device_t dev) +{ + intptr_t clk_type; + + clk_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + switch (clk_type) { + case CLK_TYPE_FIXED: + device_set_desc(dev, "Fixed clock"); + return (BUS_PROBE_DEFAULT); + case CLK_TYPE_FIXED_FACTOR: + device_set_desc(dev, "Fixed factor clock"); + return (BUS_PROBE_DEFAULT); + default: + return (ENXIO); + } +} + +static int +clk_fixed_init_fixed(struct clk_fixed_softc *sc, phandle_t node, + struct clk_fixed_def *def) +{ + uint32_t freq; + int rv; + + def->clkdef.id = 1; + rv = OF_getencprop(node, "clock-frequency", &freq, sizeof(freq)); + if (rv <= 0) + return (ENXIO); + def->freq = freq; + return (0); +} + +static int +clk_fixed_init_fixed_factor(struct clk_fixed_softc *sc, phandle_t node, + struct clk_fixed_def *def) +{ + int rv; + clk_t parent; + + def->clkdef.id = 1; + rv = OF_getencprop(node, "clock-mult", &def->mult, sizeof(def->mult)); + if (rv <= 0) + return (ENXIO); + rv = OF_getencprop(node, "clock-div", &def->div, sizeof(def->div)); + if (rv <= 0) + return (ENXIO); + /* Get name of parent clock */ + rv = clk_get_by_ofw_index(sc->dev, 0, &parent); + if (rv != 0) + return (ENXIO); + def->clkdef.parent_names = malloc(sizeof(char *), M_OFWPROP, M_WAITOK); + def->clkdef.parent_names[0] = clk_get_name(parent); + def->clkdef.parent_cnt = 1; + clk_release(parent); + return (0); +} + +static int +clk_fixed_attach(device_t dev) +{ + struct clk_fixed_softc *sc; + intptr_t clk_type; + phandle_t node; + struct clk_fixed_def def; + int rv; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + clk_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + bzero(&def, sizeof(def)); + if (clk_type == CLK_TYPE_FIXED) + rv = clk_fixed_init_fixed(sc, node, &def); + else if (clk_type == CLK_TYPE_FIXED_FACTOR) + rv = clk_fixed_init_fixed_factor(sc, node, &def); + else + rv = ENXIO; + if (rv != 0) { + device_printf(sc->dev, "Cannot FDT parameters.\n"); + goto fail; + } + rv = clk_parse_ofw_clk_name(dev, node, &def.clkdef.name); + if (rv != 0) { + device_printf(sc->dev, "Cannot parse clock name.\n"); + goto fail; + } + sc->clkdom = clkdom_create(dev); + KASSERT(sc->clkdom != NULL, ("Clock domain is NULL")); + + rv = clknode_fixed_register(sc->clkdom, &def); + if (rv != 0) { + device_printf(sc->dev, "Cannot register fixed clock.\n"); + rv = ENXIO; + goto fail; + } + + rv = clkdom_finit(sc->clkdom); + if (rv != 0) { + device_printf(sc->dev, "Clk domain finit fails.\n"); + rv = ENXIO; + goto fail; + } +#ifdef CLK_DEBUG + clkdom_dump(sc->clkdom); +#endif + free(__DECONST(char *, def.clkdef.name), M_OFWPROP); + free(def.clkdef.parent_names, M_OFWPROP); + return (bus_generic_attach(dev)); + +fail: + free(__DECONST(char *, def.clkdef.name), M_OFWPROP); + free(def.clkdef.parent_names, M_OFWPROP); + return (rv); +} + +static device_method_t clk_fixed_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, clk_fixed_probe), + DEVMETHOD(device_attach, clk_fixed_attach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(clk_fixed, clk_fixed_driver, clk_fixed_methods, + sizeof(struct clk_fixed_softc)); +static devclass_t clk_fixed_devclass; +EARLY_DRIVER_MODULE(clk_fixed, simplebus, clk_fixed_driver, + clk_fixed_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(clk_fixed, 1); + +#endif diff --git a/sys/dev/extres/clk/clk_fixed.h b/sys/dev/extres/clk/clk_fixed.h index f57ee96..e2643ff 100644 --- a/sys/dev/extres/clk/clk_fixed.h +++ b/sys/dev/extres/clk/clk_fixed.h @@ -47,7 +47,6 @@ struct clk_fixed_def { int fixed_flags; }; -int clknode_fixed_register(struct clkdom *clkdom, struct clk_fixed_def *clkdef, - struct mtx *dev_mtx); +int clknode_fixed_register(struct clkdom *clkdom, struct clk_fixed_def *clkdef); #endif /*_DEV_EXTRES_CLK_FIXED_H_*/ diff --git a/sys/dev/extres/clk/clk_gate.c b/sys/dev/extres/clk/clk_gate.c index 2107450..e0673fd 100644 --- a/sys/dev/extres/clk/clk_gate.c +++ b/sys/dev/extres/clk/clk_gate.c @@ -46,7 +46,10 @@ __FBSDID("$FreeBSD$"); CLKDEV_READ_4(clknode_get_device(_clk), off, val) #define MD4(_clk, off, clr, set ) \ CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set) - +#define DEVICE_LOCK(_clk) \ + CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) +#define DEVICE_UNLOCK(_clk) \ + CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) static int clknode_gate_init(struct clknode *clk, device_t dev); static int clknode_gate_set_gate(struct clknode *clk, bool enable); @@ -77,7 +80,9 @@ clknode_gate_init(struct clknode *clk, device_t dev) int rv; sc = clknode_get_softc(clk); + DEVICE_LOCK(clk); rv = RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); if (rv != 0) return (rv); reg = (reg >> sc->shift) & sc->mask; @@ -95,11 +100,15 @@ clknode_gate_set_gate(struct clknode *clk, bool enable) sc = clknode_get_softc(clk); sc->ungated = enable; + DEVICE_LOCK(clk); rv = MD4(clk, sc->offset, sc->mask << sc->shift, (sc->ungated ? sc->on_value : sc->off_value) << sc->shift); - if (rv != 0) + if (rv != 0) { + DEVICE_UNLOCK(clk); return (rv); + } RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); return(0); } diff --git a/sys/dev/extres/clk/clk_mux.c b/sys/dev/extres/clk/clk_mux.c index 54e0653..624f587 100644 --- a/sys/dev/extres/clk/clk_mux.c +++ b/sys/dev/extres/clk/clk_mux.c @@ -46,6 +46,10 @@ __FBSDID("$FreeBSD$"); CLKDEV_READ_4(clknode_get_device(_clk), off, val) #define MD4(_clk, off, clr, set ) \ CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set) +#define DEVICE_LOCK(_clk) \ + CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) +#define DEVICE_UNLOCK(_clk) \ + CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) static int clknode_mux_init(struct clknode *clk, device_t dev); static int clknode_mux_set_mux(struct clknode *clk, int idx); @@ -76,9 +80,12 @@ clknode_mux_init(struct clknode *clk, device_t dev) sc = clknode_get_softc(clk); + DEVICE_LOCK(clk); rv = RD4(clk, sc->offset, ®); - if (rv != 0) + DEVICE_UNLOCK(clk); + if (rv != 0) { return (rv); + } reg = (reg >> sc->shift) & sc->mask; clknode_init_parent_idx(clk, reg); return(0); @@ -93,11 +100,16 @@ clknode_mux_set_mux(struct clknode *clk, int idx) sc = clknode_get_softc(clk); + DEVICE_LOCK(clk); rv = MD4(clk, sc->offset, sc->mask << sc->shift, (idx & sc->mask) << sc->shift); - if (rv != 0) + if (rv != 0) { + DEVICE_UNLOCK(clk); return (rv); + } RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + return(0); } diff --git a/sys/dev/extres/clk/clkdev_if.m b/sys/dev/extres/clk/clkdev_if.m index b43d205..f9f5084 100644 --- a/sys/dev/extres/clk/clkdev_if.m +++ b/sys/dev/extres/clk/clkdev_if.m @@ -30,6 +30,71 @@ INTERFACE clkdev; +CODE { + #include <sys/systm.h> + #include <sys/bus.h> + static int + clkdev_default_write_4(device_t dev, bus_addr_t addr, uint32_t val) + { + device_t pdev; + + pdev = device_get_parent(dev); + if (pdev == NULL) + return (ENXIO); + + return (CLKDEV_WRITE_4(pdev, addr, val)); + } + + static int + clkdev_default_read_4(device_t dev, bus_addr_t addr, uint32_t *val) + { + device_t pdev; + + pdev = device_get_parent(dev); + if (pdev == NULL) + return (ENXIO); + + return (CLKDEV_READ_4(pdev, addr, val)); + } + + static int + clkdev_default_modify_4(device_t dev, bus_addr_t addr, + uint32_t clear_mask, uint32_t set_mask) + { + device_t pdev; + + pdev = device_get_parent(dev); + if (pdev == NULL) + return (ENXIO); + + return (CLKDEV_MODIFY_4(pdev, addr, clear_mask, set_mask)); + } + + static void + clkdev_default_device_lock(device_t dev) + { + device_t pdev; + + pdev = device_get_parent(dev); + if (pdev == NULL) + panic("clkdev_device_lock not implemented"); + + CLKDEV_DEVICE_LOCK(pdev); + } + + static void + clkdev_default_device_unlock(device_t dev) + { + device_t pdev; + + pdev = device_get_parent(dev); + if (pdev == NULL) + panic("clkdev_device_unlock not implemented"); + + CLKDEV_DEVICE_UNLOCK(pdev); + } +} + # # Write single register # @@ -37,7 +102,7 @@ METHOD int write_4 { device_t dev; bus_addr_t addr; uint32_t val; -}; +} DEFAULT clkdev_default_write_4; # # Read single register @@ -46,7 +111,7 @@ METHOD int read_4 { device_t dev; bus_addr_t addr; uint32_t *val; -}; +} DEFAULT clkdev_default_read_4; # # Modify single register @@ -56,4 +121,18 @@ METHOD int modify_4 { bus_addr_t addr; uint32_t clear_mask; uint32_t set_mask; -}; +} DEFAULT clkdev_default_modify_4; + +# +# Get exclusive access to underlying device +# +METHOD void device_lock { + device_t dev; +} DEFAULT clkdev_default_device_lock; + +# +# Release exclusive access to underlying device +# +METHOD void device_unlock { + device_t dev; +} DEFAULT clkdev_default_device_unlock; diff --git a/sys/dev/extres/phy/phy.c b/sys/dev/extres/phy/phy.c new file mode 100644 index 0000000..0e2c7e1 --- /dev/null +++ b/sys/dev/extres/phy/phy.c @@ -0,0 +1,235 @@ +/*- + * Copyright 2016 Michal Meloun <mmel@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#include "opt_platform.h" +#include <sys/cdefs.h> +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/systm.h> + +#ifdef FDT +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#endif + +#include <dev/extres/phy/phy.h> + +#include "phy_if.h" + +struct phy { + device_t consumer_dev; /* consumer device*/ + device_t provider_dev; /* provider device*/ + uintptr_t phy_id; /* phy id */ +}; + +MALLOC_DEFINE(M_PHY, "phy", "Phy framework"); + +int +phy_init(device_t consumer, phy_t phy) +{ + + return (PHY_INIT(phy->provider_dev, phy->phy_id, true)); +} + +int +phy_deinit(device_t consumer, phy_t phy) +{ + + return (PHY_INIT(phy->provider_dev, phy->phy_id, false)); +} + + +int +phy_enable(device_t consumer, phy_t phy) +{ + + return (PHY_ENABLE(phy->provider_dev, phy->phy_id, true)); +} + +int +phy_disable(device_t consumer, phy_t phy) +{ + + return (PHY_ENABLE(phy->provider_dev, phy->phy_id, false)); +} + +int +phy_status(device_t consumer, phy_t phy, int *value) +{ + + return (PHY_STATUS(phy->provider_dev, phy->phy_id, value)); +} + +int +phy_get_by_id(device_t consumer_dev, device_t provider_dev, intptr_t id, + phy_t *phy_out) +{ + phy_t phy; + + /* Create handle */ + phy = malloc(sizeof(struct phy), M_PHY, + M_WAITOK | M_ZERO); + phy->consumer_dev = consumer_dev; + phy->provider_dev = provider_dev; + phy->phy_id = id; + *phy_out = phy; + return (0); +} + +void +phy_release(phy_t phy) +{ + free(phy, M_PHY); +} + + +#ifdef FDT +int phy_default_map(device_t provider, phandle_t xref, int ncells, + pcell_t *cells, intptr_t *id) +{ + + if (ncells == 0) + *id = 1; + else if (ncells == 1) + *id = cells[0]; + else + return (ERANGE); + + return (0); +} + +int +phy_get_by_ofw_idx(device_t consumer_dev, int idx, phy_t *phy) +{ + phandle_t cnode, xnode; + pcell_t *cells; + device_t phydev; + int ncells, rv; + intptr_t id; + + cnode = ofw_bus_get_node(consumer_dev); + if (cnode <= 0) { + device_printf(consumer_dev, + "%s called on not ofw based device\n", __func__); + return (ENXIO); + } + rv = ofw_bus_parse_xref_list_alloc(cnode, "phys", "#phy-cells", idx, + &xnode, &ncells, &cells); + if (rv != 0) + return (rv); + + /* Tranlate provider to device. */ + phydev = OF_device_from_xref(xnode); + if (phydev == NULL) { + free(cells, M_OFWPROP); + return (ENODEV); + } + /* Map phy to number. */ + rv = PHY_MAP(phydev, xnode, ncells, cells, &id); + free(cells, M_OFWPROP); + if (rv != 0) + return (rv); + + return (phy_get_by_id(consumer_dev, phydev, id, phy)); +} + +int +phy_get_by_ofw_name(device_t consumer_dev, char *name, phy_t *phy) +{ + int rv, idx; + phandle_t cnode; + + cnode = ofw_bus_get_node(consumer_dev); + if (cnode <= 0) { + device_printf(consumer_dev, + "%s called on not ofw based device\n", __func__); + return (ENXIO); + } + rv = ofw_bus_find_string_index(cnode, "phy-names", name, &idx); + if (rv != 0) + return (rv); + return (phy_get_by_ofw_idx(consumer_dev, idx, phy)); +} + +int +phy_get_by_ofw_property(device_t consumer_dev, char *name, phy_t *phy) +{ + phandle_t cnode; + pcell_t *cells; + device_t phydev; + int ncells, rv; + intptr_t id; + + cnode = ofw_bus_get_node(consumer_dev); + if (cnode <= 0) { + device_printf(consumer_dev, + "%s called on not ofw based device\n", __func__); + return (ENXIO); + } + ncells = OF_getencprop_alloc(cnode, name, sizeof(pcell_t), + (void **)&cells); + if (ncells < 1) + return (ENXIO); + + /* Tranlate provider to device. */ + phydev = OF_device_from_xref(cells[0]); + if (phydev == NULL) { + free(cells, M_OFWPROP); + return (ENODEV); + } + /* Map phy to number. */ + rv = PHY_MAP(phydev, cells[0], ncells - 1 , cells + 1, &id); + free(cells, M_OFWPROP); + if (rv != 0) + return (rv); + + return (phy_get_by_id(consumer_dev, phydev, id, phy)); +} + +void +phy_register_provider(device_t provider_dev) +{ + phandle_t xref, node; + + node = ofw_bus_get_node(provider_dev); + if (node <= 0) + panic("%s called on not ofw based device.\n", __func__); + + xref = OF_xref_from_node(node); + OF_device_register_xref(xref, provider_dev); +} + +void +phy_unregister_provider(device_t provider_dev) +{ + phandle_t xref; + + xref = OF_xref_from_device(provider_dev); + OF_device_register_xref(xref, NULL); +} +#endif diff --git a/sys/dev/extres/phy/phy.h b/sys/dev/extres/phy/phy.h new file mode 100644 index 0000000..93b4fe4 --- /dev/null +++ b/sys/dev/extres/phy/phy.h @@ -0,0 +1,63 @@ +/*- + * Copyright 2016 Michal Meloun <mmel@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef DEV_EXTRES_PHY_H +#define DEV_EXTRES_PHY_H + +#include "opt_platform.h" +#include <sys/types.h> +#ifdef FDT +#include <dev/ofw/ofw_bus.h> +#endif + +typedef struct phy *phy_t; + +/* + * Provider interface + */ +#ifdef FDT +void phy_register_provider(device_t provider); +void phy_unregister_provider(device_t provider); +#endif + +/* + * Consumer interface + */ +int phy_get_by_id(device_t consumer_dev, device_t provider_dev, intptr_t id, + phy_t *phy); +void phy_release(phy_t phy); +int phy_get_by_ofw_name(device_t consumer, char *name, phy_t *phy); +int phy_get_by_ofw_idx(device_t consumer, int idx, phy_t *phy); +int phy_get_by_ofw_property(device_t consumer, char *name, phy_t *phy); + +int phy_init(device_t consumer, phy_t phy); +int phy_deinit(device_t consumer, phy_t phy); +int phy_enable(device_t consumer, phy_t phy); +int phy_disable(device_t consumer, phy_t phy); +int phy_status(device_t consumer, phy_t phy, int *value); + +#endif /* DEV_EXTRES_PHY_H */ diff --git a/sys/dev/extres/phy/phy_if.m b/sys/dev/extres/phy/phy_if.m new file mode 100644 index 0000000..e3b4e33 --- /dev/null +++ b/sys/dev/extres/phy/phy_if.m @@ -0,0 +1,84 @@ +#- +# Copyright 2016 Michal Meloun <mmel@FreeBSD.org> +# 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. +# +# $FreeBSD$ +# + +#ifdef FDT +#include <sys/types.h> +#include <dev/ofw/ofw_bus.h> +#endif + +INTERFACE phy; + +#ifdef FDT +HEADER { +int phy_default_map(device_t , phandle_t, int, pcell_t *, intptr_t *); +} + +# +# map fdt property cells to phy number +# Returns 0 on success or a standard errno value. +# +METHOD int map { + device_t provider_dev; + phandle_t xref; + int ncells; + pcell_t *cells; + intptr_t *id; +} DEFAULT phy_default_map; +#endif + +# +# Init/deinit phy +# Returns 0 on success or a standard errno value. +# +METHOD int init { + device_t provider_dev; + intptr_t id; + bool inti; +}; + +# +# Enable/disable phy +# Returns 0 on success or a standard errno value. +# +METHOD int enable { + device_t provider_dev; + intptr_t id; + bool enable; +}; + +# +# Get phy status +# Returns 0 on success or a standard errno value. +# +METHOD int status { + device_t provider_dev; + intptr_t id; + int *status; /* PHY_STATUS_* */ +}; + + diff --git a/sys/dev/extres/regulator/regdev_if.m b/sys/dev/extres/regulator/regdev_if.m new file mode 100644 index 0000000..eecbf8b --- /dev/null +++ b/sys/dev/extres/regulator/regdev_if.m @@ -0,0 +1,56 @@ +#- +# Copyright 2016 Michal Meloun <mmel@FreeBSD.org> +# 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. +# +# $FreeBSD$ +# + +#ifdef FDT +#include <sys/types.h> +#include <dev/ofw/ofw_bus.h> +#endif + +#include <machine/bus.h> + +INTERFACE regdev; + +#ifdef FDT + +HEADER { +int regdev_default_ofw_map(device_t , phandle_t, int, pcell_t *, intptr_t *); +} + +# +# map fdt property cells to regulator number +# Returns 0 on success or a standard errno value. +# +METHOD int map { + device_t provider_dev; + phandle_t xref; + int ncells; + pcell_t *cells; + intptr_t *id; +} DEFAULT regdev_default_ofw_map; + +#endif diff --git a/sys/dev/extres/regulator/regnode_if.m b/sys/dev/extres/regulator/regnode_if.m new file mode 100644 index 0000000..d92f26f --- /dev/null +++ b/sys/dev/extres/regulator/regnode_if.m @@ -0,0 +1,82 @@ +#- +# Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> +# 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. +# +# $FreeBSD$ +# + +INTERFACE regnode; +HEADER { + struct regnode; +} + +# +# Initialize regulator +# Returns 0 on success or a standard errno value. +# +METHOD int init { + struct regnode *regnode; +}; + +# +# Enable/disable regulator +# Returns 0 on success or a standard errno value. +# - enable - input. +# - delay - output, delay needed to stabilize voltage (in us) +# +METHOD int enable { + struct regnode *regnode; + bool enable; + int *udelay; +}; + +# +# Get regulator status +# Returns 0 on success or a standard errno value. +# +METHOD int status { + struct regnode *regnode; + int *status; /* REGULATOR_STATUS_* */ +}; + +# +# Set regulator voltage +# Returns 0 on success or a standard errno value. +# - min_uvolt, max_uvolt - input, requested voltage range (in uV) +# - delay - output, delay needed to stabilize voltage (in us) +METHOD int set_voltage { + struct regnode *regnode; + int min_uvolt; + int max_uvolt; + int *udelay; +}; + +# +# Get regulator voltage +# Returns 0 on success or a standard errno value. +# +METHOD int get_voltage { + struct regnode *regnode; + int *uvolt; +}; diff --git a/sys/dev/extres/regulator/regulator.c b/sys/dev/extres/regulator/regulator.c new file mode 100644 index 0000000..b941aa1 --- /dev/null +++ b/sys/dev/extres/regulator/regulator.c @@ -0,0 +1,984 @@ +/*- + * Copyright 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_platform.h" +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/queue.h> +#include <sys/kobj.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/limits.h> +#include <sys/lock.h> +#include <sys/sysctl.h> +#include <sys/systm.h> +#include <sys/sx.h> + +#ifdef FDT +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#endif +#include <dev/extres/regulator/regulator.h> + +#include "regdev_if.h" + +MALLOC_DEFINE(M_REGULATOR, "regulator", "Regulator framework"); + +/* Forward declarations. */ +struct regulator; +struct regnode; + +typedef TAILQ_HEAD(regnode_list, regnode) regnode_list_t; +typedef TAILQ_HEAD(regulator_list, regulator) regulator_list_t; + +/* Default regulator methods. */ +static int regnode_method_enable(struct regnode *regnode, bool enable, + int *udelay); +static int regnode_method_status(struct regnode *regnode, int *status); +static int regnode_method_set_voltage(struct regnode *regnode, int min_uvolt, + int max_uvolt, int *udelay); +static int regnode_method_get_voltage(struct regnode *regnode, int *uvolt); + +/* + * Regulator controller methods. + */ +static regnode_method_t regnode_methods[] = { + REGNODEMETHOD(regnode_enable, regnode_method_enable), + REGNODEMETHOD(regnode_status, regnode_method_status), + REGNODEMETHOD(regnode_set_voltage, regnode_method_set_voltage), + REGNODEMETHOD(regnode_get_voltage, regnode_method_get_voltage), + + REGNODEMETHOD_END +}; +DEFINE_CLASS_0(regnode, regnode_class, regnode_methods, 0); + +/* + * Regulator node - basic element for modelling SOC and bard power supply + * chains. Its contains producer data. + */ +struct regnode { + KOBJ_FIELDS; + + TAILQ_ENTRY(regnode) reglist_link; /* Global list entry */ + regulator_list_t consumers_list; /* Consumers list */ + + /* Cache for already resolved names */ + struct regnode *parent; /* Resolved parent */ + + /* Details of this device. */ + const char *name; /* Globally unique name */ + const char *parent_name; /* Parent name */ + + device_t pdev; /* Producer device_t */ + void *softc; /* Producer softc */ + intptr_t id; /* Per producer unique id */ +#ifdef FDT + phandle_t ofw_node; /* OFW node of regulator */ +#endif + int flags; /* REGULATOR_FLAGS_ */ + struct sx lock; /* Lock for this regulator */ + int ref_cnt; /* Reference counter */ + int enable_cnt; /* Enabled counter */ + + struct regnode_std_param std_param; /* Standard parameters */ +}; + +/* + * Per consumer data, information about how a consumer is using a regulator + * node. + * A pointer to this structure is used as a handle in the consumer interface. + */ +struct regulator { + device_t cdev; /* Consumer device */ + struct regnode *regnode; + TAILQ_ENTRY(regulator) link; /* Consumers list entry */ + + int enable_cnt; + int min_uvolt; /* Requested uvolt range */ + int max_uvolt; +}; + +/* + * Regulator names must be system wide unique. + */ +static regnode_list_t regnode_list = TAILQ_HEAD_INITIALIZER(regnode_list); + +static struct sx regnode_topo_lock; +SX_SYSINIT(regulator_topology, ®node_topo_lock, "Regulator topology lock"); + +#define REG_TOPO_SLOCK() sx_slock(®node_topo_lock) +#define REG_TOPO_XLOCK() sx_xlock(®node_topo_lock) +#define REG_TOPO_UNLOCK() sx_unlock(®node_topo_lock) +#define REG_TOPO_ASSERT() sx_assert(®node_topo_lock, SA_LOCKED) +#define REG_TOPO_XASSERT() sx_assert(®node_topo_lock, SA_XLOCKED) + +#define REGNODE_SLOCK(_sc) sx_slock(&((_sc)->lock)) +#define REGNODE_XLOCK(_sc) sx_xlock(&((_sc)->lock)) +#define REGNODE_UNLOCK(_sc) sx_unlock(&((_sc)->lock)) + +/* ---------------------------------------------------------------------------- + * + * Default regulator methods for base class. + * + */ +static int +regnode_method_enable(struct regnode *regnode, bool enable, int *udelay) +{ + + if (!enable) + return (ENXIO); + + *udelay = 0; + return (0); +} + +static int +regnode_method_status(struct regnode *regnode, int *status) +{ + *status = REGULATOR_STATUS_ENABLED; + return (0); +} + +static int +regnode_method_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt, + int *udelay) +{ + + if ((min_uvolt > regnode->std_param.max_uvolt) || + (max_uvolt < regnode->std_param.min_uvolt)) + return (ERANGE); + *udelay = 0; + return (0); +} + +static int +regnode_method_get_voltage(struct regnode *regnode, int *uvolt) +{ + + return (regnode->std_param.min_uvolt + + (regnode->std_param.max_uvolt - regnode->std_param.min_uvolt) / 2); +} + +/* ---------------------------------------------------------------------------- + * + * Internal functions. + * + */ + +static struct regnode * +regnode_find_by_name(const char *name) +{ + struct regnode *entry; + + REG_TOPO_ASSERT(); + + TAILQ_FOREACH(entry, ®node_list, reglist_link) { + if (strcmp(entry->name, name) == 0) + return (entry); + } + return (NULL); +} + +static struct regnode * +regnode_find_by_id(device_t dev, intptr_t id) +{ + struct regnode *entry; + + REG_TOPO_ASSERT(); + + TAILQ_FOREACH(entry, ®node_list, reglist_link) { + if ((entry->pdev == dev) && (entry->id == id)) + return (entry); + } + + return (NULL); +} + +/* + * Create and initialize regulator object, but do not register it. + */ +struct regnode * +regnode_create(device_t pdev, regnode_class_t regnode_class, + struct regnode_init_def *def) +{ + struct regnode *regnode; + + KASSERT(def->name != NULL, ("regulator name is NULL")); + KASSERT(def->name[0] != '\0', ("regulator name is empty")); + + REG_TOPO_SLOCK(); + if (regnode_find_by_name(def->name) != NULL) + panic("Duplicated regulator registration: %s\n", def->name); + REG_TOPO_UNLOCK(); + + /* Create object and initialize it. */ + regnode = malloc(sizeof(struct regnode), M_REGULATOR, + M_WAITOK | M_ZERO); + kobj_init((kobj_t)regnode, (kobj_class_t)regnode_class); + sx_init(®node->lock, "Regulator node lock"); + + /* Allocate softc if required. */ + if (regnode_class->size > 0) { + regnode->softc = malloc(regnode_class->size, M_REGULATOR, + M_WAITOK | M_ZERO); + } + + + /* Copy all strings unless they're flagged as static. */ + if (def->flags & REGULATOR_FLAGS_STATIC) { + regnode->name = def->name; + regnode->parent_name = def->parent_name; + } else { + regnode->name = strdup(def->name, M_REGULATOR); + if (def->parent_name != NULL) + regnode->parent_name = strdup(def->parent_name, + M_REGULATOR); + } + + /* Rest of init. */ + TAILQ_INIT(®node->consumers_list); + regnode->id = def->id; + regnode->pdev = pdev; + regnode->flags = def->flags; + regnode->parent = NULL; + regnode->std_param = def->std_param; +#ifdef FDT + regnode->ofw_node = def->ofw_node; +#endif + + return (regnode); +} + +/* Register regulator object. */ +struct regnode * +regnode_register(struct regnode *regnode) +{ + int rv; + +#ifdef FDT + if (regnode->ofw_node <= 0) + regnode->ofw_node = ofw_bus_get_node(regnode->pdev); + if (regnode->ofw_node <= 0) + return (NULL); +#endif + + rv = REGNODE_INIT(regnode); + if (rv != 0) { + printf("REGNODE_INIT failed: %d\n", rv); + return (NULL); + } + + REG_TOPO_XLOCK(); + TAILQ_INSERT_TAIL(®node_list, regnode, reglist_link); + REG_TOPO_UNLOCK(); +#ifdef FDT + OF_device_register_xref(OF_xref_from_node(regnode->ofw_node), + regnode->pdev); +#endif + return (regnode); +} + +static int +regnode_resolve_parent(struct regnode *regnode) +{ + + /* All ready resolved or no parent? */ + if ((regnode->parent != NULL) || + (regnode->parent_name == NULL)) + return (0); + + regnode->parent = regnode_find_by_name(regnode->parent_name); + if (regnode->parent == NULL) + return (ENODEV); + return (0); +} + +static void +regnode_delay(int usec) +{ + int ticks; + + if (usec == 0) + return; + ticks = (usec * hz + 999999) / 1000000; + + if (cold || ticks < 2) + DELAY(usec); + else + pause("REGULATOR", ticks); +} + +/* -------------------------------------------------------------------------- + * + * Regulator providers interface + * + */ + +const char * +regnode_get_name(struct regnode *regnode) +{ + + return (regnode->name); +} + +const char * +regnode_get_parent_name(struct regnode *regnode) +{ + + return (regnode->parent_name); +} + +int +regnode_get_flags(struct regnode *regnode) +{ + + return (regnode->flags); +} + +void * +regnode_get_softc(struct regnode *regnode) +{ + + return (regnode->softc); +} + +device_t +regnode_get_device(struct regnode *regnode) +{ + + return (regnode->pdev); +} + +struct regnode_std_param *regnode_get_stdparam(struct regnode *regnode) +{ + + return (®node->std_param); +} + +void regnode_topo_unlock(void) +{ + + REG_TOPO_UNLOCK(); +} + +void regnode_topo_xlock(void) +{ + + REG_TOPO_XLOCK(); +} + +void regnode_topo_slock(void) +{ + + REG_TOPO_SLOCK(); +} + + +/* -------------------------------------------------------------------------- + * + * Real consumers executive + * + */ +struct regnode * +regnode_get_parent(struct regnode *regnode) +{ + int rv; + + REG_TOPO_ASSERT(); + + rv = regnode_resolve_parent(regnode); + if (rv != 0) + return (NULL); + + return (regnode->parent); +} + +/* + * Enable regulator. + */ +int +regnode_enable(struct regnode *regnode) +{ + int udelay; + int rv; + + REG_TOPO_ASSERT(); + + /* Enable regulator for each node in chain, starting from source. */ + rv = regnode_resolve_parent(regnode); + if (rv != 0) + return (rv); + if (regnode->parent != NULL) { + rv = regnode_enable(regnode->parent); + if (rv != 0) + return (rv); + } + + /* Handle this node. */ + REGNODE_XLOCK(regnode); + if (regnode->enable_cnt == 0) { + rv = REGNODE_ENABLE(regnode, true, &udelay); + if (rv != 0) { + REGNODE_UNLOCK(regnode); + return (rv); + } + regnode_delay(udelay); + } + regnode->enable_cnt++; + REGNODE_UNLOCK(regnode); + return (0); +} + +/* + * Disable regulator. + */ +int +regnode_disable(struct regnode *regnode) +{ + int udelay; + int rv; + + REG_TOPO_ASSERT(); + rv = 0; + + REGNODE_XLOCK(regnode); + /* Disable regulator for each node in chain, starting from consumer. */ + if ((regnode->enable_cnt == 1) && + ((regnode->flags & REGULATOR_FLAGS_NOT_DISABLE) == 0)) { + rv = REGNODE_ENABLE(regnode, false, &udelay); + if (rv != 0) { + REGNODE_UNLOCK(regnode); + return (rv); + } + regnode_delay(udelay); + } + regnode->enable_cnt--; + REGNODE_UNLOCK(regnode); + + rv = regnode_resolve_parent(regnode); + if (rv != 0) + return (rv); + if (regnode->parent != NULL) + rv = regnode_disable(regnode->parent); + return (rv); +} + +/* + * Stop regulator. + */ +int +regnode_stop(struct regnode *regnode, int depth) +{ + int udelay; + int rv; + + REG_TOPO_ASSERT(); + rv = 0; + + REGNODE_XLOCK(regnode); + /* The first node must not be enabled. */ + if ((regnode->enable_cnt != 0) && (depth == 0)) { + REGNODE_UNLOCK(regnode); + return (EBUSY); + } + /* Disable regulator for each node in chain, starting from consumer */ + if ((regnode->enable_cnt == 0) && + ((regnode->flags & REGULATOR_FLAGS_NOT_DISABLE) == 0)) { + rv = REGNODE_ENABLE(regnode, false, &udelay); + if (rv != 0) { + REGNODE_UNLOCK(regnode); + return (rv); + } + regnode_delay(udelay); + } + REGNODE_UNLOCK(regnode); + + rv = regnode_resolve_parent(regnode); + if (rv != 0) + return (rv); + if (regnode->parent != NULL) + rv = regnode_stop(regnode->parent, depth + 1); + return (rv); +} + +/* + * Get regulator status. (REGULATOR_STATUS_*) + */ +int +regnode_status(struct regnode *regnode, int *status) +{ + int rv; + + REG_TOPO_ASSERT(); + + REGNODE_XLOCK(regnode); + rv = REGNODE_STATUS(regnode, status); + REGNODE_UNLOCK(regnode); + return (rv); +} + +/* + * Get actual regulator voltage. + */ +int +regnode_get_voltage(struct regnode *regnode, int *uvolt) +{ + int rv; + + REG_TOPO_ASSERT(); + + REGNODE_XLOCK(regnode); + rv = REGNODE_GET_VOLTAGE(regnode, uvolt); + REGNODE_UNLOCK(regnode); + + /* Pass call into parent, if regulator is in bypass mode. */ + if (rv == ENOENT) { + rv = regnode_resolve_parent(regnode); + if (rv != 0) + return (rv); + if (regnode->parent != NULL) + rv = regnode_get_voltage(regnode->parent, uvolt); + + } + return (rv); +} + +/* + * Set regulator voltage. + */ +int +regnode_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt) +{ + int udelay; + int rv; + + REG_TOPO_ASSERT(); + + REGNODE_XLOCK(regnode); + + rv = REGNODE_SET_VOLTAGE(regnode, min_uvolt, max_uvolt, &udelay); + if (rv == 0) + regnode_delay(udelay); + REGNODE_UNLOCK(regnode); + return (rv); +} + +/* + * Consumer variant of regnode_set_voltage(). + */ +static int +regnode_set_voltage_checked(struct regnode *regnode, struct regulator *reg, + int min_uvolt, int max_uvolt) +{ + int udelay; + int all_max_uvolt; + int all_min_uvolt; + struct regulator *tmp; + int rv; + + REG_TOPO_ASSERT(); + + REGNODE_XLOCK(regnode); + /* Return error if requested range is outside of regulator range. */ + if ((min_uvolt > regnode->std_param.max_uvolt) || + (max_uvolt < regnode->std_param.min_uvolt)) { + REGNODE_UNLOCK(regnode); + return (ERANGE); + } + + /* Get actual voltage range for all consumers. */ + all_min_uvolt = regnode->std_param.min_uvolt; + all_max_uvolt = regnode->std_param.max_uvolt; + TAILQ_FOREACH(tmp, ®node->consumers_list, link) { + /* Don't take requestor in account. */ + if (tmp == reg) + continue; + if (all_min_uvolt < tmp->min_uvolt) + all_min_uvolt = tmp->min_uvolt; + if (all_max_uvolt > tmp->max_uvolt) + all_max_uvolt = tmp->max_uvolt; + } + + /* Test if request fits to actual contract. */ + if ((min_uvolt > all_max_uvolt) || + (max_uvolt < all_min_uvolt)) { + REGNODE_UNLOCK(regnode); + return (ERANGE); + } + + /* Adjust new range.*/ + if (min_uvolt < all_min_uvolt) + min_uvolt = all_min_uvolt; + if (max_uvolt > all_max_uvolt) + max_uvolt = all_max_uvolt; + + rv = REGNODE_SET_VOLTAGE(regnode, min_uvolt, max_uvolt, &udelay); + regnode_delay(udelay); + REGNODE_UNLOCK(regnode); + return (rv); +} + +#ifdef FDT +phandle_t +regnode_get_ofw_node(struct regnode *regnode) +{ + + return (regnode->ofw_node); +} +#endif + +/* -------------------------------------------------------------------------- + * + * Regulator consumers interface. + * + */ +/* Helper function for regulator_get*() */ +static regulator_t +regulator_create(struct regnode *regnode, device_t cdev) +{ + struct regulator *reg; + + REG_TOPO_ASSERT(); + + reg = malloc(sizeof(struct regulator), M_REGULATOR, + M_WAITOK | M_ZERO); + reg->cdev = cdev; + reg->regnode = regnode; + reg->enable_cnt = 0; + + REGNODE_XLOCK(regnode); + regnode->ref_cnt++; + TAILQ_INSERT_TAIL(®node->consumers_list, reg, link); + reg ->min_uvolt = regnode->std_param.min_uvolt; + reg ->max_uvolt = regnode->std_param.max_uvolt; + REGNODE_UNLOCK(regnode); + + return (reg); +} + +int +regulator_enable(regulator_t reg) +{ + int rv; + struct regnode *regnode; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + REG_TOPO_SLOCK(); + rv = regnode_enable(regnode); + if (rv == 0) + reg->enable_cnt++; + REG_TOPO_UNLOCK(); + return (rv); +} + +int +regulator_disable(regulator_t reg) +{ + int rv; + struct regnode *regnode; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + KASSERT(reg->enable_cnt > 0, + ("Attempt to disable already disabled regulator: %s\n", + regnode->name)); + REG_TOPO_SLOCK(); + rv = regnode_disable(regnode); + if (rv == 0) + reg->enable_cnt--; + REG_TOPO_UNLOCK(); + return (rv); +} + +int +regulator_stop(regulator_t reg) +{ + int rv; + struct regnode *regnode; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + KASSERT(reg->enable_cnt == 0, + ("Attempt to stop already enabled regulator: %s\n", regnode->name)); + + REG_TOPO_SLOCK(); + rv = regnode_stop(regnode, 0); + REG_TOPO_UNLOCK(); + return (rv); +} + +int +regulator_status(regulator_t reg, int *status) +{ + int rv; + struct regnode *regnode; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + + REG_TOPO_SLOCK(); + rv = regnode_status(regnode, status); + REG_TOPO_UNLOCK(); + return (rv); +} + +int +regulator_get_voltage(regulator_t reg, int *uvolt) +{ + int rv; + struct regnode *regnode; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + + REG_TOPO_SLOCK(); + rv = regnode_get_voltage(regnode, uvolt); + REG_TOPO_UNLOCK(); + return (rv); +} + +int +regulator_set_voltage(regulator_t reg, int min_uvolt, int max_uvolt) +{ + struct regnode *regnode; + int rv; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + + REG_TOPO_SLOCK(); + + rv = regnode_set_voltage_checked(regnode, reg, min_uvolt, max_uvolt); + if (rv == 0) { + reg->min_uvolt = min_uvolt; + reg->max_uvolt = max_uvolt; + } + REG_TOPO_UNLOCK(); + return (rv); +} + +const char * +regulator_get_name(regulator_t reg) +{ + struct regnode *regnode; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + return (regnode->name); +} + +int +regulator_get_by_name(device_t cdev, const char *name, regulator_t *reg) +{ + struct regnode *regnode; + + REG_TOPO_SLOCK(); + regnode = regnode_find_by_name(name); + if (regnode == NULL) { + REG_TOPO_UNLOCK(); + return (ENODEV); + } + *reg = regulator_create(regnode, cdev); + REG_TOPO_UNLOCK(); + return (0); +} + +int +regulator_get_by_id(device_t cdev, device_t pdev, intptr_t id, regulator_t *reg) +{ + struct regnode *regnode; + + REG_TOPO_SLOCK(); + + regnode = regnode_find_by_id(pdev, id); + if (regnode == NULL) { + REG_TOPO_UNLOCK(); + return (ENODEV); + } + *reg = regulator_create(regnode, cdev); + REG_TOPO_UNLOCK(); + + return (0); +} + +int +regulator_release(regulator_t reg) +{ + struct regnode *regnode; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + REG_TOPO_SLOCK(); + while (reg->enable_cnt > 0) { + regnode_disable(regnode); + reg->enable_cnt--; + } + REGNODE_XLOCK(regnode); + TAILQ_REMOVE(®node->consumers_list, reg, link); + regnode->ref_cnt--; + REGNODE_UNLOCK(regnode); + REG_TOPO_UNLOCK(); + + free(reg, M_REGULATOR); + return (0); +} + +#ifdef FDT +/* Default DT mapper. */ +int +regdev_default_ofw_map(device_t dev, phandle_t xref, int ncells, + pcell_t *cells, intptr_t *id) +{ + if (ncells == 0) + *id = 1; + else if (ncells == 1) + *id = cells[0]; + else + return (ERANGE); + + return (0); +} + +int +regulator_parse_ofw_stdparam(device_t pdev, phandle_t node, + struct regnode_init_def *def) +{ + phandle_t supply_xref; + struct regnode_std_param *par; + int rv; + + par = &def->std_param; + rv = OF_getprop_alloc(node, "regulator-name", 1, + (void **)&def->name); + if (rv <= 0) { + device_printf(pdev, "%s: Missing regulator name\n", + __func__); + return (ENXIO); + } + + rv = OF_getencprop(node, "regulator-min-microvolt", &par->min_uvolt, + sizeof(par->min_uvolt)); + if (rv <= 0) + par->min_uvolt = 0; + + rv = OF_getencprop(node, "regulator-max-microvolt", &par->max_uvolt, + sizeof(par->max_uvolt)); + if (rv <= 0) + par->max_uvolt = 0; + + rv = OF_getencprop(node, "regulator-min-microamp", &par->min_uamp, + sizeof(par->min_uamp)); + if (rv <= 0) + par->min_uamp = 0; + + rv = OF_getencprop(node, "regulator-max-microamp", &par->max_uamp, + sizeof(par->max_uamp)); + if (rv <= 0) + par->max_uamp = 0; + + rv = OF_getencprop(node, "regulator-ramp-delay", &par->ramp_delay, + sizeof(par->ramp_delay)); + if (rv <= 0) + par->ramp_delay = 0; + + rv = OF_getencprop(node, "regulator-enable-ramp-delay", + &par->enable_delay, sizeof(par->enable_delay)); + if (rv <= 0) + par->enable_delay = 0; + + if (OF_hasprop(node, "regulator-boot-on")) + par->boot_on = 1; + + if (OF_hasprop(node, "regulator-always-on")) + par->always_on = 1; + + if (OF_hasprop(node, "enable-active-high")) + par->enable_active_high = 1; + + rv = OF_getencprop(node, "vin-supply", &supply_xref, + sizeof(supply_xref)); + if (rv >= 0) { + rv = OF_getprop_alloc(supply_xref, "regulator-name", 1, + (void **)&def->parent_name); + if (rv <= 0) + def->parent_name = NULL; + } + return (0); +} + +int +regulator_get_by_ofw_property(device_t cdev, char *name, regulator_t *reg) +{ + phandle_t cnode, *cells; + device_t regdev; + int ncells, rv; + intptr_t id; + + *reg = NULL; + + cnode = ofw_bus_get_node(cdev); + if (cnode <= 0) { + device_printf(cdev, "%s called on not ofw based device\n", + __func__); + return (ENXIO); + } + + cells = NULL; + ncells = OF_getencprop_alloc(cnode, name, sizeof(*cells), + (void **)&cells); + if (ncells <= 0) + return (ENXIO); + + /* Translate xref to device */ + regdev = OF_device_from_xref(cells[0]); + if (regdev == NULL) { + free(cells, M_OFWPROP); + return (ENODEV); + } + + /* Map regulator to number */ + rv = REGDEV_MAP(regdev, cells[0], ncells - 1, cells + 1, &id); + free(cells, M_OFWPROP); + if (rv != 0) + return (rv); + return (regulator_get_by_id(cdev, regdev, id, reg)); +} +#endif diff --git a/sys/dev/extres/regulator/regulator.h b/sys/dev/extres/regulator/regulator.h new file mode 100644 index 0000000..380bad6 --- /dev/null +++ b/sys/dev/extres/regulator/regulator.h @@ -0,0 +1,127 @@ +/*- + * Copyright 2016 Michal Meloun <mmel@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#ifndef _DEV_EXTRES_REGULATOR_H_ +#define _DEV_EXTRES_REGULATOR_H_ +#include "opt_platform.h" + +#include <sys/kobj.h> +#ifdef FDT +#include <dev/ofw/ofw_bus.h> +#endif +#include "regnode_if.h" + +#define REGULATOR_FLAGS_STATIC 0x00000001 /* Static strings */ +#define REGULATOR_FLAGS_NOT_DISABLE 0x00000002 /* Cannot be disabled */ + +#define REGULATOR_STATUS_ENABLED 0x00000001 +#define REGULATOR_STATUS_OVERCURRENT 0x00000002 + +typedef struct regulator *regulator_t; + +/* Standard regulator parameters. */ +struct regnode_std_param { + int min_uvolt; /* In uV */ + int max_uvolt; /* In uV */ + int min_uamp; /* In uA */ + int max_uamp; /* In uA */ + int ramp_delay; /* In uV/usec */ + int enable_delay; /* In usec */ + bool boot_on; /* Is enabled on boot */ + bool always_on; /* Must be enabled */ + int enable_active_high; +}; + +/* Initialization parameters. */ +struct regnode_init_def { + char *name; /* Regulator name */ + char *parent_name; /* Name of parent regulator */ + struct regnode_std_param std_param; /* Standard parameters */ + intptr_t id; /* Regulator ID */ + int flags; /* Flags */ +#ifdef FDT + phandle_t ofw_node; /* OFW node of regulator */ +#endif + +}; + +/* + * Shorthands for constructing method tables. + */ +#define REGNODEMETHOD KOBJMETHOD +#define REGNODEMETHOD_END KOBJMETHOD_END +#define regnode_method_t kobj_method_t +#define regnode_class_t kobj_class_t +DECLARE_CLASS(regnode_class); + +/* Providers interface. */ +struct regnode *regnode_create(device_t pdev, regnode_class_t regnode_class, + struct regnode_init_def *def); +struct regnode *regnode_register(struct regnode *regnode); +const char *regnode_get_name(struct regnode *regnode); +const char *regnode_get_parent_name(struct regnode *regnode); +struct regnode *regnode_get_parent(struct regnode *regnode); +int regnode_get_flags(struct regnode *regnode); +void *regnode_get_softc(struct regnode *regnode); +device_t regnode_get_device(struct regnode *regnode); +struct regnode_std_param *regnode_get_stdparam(struct regnode *regnode); +void regnode_topo_unlock(void); +void regnode_topo_xlock(void); +void regnode_topo_slock(void); + +int regnode_enable(struct regnode *regnode); +int regnode_disable(struct regnode *regnode); +int regnode_stop(struct regnode *regnode, int depth); +int regnode_status(struct regnode *regnode, int *status); +int regnode_get_voltage(struct regnode *regnode, int *uvolt); +int regnode_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt); +#ifdef FDT +phandle_t regnode_get_ofw_node(struct regnode *regnode); +#endif + +/* Consumers interface. */ +int regulator_get_by_name(device_t cdev, const char *name, + regulator_t *regulator); +int regulator_get_by_id(device_t cdev, device_t pdev, intptr_t id, + regulator_t *regulator); +int regulator_release(regulator_t regulator); +const char *regulator_get_name(regulator_t regulator); +int regulator_enable(regulator_t reg); +int regulator_disable(regulator_t reg); +int regulator_stop(regulator_t reg); +int regulator_status(regulator_t reg, int *status); +int regulator_get_voltage(regulator_t reg, int *uvolt); +int regulator_set_voltage(regulator_t reg, int min_uvolt, int max_uvolt); + +#ifdef FDT +int regulator_get_by_ofw_property(device_t dev, char *name, regulator_t *reg); +int regulator_parse_ofw_stdparam(device_t dev, phandle_t node, + struct regnode_init_def *def); +#endif + +#endif /* _DEV_EXTRES_REGULATOR_H_ */ diff --git a/sys/dev/extres/regulator/regulator_bus.c b/sys/dev/extres/regulator/regulator_bus.c new file mode 100644 index 0000000..5e7f4c9 --- /dev/null +++ b/sys/dev/extres/regulator/regulator_bus.c @@ -0,0 +1,89 @@ +/*- + * Copyright 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> + +#include <dev/fdt/simplebus.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_bus_subr.h> + +struct ofw_regulator_bus_softc { + struct simplebus_softc simplebus_sc; +}; + +static int +ofw_regulator_bus_probe(device_t dev) +{ + const char *name; + + name = ofw_bus_get_name(dev); + if (name == NULL || strcmp(name, "regulators") != 0) + return (ENXIO); + device_set_desc(dev, "OFW regulators bus"); + + return (0); +} + +static int +ofw_regulator_bus_attach(device_t dev) +{ + struct ofw_regulator_bus_softc *sc; + phandle_t node, child; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + simplebus_init(dev, node); + + for (child = OF_child(node); child > 0; child = OF_peer(child)) { + simplebus_add_device(dev, child, 0, NULL, -1, NULL); + } + + return (bus_generic_attach(dev)); +} + +static device_method_t ofw_regulator_bus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ofw_regulator_bus_probe), + DEVMETHOD(device_attach, ofw_regulator_bus_attach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(ofw_regulator_bus, ofw_regulator_bus_driver, + ofw_regulator_bus_methods, sizeof(struct ofw_regulator_bus_softc), + simplebus_driver); +static devclass_t ofw_regulator_bus_devclass; +EARLY_DRIVER_MODULE(ofw_regulator_bus, simplebus, ofw_regulator_bus_driver, + ofw_regulator_bus_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(ofw_regulator_bus, 1); diff --git a/sys/dev/extres/regulator/regulator_fixed.c b/sys/dev/extres/regulator/regulator_fixed.c new file mode 100644 index 0000000..5a44a72 --- /dev/null +++ b/sys/dev/extres/regulator/regulator_fixed.c @@ -0,0 +1,456 @@ +/*- + * Copyright 2016 Michal Meloun <mmel@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_platform.h" +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/gpio.h> +#include <sys/kernel.h> +#include <sys/kobj.h> +#include <sys/systm.h> +#include <sys/module.h> +#include <sys/mutex.h> + +#ifdef FDT +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#endif +#include <dev/gpio/gpiobusvar.h> +#include <dev/extres/regulator/regulator_fixed.h> + +#include "regdev_if.h" + +MALLOC_DEFINE(M_FIXEDREGULATOR, "fixedregulator", "Fixed regulator"); + +/* GPIO list for shared pins. */ +typedef TAILQ_HEAD(gpio_list, gpio_entry) gpio_list_t; +struct gpio_entry { + TAILQ_ENTRY(gpio_entry) link; + struct gpiobus_pin gpio_pin; + int use_cnt; + int enable_cnt; +}; +static gpio_list_t gpio_list = TAILQ_HEAD_INITIALIZER(gpio_list); +static struct mtx gpio_list_mtx; +MTX_SYSINIT(gpio_list_lock, &gpio_list_mtx, "Regulator GPIO lock", MTX_DEF); + +struct regnode_fixed_sc { + struct regnode_std_param *param; + bool gpio_open_drain; + struct gpio_entry *gpio_entry; +}; + +static int regnode_fixed_init(struct regnode *regnode); +static int regnode_fixed_enable(struct regnode *regnode, bool enable, + int *udelay); +static int regnode_fixed_status(struct regnode *regnode, int *status); + +static regnode_method_t regnode_fixed_methods[] = { + /* Regulator interface */ + REGNODEMETHOD(regnode_init, regnode_fixed_init), + REGNODEMETHOD(regnode_enable, regnode_fixed_enable), + REGNODEMETHOD(regnode_status, regnode_fixed_status), + REGNODEMETHOD_END +}; +DEFINE_CLASS_1(regnode_fixed, regnode_fixed_class, regnode_fixed_methods, + sizeof(struct regnode_fixed_sc), regnode_class); + +/* + * GPIO list functions. + * Two or more regulators can share single GPIO pins, so we must track all + * GPIOs in gpio_list. + * The GPIO pin is registerd and reseved for first consumer, all others share + * gpio_entry with it. + */ +static struct gpio_entry * +regnode_get_gpio_entry(struct gpiobus_pin *gpio_pin) +{ + struct gpio_entry *entry, *tmp; + device_t busdev; + int rv; + + busdev = GPIO_GET_BUS(gpio_pin->dev); + if (busdev == NULL) + return (NULL); + entry = malloc(sizeof(struct gpio_entry), M_FIXEDREGULATOR, + M_WAITOK | M_ZERO); + + mtx_lock(&gpio_list_mtx); + + TAILQ_FOREACH(tmp, &gpio_list, link) { + if (tmp->gpio_pin.dev == gpio_pin->dev && + tmp->gpio_pin.pin == gpio_pin->pin) { + tmp->use_cnt++; + mtx_unlock(&gpio_list_mtx); + free(entry, M_FIXEDREGULATOR); + return (tmp); + } + } + + /* Reserve pin. */ + /* XXX Can we call gpiobus_map_pin() with gpio_list_mtx mutex held? */ + rv = gpiobus_map_pin(busdev, gpio_pin->pin); + if (rv != 0) { + mtx_unlock(&gpio_list_mtx); + free(entry, M_FIXEDREGULATOR); + return (NULL); + } + /* Everything is OK, build new entry and insert it to list. */ + entry->gpio_pin = *gpio_pin; + entry->use_cnt = 1; + TAILQ_INSERT_TAIL(&gpio_list, entry, link); + + mtx_unlock(&gpio_list_mtx); + return (entry); +} + + +/* + * Regulator class implementation. + */ +static int +regnode_fixed_init(struct regnode *regnode) +{ + device_t dev; + struct regnode_fixed_sc *sc; + struct gpiobus_pin *pin; + uint32_t flags; + bool enable; + int rv; + + sc = regnode_get_softc(regnode); + dev = regnode_get_device(regnode); + sc->param = regnode_get_stdparam(regnode); + if (sc->gpio_entry == NULL) + return (0); + pin = &sc->gpio_entry->gpio_pin; + + flags = GPIO_PIN_OUTPUT; + if (sc->gpio_open_drain) + flags |= GPIO_PIN_OPENDRAIN; + enable = sc->param->boot_on || sc->param->always_on; + if (!sc->param->enable_active_high) + enable = !enable; + rv = GPIO_PIN_SET(pin->dev, pin->pin, enable); + if (rv != 0) { + device_printf(dev, "Cannot set GPIO pin: %d\n", pin->pin); + return (rv); + } + rv = GPIO_PIN_SETFLAGS(pin->dev, pin->pin, flags); + if (rv != 0) { + device_printf(dev, "Cannot configure GPIO pin: %d\n", pin->pin); + return (rv); + } + + return (0); +} + +/* + * Enable/disable regulator. + * Take shared GPIO pins in account + */ +static int +regnode_fixed_enable(struct regnode *regnode, bool enable, int *udelay) +{ + device_t dev; + struct regnode_fixed_sc *sc; + struct gpiobus_pin *pin; + int rv; + + sc = regnode_get_softc(regnode); + dev = regnode_get_device(regnode); + + *udelay = 0; + if (sc->param->always_on && !enable) + return (0); + if (sc->gpio_entry == NULL) + return (0); + pin = &sc->gpio_entry->gpio_pin; + if (enable) { + sc->gpio_entry->enable_cnt++; + if (sc->gpio_entry->enable_cnt > 1) + return (0); + } else { + KASSERT(sc->gpio_entry->enable_cnt > 0, + ("Invalid enable count")); + sc->gpio_entry->enable_cnt--; + if (sc->gpio_entry->enable_cnt >= 1) + return (0); + } + if (!sc->param->enable_active_high) + enable = !enable; + rv = GPIO_PIN_SET(pin->dev, pin->pin, enable); + if (rv != 0) { + device_printf(dev, "Cannot set GPIO pin: %d\n", pin->pin); + return (rv); + } + *udelay = sc->param->enable_delay; + return (0); +} + +static int +regnode_fixed_status(struct regnode *regnode, int *status) +{ + struct regnode_fixed_sc *sc; + struct gpiobus_pin *pin; + uint32_t val; + int rv; + + sc = regnode_get_softc(regnode); + + *status = 0; + if (sc->gpio_entry == NULL) { + *status = REGULATOR_STATUS_ENABLED; + return (0); + } + pin = &sc->gpio_entry->gpio_pin; + + rv = GPIO_PIN_GET(pin->dev, pin->pin, &val); + if (rv == 0) { + if (!sc->param->enable_active_high ^ (val != 0)) + *status = REGULATOR_STATUS_ENABLED; + } + return (rv); +} + +int +regnode_fixed_register(device_t dev, struct regnode_fixed_init_def *init_def) +{ + struct regnode *regnode; + struct regnode_fixed_sc *sc; + + regnode = regnode_create(dev, ®node_fixed_class, + &init_def->reg_init_def); + if (regnode == NULL) { + device_printf(dev, "Cannot create regulator.\n"); + return(ENXIO); + } + sc = regnode_get_softc(regnode); + sc->gpio_open_drain = init_def->gpio_open_drain; + if (init_def->gpio_pin != NULL) { + sc->gpio_entry = regnode_get_gpio_entry(init_def->gpio_pin); + if (sc->gpio_entry == NULL) + return(ENXIO); + } + regnode = regnode_register(regnode); + if (regnode == NULL) { + device_printf(dev, "Cannot register regulator.\n"); + return(ENXIO); + } + return (0); +} + +/* + * OFW Driver implementation. + */ +#ifdef FDT + +struct regfix_softc +{ + device_t dev; + bool attach_done; + struct regnode_fixed_init_def init_def; + phandle_t gpio_prodxref; + pcell_t *gpio_cells; + int gpio_ncells; + struct gpiobus_pin gpio_pin; +}; + +static struct ofw_compat_data compat_data[] = { + {"regulator-fixed", 1}, + {NULL, 0}, +}; + +static int +regfix_get_gpio(struct regfix_softc * sc) +{ + device_t busdev; + phandle_t node; + + int rv; + + if (sc->gpio_prodxref == 0) + return (0); + + node = ofw_bus_get_node(sc->dev); + + /* Test if controller exist. */ + sc->gpio_pin.dev = OF_device_from_xref(sc->gpio_prodxref); + if (sc->gpio_pin.dev == NULL) + return (ENODEV); + + /* Test if GPIO bus already exist. */ + busdev = GPIO_GET_BUS(sc->gpio_pin.dev); + if (busdev == NULL) + return (ENODEV); + + rv = gpio_map_gpios(sc->gpio_pin.dev, node, + OF_node_from_xref(sc->gpio_prodxref), sc->gpio_ncells, + sc->gpio_cells, &(sc->gpio_pin.pin), &(sc->gpio_pin.flags)); + if (rv != 0) { + device_printf(sc->dev, "Cannot map the gpio property.\n"); + return (ENXIO); + } + sc->init_def.gpio_pin = &sc->gpio_pin; + return (0); +} + +static int +regfix_parse_fdt(struct regfix_softc * sc) +{ + phandle_t node; + int rv; + struct regnode_init_def *init_def; + + node = ofw_bus_get_node(sc->dev); + init_def = &sc->init_def.reg_init_def; + + rv = regulator_parse_ofw_stdparam(sc->dev, node, init_def); + if (rv != 0) { + device_printf(sc->dev, "Cannot parse standard parameters.\n"); + return(rv); + } + + /* Fixed regulator uses 'startup-delay-us' property for enable_delay */ + rv = OF_getencprop(node, "startup-delay-us", + &init_def->std_param.enable_delay, + sizeof(init_def->std_param.enable_delay)); + if (rv <= 0) + init_def->std_param.enable_delay = 0; + /* GPIO pin */ + if (OF_hasprop(node, "gpio-open-drain")) + sc->init_def.gpio_open_drain = true; + + if (!OF_hasprop(node, "gpio")) + return (0); + rv = ofw_bus_parse_xref_list_alloc(node, "gpio", "#gpio-cells", 0, + &sc->gpio_prodxref, &sc->gpio_ncells, &sc->gpio_cells); + if (rv != 0) { + sc->gpio_prodxref = 0; + device_printf(sc->dev, "Malformed gpio property\n"); + return (ENXIO); + } + return (0); +} + +static void +regfix_new_pass(device_t dev) +{ + struct regfix_softc * sc; + int rv; + + sc = device_get_softc(dev); + bus_generic_new_pass(dev); + + if (sc->attach_done) + return; + + /* Try to get and configure GPIO. */ + rv = regfix_get_gpio(sc); + if (rv != 0) + return; + + /* Register regulator. */ + regnode_fixed_register(sc->dev, &sc->init_def); + sc->attach_done = true; +} + +static int +regfix_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + + device_set_desc(dev, "Fixed Regulator"); + return (BUS_PROBE_DEFAULT); +} + +static int +regfix_detach(device_t dev) +{ + + /* This device is always present. */ + return (EBUSY); +} + +static int +regfix_attach(device_t dev) +{ + struct regfix_softc * sc; + int rv; + + sc = device_get_softc(dev); + sc->dev = dev; + + /* Parse FDT data. */ + rv = regfix_parse_fdt(sc); + if (rv != 0) + return(ENXIO); + + /* Fill reset of init. */ + sc->init_def.reg_init_def.id = 1; + sc->init_def.reg_init_def.flags = REGULATOR_FLAGS_STATIC; + + /* Try to get and configure GPIO. */ + rv = regfix_get_gpio(sc); + if (rv != 0) + return (bus_generic_attach(dev)); + + /* Register regulator. */ + regnode_fixed_register(sc->dev, &sc->init_def); + sc->attach_done = true; + + return (bus_generic_attach(dev)); +} + +static device_method_t regfix_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, regfix_probe), + DEVMETHOD(device_attach, regfix_attach), + DEVMETHOD(device_detach, regfix_detach), + /* Bus interface */ + DEVMETHOD(bus_new_pass, regfix_new_pass), + /* Regdev interface */ + DEVMETHOD(regdev_map, regdev_default_ofw_map), + + DEVMETHOD_END +}; + +static devclass_t regfix_devclass; +DEFINE_CLASS_0(regfix, regfix_driver, regfix_methods, + sizeof(struct regfix_softc)); +EARLY_DRIVER_MODULE(regfix, simplebus, regfix_driver, + regfix_devclass, 0, 0, BUS_PASS_BUS); + +#endif /* FDT */ diff --git a/sys/compat/cloudabi64/cloudabi64_syscalldefs.h b/sys/dev/extres/regulator/regulator_fixed.h index d57f5f4..5cc0751 100644 --- a/sys/compat/cloudabi64/cloudabi64_syscalldefs.h +++ b/sys/dev/extres/regulator/regulator_fixed.h @@ -1,5 +1,6 @@ /*- - * Copyright (c) 2015 Nuxi, https://nuxi.nl/ + * Copyright 2016 Michal Meloun <mmel@FreeBSD.org> + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,21 +26,19 @@ * $FreeBSD$ */ -#ifndef _CLOUDABI64_SYSCALLDEFS_H_ -#define _CLOUDABI64_SYSCALLDEFS_H_ +#ifndef _DEV_EXTRES_REGULATOR_FIXED_H_ +#define _DEV_EXTRES_REGULATOR_FIXED_H_ -#include <sys/types.h> +#include <dev/gpio/gpiobusvar.h> +#include <dev/extres/regulator/regulator.h> -#include <compat/cloudabi/cloudabi_syscalldefs.h> +struct regnode_fixed_init_def { + struct regnode_init_def reg_init_def; + bool gpio_open_drain; + struct gpiobus_pin *gpio_pin; +}; -typedef uint64_t cloudabi64_size_t; -typedef uint64_t cloudabi64_uintptr_t; +int regnode_fixed_register(device_t dev, + struct regnode_fixed_init_def *init_def); -/* Import machine-dependent CloudABI definitions for 64-bit systems. */ -#define IDENT(ident) cloudabi64_##ident -#define PTR(type) cloudabi64_uintptr_t -#include <contrib/cloudabi/syscalldefs_md.h> -#undef IDENT -#undef PTR - -#endif +#endif /*_DEV_EXTRES_REGULATOR_FIXED_H_*/ diff --git a/sys/dev/fdt/fdt_common.c b/sys/dev/fdt/fdt_common.c index 6211aca..73f7ada 100644 --- a/sys/dev/fdt/fdt_common.c +++ b/sys/dev/fdt/fdt_common.c @@ -722,3 +722,16 @@ fdt_get_unit(device_t dev) return (strtol(name,NULL,0)); } + +int +fdt_get_chosen_bootargs(char *bootargs, size_t max_size) +{ + phandle_t chosen; + + chosen = OF_finddevice("/chosen"); + if (chosen == -1) + return (ENXIO); + if (OF_getprop(chosen, "bootargs", bootargs, max_size) == -1) + return (ENXIO); + return (0); +}
\ No newline at end of file diff --git a/sys/dev/fdt/fdt_common.h b/sys/dev/fdt/fdt_common.h index db27b43..17af344 100644 --- a/sys/dev/fdt/fdt_common.h +++ b/sys/dev/fdt/fdt_common.h @@ -100,5 +100,6 @@ int fdt_parent_addr_cells(phandle_t); int fdt_reg_to_rl(phandle_t, struct resource_list *); int fdt_pm(phandle_t); int fdt_get_unit(device_t); +int fdt_get_chosen_bootargs(char *bootargs, size_t max_size); #endif /* _FDT_COMMON_H_ */ diff --git a/sys/dev/fdt/simplebus.c b/sys/dev/fdt/simplebus.c index 36c278f..8d43a70 100644 --- a/sys/dev/fdt/simplebus.c +++ b/sys/dev/fdt/simplebus.c @@ -369,7 +369,7 @@ simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid, if (j == sc->nranges && sc->nranges != 0) { if (bootverbose) device_printf(bus, "Could not map resource " - "%#lx-%#lx\n", start, end); + "%#jx-%#jx\n", start, end); return (NULL); } @@ -387,8 +387,8 @@ simplebus_print_res(struct simplebus_devinfo *di) if (di == NULL) return (0); rv = 0; - rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#lx"); - rv += resource_list_print_type(&di->rl, "irq", SYS_RES_IRQ, "%ld"); + rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#jx"); + rv += resource_list_print_type(&di->rl, "irq", SYS_RES_IRQ, "%jd"); return (rv); } diff --git a/sys/dev/filemon/filemon.c b/sys/dev/filemon/filemon.c index cd40c5a..10f27ad 100644 --- a/sys/dev/filemon/filemon.c +++ b/sys/dev/filemon/filemon.c @@ -1,7 +1,7 @@ /*- * Copyright (c) 2011, David E. O'Brien. * Copyright (c) 2009-2011, Juniper Networks, Inc. - * Copyright (c) 2015, EMC Corp. + * Copyright (c) 2015-2016, EMC Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,7 +46,6 @@ __FBSDID("$FreeBSD$"); #include <sys/module.h> #include <sys/poll.h> #include <sys/proc.h> -#include <sys/queue.h> #include <sys/sx.h> #include <sys/syscall.h> #include <sys/sysent.h> @@ -80,23 +79,112 @@ static struct cdevsw filemon_cdevsw = { MALLOC_DECLARE(M_FILEMON); MALLOC_DEFINE(M_FILEMON, "filemon", "File access monitor"); +/* + * The filemon->lock protects several things currently: + * - fname1/fname2/msgbufr are pre-allocated and used per syscall + * for logging and copyins rather than stack variables. + * - Serializing the filemon's log output. + * - Preventing inheritance or removal of the filemon into proc.p_filemon. + */ struct filemon { - TAILQ_ENTRY(filemon) link; /* Link into the in-use list. */ - struct sx lock; /* Lock mutex for this filemon. */ + struct sx lock; /* Lock for this filemon. */ struct file *fp; /* Output file pointer. */ - struct proc *p; /* The process being monitored. */ char fname1[MAXPATHLEN]; /* Temporary filename buffer. */ char fname2[MAXPATHLEN]; /* Temporary filename buffer. */ char msgbufr[1024]; /* Output message buffer. */ + int error; /* Log write error, returned on close(2). */ + u_int refcnt; /* Pointer reference count. */ + u_int proccnt; /* Process count. */ }; -static TAILQ_HEAD(, filemon) filemons_inuse = TAILQ_HEAD_INITIALIZER(filemons_inuse); -static TAILQ_HEAD(, filemon) filemons_free = TAILQ_HEAD_INITIALIZER(filemons_free); -static struct sx access_lock; - static struct cdev *filemon_dev; +static void filemon_output(struct filemon *filemon, char *msg, size_t len); + +static __inline struct filemon * +filemon_acquire(struct filemon *filemon) +{ + + if (filemon != NULL) + refcount_acquire(&filemon->refcnt); + return (filemon); +} + +/* + * Release a reference and free on the last one. + */ +static void +filemon_release(struct filemon *filemon) +{ + + if (refcount_release(&filemon->refcnt) == 0) + return; + /* + * There are valid cases of releasing while locked, such as in + * filemon_untrack_processes, but none which are done where there + * is not at least 1 reference remaining. + */ + sx_assert(&filemon->lock, SA_UNLOCKED); + + sx_destroy(&filemon->lock); + free(filemon, M_FILEMON); +} + +/* + * Acquire the proc's p_filemon reference and lock the filemon. + * The proc's p_filemon may not match this filemon on return. + */ +static struct filemon * +filemon_proc_get(struct proc *p) +{ + struct filemon *filemon; + + PROC_LOCK(p); + filemon = filemon_acquire(p->p_filemon); + PROC_UNLOCK(p); + + if (filemon == NULL) + return (NULL); + /* + * The p->p_filemon may have changed by now. That case is handled + * by the exit and fork hooks and filemon_attach_proc specially. + */ + sx_xlock(&filemon->lock); + return (filemon); +} + +/* Remove and release the filemon on the given process. */ +static void +filemon_proc_drop(struct proc *p) +{ + struct filemon *filemon; + + KASSERT(p->p_filemon != NULL, ("%s: proc %p NULL p_filemon", + __func__, p)); + sx_assert(&p->p_filemon->lock, SA_XLOCKED); + PROC_LOCK(p); + filemon = p->p_filemon; + p->p_filemon = NULL; + --filemon->proccnt; + PROC_UNLOCK(p); + /* + * This should not be the last reference yet. filemon_release() + * cannot be called with filemon locked, which the caller expects + * will stay locked. + */ + KASSERT(filemon->refcnt > 1, ("%s: proc %p dropping filemon %p " + "with last reference", __func__, p, filemon)); + filemon_release(filemon); +} + +/* Unlock and release the filemon. */ +static __inline void +filemon_drop(struct filemon *filemon) +{ + + sx_xunlock(&filemon->lock); + filemon_release(filemon); +} -#include "filemon_lock.c" #include "filemon_wrapper.c" static void @@ -115,35 +203,151 @@ filemon_comment(struct filemon *filemon) filemon_output(filemon, filemon->msgbufr, len); } +/* + * Invalidate the passed filemon in all processes. + */ static void -filemon_dtr(void *data) +filemon_untrack_processes(struct filemon *filemon) { - struct filemon *filemon = data; + struct proc *p; - if (filemon != NULL) { - struct file *fp; + sx_assert(&filemon->lock, SA_XLOCKED); - /* Follow same locking order as filemon_pid_check. */ - filemon_lock_write(); - sx_xlock(&filemon->lock); + /* Avoid allproc loop if there is no need. */ + if (filemon->proccnt == 0) + return; + + /* + * Processes in this list won't go away while here since + * filemon_event_process_exit() will lock on filemon->lock + * which we hold. + */ + sx_slock(&allproc_lock); + FOREACH_PROC_IN_SYSTEM(p) { + /* + * No PROC_LOCK is needed to compare here since it is + * guaranteed to not change since we have its filemon + * locked. Everything that changes this p_filemon will + * be locked on it. + */ + if (p->p_filemon == filemon) + filemon_proc_drop(p); + } + sx_sunlock(&allproc_lock); + + /* + * It's possible some references were acquired but will be + * dropped shortly as they are restricted from being + * inherited. There is at least the reference in cdevpriv remaining. + */ + KASSERT(filemon->refcnt > 0, ("%s: filemon %p should have " + "references still.", __func__, filemon)); + KASSERT(filemon->proccnt == 0, ("%s: filemon %p should not have " + "attached procs still.", __func__, filemon)); +} - /* Remove from the in-use list. */ - TAILQ_REMOVE(&filemons_inuse, filemon, link); +/* + * Close out the log. + */ +static void +filemon_close_log(struct filemon *filemon) +{ + struct file *fp; + struct timeval now; + size_t len; - fp = filemon->fp; - filemon->fp = NULL; - filemon->p = NULL; + sx_assert(&filemon->lock, SA_XLOCKED); + if (filemon->fp == NULL) + return; - /* Add to the free list. */ - TAILQ_INSERT_TAIL(&filemons_free, filemon, link); + getmicrotime(&now); - /* Give up write access. */ - sx_xunlock(&filemon->lock); - filemon_unlock_write(); + len = snprintf(filemon->msgbufr, + sizeof(filemon->msgbufr), + "# Stop %ju.%06ju\n# Bye bye\n", + (uintmax_t)now.tv_sec, (uintmax_t)now.tv_usec); + + filemon_output(filemon, filemon->msgbufr, len); + fp = filemon->fp; + filemon->fp = NULL; + + sx_xunlock(&filemon->lock); + fdrop(fp, curthread); + sx_xlock(&filemon->lock); +} + +/* + * The devfs file is being closed. Untrace all processes. It is possible + * filemon_close/close(2) was not called. + */ +static void +filemon_dtr(void *data) +{ + struct filemon *filemon = data; - if (fp != NULL) - fdrop(fp, curthread); + if (filemon == NULL) + return; + + sx_xlock(&filemon->lock); + /* + * Detach the filemon. It cannot be inherited after this. + */ + filemon_untrack_processes(filemon); + filemon_close_log(filemon); + filemon_drop(filemon); +} + +/* Attach the filemon to the process. */ +static int +filemon_attach_proc(struct filemon *filemon, struct proc *p) +{ + struct filemon *filemon2; + + sx_assert(&filemon->lock, SA_XLOCKED); + PROC_LOCK_ASSERT(p, MA_OWNED); + KASSERT((p->p_flag & P_WEXIT) == 0, + ("%s: filemon %p attaching to exiting process %p", + __func__, filemon, p)); + + if (p->p_filemon == filemon) + return (0); + /* + * Don't allow truncating other process traces. It is + * not really intended to trace procs other than curproc + * anyhow. + */ + if (p->p_filemon != NULL && p != curproc) + return (EBUSY); + /* + * Historic behavior of filemon has been to let a child initiate + * tracing on itself and cease existing tracing. Bmake + * .META + .MAKE relies on this. It is only relevant for attaching to + * curproc. + */ + while (p->p_filemon != NULL) { + PROC_UNLOCK(p); + sx_xunlock(&filemon->lock); + while ((filemon2 = filemon_proc_get(p)) != NULL) { + /* It may have changed. */ + if (p->p_filemon == filemon2) + filemon_proc_drop(p); + filemon_drop(filemon2); + } + sx_xlock(&filemon->lock); + PROC_LOCK(p); + /* + * It may have been attached to, though unlikely. + * Try again if needed. + */ } + + KASSERT(p->p_filemon == NULL, + ("%s: proc %p didn't detach filemon %p", __func__, p, + p->p_filemon)); + p->p_filemon = filemon_acquire(filemon); + ++filemon->proccnt; + + return (0); } static int @@ -178,10 +382,16 @@ filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused, /* Set the monitored process ID. */ case FILEMON_SET_PID: + /* Invalidate any existing processes already set. */ + filemon_untrack_processes(filemon); + error = pget(*((pid_t *)data), PGET_CANDEBUG | PGET_NOTWEXIT, &p); if (error == 0) { - filemon->p = p; + KASSERT(p->p_filemon != filemon, + ("%s: proc %p didn't untrack filemon %p", + __func__, p, filemon)); + error = filemon_attach_proc(filemon, p); PROC_UNLOCK(p); } break; @@ -199,49 +409,48 @@ static int filemon_open(struct cdev *dev, int oflags __unused, int devtype __unused, struct thread *td __unused) { + int error; struct filemon *filemon; - /* Get exclusive write access. */ - filemon_lock_write(); - - if ((filemon = TAILQ_FIRST(&filemons_free)) != NULL) - TAILQ_REMOVE(&filemons_free, filemon, link); - - /* Give up write access. */ - filemon_unlock_write(); - - if (filemon == NULL) { - filemon = malloc(sizeof(struct filemon), M_FILEMON, - M_WAITOK | M_ZERO); - sx_init(&filemon->lock, "filemon"); - } - - devfs_set_cdevpriv(filemon, filemon_dtr); - - /* Get exclusive write access. */ - filemon_lock_write(); + filemon = malloc(sizeof(*filemon), M_FILEMON, + M_WAITOK | M_ZERO); + sx_init(&filemon->lock, "filemon"); + refcount_init(&filemon->refcnt, 1); - /* Add to the in-use list. */ - TAILQ_INSERT_TAIL(&filemons_inuse, filemon, link); + error = devfs_set_cdevpriv(filemon, filemon_dtr); + if (error != 0) + filemon_release(filemon); - /* Give up write access. */ - filemon_unlock_write(); - - return (0); + return (error); } +/* Called on close of last devfs file handle, before filemon_dtr(). */ static int filemon_close(struct cdev *dev __unused, int flag __unused, int fmt __unused, struct thread *td __unused) { + struct filemon *filemon; + int error; - return (0); + if ((error = devfs_get_cdevpriv((void **) &filemon)) != 0) + return (error); + + sx_xlock(&filemon->lock); + filemon_close_log(filemon); + error = filemon->error; + sx_xunlock(&filemon->lock); + /* + * Processes are still being traced but won't log anything + * now. After this call returns filemon_dtr() is called which + * will detach processes. + */ + + return (error); } static void filemon_load(void *dummy __unused) { - sx_init(&access_lock, "filemons_inuse"); /* Install the syscall wrappers. */ filemon_wrapper_install(); @@ -253,38 +462,11 @@ filemon_load(void *dummy __unused) static int filemon_unload(void) { - struct filemon *filemon; - int error = 0; - - /* Get exclusive write access. */ - filemon_lock_write(); - - if (TAILQ_FIRST(&filemons_inuse) != NULL) - error = EBUSY; - else { - destroy_dev(filemon_dev); - - /* Deinstall the syscall wrappers. */ - filemon_wrapper_deinstall(); - } - /* Give up write access. */ - filemon_unlock_write(); + destroy_dev(filemon_dev); + filemon_wrapper_deinstall(); - if (error == 0) { - /* free() filemon structs free list. */ - filemon_lock_write(); - while ((filemon = TAILQ_FIRST(&filemons_free)) != NULL) { - TAILQ_REMOVE(&filemons_free, filemon, link); - sx_destroy(&filemon->lock); - free(filemon, M_FILEMON); - } - filemon_unlock_write(); - - sx_destroy(&access_lock); - } - - return (error); + return (0); } static int diff --git a/sys/dev/filemon/filemon_wrapper.c b/sys/dev/filemon/filemon_wrapper.c index 6911dc5..87c9392 100644 --- a/sys/dev/filemon/filemon_wrapper.c +++ b/sys/dev/filemon/filemon_wrapper.c @@ -1,7 +1,7 @@ /*- * Copyright (c) 2011, David E. O'Brien. * Copyright (c) 2009-2011, Juniper Networks, Inc. - * Copyright (c) 2015, EMC Corp. + * Copyright (c) 2015-2016, EMC Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,8 +29,9 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -#include <sys/imgact.h> #include <sys/eventhandler.h> +#include <sys/filedesc.h> +#include <sys/imgact.h> #include <sys/sx.h> #include <sys/vnode.h> @@ -45,6 +46,7 @@ filemon_output(struct filemon *filemon, char *msg, size_t len) { struct uio auio; struct iovec aiov; + int error; if (filemon->fp == NULL) return; @@ -62,56 +64,33 @@ filemon_output(struct filemon *filemon, char *msg, size_t len) if (filemon->fp->f_type == DTYPE_VNODE) bwillwrite(); - fo_write(filemon->fp, &auio, curthread->td_ucred, 0, curthread); -} - -static struct filemon * -filemon_pid_check(struct proc *p) -{ - struct filemon *filemon; - - filemon_lock_read(); - if (TAILQ_EMPTY(&filemons_inuse)) { - filemon_unlock_read(); - return (NULL); - } - sx_slock(&proctree_lock); - while (p->p_pid != 0) { - TAILQ_FOREACH(filemon, &filemons_inuse, link) { - if (p == filemon->p) { - sx_sunlock(&proctree_lock); - sx_xlock(&filemon->lock); - filemon_unlock_read(); - return (filemon); - } - } - p = proc_realparent(p); - } - sx_sunlock(&proctree_lock); - filemon_unlock_read(); - return (NULL); + error = fo_write(filemon->fp, &auio, curthread->td_ucred, 0, curthread); + if (error != 0) + filemon->error = error; } static int filemon_wrapper_chdir(struct thread *td, struct chdir_args *uap) { - int ret; - size_t done; + int error, ret; size_t len; struct filemon *filemon; if ((ret = sys_chdir(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path, filemon->fname1, - sizeof(filemon->fname1), &done); + if ((filemon = filemon_proc_get(curproc)) != NULL) { + if ((error = copyinstr(uap->path, filemon->fname1, + sizeof(filemon->fname1), NULL)) != 0) { + filemon->error = error; + goto copyfail; + } len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), "C %d %s\n", curproc->p_pid, filemon->fname1); filemon_output(filemon, filemon->msgbufr, len); - - sx_xunlock(&filemon->lock); +copyfail: + filemon_drop(filemon); } } @@ -126,12 +105,11 @@ filemon_event_process_exec(void *arg __unused, struct proc *p, char *fullpath, *freepath; size_t len; - if ((filemon = filemon_pid_check(p)) != NULL) { + if ((filemon = filemon_proc_get(p)) != NULL) { fullpath = "<unknown>"; freepath = NULL; - vn_fullpath(FIRST_THREAD_IN_PROC(p), imgp->vp, &fullpath, - &freepath); + vn_fullpath(curthread, imgp->vp, &fullpath, &freepath); len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), "E %d %s\n", @@ -139,321 +117,244 @@ filemon_event_process_exec(void *arg __unused, struct proc *p, filemon_output(filemon, filemon->msgbufr, len); - sx_xunlock(&filemon->lock); + filemon_drop(filemon); free(freepath, M_TEMP); } } -static int -filemon_wrapper_open(struct thread *td, struct open_args *uap) +static void +_filemon_wrapper_openat(struct thread *td, char *upath, int flags, int fd) { - int ret; - size_t done; + int error; size_t len; + struct file *fp; struct filemon *filemon; + char *atpath, *freepath; + cap_rights_t rights; - if ((ret = sys_open(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path, filemon->fname1, - sizeof(filemon->fname1), &done); - - if (uap->flags & O_RDWR) { - /* - * We'll get the W record below, but need - * to also output an R to distingish from - * O_WRONLY. - */ - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "R %d %s\n", - curproc->p_pid, filemon->fname1); - filemon_output(filemon, filemon->msgbufr, len); - } + if ((filemon = filemon_proc_get(curproc)) != NULL) { + atpath = ""; + freepath = NULL; + fp = NULL; + if ((error = copyinstr(upath, filemon->fname1, + sizeof(filemon->fname1), NULL)) != 0) { + filemon->error = error; + goto copyfail; + } + if (filemon->fname1[0] != '/' && fd != AT_FDCWD) { + /* + * rats - we cannot do too much about this. + * the trace should show a dir we read + * recently.. output an A record as a clue + * until we can do better. + * XXX: This may be able to come out with + * the namecache lookup now. + */ len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "%c %d %s\n", - (uap->flags & O_ACCMODE) ? 'W':'R', + sizeof(filemon->msgbufr), "A %d %s\n", curproc->p_pid, filemon->fname1); filemon_output(filemon, filemon->msgbufr, len); - - sx_xunlock(&filemon->lock); + /* + * Try to resolve the path from the vnode using the + * namecache. It may be inaccurate, but better + * than nothing. + */ + if (getvnode(td, fd, + cap_rights_init(&rights, CAP_LOOKUP), &fp) == 0) { + vn_fullpath(td, fp->f_vnode, &atpath, + &freepath); + } + } + if (flags & O_RDWR) { + /* + * We'll get the W record below, but need + * to also output an R to distinguish from + * O_WRONLY. + */ + len = snprintf(filemon->msgbufr, + sizeof(filemon->msgbufr), "R %d %s%s%s\n", + curproc->p_pid, atpath, + atpath[0] != '\0' ? "/" : "", filemon->fname1); + filemon_output(filemon, filemon->msgbufr, len); } - } - return (ret); + len = snprintf(filemon->msgbufr, + sizeof(filemon->msgbufr), "%c %d %s%s%s\n", + (flags & O_ACCMODE) ? 'W':'R', + curproc->p_pid, atpath, + atpath[0] != '\0' ? "/" : "", filemon->fname1); + filemon_output(filemon, filemon->msgbufr, len); +copyfail: + filemon_drop(filemon); + if (fp != NULL) + fdrop(fp, td); + free(freepath, M_TEMP); + } } static int -filemon_wrapper_openat(struct thread *td, struct openat_args *uap) +filemon_wrapper_open(struct thread *td, struct open_args *uap) { int ret; - size_t done; - size_t len; - struct filemon *filemon; - if ((ret = sys_openat(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path, filemon->fname1, - sizeof(filemon->fname1), &done); - - filemon->fname2[0] = '\0'; - if (filemon->fname1[0] != '/' && uap->fd != AT_FDCWD) { - /* - * rats - we cannot do too much about this. - * the trace should show a dir we read - * recently.. output an A record as a clue - * until we can do better. - */ - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "A %d %s\n", - curproc->p_pid, filemon->fname1); - filemon_output(filemon, filemon->msgbufr, len); - } - if (uap->flag & O_RDWR) { - /* - * We'll get the W record below, but need - * to also output an R to distingish from - * O_WRONLY. - */ - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "R %d %s%s\n", - curproc->p_pid, filemon->fname2, filemon->fname1); - filemon_output(filemon, filemon->msgbufr, len); - } - - - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "%c %d %s%s\n", - (uap->flag & O_ACCMODE) ? 'W':'R', - curproc->p_pid, filemon->fname2, filemon->fname1); - filemon_output(filemon, filemon->msgbufr, len); - - sx_xunlock(&filemon->lock); - } - } + if ((ret = sys_open(td, uap)) == 0) + _filemon_wrapper_openat(td, uap->path, uap->flags, AT_FDCWD); return (ret); } static int -filemon_wrapper_rename(struct thread *td, struct rename_args *uap) +filemon_wrapper_openat(struct thread *td, struct openat_args *uap) { int ret; - size_t done; - size_t len; - struct filemon *filemon; - if ((ret = sys_rename(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->from, filemon->fname1, - sizeof(filemon->fname1), &done); - copyinstr(uap->to, filemon->fname2, - sizeof(filemon->fname2), &done); - - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "M %d '%s' '%s'\n", - curproc->p_pid, filemon->fname1, filemon->fname2); - - filemon_output(filemon, filemon->msgbufr, len); - - sx_xunlock(&filemon->lock); - } - } + if ((ret = sys_openat(td, uap)) == 0) + _filemon_wrapper_openat(td, uap->path, uap->flag, uap->fd); return (ret); } static int -filemon_wrapper_link(struct thread *td, struct link_args *uap) +filemon_wrapper_rename(struct thread *td, struct rename_args *uap) { - int ret; - size_t done; + int error, ret; size_t len; struct filemon *filemon; - if ((ret = sys_link(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path, filemon->fname1, - sizeof(filemon->fname1), &done); - copyinstr(uap->link, filemon->fname2, - sizeof(filemon->fname2), &done); + if ((ret = sys_rename(td, uap)) == 0) { + if ((filemon = filemon_proc_get(curproc)) != NULL) { + if (((error = copyinstr(uap->from, filemon->fname1, + sizeof(filemon->fname1), NULL)) != 0) || + ((error = copyinstr(uap->to, filemon->fname2, + sizeof(filemon->fname2), NULL)) != 0)) { + filemon->error = error; + goto copyfail; + } len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "L %d '%s' '%s'\n", + sizeof(filemon->msgbufr), "M %d '%s' '%s'\n", curproc->p_pid, filemon->fname1, filemon->fname2); filemon_output(filemon, filemon->msgbufr, len); - - sx_xunlock(&filemon->lock); +copyfail: + filemon_drop(filemon); } } return (ret); } -static int -filemon_wrapper_symlink(struct thread *td, struct symlink_args *uap) +static void +_filemon_wrapper_link(struct thread *td, char *upath1, char *upath2) { - int ret; - size_t done; - size_t len; struct filemon *filemon; + size_t len; + int error; + + if ((filemon = filemon_proc_get(curproc)) != NULL) { + if (((error = copyinstr(upath1, filemon->fname1, + sizeof(filemon->fname1), NULL)) != 0) || + ((error = copyinstr(upath2, filemon->fname2, + sizeof(filemon->fname2), NULL)) != 0)) { + filemon->error = error; + goto copyfail; + } - if ((ret = sys_symlink(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path, filemon->fname1, - sizeof(filemon->fname1), &done); - copyinstr(uap->link, filemon->fname2, - sizeof(filemon->fname2), &done); - - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "L %d '%s' '%s'\n", - curproc->p_pid, filemon->fname1, filemon->fname2); - - filemon_output(filemon, filemon->msgbufr, len); + len = snprintf(filemon->msgbufr, + sizeof(filemon->msgbufr), "L %d '%s' '%s'\n", + curproc->p_pid, filemon->fname1, filemon->fname2); - sx_xunlock(&filemon->lock); - } + filemon_output(filemon, filemon->msgbufr, len); +copyfail: + filemon_drop(filemon); } - - return (ret); } static int -filemon_wrapper_linkat(struct thread *td, struct linkat_args *uap) +filemon_wrapper_link(struct thread *td, struct link_args *uap) { int ret; - size_t done; - size_t len; - struct filemon *filemon; - - if ((ret = sys_linkat(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path1, filemon->fname1, - sizeof(filemon->fname1), &done); - copyinstr(uap->path2, filemon->fname2, - sizeof(filemon->fname2), &done); - - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "L %d '%s' '%s'\n", - curproc->p_pid, filemon->fname1, filemon->fname2); - filemon_output(filemon, filemon->msgbufr, len); - - sx_xunlock(&filemon->lock); - } - } + if ((ret = sys_link(td, uap)) == 0) + _filemon_wrapper_link(td, uap->path, uap->link); return (ret); } static int -filemon_wrapper_stat(struct thread *td, struct stat_args *uap) +filemon_wrapper_symlink(struct thread *td, struct symlink_args *uap) { int ret; - size_t done; - size_t len; - struct filemon *filemon; - if ((ret = sys_stat(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path, filemon->fname1, - sizeof(filemon->fname1), &done); - - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "S %d %s\n", - curproc->p_pid, filemon->fname1); - - filemon_output(filemon, filemon->msgbufr, len); - - sx_xunlock(&filemon->lock); - } - } + if ((ret = sys_symlink(td, uap)) == 0) + _filemon_wrapper_link(td, uap->path, uap->link); return (ret); } -#if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32) static int -filemon_wrapper_freebsd32_stat(struct thread *td, - struct freebsd32_stat_args *uap) +filemon_wrapper_linkat(struct thread *td, struct linkat_args *uap) { int ret; - size_t done; - size_t len; - struct filemon *filemon; - - if ((ret = freebsd32_stat(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path, filemon->fname1, - sizeof(filemon->fname1), &done); - - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "S %d %s\n", - curproc->p_pid, filemon->fname1); - - filemon_output(filemon, filemon->msgbufr, len); - sx_xunlock(&filemon->lock); - } - } + if ((ret = sys_linkat(td, uap)) == 0) + _filemon_wrapper_link(td, uap->path1, uap->path2); return (ret); } -#endif static void filemon_event_process_exit(void *arg __unused, struct proc *p) { size_t len; struct filemon *filemon; - struct timeval now; - /* Get timestamp before locking. */ - getmicrotime(&now); - - if ((filemon = filemon_pid_check(p)) != NULL) { + if ((filemon = filemon_proc_get(p)) != NULL) { len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), "X %d %d %d\n", p->p_pid, p->p_xexit, p->p_xsig); filemon_output(filemon, filemon->msgbufr, len); - /* Check if the monitored process is about to exit. */ - if (filemon->p == p) { - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), - "# Stop %ju.%06ju\n# Bye bye\n", - (uintmax_t)now.tv_sec, (uintmax_t)now.tv_usec); - - filemon_output(filemon, filemon->msgbufr, len); - filemon->p = NULL; - } - - sx_xunlock(&filemon->lock); + /* + * filemon_untrack_processes() may have dropped this p_filemon + * already while in filemon_proc_get() before acquiring the + * filemon lock. + */ + KASSERT(p->p_filemon == NULL || p->p_filemon == filemon, + ("%s: p %p was attached while exiting, expected " + "filemon %p or NULL", __func__, p, filemon)); + if (p->p_filemon == filemon) + filemon_proc_drop(p); + + filemon_drop(filemon); } } static int filemon_wrapper_unlink(struct thread *td, struct unlink_args *uap) { - int ret; - size_t done; + int error, ret; size_t len; struct filemon *filemon; if ((ret = sys_unlink(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path, filemon->fname1, - sizeof(filemon->fname1), &done); + if ((filemon = filemon_proc_get(curproc)) != NULL) { + if ((error = copyinstr(uap->path, filemon->fname1, + sizeof(filemon->fname1), NULL)) != 0) { + filemon->error = error; + goto copyfail; + } len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), "D %d %s\n", curproc->p_pid, filemon->fname1); filemon_output(filemon, filemon->msgbufr, len); - - sx_xunlock(&filemon->lock); +copyfail: + filemon_drop(filemon); } } @@ -467,14 +368,34 @@ filemon_event_process_fork(void *arg __unused, struct proc *p1, size_t len; struct filemon *filemon; - if ((filemon = filemon_pid_check(p1)) != NULL) { + if ((filemon = filemon_proc_get(p1)) != NULL) { len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), "F %d %d\n", p1->p_pid, p2->p_pid); filemon_output(filemon, filemon->msgbufr, len); - sx_xunlock(&filemon->lock); + /* + * filemon_untrack_processes() or + * filemon_ioctl(FILEMON_SET_PID) may have changed the parent's + * p_filemon while in filemon_proc_get() before acquiring the + * filemon lock. Only inherit if the parent is still traced by + * this filemon. + */ + if (p1->p_filemon == filemon) { + PROC_LOCK(p2); + /* + * It may have been attached to already by a new + * filemon. + */ + if (p2->p_filemon == NULL) { + p2->p_filemon = filemon_acquire(filemon); + ++filemon->proccnt; + } + PROC_UNLOCK(p2); + } + + filemon_drop(filemon); } } @@ -491,7 +412,6 @@ filemon_wrapper_install(void) sv_table[SYS_open].sy_call = (sy_call_t *) filemon_wrapper_open; sv_table[SYS_openat].sy_call = (sy_call_t *) filemon_wrapper_openat; sv_table[SYS_rename].sy_call = (sy_call_t *) filemon_wrapper_rename; - sv_table[SYS_stat].sy_call = (sy_call_t *) filemon_wrapper_stat; sv_table[SYS_unlink].sy_call = (sy_call_t *) filemon_wrapper_unlink; sv_table[SYS_link].sy_call = (sy_call_t *) filemon_wrapper_link; sv_table[SYS_symlink].sy_call = (sy_call_t *) filemon_wrapper_symlink; @@ -504,7 +424,6 @@ filemon_wrapper_install(void) sv_table[FREEBSD32_SYS_open].sy_call = (sy_call_t *) filemon_wrapper_open; sv_table[FREEBSD32_SYS_openat].sy_call = (sy_call_t *) filemon_wrapper_openat; sv_table[FREEBSD32_SYS_rename].sy_call = (sy_call_t *) filemon_wrapper_rename; - sv_table[FREEBSD32_SYS_freebsd32_stat].sy_call = (sy_call_t *) filemon_wrapper_freebsd32_stat; sv_table[FREEBSD32_SYS_unlink].sy_call = (sy_call_t *) filemon_wrapper_unlink; sv_table[FREEBSD32_SYS_link].sy_call = (sy_call_t *) filemon_wrapper_link; sv_table[FREEBSD32_SYS_symlink].sy_call = (sy_call_t *) filemon_wrapper_symlink; @@ -532,7 +451,6 @@ filemon_wrapper_deinstall(void) sv_table[SYS_open].sy_call = (sy_call_t *)sys_open; sv_table[SYS_openat].sy_call = (sy_call_t *)sys_openat; sv_table[SYS_rename].sy_call = (sy_call_t *)sys_rename; - sv_table[SYS_stat].sy_call = (sy_call_t *)sys_stat; sv_table[SYS_unlink].sy_call = (sy_call_t *)sys_unlink; sv_table[SYS_link].sy_call = (sy_call_t *)sys_link; sv_table[SYS_symlink].sy_call = (sy_call_t *)sys_symlink; @@ -545,7 +463,6 @@ filemon_wrapper_deinstall(void) sv_table[FREEBSD32_SYS_open].sy_call = (sy_call_t *)sys_open; sv_table[FREEBSD32_SYS_openat].sy_call = (sy_call_t *)sys_openat; sv_table[FREEBSD32_SYS_rename].sy_call = (sy_call_t *)sys_rename; - sv_table[FREEBSD32_SYS_freebsd32_stat].sy_call = (sy_call_t *)freebsd32_stat; sv_table[FREEBSD32_SYS_unlink].sy_call = (sy_call_t *)sys_unlink; sv_table[FREEBSD32_SYS_link].sy_call = (sy_call_t *)sys_link; sv_table[FREEBSD32_SYS_symlink].sy_call = (sy_call_t *)sys_symlink; diff --git a/sys/dev/flash/mx25l.c b/sys/dev/flash/mx25l.c index c4dad4b..6b71340 100644 --- a/sys/dev/flash/mx25l.c +++ b/sys/dev/flash/mx25l.c @@ -26,6 +26,8 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/bio.h> @@ -40,6 +42,12 @@ __FBSDID("$FreeBSD$"); #include <sys/mutex.h> #include <geom/geom_disk.h> +#ifdef FDT +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/openfirm.h> +#endif + #include <dev/spibus/spi.h> #include "spibus_if.h" @@ -48,6 +56,8 @@ __FBSDID("$FreeBSD$"); #define FL_NONE 0x00 #define FL_ERASE_4K 0x01 #define FL_ERASE_32K 0x02 +#define FL_ENABLE_4B_ADDR 0x04 +#define FL_DISABLE_4B_ADDR 0x08 /* * Define the sectorsize to be a smaller size rather than the flash @@ -105,6 +115,7 @@ struct mx25l_flash_ident flash_devices[] = { { "mx25ll32", 0xc2, 0x2016, 64 * 1024, 64, FL_NONE }, { "mx25ll64", 0xc2, 0x2017, 64 * 1024, 128, FL_NONE }, { "mx25ll128", 0xc2, 0x2018, 64 * 1024, 256, FL_ERASE_4K | FL_ERASE_32K }, + { "mx25ll256", 0xc2, 0x2019, 64 * 1024, 512, FL_ERASE_4K | FL_ERASE_32K | FL_ENABLE_4B_ADDR }, { "s25fl032", 0x01, 0x0215, 64 * 1024, 64, FL_NONE }, { "s25fl064", 0x01, 0x0216, 64 * 1024, 128, FL_NONE }, { "s25fl128", 0x01, 0x2018, 64 * 1024, 256, FL_NONE }, @@ -211,10 +222,13 @@ mx25l_set_writable(device_t dev, int writable) static void mx25l_erase_cmd(device_t dev, off_t sector, uint8_t ecmd) { - uint8_t txBuf[4], rxBuf[4]; + struct mx25l_softc *sc; + uint8_t txBuf[5], rxBuf[5]; struct spi_command cmd; int err; + sc = device_get_softc(dev); + mx25l_wait_for_device_ready(dev); mx25l_set_writable(dev, 1); @@ -225,11 +239,20 @@ mx25l_erase_cmd(device_t dev, off_t sector, uint8_t ecmd) txBuf[0] = ecmd; cmd.tx_cmd = txBuf; cmd.rx_cmd = rxBuf; - cmd.rx_cmd_sz = 4; - cmd.tx_cmd_sz = 4; - txBuf[1] = ((sector >> 16) & 0xff); - txBuf[2] = ((sector >> 8) & 0xff); - txBuf[3] = (sector & 0xff); + if (sc->sc_flags & FL_ENABLE_4B_ADDR) { + cmd.rx_cmd_sz = 5; + cmd.tx_cmd_sz = 5; + txBuf[1] = ((sector >> 24) & 0xff); + txBuf[2] = ((sector >> 16) & 0xff); + txBuf[3] = ((sector >> 8) & 0xff); + txBuf[4] = (sector & 0xff); + } else { + cmd.rx_cmd_sz = 4; + cmd.tx_cmd_sz = 4; + txBuf[1] = ((sector >> 16) & 0xff); + txBuf[2] = ((sector >> 8) & 0xff); + txBuf[3] = (sector & 0xff); + } err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd); } @@ -247,8 +270,13 @@ mx25l_write(device_t dev, off_t offset, caddr_t data, off_t count) pdev = device_get_parent(dev); sc = device_get_softc(dev); - cmd.tx_cmd_sz = 4; - cmd.rx_cmd_sz = 4; + if (sc->sc_flags & FL_ENABLE_4B_ADDR) { + cmd.tx_cmd_sz = 5; + cmd.rx_cmd_sz = 5; + } else { + cmd.tx_cmd_sz = 4; + cmd.rx_cmd_sz = 4; + } bytes_writen = 0; write_offset = offset; @@ -282,9 +310,16 @@ mx25l_write(device_t dev, off_t offset, caddr_t data, off_t count) mx25l_erase_cmd(dev, offset + bytes_writen, CMD_SECTOR_ERASE); txBuf[0] = CMD_PAGE_PROGRAM; - txBuf[1] = ((write_offset >> 16) & 0xff); - txBuf[2] = ((write_offset >> 8) & 0xff); - txBuf[3] = (write_offset & 0xff); + if (sc->sc_flags & FL_ENABLE_4B_ADDR) { + txBuf[1] = ((write_offset >> 24) & 0xff); + txBuf[2] = ((write_offset >> 16) & 0xff); + txBuf[3] = ((write_offset >> 8) & 0xff); + txBuf[4] = (write_offset & 0xff); + } else { + txBuf[1] = ((write_offset >> 16) & 0xff); + txBuf[2] = ((write_offset >> 8) & 0xff); + txBuf[3] = (write_offset & 0xff); + } bytes_to_write = MIN(FLASH_PAGE_SIZE, count - bytes_writen); @@ -336,14 +371,26 @@ mx25l_read(device_t dev, off_t offset, caddr_t data, off_t count) return (EIO); txBuf[0] = CMD_FAST_READ; - cmd.tx_cmd_sz = 5; - cmd.rx_cmd_sz = 5; - - txBuf[1] = ((offset >> 16) & 0xff); - txBuf[2] = ((offset >> 8) & 0xff); - txBuf[3] = (offset & 0xff); - /* Dummy byte */ - txBuf[4] = 0; + if (sc->sc_flags & FL_ENABLE_4B_ADDR) { + cmd.tx_cmd_sz = 6; + cmd.rx_cmd_sz = 6; + + txBuf[1] = ((offset >> 24) & 0xff); + txBuf[2] = ((offset >> 16) & 0xff); + txBuf[3] = ((offset >> 8) & 0xff); + txBuf[4] = (offset & 0xff); + /* Dummy byte */ + txBuf[5] = 0; + } else { + cmd.tx_cmd_sz = 5; + cmd.rx_cmd_sz = 5; + + txBuf[1] = ((offset >> 16) & 0xff); + txBuf[2] = ((offset >> 8) & 0xff); + txBuf[3] = (offset & 0xff); + /* Dummy byte */ + txBuf[4] = 0; + } cmd.tx_cmd = txBuf; cmd.rx_cmd = rxBuf; @@ -358,9 +405,45 @@ mx25l_read(device_t dev, off_t offset, caddr_t data, off_t count) } static int +mx25l_set_4b_mode(device_t dev, uint8_t command) +{ + uint8_t txBuf[1], rxBuf[1]; + struct spi_command cmd; + device_t pdev; + int err; + + memset(&cmd, 0, sizeof(cmd)); + memset(txBuf, 0, sizeof(txBuf)); + memset(rxBuf, 0, sizeof(rxBuf)); + + pdev = device_get_parent(dev); + + cmd.tx_cmd_sz = cmd.rx_cmd_sz = 1; + + cmd.tx_cmd = txBuf; + cmd.rx_cmd = rxBuf; + + txBuf[0] = command; + + err = SPIBUS_TRANSFER(pdev, dev, &cmd); + + mx25l_wait_for_device_ready(dev); + + return (err); +} + +static int mx25l_probe(device_t dev) { + +#ifdef FDT + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + if (!ofw_bus_is_compatible(dev, "st,m25p")) + return (ENXIO); +#endif device_set_desc(dev, "M25Pxx Flash Family"); + return (0); } @@ -397,6 +480,12 @@ mx25l_attach(device_t dev) sc->sc_sectorsize = ident->sectorsize; sc->sc_flags = ident->flags; + if (sc->sc_flags & FL_ENABLE_4B_ADDR) + mx25l_set_4b_mode(dev, CMD_ENTER_4B_MODE); + + if (sc->sc_flags & FL_DISABLE_4B_ADDR) + mx25l_set_4b_mode(dev, CMD_EXIT_4B_MODE); + /* NB: use stripesize to hold the erase/region size for RedBoot */ sc->sc_disk->d_stripesize = ident->sectorsize; diff --git a/sys/dev/flash/mx25lreg.h b/sys/dev/flash/mx25lreg.h index 7253dba..5e303e1 100644 --- a/sys/dev/flash/mx25lreg.h +++ b/sys/dev/flash/mx25lreg.h @@ -45,6 +45,8 @@ #define CMD_BULK_ERASE 0xC7 #define CMD_BLOCK_4K_ERASE 0x20 #define CMD_BLOCK_32K_ERASE 0x52 +#define CMD_ENTER_4B_MODE 0xB7 +#define CMD_EXIT_4B_MODE 0xE9 /* * Status register flags diff --git a/sys/dev/gpio/gpiobus.c b/sys/dev/gpio/gpiobus.c index 44ab581..553d574 100644 --- a/sys/dev/gpio/gpiobus.c +++ b/sys/dev/gpio/gpiobus.c @@ -390,7 +390,7 @@ gpiobus_probe_nomatch(device_t dev, device_t child) device_printf(dev, "<unknown device> at pins %s", pins); else device_printf(dev, "<unknown device> at pin %s", pins); - resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%ld"); + resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%jd"); printf("\n"); } @@ -412,7 +412,7 @@ gpiobus_print_child(device_t dev, device_t child) gpiobus_print_pins(devi, pins, sizeof(pins)); retval += printf("%s", pins); } - resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%ld"); + resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%jd"); retval += bus_print_child_footer(dev, child); return (retval); diff --git a/sys/dev/gpio/ofw_gpiobus.c b/sys/dev/gpio/ofw_gpiobus.c index be5a747..f6d2115 100644 --- a/sys/dev/gpio/ofw_gpiobus.c +++ b/sys/dev/gpio/ofw_gpiobus.c @@ -575,6 +575,7 @@ static devclass_t ofwgpiobus_devclass; DEFINE_CLASS_1(gpiobus, ofw_gpiobus_driver, ofw_gpiobus_methods, sizeof(struct gpiobus_softc), gpiobus_driver); -DRIVER_MODULE(ofw_gpiobus, gpio, ofw_gpiobus_driver, ofwgpiobus_devclass, 0, 0); +EARLY_DRIVER_MODULE(ofw_gpiobus, gpio, ofw_gpiobus_driver, ofwgpiobus_devclass, + 0, 0, BUS_PASS_BUS); MODULE_VERSION(ofw_gpiobus, 1); MODULE_DEPEND(ofw_gpiobus, gpiobus, 1, 1, 1); diff --git a/sys/dev/hyperv/include/hyperv.h b/sys/dev/hyperv/include/hyperv.h index 0a057c8..26a2bfc 100644 --- a/sys/dev/hyperv/include/hyperv.h +++ b/sys/dev/hyperv/include/hyperv.h @@ -124,6 +124,8 @@ typedef struct hv_guid { unsigned char data[16]; } __packed hv_guid; +int snprintf_hv_guid(char *, size_t, const hv_guid *); + #define HV_NIC_GUID \ .data = {0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, \ 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E} diff --git a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c index 0cec9a7..acc49b4 100644 --- a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c +++ b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c @@ -181,6 +181,7 @@ struct hn_txdesc { #define HN_CSUM_ASSIST_WIN8 (CSUM_IP | CSUM_TCP) #define HN_CSUM_ASSIST (CSUM_IP | CSUM_UDP | CSUM_TCP) +#define HN_LRO_LENLIM_MULTIRX_DEF (12 * ETHERMTU) #define HN_LRO_LENLIM_DEF (25 * ETHERMTU) /* YYY 2*MTU is a bit rough, but should be good enough. */ #define HN_LRO_LENLIM_MIN(ifp) (2 * (ifp)->if_mtu) @@ -333,6 +334,17 @@ static void hn_xmit_txeof(struct hn_tx_ring *); static void hn_xmit_taskfunc(void *, int); static void hn_xmit_txeof_taskfunc(void *, int); +#if __FreeBSD_version >= 1100099 +static void +hn_set_lro_lenlim(struct hn_softc *sc, int lenlim) +{ + int i; + + for (i = 0; i < sc->hn_rx_ring_inuse; ++i) + sc->hn_rx_ring[i].hn_lro.lro_length_lim = lenlim; +} +#endif + static int hn_ifmedia_upd(struct ifnet *ifp __unused) { @@ -530,6 +542,16 @@ netvsc_attach(device_t dev) device_printf(dev, "%d TX ring, %d RX ring\n", sc->hn_tx_ring_inuse, sc->hn_rx_ring_inuse); +#if __FreeBSD_version >= 1100099 + if (sc->hn_rx_ring_inuse > 1) { + /* + * Reduce TCP segment aggregation limit for multiple + * RX rings to increase ACK timeliness. + */ + hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MULTIRX_DEF); + } +#endif + if (device_info.link_state == 0) { sc->hn_carrier = 1; } @@ -756,13 +778,8 @@ netvsc_channel_rollup(struct hv_vmbus_channel *chan) struct hn_tx_ring *txr = chan->hv_chan_txr; #if defined(INET) || defined(INET6) struct hn_rx_ring *rxr = chan->hv_chan_rxr; - struct lro_ctrl *lro = &rxr->hn_lro; - struct lro_entry *queued; - while ((queued = SLIST_FIRST(&lro->lro_active)) != NULL) { - SLIST_REMOVE_HEAD(&lro->lro_active, next); - tcp_lro_flush(lro, queued); - } + tcp_lro_flush_all(&rxr->hn_lro); #endif /* @@ -1238,8 +1255,10 @@ netvsc_recv(struct hv_vmbus_channel *chan, netvsc_packet *packet, return (0); } else if (packet->tot_data_buf_len <= MHLEN) { m_new = m_gethdr(M_NOWAIT, MT_DATA); - if (m_new == NULL) + if (m_new == NULL) { + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); return (0); + } memcpy(mtod(m_new, void *), packet->data, packet->tot_data_buf_len); m_new->m_pkthdr.len = m_new->m_len = packet->tot_data_buf_len; @@ -1259,7 +1278,7 @@ netvsc_recv(struct hv_vmbus_channel *chan, netvsc_packet *packet, m_new = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, size); if (m_new == NULL) { - if_printf(ifp, "alloc mbuf failed.\n"); + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); return (0); } @@ -1449,14 +1468,8 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) */ NV_LOCK(sc); if (sc->hn_rx_ring[0].hn_lro.lro_length_lim < - HN_LRO_LENLIM_MIN(ifp)) { - int i; - - for (i = 0; i < sc->hn_rx_ring_inuse; ++i) { - sc->hn_rx_ring[i].hn_lro.lro_length_lim = - HN_LRO_LENLIM_MIN(ifp); - } - } + HN_LRO_LENLIM_MIN(ifp)) + hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MIN(ifp)); NV_UNLOCK(sc); #endif @@ -1789,7 +1802,7 @@ hn_lro_lenlim_sysctl(SYSCTL_HANDLER_ARGS) { struct hn_softc *sc = arg1; unsigned int lenlim; - int error, i; + int error; lenlim = sc->hn_rx_ring[0].hn_lro.lro_length_lim; error = sysctl_handle_int(oidp, &lenlim, 0, req); @@ -1801,8 +1814,7 @@ hn_lro_lenlim_sysctl(SYSCTL_HANDLER_ARGS) return EINVAL; NV_LOCK(sc); - for (i = 0; i < sc->hn_rx_ring_inuse; ++i) - sc->hn_rx_ring[i].hn_lro.lro_length_lim = lenlim; + hn_set_lro_lenlim(sc, lenlim); NV_UNLOCK(sc); return 0; } diff --git a/sys/dev/hyperv/utilities/hv_heartbeat.c b/sys/dev/hyperv/utilities/hv_heartbeat.c index c1b6da5..09bd28b 100644 --- a/sys/dev/hyperv/utilities/hv_heartbeat.c +++ b/sys/dev/hyperv/utilities/hv_heartbeat.c @@ -94,6 +94,10 @@ static int hv_heartbeat_probe(device_t dev) { const char *p = vmbus_get_type(dev); + + if (resource_disabled("hvheartbeat", 0)) + return ENXIO; + if (!memcmp(p, &service_guid, sizeof(hv_guid))) { device_set_desc(dev, "Hyper-V Heartbeat Service"); return BUS_PROBE_DEFAULT; diff --git a/sys/dev/hyperv/utilities/hv_kvp.c b/sys/dev/hyperv/utilities/hv_kvp.c index 8517918..d71310b 100644 --- a/sys/dev/hyperv/utilities/hv_kvp.c +++ b/sys/dev/hyperv/utilities/hv_kvp.c @@ -304,28 +304,11 @@ hv_kvp_convert_utf16_ipinfo_to_utf8(struct hv_kvp_ip_msg *host_ip_msg, { int err_ip, err_subnet, err_gway, err_dns, err_adap; int UNUSED_FLAG = 1; - int guid_index; struct hv_device *hv_dev; /* GUID Data Structure */ hn_softc_t *sc; /* hn softc structure */ char if_name[4]; - unsigned char guid_instance[40]; - char *guid_data = NULL; char buf[39]; - struct guid_extract { - char a1[2]; - char a2[2]; - char a3[2]; - char a4[2]; - char b1[2]; - char b2[2]; - char c1[2]; - char c2[2]; - char d[4]; - char e[12]; - }; - - struct guid_extract *id; device_t *devs; int devcnt; @@ -352,17 +335,7 @@ hv_kvp_convert_utf16_ipinfo_to_utf8(struct hv_kvp_ip_msg *host_ip_msg, /* Trying to find GUID of Network Device */ hv_dev = sc->hn_dev_obj; - for (guid_index = 0; guid_index < 16; guid_index++) { - sprintf(&guid_instance[guid_index * 2], "%02x", - hv_dev->device_id.data[guid_index]); - } - - guid_data = (char *)guid_instance; - id = (struct guid_extract *)guid_data; - snprintf(buf, sizeof(buf), "{%.2s%.2s%.2s%.2s-%.2s%.2s-%.2s%.2s-%.4s-%s}", - id->a4, id->a3, id->a2, id->a1, - id->b2, id->b1, id->c2, id->c1, id->d, id->e); - guid_data = NULL; + snprintf_hv_guid(buf, sizeof(buf), &hv_dev->device_id); sprintf(if_name, "%s%d", "hn", device_get_unit(devs[devcnt])); if (strncmp(buf, (char *)umsg->body.kvp_ip_val.adapter_id, 39) == 0) { @@ -890,6 +863,10 @@ static int hv_kvp_probe(device_t dev) { const char *p = vmbus_get_type(dev); + + if (resource_disabled("hvkvp", 0)) + return ENXIO; + if (!memcmp(p, &service_guid, sizeof(hv_guid))) { device_set_desc(dev, "Hyper-V KVP Service"); return BUS_PROBE_DEFAULT; diff --git a/sys/dev/hyperv/utilities/hv_shutdown.c b/sys/dev/hyperv/utilities/hv_shutdown.c index 20bc65e..0beed5a 100644 --- a/sys/dev/hyperv/utilities/hv_shutdown.c +++ b/sys/dev/hyperv/utilities/hv_shutdown.c @@ -116,6 +116,10 @@ static int hv_shutdown_probe(device_t dev) { const char *p = vmbus_get_type(dev); + + if (resource_disabled("hvshutdown", 0)) + return ENXIO; + if (!memcmp(p, &service_guid, sizeof(hv_guid))) { device_set_desc(dev, "Hyper-V Shutdown Service"); return BUS_PROBE_DEFAULT; diff --git a/sys/dev/hyperv/utilities/hv_timesync.c b/sys/dev/hyperv/utilities/hv_timesync.c index d1ea904..06580d7 100644 --- a/sys/dev/hyperv/utilities/hv_timesync.c +++ b/sys/dev/hyperv/utilities/hv_timesync.c @@ -171,6 +171,10 @@ static int hv_timesync_probe(device_t dev) { const char *p = vmbus_get_type(dev); + + if (resource_disabled("hvtimesync", 0)) + return ENXIO; + if (!memcmp(p, &service_guid, sizeof(hv_guid))) { device_set_desc(dev, "Hyper-V Time Synch Service"); return BUS_PROBE_DEFAULT; diff --git a/sys/dev/hyperv/vmbus/hv_connection.c b/sys/dev/hyperv/vmbus/hv_connection.c index aa1e59e..e170298 100644 --- a/sys/dev/hyperv/vmbus/hv_connection.c +++ b/sys/dev/hyperv/vmbus/hv_connection.c @@ -364,31 +364,35 @@ hv_vmbus_on_events(int cpu) /** * Send a msg on the vmbus's message connection */ -int hv_vmbus_post_message(void *buffer, size_t bufferLen) { - int ret = 0; +int hv_vmbus_post_message(void *buffer, size_t bufferLen) +{ hv_vmbus_connection_id connId; - unsigned retries = 0; + sbintime_t time = SBT_1MS; + int retries; + int ret; - /* NetScaler delays from previous code were consolidated here */ - static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000}; + connId.as_uint32_t = 0; + connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID; - /* for(each entry in delayAmount) try to post message, - * delay a little bit before retrying + /* + * We retry to cope with transient failures caused by host side's + * insufficient resources. 20 times should suffice in practice. */ - for (retries = 0; - retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) { - connId.as_uint32_t = 0; - connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID; - ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen); - if (ret != HV_STATUS_INSUFFICIENT_BUFFERS) - break; - /* TODO: KYS We should use a blocking wait call */ - DELAY(delayAmount[retries]); + for (retries = 0; retries < 20; retries++) { + ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, + bufferLen); + if (ret == HV_STATUS_SUCCESS) + return (0); + + pause_sbt("pstmsg", time, 0, C_HARDCLOCK); + if (time < SBT_1S * 2) + time *= 2; } - KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n")); + KASSERT(ret == HV_STATUS_SUCCESS, + ("Error VMBUS: Message Post Failed, ret=%d\n", ret)); - return (ret); + return (EAGAIN); } /** diff --git a/sys/dev/hyperv/vmbus/hv_et.c b/sys/dev/hyperv/vmbus/hv_et.c index d961486..9fb6e23 100644 --- a/sys/dev/hyperv/vmbus/hv_et.c +++ b/sys/dev/hyperv/vmbus/hv_et.c @@ -60,7 +60,7 @@ hv_et_start(struct eventtimer *et, sbintime_t firsttime, sbintime_t periodtime) timer_cfg.as_uint64 = 0; timer_cfg.auto_enable = 1; - timer_cfg.sintx = HV_VMBUS_MESSAGE_SINT; + timer_cfg.sintx = HV_VMBUS_TIMER_SINT; periodticks[curcpu] = sbintime2tick(periodtime); if (firsttime == 0) diff --git a/sys/dev/hyperv/vmbus/hv_hv.c b/sys/dev/hyperv/vmbus/hv_hv.c index 6afc2b8..a87b5ce 100644 --- a/sys/dev/hyperv/vmbus/hv_hv.c +++ b/sys/dev/hyperv/vmbus/hv_hv.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include <sys/malloc.h> #include <sys/pcpu.h> #include <sys/timetc.h> +#include <sys/kernel.h> #include <machine/bus.h> #include <machine/md_var.h> #include <vm/vm.h> @@ -207,8 +208,6 @@ hv_vmbus_init(void) hv_vmbus_g_context.hypercall_page = virt_addr; - tc_init(&hv_timecounter); /* register virtual timecount */ - hv_et_init(); return (0); @@ -368,6 +367,9 @@ hv_vmbus_synic_init(void *arg) wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT, shared_sint.as_uint64_t); + wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT, + shared_sint.as_uint64_t); + /* Enable the global synic bit */ sctrl.as_uint64_t = rdmsr(HV_X64_MSR_SCONTROL); sctrl.u.enable = 1; @@ -404,12 +406,23 @@ void hv_vmbus_synic_cleanup(void *arg) shared_sint.u.masked = 1; /* - * Disable the interrupt + * Disable the interrupt 0 */ wrmsr( HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT, shared_sint.as_uint64_t); + shared_sint.as_uint64_t = rdmsr( + HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT); + + shared_sint.u.masked = 1; + + /* + * Disable the interrupt 1 + */ + wrmsr( + HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT, + shared_sint.as_uint64_t); simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP); simp.u.simp_enabled = 0; simp.u.base_simp_gpa = 0; @@ -423,3 +436,14 @@ void hv_vmbus_synic_cleanup(void *arg) wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t); } +static void +hv_tc_init(void) +{ + if (vm_guest != VM_GUEST_HV) + return; + + /* register virtual timecounter */ + tc_init(&hv_timecounter); +} + +SYSINIT(hv_tc_init, SI_SUB_HYPERVISOR, SI_ORDER_FIRST, hv_tc_init, NULL); diff --git a/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c b/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c index 4895f71..669a532 100644 --- a/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c +++ b/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c @@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$"); #include <sys/pcpu.h> #include <x86/apicvar.h> +#include <dev/hyperv/include/hyperv.h> #include "hv_vmbus_priv.h" #include <contrib/dev/acpica/include/acpi.h> @@ -75,7 +76,7 @@ static char *vmbus_ids[] = { "VMBUS", NULL }; * the hypervisor. */ static void -vmbus_msg_swintr(void *arg) +vmbus_msg_swintr(void *arg, int pending __unused) { int cpu; void* page_addr; @@ -174,12 +175,15 @@ hv_vmbus_isr(struct trapframe *frame) /* Check if there are actual msgs to be process */ page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; - msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; + msg = (hv_vmbus_message*) page_addr + HV_VMBUS_TIMER_SINT; /* we call eventtimer process the message */ if (msg->header.message_type == HV_MESSAGE_TIMER_EXPIRED) { msg->header.message_type = HV_MESSAGE_TYPE_NONE; + /* call intrrupt handler of event timer */ + hv_et_intr(frame); + /* * Make sure the write to message_type (ie set to * HV_MESSAGE_TYPE_NONE) happens before we read the @@ -196,12 +200,12 @@ hv_vmbus_isr(struct trapframe *frame) */ wrmsr(HV_X64_MSR_EOM, 0); } - hv_et_intr(frame); - return (FILTER_HANDLED); } + msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) { - swi_sched(hv_vmbus_g_context.msg_swintr[cpu], 0); + taskqueue_enqueue(hv_vmbus_g_context.hv_msg_tq[cpu], + &hv_vmbus_g_context.hv_msg_task[cpu]); } return (FILTER_HANDLED); @@ -279,6 +283,23 @@ vmbus_write_ivar( return (ENOENT); } +static int +vmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen) +{ + char guidbuf[40]; + struct hv_device *dev_ctx = device_get_ivars(child); + + strlcat(buf, "classid=", buflen); + snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->class_id); + strlcat(buf, guidbuf, buflen); + + strlcat(buf, " deviceid=", buflen); + snprintf_hv_guid(guidbuf, sizeof(guidbuf), &dev_ctx->device_id); + strlcat(buf, guidbuf, buflen); + + return (0); +} + struct hv_device* hv_vmbus_child_device_create( hv_guid type, @@ -300,15 +321,17 @@ hv_vmbus_child_device_create( return (child_dev); } -static void -print_dev_guid(struct hv_device *dev) +int +snprintf_hv_guid(char *buf, size_t sz, const hv_guid *guid) { - int i; - unsigned char guid_name[100]; - for (i = 0; i < 32; i += 2) - sprintf(&guid_name[i], "%02x", dev->class_id.data[i / 2]); - if(bootverbose) - printf("VMBUS: Class ID: %s\n", guid_name); + int cnt; + const unsigned char *d = guid->data; + + cnt = snprintf(buf, sz, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + d[3], d[2], d[1], d[0], d[5], d[4], d[7], d[6], + d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); + return (cnt); } int @@ -317,8 +340,11 @@ hv_vmbus_child_device_register(struct hv_device *child_dev) device_t child; int ret = 0; - print_dev_guid(child_dev); - + if (bootverbose) { + char name[40]; + snprintf_hv_guid(name, sizeof(name), &child_dev->class_id); + printf("VMBUS: Class ID: %s\n", name); + } child = device_add_child(vmbus_devp, NULL, -1); child_dev->device = child; @@ -485,9 +511,6 @@ vmbus_bus_init(void) setup_args.vector = hv_vmbus_g_context.hv_cb_vector; CPU_FOREACH(j) { - hv_vmbus_g_context.hv_msg_intr_event[j] = NULL; - hv_vmbus_g_context.msg_swintr[j] = NULL; - snprintf(buf, sizeof(buf), "cpu%d:hyperv", j); intrcnt_add(buf, &hv_vmbus_intr_cpu[j]); @@ -504,39 +527,21 @@ vmbus_bus_init(void) */ hv_vmbus_g_context.hv_event_queue[j] = taskqueue_create_fast("hyperv event", M_WAITOK, taskqueue_thread_enqueue, &hv_vmbus_g_context.hv_event_queue[j]); - if (hv_vmbus_g_context.hv_event_queue[j] == NULL) { - if (bootverbose) - printf("VMBUS: failed to setup taskqueue\n"); - goto cleanup1; - } CPU_SETOF(j, &cpu_mask); taskqueue_start_threads_cpuset(&hv_vmbus_g_context.hv_event_queue[j], 1, PI_NET, &cpu_mask, "hvevent%d", j); /* - * Setup software interrupt thread and handler for msg handling. - */ - ret = swi_add(&hv_vmbus_g_context.hv_msg_intr_event[j], - "hv_msg", vmbus_msg_swintr, (void *)(long)j, SWI_CLOCK, 0, - &hv_vmbus_g_context.msg_swintr[j]); - if (ret) { - if(bootverbose) - printf("VMBUS: failed to setup msg swi for " - "cpu %d\n", j); - goto cleanup1; - } - - /* - * Bind the swi thread to the cpu. + * Setup per-cpu tasks and taskqueues to handle msg. */ - ret = intr_event_bind(hv_vmbus_g_context.hv_msg_intr_event[j], - j); - if (ret) { - if(bootverbose) - printf("VMBUS: failed to bind msg swi thread " - "to cpu %d\n", j); - goto cleanup1; - } + hv_vmbus_g_context.hv_msg_tq[j] = taskqueue_create_fast( + "hyperv msg", M_WAITOK, taskqueue_thread_enqueue, + &hv_vmbus_g_context.hv_msg_tq[j]); + CPU_SETOF(j, &cpu_mask); + taskqueue_start_threads_cpuset(&hv_vmbus_g_context.hv_msg_tq[j], + 1, PI_NET, &cpu_mask, "hvmsg%d", j); + TASK_INIT(&hv_vmbus_g_context.hv_msg_task[j], 0, + vmbus_msg_swintr, (void *)(long)j); /* * Prepare the per cpu msg and event pages to be called on each cpu. @@ -576,11 +581,10 @@ vmbus_bus_init(void) * remove swi and vmbus callback vector; */ CPU_FOREACH(j) { - if (hv_vmbus_g_context.hv_event_queue[j] != NULL) + if (hv_vmbus_g_context.hv_event_queue[j] != NULL) { taskqueue_free(hv_vmbus_g_context.hv_event_queue[j]); - if (hv_vmbus_g_context.msg_swintr[j] != NULL) - swi_remove(hv_vmbus_g_context.msg_swintr[j]); - hv_vmbus_g_context.hv_msg_intr_event[j] = NULL; + hv_vmbus_g_context.hv_event_queue[j] = NULL; + } } vmbus_vector_free(hv_vmbus_g_context.hv_cb_vector); @@ -645,11 +649,10 @@ vmbus_bus_exit(void) /* remove swi */ CPU_FOREACH(i) { - if (hv_vmbus_g_context.hv_event_queue[i] != NULL) + if (hv_vmbus_g_context.hv_event_queue[i] != NULL) { taskqueue_free(hv_vmbus_g_context.hv_event_queue[i]); - if (hv_vmbus_g_context.msg_swintr[i] != NULL) - swi_remove(hv_vmbus_g_context.msg_swintr[i]); - hv_vmbus_g_context.hv_msg_intr_event[i] = NULL; + hv_vmbus_g_context.hv_event_queue[i] = NULL; + } } vmbus_vector_free(hv_vmbus_g_context.hv_cb_vector); @@ -714,6 +717,7 @@ static device_method_t vmbus_methods[] = { DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, vmbus_read_ivar), DEVMETHOD(bus_write_ivar, vmbus_write_ivar), + DEVMETHOD(bus_child_pnpinfo_str, vmbus_child_pnpinfo_str), { 0, 0 } }; diff --git a/sys/dev/hyperv/vmbus/hv_vmbus_priv.h b/sys/dev/hyperv/vmbus/hv_vmbus_priv.h index 62fa22a..1a0ed04 100644 --- a/sys/dev/hyperv/vmbus/hv_vmbus_priv.h +++ b/sys/dev/hyperv/vmbus/hv_vmbus_priv.h @@ -70,6 +70,7 @@ typedef uint16_t hv_vmbus_status; * You did not supply enough message buffers to send a message. */ +#define HV_STATUS_SUCCESS ((uint16_t)0) #define HV_STATUS_INSUFFICIENT_BUFFERS ((uint16_t)0x0013) typedef void (*hv_vmbus_channel_callback)(void *context); @@ -180,7 +181,8 @@ enum { HV_VMBUS_EVENT_PORT_ID = 2, HV_VMBUS_MONITOR_CONNECTION_ID = 3, HV_VMBUS_MONITOR_PORT_ID = 3, - HV_VMBUS_MESSAGE_SINT = 2 + HV_VMBUS_MESSAGE_SINT = 2, + HV_VMBUS_TIMER_SINT = 4, }; #define HV_PRESENT_BIT 0x80000000 @@ -203,8 +205,8 @@ typedef struct { * event and msg handling. */ struct taskqueue *hv_event_queue[MAXCPU]; - struct intr_event *hv_msg_intr_event[MAXCPU]; - void *msg_swintr[MAXCPU]; + struct taskqueue *hv_msg_tq[MAXCPU]; + struct task hv_msg_task[MAXCPU]; /* * Host use this vector to intrrupt guest for vmbus channel * event and msg. diff --git a/sys/dev/ichwd/ichwd.c b/sys/dev/ichwd/ichwd.c index ec84f8f..75c41b7 100644 --- a/sys/dev/ichwd/ichwd.c +++ b/sys/dev/ichwd/ichwd.c @@ -540,9 +540,6 @@ ichwd_find_ich_lpc_bridge(struct ichwd_device **id_p) if (ich == NULL) return (NULL); - ichwd_verbose_printf(ich, "found ICH%d or equivalent chipset: %s\n", - id->ich_version, id->desc); - if (id_p) *id_p = id; @@ -573,8 +570,6 @@ ichwd_identify(driver_t *driver, device_t parent) if (dev == NULL) return; - device_set_desc_copy(dev, id_p->desc); - switch (id_p->tco_version) { case 1: break; @@ -611,10 +606,16 @@ ichwd_identify(driver_t *driver, device_t parent) static int ichwd_probe(device_t dev) { + struct ichwd_device *id_p; /* Do not claim some ISA PnP device by accident. */ if (isa_get_logicalid(dev) != 0) return (ENXIO); + + if (ichwd_find_ich_lpc_bridge(&id_p) == NULL) + return (ENXIO); + + device_set_desc_copy(dev, id_p->desc); return (0); } @@ -677,9 +678,6 @@ ichwd_attach(device_t dev) if (ichwd_clear_noreboot(sc) != 0) goto fail; - ichwd_verbose_printf(dev, "%s (ICH%d or equivalent)\n", - id_p->desc, sc->ich_version); - /* * Determine if we are coming up after a watchdog-induced reset. Some * BIOSes may clear this bit at bootup, preventing us from reporting diff --git a/sys/dev/iicbus/iicbus.c b/sys/dev/iicbus/iicbus.c index df29b09..a090bb9 100644 --- a/sys/dev/iicbus/iicbus.c +++ b/sys/dev/iicbus/iicbus.c @@ -149,7 +149,7 @@ iicbus_print_child(device_t dev, device_t child) retval += bus_print_child_header(dev, child); if (devi->addr != 0) retval += printf(" at addr %#x", devi->addr); - resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%ld"); + resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%jd"); retval += bus_print_child_footer(dev, child); return (retval); diff --git a/sys/dev/iir/iir.c b/sys/dev/iir/iir.c index bf5cec5..e74698e 100644 --- a/sys/dev/iir/iir.c +++ b/sys/dev/iir/iir.c @@ -744,9 +744,9 @@ gdt_next(struct gdt_softc *gdt) ccb->ccb_h.flags)); csio = &ccb->csio; ccbh = &ccb->ccb_h; - cmd = csio->cdb_io.cdb_bytes[0]; - /* Max CDB length is 12 bytes */ - if (csio->cdb_len > 12) { + cmd = scsiio_cdb_ptr(csio)[0]; + /* Max CDB length is 12 bytes, can't be phys addr */ + if (csio->cdb_len > 12 || (ccbh->flags & CAM_CDB_PHYS)) { ccbh->status = CAM_REQ_INVALID; --gdt_stat.io_count_act; xpt_done(ccb); diff --git a/sys/dev/iir/iir_pci.c b/sys/dev/iir/iir_pci.c index 261f5a6..5db66b1 100644 --- a/sys/dev/iir/iir_pci.c +++ b/sys/dev/iir/iir_pci.c @@ -228,7 +228,7 @@ iir_pci_attach(device_t dev) /* check and reset interface area */ bus_write_4(gdt->sc_dpmem, GDT_MPR_IC, htole32(GDT_MPR_MAGIC)); if (bus_read_4(gdt->sc_dpmem, GDT_MPR_IC) != htole32(GDT_MPR_MAGIC)) { - device_printf(dev, "cannot access DPMEM at 0x%lx (shadowed?)\n", + device_printf(dev, "cannot access DPMEM at 0x%jx (shadowed?)\n", rman_get_start(gdt->sc_dpmem)); error = ENXIO; goto err; diff --git a/sys/dev/ipmi/ipmi.c b/sys/dev/ipmi/ipmi.c index 8101717..314d4d6 100644 --- a/sys/dev/ipmi/ipmi.c +++ b/sys/dev/ipmi/ipmi.c @@ -603,6 +603,20 @@ ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req) */ static int +ipmi_reset_watchdog(struct ipmi_softc *sc) +{ + struct ipmi_request *req; + int error; + + IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), + IPMI_RESET_WDOG, 0, 0); + error = ipmi_submit_driver_request(sc, req, 0); + if (error) + device_printf(sc->ipmi_dev, "Failed to reset watchdog\n"); + return (error); +} + +static int ipmi_set_watchdog(struct ipmi_softc *sc, unsigned int sec) { struct ipmi_request *req; @@ -613,7 +627,6 @@ ipmi_set_watchdog(struct ipmi_softc *sc, unsigned int sec) IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_SET_WDOG, 6, 0); - if (sec) { req->ir_request[0] = IPMI_SET_WD_TIMER_DONT_STOP | IPMI_SET_WD_TIMER_SMS_OS; @@ -630,24 +643,10 @@ ipmi_set_watchdog(struct ipmi_softc *sc, unsigned int sec) req->ir_request[4] = 0; req->ir_request[5] = 0; } - error = ipmi_submit_driver_request(sc, req, 0); if (error) device_printf(sc->ipmi_dev, "Failed to set watchdog\n"); - else if (sec) { - IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), - IPMI_RESET_WDOG, 0, 0); - - error = ipmi_submit_driver_request(sc, req, 0); - if (error) - device_printf(sc->ipmi_dev, - "Failed to reset watchdog\n"); - } - return (error); - /* - dump_watchdog(sc); - */ } static void @@ -665,12 +664,24 @@ ipmi_wd_event(void *arg, unsigned int cmd, int *error) timeout = ((uint64_t)1 << cmd) / 1000000000; if (timeout == 0) timeout = 1; - e = ipmi_set_watchdog(sc, timeout); - if (e == 0) { - *error = 0; - sc->ipmi_watchdog_active = 1; - } else - (void)ipmi_set_watchdog(sc, 0); + if (timeout != sc->ipmi_watchdog_active) { + e = ipmi_set_watchdog(sc, timeout); + if (e == 0) { + sc->ipmi_watchdog_active = timeout; + } else { + (void)ipmi_set_watchdog(sc, 0); + sc->ipmi_watchdog_active = 0; + } + } + if (sc->ipmi_watchdog_active != 0) { + e = ipmi_reset_watchdog(sc); + if (e == 0) { + *error = 0; + } else { + (void)ipmi_set_watchdog(sc, 0); + sc->ipmi_watchdog_active = 0; + } + } } else if (atomic_readandclear_int(&sc->ipmi_watchdog_active) != 0) { e = ipmi_set_watchdog(sc, 0); if (e != 0 && cmd == 0) diff --git a/sys/dev/isci/isci_controller.c b/sys/dev/isci/isci_controller.c index b0f4285..d3ec045 100644 --- a/sys/dev/isci/isci_controller.c +++ b/sys/dev/isci/isci_controller.c @@ -740,6 +740,11 @@ void isci_action(struct cam_sim *sim, union ccb *ccb) } break; case XPT_SCSI_IO: + if (ccb->ccb_h.flags & CAM_CDB_PHYS) { + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } isci_io_request_execute_scsi_io(ccb, controller); break; #if __FreeBSD_version >= 900026 @@ -802,6 +807,7 @@ isci_controller_release_queued_ccbs(struct ISCI_CONTROLLER *controller) { struct ISCI_REMOTE_DEVICE *dev; struct ccb_hdr *ccb_h; + uint8_t *ptr; int dev_idx; KASSERT(mtx_owned(&controller->lock), ("controller lock not owned")); @@ -821,8 +827,8 @@ isci_controller_release_queued_ccbs(struct ISCI_CONTROLLER *controller) if (ccb_h == NULL) continue; - isci_log_message(1, "ISCI", "release %p %x\n", ccb_h, - ((union ccb *)ccb_h)->csio.cdb_io.cdb_bytes[0]); + ptr = scsiio_cdb_ptr(&((union ccb *)ccb_h)->csio); + isci_log_message(1, "ISCI", "release %p %x\n", ccb_h, *ptr); dev->queued_ccb_in_progress = (union ccb *)ccb_h; isci_io_request_execute_scsi_io( diff --git a/sys/dev/isci/isci_io_request.c b/sys/dev/isci/isci_io_request.c index e6b68fd..f86c126 100644 --- a/sys/dev/isci/isci_io_request.c +++ b/sys/dev/isci/isci_io_request.c @@ -86,6 +86,7 @@ isci_io_request_complete(SCI_CONTROLLER_HANDLE_T scif_controller, struct ISCI_REMOTE_DEVICE *isci_remote_device; union ccb *ccb; BOOL complete_ccb; + struct ccb_scsiio *csio; complete_ccb = TRUE; isci_controller = (struct ISCI_CONTROLLER *) sci_object_get_association(scif_controller); @@ -93,7 +94,7 @@ isci_io_request_complete(SCI_CONTROLLER_HANDLE_T scif_controller, (struct ISCI_REMOTE_DEVICE *) sci_object_get_association(remote_device); ccb = isci_request->ccb; - + csio = &ccb->csio; ccb->ccb_h.status &= ~CAM_STATUS_MASK; switch (completion_status) { @@ -124,7 +125,6 @@ isci_io_request_complete(SCI_CONTROLLER_HANDLE_T scif_controller, SCI_SSP_RESPONSE_IU_T * response_buffer; uint32_t sense_length; int error_code, sense_key, asc, ascq; - struct ccb_scsiio *csio = &ccb->csio; response_buffer = (SCI_SSP_RESPONSE_IU_T *) scif_io_request_get_response_iu_address( @@ -146,7 +146,7 @@ isci_io_request_complete(SCI_CONTROLLER_HANDLE_T scif_controller, isci_log_message(1, "ISCI", "isci: bus=%x target=%x lun=%x cdb[0]=%x status=%x key=%x asc=%x ascq=%x\n", ccb->ccb_h.path_id, ccb->ccb_h.target_id, - ccb->ccb_h.target_lun, csio->cdb_io.cdb_bytes[0], + ccb->ccb_h.target_lun, scsiio_cdb_ptr(csio), csio->scsi_status, sense_key, asc, ascq); break; } @@ -157,7 +157,7 @@ isci_io_request_complete(SCI_CONTROLLER_HANDLE_T scif_controller, isci_log_message(0, "ISCI", "isci: bus=%x target=%x lun=%x cdb[0]=%x remote device reset required\n", ccb->ccb_h.path_id, ccb->ccb_h.target_id, - ccb->ccb_h.target_lun, ccb->csio.cdb_io.cdb_bytes[0]); + ccb->ccb_h.target_lun, scsiio_cdb_ptr(csio)); break; case SCI_IO_FAILURE_TERMINATED: @@ -165,7 +165,7 @@ isci_io_request_complete(SCI_CONTROLLER_HANDLE_T scif_controller, isci_log_message(0, "ISCI", "isci: bus=%x target=%x lun=%x cdb[0]=%x terminated\n", ccb->ccb_h.path_id, ccb->ccb_h.target_id, - ccb->ccb_h.target_lun, ccb->csio.cdb_io.cdb_bytes[0]); + ccb->ccb_h.target_lun, scsiio_cdb_ptr(csio)); break; case SCI_IO_FAILURE_INVALID_STATE: @@ -208,7 +208,7 @@ isci_io_request_complete(SCI_CONTROLLER_HANDLE_T scif_controller, isci_log_message(1, "ISCI", "isci: bus=%x target=%x lun=%x cdb[0]=%x completion status=%x\n", ccb->ccb_h.path_id, ccb->ccb_h.target_id, - ccb->ccb_h.target_lun, ccb->csio.cdb_io.cdb_bytes[0], + ccb->ccb_h.target_lun, scsiio_cdb_ptr(csio), completion_status); ccb->ccb_h.status |= CAM_REQ_CMP_ERR; break; @@ -285,13 +285,13 @@ isci_io_request_complete(SCI_CONTROLLER_HANDLE_T scif_controller, * get a ready notification for this device. */ isci_log_message(1, "ISCI", "already queued %p %x\n", - ccb, ccb->csio.cdb_io.cdb_bytes[0]); + ccb, scsiio_cdb_ptr(csio)); isci_remote_device->queued_ccb_in_progress = NULL; } else { isci_log_message(1, "ISCI", "queue %p %x\n", ccb, - ccb->csio.cdb_io.cdb_bytes[0]); + scsiio_cdb_ptr(csio)); ccb->ccb_h.status |= CAM_SIM_QUEUED; TAILQ_INSERT_TAIL(&isci_remote_device->queued_ccbs, @@ -373,7 +373,7 @@ scif_cb_io_request_get_cdb_address(void * scif_user_io_request) struct ISCI_IO_REQUEST *isci_request = (struct ISCI_IO_REQUEST *)scif_user_io_request; - return (isci_request->ccb->csio.cdb_io.cdb_bytes); + return (scsiio_cdb_ptr(&isci_request->ccb->csio)); } /** diff --git a/sys/dev/iscsi/iscsi.c b/sys/dev/iscsi/iscsi.c index a9bdaf7..7172ec0 100644 --- a/sys/dev/iscsi/iscsi.c +++ b/sys/dev/iscsi/iscsi.c @@ -288,6 +288,8 @@ iscsi_session_terminate_task(struct iscsi_session *is, struct iscsi_outstanding *io, bool requeue) { + ISCSI_SESSION_LOCK_ASSERT(is); + if (io->io_ccb != NULL) { io->io_ccb->ccb_h.status &= ~(CAM_SIM_QUEUED | CAM_STATUS_MASK); if (requeue) @@ -2220,6 +2222,7 @@ iscsi_action_scsiio(struct iscsi_session *is, union ccb *ccb) error = icl_pdu_append_data(request, csio->data_ptr, len, M_NOWAIT); if (error != 0) { + iscsi_outstanding_remove(is, io); icl_pdu_free(request); if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { xpt_freeze_devq(ccb->ccb_h.path, 1); diff --git a/sys/dev/iwn/if_iwn.c b/sys/dev/iwn/if_iwn.c index 28b7b7f..bffa8de 100644 --- a/sys/dev/iwn/if_iwn.c +++ b/sys/dev/iwn/if_iwn.c @@ -2554,7 +2554,8 @@ iwn_find_eeprom_channel(struct iwn_softc *sc, struct ieee80211_channel *c) } else { for (j = 0; j < 5; j++) { for (i = 0; i < iwn_bands[j].nchan; i++) { - if (iwn_bands[j].chan[i] == c->ic_ieee) + if (iwn_bands[j].chan[i] == c->ic_ieee && + ((j == 0) ^ IEEE80211_IS_CHAN_A(c)) == 1) return &sc->eeprom_channels[j][i]; } } @@ -8700,7 +8701,9 @@ iwn_panicked(void *arg0, int pending) struct iwn_softc *sc = arg0; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); +#if 0 int error; +#endif if (vap == NULL) { printf("%s: null vap\n", __func__); @@ -8708,8 +8711,18 @@ iwn_panicked(void *arg0, int pending) } device_printf(sc->sc_dev, "%s: controller panicked, iv_state = %d; " - "resetting...\n", __func__, vap->iv_state); + "restarting\n", __func__, vap->iv_state); + /* + * This is not enough work. We need to also reinitialise + * the correct transmit state for aggregation enabled queues, + * which has a very specific requirement of + * ring index = 802.11 seqno % 256. If we don't do this (which + * we definitely don't!) then the firmware will just panic again. + */ +#if 1 + ieee80211_restart_all(ic); +#else IWN_LOCK(sc); iwn_stop_locked(sc); @@ -8726,6 +8739,7 @@ iwn_panicked(void *arg0, int pending) } IWN_UNLOCK(sc); +#endif } static void diff --git a/sys/dev/ixgbe/if_ix.c b/sys/dev/ixgbe/if_ix.c index f0e218f..5e16fbd 100644 --- a/sys/dev/ixgbe/if_ix.c +++ b/sys/dev/ixgbe/if_ix.c @@ -4749,10 +4749,6 @@ ixgbe_sysctl_advertise(SYSCTL_HANDLER_ARGS) if ((error) || (req->newptr == NULL)) return (error); - /* Checks to validate new value */ - if (adapter->advertise == advertise) /* no change */ - return (0); - return ixgbe_set_advertise(adapter, advertise); } @@ -4763,6 +4759,10 @@ ixgbe_set_advertise(struct adapter *adapter, int advertise) struct ixgbe_hw *hw; ixgbe_link_speed speed; + /* Checks to validate new value */ + if (adapter->advertise == advertise) /* no change */ + return (0); + hw = &adapter->hw; dev = adapter->dev; diff --git a/sys/dev/ixgbe/ix_txrx.c b/sys/dev/ixgbe/ix_txrx.c index 00e5dc8..66bcb25 100644 --- a/sys/dev/ixgbe/ix_txrx.c +++ b/sys/dev/ixgbe/ix_txrx.c @@ -1753,7 +1753,6 @@ ixgbe_rxeof(struct ix_queue *que) struct rx_ring *rxr = que->rxr; struct ifnet *ifp = adapter->ifp; struct lro_ctrl *lro = &rxr->lro; - struct lro_entry *queued; int i, nextp, processed = 0; u32 staterr = 0; u32 count = adapter->rx_process_limit; @@ -2003,10 +2002,7 @@ next_desc: /* * Flush any outstanding LRO work */ - while ((queued = SLIST_FIRST(&lro->lro_active)) != NULL) { - SLIST_REMOVE_HEAD(&lro->lro_active, next); - tcp_lro_flush(lro, queued); - } + tcp_lro_flush_all(lro); IXGBE_RX_UNLOCK(rxr); diff --git a/sys/dev/ixl/ixl_txrx.c b/sys/dev/ixl/ixl_txrx.c index 0ae7433..ca6e252 100644 --- a/sys/dev/ixl/ixl_txrx.c +++ b/sys/dev/ixl/ixl_txrx.c @@ -1511,7 +1511,6 @@ ixl_rxeof(struct ixl_queue *que, int count) struct ifnet *ifp = vsi->ifp; #if defined(INET6) || defined(INET) struct lro_ctrl *lro = &rxr->lro; - struct lro_entry *queued; #endif int i, nextp, processed = 0; union i40e_rx_desc *cur; @@ -1735,10 +1734,7 @@ next_desc: /* * Flush any outstanding LRO work */ - while ((queued = SLIST_FIRST(&lro->lro_active)) != NULL) { - SLIST_REMOVE_HEAD(&lro->lro_active, next); - tcp_lro_flush(lro, queued); - } + tcp_lro_flush_all(lro); #endif IXL_RX_UNLOCK(rxr); diff --git a/sys/dev/le/lebuffer_sbus.c b/sys/dev/le/lebuffer_sbus.c index 1254fc3..f2c7c5f 100644 --- a/sys/dev/le/lebuffer_sbus.c +++ b/sys/dev/le/lebuffer_sbus.c @@ -297,7 +297,7 @@ lebuffer_print_res(struct lebuffer_devinfo *ldi) rv = 0; rv += resource_list_print_type(&ldi->ldi_rl, "mem", SYS_RES_MEMORY, - "%#lx"); - rv += resource_list_print_type(&ldi->ldi_rl, "irq", SYS_RES_IRQ, "%ld"); + "%#jx"); + rv += resource_list_print_type(&ldi->ldi_rl, "irq", SYS_RES_IRQ, "%jd"); return (rv); } diff --git a/sys/dev/mca/mca_bus.c b/sys/dev/mca/mca_bus.c index 55ac9ed..2de61da 100644 --- a/sys/dev/mca/mca_bus.c +++ b/sys/dev/mca/mca_bus.c @@ -365,11 +365,11 @@ mca_print_child (device_t dev, device_t child) rid = 0; while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_IOPORT, rid++))) { if (rle->count == 1) { - snprintf(buf, sizeof(buf), "%s%lx", + snprintf(buf, sizeof(buf), "%s%jx", ((rid == 1) ? "io 0x" : "0x"), rle->start); } else { - snprintf(buf, sizeof(buf), "%s%lx-0x%lx", + snprintf(buf, sizeof(buf), "%s%jx-0x%jx", ((rid == 1) ? "io 0x" : "0x"), rle->start, (rle->start + rle->count)); @@ -381,11 +381,11 @@ mca_print_child (device_t dev, device_t child) rid = 0; while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_MEMORY, rid++))) { if (rle->count == 1) { - snprintf(buf, sizeof(buf), "%s%lx", + snprintf(buf, sizeof(buf), "%s%jx", ((rid == 1) ? "mem 0x" : "0x"), rle->start); } else { - snprintf(buf, sizeof(buf), "%s%lx-0x%lx", + snprintf(buf, sizeof(buf), "%s%jx-0x%jx", ((rid == 1) ? "mem 0x" : "0x"), rle->start, (rle->start + rle->count)); @@ -396,14 +396,14 @@ mca_print_child (device_t dev, device_t child) rid = 0; while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_IRQ, rid++))) { - snprintf(buf, sizeof(buf), "irq %ld", rle->start); + snprintf(buf, sizeof(buf), "irq %jd", rle->start); mca_reg_print(child, buf, ((rid == 1) ? &separator : NULL), &column); } rid = 0; while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_DRQ, rid++))) { - snprintf(buf, sizeof(buf), "drq %lx", rle->start); + snprintf(buf, sizeof(buf), "drq %jx", rle->start); mca_reg_print(child, buf, ((rid == 1) ? &separator : NULL), &column); } diff --git a/sys/dev/mfi/mfi.c b/sys/dev/mfi/mfi.c index ddcc853..c224186 100644 --- a/sys/dev/mfi/mfi.c +++ b/sys/dev/mfi/mfi.c @@ -2141,7 +2141,7 @@ mfi_build_syspdio(struct mfi_softc *sc, struct bio *bio) pass = &cm->cm_frame->pass; bzero(pass->cdb, 16); pass->header.cmd = MFI_CMD_PD_SCSI_IO; - switch (bio->bio_cmd & 0x03) { + switch (bio->bio_cmd) { case BIO_READ: flags = MFI_CMD_DATAIN | MFI_CMD_BIO; readop = 1; @@ -2200,7 +2200,7 @@ mfi_build_ldio(struct mfi_softc *sc, struct bio *bio) bzero(cm->cm_frame, sizeof(union mfi_frame)); cm->cm_frame->header.context = context; io = &cm->cm_frame->io; - switch (bio->bio_cmd & 0x03) { + switch (bio->bio_cmd) { case BIO_READ: io->header.cmd = MFI_CMD_LD_READ; flags = MFI_CMD_DATAIN | MFI_CMD_BIO; diff --git a/sys/dev/mlx5/mlx5_en/mlx5_en_rx.c b/sys/dev/mlx5/mlx5_en/mlx5_en_rx.c index 48339be..230dfcc 100644 --- a/sys/dev/mlx5/mlx5_en/mlx5_en_rx.c +++ b/sys/dev/mlx5/mlx5_en/mlx5_en_rx.c @@ -322,9 +322,6 @@ mlx5e_decompress_cqes(struct mlx5e_cq *cq) static int mlx5e_poll_rx_cq(struct mlx5e_rq *rq, int budget) { -#ifndef HAVE_TURBO_LRO - struct lro_entry *queued; -#endif int i; for (i = 0; i < budget; i++) { @@ -399,10 +396,7 @@ wq_ll_pop: /* ensure cq space is freed before enabling more cqes */ wmb(); #ifndef HAVE_TURBO_LRO - while ((queued = SLIST_FIRST(&rq->lro.lro_active)) != NULL) { - SLIST_REMOVE_HEAD(&rq->lro.lro_active, next); - tcp_lro_flush(&rq->lro, queued); - } + tcp_lro_flush_all(&rq->lro); #endif return (i); } diff --git a/sys/dev/mmc/host/dwmmc.c b/sys/dev/mmc/host/dwmmc.c index 5dc0626..5b24a6d 100644 --- a/sys/dev/mmc/host/dwmmc.c +++ b/sys/dev/mmc/host/dwmmc.c @@ -1178,3 +1178,4 @@ static devclass_t dwmmc_devclass; DRIVER_MODULE(dwmmc, simplebus, dwmmc_driver, dwmmc_devclass, 0, 0); DRIVER_MODULE(dwmmc, ofwbus, dwmmc_driver, dwmmc_devclass, 0, 0); DRIVER_MODULE(mmc, dwmmc, mmc_driver, mmc_devclass, NULL, NULL); +MODULE_DEPEND(dwmmc, mmc, 1, 1, 1); diff --git a/sys/dev/mmc/mmc.c b/sys/dev/mmc/mmc.c index a65ff41..9f88079 100644 --- a/sys/dev/mmc/mmc.c +++ b/sys/dev/mmc/mmc.c @@ -1811,3 +1811,5 @@ driver_t mmc_driver = { sizeof(struct mmc_softc), }; devclass_t mmc_devclass; + +MODULE_VERSION(mmc, 1); diff --git a/sys/dev/mmc/mmcreg.h b/sys/dev/mmc/mmcreg.h index f454ddb..b169d26 100644 --- a/sys/dev/mmc/mmcreg.h +++ b/sys/dev/mmc/mmcreg.h @@ -85,8 +85,11 @@ struct mmc_command { #define MMC_RSP_R1B (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE | MMC_RSP_BUSY) #define MMC_RSP_R2 (MMC_RSP_PRESENT | MMC_RSP_136 | MMC_RSP_CRC) #define MMC_RSP_R3 (MMC_RSP_PRESENT) -#define MMC_RSP_R6 (MMC_RSP_PRESENT | MMC_RSP_CRC) -#define MMC_RSP_R7 (MMC_RSP_PRESENT | MMC_RSP_CRC) +#define MMC_RSP_R4 (MMC_RSP_PRESENT) +#define MMC_RSP_R5 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE) +#define MMC_RSP_R5B (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE | MMC_RSP_BUSY) +#define MMC_RSP_R6 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE) +#define MMC_RSP_R7 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE) #define MMC_RSP(x) ((x) & MMC_RSP_MASK) uint32_t retries; uint32_t error; diff --git a/sys/dev/mvs/mvs_pci.c b/sys/dev/mvs/mvs_pci.c index 316ac00..b3262f8 100644 --- a/sys/dev/mvs/mvs_pci.c +++ b/sys/dev/mvs/mvs_pci.c @@ -396,7 +396,7 @@ mvs_alloc_resource(device_t dev, device_t child, int type, int *rid, int unit = ((struct mvs_channel *)device_get_softc(child))->unit; struct resource *res = NULL; int offset = HC_BASE(unit >> 2) + PORT_BASE(unit & 0x03); - long st; + rman_res_t st; switch (type) { case SYS_RES_MEMORY: diff --git a/sys/dev/mvs/mvs_soc.c b/sys/dev/mvs/mvs_soc.c index d4ecd8f..b34df30 100644 --- a/sys/dev/mvs/mvs_soc.c +++ b/sys/dev/mvs/mvs_soc.c @@ -342,7 +342,7 @@ mvs_alloc_resource(device_t dev, device_t child, int type, int *rid, int unit = ((struct mvs_channel *)device_get_softc(child))->unit; struct resource *res = NULL; int offset = PORT_BASE(unit & 0x03); - long st; + rman_res_t st; switch (type) { case SYS_RES_MEMORY: diff --git a/sys/dev/mxge/if_mxge.c b/sys/dev/mxge/if_mxge.c index 928917f..f78b34b 100644 --- a/sys/dev/mxge/if_mxge.c +++ b/sys/dev/mxge/if_mxge.c @@ -2819,11 +2819,7 @@ mxge_clean_rx_done(struct mxge_slice_state *ss) break; } #if defined(INET) || defined (INET6) - while (!SLIST_EMPTY(&ss->lc.lro_active)) { - struct lro_entry *lro = SLIST_FIRST(&ss->lc.lro_active); - SLIST_REMOVE_HEAD(&ss->lc.lro_active, next); - tcp_lro_flush(&ss->lc, lro); - } + tcp_lro_flush_all(&ss->lc); #endif } @@ -4612,7 +4608,7 @@ mxge_add_msix_irqs(mxge_softc_t *sc) device_printf(sc->dev, "using %d msix IRQs:", sc->num_slices); for (i = 0; i < sc->num_slices; i++) - printf(" %ld", rman_get_start(sc->msix_irq_res[i])); + printf(" %jd", rman_get_start(sc->msix_irq_res[i])); printf("\n"); } return (0); @@ -4668,7 +4664,7 @@ mxge_add_single_irq(mxge_softc_t *sc) return ENXIO; } if (mxge_verbose) - device_printf(sc->dev, "using %s irq %ld\n", + device_printf(sc->dev, "using %s irq %jd\n", sc->legacy_irq ? "INTx" : "MSI", rman_get_start(sc->irq_res)); err = bus_setup_intr(sc->dev, sc->irq_res, @@ -4823,7 +4819,7 @@ mxge_attach(device_t dev) sc->sram = rman_get_virtual(sc->mem_res); sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100; if (sc->sram_size > rman_get_size(sc->mem_res)) { - device_printf(dev, "impossible memory region size %ld\n", + device_printf(dev, "impossible memory region size %jd\n", rman_get_size(sc->mem_res)); err = ENXIO; goto abort_with_mem_res; diff --git a/sys/dev/ncr/ncr.c b/sys/dev/ncr/ncr.c index 6c941e1..111cd69 100644 --- a/sys/dev/ncr/ncr.c +++ b/sys/dev/ncr/ncr.c @@ -3860,6 +3860,16 @@ ncr_action (struct cam_sim *sim, union ccb *ccb) csio = &ccb->csio; /* + * Make sure we support this request. We can't do + * PHYS pointers. + */ + if (ccb->ccb_h.flags & CAM_CDB_PHYS) { + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + return; + } + + /* * Last time we need to check if this CCB needs to * be aborted. */ @@ -4070,8 +4080,7 @@ ncr_action (struct cam_sim *sim, union ccb *ccb) /* ** command */ - /* XXX JGibbs - Support other command types */ - cp->phys.cmd.addr = vtophys (csio->cdb_io.cdb_bytes); + cp->phys.cmd.addr = vtophys (scsiio_cdb_ptr(csio)); cp->phys.cmd.size = csio->cdb_len; /* ** sense command @@ -4083,7 +4092,6 @@ ncr_action (struct cam_sim *sim, union ccb *ccb) */ cp->sensecmd[0] = 0x03; cp->sensecmd[1] = ccb->ccb_h.target_lun << 5; - cp->sensecmd[4] = sizeof(struct scsi_sense_data); cp->sensecmd[4] = csio->sense_len; /* ** sense data diff --git a/sys/dev/nctgpio/nctgpio.c b/sys/dev/nctgpio/nctgpio.c new file mode 100644 index 0000000..ab547c0 --- /dev/null +++ b/sys/dev/nctgpio/nctgpio.c @@ -0,0 +1,802 @@ +/*- + * Copyright (c) 2016 Daniel Wyatt <Daniel.Wyatt@gmail.com> + * 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. + * + * $FreeBSD$ + * + */ + +/* + * Nuvoton GPIO driver. + * + */ + +#include <sys/cdefs.h> + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/eventhandler.h> +#include <sys/lock.h> + +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/gpio.h> + +#include <isa/isavar.h> + +#include <machine/bus.h> +#include <machine/resource.h> + +#include <dev/gpio/gpiobusvar.h> + +#include "gpio_if.h" + +/* + * Global configuration registers (CR). + */ +#define NCT_CR_LDN 0x07 /* Logical Device Number */ +#define NCT_CR_CHIP_ID 0x20 /* Chip ID */ +#define NCT_CR_CHIP_ID_H 0x20 /* Chip ID (high byte) */ +#define NCT_CR_CHIP_ID_L 0x21 /* Chip ID (low byte) */ +#define NCT_CR_OPT_1 0x26 /* Global Options (1) */ + +/* Logical Device Numbers. */ +#define NCT_LDN_GPIO 0x07 +#define NCT_LDN_GPIO_CFG 0x08 +#define NCT_LDN_GPIO_MODE 0x0f + +/* Logical Device 7 */ +#define NCT_LD7_GPIO_ENABLE 0x30 +#define NCT_LD7_GPIO0_IOR 0xe0 +#define NCT_LD7_GPIO0_DAT 0xe1 +#define NCT_LD7_GPIO0_INV 0xe2 +#define NCT_LD7_GPIO0_DST 0xe3 +#define NCT_LD7_GPIO1_IOR 0xe4 +#define NCT_LD7_GPIO1_DAT 0xe5 +#define NCT_LD7_GPIO1_INV 0xe6 +#define NCT_LD7_GPIO1_DST 0xe7 + +/* Logical Device F */ +#define NCT_LDF_GPIO0_OUTCFG 0xe0 +#define NCT_LDF_GPIO1_OUTCFG 0xe1 + +#define NCT_EXTFUNC_ENTER 0x87 +#define NCT_EXTFUNC_EXIT 0xaa + +#define NCT_MAX_PIN 15 +#define NCT_IS_VALID_PIN(_p) ((_p) >= 0 && (_p) <= NCT_MAX_PIN) + +#define NCT_PIN_BIT(_p) (1 << ((_p) % 8)) + +#define NCT_GPIO_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ + GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \ + GPIO_PIN_INVIN | GPIO_PIN_INVOUT) + +struct nct_softc { + device_t dev; + device_t busdev; + struct mtx mtx; + struct resource *portres; + int rid; + struct gpio_pin pins[NCT_MAX_PIN]; +}; + +#define GPIO_LOCK_INIT(_sc) mtx_init(&(_sc)->mtx, \ + device_get_nameunit(dev), NULL, MTX_DEF) +#define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->mtx) +#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->mtx) +#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) +#define GPIO_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED) +#define GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED) + +#define NCT_BARRIER_WRITE(_sc) \ + bus_barrier((_sc)->portres, 0, 2, BUS_SPACE_BARRIER_WRITE) + +#define NCT_BARRIER_READ_WRITE(_sc) \ + bus_barrier((_sc)->portres, 0, 2, \ + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE) + +static void ext_cfg_enter(struct nct_softc *); +static void ext_cfg_exit(struct nct_softc *); + +/* + * Potential Extended Function Enable Register addresses. + * Same address as EFIR. + */ +uint8_t probe_addrs[] = {0x2e, 0x4e}; + +struct nuvoton_vendor_device_id { + uint16_t chip_id; + const char * descr; +} nct_devs[] = { + { + .chip_id = 0x1061, + .descr = "Nuvoton NCT5104D", + }, + { + .chip_id = 0xc452, + .descr = "Nuvoton NCT5104D (PC-Engines APU)", + }, +}; + +static void +write_cfg_reg_1(struct nct_softc *sc, uint8_t reg, uint8_t value) +{ + GPIO_ASSERT_LOCKED(sc); + bus_write_1(sc->portres, 0, reg); + NCT_BARRIER_WRITE(sc); + bus_write_1(sc->portres, 1, value); + NCT_BARRIER_WRITE(sc); +} + +static uint8_t +read_cfg_reg_1(struct nct_softc *sc, uint8_t reg) +{ + uint8_t value; + + GPIO_ASSERT_LOCKED(sc); + bus_write_1(sc->portres, 0, reg); + NCT_BARRIER_READ_WRITE(sc); + value = bus_read_1(sc->portres, 1); + NCT_BARRIER_READ_WRITE(sc); + + return (value); +} + +static uint16_t +read_cfg_reg_2(struct nct_softc *sc, uint8_t reg) +{ + uint16_t value; + + value = read_cfg_reg_1(sc, reg) << 8; + value |= read_cfg_reg_1(sc, reg + 1); + + return (value); +} + +/* + * Enable extended function mode. + * + */ +static void +ext_cfg_enter(struct nct_softc *sc) +{ + GPIO_ASSERT_LOCKED(sc); + bus_write_1(sc->portres, 0, NCT_EXTFUNC_ENTER); + NCT_BARRIER_WRITE(sc); + bus_write_1(sc->portres, 0, NCT_EXTFUNC_ENTER); + NCT_BARRIER_WRITE(sc); +} + +/* + * Disable extended function mode. + * + */ +static void +ext_cfg_exit(struct nct_softc *sc) +{ + GPIO_ASSERT_LOCKED(sc); + bus_write_1(sc->portres, 0, NCT_EXTFUNC_EXIT); + NCT_BARRIER_WRITE(sc); +} + +/* + * Select a Logical Device. + */ +static void +select_ldn(struct nct_softc *sc, uint8_t ldn) +{ + write_cfg_reg_1(sc, NCT_CR_LDN, ldn); +} + +/* + * Get the GPIO Input/Output register address + * for a pin. + */ +static uint8_t +nct_ior_addr(uint32_t pin_num) +{ + uint8_t addr; + + addr = NCT_LD7_GPIO0_IOR; + if (pin_num > 7) + addr = NCT_LD7_GPIO1_IOR; + + return (addr); +} + +/* + * Get the GPIO Data register address for a pin. + */ +static uint8_t +nct_dat_addr(uint32_t pin_num) +{ + uint8_t addr; + + addr = NCT_LD7_GPIO0_DAT; + if (pin_num > 7) + addr = NCT_LD7_GPIO1_DAT; + + return (addr); +} + +/* + * Get the GPIO Inversion register address + * for a pin. + */ +static uint8_t +nct_inv_addr(uint32_t pin_num) +{ + uint8_t addr; + + addr = NCT_LD7_GPIO0_INV; + if (pin_num > 7) + addr = NCT_LD7_GPIO1_INV; + + return (addr); +} + +/* + * Get the GPIO Output Configuration/Mode + * register address for a pin. + */ +static uint8_t +nct_outcfg_addr(uint32_t pin_num) +{ + uint8_t addr; + + addr = NCT_LDF_GPIO0_OUTCFG; + if (pin_num > 7) + addr = NCT_LDF_GPIO1_OUTCFG; + + return (addr); +} + +/* + * Set a pin to output mode. + */ +static void +nct_set_pin_is_output(struct nct_softc *sc, uint32_t pin_num) +{ + uint8_t reg; + uint8_t ior; + + reg = nct_ior_addr(pin_num); + select_ldn(sc, NCT_LDN_GPIO); + ior = read_cfg_reg_1(sc, reg); + ior &= ~(NCT_PIN_BIT(pin_num)); + write_cfg_reg_1(sc, reg, ior); +} + +/* + * Set a pin to input mode. + */ +static void +nct_set_pin_is_input(struct nct_softc *sc, uint32_t pin_num) +{ + uint8_t reg; + uint8_t ior; + + reg = nct_ior_addr(pin_num); + select_ldn(sc, NCT_LDN_GPIO); + ior = read_cfg_reg_1(sc, reg); + ior |= NCT_PIN_BIT(pin_num); + write_cfg_reg_1(sc, reg, ior); +} + +/* + * Check whether a pin is configured as an input. + */ +static bool +nct_pin_is_input(struct nct_softc *sc, uint32_t pin_num) +{ + uint8_t reg; + uint8_t ior; + + reg = nct_ior_addr(pin_num); + select_ldn(sc, NCT_LDN_GPIO); + ior = read_cfg_reg_1(sc, reg); + + return (ior & NCT_PIN_BIT(pin_num)); +} + +/* + * Write a value to an output pin. + */ +static void +nct_write_pin(struct nct_softc *sc, uint32_t pin_num, uint8_t data) +{ + uint8_t reg; + uint8_t value; + + reg = nct_dat_addr(pin_num); + select_ldn(sc, NCT_LDN_GPIO); + value = read_cfg_reg_1(sc, reg); + if (data) + value |= NCT_PIN_BIT(pin_num); + else + value &= ~(NCT_PIN_BIT(pin_num)); + + write_cfg_reg_1(sc, reg, value); +} + +static bool +nct_read_pin(struct nct_softc *sc, uint32_t pin_num) +{ + uint8_t reg; + + reg = nct_dat_addr(pin_num); + select_ldn(sc, NCT_LDN_GPIO); + + return (read_cfg_reg_1(sc, reg) & NCT_PIN_BIT(pin_num)); +} + +static void +nct_set_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num) +{ + uint8_t reg; + uint8_t inv; + + reg = nct_inv_addr(pin_num); + select_ldn(sc, NCT_LDN_GPIO); + inv = read_cfg_reg_1(sc, reg); + inv |= (NCT_PIN_BIT(pin_num)); + write_cfg_reg_1(sc, reg, inv); +} + +static void +nct_set_pin_not_inverted(struct nct_softc *sc, uint32_t pin_num) +{ + uint8_t reg; + uint8_t inv; + + reg = nct_inv_addr(pin_num); + select_ldn(sc, NCT_LDN_GPIO); + inv = read_cfg_reg_1(sc, reg); + inv &= ~(NCT_PIN_BIT(pin_num)); + write_cfg_reg_1(sc, reg, inv); +} + +static bool +nct_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num) +{ + uint8_t reg; + uint8_t inv; + + reg = nct_inv_addr(pin_num); + select_ldn(sc, NCT_LDN_GPIO); + inv = read_cfg_reg_1(sc, reg); + + return (inv & NCT_PIN_BIT(pin_num)); +} + +static void +nct_set_pin_opendrain(struct nct_softc *sc, uint32_t pin_num) +{ + uint8_t reg; + uint8_t outcfg; + + reg = nct_outcfg_addr(pin_num); + select_ldn(sc, NCT_LDN_GPIO_MODE); + outcfg = read_cfg_reg_1(sc, reg); + outcfg |= (NCT_PIN_BIT(pin_num)); + write_cfg_reg_1(sc, reg, outcfg); +} + +static void +nct_set_pin_pushpull(struct nct_softc *sc, uint32_t pin_num) +{ + uint8_t reg; + uint8_t outcfg; + + reg = nct_outcfg_addr(pin_num); + select_ldn(sc, NCT_LDN_GPIO_MODE); + outcfg = read_cfg_reg_1(sc, reg); + outcfg &= ~(NCT_PIN_BIT(pin_num)); + write_cfg_reg_1(sc, reg, outcfg); +} + +static bool +nct_pin_is_opendrain(struct nct_softc *sc, uint32_t pin_num) +{ + uint8_t reg; + uint8_t outcfg; + + reg = nct_outcfg_addr(pin_num); + select_ldn(sc, NCT_LDN_GPIO_MODE); + outcfg = read_cfg_reg_1(sc, reg); + + return (outcfg & NCT_PIN_BIT(pin_num)); +} + +static void +nct_identify(driver_t *driver, device_t parent) +{ + if (device_find_child(parent, driver->name, 0) != NULL) + return; + + BUS_ADD_CHILD(parent, 0, driver->name, 0); +} + +static int +nct_probe(device_t dev) +{ + int i, j; + int rc; + struct nct_softc *sc; + uint16_t chipid; + + /* Make sure we do not claim some ISA PNP device. */ + if (isa_get_logicalid(dev) != 0) + return (ENXIO); + + sc = device_get_softc(dev); + + for (i = 0; i < sizeof(probe_addrs) / sizeof(*probe_addrs); i++) { + sc->rid = 0; + sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid, + probe_addrs[i], probe_addrs[i] + 1, 2, RF_ACTIVE); + if (sc->portres == NULL) + continue; + + GPIO_LOCK_INIT(sc); + + GPIO_ASSERT_UNLOCKED(sc); + GPIO_LOCK(sc); + ext_cfg_enter(sc); + chipid = read_cfg_reg_2(sc, NCT_CR_CHIP_ID); + ext_cfg_exit(sc); + GPIO_UNLOCK(sc); + + GPIO_LOCK_DESTROY(sc); + + bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres); + bus_delete_resource(dev, SYS_RES_IOPORT, sc->rid); + + for (j = 0; j < sizeof(nct_devs) / sizeof(*nct_devs); j++) { + if (chipid == nct_devs[j].chip_id) { + rc = bus_set_resource(dev, SYS_RES_IOPORT, 0, probe_addrs[i], 2); + if (rc != 0) { + device_printf(dev, "bus_set_resource failed for address 0x%02X\n", probe_addrs[i]); + continue; + } + device_set_desc(dev, nct_devs[j].descr); + return (BUS_PROBE_DEFAULT); + } + } + } + return (ENXIO); +} + +static int +nct_attach(device_t dev) +{ + struct nct_softc *sc; + int i; + + sc = device_get_softc(dev); + + sc->rid = 0; + sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid, + 0ul, ~0ul, 2, RF_ACTIVE); + if (sc->portres == NULL) { + device_printf(dev, "cannot allocate ioport\n"); + return (ENXIO); + } + + GPIO_LOCK_INIT(sc); + + GPIO_ASSERT_UNLOCKED(sc); + GPIO_LOCK(sc); + ext_cfg_enter(sc); + select_ldn(sc, NCT_LDN_GPIO); + /* Enable gpio0 and gpio1. */ + write_cfg_reg_1(sc, NCT_LD7_GPIO_ENABLE, + read_cfg_reg_1(sc, NCT_LD7_GPIO_ENABLE) | 0x03); + + for (i = 0; i <= NCT_MAX_PIN; i++) { + struct gpio_pin *pin; + + pin = &sc->pins[i]; + pin->gp_pin = i; + pin->gp_caps = NCT_GPIO_CAPS; + pin->gp_flags = 0; + + snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%02u", i); + pin->gp_name[GPIOMAXNAME - 1] = '\0'; + + if (nct_pin_is_input(sc, i)) + pin->gp_flags |= GPIO_PIN_INPUT; + else + pin->gp_flags |= GPIO_PIN_OUTPUT; + + if (nct_pin_is_opendrain(sc, i)) + pin->gp_flags |= GPIO_PIN_OPENDRAIN; + else + pin->gp_flags |= GPIO_PIN_PUSHPULL; + + if (nct_pin_is_inverted(sc, i)) + pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT); + } + GPIO_UNLOCK(sc); + + sc->busdev = gpiobus_attach_bus(dev); + if (sc->busdev == NULL) { + GPIO_ASSERT_UNLOCKED(sc); + GPIO_LOCK(sc); + ext_cfg_exit(sc); + GPIO_UNLOCK(sc); + bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres); + GPIO_LOCK_DESTROY(sc); + + return (ENXIO); + } + + return (0); +} + +static int +nct_detach(device_t dev) +{ + struct nct_softc *sc; + + sc = device_get_softc(dev); + gpiobus_detach_bus(dev); + + GPIO_ASSERT_UNLOCKED(sc); + GPIO_LOCK(sc); + ext_cfg_exit(sc); + GPIO_UNLOCK(sc); + + /* Cleanup resources. */ + bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres); + + GPIO_LOCK_DESTROY(sc); + + return (0); +} + +static device_t +nct_gpio_get_bus(device_t dev) +{ + struct nct_softc *sc; + + sc = device_get_softc(dev); + + return (sc->busdev); +} + +static int +nct_gpio_pin_max(device_t dev, int *npins) +{ + *npins = NCT_MAX_PIN; + + return (0); +} + +static int +nct_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value) +{ + struct nct_softc *sc; + + if (!NCT_IS_VALID_PIN(pin_num)) + return (EINVAL); + + sc = device_get_softc(dev); + GPIO_ASSERT_UNLOCKED(sc); + GPIO_LOCK(sc); + nct_write_pin(sc, pin_num, pin_value); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +nct_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value) +{ + struct nct_softc *sc; + + if (!NCT_IS_VALID_PIN(pin_num)) + return (EINVAL); + + sc = device_get_softc(dev); + GPIO_ASSERT_UNLOCKED(sc); + GPIO_LOCK(sc); + *pin_value = nct_read_pin(sc, pin_num); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +nct_gpio_pin_toggle(device_t dev, uint32_t pin_num) +{ + struct nct_softc *sc; + + if (!NCT_IS_VALID_PIN(pin_num)) + return (EINVAL); + + sc = device_get_softc(dev); + GPIO_ASSERT_UNLOCKED(sc); + GPIO_LOCK(sc); + if (nct_read_pin(sc, pin_num)) + nct_write_pin(sc, pin_num, 0); + else + nct_write_pin(sc, pin_num, 1); + + GPIO_UNLOCK(sc); + + return (0); +} + +static int +nct_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *caps) +{ + struct nct_softc *sc; + + if (!NCT_IS_VALID_PIN(pin_num)) + return (EINVAL); + + sc = device_get_softc(dev); + GPIO_ASSERT_UNLOCKED(sc); + GPIO_LOCK(sc); + *caps = sc->pins[pin_num].gp_caps; + GPIO_UNLOCK(sc); + + return (0); +} + +static int +nct_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *flags) +{ + struct nct_softc *sc; + + if (!NCT_IS_VALID_PIN(pin_num)) + return (EINVAL); + + sc = device_get_softc(dev); + GPIO_ASSERT_UNLOCKED(sc); + GPIO_LOCK(sc); + *flags = sc->pins[pin_num].gp_flags; + GPIO_UNLOCK(sc); + + return (0); +} + +static int +nct_gpio_pin_getname(device_t dev, uint32_t pin_num, char *name) +{ + struct nct_softc *sc; + + if (!NCT_IS_VALID_PIN(pin_num)) + return (EINVAL); + + sc = device_get_softc(dev); + GPIO_ASSERT_UNLOCKED(sc); + GPIO_LOCK(sc); + memcpy(name, sc->pins[pin_num].gp_name, GPIOMAXNAME); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +nct_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags) +{ + struct nct_softc *sc; + struct gpio_pin *pin; + + if (!NCT_IS_VALID_PIN(pin_num)) + return (EINVAL); + + sc = device_get_softc(dev); + pin = &sc->pins[pin_num]; + if ((flags & pin->gp_caps) != flags) + return (EINVAL); + + GPIO_ASSERT_UNLOCKED(sc); + GPIO_LOCK(sc); + if (flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) { + if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == + (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) { + GPIO_UNLOCK(sc); + return (EINVAL); + } + + if (flags & GPIO_PIN_INPUT) + nct_set_pin_is_input(sc, pin_num); + else + nct_set_pin_is_output(sc, pin_num); + } + + if (flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) { + if (flags & GPIO_PIN_INPUT) { + GPIO_UNLOCK(sc); + return (EINVAL); + } + + if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) == + (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) { + GPIO_UNLOCK(sc); + return (EINVAL); + } + + if (flags & GPIO_PIN_OPENDRAIN) + nct_set_pin_opendrain(sc, pin_num); + else + nct_set_pin_pushpull(sc, pin_num); + } + + if (flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) { + if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) != + (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) { + GPIO_UNLOCK(sc); + return (EINVAL); + } + + if (flags & GPIO_PIN_INVIN) + nct_set_pin_is_inverted(sc, pin_num); + else + nct_set_pin_not_inverted(sc, pin_num); + } + + pin->gp_flags = flags; + GPIO_UNLOCK(sc); + + return (0); +} + +static device_method_t nct_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, nct_identify), + DEVMETHOD(device_probe, nct_probe), + DEVMETHOD(device_attach, nct_attach), + DEVMETHOD(device_detach, nct_detach), + + /* GPIO */ + DEVMETHOD(gpio_get_bus, nct_gpio_get_bus), + DEVMETHOD(gpio_pin_max, nct_gpio_pin_max), + DEVMETHOD(gpio_pin_get, nct_gpio_pin_get), + DEVMETHOD(gpio_pin_set, nct_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, nct_gpio_pin_toggle), + DEVMETHOD(gpio_pin_getname, nct_gpio_pin_getname), + DEVMETHOD(gpio_pin_getcaps, nct_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_getflags, nct_gpio_pin_getflags), + DEVMETHOD(gpio_pin_setflags, nct_gpio_pin_setflags), + + DEVMETHOD_END +}; + +static driver_t nct_isa_driver = { + "gpio", + nct_methods, + sizeof(struct nct_softc) +}; + +static devclass_t nct_devclass; + +DRIVER_MODULE(nctgpio, isa, nct_isa_driver, nct_devclass, NULL, NULL); +MODULE_DEPEND(nctgpio, gpiobus, 1, 1, 1); diff --git a/sys/dev/netmap/netmap_generic.c b/sys/dev/netmap/netmap_generic.c index bc5b452..9129960 100644 --- a/sys/dev/netmap/netmap_generic.c +++ b/sys/dev/netmap/netmap_generic.c @@ -129,8 +129,9 @@ static inline struct mbuf * netmap_get_mbuf(int len) { struct mbuf *m; - m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR | M_NOFREE); + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m) { + m->m_flags |= M_NOFREE; /* XXXNP: Almost certainly incorrect. */ m->m_ext.ext_arg1 = m->m_ext.ext_buf; // XXX save m->m_ext.ext_free = (void *)netmap_default_mbuf_destructor; m->m_ext.ext_type = EXT_EXTREF; diff --git a/sys/dev/oce/oce_if.c b/sys/dev/oce/oce_if.c index f0cce5f..3704612 100644 --- a/sys/dev/oce/oce_if.c +++ b/sys/dev/oce/oce_if.c @@ -1497,16 +1497,12 @@ static void oce_rx_flush_lro(struct oce_rq *rq) { struct lro_ctrl *lro = &rq->lro; - struct lro_entry *queued; POCE_SOFTC sc = (POCE_SOFTC) rq->parent; if (!IF_LRO_ENABLED(sc)) return; - while ((queued = SLIST_FIRST(&lro->lro_active)) != NULL) { - SLIST_REMOVE_HEAD(&lro->lro_active, next); - tcp_lro_flush(lro, queued); - } + tcp_lro_flush_all(lro); rq->lro_pkts_queued = 0; return; diff --git a/sys/dev/ofw/ofw_iicbus.c b/sys/dev/ofw/ofw_iicbus.c index c0fa054..73cad7c 100644 --- a/sys/dev/ofw/ofw_iicbus.c +++ b/sys/dev/ofw/ofw_iicbus.c @@ -80,8 +80,10 @@ static devclass_t ofwiicbus_devclass; DEFINE_CLASS_1(iicbus, ofw_iicbus_driver, ofw_iicbus_methods, sizeof(struct iicbus_softc), iicbus_driver); -DRIVER_MODULE(ofw_iicbus, iicbb, ofw_iicbus_driver, ofwiicbus_devclass, 0, 0); -DRIVER_MODULE(ofw_iicbus, iichb, ofw_iicbus_driver, ofwiicbus_devclass, 0, 0); +EARLY_DRIVER_MODULE(ofw_iicbus, iicbb, ofw_iicbus_driver, ofwiicbus_devclass, + 0, 0, BUS_PASS_BUS); +EARLY_DRIVER_MODULE(ofw_iicbus, iichb, ofw_iicbus_driver, ofwiicbus_devclass, + 0, 0, BUS_PASS_BUS); MODULE_VERSION(ofw_iicbus, 1); MODULE_DEPEND(ofw_iicbus, iicbus, 1, 1, 1); diff --git a/sys/dev/ofw/ofwbus.c b/sys/dev/ofw/ofwbus.c index 8eb5dd5..fc43410 100644 --- a/sys/dev/ofw/ofwbus.c +++ b/sys/dev/ofw/ofwbus.c @@ -200,8 +200,8 @@ ofwbus_alloc_resource(device_t bus, device_t child, int type, int *rid, return (NULL); } start = rle->start; - count = ulmax(count, rle->count); - end = ulmax(rle->end, start + count - 1); + count = ummax(count, rle->count); + end = ummax(rle->end, start + count - 1); } switch (type) { diff --git a/sys/dev/ofw/ofwpci.c b/sys/dev/ofw/ofwpci.c new file mode 100644 index 0000000..2872fe1 --- /dev/null +++ b/sys/dev/ofw/ofwpci.c @@ -0,0 +1,628 @@ +/*- + * Copyright (c) 2011 Nathan Whitehorn + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/rman.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_pci.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofwpci.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> + +#include <machine/bus.h> +#include <machine/md_var.h> +#include <machine/resource.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include "pcib_if.h" + +/* + * If it is necessary to set another value of this for + * some platforms it should be set at fdt.h file + */ +#ifndef PCI_MAP_INTR +#define PCI_MAP_INTR 4 +#endif + +#define PCI_INTR_PINS 4 + +/* + * bus interface. + */ +static struct resource * ofw_pci_alloc_resource(device_t, device_t, + int, int *, rman_res_t, rman_res_t, rman_res_t, u_int); +static int ofw_pci_release_resource(device_t, device_t, int, int, + struct resource *); +static int ofw_pci_activate_resource(device_t, device_t, int, int, + struct resource *); +static int ofw_pci_deactivate_resource(device_t, device_t, int, int, + struct resource *); +static int ofw_pci_adjust_resource(device_t, device_t, int, + struct resource *, rman_res_t, rman_res_t); + +#ifdef __powerpc__ +static bus_space_tag_t ofw_pci_bus_get_bus_tag(device_t, device_t); +#endif + +/* + * pcib interface + */ +static int ofw_pci_maxslots(device_t); + +/* + * ofw_bus interface + */ +static phandle_t ofw_pci_get_node(device_t, device_t); + +/* + * local methods + */ +static int ofw_pci_fill_ranges(phandle_t, struct ofw_pci_range *); + +/* + * Driver methods. + */ +static device_method_t ofw_pci_methods[] = { + + /* Device interface */ + DEVMETHOD(device_attach, ofw_pci_attach), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_read_ivar, ofw_pci_read_ivar), + DEVMETHOD(bus_write_ivar, ofw_pci_write_ivar), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + DEVMETHOD(bus_alloc_resource, ofw_pci_alloc_resource), + DEVMETHOD(bus_release_resource, ofw_pci_release_resource), + DEVMETHOD(bus_activate_resource, ofw_pci_activate_resource), + DEVMETHOD(bus_deactivate_resource, ofw_pci_deactivate_resource), + DEVMETHOD(bus_adjust_resource, ofw_pci_adjust_resource), +#ifdef __powerpc__ + DEVMETHOD(bus_get_bus_tag, ofw_pci_bus_get_bus_tag), +#endif + + /* pcib interface */ + DEVMETHOD(pcib_maxslots, ofw_pci_maxslots), + DEVMETHOD(pcib_route_interrupt, ofw_pci_route_interrupt), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, ofw_pci_get_node), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(ofw_pci, ofw_pci_driver, ofw_pci_methods, 0); + +int +ofw_pci_init(device_t dev) +{ + struct ofw_pci_softc *sc; + phandle_t node; + u_int32_t busrange[2]; + struct ofw_pci_range *rp; + int error; + struct ofw_pci_cell_info *cell_info; + + node = ofw_bus_get_node(dev); + sc = device_get_softc(dev); + sc->sc_initialized = 1; + sc->sc_range = NULL; + + cell_info = (struct ofw_pci_cell_info *)malloc(sizeof(*cell_info), + M_DEVBUF, M_WAITOK | M_ZERO); + + sc->sc_cell_info = cell_info; + + if (OF_getencprop(node, "bus-range", busrange, sizeof(busrange)) != 8) + busrange[0] = 0; + + sc->sc_dev = dev; + sc->sc_node = node; + sc->sc_bus = busrange[0]; + + if (sc->sc_quirks & OFW_PCI_QUIRK_RANGES_ON_CHILDREN) { + phandle_t c; + int n, i; + + sc->sc_nrange = 0; + for (c = OF_child(node); c != 0; c = OF_peer(c)) { + n = ofw_pci_nranges(c, cell_info); + if (n > 0) + sc->sc_nrange += n; + } + if (sc->sc_nrange == 0) { + error = ENXIO; + goto out; + } + sc->sc_range = malloc(sc->sc_nrange * sizeof(sc->sc_range[0]), + M_DEVBUF, M_WAITOK); + i = 0; + for (c = OF_child(node); c != 0; c = OF_peer(c)) { + n = ofw_pci_fill_ranges(c, &sc->sc_range[i]); + if (n > 0) + i += n; + } + KASSERT(i == sc->sc_nrange, ("range count mismatch")); + } else { + sc->sc_nrange = ofw_pci_nranges(node, cell_info); + if (sc->sc_nrange <= 0) { + device_printf(dev, "could not getranges\n"); + error = ENXIO; + goto out; + } + sc->sc_range = malloc(sc->sc_nrange * sizeof(sc->sc_range[0]), + M_DEVBUF, M_WAITOK); + ofw_pci_fill_ranges(node, sc->sc_range); + } + + sc->sc_io_rman.rm_type = RMAN_ARRAY; + sc->sc_io_rman.rm_descr = "PCI I/O Ports"; + error = rman_init(&sc->sc_io_rman); + if (error) { + device_printf(dev, "rman_init() failed. error = %d\n", error); + goto out; + } + + sc->sc_mem_rman.rm_type = RMAN_ARRAY; + sc->sc_mem_rman.rm_descr = "PCI Memory"; + error = rman_init(&sc->sc_mem_rman); + if (error) { + device_printf(dev, "rman_init() failed. error = %d\n", error); + goto out; + } + + for (rp = sc->sc_range; rp < sc->sc_range + sc->sc_nrange && + rp->pci_hi != 0; rp++) { + error = 0; + + switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { + case OFW_PCI_PHYS_HI_SPACE_CONFIG: + break; + case OFW_PCI_PHYS_HI_SPACE_IO: + error = rman_manage_region(&sc->sc_io_rman, rp->pci, + rp->pci + rp->size - 1); + break; + case OFW_PCI_PHYS_HI_SPACE_MEM32: + case OFW_PCI_PHYS_HI_SPACE_MEM64: + error = rman_manage_region(&sc->sc_mem_rman, rp->pci, + rp->pci + rp->size - 1); + break; + } + + if (error) { + device_printf(dev, + "rman_manage_region(%x, %#jx, %#jx) failed. " + "error = %d\n", rp->pci_hi & + OFW_PCI_PHYS_HI_SPACEMASK, rp->pci, + rp->pci + rp->size - 1, error); + goto out; + } + } + + ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(cell_t)); + return (0); + +out: + free(cell_info, M_DEVBUF); + free(sc->sc_range, M_DEVBUF); + rman_fini(&sc->sc_io_rman); + rman_fini(&sc->sc_mem_rman); + + return (error); +} + +int +ofw_pci_attach(device_t dev) +{ + struct ofw_pci_softc *sc; + int error; + + sc = device_get_softc(dev); + if (!sc->sc_initialized) { + error = ofw_pci_init(dev); + if (error) + return (error); + } + + device_add_child(dev, "pci", -1); + return (bus_generic_attach(dev)); +} + +static int +ofw_pci_maxslots(device_t dev) +{ + + return (PCI_SLOTMAX); +} + +int +ofw_pci_route_interrupt(device_t bus, device_t dev, int pin) +{ + struct ofw_pci_softc *sc; + struct ofw_pci_register reg; + uint32_t pintr, mintr[PCI_MAP_INTR]; + int intrcells; + phandle_t iparent; + + sc = device_get_softc(bus); + pintr = pin; + + /* Fabricate imap information in case this isn't an OFW device */ + bzero(®, sizeof(reg)); + reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) | + (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) | + (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT); + + intrcells = ofw_bus_lookup_imap(ofw_bus_get_node(dev), + &sc->sc_pci_iinfo, ®, sizeof(reg), &pintr, sizeof(pintr), + mintr, sizeof(mintr), &iparent); + if (intrcells != 0) { + pintr = ofw_bus_map_intr(dev, iparent, intrcells, mintr); + return (pintr); + } + + /* + * Maybe it's a real interrupt, not an intpin + */ + if (pin > PCI_INTR_PINS) + return (pin); + + device_printf(bus, "could not route pin %d for device %d.%d\n", + pin, pci_get_slot(dev), pci_get_function(dev)); + return (PCI_INVALID_IRQ); +} + +int +ofw_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +{ + struct ofw_pci_softc *sc; + + sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_DOMAIN: + *result = device_get_unit(dev); + return (0); + case PCIB_IVAR_BUS: + *result = sc->sc_bus; + return (0); + default: + break; + } + + return (ENOENT); +} + +int +ofw_pci_write_ivar(device_t dev, device_t child, int which, uintptr_t value) +{ + struct ofw_pci_softc *sc; + + sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_BUS: + sc->sc_bus = value; + return (0); + default: + break; + } + + return (ENOENT); +} + +int +ofw_pci_nranges(phandle_t node, struct ofw_pci_cell_info *info) +{ + ssize_t nbase_ranges; + + if (info == NULL) + return (-1); + + info->host_address_cells = 1; + info->size_cells = 2; + info->pci_address_cell = 3; + + OF_getencprop(OF_parent(node), "#address-cells", + &(info->host_address_cells), sizeof(info->host_address_cells)); + OF_getencprop(node, "#address-cells", + &(info->pci_address_cell), sizeof(info->pci_address_cell)); + OF_getencprop(node, "#size-cells", &(info->size_cells), + sizeof(info->size_cells)); + + nbase_ranges = OF_getproplen(node, "ranges"); + if (nbase_ranges <= 0) + return (-1); + + return (nbase_ranges / sizeof(cell_t) / + (info->pci_address_cell + info->host_address_cells + + info->size_cells)); +} + +static struct resource * +ofw_pci_alloc_resource(device_t bus, device_t child, int type, int *rid, + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) +{ + struct ofw_pci_softc *sc; + struct resource *rv; + struct rman *rm; + int needactivate; + + needactivate = flags & RF_ACTIVE; + flags &= ~RF_ACTIVE; + + sc = device_get_softc(bus); + + switch (type) { + case SYS_RES_MEMORY: + rm = &sc->sc_mem_rman; + break; + + case SYS_RES_IOPORT: + rm = &sc->sc_io_rman; + break; + + case SYS_RES_IRQ: + return (bus_alloc_resource(bus, type, rid, start, end, count, + flags)); + + default: + device_printf(bus, "unknown resource request from %s\n", + device_get_nameunit(child)); + return (NULL); + } + + rv = rman_reserve_resource(rm, start, end, count, flags, child); + if (rv == NULL) { + device_printf(bus, "failed to reserve resource for %s\n", + device_get_nameunit(child)); + return (NULL); + } + + rman_set_rid(rv, *rid); + + if (needactivate) { + if (bus_activate_resource(child, type, *rid, rv) != 0) { + device_printf(bus, + "failed to activate resource for %s\n", + device_get_nameunit(child)); + rman_release_resource(rv); + return (NULL); + } + } + + return (rv); +} + +static int +ofw_pci_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) +{ + + if (rman_get_flags(res) & RF_ACTIVE) { + int error = bus_deactivate_resource(child, type, rid, res); + if (error) + return error; + } + + return (rman_release_resource(res)); +} + +static int +ofw_pci_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) +{ + struct ofw_pci_softc *sc; + bus_space_handle_t handle; + bus_space_tag_t tag; + int rv; + + sc = device_get_softc(bus); + + if (type == SYS_RES_IRQ) { + return (bus_activate_resource(bus, type, rid, res)); + } + if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { + struct ofw_pci_range *rp; + vm_paddr_t start; + int space; + + start = (vm_paddr_t)rman_get_start(res); + + /* + * Map this through the ranges list + */ + for (rp = sc->sc_range; rp < sc->sc_range + sc->sc_nrange && + rp->pci_hi != 0; rp++) { + if (start < rp->pci || start >= rp->pci + rp->size) + continue; + + switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { + case OFW_PCI_PHYS_HI_SPACE_IO: + space = SYS_RES_IOPORT; + break; + case OFW_PCI_PHYS_HI_SPACE_MEM32: + case OFW_PCI_PHYS_HI_SPACE_MEM64: + space = SYS_RES_MEMORY; + break; + default: + space = -1; + } + + if (type == space) { + start += (rp->host - rp->pci); + break; + } + } + + if (bootverbose) + printf("ofw_pci mapdev: start %jx, len %jd\n", + (rman_res_t)start, rman_get_size(res)); + + tag = BUS_GET_BUS_TAG(child, child); + if (tag == NULL) + return (ENOMEM); + + rman_set_bustag(res, tag); + rv = bus_space_map(tag, start, + rman_get_size(res), 0, &handle); + if (rv != 0) + return (ENOMEM); + + rman_set_bushandle(res, handle); + rman_set_virtual(res, (void *)handle); /* XXX for powerpc only ? */ + } + + return (rman_activate_resource(res)); +} + +#ifdef __powerpc__ +static bus_space_tag_t +ofw_pci_bus_get_bus_tag(device_t bus, device_t child) +{ + + return (&bs_le_tag); +} +#endif + +static int +ofw_pci_deactivate_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) +{ + + /* + * If this is a memory resource, unmap it. + */ + if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { + u_int32_t psize; + + psize = rman_get_size(res); + pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize); + } + + return (rman_deactivate_resource(res)); +} + +static int +ofw_pci_adjust_resource(device_t bus, device_t child, int type, + struct resource *res, rman_res_t start, rman_res_t end) +{ + struct rman *rm = NULL; + struct ofw_pci_softc *sc = device_get_softc(bus); + + KASSERT(!(rman_get_flags(res) & RF_ACTIVE), + ("active resources cannot be adjusted")); + if (rman_get_flags(res) & RF_ACTIVE) + return (EINVAL); + + switch (type) { + case SYS_RES_MEMORY: + rm = &sc->sc_mem_rman; + break; + case SYS_RES_IOPORT: + rm = &sc->sc_io_rman; + break; + default: + return (ENXIO); + } + + if (!rman_is_region_manager(res, rm)) + return (EINVAL); + + return (rman_adjust_resource(res, start, end)); +} + +static phandle_t +ofw_pci_get_node(device_t bus, device_t dev) +{ + struct ofw_pci_softc *sc; + + sc = device_get_softc(bus); + /* We only have one child, the PCI bus, which needs our own node. */ + + return (sc->sc_node); +} + +static int +ofw_pci_fill_ranges(phandle_t node, struct ofw_pci_range *ranges) +{ + int host_address_cells = 1, pci_address_cells = 3, size_cells = 2; + cell_t *base_ranges; + ssize_t nbase_ranges; + int nranges; + int i, j, k; + + OF_getencprop(OF_parent(node), "#address-cells", &host_address_cells, + sizeof(host_address_cells)); + OF_getencprop(node, "#address-cells", &pci_address_cells, + sizeof(pci_address_cells)); + OF_getencprop(node, "#size-cells", &size_cells, sizeof(size_cells)); + + nbase_ranges = OF_getproplen(node, "ranges"); + if (nbase_ranges <= 0) + return (-1); + nranges = nbase_ranges / sizeof(cell_t) / + (pci_address_cells + host_address_cells + size_cells); + + base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); + OF_getencprop(node, "ranges", base_ranges, nbase_ranges); + + for (i = 0, j = 0; i < nranges; i++) { + ranges[i].pci_hi = base_ranges[j++]; + ranges[i].pci = 0; + for (k = 0; k < pci_address_cells - 1; k++) { + ranges[i].pci <<= 32; + ranges[i].pci |= base_ranges[j++]; + } + ranges[i].host = 0; + for (k = 0; k < host_address_cells; k++) { + ranges[i].host <<= 32; + ranges[i].host |= base_ranges[j++]; + } + ranges[i].size = 0; + for (k = 0; k < size_cells; k++) { + ranges[i].size <<= 32; + ranges[i].size |= base_ranges[j++]; + } + } + + free(base_ranges, M_DEVBUF); + return (nranges); +} diff --git a/sys/dev/ofw/ofwpci.h b/sys/dev/ofw/ofwpci.h new file mode 100644 index 0000000..e9825ad --- /dev/null +++ b/sys/dev/ofw/ofwpci.h @@ -0,0 +1,83 @@ +/*- + * Copyright (c) 2011 Nathan Whitehorn + * 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. + * + * $FreeBSD$ + */ + +#ifndef _DEV_OFW_OFWPCI_H_ +#define _DEV_OFW_OFWPCI_H_ + +/* + * Export class definition for inheritance purposes + */ +DECLARE_CLASS(ofw_pci_driver); + +struct ofw_pci_cell_info { + pcell_t host_address_cells; + pcell_t pci_address_cell; + pcell_t size_cells; + }; + +struct ofw_pci_range { + uint32_t pci_hi; + uint64_t pci; + uint64_t host; + uint64_t size; +}; + +/* + * Quirks for some adapters + */ +enum { + OFW_PCI_QUIRK_RANGES_ON_CHILDREN = 1, +}; + +struct ofw_pci_softc { + device_t sc_dev; + phandle_t sc_node; + int sc_bus; + int sc_initialized; + int sc_quirks; + + struct ofw_pci_range *sc_range; + int sc_nrange; + struct ofw_pci_cell_info *sc_cell_info; + + struct rman sc_io_rman; + struct rman sc_mem_rman; + bus_space_tag_t sc_memt; + bus_dma_tag_t sc_dmat; + + struct ofw_bus_iinfo sc_pci_iinfo; +}; + +int ofw_pci_init(device_t); +int ofw_pci_attach(device_t); +int ofw_pci_read_ivar(device_t, device_t, int, uintptr_t *); +int ofw_pci_write_ivar(device_t, device_t, int, uintptr_t); +int ofw_pci_route_interrupt(device_t, device_t, int); +int ofw_pci_nranges(phandle_t, struct ofw_pci_cell_info *); + +#endif /* _DEV_OFW_OFWPCI_H_ */ diff --git a/sys/dev/pccard/pccard.c b/sys/dev/pccard/pccard.c index 0681242..04dcc44 100644 --- a/sys/dev/pccard/pccard.c +++ b/sys/dev/pccard/pccard.c @@ -507,7 +507,7 @@ pccard_function_init(struct pccard_function *pf, int entry) end = start + ios->length - 1; else end = ~0; - DEVPRINTF((bus, "I/O rid %d start %#lx end %#lx\n", + DEVPRINTF((bus, "I/O rid %d start %#jx end %#jx\n", i, start, end)); rid = i; len = ios->length; @@ -531,7 +531,7 @@ pccard_function_init(struct pccard_function *pf, int entry) end = start + mems->length - 1; else end = ~0; - DEVPRINTF((bus, "Memory rid %d start %#lx end %#lx\ncardaddr %#lx hostaddr %#lx length %#lx\n", + DEVPRINTF((bus, "Memory rid %d start %#jx end %#jx\ncardaddr %#jx hostaddr %#jx length %#jx\n", i, start, end, mems->cardaddr, mems->hostaddr, mems->length)); rid = i; @@ -602,7 +602,7 @@ pccard_function_free(struct pccard_function *pf) device_printf(pf->sc->dev, "function_free: Resource still owned by " "child, oops. " - "(type=%d, rid=%d, addr=%#lx)\n", + "(type=%d, rid=%d, addr=%#jx)\n", rle->type, rle->rid, rman_get_start(rle->res)); BUS_RELEASE_RESOURCE(device_get_parent(pf->sc->dev), @@ -697,7 +697,7 @@ pccard_function_enable(struct pccard_function *pf) &pf->ccr_rid, PCCARD_MEM_PAGE_SIZE, RF_ACTIVE); if (!pf->ccr_res) goto bad; - DEVPRINTF((dev, "ccr_res == %#lx-%#lx, base=%#x\n", + DEVPRINTF((dev, "ccr_res == %#jx-%#jx, base=%#x\n", rman_get_start(pf->ccr_res), rman_get_end(pf->ccr_res), pf->ccr_base)); CARD_SET_RES_FLAGS(device_get_parent(dev), dev, SYS_RES_MEMORY, @@ -1025,26 +1025,6 @@ pccard_child_location_str(device_t bus, device_t child, char *buf, return (0); } -/* XXX Maybe this should be in subr_bus? */ -static void -pccard_safe_quote(char *dst, const char *src, size_t len) -{ - char *walker = dst, *ep = dst + len - 1; - - if (len == 0) - return; - while (src != NULL && walker < ep) - { - if (*src == '"') { - if (ep - walker < 2) - break; - *walker++ = '\\'; - } - *walker++ = *src++; - } - *walker = '\0'; -} - static int pccard_child_pnpinfo_str(device_t bus, device_t child, char *buf, size_t buflen) @@ -1054,8 +1034,8 @@ pccard_child_pnpinfo_str(device_t bus, device_t child, char *buf, struct pccard_softc *sc = PCCARD_SOFTC(bus); char cis0[128], cis1[128]; - pccard_safe_quote(cis0, sc->card.cis1_info[0], sizeof(cis0)); - pccard_safe_quote(cis1, sc->card.cis1_info[1], sizeof(cis1)); + devctl_safe_quote(cis0, sc->card.cis1_info[0], sizeof(cis0)); + devctl_safe_quote(cis1, sc->card.cis1_info[1], sizeof(cis1)); snprintf(buf, buflen, "manufacturer=0x%04x product=0x%04x " "cisvendor=\"%s\" cisproduct=\"%s\" function_type=%d", sc->card.manufacturer, sc->card.product, cis0, cis1, pf->function); @@ -1197,7 +1177,7 @@ pccard_release_resource(device_t dev, device_t child, int type, int rid, if (!rle) { device_printf(dev, "Allocated resource not found, " - "%d %#x %#lx %#lx\n", + "%d %#x %#jx %#jx\n", type, rid, rman_get_start(r), rman_get_size(r)); return ENOENT; } diff --git a/sys/dev/pccard/pccard_cis.c b/sys/dev/pccard/pccard_cis.c index 4bd06b5..4bd5e77 100644 --- a/sys/dev/pccard/pccard_cis.c +++ b/sys/dev/pccard/pccard_cis.c @@ -151,7 +151,7 @@ pccard_scan_cis(device_t bus, device_t dev, pccard_scan_t fct, void *arg) tuple.memh = rman_get_bushandle(res); tuple.ptr = 0; - DPRINTF(("cis mem map %#x (resource: %#lx)\n", + DPRINTF(("cis mem map %#x (resource: %#jx)\n", (unsigned int) tuple.memh, rman_get_start(res))); tuple.mult = 2; @@ -576,9 +576,9 @@ pccard_print_cis(device_t dev) printf("; iomask %#lx, iospace", cfe->iomask); for (i = 0; i < cfe->num_iospace; i++) { - printf(" %#lx", cfe->iospace[i].start); + printf(" %#jx", cfe->iospace[i].start); if (cfe->iospace[i].length) - printf("-%#lx", + printf("-%#jx", cfe->iospace[i].start + cfe->iospace[i].length - 1); } @@ -587,14 +587,14 @@ pccard_print_cis(device_t dev) printf("; memspace"); for (i = 0; i < cfe->num_memspace; i++) { - printf(" %#lx", + printf(" %#jx", cfe->memspace[i].cardaddr); if (cfe->memspace[i].length) - printf("-%#lx", + printf("-%#jx", cfe->memspace[i].cardaddr + cfe->memspace[i].length - 1); if (cfe->memspace[i].hostaddr) - printf("@%#lx", + printf("@%#jx", cfe->memspace[i].hostaddr); } } diff --git a/sys/dev/pccbb/pccbb.c b/sys/dev/pccbb/pccbb.c index 9398771..067aa5a 100644 --- a/sys/dev/pccbb/pccbb.c +++ b/sys/dev/pccbb/pccbb.c @@ -229,7 +229,7 @@ cbb_destroy_res(struct cbb_softc *sc) while ((rle = SLIST_FIRST(&sc->rl)) != NULL) { device_printf(sc->dev, "Danger Will Robinson: Resource " "left allocated! This is a bug... " - "(rid=%x, type=%d, addr=%lx)\n", rle->rid, rle->type, + "(rid=%x, type=%d, addr=%jx)\n", rle->rid, rle->type, rman_get_start(rle->res)); SLIST_REMOVE_HEAD(&sc->rl, link); free(rle, M_DEVBUF); @@ -1241,8 +1241,8 @@ cbb_cardbus_alloc_resource(device_t brdev, device_t child, int type, case SYS_RES_IRQ: tmp = rman_get_start(sc->irq_res); if (start > tmp || end < tmp || count != 1) { - device_printf(child, "requested interrupt %ld-%ld," - "count = %ld not supported by cbb\n", + device_printf(child, "requested interrupt %jd-%jd," + "count = %jd not supported by cbb\n", start, end, count); return (NULL); } @@ -1425,8 +1425,8 @@ cbb_pcic_alloc_resource(device_t brdev, device_t child, int type, int *rid, case SYS_RES_IRQ: tmp = rman_get_start(sc->irq_res); if (start > tmp || end < tmp || count != 1) { - device_printf(child, "requested interrupt %ld-%ld," - "count = %ld not supported by cbb\n", + device_printf(child, "requested interrupt %jd-%jd," + "count = %jd not supported by cbb\n", start, end, count); return (NULL); } diff --git a/sys/dev/pccbb/pccbb_pci.c b/sys/dev/pccbb/pccbb_pci.c index e739027..4ec8d9b 100644 --- a/sys/dev/pccbb/pccbb_pci.c +++ b/sys/dev/pccbb/pccbb_pci.c @@ -313,7 +313,7 @@ cbb_pci_attach(device_t brdev) mtx_destroy(&sc->mtx); return (ENOMEM); } else { - DEVPRINTF((brdev, "Found memory at %08lx\n", + DEVPRINTF((brdev, "Found memory at %jx\n", rman_get_start(sc->base_res))); } diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 492efe0..8343181 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -1643,7 +1643,7 @@ pci_alloc_msix_method(device_t dev, device_t child, int *count) if (bootverbose) { rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, 1); if (actual == 1) - device_printf(child, "using IRQ %lu for MSI-X\n", + device_printf(child, "using IRQ %ju for MSI-X\n", rle->start); else { int run; @@ -1653,7 +1653,7 @@ pci_alloc_msix_method(device_t dev, device_t child, int *count) * IRQ values as ranges. 'irq' is the previous IRQ. * 'run' is true if we are in a range. */ - device_printf(child, "using IRQs %lu", rle->start); + device_printf(child, "using IRQs %ju", rle->start); irq = rle->start; run = 0; for (i = 1; i < actual; i++) { @@ -1674,7 +1674,7 @@ pci_alloc_msix_method(device_t dev, device_t child, int *count) } /* Start new range. */ - printf(",%lu", rle->start); + printf(",%ju", rle->start); irq = rle->start; } @@ -3572,13 +3572,13 @@ pci_alloc_secbus(device_t dev, device_t child, int *rid, rman_res_t start, start, end, count, flags & ~RF_ACTIVE); if (res == NULL) { resource_list_delete(rl, PCI_RES_BUS, *rid); - device_printf(child, "allocating %lu bus%s failed\n", + device_printf(child, "allocating %ju bus%s failed\n", count, count == 1 ? "" : "es"); return (NULL); } if (bootverbose) device_printf(child, - "Lazy allocation of %lu bus%s at %lu\n", count, + "Lazy allocation of %ju bus%s at %ju\n", count, count == 1 ? "" : "es", rman_get_start(res)); PCI_WRITE_CONFIG(dev, child, sec_reg, rman_get_start(res), 1); PCI_WRITE_CONFIG(dev, child, sub_reg, rman_get_end(res), 1); @@ -4391,9 +4391,9 @@ pci_print_child(device_t dev, device_t child) retval += bus_print_child_header(dev, child); - retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx"); - retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); - retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#jx"); + retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); if (device_get_flags(dev)) retval += printf(" flags %#x", device_get_flags(dev)); @@ -4971,13 +4971,13 @@ pci_reserve_map(device_t dev, device_t child, int type, int *rid, if (res == NULL) { resource_list_delete(rl, type, *rid); device_printf(child, - "%#lx bytes of rid %#x res %d failed (%#lx, %#lx).\n", + "%#jx bytes of rid %#x res %d failed (%#jx, %#jx).\n", count, *rid, type, start, end); goto out; } if (bootverbose) device_printf(child, - "Lazy allocation of %#lx bytes rid %#x type %d at %#lx\n", + "Lazy allocation of %#jx bytes rid %#x type %d at %#jx\n", count, *rid, type, rman_get_start(res)); map = rman_get_start(res); pci_write_bar(child, pm, map); @@ -5254,7 +5254,7 @@ pci_delete_resource(device_t dev, device_t child, int type, int rid) resource_list_busy(rl, type, rid)) { device_printf(dev, "delete_resource: " "Resource still owned by child, oops. " - "(type=%d, rid=%d, addr=%lx)\n", + "(type=%d, rid=%d, addr=%jx)\n", type, rid, rman_get_start(rle->res)); return; } diff --git a/sys/dev/pci/pci_host_generic.c b/sys/dev/pci/pci_host_generic.c index f0c8d45..110ee68 100644 --- a/sys/dev/pci/pci_host_generic.c +++ b/sys/dev/pci/pci_host_generic.c @@ -540,7 +540,7 @@ generic_pcie_alloc_resource_pcie(device_t dev, device_t child, int type, int *ri if (bootverbose) { device_printf(dev, - "rman_reserve_resource: start=%#lx, end=%#lx, count=%#lx\n", + "rman_reserve_resource: start=%#jx, end=%#jx, count=%#jx\n", start, end, count); } @@ -560,7 +560,7 @@ generic_pcie_alloc_resource_pcie(device_t dev, device_t child, int type, int *ri fail: device_printf(dev, "%s FAIL: type=%d, rid=%d, " - "start=%016lx, end=%016lx, count=%016lx, flags=%x\n", + "start=%016jx, end=%016jx, count=%016jx, flags=%x\n", __func__, type, *rid, start, end, count, flags); return (NULL); @@ -744,7 +744,7 @@ generic_pcie_alloc_resource_ofw(device_t bus, device_t child, int type, int *rid if (i == MAX_RANGES_TUPLES) { device_printf(bus, "Could not map resource " - "%#lx-%#lx\n", start, end); + "%#jx-%#jx\n", start, end); return (NULL); } } diff --git a/sys/dev/pci/pci_pci.c b/sys/dev/pci/pci_pci.c index 24f9b3a..112b198 100644 --- a/sys/dev/pci/pci_pci.c +++ b/sys/dev/pci/pci_pci.c @@ -247,7 +247,7 @@ pcib_is_isa_range(struct pcib_softc *sc, rman_res_t start, rman_res_t end, alias: if (bootverbose) device_printf(sc->dev, - "I/O range %#lx-%#lx overlaps with an ISA alias\n", start, + "I/O range %#jx-%#jx overlaps with an ISA alias\n", start, end); return (1); } @@ -339,7 +339,7 @@ alloc_ranges(rman_res_t start, rman_res_t end, void *arg) rid = w->reg; if (bootverbose) device_printf(as->sc->dev, - "allocating non-ISA range %#lx-%#lx\n", start, end); + "allocating non-ISA range %#jx-%#jx\n", start, end); as->res[as->count] = bus_alloc_resource(as->sc->dev, SYS_RES_IOPORT, &rid, start, end, end - start + 1, 0); if (as->res[as->count] == NULL) @@ -621,7 +621,7 @@ pcib_suballoc_bus(struct pcib_secbus *bus, device_t child, int *rid, if (bootverbose) device_printf(bus->dev, - "allocated bus range (%lu-%lu) for rid %d of %s\n", + "allocated bus range (%ju-%ju) for rid %d of %s\n", rman_get_start(res), rman_get_end(res), *rid, pcib_child_name(child)); rman_set_rid(res, *rid); @@ -646,7 +646,7 @@ pcib_grow_subbus(struct pcib_secbus *bus, rman_res_t new_end) if (error) return (error); if (bootverbose) - device_printf(bus->dev, "grew bus range to %lu-%lu\n", + device_printf(bus->dev, "grew bus range to %ju-%ju\n", rman_get_start(bus->res), rman_get_end(bus->res)); error = rman_manage_region(&bus->rman, old_end + 1, rman_get_end(bus->res)); @@ -694,8 +694,8 @@ pcib_alloc_subbus(struct pcib_secbus *bus, device_t child, int *rid, /* Finally, attempt to grow the existing resource. */ if (bootverbose) { device_printf(bus->dev, - "attempting to grow bus range for %lu buses\n", count); - printf("\tback candidate range: %lu-%lu\n", start_free, + "attempting to grow bus range for %ju buses\n", count); + printf("\tback candidate range: %ju-%ju\n", start_free, new_end); } if (pcib_grow_subbus(bus, new_end) == 0) @@ -1174,7 +1174,7 @@ pcib_suballoc_resource(struct pcib_softc *sc, struct pcib_window *w, if (bootverbose) device_printf(sc->dev, - "allocated %s range (%#lx-%#lx) for rid %x of %s\n", + "allocated %s range (%#jx-%#jx) for rid %x of %s\n", w->name, rman_get_start(res), rman_get_end(res), *rid, pcib_child_name(child)); rman_set_rid(res, *rid); @@ -1401,7 +1401,7 @@ pcib_grow_window(struct pcib_softc *sc, struct pcib_window *w, int type, if (error) { if (bootverbose) device_printf(sc->dev, - "failed to allocate initial %s window (%#lx-%#lx,%#lx)\n", + "failed to allocate initial %s window (%#jx-%#jx,%#jx)\n", w->name, start, end, count); return (error); } @@ -1433,7 +1433,7 @@ pcib_grow_window(struct pcib_softc *sc, struct pcib_window *w, int type, */ if (bootverbose) device_printf(sc->dev, - "attempting to grow %s window for (%#lx-%#lx,%#lx)\n", + "attempting to grow %s window for (%#jx-%#jx,%#jx)\n", w->name, start, end, count); align = (rman_res_t)1 << RF_ALIGNMENT(flags); if (start < w->base) { @@ -1457,7 +1457,7 @@ pcib_grow_window(struct pcib_softc *sc, struct pcib_window *w, int type, */ if (front >= start && front <= end_free) { if (bootverbose) - printf("\tfront candidate range: %#lx-%#lx\n", + printf("\tfront candidate range: %#jx-%#jx\n", front, end_free); front &= ~wmask; front = w->base - front; @@ -1485,7 +1485,7 @@ pcib_grow_window(struct pcib_softc *sc, struct pcib_window *w, int type, */ if (back <= end && start_free <= back) { if (bootverbose) - printf("\tback candidate range: %#lx-%#lx\n", + printf("\tback candidate range: %#jx-%#jx\n", start_free, back); back |= wmask; back -= w->limit; @@ -1709,7 +1709,7 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, #endif } if (end < start) { - device_printf(dev, "ioport: end (%lx) < start (%lx)\n", + device_printf(dev, "ioport: end (%jx) < start (%jx)\n", end, start); start = 0; end = 0; @@ -1717,13 +1717,13 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, } if (!ok) { device_printf(dev, "%s%srequested unsupported I/O " - "range 0x%lx-0x%lx (decoding 0x%x-0x%x)\n", + "range 0x%jx-0x%jx (decoding 0x%x-0x%x)\n", name, suffix, start, end, sc->iobase, sc->iolimit); return (NULL); } if (bootverbose) device_printf(dev, - "%s%srequested I/O range 0x%lx-0x%lx: in range\n", + "%s%srequested I/O range 0x%jx-0x%jx: in range\n", name, suffix, start, end); break; @@ -1778,7 +1778,7 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, #endif } if (end < start) { - device_printf(dev, "memory: end (%lx) < start (%lx)\n", + device_printf(dev, "memory: end (%jx) < start (%jx)\n", end, start); start = 0; end = 0; @@ -1786,7 +1786,7 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, } if (!ok && bootverbose) device_printf(dev, - "%s%srequested unsupported memory range %#lx-%#lx " + "%s%srequested unsupported memory range %#jx-%#jx " "(decoding %#jx-%#jx, %#jx-%#jx)\n", name, suffix, start, end, (uintmax_t)sc->membase, (uintmax_t)sc->memlimit, @@ -1795,7 +1795,7 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, return (NULL); if (bootverbose) device_printf(dev,"%s%srequested memory range " - "0x%lx-0x%lx: good\n", + "0x%jx-0x%jx: good\n", name, suffix, start, end); break; diff --git a/sys/dev/pci/pci_subr.c b/sys/dev/pci/pci_subr.c index 77c60c3..f281117 100644 --- a/sys/dev/pci/pci_subr.c +++ b/sys/dev/pci/pci_subr.c @@ -185,7 +185,7 @@ pcib_host_res_decodes(struct pcib_host_resources *hr, int type, rman_res_t start int rid; if (bootverbose) - device_printf(hr->hr_pcib, "decoding %d %srange %#lx-%#lx\n", + device_printf(hr->hr_pcib, "decoding %d %srange %#jx-%#jx\n", type, flags & RF_PREFETCHABLE ? "prefetchable ": "", start, end); rid = resource_list_add_next(&hr->hr_rl, type, start, end, @@ -229,8 +229,8 @@ restart: if (((flags & RF_PREFETCHABLE) != 0) != ((rle->flags & RLE_PREFETCH) != 0)) continue; - new_start = ulmax(start, rle->start); - new_end = ulmin(end, rle->end); + new_start = ummax(start, rle->start); + new_end = ummin(end, rle->end); if (new_start > new_end || new_start + count - 1 > new_end || new_start + count < new_start) @@ -240,7 +240,7 @@ restart: if (r != NULL) { if (bootverbose) device_printf(hr->hr_pcib, - "allocated type %d (%#lx-%#lx) for rid %x of %s\n", + "allocated type %d (%#jx-%#jx) for rid %x of %s\n", type, rman_get_start(r), rman_get_end(r), *rid, pcib_child_name(dev)); return (r); diff --git a/sys/dev/ppbus/vpo.c b/sys/dev/ppbus/vpo.c index 9c9054f..f508275 100644 --- a/sys/dev/ppbus/vpo.c +++ b/sys/dev/ppbus/vpo.c @@ -187,17 +187,19 @@ vpo_intr(struct vpo_data *vpo, struct ccb_scsiio *csio) #ifdef VP0_DEBUG int i; #endif + uint8_t *ptr; + ptr = scsiio_cdb_ptr(csio); if (vpo->vpo_isplus) { errno = imm_do_scsi(&vpo->vpo_io, VP0_INITIATOR, csio->ccb_h.target_id, - (char *)&csio->cdb_io.cdb_bytes, csio->cdb_len, + ptr, csio->cdb_len, (char *)csio->data_ptr, csio->dxfer_len, &vpo->vpo_stat, &vpo->vpo_count, &vpo->vpo_error); } else { errno = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR, csio->ccb_h.target_id, - (char *)&csio->cdb_io.cdb_bytes, csio->cdb_len, + ptr, csio->cdb_len, (char *)csio->data_ptr, csio->dxfer_len, &vpo->vpo_stat, &vpo->vpo_count, &vpo->vpo_error); } @@ -208,7 +210,7 @@ vpo_intr(struct vpo_data *vpo, struct ccb_scsiio *csio) /* dump of command */ for (i=0; i<csio->cdb_len; i++) - printf("%x ", ((char *)&csio->cdb_io.cdb_bytes)[i]); + printf("%x ", ((char *)ptr)[i]); printf("\n"); #endif @@ -307,11 +309,15 @@ vpo_action(struct cam_sim *sim, union ccb *ccb) csio = &ccb->csio; + if (ccb->ccb_h.flags & CAM_CDB_PHYS) { + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } #ifdef VP0_DEBUG device_printf(vpo->vpo_dev, "XPT_SCSI_IO (0x%x) request\n", - csio->cdb_io.cdb_bytes[0]); + *scsiio_cdb_ptr(csio)); #endif - vpo_intr(vpo, csio); xpt_done(ccb); diff --git a/sys/dev/ppc/ppc.c b/sys/dev/ppc/ppc.c index a23f06c..4c6761f 100644 --- a/sys/dev/ppc/ppc.c +++ b/sys/dev/ppc/ppc.c @@ -1699,7 +1699,7 @@ ppc_probe(device_t dev, int rid) next_bios_ppc += 1; if (bootverbose) device_printf(dev, - "parallel port found at 0x%lx\n", port); + "parallel port found at 0x%jx\n", port); } #else if ((next_bios_ppc < BIOS_MAX_PPC) && @@ -1707,7 +1707,7 @@ ppc_probe(device_t dev, int rid) port = *(BIOS_PORTS + next_bios_ppc++); if (bootverbose) device_printf(dev, - "parallel port found at 0x%lx\n", port); + "parallel port found at 0x%jx\n", port); } else { device_printf(dev, "parallel port not found.\n"); return (ENXIO); diff --git a/sys/dev/proto/proto_bus_isa.c b/sys/dev/proto/proto_bus_isa.c index e7438bf..4dad91c 100644 --- a/sys/dev/proto/proto_bus_isa.c +++ b/sys/dev/proto/proto_bus_isa.c @@ -80,7 +80,7 @@ proto_isa_probe(device_t dev) return (ENODEV); sb = sbuf_new_auto(); - sbuf_printf(sb, "%s:%#lx", proto_isa_prefix, rman_get_start(res)); + sbuf_printf(sb, "%s:%#jx", proto_isa_prefix, rman_get_start(res)); sbuf_finish(sb); device_set_desc_copy(dev, sbuf_data(sb)); sbuf_delete(sb); diff --git a/sys/dev/qlxgb/qla_isr.c b/sys/dev/qlxgb/qla_isr.c index 3169fd1..983fc12 100644 --- a/sys/dev/qlxgb/qla_isr.c +++ b/sys/dev/qlxgb/qla_isr.c @@ -267,7 +267,6 @@ qla_rcv_isr(qla_host_t *ha, uint32_t sds_idx, uint32_t count) uint32_t comp_idx, desc_count; q80_stat_desc_t *sdesc; struct lro_ctrl *lro; - struct lro_entry *queued; uint32_t ret = 0; dev = ha->pci_dev; @@ -324,11 +323,7 @@ qla_rcv_isr(qla_host_t *ha, uint32_t sds_idx, uint32_t count) } } - while((!SLIST_EMPTY(&lro->lro_active))) { - queued = SLIST_FIRST(&lro->lro_active); - SLIST_REMOVE_HEAD(&lro->lro_active, next); - tcp_lro_flush(lro, queued); - } + tcp_lro_flush_all(lro); if (hw->sds[sds_idx].sdsr_next != comp_idx) { QL_UPDATE_SDS_CONSUMER_INDEX(ha, sds_idx, comp_idx); diff --git a/sys/dev/qlxge/qls_isr.c b/sys/dev/qlxge/qls_isr.c index 03b3a3c..4cca895 100644 --- a/sys/dev/qlxge/qls_isr.c +++ b/sys/dev/qlxge/qls_isr.c @@ -232,7 +232,6 @@ qls_cq_isr(qla_host_t *ha, uint32_t cq_idx) uint32_t i, cq_comp_idx; int ret = 0, tx_comp_done = 0; struct lro_ctrl *lro; - struct lro_entry *queued; cq_b = ha->rx_ring[cq_idx].cq_base_vaddr; lro = &ha->rx_ring[cq_idx].lro; @@ -287,11 +286,7 @@ qls_cq_isr(qla_host_t *ha, uint32_t cq_idx) } } - while((!SLIST_EMPTY(&lro->lro_active))) { - queued = SLIST_FIRST(&lro->lro_active); - SLIST_REMOVE_HEAD(&lro->lro_active, next); - tcp_lro_flush(lro, queued); - } + tcp_lro_flush_all(lro); ha->rx_ring[cq_idx].cq_next = cq_comp_idx; diff --git a/sys/dev/random/random_harvestq.c b/sys/dev/random/random_harvestq.c index c56d1ec..081ba83 100644 --- a/sys/dev/random/random_harvestq.c +++ b/sys/dev/random/random_harvestq.c @@ -183,7 +183,8 @@ random_kthread(void) /* NOTREACHED */ } /* This happens well after SI_SUB_RANDOM */ -SYSINIT(random_device_h_proc, SI_SUB_CREATE_INIT, SI_ORDER_ANY, kproc_start, &random_proc_kp); +SYSINIT(random_device_h_proc, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, kproc_start, + &random_proc_kp); /* * Run through all fast sources reading entropy for the given diff --git a/sys/dev/sdhci/sdhci_fdt.c b/sys/dev/sdhci/sdhci_fdt.c index e49eda8..27709d0 100644 --- a/sys/dev/sdhci/sdhci_fdt.c +++ b/sys/dev/sdhci/sdhci_fdt.c @@ -308,3 +308,4 @@ DRIVER_MODULE(sdhci_fdt, simplebus, sdhci_fdt_driver, sdhci_fdt_devclass, NULL, NULL); MODULE_DEPEND(sdhci_fdt, sdhci, 1, 1, 1); DRIVER_MODULE(mmc, sdhci_fdt, mmc_driver, mmc_devclass, NULL, NULL); +MODULE_DEPEND(sdhci_fdt, mmc, 1, 1, 1); diff --git a/sys/dev/sdhci/sdhci_pci.c b/sys/dev/sdhci/sdhci_pci.c index e0fd29c..3ed43ab 100644 --- a/sys/dev/sdhci/sdhci_pci.c +++ b/sys/dev/sdhci/sdhci_pci.c @@ -475,3 +475,4 @@ DRIVER_MODULE(sdhci_pci, pci, sdhci_pci_driver, sdhci_pci_devclass, NULL, NULL); MODULE_DEPEND(sdhci_pci, sdhci, 1, 1, 1); DRIVER_MODULE(mmc, sdhci_pci, mmc_driver, mmc_devclass, NULL, NULL); +MODULE_DEPEND(sdhci_pci, mmc, 1, 1, 1); diff --git a/sys/dev/sfxge/sfxge_mcdi.c b/sys/dev/sfxge/sfxge_mcdi.c index 3a85c28..85d7997 100644 --- a/sys/dev/sfxge/sfxge_mcdi.c +++ b/sys/dev/sfxge/sfxge_mcdi.c @@ -250,10 +250,6 @@ sfxge_mcdi_ioctl(struct sfxge_softc *sc, sfxge_ioc_t *ip) } mcdibuf = malloc(SFXGE_MCDI_MAX_PAYLOAD, M_TEMP, M_WAITOK | M_ZERO); - if (mcdibuf == NULL) { - rc = ENOMEM; - goto fail4; - } if ((rc = copyin(ip->u.mcdi.payload, mcdibuf, ip->u.mcdi.len)) != 0) { goto fail5; } @@ -292,7 +288,6 @@ sfxge_mcdi_ioctl(struct sfxge_softc *sc, sfxge_ioc_t *ip) fail6: fail5: free(mcdibuf, M_TEMP); -fail4: fail3: fail2: fail1: diff --git a/sys/dev/sfxge/sfxge_nvram.c b/sys/dev/sfxge/sfxge_nvram.c index c4fa224..9113a89 100644 --- a/sys/dev/sfxge/sfxge_nvram.c +++ b/sys/dev/sfxge/sfxge_nvram.c @@ -75,10 +75,6 @@ sfxge_nvram_rw(struct sfxge_softc *sc, sfxge_ioc_t *ip, efx_nvram_type_t type, goto fail1; buf = malloc(chunk_size, M_TEMP, M_WAITOK); - if (buf == NULL) { - rc = ENOMEM; - goto fail2; - } off = 0; while (total_size) { @@ -108,7 +104,6 @@ sfxge_nvram_rw(struct sfxge_softc *sc, sfxge_ioc_t *ip, efx_nvram_type_t type, fail3: free(buf, M_TEMP); -fail2: efx_nvram_rw_finish(enp, type); fail1: return (rc); diff --git a/sys/dev/siba/siba.c b/sys/dev/siba/siba.c index 3489ddc..f0bcd9f 100644 --- a/sys/dev/siba/siba.c +++ b/sys/dev/siba/siba.c @@ -597,8 +597,8 @@ siba_print_all_resources(device_t dev) if (STAILQ_FIRST(rl)) retval += printf(" at"); - retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); - retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); return (retval); } diff --git a/sys/dev/siis/siis.c b/sys/dev/siis/siis.c index 9ceb444..cb9d800 100644 --- a/sys/dev/siis/siis.c +++ b/sys/dev/siis/siis.c @@ -320,7 +320,7 @@ siis_alloc_resource(device_t dev, device_t child, int type, int *rid, int unit = ((struct siis_channel *)device_get_softc(child))->unit; struct resource *res = NULL; int offset = unit << 13; - long st; + rman_res_t st; switch (type) { case SYS_RES_MEMORY: diff --git a/sys/dev/sound/isa/ad1816.c b/sys/dev/sound/isa/ad1816.c index f3bb89a..2ae39cf 100644 --- a/sys/dev/sound/isa/ad1816.c +++ b/sys/dev/sound/isa/ad1816.c @@ -624,11 +624,11 @@ ad1816_attach(device_t dev) goto no; } if (ad1816->drq2) - snprintf(status2, SND_STATUSLEN, ":%ld", rman_get_start(ad1816->drq2)); + snprintf(status2, SND_STATUSLEN, ":%jd", rman_get_start(ad1816->drq2)); else status2[0] = '\0'; - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %u %s", + snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd drq %jd%s bufsz %u %s", rman_get_start(ad1816->io_base), rman_get_start(ad1816->irq), rman_get_start(ad1816->drq1), diff --git a/sys/dev/sound/isa/ess.c b/sys/dev/sound/isa/ess.c index 5fd9529..e4032de 100644 --- a/sys/dev/sound/isa/ess.c +++ b/sys/dev/sound/isa/ess.c @@ -867,12 +867,12 @@ ess_attach(device_t dev) } if (sc->drq2) - snprintf(buf, SND_STATUSLEN, ":%ld", rman_get_start(sc->drq2)); + snprintf(buf, SND_STATUSLEN, ":%jd", rman_get_start(sc->drq2)); else buf[0] = '\0'; - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %u %s", - rman_get_start(sc->io_base), rman_get_start(sc->irq), + snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd drq %jd%s bufsz %u %s", + rman_get_start(sc->io_base), rman_get_start(sc->irq), rman_get_start(sc->drq1), buf, sc->bufsize, PCM_KLDSTRING(snd_ess)); diff --git a/sys/dev/sound/isa/mss.c b/sys/dev/sound/isa/mss.c index 7aaef06..62f6617 100644 --- a/sys/dev/sound/isa/mss.c +++ b/sys/dev/sound/isa/mss.c @@ -1326,7 +1326,7 @@ mss_probe(device_t dev) } tmp &= 0x3f; if (!(tmp == 0x04 || tmp == 0x0f || tmp == 0x00 || tmp == 0x05)) { - BVDDB(printf("No MSS signature detected on port 0x%lx (0x%x)\n", + BVDDB(printf("No MSS signature detected on port 0x%jx (0x%x)\n", rman_get_start(mss->io_base), tmpx)); goto no; } @@ -1767,7 +1767,7 @@ mss_doattach(device_t dev, struct mss_info *mss) else status2[0] = '\0'; - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %d%s bufsz %u", + snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd drq %d%s bufsz %u", rman_get_start(mss->io_base), rman_get_start(mss->irq), pdma, status2, mss->bufsize); if (pcm_register(dev, mss, 1, 1)) goto no; diff --git a/sys/dev/sound/isa/sb16.c b/sys/dev/sound/isa/sb16.c index 536de77..946c6bf 100644 --- a/sys/dev/sound/isa/sb16.c +++ b/sys/dev/sound/isa/sb16.c @@ -854,11 +854,11 @@ sb16_attach(device_t dev) } if (!(pcm_getflags(dev) & SD_F_SIMPLEX)) - snprintf(status2, SND_STATUSLEN, ":%ld", rman_get_start(sb->drq2)); + snprintf(status2, SND_STATUSLEN, ":%jd", rman_get_start(sb->drq2)); else status2[0] = '\0'; - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %u %s", + snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd drq %jd%s bufsz %u %s", rman_get_start(sb->io_base), rman_get_start(sb->irq), rman_get_start(sb->drq1), status2, sb->bufsize, PCM_KLDSTRING(snd_sb16)); diff --git a/sys/dev/sound/isa/sb8.c b/sys/dev/sound/isa/sb8.c index 7c39515..c9ed746 100644 --- a/sys/dev/sound/isa/sb8.c +++ b/sys/dev/sound/isa/sb8.c @@ -749,7 +749,7 @@ sb_attach(device_t dev) goto no; } - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld bufsz %u %s", + snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd drq %jd bufsz %u %s", rman_get_start(sb->io_base), rman_get_start(sb->irq), rman_get_start(sb->drq), sb->bufsize, PCM_KLDSTRING(snd_sb8)); diff --git a/sys/dev/sound/pci/als4000.c b/sys/dev/sound/pci/als4000.c index b214333..bb38749 100644 --- a/sys/dev/sound/pci/als4000.c +++ b/sys/dev/sound/pci/als4000.c @@ -848,7 +848,7 @@ als_pci_attach(device_t dev) pcm_addchan(dev, PCMDIR_PLAY, &alspchan_class, sc); pcm_addchan(dev, PCMDIR_REC, &alsrchan_class, sc); - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", + snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd %s", rman_get_start(sc->reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_als4000)); pcm_setstatus(dev, status); return 0; diff --git a/sys/dev/sound/pci/atiixp.c b/sys/dev/sound/pci/atiixp.c index 8df67aa..ca9a539 100644 --- a/sys/dev/sound/pci/atiixp.c +++ b/sys/dev/sound/pci/atiixp.c @@ -1097,7 +1097,7 @@ atiixp_chip_post_init(void *arg) "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), sysctl_atiixp_polling, "I", "Enable polling mode"); - snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s", + snprintf(status, SND_STATUSLEN, "at memory 0x%jx irq %jd %s", rman_get_start(sc->reg), rman_get_start(sc->irq), PCM_KLDSTRING(snd_atiixp)); diff --git a/sys/dev/sound/pci/aureal.c b/sys/dev/sound/pci/aureal.c index 67af075..f990f43 100644 --- a/sys/dev/sound/pci/aureal.c +++ b/sys/dev/sound/pci/aureal.c @@ -645,7 +645,7 @@ au_pci_attach(device_t dev) goto bad; } - snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld %s", + snprintf(status, SND_STATUSLEN, "at %s 0x%jx irq %jd %s", (type[0] == SYS_RES_IOPORT)? "io" : "memory", rman_get_start(reg[0]), rman_get_start(irq),PCM_KLDSTRING(snd_aureal)); diff --git a/sys/dev/sound/pci/cmi.c b/sys/dev/sound/pci/cmi.c index 6075a92..9e62659 100644 --- a/sys/dev/sound/pci/cmi.c +++ b/sys/dev/sound/pci/cmi.c @@ -995,7 +995,7 @@ cmi_attach(device_t dev) pcm_addchan(dev, PCMDIR_PLAY, &cmichan_class, sc); pcm_addchan(dev, PCMDIR_REC, &cmichan_class, sc); - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", + snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd %s", rman_get_start(sc->reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_cmi)); pcm_setstatus(dev, status); diff --git a/sys/dev/sound/pci/cs4281.c b/sys/dev/sound/pci/cs4281.c index 929b9d7..4750daf 100644 --- a/sys/dev/sound/pci/cs4281.c +++ b/sys/dev/sound/pci/cs4281.c @@ -849,7 +849,7 @@ cs4281_pci_attach(device_t dev) pcm_addchan(dev, PCMDIR_PLAY, &cs4281chan_class, sc); pcm_addchan(dev, PCMDIR_REC, &cs4281chan_class, sc); - snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld %s", + snprintf(status, SND_STATUSLEN, "at %s 0x%jx irq %jd %s", (sc->regtype == SYS_RES_IOPORT)? "io" : "memory", rman_get_start(sc->reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_cs4281)); pcm_setstatus(dev, status); diff --git a/sys/dev/sound/pci/csapcm.c b/sys/dev/sound/pci/csapcm.c index 89ffd2b..5a1544d 100644 --- a/sys/dev/sound/pci/csapcm.c +++ b/sys/dev/sound/pci/csapcm.c @@ -821,7 +821,7 @@ pcmcsa_attach(device_t dev) return (ENXIO); } - snprintf(status, SND_STATUSLEN, "at irq %ld %s", + snprintf(status, SND_STATUSLEN, "at irq %jd %s", rman_get_start(resp->irq),PCM_KLDSTRING(snd_csa)); /* Enable interrupt. */ diff --git a/sys/dev/sound/pci/ds1.c b/sys/dev/sound/pci/ds1.c index 302271a..23f3d5c 100644 --- a/sys/dev/sound/pci/ds1.c +++ b/sys/dev/sound/pci/ds1.c @@ -1010,7 +1010,7 @@ ds_pci_attach(device_t dev) goto bad; } - snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s", + snprintf(status, SND_STATUSLEN, "at memory 0x%jx irq %jd %s", rman_get_start(sc->reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_ds1)); if (pcm_register(dev, sc, DS1_CHANS, 2)) diff --git a/sys/dev/sound/pci/emu10k1.c b/sys/dev/sound/pci/emu10k1.c index c4a13f3..f1d77ee 100644 --- a/sys/dev/sound/pci/emu10k1.c +++ b/sys/dev/sound/pci/emu10k1.c @@ -2132,7 +2132,7 @@ emu_pci_attach(device_t dev) goto bad; } - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", + snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd %s", rman_get_start(sc->reg), rman_get_start(sc->irq), PCM_KLDSTRING(snd_emu10k1)); diff --git a/sys/dev/sound/pci/emu10kx.c b/sys/dev/sound/pci/emu10kx.c index b2eaf2c..bd97ab8 100644 --- a/sys/dev/sound/pci/emu10kx.c +++ b/sys/dev/sound/pci/emu10kx.c @@ -3225,7 +3225,7 @@ emu_pci_attach(device_t dev) device_printf(dev, "unable to create control device\n"); goto bad; } - snprintf(status, 255, "rev %d at io 0x%lx irq %ld", sc->rev, rman_get_start(sc->reg), rman_get_start(sc->irq)); + snprintf(status, 255, "rev %d at io 0x%jx irq %jd", sc->rev, rman_get_start(sc->reg), rman_get_start(sc->irq)); /* Voices */ for (i = 0; i < NUM_G; i++) { diff --git a/sys/dev/sound/pci/envy24.c b/sys/dev/sound/pci/envy24.c index c4eaa10..9272a95 100644 --- a/sys/dev/sound/pci/envy24.c +++ b/sys/dev/sound/pci/envy24.c @@ -2599,7 +2599,7 @@ envy24_pci_attach(device_t dev) /* set status iformation */ snprintf(status, SND_STATUSLEN, - "at io 0x%lx:%ld,0x%lx:%ld,0x%lx:%ld,0x%lx:%ld irq %ld", + "at io 0x%jx:%jd,0x%jx:%jd,0x%jx:%jd,0x%jx:%jd irq %jd", rman_get_start(sc->cs), rman_get_end(sc->cs) - rman_get_start(sc->cs) + 1, rman_get_start(sc->ddma), diff --git a/sys/dev/sound/pci/envy24ht.c b/sys/dev/sound/pci/envy24ht.c index 85a36c2..fe1fb19 100644 --- a/sys/dev/sound/pci/envy24ht.c +++ b/sys/dev/sound/pci/envy24ht.c @@ -2507,7 +2507,7 @@ envy24ht_pci_attach(device_t dev) /* set status iformation */ snprintf(status, SND_STATUSLEN, - "at io 0x%lx:%ld,0x%lx:%ld irq %ld", + "at io 0x%jx:%jd,0x%jx:%jd irq %jd", rman_get_start(sc->cs), rman_get_end(sc->cs) - rman_get_start(sc->cs) + 1, rman_get_start(sc->mt), diff --git a/sys/dev/sound/pci/es137x.c b/sys/dev/sound/pci/es137x.c index 0dd88a8..7032c84 100644 --- a/sys/dev/sound/pci/es137x.c +++ b/sys/dev/sound/pci/es137x.c @@ -1856,7 +1856,7 @@ es_pci_attach(device_t dev) goto bad; } - snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld %s", + snprintf(status, SND_STATUSLEN, "at %s 0x%jx irq %jd %s", (es->regtype == SYS_RES_IOPORT)? "io" : "memory", rman_get_start(es->reg), rman_get_start(es->irq), PCM_KLDSTRING(snd_es137x)); diff --git a/sys/dev/sound/pci/fm801.c b/sys/dev/sound/pci/fm801.c index 82b1d77..252e714 100644 --- a/sys/dev/sound/pci/fm801.c +++ b/sys/dev/sound/pci/fm801.c @@ -639,7 +639,7 @@ fm801_pci_attach(device_t dev) goto oops; } - snprintf(status, 64, "at %s 0x%lx irq %ld %s", + snprintf(status, 64, "at %s 0x%jx irq %jd %s", (fm801->regtype == SYS_RES_IOPORT)? "io" : "memory", rman_get_start(fm801->reg), rman_get_start(fm801->irq),PCM_KLDSTRING(snd_fm801)); diff --git a/sys/dev/sound/pci/hda/hdac.c b/sys/dev/sound/pci/hda/hdac.c index b0f250c..c761acb 100644 --- a/sys/dev/sound/pci/hda/hdac.c +++ b/sys/dev/sound/pci/hda/hdac.c @@ -159,6 +159,7 @@ static const struct { { HDA_ATI_RV940, "ATI RV940", 0, 0 }, { HDA_ATI_RV970, "ATI RV970", 0, 0 }, { HDA_ATI_R1000, "ATI R1000", 0, 0 }, + { HDA_AMD_HUDSON2, "AMD Hudson-2", 0, 0 }, { HDA_RDC_M3010, "RDC M3010", 0, 0 }, { HDA_VIA_VT82XX, "VIA VT8251/8237A",0, 0 }, { HDA_SIS_966, "SiS 966", 0, 0 }, @@ -167,6 +168,7 @@ static const struct { { HDA_INTEL_ALL, "Intel", 0, 0 }, { HDA_NVIDIA_ALL, "NVIDIA", 0, 0 }, { HDA_ATI_ALL, "ATI", 0, 0 }, + { HDA_AMD_ALL, "AMD", 0, 0 }, { HDA_VIA_ALL, "VIA", 0, 0 }, { HDA_SIS_ALL, "SiS", 0, 0 }, { HDA_ULI_ALL, "ULI", 0, 0 }, diff --git a/sys/dev/sound/pci/hda/hdac.h b/sys/dev/sound/pci/hda/hdac.h index 9538b307..1fc265a 100644 --- a/sys/dev/sound/pci/hda/hdac.h +++ b/sys/dev/sound/pci/hda/hdac.h @@ -136,6 +136,10 @@ #define HDA_ATI_R1000 HDA_MODEL_CONSTRUCT(ATI, 0xaaa0) #define HDA_ATI_ALL HDA_MODEL_CONSTRUCT(ATI, 0xffff) +#define AMD_VENDORID 0x1022 +#define HDA_AMD_HUDSON2 HDA_MODEL_CONSTRUCT(AMD, 0x780d) +#define HDA_AMD_ALL HDA_MODEL_CONSTRUCT(AMD, 0xffff) + /* RDC */ #define RDC_VENDORID 0x17f3 #define HDA_RDC_M3010 HDA_MODEL_CONSTRUCT(RDC, 0x3010) diff --git a/sys/dev/sound/pci/hdspe-pcm.c b/sys/dev/sound/pci/hdspe-pcm.c index dc62377..d84712d 100644 --- a/sys/dev/sound/pci/hdspe-pcm.c +++ b/sys/dev/sound/pci/hdspe-pcm.c @@ -668,7 +668,7 @@ hdspe_pcm_attach(device_t dev) scp->chnum++; } - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", + snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd %s", rman_get_start(scp->sc->cs), rman_get_start(scp->sc->irq), PCM_KLDSTRING(snd_hdspe)); diff --git a/sys/dev/sound/pci/ich.c b/sys/dev/sound/pci/ich.c index 4895ad6..61b6781 100644 --- a/sys/dev/sound/pci/ich.c +++ b/sys/dev/sound/pci/ich.c @@ -687,7 +687,7 @@ ich_setstatus(struct sc_info *sc) char status[SND_STATUSLEN]; snprintf(status, SND_STATUSLEN, - "at io 0x%lx, 0x%lx irq %ld bufsz %u %s", + "at io 0x%jx, 0x%jx irq %jd bufsz %u %s", rman_get_start(sc->nambar), rman_get_start(sc->nabmbar), rman_get_start(sc->irq), sc->bufsz,PCM_KLDSTRING(snd_ich)); diff --git a/sys/dev/sound/pci/maestro.c b/sys/dev/sound/pci/maestro.c index c7cbb35..fbadf3b 100644 --- a/sys/dev/sound/pci/maestro.c +++ b/sys/dev/sound/pci/maestro.c @@ -1917,7 +1917,7 @@ agg_attach(device_t dev) adjust_pchbase(ess->pch, ess->playchns, ess->bufsz); snprintf(status, SND_STATUSLEN, - "port 0x%lx-0x%lx irq %ld at device %d.%d on pci%d", + "port 0x%jx-0x%jx irq %jd at device %d.%d on pci%d", rman_get_start(reg), rman_get_end(reg), rman_get_start(irq), pci_get_slot(dev), pci_get_function(dev), pci_get_bus(dev)); pcm_setstatus(dev, status); diff --git a/sys/dev/sound/pci/maestro3.c b/sys/dev/sound/pci/maestro3.c index 20a9bda..5bab021 100644 --- a/sys/dev/sound/pci/maestro3.c +++ b/sys/dev/sound/pci/maestro3.c @@ -1440,7 +1440,7 @@ m3_pci_attach(device_t dev) goto bad; } } - snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld %s", + snprintf(status, SND_STATUSLEN, "at %s 0x%jx irq %jd %s", (sc->regtype == SYS_RES_IOPORT)? "io" : "memory", rman_get_start(sc->reg), rman_get_start(sc->irq), PCM_KLDSTRING(snd_maestro3)); diff --git a/sys/dev/sound/pci/neomagic.c b/sys/dev/sound/pci/neomagic.c index 71c1bf1..8a78bf7 100644 --- a/sys/dev/sound/pci/neomagic.c +++ b/sys/dev/sound/pci/neomagic.c @@ -702,7 +702,7 @@ nm_pci_attach(device_t dev) goto bad; } - snprintf(status, SND_STATUSLEN, "at memory 0x%lx, 0x%lx irq %ld %s", + snprintf(status, SND_STATUSLEN, "at memory 0x%jx, 0x%jx irq %jd %s", rman_get_start(sc->buf), rman_get_start(sc->reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_neomagic)); diff --git a/sys/dev/sound/pci/solo.c b/sys/dev/sound/pci/solo.c index 534d810..dc46ac8 100644 --- a/sys/dev/sound/pci/solo.c +++ b/sys/dev/sound/pci/solo.c @@ -1051,7 +1051,7 @@ ess_attach(device_t dev) if (mixer_init(dev, &solomixer_class, sc)) goto no; - snprintf(status, SND_STATUSLEN, "at io 0x%lx,0x%lx,0x%lx irq %ld %s", + snprintf(status, SND_STATUSLEN, "at io 0x%jx,0x%jx,0x%jx irq %jd %s", rman_get_start(sc->io), rman_get_start(sc->sb), rman_get_start(sc->vc), rman_get_start(sc->irq),PCM_KLDSTRING(snd_solo)); diff --git a/sys/dev/sound/pci/t4dwave.c b/sys/dev/sound/pci/t4dwave.c index ef48890..32ddd00 100644 --- a/sys/dev/sound/pci/t4dwave.c +++ b/sys/dev/sound/pci/t4dwave.c @@ -948,7 +948,7 @@ tr_pci_attach(device_t dev) goto bad; } - snprintf(status, 64, "at io 0x%lx irq %ld %s", + snprintf(status, 64, "at io 0x%jx irq %jd %s", rman_get_start(tr->reg), rman_get_start(tr->irq),PCM_KLDSTRING(snd_t4dwave)); if (pcm_register(dev, tr, dacn, 1)) diff --git a/sys/dev/sound/pci/via8233.c b/sys/dev/sound/pci/via8233.c index 0694cef..45fe175 100644 --- a/sys/dev/sound/pci/via8233.c +++ b/sys/dev/sound/pci/via8233.c @@ -1348,7 +1348,7 @@ via_attach(device_t dev) ac97_setextmode(via->codec, ext); } - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", + snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd %s", rman_get_start(via->reg), rman_get_start(via->irq), PCM_KLDSTRING(snd_via8233)); diff --git a/sys/dev/sound/pci/via82c686.c b/sys/dev/sound/pci/via82c686.c index 6b615e8..4484d6e 100644 --- a/sys/dev/sound/pci/via82c686.c +++ b/sys/dev/sound/pci/via82c686.c @@ -590,7 +590,7 @@ via_attach(device_t dev) NSEGS * sizeof(struct via_dma_op), dma_cb, via, 0) != 0) goto bad; - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", + snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd %s", rman_get_start(via->reg), rman_get_start(via->irq), PCM_KLDSTRING(snd_via82c686)); diff --git a/sys/dev/sound/pci/vibes.c b/sys/dev/sound/pci/vibes.c index f16e3ef..22c84c8 100644 --- a/sys/dev/sound/pci/vibes.c +++ b/sys/dev/sound/pci/vibes.c @@ -815,7 +815,7 @@ sv_attach(device_t dev) { ((mu - ml) % 0x200)) { device_printf(dev, "sv_attach: resource assumptions not met " "(midi 0x%08lx, games 0x%08lx)\n", - midi_start, games_start); + (u_long)midi_start, (u_long)games_start); goto fail; } @@ -874,7 +874,7 @@ sv_attach(device_t dev) { pcm_addchan(dev, PCMDIR_PLAY, &svpchan_class, sc); pcm_addchan(dev, PCMDIR_REC, &svrchan_class, sc); - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", + snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd %s", rman_get_start(sc->enh_reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_vibes)); pcm_setstatus(dev, status); diff --git a/sys/dev/uart/uart_dev_ns8250.c b/sys/dev/uart/uart_dev_ns8250.c index cb52ce7..a36afef 100644 --- a/sys/dev/uart/uart_dev_ns8250.c +++ b/sys/dev/uart/uart_dev_ns8250.c @@ -398,7 +398,6 @@ struct uart_class uart_ns8250_class = { static struct ofw_compat_data compat_data[] = { {"ns16550", (uintptr_t)&uart_ns8250_class}, {"ns16550a", (uintptr_t)&uart_ns8250_class}, - {"snps,dw-apb-uart", (uintptr_t)&uart_ns8250_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(compat_data); @@ -451,19 +450,9 @@ ns8250_bus_attach(struct uart_softc *sc) pcell_t cell; #endif - ns8250->busy_detect = 0; - #ifdef FDT - /* - * Check whether uart requires to read USR reg when IIR_BUSY and - * has broken txfifo. - */ - ns8250->busy_detect = ofw_bus_is_compatible(sc->sc_dev, "snps,dw-apb-uart"); + /* Check whether uart has a broken txfifo. */ node = ofw_bus_get_node(sc->sc_dev); - /* XXX: This is kept for a short time for compatibility with older device trees */ - if ((OF_getencprop(node, "busy-detect", &cell, sizeof(cell))) > 0 - && cell != 0) - ns8250->busy_detect = 1; if ((OF_getencprop(node, "broken-txfifo", &cell, sizeof(cell))) > 0) broken_txfifo = cell ? 1 : 0; #endif diff --git a/sys/dev/uart/uart_dev_snps.c b/sys/dev/uart/uart_dev_snps.c new file mode 100644 index 0000000..af4000f --- /dev/null +++ b/sys/dev/uart/uart_dev_snps.c @@ -0,0 +1,283 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_bus.h> +#include <dev/uart/uart_cpu_fdt.h> +#include <dev/uart/uart_dev_ns8250.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#ifdef EXT_RESOURCES +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> +#endif + +#include "uart_if.h" + +struct snps_softc { + struct ns8250_softc ns8250; + +#ifdef EXT_RESOURCES + clk_t baudclk; + clk_t apb_pclk; + hwreset_t reset; +#endif +}; + +static int +snps_uart_attach(struct uart_softc *uart_sc) +{ + struct snps_softc *sc; + + sc = (struct snps_softc *)uart_sc; + + /* UART requires to read USR reg when IIR_BUSY */ + sc->ns8250.busy_detect = 1; + + return (ns8250_bus_attach(uart_sc)); +} + +static kobj_method_t snps_methods[] = { + KOBJMETHOD(uart_probe, ns8250_bus_probe), + KOBJMETHOD(uart_attach, snps_uart_attach), + KOBJMETHOD(uart_detach, ns8250_bus_detach), + KOBJMETHOD(uart_flush, ns8250_bus_flush), + KOBJMETHOD(uart_getsig, ns8250_bus_getsig), + KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl), + KOBJMETHOD(uart_ipend, ns8250_bus_ipend), + KOBJMETHOD(uart_param, ns8250_bus_param), + KOBJMETHOD(uart_receive, ns8250_bus_receive), + KOBJMETHOD(uart_setsig, ns8250_bus_setsig), + KOBJMETHOD(uart_transmit, ns8250_bus_transmit), + KOBJMETHOD(uart_grab, ns8250_bus_grab), + KOBJMETHOD(uart_ungrab, ns8250_bus_ungrab), + KOBJMETHOD_END +}; + +struct uart_class uart_snps_class = { + "snps", + snps_methods, + sizeof(struct snps_softc), + .uc_ops = &uart_ns8250_ops, + .uc_range = 8, + .uc_rclk = 0, +}; + +static struct ofw_compat_data compat_data[] = { + { "snps,dw-apb-uart", (uintptr_t)&uart_snps_class }, + { NULL, (uintptr_t)NULL } +}; +UART_FDT_CLASS(compat_data); + +#ifdef EXT_RESOURCES +static int +snps_get_clocks(device_t dev, clk_t *baudclk, clk_t *apb_pclk) +{ + struct snps_softc *sc; + + sc = device_get_softc(dev); + *baudclk = NULL; + *apb_pclk = NULL; + + /* Baud clock is either named "baudclk", or there is a single + * unnamed clock. + */ + if (clk_get_by_ofw_name(dev, "baudclk", baudclk) != 0 && + clk_get_by_ofw_index(dev, 0, baudclk) != 0) + return (ENOENT); + + /* APB peripheral clock is optional */ + (void)clk_get_by_ofw_name(dev, "apb_pclk", apb_pclk); + + return (0); +} +#endif + +static int +snps_probe(device_t dev) +{ + struct snps_softc *sc; + struct uart_class *uart_class; + phandle_t node; + uint32_t shift, clock; + uint64_t freq; + int error; +#ifdef EXT_RESOURCES + clk_t baudclk, apb_pclk; + hwreset_t reset; +#endif + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + uart_class = (struct uart_class *)ofw_bus_search_compatible(dev, + compat_data)->ocd_data; + if (uart_class == NULL) + return (ENXIO); + + freq = 0; + sc = device_get_softc(dev); + sc->ns8250.base.sc_class = uart_class; + + node = ofw_bus_get_node(dev); + if (OF_getencprop(node, "reg-shift", &shift, sizeof(shift)) <= 0) + shift = 0; + if (OF_getencprop(node, "clock-frequency", &clock, sizeof(clock)) <= 0) + clock = 0; + +#ifdef EXT_RESOURCES + if (hwreset_get_by_ofw_idx(dev, 0, &reset) == 0) { + error = hwreset_deassert(reset); + if (error != 0) { + device_printf(dev, "cannot de-assert reset\n"); + return (error); + } + } + + if (snps_get_clocks(dev, &baudclk, &apb_pclk) == 0) { + error = clk_enable(baudclk); + if (error != 0) { + device_printf(dev, "cannot enable baud clock\n"); + return (error); + } + if (apb_pclk != NULL) { + error = clk_enable(apb_pclk); + if (error != 0) { + device_printf(dev, + "cannot enable peripheral clock\n"); + return (error); + } + } + + if (clock == 0) { + error = clk_get_freq(baudclk, &freq); + if (error != 0) { + device_printf(dev, "cannot get frequency\n"); + return (error); + } + clock = (uint32_t)freq; + } + } +#endif + + if (bootverbose && clock == 0) + device_printf(dev, "could not determine frequency\n"); + + error = uart_bus_probe(dev, (int)shift, (int)clock, 0, 0); + if (error != 0) + return (error); + +#ifdef EXT_RESOURCES + /* XXX uart_bus_probe has changed the softc, so refresh it */ + sc = device_get_softc(dev); + + /* Store clock and reset handles for detach */ + sc->baudclk = baudclk; + sc->apb_pclk = apb_pclk; + sc->reset = reset; +#endif + + return (0); +} + +static int +snps_detach(device_t dev) +{ +#ifdef EXT_RESOURCES + struct snps_softc *sc; + clk_t baudclk, apb_pclk; + hwreset_t reset; +#endif + int error; + +#ifdef EXT_RESOURCES + sc = device_get_softc(dev); + baudclk = sc->baudclk; + apb_pclk = sc->apb_pclk; + reset = sc->reset; +#endif + + error = uart_bus_detach(dev); + if (error != 0) + return (error); + +#ifdef EXT_RESOURCES + if (reset != NULL) { + error = hwreset_assert(reset); + if (error != 0) { + device_printf(dev, "cannot assert reset\n"); + return (error); + } + hwreset_release(reset); + } + if (apb_pclk != NULL) { + error = clk_release(apb_pclk); + if (error != 0) { + device_printf(dev, "cannot release peripheral clock\n"); + return (error); + } + } + if (baudclk != NULL) { + error = clk_release(baudclk); + if (error != 0) { + device_printf(dev, "cannot release baud clock\n"); + return (error); + } + } +#endif + + return (0); +} + +static device_method_t snps_bus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, snps_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, snps_detach), + DEVMETHOD_END +}; + +static driver_t snps_uart_driver = { + uart_driver_name, + snps_bus_methods, + sizeof(struct snps_softc) +}; + +DRIVER_MODULE(uart_snps, simplebus, snps_uart_driver, uart_devclass, 0, 0); diff --git a/sys/dev/usb/wlan/if_urtwn.c b/sys/dev/urtwn/if_urtwn.c index 8b56847..95de7ce 100644 --- a/sys/dev/usb/wlan/if_urtwn.c +++ b/sys/dev/urtwn/if_urtwn.c @@ -78,8 +78,8 @@ __FBSDID("$FreeBSD$"); #include <dev/usb/usb_debug.h> -#include <dev/usb/wlan/if_urtwnreg.h> -#include <dev/usb/wlan/if_urtwnvar.h> +#include <dev/urtwn/if_urtwnreg.h> +#include <dev/urtwn/if_urtwnvar.h> #ifdef USB_DEBUG enum { @@ -95,6 +95,7 @@ enum { URTWN_DEBUG_ROM = 0x00000200, /* various ROM info */ URTWN_DEBUG_KEY = 0x00000400, /* crypto keys management */ URTWN_DEBUG_TXPWR = 0x00000800, /* dump Tx power values */ + URTWN_DEBUG_RSSI = 0x00001000, /* dump RSSI lookups */ URTWN_DEBUG_ANY = 0xffffffff }; @@ -109,6 +110,9 @@ enum { #define IEEE80211_HAS_ADDR4(wh) IEEE80211_IS_DSTODS(wh) +static int urtwn_enable_11n = 1; +TUNABLE_INT("hw.usb.urtwn.enable_11n", &urtwn_enable_11n); + /* various supported device vendors/products */ static const STRUCT_USB_HOST_ID urtwn_devs[] = { #define URTWN_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } @@ -465,6 +469,19 @@ urtwn_match(device_t self) return (usbd_lookup_id_by_uaa(urtwn_devs, sizeof(urtwn_devs), uaa)); } +static void +urtwn_update_chw(struct ieee80211com *ic) +{ +} + +static int +urtwn_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) +{ + + /* We're driving this ourselves (eventually); don't involve net80211 */ + return (0); +} + static int urtwn_attach(device_t self) { @@ -555,7 +572,9 @@ urtwn_attach(device_t self) | IEEE80211_C_HOSTAP /* hostap mode */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ +#if 0 | IEEE80211_C_BGSCAN /* capable of bg scanning */ +#endif | IEEE80211_C_WPA /* 802.11i */ | IEEE80211_C_WME /* 802.11e */ ; @@ -565,9 +584,27 @@ urtwn_attach(device_t self) IEEE80211_CRYPTO_TKIP | IEEE80211_CRYPTO_AES_CCM; + /* Assume they're all 11n capable for now */ + if (urtwn_enable_11n) { + device_printf(self, "enabling 11n\n"); + ic->ic_htcaps = IEEE80211_HTC_HT | + IEEE80211_HTC_AMPDU | + IEEE80211_HTC_AMSDU | + IEEE80211_HTCAP_MAXAMSDU_3839 | + IEEE80211_HTCAP_SMPS_OFF; + /* no HT40 just yet */ + // ic->ic_htcaps |= IEEE80211_HTCAP_CHWIDTH40; + + /* XXX TODO: verify chains versus streams for urtwn */ + ic->ic_txstream = sc->ntxchains; + ic->ic_rxstream = sc->nrxchains; + } + memset(bands, 0, sizeof(bands)); setbit(bands, IEEE80211_MODE_11B); setbit(bands, IEEE80211_MODE_11G); + if (urtwn_enable_11n) + setbit(bands, IEEE80211_MODE_11NG); ieee80211_init_channels(ic, NULL, bands); ieee80211_ifattach(ic); @@ -589,6 +626,8 @@ urtwn_attach(device_t self) sc->sc_node_free = ic->ic_node_free; ic->ic_node_free = urtwn_r88e_node_free; } + ic->ic_update_chw = urtwn_update_chw; + ic->ic_ampdu_enable = urtwn_ampdu_enable; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), URTWN_TX_RADIOTAP_PRESENT, @@ -1005,6 +1044,9 @@ urtwn_rx_frame(struct urtwn_softc *sc, struct mbuf *m, int8_t *rssi_p) tap->wr_tsft &= 0xffffffff00000000; tap->wr_tsft += stat->rxdw5; + /* XXX 20/40? */ + /* XXX shortgi? */ + /* Map HW rate index to 802.11 rate. */ if (!(rxdw3 & R92C_RXDW3_HT)) { tap->wr_rate = ridx2rate[rate]; @@ -1081,6 +1123,8 @@ tr_setup: nf = URTWN_NOISE_FLOOR; if (ni != NULL) { + if (ni->ni_flags & IEEE80211_NODE_HT) + m->m_flags |= M_AMPDU; (void)ieee80211_input(ni, m, rssi - nf, nf); ieee80211_free_node(ni); } else { @@ -1827,7 +1871,7 @@ urtwn_ra_init(struct urtwn_softc *sc) struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni; - struct ieee80211_rateset *rs; + struct ieee80211_rateset *rs, *rs_ht; struct r92c_fw_cmd_macid_cfg cmd; uint32_t rates, basicrates; uint8_t mode; @@ -1835,10 +1879,13 @@ urtwn_ra_init(struct urtwn_softc *sc) ni = ieee80211_ref_node(vap->iv_bss); rs = &ni->ni_rates; + rs_ht = (struct ieee80211_rateset *) &ni->ni_htrates; /* Get normal and basic rates mask. */ rates = basicrates = 0; maxrate = maxbasicrate = 0; + + /* This is for 11bg */ for (i = 0; i < rs->rs_nrates; i++) { /* Convert 802.11 rate to HW rate index. */ for (j = 0; j < nitems(ridx2rate); j++) @@ -1856,10 +1903,32 @@ urtwn_ra_init(struct urtwn_softc *sc) maxbasicrate = j; } } + + /* If we're doing 11n, enable 11n rates */ + if (ni->ni_flags & IEEE80211_NODE_HT) { + for (i = 0; i < rs_ht->rs_nrates; i++) { + if ((rs_ht->rs_rates[i] & 0x7f) > 0xf) + continue; + /* 11n rates start at index 12 */ + j = ((rs_ht->rs_rates[i]) & 0xf) + 12; + rates |= (1 << j); + + /* Guard against the rate table being oddly ordered */ + if (j > maxrate) + maxrate = j; + } + } + +#if 0 + if (ic->ic_curmode == IEEE80211_MODE_11NG) + raid = R92C_RAID_11GN; +#endif + /* NB: group addressed frames are done at 11bg rates for now */ if (ic->ic_curmode == IEEE80211_MODE_11B) mode = R92C_RAID_11B; else mode = R92C_RAID_11BG; + /* XXX misleading 'mode' value here for unicast frames */ URTWN_DPRINTF(sc, URTWN_DEBUG_RA, "%s: mode 0x%x, rates 0x%08x, basicrates 0x%08x\n", __func__, mode, rates, basicrates); @@ -1874,6 +1943,7 @@ urtwn_ra_init(struct urtwn_softc *sc) "could not add broadcast station\n"); return (error); } + /* Set initial MRR rate. */ URTWN_DPRINTF(sc, URTWN_DEBUG_RA, "%s: maxbasicrate %d\n", __func__, maxbasicrate); @@ -1881,6 +1951,12 @@ urtwn_ra_init(struct urtwn_softc *sc) maxbasicrate); /* Set rates mask for unicast frames. */ + if (ni->ni_flags & IEEE80211_NODE_HT) + mode = R92C_RAID_11GN; + else if (ic->ic_curmode == IEEE80211_MODE_11B) + mode = R92C_RAID_11B; + else + mode = R92C_RAID_11BG; cmd.macid = URTWN_MACID_BSS | URTWN_MACID_VALID; cmd.mask = htole32(mode << 28 | rates); error = urtwn_fw_cmd(sc, R92C_CMD_MACID_CONFIG, &cmd, sizeof(cmd)); @@ -1896,7 +1972,11 @@ urtwn_ra_init(struct urtwn_softc *sc) maxrate); /* Indicate highest supported rate. */ - ni->ni_txrate = rs->rs_rates[rs->rs_nrates - 1]; + if (ni->ni_flags & IEEE80211_NODE_HT) + ni->ni_txrate = rs_ht->rs_rates[rs_ht->rs_nrates - 1] + | IEEE80211_RATE_MCS; + else + ni->ni_txrate = rs->rs_rates[rs->rs_nrates - 1]; ieee80211_free_node(ni); return (0); @@ -2559,7 +2639,7 @@ urtwn_update_avgrssi(struct urtwn_softc *sc, int rate, int8_t rssi) sc->avg_pwdb = ((sc->avg_pwdb * 19 + pwdb) / 20) + 1; else sc->avg_pwdb = ((sc->avg_pwdb * 19 + pwdb) / 20); - URTWN_DPRINTF(sc, URTWN_DEBUG_RA, "%s: PWDB %d, EMA %d\n", __func__, + URTWN_DPRINTF(sc, URTWN_DEBUG_RSSI, "%s: PWDB %d, EMA %d\n", __func__, pwdb, sc->avg_pwdb); } @@ -2643,7 +2723,12 @@ urtwn_r88e_get_rssi(struct urtwn_softc *sc, int rate, void *physt) static __inline uint8_t rate2ridx(uint8_t rate) { + if (rate & IEEE80211_RATE_MCS) { + /* 11n rates start at idx 12 */ + return ((rate & 0xf) + 12); + } switch (rate) { + /* 11g */ case 12: return 4; case 18: return 5; case 24: return 6; @@ -2652,6 +2737,7 @@ rate2ridx(uint8_t rate) case 72: return 9; case 96: return 10; case 108: return 11; + /* 11b */ case 2: return 0; case 4: return 1; case 11: return 2; @@ -2711,15 +2797,24 @@ urtwn_tx_data(struct urtwn_softc *sc, struct ieee80211_node *ni, (void) ieee80211_ratectl_rate(ni, NULL, 0); rate = ni->ni_txrate; } else { - if (ic->ic_curmode != IEEE80211_MODE_11B) + /* XXX TODO: drop the default rate for 11b/11g? */ + if (ni->ni_flags & IEEE80211_NODE_HT) + rate = IEEE80211_RATE_MCS | 0x4; /* MCS4 */ + else if (ic->ic_curmode != IEEE80211_MODE_11B) rate = 108; else rate = 22; } } + /* + * XXX TODO: this should be per-node, for 11b versus 11bg + * nodes in hostap mode + */ ridx = rate2ridx(rate); - if (ic->ic_curmode != IEEE80211_MODE_11B) + if (ni->ni_flags & IEEE80211_NODE_HT) + raid = R92C_RAID_11GN; + else if (ic->ic_curmode != IEEE80211_MODE_11B) raid = R92C_RAID_11BG; else raid = R92C_RAID_11B; @@ -2763,7 +2858,10 @@ urtwn_tx_data(struct urtwn_softc *sc, struct ieee80211_node *ni, } else txd->txdw1 |= htole32(R92C_TXDW1_AGGBK); - if (ic->ic_flags & IEEE80211_F_USEPROT) { + /* protmode, non-HT */ + /* XXX TODO: noack frames? */ + if ((rate & 0x80) == 0 && + (ic->ic_flags & IEEE80211_F_USEPROT)) { switch (ic->ic_protmode) { case IEEE80211_PROT_CTSONLY: txd->txdw4 |= htole32( @@ -2779,8 +2877,21 @@ urtwn_tx_data(struct urtwn_softc *sc, struct ieee80211_node *ni, break; } } + + /* protmode, HT */ + /* XXX TODO: noack frames? */ + if ((rate & 0x80) && + (ic->ic_htprotmode == IEEE80211_PROT_RTSCTS)) { + txd->txdw4 |= htole32( + R92C_TXDW4_RTSEN | + R92C_TXDW4_HWRTSEN); + } + + /* XXX TODO: rtsrate is configurable? 24mbit may + * be a bit high for RTS rate? */ txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, URTWN_RIDX_OFDM24)); + txd->txdw5 |= htole32(0x0001ff00); } else /* IEEE80211_FC0_TYPE_MGT */ qsel = R92C_TXDW1_QSEL_MGNT; @@ -2793,14 +2904,21 @@ urtwn_tx_data(struct urtwn_softc *sc, struct ieee80211_node *ni, SM(R92C_TXDW1_QSEL, qsel) | SM(R92C_TXDW1_RAID, raid)); + /* XXX TODO: 40MHZ flag? */ + /* XXX TODO: AMPDU flag? (AGG_ENABLE or AGG_BREAK?) Density shift? */ + /* XXX Short preamble? */ + /* XXX Short-GI? */ + if (sc->chip & URTWN_CHIP_88E) txd->txdw1 |= htole32(SM(R88E_TXDW1_MACID, macid)); else txd->txdw1 |= htole32(SM(R92C_TXDW1_MACID, macid)); txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, ridx)); + /* Force this rate if needed. */ if (URTWN_CHIP_HAS_RATECTL(sc) || ismcast || + (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) || (m->m_flags & M_EAPOL) || type != IEEE80211_FC0_TYPE_DATA) txd->txdw4 |= htole32(R92C_TXDW4_DRVRATE); @@ -2888,6 +3006,8 @@ urtwn_tx_raw(struct urtwn_softc *sc, struct ieee80211_node *ni, } } + /* XXX TODO: 11n checks, matching urtwn_tx_data() */ + wh = mtod(m, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; @@ -2916,6 +3036,7 @@ urtwn_tx_raw(struct urtwn_softc *sc, struct ieee80211_node *ni, else txd->txdw1 |= htole32(SM(R92C_TXDW1_MACID, URTWN_MACID_BC)); + /* XXX TODO: rate index/config (RAID) for 11n? */ txd->txdw1 |= htole32(SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_MGNT)); txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER, cipher)); diff --git a/sys/dev/usb/wlan/if_urtwnreg.h b/sys/dev/urtwn/if_urtwnreg.h index 72835f3..72835f3 100644 --- a/sys/dev/usb/wlan/if_urtwnreg.h +++ b/sys/dev/urtwn/if_urtwnreg.h diff --git a/sys/dev/usb/wlan/if_urtwnvar.h b/sys/dev/urtwn/if_urtwnvar.h index ba8ca81..ba8ca81 100644 --- a/sys/dev/usb/wlan/if_urtwnvar.h +++ b/sys/dev/urtwn/if_urtwnvar.h diff --git a/sys/dev/usb/controller/ehci_pci.c b/sys/dev/usb/controller/ehci_pci.c index 3a6f725..cad351c 100644 --- a/sys/dev/usb/controller/ehci_pci.c +++ b/sys/dev/usb/controller/ehci_pci.c @@ -112,6 +112,8 @@ ehci_pci_match(device_t self) case 0x20951022: return ("AMD CS5536 (Geode) USB 2.0 controller"); + case 0x78081022: + return ("AMD FCH USB 2.0 controller"); case 0x43451002: return "ATI SB200 USB 2.0 controller"; @@ -168,6 +170,10 @@ ehci_pci_match(device_t self) return ("Intel Wildcat Point USB 2.0 controller USB-A"); case 0x8cad8086: return ("Intel Wildcat Point USB 2.0 controller USB-B"); + case 0x8d268086: + return ("Intel Wellsburg USB 2.0 controller"); + case 0x8d2d8086: + return ("Intel Wellsburg USB 2.0 controller"); case 0x9c268086: return ("Intel Lynx Point LP USB 2.0 controller USB"); diff --git a/sys/dev/usb/controller/ohci_pci.c b/sys/dev/usb/controller/ohci_pci.c index c50de54..96950a7 100644 --- a/sys/dev/usb/controller/ohci_pci.c +++ b/sys/dev/usb/controller/ohci_pci.c @@ -124,9 +124,10 @@ ohci_pci_match(device_t self) case 0x740c1022: return ("AMD-756 USB Controller"); - case 0x74141022: return ("AMD-766 USB Controller"); + case 0x78071022: + return ("AMD FCH USB Controller"); case 0x43741002: return "ATI SB400 USB Controller"; diff --git a/sys/dev/usb/controller/uhci_pci.c b/sys/dev/usb/controller/uhci_pci.c index e7e4412..5916996 100644 --- a/sys/dev/usb/controller/uhci_pci.c +++ b/sys/dev/usb/controller/uhci_pci.c @@ -161,6 +161,12 @@ uhci_pci_match(device_t self) case 0x24de8086: return ("Intel 82801EB (ICH5) USB controller USB-D"); + case 0x25a98086: + return ("Intel 6300ESB USB controller USB-A"); + + case 0x25aa8086: + return ("Intel 6300ESB USB controller USB-B"); + case 0x26588086: return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-A"); diff --git a/sys/dev/usb/controller/xhci_pci.c b/sys/dev/usb/controller/xhci_pci.c index 0a8a95b..d4355e9 100644 --- a/sys/dev/usb/controller/xhci_pci.c +++ b/sys/dev/usb/controller/xhci_pci.c @@ -95,6 +95,9 @@ xhci_pci_match(device_t self) uint32_t device_id = pci_get_devid(self); switch (device_id) { + case 0x78141022: + return ("AMD FCH USB 3.0 controller"); + case 0x01941033: return ("NEC uPD720200 USB 3.0 controller"); @@ -115,6 +118,8 @@ xhci_pci_match(device_t self) return ("Intel Lynx Point USB 3.0 controller"); case 0x8cb18086: return ("Intel Wildcat Point USB 3.0 controller"); + case 0x8d318086: + return ("Intel Wellsburg USB 3.0 controller"); case 0x9cb18086: return ("Broadwell Integrated PCH-LP chipset USB 3.0 controller"); diff --git a/sys/dev/usb/usb_busdma.c b/sys/dev/usb/usb_busdma.c index 821e0fb..75378a2 100644 --- a/sys/dev/usb/usb_busdma.c +++ b/sys/dev/usb/usb_busdma.c @@ -472,17 +472,22 @@ usb_pc_common_mem_cb(void *arg, bus_dma_segment_t *segs, pc->page_offset_buf = rem; pc->page_offset_end += rem; #ifdef USB_DEBUG - if (nseg > 1 && - ((segs->ds_addr + segs->ds_len) & (USB_PAGE_SIZE - 1)) != - ((segs + 1)->ds_addr & (USB_PAGE_SIZE - 1))) { - /* - * This check verifies there is no page offset hole - * between the first and second segment. See the - * BUS_DMA_KEEP_PG_OFFSET flag. - */ - DPRINTFN(0, "Page offset was not preserved\n"); - error = 1; - goto done; + if (nseg > 1) { + int x; + + for (x = 0; x != nseg - 1; x++) { + if (((segs[x].ds_addr + segs[x].ds_len) & (USB_PAGE_SIZE - 1)) == + ((segs[x + 1].ds_addr & (USB_PAGE_SIZE - 1)))) + continue; + /* + * This check verifies there is no page offset + * hole between any of the segments. See the + * BUS_DMA_KEEP_PG_OFFSET flag. + */ + DPRINTFN(0, "Page offset was not preserved\n"); + error = 1; + goto done; + } } #endif while (pc->ismultiseg) { diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs index 07b3f73..7f75d5a 100644 --- a/sys/dev/usb/usbdevs +++ b/sys/dev/usb/usbdevs @@ -1700,6 +1700,7 @@ product DISPLAYLINK UM7X0 0x401a nanovision MiMo product DISPLAYLINK LT1421 0x03e0 Lenovo ThinkVision LT1421 product DISPLAYLINK POLARIS2 0x0117 Polaris2 USB dock product DISPLAYLINK PLUGABLE 0x0377 Plugable docking station +product DISPLAYLINK ITEC 0x02e9 i-tec USB 2.0 Docking Station /* DMI products */ product DMI CFSM_RW 0xa109 CF/SM Reader/Writer diff --git a/sys/dev/usb/video/udl.c b/sys/dev/usb/video/udl.c index 0d7c504..1096ed3 100644 --- a/sys/dev/usb/video/udl.c +++ b/sys/dev/usb/video/udl.c @@ -177,7 +177,8 @@ static const STRUCT_USB_HOST_ID udl_devs[] = { {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_PLUGABLE, DL160)}, {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LUM70, DL125)}, {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_POLARIS2, DLUNK)}, - {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LT1421, DLUNK)} + {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LT1421, DLUNK)}, + {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_ITEC, DL165)}, }; static void diff --git a/sys/dev/usb/wlan/if_rum.c b/sys/dev/usb/wlan/if_rum.c index e428a8f..08be717 100644 --- a/sys/dev/usb/wlan/if_rum.c +++ b/sys/dev/usb/wlan/if_rum.c @@ -154,6 +154,7 @@ static usb_callback_t rum_bulk_write_callback; static usb_error_t rum_do_request(struct rum_softc *sc, struct usb_device_request *req, void *data); +static usb_error_t rum_do_mcu_request(struct rum_softc *sc, int); static struct ieee80211vap *rum_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], @@ -165,6 +166,11 @@ static int rum_cmd_sleepable(struct rum_softc *, const void *, static void rum_tx_free(struct rum_tx_data *, int); static void rum_setup_tx_list(struct rum_softc *); static void rum_unsetup_tx_list(struct rum_softc *); +static void rum_beacon_miss(struct ieee80211vap *); +static void rum_sta_recv_mgmt(struct ieee80211_node *, + struct mbuf *, int, + const struct ieee80211_rx_stats *, int, int); +static int rum_set_power_state(struct rum_softc *, int); static int rum_newstate(struct ieee80211vap *, enum ieee80211_state, int); static uint8_t rum_crypto_mode(struct rum_softc *, u_int, int); @@ -232,6 +238,8 @@ static int rum_init(struct rum_softc *); static void rum_stop(struct rum_softc *); static void rum_load_microcode(struct rum_softc *, const uint8_t *, size_t); +static int rum_set_sleep_time(struct rum_softc *, uint16_t); +static int rum_reset(struct ieee80211vap *, u_long); static int rum_set_beacon(struct rum_softc *, struct ieee80211vap *); static int rum_alloc_beacon(struct rum_softc *, @@ -530,6 +538,8 @@ rum_attach(device_t self) | IEEE80211_C_BGSCAN /* bg scanning supported */ | IEEE80211_C_WPA /* 802.11i */ | IEEE80211_C_WME /* 802.11e */ + | IEEE80211_C_PMGT /* Station-side power mgmt */ + | IEEE80211_C_SWSLEEP /* net80211 managed power mgmt */ ; ic->ic_cryptocaps = @@ -629,6 +639,20 @@ rum_do_request(struct rum_softc *sc, return (err); } +static usb_error_t +rum_do_mcu_request(struct rum_softc *sc, int request) +{ + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RT2573_MCU_CNTL; + USETW(req.wValue, request); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + return (rum_do_request(sc, &req, NULL)); +} + static struct ieee80211vap * rum_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, @@ -659,8 +683,24 @@ rum_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, vap->iv_key_set = rum_key_set; vap->iv_key_delete = rum_key_delete; vap->iv_update_beacon = rum_update_beacon; + vap->iv_reset = rum_reset; vap->iv_max_aid = RT2573_ADDR_MAX; + if (opmode == IEEE80211_M_STA) { + /* + * Move device to the sleep state when + * beacon is received and there is no data for us. + * + * Used only for IEEE80211_S_SLEEP state. + */ + rvp->recv_mgmt = vap->iv_recv_mgmt; + vap->iv_recv_mgmt = rum_sta_recv_mgmt; + + /* Ignored while sleeping. */ + rvp->bmiss = vap->iv_bmiss; + vap->iv_bmiss = rum_beacon_miss; + } + usb_callout_init_mtx(&rvp->ratectl_ch, &sc->sc_mtx, 0); TASK_INIT(&rvp->ratectl_task, 0, rum_ratectl_task, rvp); ieee80211_ratectl_init(vap); @@ -795,6 +835,89 @@ rum_unsetup_tx_list(struct rum_softc *sc) } } +static void +rum_beacon_miss(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct rum_softc *sc = ic->ic_softc; + struct rum_vap *rvp = RUM_VAP(vap); + int sleep; + + RUM_LOCK(sc); + if (sc->sc_sleeping && sc->sc_sleep_end < ticks) { + DPRINTFN(12, "dropping 'sleeping' bit, " + "device must be awake now\n"); + + sc->sc_sleeping = 0; + } + + sleep = sc->sc_sleeping; + RUM_UNLOCK(sc); + + if (!sleep) + rvp->bmiss(vap); +#ifdef USB_DEBUG + else + DPRINTFN(13, "bmiss event is ignored whilst sleeping\n"); +#endif +} + +static void +rum_sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, + const struct ieee80211_rx_stats *rxs, + int rssi, int nf) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct rum_softc *sc = vap->iv_ic->ic_softc; + struct rum_vap *rvp = RUM_VAP(vap); + + if (vap->iv_state == IEEE80211_S_SLEEP && + subtype == IEEE80211_FC0_SUBTYPE_BEACON) { + RUM_LOCK(sc); + DPRINTFN(12, "beacon, mybss %d (flags %02X)\n", + !!(sc->last_rx_flags & RT2573_RX_MYBSS), + sc->last_rx_flags); + + if ((sc->last_rx_flags & (RT2573_RX_MYBSS | RT2573_RX_BC)) == + (RT2573_RX_MYBSS | RT2573_RX_BC)) { + /* + * Put it to sleep here; in case if there is a data + * for us, iv_recv_mgmt() will wakeup the device via + * SLEEP -> RUN state transition. + */ + rum_set_power_state(sc, 1); + } + RUM_UNLOCK(sc); + } + + rvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf); +} + +static int +rum_set_power_state(struct rum_softc *sc, int sleep) +{ + usb_error_t uerror; + + RUM_LOCK_ASSERT(sc); + + DPRINTFN(12, "moving to %s state (sleep time %u)\n", + sleep ? "sleep" : "awake", sc->sc_sleep_time); + + uerror = rum_do_mcu_request(sc, + sleep ? RT2573_MCU_SLEEP : RT2573_MCU_WAKEUP); + if (uerror != USB_ERR_NORMAL_COMPLETION) { + device_printf(sc->sc_dev, + "%s: could not change power state: %s\n", + __func__, usbd_errstr(uerror)); + return (EIO); + } + + sc->sc_sleeping = !!sleep; + sc->sc_sleep_end = sleep ? ticks + sc->sc_sleep_time : 0; + + return (0); +} + static int rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { @@ -804,7 +927,8 @@ rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) const struct ieee80211_txparam *tp; enum ieee80211_state ostate; struct ieee80211_node *ni; - int ret; + usb_error_t uerror; + int ret = 0; ostate = vap->iv_state; DPRINTF("%s -> %s\n", @@ -815,6 +939,17 @@ rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) RUM_LOCK(sc); usb_callout_stop(&rvp->ratectl_ch); + if (ostate == IEEE80211_S_SLEEP && vap->iv_opmode == IEEE80211_M_STA) { + rum_clrbits(sc, RT2573_TXRX_CSR4, RT2573_ACKCTS_PWRMGT); + rum_clrbits(sc, RT2573_MAC_CSR11, RT2573_AUTO_WAKEUP); + + /* + * Ignore any errors; + * any subsequent TX will wakeup it anyway + */ + (void) rum_set_power_state(sc, 0); + } + switch (nstate) { case IEEE80211_S_INIT: if (ostate == IEEE80211_S_RUN) @@ -823,6 +958,9 @@ rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) break; case IEEE80211_S_RUN: + if (ostate == IEEE80211_S_SLEEP) + break; /* already handled */ + ni = ieee80211_ref_node(vap->iv_bss); if (vap->iv_opmode != IEEE80211_M_MONITOR) { @@ -857,20 +995,39 @@ rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) rum_ratectl_start(sc, ni); +run_fail: ieee80211_free_node(ni); break; + case IEEE80211_S_SLEEP: + /* Implemented for STA mode only. */ + if (vap->iv_opmode != IEEE80211_M_STA) + break; + + uerror = rum_setbits(sc, RT2573_MAC_CSR11, RT2573_AUTO_WAKEUP); + if (uerror != USB_ERR_NORMAL_COMPLETION) { + ret = EIO; + break; + } + + uerror = rum_setbits(sc, RT2573_TXRX_CSR4, RT2573_ACKCTS_PWRMGT); + if (uerror != USB_ERR_NORMAL_COMPLETION) { + ret = EIO; + break; + } + + ret = rum_set_power_state(sc, 1); + if (ret != 0) { + device_printf(sc->sc_dev, + "%s: could not move to the SLEEP state: %s\n", + __func__, usbd_errstr(uerror)); + } + break; default: break; } RUM_UNLOCK(sc); IEEE80211_LOCK(ic); - return (rvp->newstate(vap, nstate, arg)); - -run_fail: - RUM_UNLOCK(sc); - IEEE80211_LOCK(ic); - ieee80211_free_node(ni); - return ret; + return (ret == 0 ? rvp->newstate(vap, nstate, arg) : ret); } static void @@ -1001,6 +1158,7 @@ rum_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) rssi = rum_get_rssi(sc, sc->sc_rx_desc.rssi); flags = le32toh(sc->sc_rx_desc.flags); + sc->last_rx_flags = flags; if (flags & RT2573_RX_CRC_ERROR) { /* * This should not happen since we did not @@ -1995,6 +2153,7 @@ rum_enable_tsf_sync(struct rum_softc *sc) struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t tmp; + uint16_t bintval; if (vap->iv_opmode != IEEE80211_M_STA) { /* @@ -2008,7 +2167,8 @@ rum_enable_tsf_sync(struct rum_softc *sc) tmp = rum_read(sc, RT2573_TXRX_CSR9) & 0xff000000; /* set beacon interval (in 1/16ms unit) */ - tmp |= vap->iv_bss->ni_intval * 16; + bintval = vap->iv_bss->ni_intval; + tmp |= bintval * 16; tmp |= RT2573_TSF_TIMER_EN | RT2573_TBTT_TIMER_EN; switch (vap->iv_opmode) { @@ -2042,7 +2202,8 @@ rum_enable_tsf_sync(struct rum_softc *sc) if (rum_write(sc, RT2573_TXRX_CSR9, tmp) != 0) return EIO; - return 0; + /* refresh current sleep time */ + return (rum_set_sleep_time(sc, bintval)); } static void @@ -2460,7 +2621,6 @@ rum_stop(struct rum_softc *sc) static void rum_load_microcode(struct rum_softc *sc, const uint8_t *ucode, size_t size) { - struct usb_device_request req; uint16_t reg = RT2573_MCU_CODE_BASE; usb_error_t err; @@ -2475,14 +2635,8 @@ rum_load_microcode(struct rum_softc *sc, const uint8_t *ucode, size_t size) } } - req.bmRequestType = UT_WRITE_VENDOR_DEVICE; - req.bRequest = RT2573_MCU_CNTL; - USETW(req.wValue, RT2573_MCU_RUN); - USETW(req.wIndex, 0); - USETW(req.wLength, 0); - - err = rum_do_request(sc, &req, NULL); - if (err != 0) { + err = rum_do_mcu_request(sc, RT2573_MCU_RUN); + if (err != USB_ERR_NORMAL_COMPLETION) { device_printf(sc->sc_dev, "could not run firmware: %s\n", usbd_errstr(err)); } @@ -2492,6 +2646,72 @@ rum_load_microcode(struct rum_softc *sc, const uint8_t *ucode, size_t size) } static int +rum_set_sleep_time(struct rum_softc *sc, uint16_t bintval) +{ + struct ieee80211com *ic = &sc->sc_ic; + usb_error_t uerror; + int exp, delay; + + RUM_LOCK_ASSERT(sc); + + exp = ic->ic_lintval / bintval; + delay = ic->ic_lintval % bintval; + + if (exp > RT2573_TBCN_EXP_MAX) + exp = RT2573_TBCN_EXP_MAX; + if (delay > RT2573_TBCN_DELAY_MAX) + delay = RT2573_TBCN_DELAY_MAX; + + uerror = rum_modbits(sc, RT2573_MAC_CSR11, + RT2573_TBCN_EXP(exp) | + RT2573_TBCN_DELAY(delay), + RT2573_TBCN_EXP(RT2573_TBCN_EXP_MAX) | + RT2573_TBCN_DELAY(RT2573_TBCN_DELAY_MAX)); + + if (uerror != USB_ERR_NORMAL_COMPLETION) + return (EIO); + + sc->sc_sleep_time = IEEE80211_TU_TO_TICKS(exp * bintval + delay); + + return (0); +} + +static int +rum_reset(struct ieee80211vap *vap, u_long cmd) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni; + struct rum_softc *sc = ic->ic_softc; + int error; + + switch (cmd) { + case IEEE80211_IOC_POWERSAVE: + error = 0; + break; + case IEEE80211_IOC_POWERSAVESLEEP: + ni = ieee80211_ref_node(vap->iv_bss); + + RUM_LOCK(sc); + error = rum_set_sleep_time(sc, ni->ni_intval); + if (vap->iv_state == IEEE80211_S_SLEEP) { + /* Use new values for wakeup timer. */ + rum_clrbits(sc, RT2573_MAC_CSR11, RT2573_AUTO_WAKEUP); + rum_setbits(sc, RT2573_MAC_CSR11, RT2573_AUTO_WAKEUP); + } + /* XXX send reassoc */ + RUM_UNLOCK(sc); + + ieee80211_free_node(ni); + break; + default: + error = ENETRESET; + break; + } + + return (error); +} + +static int rum_set_beacon(struct rum_softc *sc, struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; @@ -2924,14 +3144,15 @@ rum_scan_end(struct ieee80211com *ic) { struct rum_softc *sc = ic->ic_softc; - RUM_LOCK(sc); - if (ic->ic_opmode != IEEE80211_M_AHDEMO) - rum_enable_tsf_sync(sc); - else - rum_enable_tsf(sc); - rum_set_bssid(sc, sc->sc_bssid); - RUM_UNLOCK(sc); - + if (ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) { + RUM_LOCK(sc); + if (ic->ic_opmode != IEEE80211_M_AHDEMO) + rum_enable_tsf_sync(sc); + else + rum_enable_tsf(sc); + rum_set_bssid(sc, sc->sc_bssid); + RUM_UNLOCK(sc); + } } static void diff --git a/sys/dev/usb/wlan/if_rumreg.h b/sys/dev/usb/wlan/if_rumreg.h index 06c0a81..96726b1 100644 --- a/sys/dev/usb/wlan/if_rumreg.h +++ b/sys/dev/usb/wlan/if_rumreg.h @@ -136,6 +136,13 @@ /* possible flags for register MAC_CSR5 */ #define RT2573_NUM_BSSID_MSK(n) (((n * 3) & 3) << 16) +/* possible flags for register MAC_CSR11 */ +#define RT2573_AUTO_WAKEUP (1 << 15) +#define RT2573_TBCN_EXP(n) ((n) << 8) +#define RT2573_TBCN_EXP_MAX 0x7f +#define RT2573_TBCN_DELAY(t) (t) +#define RT2573_TBCN_DELAY_MAX 0xff + /* possible flags for register TXRX_CSR0 */ /* Tx filter flags are in the low 16 bits */ #define RT2573_AUTO_TX_SEQ (1 << 15) @@ -152,6 +159,7 @@ #define RT2573_DROP_ACKCTS (1 << 25) /* possible flags for register TXRX_CSR4 */ +#define RT2573_ACKCTS_PWRMGT (1 << 16) #define RT2573_SHORT_PREAMBLE (1 << 18) #define RT2573_MRR_ENABLED (1 << 19) #define RT2573_MRR_CCK_FALLBACK (1 << 22) @@ -188,7 +196,10 @@ #define RT2573_LED_ON 0x1e1e #define RT2573_LED_OFF 0x0 -#define RT2573_MCU_RUN (1 << 3) +/* USB vendor requests */ +#define RT2573_MCU_SLEEP 7 +#define RT2573_MCU_RUN 8 +#define RT2573_MCU_WAKEUP 9 #define RT2573_SMART_MODE (1 << 0) diff --git a/sys/dev/usb/wlan/if_rumvar.h b/sys/dev/usb/wlan/if_rumvar.h index d494468..6c42569 100644 --- a/sys/dev/usb/wlan/if_rumvar.h +++ b/sys/dev/usb/wlan/if_rumvar.h @@ -96,6 +96,11 @@ struct rum_vap { int (*newstate)(struct ieee80211vap *, enum ieee80211_state, int); + void (*bmiss)(struct ieee80211vap *); + void (*recv_mgmt)(struct ieee80211_node *, + struct mbuf *, int, + const struct ieee80211_rx_stats *, + int, int); }; #define RUM_VAP(vap) ((struct rum_vap *)(vap)) @@ -124,6 +129,10 @@ struct rum_softc { struct mtx sc_mtx; + int sc_sleep_end; + int sc_sleep_time; + uint8_t last_rx_flags; + struct rum_cmdq cmdq[RUM_CMDQ_SIZE]; struct mtx cmdq_mtx; struct task cmdq_task; @@ -135,6 +144,7 @@ struct rum_softc { uint8_t txpow[44]; u_int sc_detached:1, sc_running:1, + sc_sleeping:1, sc_clr_shkeys:1; uint8_t sc_bssid[IEEE80211_ADDR_LEN]; diff --git a/sys/dev/vnic/nic_main.c b/sys/dev/vnic/nic_main.c index 0b21952..ae04326 100644 --- a/sys/dev/vnic/nic_main.c +++ b/sys/dev/vnic/nic_main.c @@ -119,7 +119,7 @@ static int nicpf_detach(device_t); #ifdef PCI_IOV static int nicpf_iov_init(device_t, uint16_t, const nvlist_t *); static void nicpf_iov_uninit(device_t); -static int nicpf_iov_addr_vf(device_t, uint16_t, const nvlist_t *); +static int nicpf_iov_add_vf(device_t, uint16_t, const nvlist_t *); #endif static device_method_t nicpf_methods[] = { @@ -131,7 +131,7 @@ static device_method_t nicpf_methods[] = { #ifdef PCI_IOV DEVMETHOD(pci_iov_init, nicpf_iov_init), DEVMETHOD(pci_iov_uninit, nicpf_iov_uninit), - DEVMETHOD(pci_iov_add_vf, nicpf_iov_addr_vf), + DEVMETHOD(pci_iov_add_vf, nicpf_iov_add_vf), #endif DEVMETHOD_END, }; @@ -269,18 +269,9 @@ nicpf_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t *params) nic = device_get_softc(dev); - nic->num_vf_en = 0; if (num_vfs == 0) return (ENXIO); - if (num_vfs > MAX_NUM_VFS_SUPPORTED) - return (EINVAL); - /* - * Just set variables here. - * The number of VFs will be written to configuration - * space later in PCI_ADD_VF(). - */ - nic->num_vf_en = num_vfs; nic->flags |= NIC_SRIOV_ENABLED; return (0); @@ -294,7 +285,7 @@ nicpf_iov_uninit(device_t dev) } static int -nicpf_iov_addr_vf(device_t dev, uint16_t vfnum, const nvlist_t *params) +nicpf_iov_add_vf(device_t dev, uint16_t vfnum, const nvlist_t *params) { const void *mac; struct nicpf *nic; @@ -306,6 +297,9 @@ nicpf_iov_addr_vf(device_t dev, uint16_t vfnum, const nvlist_t *params) if ((nic->flags & NIC_SRIOV_ENABLED) == 0) return (ENXIO); + if (vfnum > (nic->num_vf_en - 1)) + return (EINVAL); + if (nvlist_exists_binary(params, "mac-addr") != 0) { mac = nvlist_get_binary(params, "mac-addr", &size); bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vfnum]); @@ -1094,11 +1088,8 @@ static int nic_sriov_init(device_t dev, struct nicpf *nic) } /* Fix-up the number of enabled VFs */ total_vf_cnt = pci_read_config(dev, iov_pos + PCIR_SRIOV_TOTAL_VFS, 2); - if (total_vf_cnt < nic->num_vf_en) - nic->num_vf_en = total_vf_cnt; - if (total_vf_cnt == 0) - return (0); + return (ENXIO); /* Attach SR-IOV */ pf_schema = pci_iov_schema_alloc_node(); @@ -1116,7 +1107,6 @@ static int nic_sriov_init(device_t dev, struct nicpf *nic) device_printf(dev, "Failed to initialize SR-IOV (error=%d)\n", err); - nic->num_vf_en = 0; return (err); } #endif diff --git a/sys/dev/vnic/nicvf_main.c b/sys/dev/vnic/nicvf_main.c index 4625846..5b74ac9 100644 --- a/sys/dev/vnic/nicvf_main.c +++ b/sys/dev/vnic/nicvf_main.c @@ -661,12 +661,6 @@ nicvf_if_transmit(struct ifnet *ifp, struct mbuf *mbuf) sq = &qs->sq[qidx]; - if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != - IFF_DRV_RUNNING) { - err = drbr_enqueue(ifp, sq->br, mbuf); - return (err); - } - if (mbuf->m_next != NULL && (mbuf->m_pkthdr.csum_flags & (CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_SCTP)) != 0) { @@ -679,17 +673,23 @@ nicvf_if_transmit(struct ifnet *ifp, struct mbuf *mbuf) } } + err = drbr_enqueue(ifp, sq->br, mbuf); + if (((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING) || !nic->link_up || (err != 0)) { + /* + * Try to enqueue packet to the ring buffer. + * If the driver is not active, link down or enqueue operation + * failed, return with the appropriate error code. + */ + return (err); + } + if (NICVF_TX_TRYLOCK(sq) != 0) { - err = nicvf_tx_mbuf_locked(sq, mbuf); + err = nicvf_xmit_locked(sq); NICVF_TX_UNLOCK(sq); return (err); - } else { - err = drbr_enqueue(ifp, sq->br, mbuf); - if (err != 0) - return (err); - + } else taskqueue_enqueue(sq->snd_taskq, &sq->snd_task); - } return (0); } diff --git a/sys/dev/vnic/nicvf_queues.c b/sys/dev/vnic/nicvf_queues.c index 13ea636..9b46d7c 100644 --- a/sys/dev/vnic/nicvf_queues.c +++ b/sys/dev/vnic/nicvf_queues.c @@ -48,7 +48,6 @@ __FBSDID("$FreeBSD$"); #include <sys/proc.h> #include <sys/sockio.h> #include <sys/socket.h> -#include <sys/stdatomic.h> #include <sys/cpuset.h> #include <sys/lock.h> #include <sys/mutex.h> @@ -61,11 +60,12 @@ __FBSDID("$FreeBSD$"); #include <machine/bus.h> #include <machine/vmparam.h> -#include <net/ethernet.h> #include <net/if.h> #include <net/if_var.h> #include <net/if_media.h> #include <net/ifq.h> +#include <net/bpf.h> +#include <net/ethernet.h> #include <netinet/in_systm.h> #include <netinet/in.h> @@ -106,6 +106,8 @@ static void nicvf_cmp_queue_config(struct nicvf *, struct queue_set *, int, boolean_t); static void nicvf_sq_free_used_descs(struct nicvf *, struct snd_queue *, int); +static int nicvf_tx_mbuf_locked(struct snd_queue *, struct mbuf **); + static void nicvf_rbdr_task(void *, int); static void nicvf_rbdr_task_nowait(void *, int); @@ -740,10 +742,10 @@ nicvf_cq_intr_handler(struct nicvf *nic, uint8_t cq_idx) int cqe_count, cqe_head; struct queue_set *qs = nic->qs; struct cmp_queue *cq = &qs->cq[cq_idx]; + struct snd_queue *sq = &qs->sq[cq_idx]; struct rcv_queue *rq; struct cqe_rx_t *cq_desc; struct lro_ctrl *lro; - struct lro_entry *queued; int rq_idx; int cmp_err; @@ -819,6 +821,7 @@ done: ((if_getdrvflags(nic->ifp) & IFF_DRV_RUNNING) != 0)) { /* Reenable TXQ if its stopped earlier due to SQ full */ if_setdrvflagbits(nic->ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); + taskqueue_enqueue(sq->snd_taskq, &sq->snd_task); } out: /* @@ -827,10 +830,7 @@ out: rq_idx = cq_idx; rq = &nic->qs->rq[rq_idx]; lro = &rq->lro; - while ((queued = SLIST_FIRST(&lro->lro_active)) != NULL) { - SLIST_REMOVE_HEAD(&lro->lro_active, next); - tcp_lro_flush(lro, queued); - } + tcp_lro_flush_all(lro); NICVF_CMP_UNLOCK(cq); @@ -992,25 +992,62 @@ nicvf_free_cmp_queue(struct nicvf *nic, struct cmp_queue *cq) memset(cq->mtx_name, 0, sizeof(cq->mtx_name)); } -static void -nicvf_snd_task(void *arg, int pending) +int +nicvf_xmit_locked(struct snd_queue *sq) { - struct snd_queue *sq = (struct snd_queue *)arg; - struct mbuf *mbuf; + struct nicvf *nic; + struct ifnet *ifp; + struct mbuf *next; + int err; - NICVF_TX_LOCK(sq); - while (1) { - mbuf = drbr_dequeue(NULL, sq->br); - if (mbuf == NULL) - break; + NICVF_TX_LOCK_ASSERT(sq); + + nic = sq->nic; + ifp = nic->ifp; + err = 0; + + while ((next = drbr_peek(ifp, sq->br)) != NULL) { + err = nicvf_tx_mbuf_locked(sq, &next); + if (err != 0) { + if (next == NULL) + drbr_advance(ifp, sq->br); + else + drbr_putback(ifp, sq->br, next); - if (nicvf_tx_mbuf_locked(sq, mbuf) != 0) { - /* XXX ARM64TODO: Increase Tx drop counter */ - m_freem(mbuf); break; } + drbr_advance(ifp, sq->br); + /* Send a copy of the frame to the BPF listener */ + ETHER_BPF_MTAP(ifp, next); } + return (err); +} + +static void +nicvf_snd_task(void *arg, int pending) +{ + struct snd_queue *sq = (struct snd_queue *)arg; + struct nicvf *nic; + struct ifnet *ifp; + int err; + + nic = sq->nic; + ifp = nic->ifp; + + /* + * Skip sending anything if the driver is not running, + * SQ full or link is down. + */ + if (((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING) || !nic->link_up) + return; + + NICVF_TX_LOCK(sq); + err = nicvf_xmit_locked(sq); NICVF_TX_UNLOCK(sq); + /* Try again */ + if (err != 0) + taskqueue_enqueue(sq->snd_taskq, &sq->snd_task); } /* Initialize transmit queue */ @@ -1048,7 +1085,7 @@ nicvf_init_snd_queue(struct nicvf *nic, struct snd_queue *sq, int q_len, sq->desc = sq->dmem.base; sq->head = sq->tail = 0; - atomic_store_rel_int(&sq->free_cnt, q_len - 1); + sq->free_cnt = q_len - 1; sq->thresh = SND_QUEUE_THRESH; sq->idx = qidx; sq->nic = nic; @@ -1640,7 +1677,7 @@ nicvf_get_sq_desc(struct snd_queue *sq, int desc_cnt) int qentry; qentry = sq->tail; - atomic_subtract_int(&sq->free_cnt, desc_cnt); + sq->free_cnt -= desc_cnt; sq->tail += desc_cnt; sq->tail &= (sq->dmem.q_len - 1); @@ -1652,7 +1689,7 @@ static void nicvf_put_sq_desc(struct snd_queue *sq, int desc_cnt) { - atomic_add_int(&sq->free_cnt, desc_cnt); + sq->free_cnt += desc_cnt; sq->head += desc_cnt; sq->head &= (sq->dmem.q_len - 1); } @@ -1772,7 +1809,6 @@ nicvf_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry, } ip = (struct ip *)(mbuf->m_data + ehdrlen); - ip->ip_sum = 0; iphlen = ip->ip_hl << 2; poff = ehdrlen + iphlen; @@ -1864,8 +1900,8 @@ static inline void nicvf_sq_add_gather_subdesc(struct snd_queue *sq, int qentry, } /* Put an mbuf to a SQ for packet transfer. */ -int -nicvf_tx_mbuf_locked(struct snd_queue *sq, struct mbuf *mbuf) +static int +nicvf_tx_mbuf_locked(struct snd_queue *sq, struct mbuf **mbufp) { bus_dma_segment_t segs[256]; struct nicvf *nic; @@ -1883,15 +1919,17 @@ nicvf_tx_mbuf_locked(struct snd_queue *sq, struct mbuf *mbuf) snd_buff = &sq->snd_buff[sq->tail]; err = bus_dmamap_load_mbuf_sg(sq->snd_buff_dmat, snd_buff->dmap, - mbuf, segs, &nsegs, BUS_DMA_NOWAIT); - if (err != 0) { + *mbufp, segs, &nsegs, BUS_DMA_NOWAIT); + if (__predict_false(err != 0)) { /* ARM64TODO: Add mbuf defragmenting if we lack maps */ + m_freem(*mbufp); + *mbufp = NULL; return (err); } /* Set how many subdescriptors is required */ nic = sq->nic; - if (mbuf->m_pkthdr.tso_segsz != 0 && nic->hw_tso) + if ((*mbufp)->m_pkthdr.tso_segsz != 0 && nic->hw_tso) subdesc_cnt = MIN_SQ_DESC_PER_PKT_XMIT; else subdesc_cnt = MIN_SQ_DESC_PER_PKT_XMIT + nsegs - 1; @@ -1905,10 +1943,15 @@ nicvf_tx_mbuf_locked(struct snd_queue *sq, struct mbuf *mbuf) qentry = nicvf_get_sq_desc(sq, subdesc_cnt); /* Add SQ header subdesc */ - err = nicvf_sq_add_hdr_subdesc(sq, qentry, subdesc_cnt - 1, mbuf, - mbuf->m_pkthdr.len); + err = nicvf_sq_add_hdr_subdesc(sq, qentry, subdesc_cnt - 1, *mbufp, + (*mbufp)->m_pkthdr.len); if (err != 0) { + nicvf_put_sq_desc(sq, subdesc_cnt); bus_dmamap_unload(sq->snd_buff_dmat, snd_buff->dmap); + if (err == ENOBUFS) { + m_freem(*mbufp); + *mbufp = NULL; + } return (err); } @@ -1985,19 +2028,23 @@ nicvf_get_rcv_mbuf(struct nicvf *nic, struct cqe_rx_t *cqe_rx) /* * HW by default verifies IP & TCP/UDP/SCTP checksums */ - - /* XXX: Do we need to include IP with options too? */ - if (__predict_true(cqe_rx->l3_type == L3TYPE_IPV4 || - cqe_rx->l3_type == L3TYPE_IPV6)) { + if (__predict_true(cqe_rx->l3_type == L3TYPE_IPV4)) { mbuf->m_pkthdr.csum_flags = (CSUM_IP_CHECKED | CSUM_IP_VALID); } - if (cqe_rx->l4_type == L4TYPE_TCP || - cqe_rx->l4_type == L4TYPE_UDP || - cqe_rx->l4_type == L4TYPE_SCTP) { + + switch (cqe_rx->l4_type) { + case L4TYPE_UDP: + case L4TYPE_TCP: /* fall through */ mbuf->m_pkthdr.csum_flags |= (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); - mbuf->m_pkthdr.csum_data = htons(0xffff); + mbuf->m_pkthdr.csum_data = 0xffff; + break; + case L4TYPE_SCTP: + mbuf->m_pkthdr.csum_flags |= CSUM_SCTP_VALID; + break; + default: + break; } } } diff --git a/sys/dev/vnic/nicvf_queues.h b/sys/dev/vnic/nicvf_queues.h index 8a27880..0fe8e3b 100644 --- a/sys/dev/vnic/nicvf_queues.h +++ b/sys/dev/vnic/nicvf_queues.h @@ -388,7 +388,7 @@ void nicvf_disable_intr(struct nicvf *, int, int); void nicvf_clear_intr(struct nicvf *, int, int); int nicvf_is_intr_enabled(struct nicvf *, int, int); -int nicvf_tx_mbuf_locked(struct snd_queue *, struct mbuf *); +int nicvf_xmit_locked(struct snd_queue *sq); /* Register access APIs */ void nicvf_reg_write(struct nicvf *, uint64_t, uint64_t); diff --git a/sys/dev/vnic/thunder_bgx_fdt.c b/sys/dev/vnic/thunder_bgx_fdt.c index ec6e68f..b4d77cf 100644 --- a/sys/dev/vnic/thunder_bgx_fdt.c +++ b/sys/dev/vnic/thunder_bgx_fdt.c @@ -285,18 +285,9 @@ bgx_fdt_init_phy(struct bgx *bgx) continue; } - if (OF_getencprop(child, "phy-handle", &phy, - sizeof(phy)) <= 0) { - if (bootverbose) { - device_printf(bgx->dev, - "No phy-handle in PHY node. Skipping...\n"); - } - continue; - } /* Acquire PHY address */ - phy = OF_node_from_xref(phy); - if (OF_getencprop(phy, "reg", &bgx->lmac[lmac].phyaddr, + if (OF_getencprop(child, "reg", &bgx->lmac[lmac].phyaddr, sizeof(bgx->lmac[lmac].phyaddr)) <= 0) { if (bootverbose) { device_printf(bgx->dev, @@ -305,6 +296,15 @@ bgx_fdt_init_phy(struct bgx *bgx) bgx->lmac[lmac].phyaddr = MII_PHY_ANY; } + if (OF_getencprop(child, "phy-handle", &phy, + sizeof(phy)) <= 0) { + if (bootverbose) { + device_printf(bgx->dev, + "No phy-handle in PHY node. Skipping...\n"); + } + continue; + } + phy = OF_instance_to_package(phy); /* * Get PHY interface (MDIO bus) device. * Driver must be already attached. @@ -321,7 +321,7 @@ bgx_fdt_init_phy(struct bgx *bgx) } /* Get mac address from FDT */ - bgx_fdt_get_macaddr(phy, bgx->lmac[lmac].mac); + bgx_fdt_get_macaddr(child, bgx->lmac[lmac].mac); bgx->lmac[lmac].lmacid = lmac; lmac++; diff --git a/sys/dev/vt/hw/vga/vt_vga.c b/sys/dev/vt/hw/vga/vt_vga.c index 4661f35..62c9bf3 100644 --- a/sys/dev/vt/hw/vga/vt_vga.c +++ b/sys/dev/vt/hw/vga/vt_vga.c @@ -1214,7 +1214,6 @@ vga_init(struct vt_device *vd) if (vd->vd_softc == NULL) vd->vd_softc = (void *)&vga_conssoftc; sc = vd->vd_softc; - textmode = 0; if (vd->vd_flags & VDF_DOWNGRADE && vd->vd_video_dev != NULL) vga_pci_repost(vd->vd_video_dev); @@ -1229,6 +1228,13 @@ vga_init(struct vt_device *vd) bus_space_map(sc->vga_reg_tag, VGA_REG_BASE, VGA_REG_SIZE, 0, &sc->vga_reg_handle); + /* + * If "hw.vga.textmode" is not set and we're running on hypervisor, + * we use text mode by default, this is because when we're on + * hypervisor, vt(4) is usually much slower in graphics mode than + * in text mode, especially when we're on Hyper-V. + */ + textmode = vm_guest != VM_GUEST_NO; TUNABLE_INT_FETCH("hw.vga.textmode", &textmode); if (textmode) { vd->vd_flags |= VDF_TEXTMODE; diff --git a/sys/dev/vxge/vxge.c b/sys/dev/vxge/vxge.c index 9a3cab6..84320f6 100644 --- a/sys/dev/vxge/vxge.c +++ b/sys/dev/vxge/vxge.c @@ -996,7 +996,6 @@ vxge_rx_compl(vxge_hal_vpath_h vpath_handle, vxge_hal_rxd_h rxdh, vxge_vpath_t *vpath = (vxge_vpath_t *) userdata; vxge_dev_t *vdev = vpath->vdev; - struct lro_entry *queued = NULL; struct lro_ctrl *lro = &vpath->lro; /* get the interface pointer */ @@ -1083,12 +1082,8 @@ vxge_rx_compl(vxge_hal_vpath_h vpath_handle, vxge_hal_rxd_h rxdh, &dtr_priv, &t_code) == VXGE_HAL_OK); /* Flush any outstanding LRO work */ - if (vpath->lro_enable && vpath->lro.lro_cnt) { - while ((queued = SLIST_FIRST(&lro->lro_active)) != NULL) { - SLIST_REMOVE_HEAD(&lro->lro_active, next); - tcp_lro_flush(lro, queued); - } - } + if (vpath->lro_enable && vpath->lro.lro_cnt) + tcp_lro_flush_all(lro); return (status); } diff --git a/sys/dev/wbwd/wbwd.c b/sys/dev/wbwd/wbwd.c index 484b5bf..53e9084 100644 --- a/sys/dev/wbwd/wbwd.c +++ b/sys/dev/wbwd/wbwd.c @@ -91,8 +91,11 @@ __FBSDID("$FreeBSD$"); #define WB_LDN8_CRF7_FORCE 0x20 /* 1: force timeout (self-clear) */ #define WB_LDN8_CRF7_TS 0x10 /* 0: counting, 1: fired */ #define WB_LDN8_CRF7_IRQS 0x0f /* irq source for watchdog, 2 == SMI */ -#define WB_LDN8_CRF7_CLEAR_MASK \ - (WB_LDN8_CRF7_MOUSE|WB_LDN8_CRF7_KEYB|WB_LDN8_CRF7_TS|WB_LDN8_CRF7_IRQS) + +enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, + w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, + w83627dhg_p, w83667hg_b, nct6775, nct6776, nct6779, nct6791, + nct6792, nct6102 }; struct wb_softc { device_t dev; @@ -103,6 +106,10 @@ struct wb_softc { eventhandler_tag ev_tag; int (*ext_cfg_enter_f)(struct wb_softc *, u_short); void (*ext_cfg_exit_f)(struct wb_softc *, u_short); + enum chips chip; + uint8_t ctl_reg; + uint8_t time_reg; + uint8_t csr_reg; int debug_verbose; /* @@ -144,46 +151,104 @@ struct winbond_superio_cfg { }; struct winbond_vendor_device_id { - uint16_t vendor_id; uint8_t device_id; - uint8_t device_rev; + enum chips chip; const char * descr; } wb_devs[] = { { - .vendor_id = 0x5ca3, .device_id = 0x52, - .device_rev = 0x17, - .descr = "Winbond 83627HF/F/HG/G Rev. G", + .chip = w83627hf, + .descr = "Winbond 83627HF/F/HG/G", }, { - .vendor_id = 0x5ca3, - .device_id = 0x52, - .device_rev = 0x3a, - .descr = "Winbond 83627HF/F/HG/G Rev. J", + .device_id = 0x59, + .chip = w83627s, + .descr = "Winbond 83627S", }, { - .vendor_id = 0x5ca3, - .device_id = 0x52, - .device_rev = 0x41, - .descr = "Winbond 83627HF/F/HG/G Rev. UD-A", + .device_id = 0x60, + .chip = w83697hf, + .descr = "Winbond 83697HF", + }, + { + .device_id = 0x68, + .chip = w83697ug, + .descr = "Winbond 83697UG", + }, + { + .device_id = 0x70, + .chip = w83637hf, + .descr = "Winbond 83637HF", + }, + { + .device_id = 0x82, + .chip = w83627thf, + .descr = "Winbond 83627THF", + }, + { + .device_id = 0x85, + .chip = w83687thf, + .descr = "Winbond 83687THF", + }, + { + .device_id = 0x88, + .chip = w83627ehf, + .descr = "Winbond 83627EHF", }, { - .vendor_id = 0x5ca3, .device_id = 0xa0, - .device_rev = 0x25, - .descr = "Winbond 83627DHG IC ver. 5", + .chip = w83627dhg, + .descr = "Winbond 83627DHG", + }, + { + .device_id = 0xa2, + .chip = w83627uhg, + .descr = "Winbond 83627UHG", + }, + { + .device_id = 0xa5, + .chip = w83667hg, + .descr = "Winbond 83667HG", }, { - .vendor_id = 0x5ca3, .device_id = 0xb0, - .device_rev = 0x73, - .descr = "Winbond 83627DHG-P", + .chip = w83627dhg_p, + .descr = "Winbond 83627DHG-P", + }, + { + .device_id = 0xb3, + .chip = w83667hg_b, + .descr = "Winbond 83667HG-B", + }, + { + .device_id = 0xb4, + .chip = nct6775, + .descr = "Nuvoton NCT6775", }, { - .vendor_id = 0x5ca3, .device_id = 0xc3, - .device_rev = 0x33, - .descr = "Nuvoton WPCM450RA0BX", + .chip = nct6776, + .descr = "Nuvoton NCT6776", + }, + { + .device_id = 0xc4, + .chip = nct6102, + .descr = "Nuvoton NCT6102", + }, + { + .device_id = 0xc5, + .chip = nct6779, + .descr = "Nuvoton NCT6779", + }, + { + .device_id = 0xc8, + .chip = nct6791, + .descr = "Nuvoton NCT6791", + }, + { + .device_id = 0xc9, + .chip = nct6792, + .descr = "Nuvoton NCT6792", }, }; @@ -231,6 +296,22 @@ read_efdr_1(struct wb_softc *sc, u_short baseport) return (inb(baseport + 1)); } +static void +write_reg(struct wb_softc *sc, uint8_t reg, uint8_t value) +{ + + write_efir_1(sc, 0, reg); + write_efdr_1(sc, 0, value); +} + +static uint8_t +read_reg(struct wb_softc *sc, uint8_t reg) +{ + + write_efir_1(sc, 0, reg); + return (read_efdr_1(sc, 0)); +} + /* * Return the watchdog related registers as we last read them. This will * usually not give the current timeout or state on whether the watchdog @@ -248,9 +329,9 @@ sysctl_wb_debug(SYSCTL_HANDLER_ARGS) sbuf_new_for_sysctl(&sb, NULL, 64, req); sbuf_printf(&sb, "LDN8 (GPIO2, Watchdog): "); - sbuf_printf(&sb, "CRF5 0x%02x ", sc->reg_1); - sbuf_printf(&sb, "CRF6 0x%02x ", sc->reg_timeout); - sbuf_printf(&sb, "CRF7 0x%02x", sc->reg_2); + sbuf_printf(&sb, "CR%02X 0x%02x ", sc->ctl_reg, sc->reg_1); + sbuf_printf(&sb, "CR%02X 0x%02x ", sc->time_reg, sc->reg_timeout); + sbuf_printf(&sb, "CR%02X 0x%02x", sc->csr_reg, sc->reg_2); error = sbuf_finish(&sb); sbuf_delete(&sb); @@ -269,23 +350,17 @@ sysctl_wb_debug_current(SYSCTL_HANDLER_ARGS) sc = arg1; - /* - * Enter extended function mode in case someone else has been - * poking on the registers. We will not leave it though. - */ if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) return (ENXIO); /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ - write_efir_1(sc, 0, WB_LDN_REG); - write_efdr_1(sc, 0, WB_LDN_REG_LDN8); + write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8); + + sc->reg_1 = read_reg(sc, sc->ctl_reg); + sc->reg_timeout = read_reg(sc, sc->time_reg); + sc->reg_2 = read_reg(sc, sc->csr_reg); - write_efir_1(sc, 0, WB_LDN8_CRF5); - sc->reg_1 = read_efdr_1(sc, 0); - write_efir_1(sc, 0, WB_LDN8_CRF6); - sc->reg_timeout = read_efdr_1(sc, 0); - write_efir_1(sc, 0, WB_LDN8_CRF7); - sc->reg_2 = read_efdr_1(sc, 0); + (*sc->ext_cfg_exit_f)(sc, 0); return (sysctl_wb_debug(oidp, arg1, arg2, req)); } @@ -326,10 +401,6 @@ sysctl_wb_force_test_nmi(SYSCTL_HANDLER_ARGS) } #endif - /* - * Enter extended function mode in case someone else has been - * poking on the registers. We will not leave it though. - */ if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) return (ENXIO); @@ -343,16 +414,14 @@ sysctl_wb_force_test_nmi(SYSCTL_HANDLER_ARGS) #endif /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ - write_efir_1(sc, 0, WB_LDN_REG); - write_efdr_1(sc, 0, WB_LDN_REG_LDN8); + write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8); /* Force watchdog to fire. */ - write_efir_1(sc, 0, WB_LDN8_CRF7); - sc->reg_2 = read_efdr_1(sc, 0); + sc->reg_2 = read_reg(sc, sc->csr_reg); sc->reg_2 |= WB_LDN8_CRF7_FORCE; + write_reg(sc, sc->csr_reg, sc->reg_2); - write_efir_1(sc, 0, WB_LDN8_CRF7); - write_efdr_1(sc, 0, sc->reg_2); + (*sc->ext_cfg_exit_f)(sc, 0); return (0); } @@ -414,30 +483,36 @@ static int wb_set_watchdog(struct wb_softc *sc, unsigned int timeout) { + if (timeout != 0) { + /* + * In case an override is set, let it override. It may lead + * to strange results as we do not check the input of the sysctl. + */ + if (sc->timeout_override > 0) + timeout = sc->timeout_override; + + /* Make sure we support the requested timeout. */ + if (timeout > 255 * 60) + return (EINVAL); + } + if (sc->debug_verbose) wb_print_state(sc, "Before watchdog counter (re)load"); - /* - * Enter extended function mode in case someone else has been - * poking on the registers. We will not leave it though. - */ if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) return (ENXIO); /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog) */ - write_efir_1(sc, 0, WB_LDN_REG); - write_efdr_1(sc, 0, WB_LDN_REG_LDN8); + write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8); /* Disable and validate or arm/reset watchdog. */ if (timeout == 0) { /* Disable watchdog. */ - write_efir_1(sc, 0, WB_LDN8_CRF6); - write_efdr_1(sc, 0, 0x00); + write_reg(sc, sc->time_reg, 0x00); + sc->reg_timeout = read_reg(sc, sc->time_reg); + (*sc->ext_cfg_exit_f)(sc, 0); /* Re-check. */ - write_efir_1(sc, 0, WB_LDN8_CRF6); - sc->reg_timeout = read_efdr_1(sc, 0); - if (sc->reg_timeout != 0x00) { device_printf(sc->dev, "Failed to disable watchdog: " "0x%02x.\n", sc->reg_timeout); @@ -445,20 +520,8 @@ wb_set_watchdog(struct wb_softc *sc, unsigned int timeout) } } else { - /* - * In case an override is set, let it override. It may lead - * to strange results as we do not check the input of the sysctl. - */ - if (sc->timeout_override > 0) - timeout = sc->timeout_override; - - /* Make sure we support the requested timeout. */ - if (timeout > 255 * 60) - return (EINVAL); - /* Read current scaling factor. */ - write_efir_1(sc, 0, WB_LDN8_CRF5); - sc->reg_1 = read_efdr_1(sc, 0); + sc->reg_1 = read_reg(sc, sc->ctl_reg); if (timeout > 255) { /* Set scaling factor to 60s. */ @@ -473,21 +536,18 @@ wb_set_watchdog(struct wb_softc *sc, unsigned int timeout) } /* In case we fired before we need to clear to fire again. */ - write_efir_1(sc, 0, WB_LDN8_CRF7); - sc->reg_2 = read_efdr_1(sc, 0); + sc->reg_2 = read_reg(sc, sc->csr_reg); if (sc->reg_2 & WB_LDN8_CRF7_TS) { sc->reg_2 &= ~WB_LDN8_CRF7_TS; - write_efir_1(sc, 0, WB_LDN8_CRF7); - write_efdr_1(sc, 0, sc->reg_2); + write_reg(sc, sc->csr_reg, sc->reg_2); } /* Write back scaling factor. */ - write_efir_1(sc, 0, WB_LDN8_CRF5); - write_efdr_1(sc, 0, sc->reg_1); + write_reg(sc, sc->ctl_reg, sc->reg_1); /* Set timer and arm/reset the watchdog. */ - write_efir_1(sc, 0, WB_LDN8_CRF6); - write_efdr_1(sc, 0, sc->reg_timeout); + write_reg(sc, sc->time_reg, sc->reg_timeout); + (*sc->ext_cfg_exit_f)(sc, 0); } if (sc->debug_verbose) @@ -556,6 +616,7 @@ wb_probe_enable(device_t dev, int probe) struct wb_softc *sc; int error, found, i, j; uint8_t dev_id, dev_rev, cr26; + char buf[128]; if (dev == NULL) sc = NULL; @@ -566,6 +627,7 @@ wb_probe_enable(device_t dev, int probe) } error = ENXIO; + found = 0; for (i = 0; i < sizeof(probe_addrs) / sizeof(*probe_addrs); i++) { if (sc != NULL) { @@ -578,7 +640,6 @@ wb_probe_enable(device_t dev, int probe) sc->bsh = rman_get_bushandle(sc->portres); } - found = 0; error = (*probe_addrs[i].ext_cfg_enter_f)(sc, probe_addrs[i].efer); if (error != 0) goto cleanup; @@ -591,6 +652,9 @@ wb_probe_enable(device_t dev, int probe) write_efir_1(sc, probe_addrs[i].efer, WB_CR26); cr26 = read_efdr_1(sc, probe_addrs[i].efer); + if (dev_id == 0xff && dev_rev == 0xff) + goto cleanup; + /* HEFRAS of 0 means EFER at 0x2e, 1 means EFER at 0x4e. */ if (((cr26 & 0x40) == 0x00 && probe_addrs[i].efer != 0x2e) || ((cr26 & 0x40) == 0x40 && probe_addrs[i].efer != 0x4e)) { @@ -602,36 +666,30 @@ wb_probe_enable(device_t dev, int probe) goto cleanup; } - if (dev_id == 0xff && dev_rev == 0xff) - goto cleanup; - for (j = 0; j < sizeof(wb_devs) / sizeof(*wb_devs); j++) { - if (wb_devs[j].device_id == dev_id && - wb_devs[j].device_rev == dev_rev) { - if (probe && dev != NULL) - device_set_desc(dev, wb_devs[j].descr); - found++; + if (wb_devs[j].device_id == dev_id) { + found = 1; break; } } - if (!found) { - if (probe && dev != NULL) { - device_set_desc(dev, "Unknown Winbond/Nuvoton model"); - device_printf(dev, "DevID 0x%02x DevRev 0x%02x, " - "please report this.\n", dev_id, dev_rev); - } - found++; + if (probe && dev != NULL) { + snprintf(buf, sizeof(buf), + "%s (0x%02x/0x%02x) Watchdog Timer", + found ? wb_devs[j].descr : + "Unknown Winbond/Nuvoton", dev_id, dev_rev); + device_set_desc_copy(dev, buf); + } + + /* If this is hinted attach, try to guess the model. */ + if (dev != NULL && !found) { + found = 1; + j = 0; } - if (probe && found && bootverbose && dev != NULL) - device_printf(dev, "%s EFER 0x%02x ID 0x%02x Rev 0x%02x" - " CR26 0x%02x (probing)\n", device_get_desc(dev), - probe_addrs[i].efer, dev_id, dev_rev, cr26); cleanup: if (probe || !found) { (*probe_addrs[i].ext_cfg_exit_f)(sc, probe_addrs[i].efer); - if (sc != NULL) (void) bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres); @@ -646,9 +704,21 @@ cleanup: if (sc != NULL) { sc->ext_cfg_enter_f = probe_addrs[i].ext_cfg_enter_f; sc->ext_cfg_exit_f = probe_addrs[i].ext_cfg_exit_f; + sc->chip = wb_devs[j].chip; + sc->ctl_reg = 0xf5; + sc->time_reg = 0xf6; + sc->csr_reg = 0xf7; + if (sc->chip == w83697hf || + sc->chip == w83697ug) { + sc->ctl_reg = 0xf3; + sc->time_reg = 0xf4; + } else if (sc->chip == nct6102) { + sc->ctl_reg = 0xf0; + sc->time_reg = 0xf1; + sc->csr_reg = 0xf2; + } } - error = BUS_PROBE_DEFAULT; - break; + return (BUS_PROBE_SPECIFIC); } else error = ENXIO; } @@ -659,15 +729,10 @@ cleanup: static void wb_identify(driver_t *driver, device_t parent) { - device_t dev; - if ((dev = device_find_child(parent, driver->name, 0)) == NULL) { - if (wb_probe_enable(dev, 1) != BUS_PROBE_DEFAULT) { - if (bootverbose) - device_printf(dev, "can not find compatible Winbond chip.\n"); - } else - dev = BUS_ADD_CHILD(parent, 0, driver->name, 0); - return; + if (device_find_child(parent, driver->name, 0) == NULL) { + if (wb_probe_enable(NULL, 1) <= 0) + BUS_ADD_CHILD(parent, 0, driver->name, 0); } } @@ -690,6 +755,7 @@ wb_attach(device_t dev) struct sysctl_oid *soid; unsigned long timeout; int error; + uint8_t t; error = wb_probe_enable(dev, 0); if (error > 0) @@ -700,37 +766,95 @@ wb_attach(device_t dev) ("%s: successfull probe result but not setup correctly", __func__)); /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ - write_efir_1(sc, 0, WB_LDN_REG); - write_efdr_1(sc, 0, WB_LDN_REG_LDN8); - - /* Make sure LDN8 is enabled (Do we need to? Also affects GPIO). */ - write_efir_1(sc, 0, WB_LDN8_CR30); - write_efdr_1(sc, 0, WB_LDN8_CR30_ACTIVE); + write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8); + + /* Make sure WDT is enabled. */ + write_reg(sc, WB_LDN8_CR30, + read_reg(sc, WB_LDN8_CR30) | WB_LDN8_CR30_ACTIVE); + + switch (sc->chip) { + case w83627hf: + case w83627s: + t = read_reg(sc, 0x2B) & ~0x10; + write_reg(sc, 0x2B, t); /* set GPIO24 to WDT0 */ + break; + case w83697hf: + /* Set pin 119 to WDTO# mode (= CR29, WDT0) */ + t = read_reg(sc, 0x29) & ~0x60; + t |= 0x20; + write_reg(sc, 0x29, t); + break; + case w83697ug: + /* Set pin 118 to WDTO# mode */ + t = read_reg(sc, 0x2b) & ~0x04; + write_reg(sc, 0x2b, t); + break; + case w83627thf: + t = (read_reg(sc, 0x2B) & ~0x08) | 0x04; + write_reg(sc, 0x2B, t); /* set GPIO3 to WDT0 */ + break; + case w83627dhg: + case w83627dhg_p: + t = read_reg(sc, 0x2D) & ~0x01; /* PIN77 -> WDT0# */ + write_reg(sc, 0x2D, t); /* set GPIO5 to WDT0 */ + t = read_reg(sc, sc->ctl_reg); + t |= 0x02; /* enable the WDTO# output low pulse + * to the KBRST# pin */ + write_reg(sc, sc->ctl_reg, t); + break; + case w83637hf: + break; + case w83687thf: + t = read_reg(sc, 0x2C) & ~0x80; /* PIN47 -> WDT0# */ + write_reg(sc, 0x2C, t); + break; + case w83627ehf: + case w83627uhg: + case w83667hg: + case w83667hg_b: + case nct6775: + case nct6776: + case nct6779: + case nct6791: + case nct6792: + case nct6102: + /* + * These chips have a fixed WDTO# output pin (W83627UHG), + * or support more than one WDTO# output pin. + * Don't touch its configuration, and hope the BIOS + * does the right thing. + */ + t = read_reg(sc, sc->ctl_reg); + t |= 0x02; /* enable the WDTO# output low pulse + * to the KBRST# pin */ + write_reg(sc, sc->ctl_reg, t); + break; + default: + break; + } /* Read the current watchdog configuration. */ - write_efir_1(sc, 0, WB_LDN8_CRF5); - sc->reg_1 = read_efdr_1(sc, 0); - write_efir_1(sc, 0, WB_LDN8_CRF6); - sc->reg_timeout = read_efdr_1(sc, 0); - write_efir_1(sc, 0, WB_LDN8_CRF7); - sc->reg_2 = read_efdr_1(sc, 0); + sc->reg_1 = read_reg(sc, sc->ctl_reg); + sc->reg_timeout = read_reg(sc, sc->time_reg); + sc->reg_2 = read_reg(sc, sc->csr_reg); /* Print current state if bootverbose or watchdog already enabled. */ if (bootverbose || (sc->reg_timeout > 0x00)) wb_print_state(sc, "Before watchdog attach"); + sc->reg_1 &= ~WB_LDN8_CRF5_KEYB_P20; + sc->reg_1 |= WB_LDN8_CRF5_KBRST; + write_reg(sc, sc->ctl_reg, sc->reg_1); + /* - * Clear a previous watchdog timeout event (if (still) set). - * Disable all all interrupt reset sources (defaults). + * Clear a previous watchdog timeout event (if still set). + * Disable timer reset on mouse interrupts. Leave reset on keyboard, + * since one of my boards is getting stuck in reboot without it. */ - sc->reg_1 &= ~(WB_LDN8_CRF5_KEYB_P20); - sc->reg_1 |= WB_LDN8_CRF5_KBRST; - write_efir_1(sc, 0, WB_LDN8_CRF5); - write_efdr_1(sc, 0, sc->reg_1); + sc->reg_2 &= ~(WB_LDN8_CRF7_MOUSE|WB_LDN8_CRF7_TS); + write_reg(sc, sc->csr_reg, sc->reg_2); - sc->reg_2 &= ~WB_LDN8_CRF7_CLEAR_MASK; - write_efir_1(sc, 0, WB_LDN8_CRF7); - write_efdr_1(sc, 0, sc->reg_2); + (*sc->ext_cfg_exit_f)(sc, 0); /* Read global timeout override tunable, Add per device sysctls. */ if (TUNABLE_ULONG_FETCH("hw.wbwd.timeout_override", &timeout)) { diff --git a/sys/dev/wl/if_wl.c b/sys/dev/wl/if_wl.c index 7146a25..6414d33 100644 --- a/sys/dev/wl/if_wl.c +++ b/sys/dev/wl/if_wl.c @@ -495,7 +495,7 @@ wlattach(device_t device) } #ifdef WLDEBUG - printf("wlattach: base %lx, unit %d\n", rman_get_start(sc->res_ioport), + printf("wlattach: base %jx, unit %d\n", rman_get_start(sc->res_ioport), device_get_unit(device)); #endif diff --git a/sys/dev/wpi/if_wpi.c b/sys/dev/wpi/if_wpi.c index 824f75b..e435096 100644 --- a/sys/dev/wpi/if_wpi.c +++ b/sys/dev/wpi/if_wpi.c @@ -284,7 +284,6 @@ static void wpi_scan_end(struct ieee80211com *); static void wpi_set_channel(struct ieee80211com *); static void wpi_scan_curchan(struct ieee80211_scan_state *, unsigned long); static void wpi_scan_mindwell(struct ieee80211_scan_state *); -static void wpi_hw_reset(void *, int); static device_method_t wpi_methods[] = { /* Device interface */ @@ -531,18 +530,9 @@ wpi_attach(device_t dev) callout_init_mtx(&sc->scan_timeout, &sc->rxon_mtx, 0); callout_init_mtx(&sc->tx_timeout, &sc->txq_state_mtx, 0); callout_init_mtx(&sc->watchdog_rfkill, &sc->sc_mtx, 0); - TASK_INIT(&sc->sc_reinittask, 0, wpi_hw_reset, sc); TASK_INIT(&sc->sc_radiooff_task, 0, wpi_radio_off, sc); TASK_INIT(&sc->sc_radioon_task, 0, wpi_radio_on, sc); - sc->sc_tq = taskqueue_create("wpi_taskq", M_WAITOK, - taskqueue_thread_enqueue, &sc->sc_tq); - error = taskqueue_start_threads(&sc->sc_tq, 1, 0, "wpi_taskq"); - if (error != 0) { - device_printf(dev, "can't start threads, error %d\n", error); - goto fail; - } - wpi_sysctlattach(sc); /* @@ -695,14 +685,10 @@ wpi_detach(device_t dev) if (ic->ic_vap_create == wpi_vap_create) { ieee80211_draintask(ic, &sc->sc_radioon_task); + ieee80211_draintask(ic, &sc->sc_radiooff_task); wpi_stop(sc); - if (sc->sc_tq != NULL) { - taskqueue_drain_all(sc->sc_tq); - taskqueue_free(sc->sc_tq); - } - callout_drain(&sc->watchdog_rfkill); callout_drain(&sc->tx_timeout); callout_drain(&sc->scan_timeout); @@ -1517,7 +1503,8 @@ wpi_find_eeprom_channel(struct wpi_softc *sc, struct ieee80211_channel *c) for (j = 0; j < WPI_CHAN_BANDS_COUNT; j++) for (i = 0; i < wpi_bands[j].nchan; i++) - if (wpi_bands[j].chan[i] == c->ic_ieee) + if (wpi_bands[j].chan[i] == c->ic_ieee && + ((j == 0) ^ IEEE80211_IS_CHAN_A(c)) == 1) return &sc->eeprom_channels[j][i]; return NULL; @@ -2332,7 +2319,7 @@ wpi_notif_intr(struct wpi_softc *sc) WPI_NT_LOCK(sc); wpi_clear_node_table(sc); WPI_NT_UNLOCK(sc); - taskqueue_enqueue(sc->sc_tq, + ieee80211_runtask(ic, &sc->sc_radiooff_task); return; } @@ -2569,6 +2556,8 @@ wpi_intr(void *arg) WPI_WRITE(sc, WPI_FH_INT, r2); if (__predict_false(r1 & (WPI_INT_SW_ERR | WPI_INT_HW_ERR))) { + struct ieee80211com *ic = &sc->sc_ic; + device_printf(sc->sc_dev, "fatal firmware error\n"); #ifdef WPI_DEBUG wpi_debug_registers(sc); @@ -2577,7 +2566,7 @@ wpi_intr(void *arg) DPRINTF(sc, WPI_DEBUG_HW, "(%s)\n", (r1 & WPI_INT_SW_ERR) ? "(Software Error)" : "(Hardware Error)"); - taskqueue_enqueue(sc->sc_tq, &sc->sc_reinittask); + ieee80211_restart_all(ic); goto end; } @@ -3200,7 +3189,7 @@ wpi_scan_timeout(void *arg) struct ieee80211com *ic = &sc->sc_ic; ic_printf(ic, "scan timeout\n"); - taskqueue_enqueue(sc->sc_tq, &sc->sc_reinittask); + ieee80211_restart_all(ic); } static void @@ -3210,7 +3199,7 @@ wpi_tx_timeout(void *arg) struct ieee80211com *ic = &sc->sc_ic; ic_printf(ic, "device timeout\n"); - taskqueue_enqueue(sc->sc_tq, &sc->sc_reinittask); + ieee80211_restart_all(ic); } static void @@ -3227,8 +3216,10 @@ wpi_parent(struct ieee80211com *ic) ieee80211_notify_radio(ic, 0); ieee80211_stop(vap); } - } else + } else { + ieee80211_notify_radio(ic, 0); wpi_stop(sc); + } } /* @@ -5654,23 +5645,3 @@ wpi_scan_mindwell(struct ieee80211_scan_state *ss) { /* NB: don't try to abort scan; wait for firmware to finish */ } - -static void -wpi_hw_reset(void *arg, int pending) -{ - struct wpi_softc *sc = arg; - struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - ieee80211_notify_radio(ic, 0); - if (vap != NULL && (ic->ic_flags & IEEE80211_F_SCAN)) - ieee80211_cancel_scan(vap); - - wpi_stop(sc); - if (vap != NULL) { - ieee80211_stop(vap); - ieee80211_init(vap); - } -} diff --git a/sys/dev/wpi/if_wpivar.h b/sys/dev/wpi/if_wpivar.h index 1957740..437f1d8 100644 --- a/sys/dev/wpi/if_wpivar.h +++ b/sys/dev/wpi/if_wpivar.h @@ -228,13 +228,9 @@ struct wpi_softc { struct wpi_dma_info fw_dma; /* Tasks used by the driver. */ - struct task sc_reinittask; struct task sc_radiooff_task; struct task sc_radioon_task; - /* Taskqueue */ - struct taskqueue *sc_tq; - /* Eeprom info. */ uint8_t cap; uint16_t rev; diff --git a/sys/dev/xe/if_xe.c b/sys/dev/xe/if_xe.c index d6ccae9..e18af8c 100644 --- a/sys/dev/xe/if_xe.c +++ b/sys/dev/xe/if_xe.c @@ -1984,7 +1984,7 @@ xe_activate(device_t dev) sc->port_res); start = (rman_get_start(sc->port_res) + 15) & ~0xf; } while (1); - DEVPRINTF(1, (dev, "RealPort port 0x%0lx, size 0x%0lx\n", + DEVPRINTF(1, (dev, "RealPort port 0x%0jx, size 0x%0jx\n", bus_get_resource_start(dev, SYS_RES_IOPORT, sc->port_rid), bus_get_resource_count(dev, SYS_RES_IOPORT, sc->port_rid))); } else if (sc->ce2) { @@ -2024,7 +2024,7 @@ xe_activate(device_t dev) sc->port_res); sc->port_res = NULL; } - DEVPRINTF(1, (dev, "CEM2/CEM3 port 0x%0lx, size 0x%0lx\n", + DEVPRINTF(1, (dev, "CEM2/CEM3 port 0x%0jx, size 0x%0jx\n", bus_get_resource_start(dev, SYS_RES_IOPORT, sc->port_rid), bus_get_resource_count(dev, SYS_RES_IOPORT, sc->port_rid))); } diff --git a/sys/dev/xe/if_xe_pccard.c b/sys/dev/xe/if_xe_pccard.c index bbe6253..fb2cff6 100644 --- a/sys/dev/xe/if_xe_pccard.c +++ b/sys/dev/xe/if_xe_pccard.c @@ -140,7 +140,7 @@ xe_cemfix(device_t dev) DEVPRINTF(2, (dev, "cemfix\n")); - DEVPRINTF(1, (dev, "CEM I/O port 0x%0lx, size 0x%0lx\n", + DEVPRINTF(1, (dev, "CEM I/O port 0x%0jx, size 0x%0jx\n", bus_get_resource_start(dev, SYS_RES_IOPORT, sc->port_rid), bus_get_resource_count(dev, SYS_RES_IOPORT, sc->port_rid))); diff --git a/sys/dev/xen/blkfront/blkfront.c b/sys/dev/xen/blkfront/blkfront.c index d35e04a..ef36b94 100644 --- a/sys/dev/xen/blkfront/blkfront.c +++ b/sys/dev/xen/blkfront/blkfront.c @@ -855,6 +855,20 @@ xbd_feature_string(struct xbd_softc *sc, char *features, size_t len) feature_cnt++; } + if ((sc->xbd_flags & XBDF_DISCARD) != 0) { + if (feature_cnt != 0) + sbuf_printf(&sb, ", "); + sbuf_printf(&sb, "discard"); + feature_cnt++; + } + + if ((sc->xbd_flags & XBDF_PERSISTENT) != 0) { + if (feature_cnt != 0) + sbuf_printf(&sb, ", "); + sbuf_printf(&sb, "persistent_grants"); + feature_cnt++; + } + (void) sbuf_finish(&sb); return (sbuf_len(&sb)); } @@ -985,7 +999,8 @@ xbd_vdevice_to_unit(uint32_t vdevice, int *unit, const char **name) int xbd_instance_create(struct xbd_softc *sc, blkif_sector_t sectors, - int vdevice, uint16_t vdisk_info, unsigned long sector_size) + int vdevice, uint16_t vdisk_info, unsigned long sector_size, + unsigned long phys_sector_size) { char features[80]; int unit, error = 0; @@ -1013,6 +1028,8 @@ xbd_instance_create(struct xbd_softc *sc, blkif_sector_t sectors, sc->xbd_disk->d_name = name; sc->xbd_disk->d_drv1 = sc; sc->xbd_disk->d_sectorsize = sector_size; + sc->xbd_disk->d_stripesize = phys_sector_size; + sc->xbd_disk->d_stripeoffset = 0; sc->xbd_disk->d_mediasize = sectors * sector_size; sc->xbd_disk->d_maxsize = sc->xbd_max_request_size; @@ -1206,7 +1223,7 @@ static void xbd_connect(struct xbd_softc *sc) { device_t dev = sc->xbd_dev; - unsigned long sectors, sector_size; + unsigned long sectors, sector_size, phys_sector_size; unsigned int binfo; int err, feature_barrier, feature_flush; int i, j; @@ -1229,6 +1246,11 @@ xbd_connect(struct xbd_softc *sc) return; } err = xs_gather(XST_NIL, xenbus_get_otherend_path(dev), + "physical-sector-size", "%lu", &phys_sector_size, + NULL); + if (err || phys_sector_size <= sector_size) + phys_sector_size = 0; + err = xs_gather(XST_NIL, xenbus_get_otherend_path(dev), "feature-barrier", "%lu", &feature_barrier, NULL); if (err == 0 && feature_barrier != 0) @@ -1330,7 +1352,7 @@ xbd_connect(struct xbd_softc *sc) bus_print_child_footer(device_get_parent(dev), dev); xbd_instance_create(sc, sectors, sc->xbd_vdevice, binfo, - sector_size); + sector_size, phys_sector_size); } (void)xenbus_set_state(dev, XenbusStateConnected); diff --git a/sys/dev/xen/blkfront/block.h b/sys/dev/xen/blkfront/block.h index 28c6ff2..ddb4088 100644 --- a/sys/dev/xen/blkfront/block.h +++ b/sys/dev/xen/blkfront/block.h @@ -159,10 +159,12 @@ typedef enum { XBDF_READY = 1 << 3, /* Is ready */ XBDF_CM_SHORTAGE = 1 << 4, /* Free cm resource shortage active. */ XBDF_GNT_SHORTAGE = 1 << 5, /* Grant ref resource shortage active */ - XBDF_WAIT_IDLE = 1 << 6 /* + XBDF_WAIT_IDLE = 1 << 6, /* * No new work until oustanding work * completes. */ + XBDF_DISCARD = 1 << 7, /* backend supports discard */ + XBDF_PERSISTENT = 1 << 8 /* backend supports persistent grants */ } xbd_flag_t; /* @@ -200,7 +202,8 @@ struct xbd_softc { }; int xbd_instance_create(struct xbd_softc *, blkif_sector_t sectors, int device, - uint16_t vdisk_info, unsigned long sector_size); + uint16_t vdisk_info, unsigned long sector_size, + unsigned long phys_sector_size); static inline void xbd_added_qentry(struct xbd_softc *sc, xbd_q_index_t index) diff --git a/sys/dev/xen/netfront/netfront.c b/sys/dev/xen/netfront/netfront.c index 22ca81c..7add70a 100644 --- a/sys/dev/xen/netfront/netfront.c +++ b/sys/dev/xen/netfront/netfront.c @@ -1202,7 +1202,6 @@ xn_rxeof(struct netfront_rxq *rxq) struct netfront_info *np = rxq->info; #if (defined(INET) || defined(INET6)) struct lro_ctrl *lro = &rxq->lro; - struct lro_entry *queued; #endif struct netfront_rx_info rinfo; struct netif_rx_response *rx = &rinfo.rx; @@ -1296,11 +1295,7 @@ xn_rxeof(struct netfront_rxq *rxq) /* * Flush any outstanding LRO work */ - while (!SLIST_EMPTY(&lro->lro_active)) { - queued = SLIST_FIRST(&lro->lro_active); - SLIST_REMOVE_HEAD(&lro->lro_active, next); - tcp_lro_flush(lro, queued); - } + tcp_lro_flush_all(lro); #endif xn_alloc_rx_buffers(rxq); diff --git a/sys/fs/autofs/autofs.c b/sys/fs/autofs/autofs.c index c4b1ec3..eee74ea 100644 --- a/sys/fs/autofs/autofs.c +++ b/sys/fs/autofs/autofs.c @@ -77,6 +77,7 @@ #include <sys/sysctl.h> #include <sys/syscallsubr.h> #include <sys/taskqueue.h> +#include <sys/tree.h> #include <sys/vnode.h> #include <machine/atomic.h> #include <vm/uma.h> @@ -149,6 +150,15 @@ TUNABLE_INT("vfs.autofs.interruptible", &autofs_interruptible); SYSCTL_INT(_vfs_autofs, OID_AUTO, interruptible, CTLFLAG_RWTUN, &autofs_interruptible, 1, "Allow requests to be interrupted by signal"); +static int +autofs_node_cmp(const struct autofs_node *a, const struct autofs_node *b) +{ + + return (strcmp(a->an_name, b->an_name)); +} + +RB_GENERATE(autofs_node_tree, autofs_node, an_link, autofs_node_cmp); + int autofs_init(struct vfsconf *vfsp) { diff --git a/sys/fs/autofs/autofs.h b/sys/fs/autofs/autofs.h index 27e380b..bab2282 100644 --- a/sys/fs/autofs/autofs.h +++ b/sys/fs/autofs/autofs.h @@ -65,11 +65,12 @@ extern int autofs_mount_on_stat; #define AUTOFS_ASSERT_UNLOCKED(X) sx_assert(&X->am_lock, SA_UNLOCKED) struct autofs_node { - TAILQ_ENTRY(autofs_node) an_next; + RB_ENTRY(autofs_node) an_link; char *an_name; int an_fileno; struct autofs_node *an_parent; - TAILQ_HEAD(, autofs_node) an_children; + RB_HEAD(autofs_node_tree, + autofs_node) an_children; struct autofs_mount *an_mount; struct vnode *an_vnode; struct sx an_vnode_lock; @@ -136,4 +137,6 @@ void autofs_node_delete(struct autofs_node *anp); int autofs_node_vn(struct autofs_node *anp, struct mount *mp, int flags, struct vnode **vpp); +RB_PROTOTYPE(autofs_node_tree, autofs_node, an_link, autofs_node_cmp); + #endif /* !AUTOFS_H */ diff --git a/sys/fs/autofs/autofs_vfsops.c b/sys/fs/autofs/autofs_vfsops.c index 722ff24..b427a7f 100644 --- a/sys/fs/autofs/autofs_vfsops.c +++ b/sys/fs/autofs/autofs_vfsops.c @@ -42,6 +42,7 @@ #include <sys/stat.h> #include <sys/sx.h> #include <sys/taskqueue.h> +#include <sys/tree.h> #include <sys/vnode.h> #include <fs/autofs/autofs.h> @@ -158,10 +159,10 @@ autofs_unmount(struct mount *mp, int mntflags) /* * Not terribly efficient, but at least not recursive. */ - while (!TAILQ_EMPTY(&->am_root->an_children)) { - anp = TAILQ_FIRST(&->am_root->an_children); - while (!TAILQ_EMPTY(&anp->an_children)) - anp = TAILQ_FIRST(&anp->an_children); + while (!RB_EMPTY(&->am_root->an_children)) { + anp = RB_MIN(autofs_node_tree, &->am_root->an_children); + while (!RB_EMPTY(&anp->an_children)) + anp = RB_MIN(autofs_node_tree, &anp->an_children); autofs_node_delete(anp); } autofs_node_delete(amp->am_root); diff --git a/sys/fs/autofs/autofs_vnops.c b/sys/fs/autofs/autofs_vnops.c index 515d228..0ab6ec1 100644 --- a/sys/fs/autofs/autofs_vnops.c +++ b/sys/fs/autofs/autofs_vnops.c @@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$"); #include <sys/stat.h> #include <sys/systm.h> #include <sys/taskqueue.h> +#include <sys/tree.h> #include <sys/vnode.h> #include <machine/atomic.h> #include <vm/uma.h> @@ -371,7 +372,7 @@ autofs_dirent_reclen(const char *name) { size_t reclen; - autofs_readdir_one(NULL, name, -1, &reclen); + (void)autofs_readdir_one(NULL, name, -1, &reclen); return (reclen); } @@ -450,7 +451,7 @@ autofs_readdir(struct vop_readdir_args *ap) * Write out the directory entries for subdirectories. */ AUTOFS_SLOCK(amp); - TAILQ_FOREACH(child, &anp->an_children, an_next) { + RB_FOREACH(child, autofs_node_tree, &anp->an_children) { /* * Check the offset to skip entries returned by previous * calls to getdents(). @@ -575,8 +576,8 @@ autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp, anp->an_parent = parent; anp->an_mount = amp; if (parent != NULL) - TAILQ_INSERT_TAIL(&parent->an_children, anp, an_next); - TAILQ_INIT(&anp->an_children); + RB_INSERT(autofs_node_tree, &parent->an_children, anp); + RB_INIT(&anp->an_children); *anpp = anp; return (0); @@ -586,27 +587,28 @@ int autofs_node_find(struct autofs_node *parent, const char *name, int namelen, struct autofs_node **anpp) { - struct autofs_node *anp; + struct autofs_node *anp, find; + int error; AUTOFS_ASSERT_LOCKED(parent->an_mount); - TAILQ_FOREACH(anp, &parent->an_children, an_next) { - if (namelen >= 0) { - if (strlen(anp->an_name) != namelen) - continue; - if (strncmp(anp->an_name, name, namelen) != 0) - continue; - } else { - if (strcmp(anp->an_name, name) != 0) - continue; - } + if (namelen >= 0) + find.an_name = strndup(name, namelen, M_AUTOFS); + else + find.an_name = strdup(name, M_AUTOFS); + anp = RB_FIND(autofs_node_tree, &parent->an_children, &find); + if (anp != NULL) { + error = 0; if (anpp != NULL) *anpp = anp; - return (0); + } else { + error = ENOENT; } - return (ENOENT); + free(find.an_name, M_AUTOFS); + + return (error); } void @@ -615,13 +617,13 @@ autofs_node_delete(struct autofs_node *anp) struct autofs_node *parent; AUTOFS_ASSERT_XLOCKED(anp->an_mount); - KASSERT(TAILQ_EMPTY(&anp->an_children), ("have children")); + KASSERT(RB_EMPTY(&anp->an_children), ("have children")); callout_drain(&anp->an_callout); parent = anp->an_parent; if (parent != NULL) - TAILQ_REMOVE(&parent->an_children, anp, an_next); + RB_REMOVE(autofs_node_tree, &parent->an_children, anp); sx_destroy(&anp->an_vnode_lock); free(anp->an_name, M_AUTOFS); uma_zfree(autofs_node_zone, anp); diff --git a/sys/fs/cd9660/cd9660_vnops.c b/sys/fs/cd9660/cd9660_vnops.c index 47d4f75..cab8db7 100644 --- a/sys/fs/cd9660/cd9660_vnops.c +++ b/sys/fs/cd9660/cd9660_vnops.c @@ -341,11 +341,9 @@ cd9660_read(ap) } else error = bread(vp, lbn, size, NOCRED, &bp); } - n = MIN(n, size - bp->b_resid); - if (error) { - brelse(bp); + if (error != 0) return (error); - } + n = MIN(n, size - bp->b_resid); error = uiomove(bp->b_data + on, (int)n, uio); brelse(bp); diff --git a/sys/fs/ext2fs/ext2_alloc.c b/sys/fs/ext2fs/ext2_alloc.c index 12067d2..9d5417c 100644 --- a/sys/fs/ext2fs/ext2_alloc.c +++ b/sys/fs/ext2fs/ext2_alloc.c @@ -406,10 +406,8 @@ ext2_valloc(struct vnode *pvp, int mode, struct ucred *cred, struct vnode **vpp) /* * Set up a new generation number for this inode. - * XXX check if this makes sense in ext2 */ - if (ip->i_gen == 0 || ++ip->i_gen == 0) - ip->i_gen = random() / 2 + 1; + ip->i_gen = arc4random(); vfs_timestamp(&ts); ip->i_birthtime = ts.tv_sec; diff --git a/sys/fs/ext2fs/ext2_dinode.h b/sys/fs/ext2fs/ext2_dinode.h index 3f5ddef..a172a5b 100644 --- a/sys/fs/ext2fs/ext2_dinode.h +++ b/sys/fs/ext2fs/ext2_dinode.h @@ -131,6 +131,7 @@ struct ext2fs_dinode { uint32_t e2di_crtime; /* 144: Creation (birth)time */ uint32_t e2di_crtime_extra; /* 148: Extra creation (birth)time */ uint32_t e2di_version_hi; /* 152: High bits of inode version */ + uint32_t e2di_projid; /* 156: Project ID */ }; #endif /* !_FS_EXT2FS_EXT2_DINODE_H_ */ diff --git a/sys/fs/ext2fs/ext2fs.h b/sys/fs/ext2fs/ext2fs.h index b8c4701..c23f2a2 100644 --- a/sys/fs/ext2fs/ext2fs.h +++ b/sys/fs/ext2fs/ext2fs.h @@ -72,7 +72,7 @@ struct ext2fs { uint32_t e2fs_first_ino; /* first non-reserved inode */ uint16_t e2fs_inode_size; /* size of inode structure */ uint16_t e2fs_block_group_nr; /* block grp number of this sblk*/ - uint32_t e2fs_features_compat; /* compatible feature set */ + uint32_t e2fs_features_compat; /* compatible feature set */ uint32_t e2fs_features_incompat; /* incompatible feature set */ uint32_t e2fs_features_rocompat; /* RO-compatible feature set */ uint8_t e2fs_uuid[16]; /* 128-bit uuid for volume */ @@ -88,25 +88,54 @@ struct ext2fs { uint32_t e3fs_last_orphan; /* start of list of inodes to delete */ uint32_t e3fs_hash_seed[4]; /* HTREE hash seed */ char e3fs_def_hash_version; /* Default hash version to use */ - char e3fs_reserved_char_pad; + char e3fs_jnl_backup_type; + uint16_t e3fs_desc_size; /* size of group descriptor */ uint32_t e3fs_default_mount_opts; uint32_t e3fs_first_meta_bg; /* First metablock block group */ - uint32_t e3fs_mkfs_time; /* when the fs was created */ - uint32_t e3fs_jnl_blks[17]; /* backup of the journal inode */ - uint32_t e4fs_bcount_hi; /* block count */ - uint32_t e4fs_rbcount_hi; /* reserved blocks count */ - uint32_t e4fs_fbcount_hi; /* free blocks count */ - uint16_t e4fs_min_extra_isize;/* all inodes have at least some bytes */ + uint32_t e3fs_mkfs_time; /* when the fs was created */ + uint32_t e3fs_jnl_blks[17]; /* backup of the journal inode */ + uint32_t e4fs_bcount_hi; /* high bits of blocks count */ + uint32_t e4fs_rbcount_hi; /* high bits of reserved blocks count */ + uint32_t e4fs_fbcount_hi; /* high bits of free blocks count */ + uint16_t e4fs_min_extra_isize; /* all inodes have at least some bytes */ uint16_t e4fs_want_extra_isize; /* inodes must reserve some bytes */ - uint32_t e4fs_flags; /* miscellaneous flags */ - uint16_t e4fs_raid_stride; /* RAID stride */ - uint16_t e4fs_mmpintv; /* number of seconds to wait in MMP checking */ - uint64_t e4fs_mmpblk; /* block for multi-mount protection */ - uint32_t e4fs_raid_stripe_wid;/* blocks on all data disks (N * stride) */ - uint8_t e4fs_log_gpf; /* FLEX_BG group size */ - uint8_t e4fs_char_pad2; - uint16_t e4fs_pad; - uint32_t reserved2[162]; /* Padding to the end of the block */ + uint32_t e4fs_flags; /* miscellaneous flags */ + uint16_t e4fs_raid_stride; /* RAID stride */ + uint16_t e4fs_mmpintv; /* number of seconds to wait in MMP checking */ + uint64_t e4fs_mmpblk; /* block for multi-mount protection */ + uint32_t e4fs_raid_stripe_wid; /* blocks on all data disks (N * stride) */ + uint8_t e4fs_log_gpf; /* FLEX_BG group size */ + uint8_t e4fs_chksum_type; /* metadata checksum algorithm used */ + uint8_t e4fs_encrypt; /* versioning level for encryption */ + uint8_t e4fs_reserved_pad; + uint64_t e4fs_kbytes_written; /* number of lifetime kilobytes */ + uint32_t e4fs_snapinum; /* inode number of active snapshot */ + uint32_t e4fs_snapid; /* sequential ID of active snapshot */ + uint64_t e4fs_snaprbcount; /* reserved blocks for active snapshot */ + uint32_t e4fs_snaplist; /* inode number for on-disk snapshot */ + uint32_t e4fs_errcount; /* number of file system errors */ + uint32_t e4fs_first_errtime; /* first time an error happened */ + uint32_t e4fs_first_errino; /* inode involved in first error */ + uint64_t e4fs_first_errblk; /* block involved of first error */ + uint8_t e4fs_first_errfunc[32]; /* function where error happened */ + uint32_t e4fs_first_errline; /* line number where error happened */ + uint32_t e4fs_last_errtime; /* most recent time of an error */ + uint32_t e4fs_last_errino; /* inode involved in last error */ + uint32_t e4fs_last_errline; /* line number where error happened */ + uint64_t e4fs_last_errblk; /* block involved of last error */ + uint8_t e4fs_last_errfunc[32]; /* function where error happened */ + uint8_t e4fs_mount_opts[64]; + uint32_t e4fs_usrquota_inum; /* inode for tracking user quota */ + uint32_t e4fs_grpquota_inum; /* inode for tracking group quota */ + uint32_t e4fs_overhead_clusters; /* overhead blocks/clusters */ + uint32_t e4fs_backup_bgs[2]; /* groups with sparse_super2 SBs */ + uint8_t e4fs_encrypt_algos[4]; /* encryption algorithms in use */ + uint8_t e4fs_encrypt_pw_salt[16]; /* salt used for string2key */ + uint32_t e4fs_lpf_ino; /* location of the lost+found inode */ + uint32_t e4fs_proj_quota_inum; /* inode for tracking project quota */ + uint32_t e4fs_chksum_seed; /* checksum seed */ + uint32_t e4fs_reserved[98]; /* padding to the end of the block */ + uint32_t e4fs_sbchksum; /* superblock checksum */ }; /* diff --git a/sys/geom/sched/g_sched.c b/sys/geom/sched/g_sched.c index ea1fd41..d4e3ca0 100644 --- a/sys/geom/sched/g_sched.c +++ b/sys/geom/sched/g_sched.c @@ -1316,7 +1316,8 @@ g_sched_destroy(struct g_geom *gp, boolean_t force) gsp->gs_fini(sc->sc_data); g_gsched_unref(gsp); sc->sc_gsched = NULL; - } + } else + error = 0; if ((sc->sc_flags & G_SCHED_PROXYING) && oldpp) { error = g_destroy_proxy(gp, oldpp); diff --git a/sys/i386/conf/GENERIC.hints b/sys/i386/conf/GENERIC.hints index fb30240..ffd10a6 100644 --- a/sys/i386/conf/GENERIC.hints +++ b/sys/i386/conf/GENERIC.hints @@ -38,6 +38,5 @@ hint.atrtc.0.irq="8" hint.attimer.0.at="isa" hint.attimer.0.port="0x40" hint.attimer.0.irq="0" -hint.wbwd.0.at="isa" hint.acpi_throttle.0.disabled="1" hint.p4tcc.0.disabled="1" diff --git a/sys/i386/i386/vm_machdep.c b/sys/i386/i386/vm_machdep.c index 62e99aa..fe6297a 100644 --- a/sys/i386/i386/vm_machdep.c +++ b/sys/i386/i386/vm_machdep.c @@ -127,8 +127,8 @@ get_pcb_user_save_td(struct thread *td) vm_offset_t p; p = td->td_kstack + td->td_kstack_pages * PAGE_SIZE - - cpu_max_ext_state_size; - KASSERT((p % 64) == 0, ("Unaligned pcb_user_save area")); + roundup2(cpu_max_ext_state_size, XSAVE_AREA_ALIGN); + KASSERT((p % XSAVE_AREA_ALIGN) == 0, ("Unaligned pcb_user_save area")); return ((union savefpu *)p); } @@ -147,7 +147,8 @@ get_pcb_td(struct thread *td) vm_offset_t p; p = td->td_kstack + td->td_kstack_pages * PAGE_SIZE - - cpu_max_ext_state_size - sizeof(struct pcb); + roundup2(cpu_max_ext_state_size, XSAVE_AREA_ALIGN) - + sizeof(struct pcb); return ((struct pcb *)p); } diff --git a/sys/i386/include/intr_machdep.h b/sys/i386/include/intr_machdep.h index 0a2a6d5..d1be076 100644 --- a/sys/i386/include/intr_machdep.h +++ b/sys/i386/include/intr_machdep.h @@ -83,7 +83,7 @@ #ifndef LOCORE -typedef void inthand_t(u_int cs, u_int ef, u_int esp, u_int ss); +typedef void inthand_t(void); #define IDTVEC(name) __CONCAT(X,name) diff --git a/sys/i386/include/md_var.h b/sys/i386/include/md_var.h index f854373..c4c9ca9 100644 --- a/sys/i386/include/md_var.h +++ b/sys/i386/include/md_var.h @@ -46,7 +46,6 @@ extern int szosigcode; #endif extern uint32_t *vm_page_dump; -typedef void alias_for_inthand_t(u_int cs, u_int ef, u_int esp, u_int ss); struct segment_descriptor; union savefpu; diff --git a/sys/i386/linux/linux_dummy.c b/sys/i386/linux/linux_dummy.c index 4c0cad0..fd73c9d 100644 --- a/sys/i386/linux/linux_dummy.c +++ b/sys/i386/linux/linux_dummy.c @@ -71,7 +71,6 @@ DUMMY(pivot_root); DUMMY(mincore); DUMMY(lookup_dcookie); DUMMY(remap_file_pages); -DUMMY(fstatfs64); DUMMY(mbind); DUMMY(get_mempolicy); DUMMY(set_mempolicy); diff --git a/sys/i386/linux/linux_proto.h b/sys/i386/linux/linux_proto.h index 884292f..7e260d8 100644 --- a/sys/i386/linux/linux_proto.h +++ b/sys/i386/linux/linux_proto.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/i386/linux/syscalls.master 293907 2016-01-14 10:13:58Z glebius + * created from FreeBSD: head/sys/i386/linux/syscalls.master 297061 2016-03-20 13:21:20Z dchagin */ #ifndef _LINUX_SYSPROTO_H_ @@ -848,7 +848,9 @@ struct linux_statfs64_args { char buf_l_[PADL_(struct l_statfs64_buf *)]; struct l_statfs64_buf * buf; char buf_r_[PADR_(struct l_statfs64_buf *)]; }; struct linux_fstatfs64_args { - register_t dummy; + char fd_l_[PADL_(l_uint)]; l_uint fd; char fd_r_[PADR_(l_uint)]; + char bufsize_l_[PADL_(size_t)]; size_t bufsize; char bufsize_r_[PADR_(size_t)]; + char buf_l_[PADL_(struct l_statfs64_buf *)]; struct l_statfs64_buf * buf; char buf_r_[PADR_(struct l_statfs64_buf *)]; }; struct linux_tgkill_args { char tgid_l_[PADL_(int)]; int tgid; char tgid_r_[PADR_(int)]; diff --git a/sys/i386/linux/linux_syscall.h b/sys/i386/linux/linux_syscall.h index 1be670a..918f838 100644 --- a/sys/i386/linux/linux_syscall.h +++ b/sys/i386/linux/linux_syscall.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/i386/linux/syscalls.master 293907 2016-01-14 10:13:58Z glebius + * created from FreeBSD: head/sys/i386/linux/syscalls.master 297061 2016-03-20 13:21:20Z dchagin */ #define LINUX_SYS_linux_exit 1 diff --git a/sys/i386/linux/linux_syscalls.c b/sys/i386/linux/linux_syscalls.c index 68a81ef..4a5cd4c 100644 --- a/sys/i386/linux/linux_syscalls.c +++ b/sys/i386/linux/linux_syscalls.c @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/i386/linux/syscalls.master 293907 2016-01-14 10:13:58Z glebius + * created from FreeBSD: head/sys/i386/linux/syscalls.master 297061 2016-03-20 13:21:20Z dchagin */ const char *linux_syscallnames[] = { diff --git a/sys/i386/linux/linux_sysent.c b/sys/i386/linux/linux_sysent.c index d853d1d..181141d 100644 --- a/sys/i386/linux/linux_sysent.c +++ b/sys/i386/linux/linux_sysent.c @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/i386/linux/syscalls.master 293907 2016-01-14 10:13:58Z glebius + * created from FreeBSD: head/sys/i386/linux/syscalls.master 297061 2016-03-20 13:21:20Z dchagin */ #include <sys/param.h> @@ -287,7 +287,7 @@ struct sysent linux_sysent[] = { { AS(linux_clock_getres_args), (sy_call_t *)linux_clock_getres, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC }, /* 266 = linux_clock_getres */ { AS(linux_clock_nanosleep_args), (sy_call_t *)linux_clock_nanosleep, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC }, /* 267 = linux_clock_nanosleep */ { AS(linux_statfs64_args), (sy_call_t *)linux_statfs64, AUE_STATFS, NULL, 0, 0, 0, SY_THR_STATIC }, /* 268 = linux_statfs64 */ - { 0, (sy_call_t *)linux_fstatfs64, AUE_FSTATFS, NULL, 0, 0, 0, SY_THR_STATIC }, /* 269 = linux_fstatfs64 */ + { AS(linux_fstatfs64_args), (sy_call_t *)linux_fstatfs64, AUE_FSTATFS, NULL, 0, 0, 0, SY_THR_STATIC }, /* 269 = linux_fstatfs64 */ { AS(linux_tgkill_args), (sy_call_t *)linux_tgkill, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC }, /* 270 = linux_tgkill */ { AS(linux_utimes_args), (sy_call_t *)linux_utimes, AUE_UTIMES, NULL, 0, 0, 0, SY_THR_STATIC }, /* 271 = linux_utimes */ { AS(linux_fadvise64_64_args), (sy_call_t *)linux_fadvise64_64, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC }, /* 272 = linux_fadvise64_64 */ diff --git a/sys/i386/linux/linux_systrace_args.c b/sys/i386/linux/linux_systrace_args.c index 85fc9ca..7feba47 100644 --- a/sys/i386/linux/linux_systrace_args.c +++ b/sys/i386/linux/linux_systrace_args.c @@ -1870,7 +1870,11 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) } /* linux_fstatfs64 */ case 269: { - *n_args = 0; + struct linux_fstatfs64_args *p = params; + iarg[0] = p->fd; /* l_uint */ + uarg[1] = p->bufsize; /* size_t */ + uarg[2] = (intptr_t) p->buf; /* struct l_statfs64_buf * */ + *n_args = 3; break; } /* linux_tgkill */ @@ -5265,6 +5269,19 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; /* linux_fstatfs64 */ case 269: + switch(ndx) { + case 0: + p = "l_uint"; + break; + case 1: + p = "size_t"; + break; + case 2: + p = "struct l_statfs64_buf *"; + break; + default: + break; + }; break; /* linux_tgkill */ case 270: @@ -7138,6 +7155,9 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; /* linux_fstatfs64 */ case 269: + if (ndx == 0 || ndx == 1) + p = "int"; + break; /* linux_tgkill */ case 270: if (ndx == 0 || ndx == 1) diff --git a/sys/i386/linux/syscalls.master b/sys/i386/linux/syscalls.master index 248770c..1032ef0 100644 --- a/sys/i386/linux/syscalls.master +++ b/sys/i386/linux/syscalls.master @@ -452,7 +452,7 @@ 267 AUE_NULL STD { int linux_clock_nanosleep(clockid_t which, int flags, \ struct l_timespec *rqtp, struct l_timespec *rmtp); } 268 AUE_STATFS STD { int linux_statfs64(char *path, size_t bufsize, struct l_statfs64_buf *buf); } -269 AUE_FSTATFS STD { int linux_fstatfs64(void); } +269 AUE_FSTATFS STD { int linux_fstatfs64(l_uint fd, size_t bufsize, struct l_statfs64_buf *buf); } 270 AUE_NULL STD { int linux_tgkill(int tgid, int pid, int sig); } 271 AUE_UTIMES STD { int linux_utimes(char *fname, \ struct l_timeval *tptr); } diff --git a/sys/isa/isa_common.c b/sys/isa/isa_common.c index d2c8113..83e988c 100644 --- a/sys/isa/isa_common.c +++ b/sys/isa/isa_common.c @@ -629,10 +629,10 @@ isa_print_all_resources(device_t dev) if (STAILQ_FIRST(rl) || device_get_flags(dev)) retval += printf(" at"); - retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx"); - retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#lx"); - retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); - retval += resource_list_print_type(rl, "drq", SYS_RES_DRQ, "%ld"); + retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#jx"); + retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#jx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); + retval += resource_list_print_type(rl, "drq", SYS_RES_DRQ, "%jd"); if (device_get_flags(dev)) retval += printf(" flags %#x", device_get_flags(dev)); #ifdef ISAPNP diff --git a/sys/kern/imgact_binmisc.c b/sys/kern/imgact_binmisc.c index dd57717..39ca156 100644 --- a/sys/kern/imgact_binmisc.c +++ b/sys/kern/imgact_binmisc.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2013-15, Stacey D. Son + * Copyright (c) 2013-16, Stacey D. Son * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -220,16 +220,17 @@ imgact_binmisc_add_entry(ximgact_binmisc_entry_t *xbe) { imgact_binmisc_entry_t *ibe; char *p; + int cnt; if (xbe->xbe_msize > IBE_MAGIC_MAX) return (EINVAL); - for(p = xbe->xbe_name; *p != 0; p++) - if (!isascii((int)*p)) + for(cnt = 0, p = xbe->xbe_name; *p != 0; cnt++, p++) + if (cnt >= IBE_NAME_MAX || !isascii((int)*p)) return (EINVAL); - for(p = xbe->xbe_interpreter; *p != 0; p++) - if (!isascii((int)*p)) + for(cnt = 0, p = xbe->xbe_interpreter; *p != 0; cnt++, p++) + if (cnt >= IBE_INTERP_LEN_MAX || !isascii((int)*p)) return (EINVAL); /* Make sure we don't have any invalid #'s. */ @@ -266,8 +267,6 @@ imgact_binmisc_add_entry(ximgact_binmisc_entry_t *xbe) /* Preallocate a new entry. */ ibe = imgact_binmisc_new_entry(xbe); - if (!ibe) - return (ENOMEM); SLIST_INSERT_HEAD(&interpreter_list, ibe, link); interp_list_entry_count++; diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index 43d4800..0bed714 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -1370,10 +1370,6 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) * and write it out following the notes. */ hdr = malloc(hdrsize, M_TEMP, M_WAITOK); - if (hdr == NULL) { - error = EINVAL; - goto done; - } error = __elfN(corehdr)(¶ms, seginfo.count, hdr, hdrsize, ¬elst, notesz); diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c index 96c5229..47f2f90 100644 --- a/sys/kern/init_sysent.c +++ b/sys/kern/init_sysent.c @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/kern/syscalls.master 296572 2016-03-09 19:05:11Z jhb + * created from FreeBSD: head/sys/kern/syscalls.master 297167 2016-03-21 21:37:33Z jhb */ #include "opt_compat.h" diff --git a/sys/kern/kern_condvar.c b/sys/kern/kern_condvar.c index 8061829..6f891e0 100644 --- a/sys/kern/kern_condvar.c +++ b/sys/kern/kern_condvar.c @@ -122,15 +122,8 @@ _cv_wait(struct cv *cvp, struct lock_object *lock) "Waiting on \"%s\"", cvp->cv_description); class = LOCK_CLASS(lock); - if (cold || SCHEDULER_STOPPED()) { - /* - * During autoconfiguration, just give interrupts - * a chance, then just return. Don't run any other - * thread or panic below, in case this is the idle - * process and already asleep. - */ + if (SCHEDULER_STOPPED()) return; - } sleepq_lock(cvp); @@ -183,13 +176,7 @@ _cv_wait_unlock(struct cv *cvp, struct lock_object *lock) ("cv_wait_unlock cannot be used with Giant")); class = LOCK_CLASS(lock); - if (cold || SCHEDULER_STOPPED()) { - /* - * During autoconfiguration, just give interrupts - * a chance, then just return. Don't run any other - * thread or panic below, in case this is the idle - * process and already asleep. - */ + if (SCHEDULER_STOPPED()) { class->lc_unlock(lock); return; } @@ -240,15 +227,8 @@ _cv_wait_sig(struct cv *cvp, struct lock_object *lock) "Waiting on \"%s\"", cvp->cv_description); class = LOCK_CLASS(lock); - if (cold || SCHEDULER_STOPPED()) { - /* - * After a panic, or during autoconfiguration, just give - * interrupts a chance, then just return; don't run any other - * procs or panic below, in case this is the idle process and - * already asleep. - */ + if (SCHEDULER_STOPPED()) return (0); - } sleepq_lock(cvp); @@ -307,15 +287,8 @@ _cv_timedwait_sbt(struct cv *cvp, struct lock_object *lock, sbintime_t sbt, "Waiting on \"%s\"", cvp->cv_description); class = LOCK_CLASS(lock); - if (cold || SCHEDULER_STOPPED()) { - /* - * After a panic, or during autoconfiguration, just give - * interrupts a chance, then just return; don't run any other - * thread or panic below, in case this is the idle process and - * already asleep. - */ - return 0; - } + if (SCHEDULER_STOPPED()) + return (0); sleepq_lock(cvp); @@ -376,15 +349,8 @@ _cv_timedwait_sig_sbt(struct cv *cvp, struct lock_object *lock, "Waiting on \"%s\"", cvp->cv_description); class = LOCK_CLASS(lock); - if (cold || SCHEDULER_STOPPED()) { - /* - * After a panic, or during autoconfiguration, just give - * interrupts a chance, then just return; don't run any other - * thread or panic below, in case this is the idle process and - * already asleep. - */ - return 0; - } + if (SCHEDULER_STOPPED()) + return (0); sleepq_lock(cvp); diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index 00bd54b..b37adcc 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -3958,7 +3958,7 @@ badfo_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred, static int badfo_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio, struct uio *trl_uio, off_t offset, size_t nbytes, off_t *sent, int flags, - int kflags, struct thread *td) + struct thread *td) { return (EBADF); @@ -4044,7 +4044,7 @@ invfo_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred, int invfo_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio, struct uio *trl_uio, off_t offset, size_t nbytes, off_t *sent, int flags, - int kflags, struct thread *td) + struct thread *td) { return (EINVAL); diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index 69f0774..7c88fe0 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -1570,8 +1570,6 @@ exec_register(execsw_arg) for (es = execsw; *es; es++) count++; newexecsw = malloc(count * sizeof(*es), M_TEMP, M_WAITOK); - if (newexecsw == NULL) - return (ENOMEM); xs = newexecsw; if (execsw) for (es = execsw; *es; es++) @@ -1604,8 +1602,6 @@ exec_unregister(execsw_arg) if (*es != execsw_arg) count++; newexecsw = malloc(count * sizeof(*es), M_TEMP, M_WAITOK); - if (newexecsw == NULL) - return (ENOMEM); xs = newexecsw; for (es = execsw; *es; es++) if (*es != execsw_arg) diff --git a/sys/kern/kern_fail.c b/sys/kern/kern_fail.c index 3737aa3..ec466dd 100644 --- a/sys/kern/kern_fail.c +++ b/sys/kern/kern_fail.c @@ -52,17 +52,25 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_stack.h" + #include <sys/ctype.h> #include <sys/errno.h> #include <sys/fail.h> #include <sys/kernel.h> #include <sys/libkern.h> +#include <sys/limits.h> #include <sys/lock.h> #include <sys/malloc.h> #include <sys/mutex.h> #include <sys/proc.h> #include <sys/sbuf.h> +#include <sys/sleepqueue.h> +#include <sys/sx.h> +#include <sys/sysctl.h> +#include <sys/types.h> +#include <machine/atomic.h> #include <machine/stdarg.h> #ifdef ILOG_DEFINE_FOR_FILE @@ -72,11 +80,45 @@ ILOG_DEFINE_FOR_FILE(L_ISI_FAIL_POINT, L_ILOG, fail_point); static MALLOC_DEFINE(M_FAIL_POINT, "Fail Points", "fail points system"); #define fp_free(ptr) free(ptr, M_FAIL_POINT) #define fp_malloc(size, flags) malloc((size), M_FAIL_POINT, (flags)) +#define fs_free(ptr) fp_free(ptr) +#define fs_malloc() fp_malloc(sizeof(struct fail_point_setting), \ + M_WAITOK | M_ZERO) + + /** + * These define the wchans that are used for sleeping, pausing respectively. + * They are chosen arbitrarily but need to be distinct to the failpoint and + * the sleep/pause distinction. + */ +#define FP_SLEEP_CHANNEL(fp) (void*)(fp) +#define FP_PAUSE_CHANNEL(fp) __DEVOLATILE(void*, &fp->fp_setting) + +/** + * Don't allow more than this many entries in a fail point set by sysctl. + * The 99.99...% case is to have 1 entry. I can't imagine having this many + * entries, so it should not limit us. Saves on re-mallocs while holding + * a non-sleepable lock. + */ +#define FP_MAX_ENTRY_COUNT 20 + +/* Used to drain sbufs to the sysctl output */ +int fail_sysctl_drain_func(void *, const char *, int); + +/* Head of tailq of struct fail_point_entry */ +TAILQ_HEAD(fail_point_entry_queue, fail_point_entry); + +/** + * fp entries garbage list; outstanding entries are cleaned up in the + * garbage collector + */ +STAILQ_HEAD(fail_point_setting_garbage, fail_point_setting); +static struct fail_point_setting_garbage fp_setting_garbage = + STAILQ_HEAD_INITIALIZER(fp_setting_garbage); +static struct mtx mtx_garbage_list; +MTX_SYSINIT(mtx_garbage_list, &mtx_garbage_list, "fail point garbage mtx", + MTX_SPIN); -static struct mtx g_fp_mtx; -MTX_SYSINIT(g_fp_mtx, &g_fp_mtx, "fail point mtx", MTX_DEF); -#define FP_LOCK() mtx_lock(&g_fp_mtx) -#define FP_UNLOCK() mtx_unlock(&g_fp_mtx) +static struct sx sx_fp_set; +SX_SYSINIT(sx_fp_set, &sx_fp_set, "fail point set sx"); /** * Failpoint types. @@ -90,7 +132,11 @@ enum fail_point_t { FAIL_POINT_BREAK, /**< break into the debugger */ FAIL_POINT_PRINT, /**< print a message */ FAIL_POINT_SLEEP, /**< sleep for some msecs */ - FAIL_POINT_NUMTYPES + FAIL_POINT_PAUSE, /**< sleep until failpoint is set to off */ + FAIL_POINT_YIELD, /**< yield the cpu */ + FAIL_POINT_DELAY, /**< busy wait the cpu */ + FAIL_POINT_NUMTYPES, + FAIL_POINT_INVALID = -1 }; static struct { @@ -104,53 +150,307 @@ static struct { [FAIL_POINT_BREAK] = FP_TYPE_NM_LEN("break"), [FAIL_POINT_PRINT] = FP_TYPE_NM_LEN("print"), [FAIL_POINT_SLEEP] = FP_TYPE_NM_LEN("sleep"), + [FAIL_POINT_PAUSE] = FP_TYPE_NM_LEN("pause"), + [FAIL_POINT_YIELD] = FP_TYPE_NM_LEN("yield"), + [FAIL_POINT_DELAY] = FP_TYPE_NM_LEN("delay"), }; +#define FE_COUNT_UNTRACKED (INT_MIN) + /** * Internal structure tracking a single term of a complete failpoint. * @ingroup failpoint_private */ struct fail_point_entry { - enum fail_point_t fe_type; /**< type of entry */ + volatile bool fe_stale; + enum fail_point_t fe_type; /**< type of entry */ int fe_arg; /**< argument to type (e.g. return value) */ int fe_prob; /**< likelihood of firing in millionths */ - int fe_count; /**< number of times to fire, 0 means always */ + int fe_count; /**< number of times to fire, -1 means infinite */ pid_t fe_pid; /**< only fail for this process */ - TAILQ_ENTRY(fail_point_entry) fe_entries; /**< next entry in fail point */ + struct fail_point *fe_parent; /**< backpointer to fp */ + TAILQ_ENTRY(fail_point_entry) fe_entries; /**< next entry ptr */ }; +struct fail_point_setting { + STAILQ_ENTRY(fail_point_setting) fs_garbage_link; + struct fail_point_entry_queue fp_entry_queue; + struct fail_point * fs_parent; + struct mtx feq_mtx; /* Gives fail_point_pause something to do. */ +}; + +/** + * Defines stating the equivalent of probablilty one (100%) + */ +enum { + PROB_MAX = 1000000, /* probability between zero and this number */ + PROB_DIGITS = 6 /* number of zero's in above number */ +}; + +/* Get a ref on an fp's fp_setting */ +static inline struct fail_point_setting *fail_point_setting_get_ref( + struct fail_point *fp); +/* Release a ref on an fp_setting */ +static inline void fail_point_setting_release_ref(struct fail_point *fp); +/* Allocate and initialize a struct fail_point_setting */ +static struct fail_point_setting *fail_point_setting_new(struct + fail_point *); +/* Free a struct fail_point_setting */ +static void fail_point_setting_destroy(struct fail_point_setting *fp_setting); +/* Allocate and initialize a struct fail_point_entry */ +static struct fail_point_entry *fail_point_entry_new(struct + fail_point_setting *); +/* Free a struct fail_point_entry */ +static void fail_point_entry_destroy(struct fail_point_entry *fp_entry); +/* Append fp setting to garbage list */ +static inline void fail_point_setting_garbage_append( + struct fail_point_setting *fp_setting); +/* Swap fp's setting with fp_setting_new */ +static inline struct fail_point_setting * + fail_point_swap_settings(struct fail_point *fp, + struct fail_point_setting *fp_setting_new); +/* Free up any zero-ref setting in the garbage queue */ +static void fail_point_garbage_collect(void); +/* If this fail point's setting are empty, then swap it out to NULL. */ +static inline void fail_point_eval_swap_out(struct fail_point *fp, + struct fail_point_setting *fp_setting); + +bool +fail_point_is_off(struct fail_point *fp) +{ + bool return_val; + struct fail_point_setting *fp_setting; + struct fail_point_entry *ent; + + return_val = true; + + fp_setting = fail_point_setting_get_ref(fp); + if (fp_setting != NULL) { + TAILQ_FOREACH(ent, &fp_setting->fp_entry_queue, + fe_entries) { + if (!ent->fe_stale) { + return_val = false; + break; + } + } + } + fail_point_setting_release_ref(fp); + + return (return_val); +} + +/* Allocate and initialize a struct fail_point_setting */ +static struct fail_point_setting * +fail_point_setting_new(struct fail_point *fp) +{ + struct fail_point_setting *fs_new; + + fs_new = fs_malloc(); + fs_new->fs_parent = fp; + TAILQ_INIT(&fs_new->fp_entry_queue); + mtx_init(&fs_new->feq_mtx, "fail point entries", NULL, MTX_SPIN); + + fail_point_setting_garbage_append(fs_new); + + return (fs_new); +} + +/* Free a struct fail_point_setting */ +static void +fail_point_setting_destroy(struct fail_point_setting *fp_setting) +{ + struct fail_point_entry *ent; + + while (!TAILQ_EMPTY(&fp_setting->fp_entry_queue)) { + ent = TAILQ_FIRST(&fp_setting->fp_entry_queue); + TAILQ_REMOVE(&fp_setting->fp_entry_queue, ent, fe_entries); + fail_point_entry_destroy(ent); + } + + fs_free(fp_setting); +} + +/* Allocate and initialize a struct fail_point_entry */ +static struct fail_point_entry * +fail_point_entry_new(struct fail_point_setting *fp_setting) +{ + struct fail_point_entry *fp_entry; + + fp_entry = fp_malloc(sizeof(struct fail_point_entry), + M_WAITOK | M_ZERO); + fp_entry->fe_parent = fp_setting->fs_parent; + fp_entry->fe_prob = PROB_MAX; + fp_entry->fe_pid = NO_PID; + fp_entry->fe_count = FE_COUNT_UNTRACKED; + TAILQ_INSERT_TAIL(&fp_setting->fp_entry_queue, fp_entry, + fe_entries); + + return (fp_entry); +} + +/* Free a struct fail_point_entry */ +static void +fail_point_entry_destroy(struct fail_point_entry *fp_entry) +{ + + fp_free(fp_entry); +} + +/* Get a ref on an fp's fp_setting */ +static inline struct fail_point_setting * +fail_point_setting_get_ref(struct fail_point *fp) +{ + struct fail_point_setting *fp_setting; + + /* Invariant: if we have a ref, our pointer to fp_setting is safe */ + atomic_add_acq_32(&fp->fp_ref_cnt, 1); + fp_setting = fp->fp_setting; + + return (fp_setting); +} + +/* Release a ref on an fp_setting */ +static inline void +fail_point_setting_release_ref(struct fail_point *fp) +{ + + KASSERT(&fp->fp_ref_cnt > 0, ("Attempting to deref w/no refs")); + atomic_subtract_rel_32(&fp->fp_ref_cnt, 1); +} + +/* Append fp entries to fp garbage list */ +static inline void +fail_point_setting_garbage_append(struct fail_point_setting *fp_setting) +{ + + mtx_lock_spin(&mtx_garbage_list); + STAILQ_INSERT_TAIL(&fp_setting_garbage, fp_setting, + fs_garbage_link); + mtx_unlock_spin(&mtx_garbage_list); +} + +/* Swap fp's entries with fp_setting_new */ +static struct fail_point_setting * +fail_point_swap_settings(struct fail_point *fp, + struct fail_point_setting *fp_setting_new) +{ + struct fail_point_setting *fp_setting_old; + + fp_setting_old = fp->fp_setting; + fp->fp_setting = fp_setting_new; + + return (fp_setting_old); +} + +static inline void +fail_point_eval_swap_out(struct fail_point *fp, + struct fail_point_setting *fp_setting) +{ + + /* We may have already been swapped out and replaced; ignore. */ + if (fp->fp_setting == fp_setting) + fail_point_swap_settings(fp, NULL); +} + +/* Free up any zero-ref entries in the garbage queue */ +static void +fail_point_garbage_collect() +{ + struct fail_point_setting *fs_current, *fs_next; + struct fail_point_setting_garbage fp_ents_free_list; + + /** + * We will transfer the entries to free to fp_ents_free_list while holding + * the spin mutex, then free it after we drop the lock. This avoids + * triggering witness due to sleepable mutexes in the memory + * allocator. + */ + STAILQ_INIT(&fp_ents_free_list); + + mtx_lock_spin(&mtx_garbage_list); + STAILQ_FOREACH_SAFE(fs_current, &fp_setting_garbage, fs_garbage_link, + fs_next) { + if (fs_current->fs_parent->fp_setting != fs_current && + fs_current->fs_parent->fp_ref_cnt == 0) { + STAILQ_REMOVE(&fp_setting_garbage, fs_current, + fail_point_setting, fs_garbage_link); + STAILQ_INSERT_HEAD(&fp_ents_free_list, fs_current, + fs_garbage_link); + } + } + mtx_unlock_spin(&mtx_garbage_list); + + STAILQ_FOREACH_SAFE(fs_current, &fp_ents_free_list, fs_garbage_link, + fs_next) + fail_point_setting_destroy(fs_current); +} + +/* Drain out all refs from this fail point */ +static inline void +fail_point_drain(struct fail_point *fp, int expected_ref) +{ + struct fail_point_setting *entries; + + entries = fail_point_swap_settings(fp, NULL); + /** + * We have unpaused all threads; so we will wait no longer + * than the time taken for the longest remaining sleep, or + * the length of time of a long-running code block. + */ + while (fp->fp_ref_cnt > expected_ref) { + wakeup(FP_PAUSE_CHANNEL(fp)); + tsleep(&fp, PWAIT, "fail_point_drain", hz / 100); + } + fail_point_swap_settings(fp, entries); +} + static inline void -fail_point_sleep(struct fail_point *fp, struct fail_point_entry *ent, - int msecs, enum fail_point_return_code *pret) +fail_point_pause(struct fail_point *fp, enum fail_point_return_code *pret, + struct mtx *mtx_sleep) { - /* convert from millisecs to ticks, rounding up */ - int timo = ((msecs * hz) + 999) / 1000; + + if (fp->fp_pre_sleep_fn) + fp->fp_pre_sleep_fn(fp->fp_pre_sleep_arg); + + msleep_spin(FP_PAUSE_CHANNEL(fp), mtx_sleep, "failpt", 0); + + if (fp->fp_post_sleep_fn) + fp->fp_post_sleep_fn(fp->fp_post_sleep_arg); +} + +static inline void +fail_point_sleep(struct fail_point *fp, int msecs, + enum fail_point_return_code *pret) +{ + int timo; + + /* Convert from millisecs to ticks, rounding up */ + timo = howmany(msecs * hz, 1000); if (timo > 0) { - if (fp->fp_sleep_fn == NULL) { - msleep(fp, &g_fp_mtx, PWAIT, "failpt", timo); + if (!(fp->fp_flags & FAIL_POINT_USE_TIMEOUT_PATH)) { + if (fp->fp_pre_sleep_fn) + fp->fp_pre_sleep_fn(fp->fp_pre_sleep_arg); + + tsleep(FP_SLEEP_CHANNEL(fp), PWAIT, "failpt", timo); + + if (fp->fp_post_sleep_fn) + fp->fp_post_sleep_fn(fp->fp_post_sleep_arg); } else { - timeout(fp->fp_sleep_fn, fp->fp_sleep_arg, timo); + if (fp->fp_pre_sleep_fn) + fp->fp_pre_sleep_fn(fp->fp_pre_sleep_arg); + + timeout(fp->fp_post_sleep_fn, fp->fp_post_sleep_arg, + timo); *pret = FAIL_POINT_RC_QUEUED; } } } - -/** - * Defines stating the equivalent of probablilty one (100%) - */ -enum { - PROB_MAX = 1000000, /* probability between zero and this number */ - PROB_DIGITS = 6, /* number of zero's in above number */ -}; - -static char *parse_fail_point(struct fail_point_entries *, char *); -static char *parse_term(struct fail_point_entries *, char *); +static char *parse_fail_point(struct fail_point_setting *, char *); +static char *parse_term(struct fail_point_setting *, char *); static char *parse_number(int *out_units, int *out_decimal, char *); static char *parse_type(struct fail_point_entry *, char *); -static void free_entry(struct fail_point_entries *, struct fail_point_entry *); -static void clear_entries(struct fail_point_entries *); /** * Initialize a fail_point. The name is formed in a printf-like fashion @@ -167,7 +467,7 @@ fail_point_init(struct fail_point *fp, const char *fmt, ...) char *name; int n; - TAILQ_INIT(&fp->fp_entries); + fp->fp_setting = NULL; fp->fp_flags = 0; /* Figure out the size of the name. */ @@ -185,25 +485,33 @@ fail_point_init(struct fail_point *fp, const char *fmt, ...) fp->fp_name = name; fp->fp_location = ""; fp->fp_flags |= FAIL_POINT_DYNAMIC_NAME; - fp->fp_sleep_fn = NULL; - fp->fp_sleep_arg = NULL; + fp->fp_pre_sleep_fn = NULL; + fp->fp_pre_sleep_arg = NULL; + fp->fp_post_sleep_fn = NULL; + fp->fp_post_sleep_arg = NULL; } /** - * Free the resources held by a fail_point. - * + * Free the resources held by a fail_point, and wake any paused threads. + * Thou shalt not allow threads to hit this fail point after you enter this + * function, nor shall you call this multiple times for a given fp. * @ingroup failpoint */ void fail_point_destroy(struct fail_point *fp) { + fail_point_drain(fp, 0); + if ((fp->fp_flags & FAIL_POINT_DYNAMIC_NAME) != 0) { fp_free(__DECONST(void *, fp->fp_name)); fp->fp_name = NULL; } fp->fp_flags = 0; - clear_entries(&fp->fp_entries); + + sx_xlock(&sx_fp_set); + fail_point_garbage_collect(); + sx_xunlock(&sx_fp_set); } /** @@ -216,21 +524,51 @@ fail_point_destroy(struct fail_point *fp) enum fail_point_return_code fail_point_eval_nontrivial(struct fail_point *fp, int *return_value) { - enum fail_point_return_code ret = FAIL_POINT_RC_CONTINUE; - struct fail_point_entry *ent, *next; + bool execute = false; + struct fail_point_entry *ent; + struct fail_point_setting *fp_setting; + enum fail_point_return_code ret; + int cont; + int count; int msecs; + int usecs; + + ret = FAIL_POINT_RC_CONTINUE; + cont = 0; /* don't continue by default */ + + fp_setting = fail_point_setting_get_ref(fp); + if (fp_setting == NULL) + goto abort; - FP_LOCK(); + TAILQ_FOREACH(ent, &fp_setting->fp_entry_queue, fe_entries) { - TAILQ_FOREACH_SAFE(ent, &fp->fp_entries, fe_entries, next) { - int cont = 0; /* don't continue by default */ + if (ent->fe_stale) + continue; if (ent->fe_prob < PROB_MAX && ent->fe_prob < random() % PROB_MAX) continue; + if (ent->fe_pid != NO_PID && ent->fe_pid != curproc->p_pid) continue; + if (ent->fe_count != FE_COUNT_UNTRACKED) { + count = ent->fe_count; + while (count > 0) { + if (atomic_cmpset_32(&ent->fe_count, count, count - 1)) { + count--; + execute = true; + break; + } + count = ent->fe_count; + } + if (execute == false) + /* We lost the race; consider the entry stale and bail now */ + continue; + if (count == 0) + ent->fe_stale = true; + } + switch (ent->fe_type) { case FAIL_POINT_PANIC: panic("fail point %s panicking", fp->fp_name); @@ -244,7 +582,7 @@ fail_point_eval_nontrivial(struct fail_point *fp, int *return_value) case FAIL_POINT_BREAK: printf("fail point %s breaking to debugger\n", - fp->fp_name); + fp->fp_name); breakpoint(); break; @@ -254,51 +592,95 @@ fail_point_eval_nontrivial(struct fail_point *fp, int *return_value) break; case FAIL_POINT_SLEEP: - /* - * Free the entry now if necessary, since - * we're about to drop the mutex and sleep. - */ msecs = ent->fe_arg; - if (ent->fe_count > 0 && --ent->fe_count == 0) { - free_entry(&fp->fp_entries, ent); - ent = NULL; - } - if (msecs) - fail_point_sleep(fp, ent, msecs, &ret); + fail_point_sleep(fp, msecs, &ret); + break; + + case FAIL_POINT_PAUSE: + /** + * Pausing is inherently strange with multiple + * entries given our design. That is because some + * entries could be unreachable, for instance in cases like: + * pause->return. We can never reach the return entry. + * The sysctl layer actually truncates all entries after + * a pause for this reason. + */ + mtx_lock_spin(&fp_setting->feq_mtx); + fail_point_pause(fp, &ret, &fp_setting->feq_mtx); + mtx_unlock_spin(&fp_setting->feq_mtx); + break; + + case FAIL_POINT_YIELD: + kern_yield(-1); + break; + + case FAIL_POINT_DELAY: + usecs = ent->fe_arg; + DELAY(usecs); break; default: break; } - if (ent != NULL && ent->fe_count > 0 && --ent->fe_count == 0) - free_entry(&fp->fp_entries, ent); if (cont == 0) break; } - /* Get rid of "off"s at the end. */ - while ((ent = TAILQ_LAST(&fp->fp_entries, fail_point_entries)) && - ent->fe_type == FAIL_POINT_OFF) - free_entry(&fp->fp_entries, ent); + if (fail_point_is_off(fp)) + fail_point_eval_swap_out(fp, fp_setting); - FP_UNLOCK(); +abort: + fail_point_setting_release_ref(fp); return (ret); + } /** * Translate internal fail_point structure into human-readable text. */ static void -fail_point_get(struct fail_point *fp, struct sbuf *sb) +fail_point_get(struct fail_point *fp, struct sbuf *sb, + bool verbose) { struct fail_point_entry *ent; + struct fail_point_setting *fp_setting; + struct fail_point_entry *fp_entry_cpy; + int cnt_sleeping; + int idx; + int printed_entry_count; + + cnt_sleeping = 0; + idx = 0; + printed_entry_count = 0; + + fp_entry_cpy = fp_malloc(sizeof(struct fail_point_entry) * + (FP_MAX_ENTRY_COUNT + 1), M_WAITOK); + + fp_setting = fail_point_setting_get_ref(fp); - FP_LOCK(); + if (fp_setting != NULL) { + TAILQ_FOREACH(ent, &fp_setting->fp_entry_queue, fe_entries) { + if (ent->fe_stale) + continue; - TAILQ_FOREACH(ent, &fp->fp_entries, fe_entries) { + KASSERT(printed_entry_count < FP_MAX_ENTRY_COUNT, + ("FP entry list larger than allowed")); + + fp_entry_cpy[printed_entry_count] = *ent; + ++printed_entry_count; + } + } + fail_point_setting_release_ref(fp); + + /* This is our equivalent of a NULL terminator */ + fp_entry_cpy[printed_entry_count].fe_type = FAIL_POINT_INVALID; + + while (idx < printed_entry_count) { + ent = &fp_entry_cpy[idx]; + ++idx; if (ent->fe_prob < PROB_MAX) { int decimal = ent->fe_prob % (PROB_MAX / 100); int units = ent->fe_prob / (PROB_MAX / 100); @@ -313,7 +695,7 @@ fail_point_get(struct fail_point *fp, struct sbuf *sb) } sbuf_printf(sb, "%%"); } - if (ent->fe_count > 0) + if (ent->fe_count >= 0) sbuf_printf(sb, "%d*", ent->fe_count); sbuf_printf(sb, "%s", fail_type_strings[ent->fe_type].name); if (ent->fe_arg) @@ -323,10 +705,33 @@ fail_point_get(struct fail_point *fp, struct sbuf *sb) if (TAILQ_NEXT(ent, fe_entries)) sbuf_printf(sb, "->"); } - if (TAILQ_EMPTY(&fp->fp_entries)) + if (!printed_entry_count) sbuf_printf(sb, "off"); - FP_UNLOCK(); + fp_free(fp_entry_cpy); + if (verbose) { +#ifdef STACK + /* Print number of sleeping threads. queue=0 is the argument + * used by msleep when sending our threads to sleep. */ + sbuf_printf(sb, "\nsleeping_thread_stacks = {\n"); + sleepq_sbuf_print_stacks(sb, FP_SLEEP_CHANNEL(fp), 0, + &cnt_sleeping); + + sbuf_printf(sb, "},\n"); +#endif + sbuf_printf(sb, "sleeping_thread_count = %d,\n", + cnt_sleeping); + +#ifdef STACK + sbuf_printf(sb, "paused_thread_stacks = {\n"); + sleepq_sbuf_print_stacks(sb, FP_PAUSE_CHANNEL(fp), 0, + &cnt_sleeping); + + sbuf_printf(sb, "},\n"); +#endif + sbuf_printf(sb, "paused_thread_count = %d\n", + cnt_sleeping); + } } /** @@ -336,38 +741,91 @@ fail_point_get(struct fail_point *fp, struct sbuf *sb) static int fail_point_set(struct fail_point *fp, char *buf) { - int error = 0; struct fail_point_entry *ent, *ent_next; - struct fail_point_entries new_entries; + struct fail_point_setting *entries; + bool should_wake_paused; + bool should_truncate; + int error; + + error = 0; + should_wake_paused = false; + should_truncate = false; /* Parse new entries. */ - TAILQ_INIT(&new_entries); - if (!parse_fail_point(&new_entries, buf)) { - clear_entries(&new_entries); + /** + * ref protects our new malloc'd stuff from being garbage collected + * before we link it. + */ + fail_point_setting_get_ref(fp); + entries = fail_point_setting_new(fp); + if (parse_fail_point(entries, buf) == NULL) { + STAILQ_REMOVE(&fp_setting_garbage, entries, + fail_point_setting, fs_garbage_link); + fail_point_setting_destroy(entries); error = EINVAL; goto end; } - FP_LOCK(); - - /* Move new entries in. */ - TAILQ_SWAP(&fp->fp_entries, &new_entries, fail_point_entry, fe_entries); - clear_entries(&new_entries); + /** + * Transfer the entries we are going to keep to a new list. + * Get rid of useless zero probability entries, and entries with hit + * count 0. + * If 'off' is present, and it has no hit count set, then all entries + * after it are discarded since they are unreachable. + */ + TAILQ_FOREACH_SAFE(ent, &entries->fp_entry_queue, fe_entries, ent_next) { + if (ent->fe_prob == 0 || ent->fe_count == 0) { + printf("Discarding entry which cannot execute %s\n", + fail_type_strings[ent->fe_type].name); + TAILQ_REMOVE(&entries->fp_entry_queue, ent, + fe_entries); + fp_free(ent); + continue; + } else if (should_truncate) { + printf("Discarding unreachable entry %s\n", + fail_type_strings[ent->fe_type].name); + TAILQ_REMOVE(&entries->fp_entry_queue, ent, + fe_entries); + fp_free(ent); + continue; + } - /* Get rid of useless zero probability entries. */ - TAILQ_FOREACH_SAFE(ent, &fp->fp_entries, fe_entries, ent_next) { - if (ent->fe_prob == 0) - free_entry(&fp->fp_entries, ent); + if (ent->fe_type == FAIL_POINT_OFF) { + should_wake_paused = true; + if (ent->fe_count == FE_COUNT_UNTRACKED) { + should_truncate = true; + TAILQ_REMOVE(&entries->fp_entry_queue, ent, + fe_entries); + fp_free(ent); + } + } else if (ent->fe_type == FAIL_POINT_PAUSE) { + should_truncate = true; + } else if (ent->fe_type == FAIL_POINT_SLEEP && (fp->fp_flags & + FAIL_POINT_NONSLEEPABLE)) { + /** + * If this fail point is annotated as being in a + * non-sleepable ctx, convert sleep to delay and + * convert the msec argument to usecs. + */ + printf("Sleep call request on fail point in " + "non-sleepable context; using delay instead " + "of sleep\n"); + ent->fe_type = FAIL_POINT_DELAY; + ent->fe_arg *= 1000; + } } - /* Get rid of "off"s at the end. */ - while ((ent = TAILQ_LAST(&fp->fp_entries, fail_point_entries)) && - ent->fe_type == FAIL_POINT_OFF) - free_entry(&fp->fp_entries, ent); - - FP_UNLOCK(); + if (TAILQ_EMPTY(&entries->fp_entry_queue)) { + entries = fail_point_swap_settings(fp, NULL); + if (entries != NULL) + wakeup(FP_PAUSE_CHANNEL(fp)); + } else { + if (should_wake_paused) + wakeup(FP_PAUSE_CHANNEL(fp)); + fail_point_swap_settings(fp, entries); + } - end: +end: #ifdef IWARNING if (error) IWARNING("Failed to set %s %s to %s", @@ -377,6 +835,7 @@ fail_point_set(struct fail_point *fp, char *buf) fp->fp_name, fp->fp_location, buf); #endif /* IWARNING */ + fail_point_setting_release_ref(fp); return (error); } @@ -385,25 +844,33 @@ fail_point_set(struct fail_point *fp, char *buf) /** * Handle kernel failpoint set/get. */ + int fail_point_sysctl(SYSCTL_HANDLER_ARGS) { - struct fail_point *fp = arg1; - char *buf = NULL; + struct fail_point *fp; + char *buf; + struct sbuf *sb_check; struct sbuf sb; int error; - /* Retrieving */ - sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND | SBUF_INCLUDENUL); - fail_point_get(fp, &sb); - sbuf_trim(&sb); - error = sbuf_finish(&sb); - if (error == 0) - error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb)); - sbuf_delete(&sb); + error = 0; + fp = arg1; + buf = NULL; + + sb_check = sbuf_new(&sb, NULL, 1024, SBUF_AUTOEXTEND); + if (sb_check != &sb) + return (ENOMEM); + + sbuf_set_drain(&sb, (sbuf_drain_func *)fail_sysctl_drain_func, req); /* Setting */ - if (!error && req->newptr) { + /** + * Lock protects any new entries from being garbage collected before we + * can link them to the fail point. + */ + sx_xlock(&sx_fp_set); + if (req->newptr) { if (req->newlen > MAX_FAIL_POINT_BUF) { error = EINVAL; goto out; @@ -417,31 +884,95 @@ fail_point_sysctl(SYSCTL_HANDLER_ARGS) buf[req->newlen] = '\0'; error = fail_point_set(fp, buf); - } + } + + fail_point_garbage_collect(); + sx_xunlock(&sx_fp_set); + + /* Retrieving. */ + fail_point_get(fp, &sb, false); out: - fp_free(buf); + sbuf_finish(&sb); + sbuf_delete(&sb); + + if (buf) + fp_free(buf); + return (error); } +int +fail_point_sysctl_status(SYSCTL_HANDLER_ARGS) +{ + struct fail_point *fp; + struct sbuf sb, *sb_check; + + fp = arg1; + + sb_check = sbuf_new(&sb, NULL, 1024, SBUF_AUTOEXTEND); + if (sb_check != &sb) + return (ENOMEM); + + sbuf_set_drain(&sb, (sbuf_drain_func *)fail_sysctl_drain_func, req); + + /* Retrieving. */ + fail_point_get(fp, &sb, true); + + sbuf_finish(&sb); + sbuf_delete(&sb); + + /** + * Lock protects any new entries from being garbage collected before we + * can link them to the fail point. + */ + sx_xlock(&sx_fp_set); + fail_point_garbage_collect(); + sx_xunlock(&sx_fp_set); + + return (0); +} + +int +fail_sysctl_drain_func(void *sysctl_args, const char *buf, int len) +{ + struct sysctl_req *sa; + int error; + + sa = sysctl_args; + + error = SYSCTL_OUT(sa, buf, len); + + if (error == ENOMEM) + return (-1); + else + return (len); +} + + /** * Internal helper function to translate a human-readable failpoint string * into a internally-parsable fail_point structure. */ static char * -parse_fail_point(struct fail_point_entries *ents, char *p) +parse_fail_point(struct fail_point_setting *ents, char *p) { /* <fail_point> :: * <term> ( "->" <term> )* */ + uint8_t term_count; + + term_count = 1; + p = parse_term(ents, p); if (p == NULL) return (NULL); + while (*p != '\0') { - if (p[0] != '-' || p[1] != '>') - return (NULL); - p = parse_term(ents, p + 2); - if (p == NULL) + term_count++; + if (p[0] != '-' || p[1] != '>' || + (p = parse_term(ents, p+2)) == NULL || + term_count > FP_MAX_ENTRY_COUNT) return (NULL); } return (p); @@ -451,14 +982,11 @@ parse_fail_point(struct fail_point_entries *ents, char *p) * Internal helper function to parse an individual term from a failpoint. */ static char * -parse_term(struct fail_point_entries *ents, char *p) +parse_term(struct fail_point_setting *ents, char *p) { struct fail_point_entry *ent; - ent = fp_malloc(sizeof *ent, M_WAITOK | M_ZERO); - ent->fe_prob = PROB_MAX; - ent->fe_pid = NO_PID; - TAILQ_INSERT_TAIL(ents, ent, fe_entries); + ent = fail_point_entry_new(ents); /* * <term> :: @@ -483,7 +1011,7 @@ parse_term(struct fail_point_entries *ents, char *p) if (ent->fe_prob > PROB_MAX) ent->fe_prob = PROB_MAX; } else if (*p == '*') { - if (!units || decimal) + if (!units || units < 0 || decimal) return (NULL); ent->fe_count = units; } else @@ -500,7 +1028,7 @@ parse_term(struct fail_point_entries *ents, char *p) /* [ "(" <integer> ")" ] */ if (*p != '(') - return p; + return (p); p++; if (!isdigit(*p) && *p != '-') return (NULL); @@ -509,7 +1037,7 @@ parse_term(struct fail_point_entries *ents, char *p) return (NULL); /* [ "[pid " <integer> "]" ] */ -#define PID_STRING "[pid " +#define PID_STRING "[pid " if (strncmp(p, PID_STRING, sizeof(PID_STRING) - 1) != 0) return (p); p += sizeof(PID_STRING) - 1; @@ -530,7 +1058,7 @@ parse_number(int *out_units, int *out_decimal, char *p) { char *old_p; - /* + /** * <number> :: * <integer> [ "." <integer> ] | * "." <integer> @@ -584,29 +1112,17 @@ parse_type(struct fail_point_entry *ent, char *beg) return (NULL); } -/** - * Internal helper function to free an individual failpoint term. - */ -static void -free_entry(struct fail_point_entries *ents, struct fail_point_entry *ent) -{ - TAILQ_REMOVE(ents, ent, fe_entries); - fp_free(ent); -} +/* The fail point sysctl tree. */ +SYSCTL_NODE(_debug, OID_AUTO, fail_point, CTLFLAG_RW, 0, "fail points"); -/** - * Internal helper function to clear out all failpoint terms for a single - * failpoint. - */ -static void -clear_entries(struct fail_point_entries *ents) +/* Debugging/testing stuff for fail point */ +static int +sysctl_test_fail_point(SYSCTL_HANDLER_ARGS) { - struct fail_point_entry *ent, *ent_next; - TAILQ_FOREACH_SAFE(ent, ents, fe_entries, ent_next) - fp_free(ent); - TAILQ_INIT(ents); + KFAIL_POINT_RETURN(DEBUG_FP, test_fail_point); + return (0); } - -/* The fail point sysctl tree. */ -SYSCTL_NODE(_debug, OID_AUTO, fail_point, CTLFLAG_RW, 0, "fail points"); +SYSCTL_OID(_debug_fail_point, OID_AUTO, test_trigger_fail_point, + CTLTYPE_STRING | CTLFLAG_RD, NULL, 0, sysctl_test_fail_point, "A", + "Trigger test fail points"); diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c index 7454d79..e204291 100644 --- a/sys/kern/kern_linker.c +++ b/sys/kern/kern_linker.c @@ -1763,8 +1763,6 @@ linker_hints_lookup(const char *path, int pathlen, const char *modname, goto bad; } hints = malloc(vattr.va_size, M_TEMP, M_WAITOK); - if (hints == NULL) - goto bad; error = vn_rdwr(UIO_READ, nd.ni_vp, (caddr_t)hints, vattr.va_size, 0, UIO_SYSSPACE, IO_NODELOCKED, cred, NOCRED, &reclen, td); if (error) diff --git a/sys/kern/kern_mbuf.c b/sys/kern/kern_mbuf.c index 3074589..5e634d7 100644 --- a/sys/kern/kern_mbuf.c +++ b/sys/kern/kern_mbuf.c @@ -424,6 +424,7 @@ mb_ctor_mbuf(void *mem, int size, void *arg, int how) m = (struct mbuf *)mem; flags = args->flags; + MPASS((flags & M_NOFREE) == 0); error = m_init(m, how, type, flags); @@ -572,6 +573,7 @@ mb_ctor_pack(void *mem, int size, void *arg, int how) args = (struct mb_args *)arg; flags = args->flags; type = args->type; + MPASS((flags & M_NOFREE) == 0); #ifdef INVARIANTS trash_ctor(m->m_ext.ext_buf, MCLBYTES, arg, how); @@ -731,6 +733,7 @@ m_clget(struct mbuf *m, int how) zone_drain(zone_pack); uma_zalloc_arg(zone_clust, m, how); } + MBUF_PROBE2(m__clget, m, how); return (m->m_flags & M_EXT); } @@ -745,6 +748,7 @@ void * m_cljget(struct mbuf *m, int how, int size) { uma_zone_t zone; + void *retval; if (m != NULL) { KASSERT((m->m_flags & M_EXT) == 0, ("%s: mbuf %p has M_EXT", @@ -753,7 +757,11 @@ m_cljget(struct mbuf *m, int how, int size) } zone = m_getzone(size); - return (uma_zalloc_arg(zone, m, how)); + retval = uma_zalloc_arg(zone, m, how); + + MBUF_PROBE4(m__cljget, m, how, size, retval); + + return (retval); } /* @@ -934,6 +942,7 @@ void m_freem(struct mbuf *mb) { + MBUF_PROBE1(m__freem, mb); while (mb != NULL) mb = m_free(mb); } diff --git a/sys/kern/kern_osd.c b/sys/kern/kern_osd.c index cc9bed1..41d518f 100644 --- a/sys/kern/kern_osd.c +++ b/sys/kern/kern_osd.c @@ -44,6 +44,23 @@ __FBSDID("$FreeBSD$"); /* OSD (Object Specific Data) */ +/* + * Lock key: + * (m) osd_module_lock + * (o) osd_object_lock + * (l) osd_list_lock + */ +struct osd_master { + struct sx osd_module_lock; + struct rmlock osd_object_lock; + struct mtx osd_list_lock; + LIST_HEAD(, osd) osd_list; /* (l) */ + osd_destructor_t *osd_destructors; /* (o) */ + osd_method_t *osd_methods; /* (m) */ + u_int osd_ntslots; /* (m) */ + const u_int osd_nmethods; +}; + static MALLOC_DEFINE(M_OSD, "osd", "Object Specific Data"); static int osd_debug = 0; @@ -61,25 +78,12 @@ static void do_osd_del(u_int type, struct osd *osd, u_int slot, int list_locked); /* - * Lists of objects with OSD. - * - * Lock key: - * (m) osd_module_lock - * (o) osd_object_lock - * (l) osd_list_lock + * List of objects with OSD. */ -static LIST_HEAD(, osd) osd_list[OSD_LAST + 1]; /* (m) */ -static osd_method_t *osd_methods[OSD_LAST + 1]; /* (m) */ -static u_int osd_nslots[OSD_LAST + 1]; /* (m) */ -static osd_destructor_t *osd_destructors[OSD_LAST + 1]; /* (o) */ -static const u_int osd_nmethods[OSD_LAST + 1] = { - [OSD_JAIL] = PR_MAXMETHOD, +struct osd_master osdm[OSD_LAST + 1] = { + [OSD_JAIL] = { .osd_nmethods = PR_MAXMETHOD }, }; -static struct sx osd_module_lock[OSD_LAST + 1]; -static struct rmlock osd_object_lock[OSD_LAST + 1]; -static struct mtx osd_list_lock[OSD_LAST + 1]; - static void osd_default_destructor(void *value __unused) { @@ -101,12 +105,12 @@ osd_register(u_int type, osd_destructor_t destructor, osd_method_t *methods) if (destructor == NULL) destructor = osd_default_destructor; - sx_xlock(&osd_module_lock[type]); + sx_xlock(&osdm[type].osd_module_lock); /* * First, we try to find unused slot. */ - for (i = 0; i < osd_nslots[type]; i++) { - if (osd_destructors[type][i] == NULL) { + for (i = 0; i < osdm[type].osd_ntslots; i++) { + if (osdm[type].osd_destructors[i] == NULL) { OSD_DEBUG("Unused slot found (type=%u, slot=%u).", type, i); break; @@ -115,31 +119,31 @@ osd_register(u_int type, osd_destructor_t destructor, osd_method_t *methods) /* * If no unused slot was found, allocate one. */ - if (i == osd_nslots[type]) { - osd_nslots[type]++; - if (osd_nmethods[type] != 0) - osd_methods[type] = realloc(osd_methods[type], - sizeof(osd_method_t) * osd_nslots[type] * - osd_nmethods[type], M_OSD, M_WAITOK); - newptr = malloc(sizeof(osd_destructor_t) * osd_nslots[type], - M_OSD, M_WAITOK); - rm_wlock(&osd_object_lock[type]); - bcopy(osd_destructors[type], newptr, + if (i == osdm[type].osd_ntslots) { + osdm[type].osd_ntslots++; + if (osdm[type].osd_nmethods != 0) + osdm[type].osd_methods = realloc(osdm[type].osd_methods, + sizeof(osd_method_t) * osdm[type].osd_ntslots * + osdm[type].osd_nmethods, M_OSD, M_WAITOK); + newptr = malloc(sizeof(osd_destructor_t) * + osdm[type].osd_ntslots, M_OSD, M_WAITOK); + rm_wlock(&osdm[type].osd_object_lock); + bcopy(osdm[type].osd_destructors, newptr, sizeof(osd_destructor_t) * i); - free(osd_destructors[type], M_OSD); - osd_destructors[type] = newptr; - rm_wunlock(&osd_object_lock[type]); + free(osdm[type].osd_destructors, M_OSD); + osdm[type].osd_destructors = newptr; + rm_wunlock(&osdm[type].osd_object_lock); OSD_DEBUG("New slot allocated (type=%u, slot=%u).", type, i + 1); } - osd_destructors[type][i] = destructor; - if (osd_nmethods[type] != 0) { - for (m = 0; m < osd_nmethods[type]; m++) - osd_methods[type][i * osd_nmethods[type] + m] = - methods != NULL ? methods[m] : NULL; + osdm[type].osd_destructors[i] = destructor; + if (osdm[type].osd_nmethods != 0) { + for (m = 0; m < osdm[type].osd_nmethods; m++) + osdm[type].osd_methods[i * osdm[type].osd_nmethods + m] + = methods != NULL ? methods[m] : NULL; } - sx_xunlock(&osd_module_lock[type]); + sx_xunlock(&osdm[type].osd_module_lock); return (i + 1); } @@ -150,105 +154,142 @@ osd_deregister(u_int type, u_int slot) KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type.")); KASSERT(slot > 0, ("Invalid slot.")); - KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot.")); + KASSERT(osdm[type].osd_destructors[slot - 1] != NULL, ("Unused slot.")); - sx_xlock(&osd_module_lock[type]); - rm_wlock(&osd_object_lock[type]); + sx_xlock(&osdm[type].osd_module_lock); + rm_wlock(&osdm[type].osd_object_lock); /* * Free all OSD for the given slot. */ - mtx_lock(&osd_list_lock[type]); - LIST_FOREACH_SAFE(osd, &osd_list[type], osd_next, tosd) + mtx_lock(&osdm[type].osd_list_lock); + LIST_FOREACH_SAFE(osd, &osdm[type].osd_list, osd_next, tosd) do_osd_del(type, osd, slot, 1); - mtx_unlock(&osd_list_lock[type]); + mtx_unlock(&osdm[type].osd_list_lock); /* * Set destructor to NULL to free the slot. */ - osd_destructors[type][slot - 1] = NULL; - if (slot == osd_nslots[type]) { - osd_nslots[type]--; - osd_destructors[type] = realloc(osd_destructors[type], - sizeof(osd_destructor_t) * osd_nslots[type], M_OSD, + osdm[type].osd_destructors[slot - 1] = NULL; + if (slot == osdm[type].osd_ntslots) { + osdm[type].osd_ntslots--; + osdm[type].osd_destructors = realloc(osdm[type].osd_destructors, + sizeof(osd_destructor_t) * osdm[type].osd_ntslots, M_OSD, M_NOWAIT | M_ZERO); - if (osd_nmethods[type] != 0) - osd_methods[type] = realloc(osd_methods[type], - sizeof(osd_method_t) * osd_nslots[type] * - osd_nmethods[type], M_OSD, M_NOWAIT | M_ZERO); + if (osdm[type].osd_nmethods != 0) + osdm[type].osd_methods = realloc(osdm[type].osd_methods, + sizeof(osd_method_t) * osdm[type].osd_ntslots * + osdm[type].osd_nmethods, M_OSD, M_NOWAIT | M_ZERO); /* * We always reallocate to smaller size, so we assume it will * always succeed. */ - KASSERT(osd_destructors[type] != NULL && - (osd_nmethods[type] == 0 || osd_methods[type] != NULL), - ("realloc() failed")); + KASSERT(osdm[type].osd_destructors != NULL && + (osdm[type].osd_nmethods == 0 || + osdm[type].osd_methods != NULL), ("realloc() failed")); OSD_DEBUG("Deregistration of the last slot (type=%u, slot=%u).", type, slot); } else { OSD_DEBUG("Slot deregistration (type=%u, slot=%u).", type, slot); } - rm_wunlock(&osd_object_lock[type]); - sx_xunlock(&osd_module_lock[type]); + rm_wunlock(&osdm[type].osd_object_lock); + sx_xunlock(&osdm[type].osd_module_lock); } int osd_set(u_int type, struct osd *osd, u_int slot, void *value) { + + return (osd_set_reserved(type, osd, slot, NULL, value)); +} + +void * +osd_reserve(u_int slot) +{ + + KASSERT(slot > 0, ("Invalid slot.")); + + OSD_DEBUG("Reserving slot array (slot=%u).", slot); + return (malloc(sizeof(void *) * slot, M_OSD, M_WAITOK | M_ZERO)); +} + +int +osd_set_reserved(u_int type, struct osd *osd, u_int slot, void *rsv, + void *value) +{ struct rm_priotracker tracker; KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type.")); KASSERT(slot > 0, ("Invalid slot.")); - KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot.")); + KASSERT(osdm[type].osd_destructors[slot - 1] != NULL, ("Unused slot.")); - rm_rlock(&osd_object_lock[type], &tracker); + rm_rlock(&osdm[type].osd_object_lock, &tracker); if (slot > osd->osd_nslots) { + void *newptr; + if (value == NULL) { OSD_DEBUG( "Not allocating null slot (type=%u, slot=%u).", type, slot); - rm_runlock(&osd_object_lock[type], &tracker); + rm_runlock(&osdm[type].osd_object_lock, &tracker); + if (rsv) + osd_free_reserved(rsv); return (0); - } else if (osd->osd_nslots == 0) { + } + + /* + * Too few slots allocated here, so we need to extend or create + * the array. + */ + if (rsv) { /* - * First OSD for this object, so we need to allocate - * space and put it onto the list. + * Use the reserve passed in (assumed to be + * the right size). */ - osd->osd_slots = malloc(sizeof(void *) * slot, M_OSD, - M_NOWAIT | M_ZERO); - if (osd->osd_slots == NULL) { - rm_runlock(&osd_object_lock[type], &tracker); - return (ENOMEM); + newptr = rsv; + if (osd->osd_nslots != 0) { + memcpy(newptr, osd->osd_slots, + sizeof(void *) * osd->osd_nslots); + free(osd->osd_slots, M_OSD); } - osd->osd_nslots = slot; - mtx_lock(&osd_list_lock[type]); - LIST_INSERT_HEAD(&osd_list[type], osd, osd_next); - mtx_unlock(&osd_list_lock[type]); - OSD_DEBUG("Setting first slot (type=%u).", type); } else { - void *newptr; - - /* - * Too few slots allocated here, needs to extend - * the array. - */ newptr = realloc(osd->osd_slots, sizeof(void *) * slot, M_OSD, M_NOWAIT | M_ZERO); if (newptr == NULL) { - rm_runlock(&osd_object_lock[type], &tracker); + rm_runlock(&osdm[type].osd_object_lock, + &tracker); return (ENOMEM); } - osd->osd_slots = newptr; - osd->osd_nslots = slot; - OSD_DEBUG("Growing slots array (type=%u).", type); } - } + if (osd->osd_nslots == 0) { + /* + * First OSD for this object, so we need to put it + * onto the list. + */ + mtx_lock(&osdm[type].osd_list_lock); + LIST_INSERT_HEAD(&osdm[type].osd_list, osd, osd_next); + mtx_unlock(&osdm[type].osd_list_lock); + OSD_DEBUG("Setting first slot (type=%u).", type); + } else + OSD_DEBUG("Growing slots array (type=%u).", type); + osd->osd_slots = newptr; + osd->osd_nslots = slot; + } else if (rsv) + osd_free_reserved(rsv); OSD_DEBUG("Setting slot value (type=%u, slot=%u, value=%p).", type, slot, value); osd->osd_slots[slot - 1] = value; - rm_runlock(&osd_object_lock[type], &tracker); + rm_runlock(&osdm[type].osd_object_lock, &tracker); return (0); } +void +osd_free_reserved(void *rsv) +{ + + OSD_DEBUG("Discarding reserved slot array."); + free(rsv, M_OSD); +} + void * osd_get(u_int type, struct osd *osd, u_int slot) { @@ -257,9 +298,9 @@ osd_get(u_int type, struct osd *osd, u_int slot) KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type.")); KASSERT(slot > 0, ("Invalid slot.")); - KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot.")); + KASSERT(osdm[type].osd_destructors[slot - 1] != NULL, ("Unused slot.")); - rm_rlock(&osd_object_lock[type], &tracker); + rm_rlock(&osdm[type].osd_object_lock, &tracker); if (slot > osd->osd_nslots) { value = NULL; OSD_DEBUG("Slot doesn't exist (type=%u, slot=%u).", type, slot); @@ -268,7 +309,7 @@ osd_get(u_int type, struct osd *osd, u_int slot) OSD_DEBUG("Returning slot value (type=%u, slot=%u, value=%p).", type, slot, value); } - rm_runlock(&osd_object_lock[type], &tracker); + rm_runlock(&osdm[type].osd_object_lock, &tracker); return (value); } @@ -277,9 +318,9 @@ osd_del(u_int type, struct osd *osd, u_int slot) { struct rm_priotracker tracker; - rm_rlock(&osd_object_lock[type], &tracker); + rm_rlock(&osdm[type].osd_object_lock, &tracker); do_osd_del(type, osd, slot, 0); - rm_runlock(&osd_object_lock[type], &tracker); + rm_runlock(&osdm[type].osd_object_lock, &tracker); } static void @@ -289,7 +330,7 @@ do_osd_del(u_int type, struct osd *osd, u_int slot, int list_locked) KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type.")); KASSERT(slot > 0, ("Invalid slot.")); - KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot.")); + KASSERT(osdm[type].osd_destructors[slot - 1] != NULL, ("Unused slot.")); OSD_DEBUG("Deleting slot (type=%u, slot=%u).", type, slot); @@ -298,7 +339,7 @@ do_osd_del(u_int type, struct osd *osd, u_int slot, int list_locked) return; } if (osd->osd_slots[slot - 1] != NULL) { - osd_destructors[type][slot - 1](osd->osd_slots[slot - 1]); + osdm[type].osd_destructors[slot - 1](osd->osd_slots[slot - 1]); osd->osd_slots[slot - 1] = NULL; } for (i = osd->osd_nslots - 1; i >= 0; i--) { @@ -312,10 +353,10 @@ do_osd_del(u_int type, struct osd *osd, u_int slot, int list_locked) /* No values left for this object. */ OSD_DEBUG("No more slots left (type=%u).", type); if (!list_locked) - mtx_lock(&osd_list_lock[type]); + mtx_lock(&osdm[type].osd_list_lock); LIST_REMOVE(osd, osd_next); if (!list_locked) - mtx_unlock(&osd_list_lock[type]); + mtx_unlock(&osdm[type].osd_list_lock); free(osd->osd_slots, M_OSD); osd->osd_slots = NULL; osd->osd_nslots = 0; @@ -341,21 +382,21 @@ osd_call(u_int type, u_int method, void *obj, void *data) int error, i; KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type.")); - KASSERT(method < osd_nmethods[type], ("Invalid method.")); + KASSERT(method < osdm[type].osd_nmethods, ("Invalid method.")); /* * Call this method for every slot that defines it, stopping if an * error is encountered. */ error = 0; - sx_slock(&osd_module_lock[type]); - for (i = 0; i < osd_nslots[type]; i++) { - methodfun = - osd_methods[type][i * osd_nmethods[type] + method]; + sx_slock(&osdm[type].osd_module_lock); + for (i = 0; i < osdm[type].osd_ntslots; i++) { + methodfun = osdm[type].osd_methods[i * osdm[type].osd_nmethods + + method]; if (methodfun != NULL && (error = methodfun(obj, data)) != 0) break; } - sx_sunlock(&osd_module_lock[type]); + sx_sunlock(&osdm[type].osd_module_lock); return (error); } @@ -373,14 +414,14 @@ osd_exit(u_int type, struct osd *osd) return; } - rm_rlock(&osd_object_lock[type], &tracker); + rm_rlock(&osdm[type].osd_object_lock, &tracker); for (i = 1; i <= osd->osd_nslots; i++) { - if (osd_destructors[type][i - 1] != NULL) + if (osdm[type].osd_destructors[i - 1] != NULL) do_osd_del(type, osd, i, 0); else OSD_DEBUG("Unused slot (type=%u, slot=%u).", type, i); } - rm_runlock(&osd_object_lock[type], &tracker); + rm_runlock(&osdm[type].osd_object_lock, &tracker); OSD_DEBUG("Object exit (type=%u).", type); } @@ -390,13 +431,13 @@ osd_init(void *arg __unused) u_int i; for (i = OSD_FIRST; i <= OSD_LAST; i++) { - osd_nslots[i] = 0; - LIST_INIT(&osd_list[i]); - sx_init(&osd_module_lock[i], "osd_module"); - rm_init(&osd_object_lock[i], "osd_object"); - mtx_init(&osd_list_lock[i], "osd_list", NULL, MTX_DEF); - osd_destructors[i] = NULL; - osd_methods[i] = NULL; + sx_init(&osdm[i].osd_module_lock, "osd_module"); + rm_init(&osdm[i].osd_object_lock, "osd_object"); + mtx_init(&osdm[i].osd_list_lock, "osd_list", NULL, MTX_DEF); + LIST_INIT(&osdm[i].osd_list); + osdm[i].osd_destructors = NULL; + osdm[i].osd_ntslots = 0; + osdm[i].osd_methods = NULL; } } SYSINIT(osd, SI_SUB_LOCK, SI_ORDER_ANY, osd_init, NULL); diff --git a/sys/kern/kern_racct.c b/sys/kern/kern_racct.c index 1c3c9d7..419c69e 100644 --- a/sys/kern/kern_racct.c +++ b/sys/kern/kern_racct.c @@ -114,6 +114,8 @@ SDT_PROBE_DEFINE3(racct, , rusage, set, "struct proc *", "int", "uint64_t"); SDT_PROBE_DEFINE3(racct, , rusage, set__failure, "struct proc *", "int", "uint64_t"); +SDT_PROBE_DEFINE3(racct, , rusage, set__force, + "struct proc *", "int", "uint64_t"); SDT_PROBE_DEFINE3(racct, , rusage, sub, "struct proc *", "int", "uint64_t"); SDT_PROBE_DEFINE3(racct, , rusage, sub__cred, @@ -532,7 +534,7 @@ racct_adjust_resource(struct racct *racct, int resource, } static int -racct_add_locked(struct proc *p, int resource, uint64_t amount) +racct_add_locked(struct proc *p, int resource, uint64_t amount, int force) { #ifdef RCTL int error; @@ -540,8 +542,6 @@ racct_add_locked(struct proc *p, int resource, uint64_t amount) ASSERT_RACCT_ENABLED(); - SDT_PROBE3(racct, , rusage, add, p, resource, amount); - /* * We need proc lock to dereference p->p_ucred. */ @@ -549,7 +549,7 @@ racct_add_locked(struct proc *p, int resource, uint64_t amount) #ifdef RCTL error = rctl_enforce(p, resource, amount); - if (error && RACCT_IS_DENIABLE(resource)) { + if (error && !force && RACCT_IS_DENIABLE(resource)) { SDT_PROBE3(racct, , rusage, add__failure, p, resource, amount); return (error); } @@ -572,12 +572,32 @@ racct_add(struct proc *p, int resource, uint64_t amount) if (!racct_enable) return (0); + SDT_PROBE3(racct, , rusage, add, p, resource, amount); + mtx_lock(&racct_lock); - error = racct_add_locked(p, resource, amount); + error = racct_add_locked(p, resource, amount, 0); mtx_unlock(&racct_lock); return (error); } +/* + * Increase allocation of 'resource' by 'amount' for process 'p'. + * Doesn't check for limits and never fails. + */ +void +racct_add_force(struct proc *p, int resource, uint64_t amount) +{ + + if (!racct_enable) + return; + + SDT_PROBE3(racct, , rusage, add__force, p, resource, amount); + + mtx_lock(&racct_lock); + racct_add_locked(p, resource, amount, 1); + mtx_unlock(&racct_lock); +} + static void racct_add_cred_locked(struct ucred *cred, int resource, uint64_t amount) { @@ -597,8 +617,6 @@ racct_add_cred_locked(struct ucred *cred, int resource, uint64_t amount) /* * Increase allocation of 'resource' by 'amount' for credential 'cred'. * Doesn't check for limits and never fails. - * - * XXX: Shouldn't this ever return an error? */ void racct_add_cred(struct ucred *cred, int resource, uint64_t amount) @@ -612,32 +630,8 @@ racct_add_cred(struct ucred *cred, int resource, uint64_t amount) mtx_unlock(&racct_lock); } -/* - * Increase allocation of 'resource' by 'amount' for process 'p'. - * Doesn't check for limits and never fails. - */ -void -racct_add_force(struct proc *p, int resource, uint64_t amount) -{ - - if (!racct_enable) - return; - - SDT_PROBE3(racct, , rusage, add__force, p, resource, amount); - - /* - * We need proc lock to dereference p->p_ucred. - */ - PROC_LOCK_ASSERT(p, MA_OWNED); - - mtx_lock(&racct_lock); - racct_adjust_resource(p->p_racct, resource, amount); - racct_add_cred_locked(p->p_ucred, resource, amount); - mtx_unlock(&racct_lock); -} - static int -racct_set_locked(struct proc *p, int resource, uint64_t amount) +racct_set_locked(struct proc *p, int resource, uint64_t amount, int force) { int64_t old_amount, decayed_amount; int64_t diff_proc, diff_cred; @@ -647,8 +641,6 @@ racct_set_locked(struct proc *p, int resource, uint64_t amount) ASSERT_RACCT_ENABLED(); - SDT_PROBE3(racct, , rusage, set, p, resource, amount); - /* * We need proc lock to dereference p->p_ucred. */ @@ -678,7 +670,7 @@ racct_set_locked(struct proc *p, int resource, uint64_t amount) #ifdef RCTL if (diff_proc > 0) { error = rctl_enforce(p, resource, diff_proc); - if (error && RACCT_IS_DENIABLE(resource)) { + if (error && !force && RACCT_IS_DENIABLE(resource)) { SDT_PROBE3(racct, , rusage, set__failure, p, resource, amount); return (error); @@ -709,51 +701,14 @@ racct_set(struct proc *p, int resource, uint64_t amount) if (!racct_enable) return (0); + SDT_PROBE3(racct, , rusage, set__force, p, resource, amount); + mtx_lock(&racct_lock); - error = racct_set_locked(p, resource, amount); + error = racct_set_locked(p, resource, amount, 0); mtx_unlock(&racct_lock); return (error); } -static void -racct_set_force_locked(struct proc *p, int resource, uint64_t amount) -{ - int64_t old_amount, decayed_amount; - int64_t diff_proc, diff_cred; - - ASSERT_RACCT_ENABLED(); - - SDT_PROBE3(racct, , rusage, set, p, resource, amount); - - /* - * We need proc lock to dereference p->p_ucred. - */ - PROC_LOCK_ASSERT(p, MA_OWNED); - - old_amount = p->p_racct->r_resources[resource]; - /* - * The diffs may be negative. - */ - diff_proc = amount - old_amount; - if (RACCT_IS_DECAYING(resource)) { - /* - * Resources in per-credential racct containers may decay. - * If this is the case, we need to calculate the difference - * between the new amount and the proportional value of the - * old amount that has decayed in the ucred racct containers. - */ - decayed_amount = old_amount * RACCT_DECAY_FACTOR / FSCALE; - diff_cred = amount - decayed_amount; - } else - diff_cred = diff_proc; - - racct_adjust_resource(p->p_racct, resource, diff_proc); - if (diff_cred > 0) - racct_add_cred_locked(p->p_ucred, resource, diff_cred); - else if (diff_cred < 0) - racct_sub_cred_locked(p->p_ucred, resource, -diff_cred); -} - void racct_set_force(struct proc *p, int resource, uint64_t amount) { @@ -761,8 +716,10 @@ racct_set_force(struct proc *p, int resource, uint64_t amount) if (!racct_enable) return; + SDT_PROBE3(racct, , rusage, set, p, resource, amount); + mtx_lock(&racct_lock); - racct_set_force_locked(p, resource, amount); + racct_set_locked(p, resource, amount, 1); mtx_unlock(&racct_lock); } @@ -930,13 +887,13 @@ racct_proc_fork(struct proc *parent, struct proc *child) continue; error = racct_set_locked(child, i, - parent->p_racct->r_resources[i]); + parent->p_racct->r_resources[i], 0); if (error != 0) goto out; } - error = racct_add_locked(child, RACCT_NPROC, 1); - error += racct_add_locked(child, RACCT_NTHR, 1); + error = racct_add_locked(child, RACCT_NPROC, 1, 0); + error += racct_add_locked(child, RACCT_NTHR, 1, 0); out: mtx_unlock(&racct_lock); @@ -1002,7 +959,7 @@ racct_proc_exit(struct proc *p) pct = racct_getpcpu(p, pct_estimate); mtx_lock(&racct_lock); - racct_set_locked(p, RACCT_CPU, runtime); + racct_set_locked(p, RACCT_CPU, runtime, 0); racct_add_cred_locked(p->p_ucred, RACCT_PCTCPU, pct); for (i = 0; i <= RACCT_MAX; i++) { @@ -1010,7 +967,7 @@ racct_proc_exit(struct proc *p) continue; if (!RACCT_IS_RECLAIMABLE(i)) continue; - racct_set_locked(p, i, 0); + racct_set_locked(p, i, 0, 0); } mtx_unlock(&racct_lock); @@ -1150,23 +1107,21 @@ racct_proc_wakeup(struct proc *p) } static void -racct_decay_resource(struct racct *racct, void * res, void* dummy) +racct_decay_callback(struct racct *racct, void *dummy1, void *dummy2) { - int resource; int64_t r_old, r_new; ASSERT_RACCT_ENABLED(); mtx_assert(&racct_lock, MA_OWNED); - resource = *(int *)res; - r_old = racct->r_resources[resource]; + r_old = racct->r_resources[RACCT_PCTCPU]; /* If there is nothing to decay, just exit. */ if (r_old <= 0) return; r_new = r_old * RACCT_DECAY_FACTOR / FSCALE; - racct->r_resources[resource] = r_new; + racct->r_resources[RACCT_PCTCPU] = r_new; } static void @@ -1184,17 +1139,17 @@ racct_decay_post(void) } static void -racct_decay(int resource) +racct_decay(void) { ASSERT_RACCT_ENABLED(); - ui_racct_foreach(racct_decay_resource, racct_decay_pre, - racct_decay_post, &resource, NULL); - loginclass_racct_foreach(racct_decay_resource, racct_decay_pre, - racct_decay_post, &resource, NULL); - prison_racct_foreach(racct_decay_resource, racct_decay_pre, - racct_decay_post, &resource, NULL); + ui_racct_foreach(racct_decay_callback, racct_decay_pre, + racct_decay_post, NULL, NULL); + loginclass_racct_foreach(racct_decay_callback, racct_decay_pre, + racct_decay_post, NULL, NULL); + prison_racct_foreach(racct_decay_callback, racct_decay_pre, + racct_decay_post, NULL, NULL); } static void @@ -1209,7 +1164,7 @@ racctd(void) ASSERT_RACCT_ENABLED(); for (;;) { - racct_decay(RACCT_PCTCPU); + racct_decay(); sx_slock(&allproc_lock); @@ -1249,11 +1204,11 @@ racctd(void) pct_estimate = 0; pct = racct_getpcpu(p, pct_estimate); mtx_lock(&racct_lock); - racct_set_force_locked(p, RACCT_PCTCPU, pct); - racct_set_locked(p, RACCT_CPU, runtime); + racct_set_locked(p, RACCT_PCTCPU, pct, 1); + racct_set_locked(p, RACCT_CPU, runtime, 0); racct_set_locked(p, RACCT_WALLCLOCK, (uint64_t)wallclock.tv_sec * 1000000 + - wallclock.tv_usec); + wallclock.tv_usec, 0); mtx_unlock(&racct_lock); PROC_UNLOCK(p); } diff --git a/sys/kern/kern_rctl.c b/sys/kern/kern_rctl.c index b0d3fd6..1970ed8 100644 --- a/sys/kern/kern_rctl.c +++ b/sys/kern/kern_rctl.c @@ -78,10 +78,16 @@ FEATURE(rctl, "Resource Limits"); #define RCTL_PCPU_SHIFT (10 * 1000000) unsigned int rctl_maxbufsize = RCTL_MAX_OUTBUFSIZE; +static int rctl_log_rate_limit = 10; +static int rctl_devctl_rate_limit = 10; SYSCTL_NODE(_kern_racct, OID_AUTO, rctl, CTLFLAG_RW, 0, "Resource Limits"); SYSCTL_UINT(_kern_racct_rctl, OID_AUTO, maxbufsize, CTLFLAG_RWTUN, &rctl_maxbufsize, 0, "Maximum output buffer size"); +SYSCTL_UINT(_kern_racct_rctl, OID_AUTO, log_rate_limit, CTLFLAG_RW, + &rctl_log_rate_limit, 0, "Maximum number of log messages per second"); +SYSCTL_UINT(_kern_racct_rctl, OID_AUTO, devctl_rate_limit, CTLFLAG_RW, + &rctl_devctl_rate_limit, 0, "Maximum number of devctl messages per second"); /* * 'rctl_rule_link' connects a rule with every racct it's related to. @@ -219,43 +225,43 @@ rctl_resource_name(int resource) panic("rctl_resource_name: unknown resource %d", resource); } -/* - * Return the amount of resource that can be allocated by 'p' before - * hitting 'rule'. - */ -static int64_t -rctl_available_resource(const struct proc *p, const struct rctl_rule *rule) +static struct racct * +rctl_proc_rule_to_racct(const struct proc *p, const struct rctl_rule *rule) { - int resource; - int64_t available = INT64_MAX; struct ucred *cred = p->p_ucred; ASSERT_RACCT_ENABLED(); rw_assert(&rctl_lock, RA_LOCKED); - resource = rule->rr_resource; switch (rule->rr_per) { case RCTL_SUBJECT_TYPE_PROCESS: - available = rule->rr_amount - - p->p_racct->r_resources[resource]; - break; + return (p->p_racct); case RCTL_SUBJECT_TYPE_USER: - available = rule->rr_amount - - cred->cr_ruidinfo->ui_racct->r_resources[resource]; - break; + return (cred->cr_ruidinfo->ui_racct); case RCTL_SUBJECT_TYPE_LOGINCLASS: - available = rule->rr_amount - - cred->cr_loginclass->lc_racct->r_resources[resource]; - break; + return (cred->cr_loginclass->lc_racct); case RCTL_SUBJECT_TYPE_JAIL: - available = rule->rr_amount - - cred->cr_prison->pr_prison_racct->prr_racct-> - r_resources[resource]; - break; + return (cred->cr_prison->pr_prison_racct->prr_racct); default: - panic("rctl_compute_available: unknown per %d", - rule->rr_per); + panic("%s: unknown per %d", __func__, rule->rr_per); } +} + +/* + * Return the amount of resource that can be allocated by 'p' before + * hitting 'rule'. + */ +static int64_t +rctl_available_resource(const struct proc *p, const struct rctl_rule *rule) +{ + int64_t available; + const struct racct *racct; + + ASSERT_RACCT_ENABLED(); + rw_assert(&rctl_lock, RA_LOCKED); + + racct = rctl_proc_rule_to_racct(p, rule); + available = rule->rr_amount - racct->r_resources[rule->rr_resource]; return (available); } @@ -336,13 +342,13 @@ rctl_pcpu_available(const struct proc *p) { int rctl_enforce(struct proc *p, int resource, uint64_t amount) { + static struct timeval log_lasttime, devctl_lasttime; + static int log_curtime = 0, devctl_curtime = 0; struct rctl_rule *rule; struct rctl_rule_link *link; struct sbuf sb; int should_deny = 0; char *buf; - static int curtime = 0; - static struct timeval lasttime; ASSERT_RACCT_ENABLED(); @@ -383,7 +389,8 @@ rctl_enforce(struct proc *p, int resource, uint64_t amount) if (p->p_state != PRS_NORMAL) continue; - if (!ppsratecheck(&lasttime, &curtime, 10)) + if (!ppsratecheck(&log_lasttime, &log_curtime, + rctl_log_rate_limit)) continue; buf = malloc(RCTL_LOG_BUFSIZE, M_RCTL, M_NOWAIT); @@ -409,6 +416,10 @@ rctl_enforce(struct proc *p, int resource, uint64_t amount) if (p->p_state != PRS_NORMAL) continue; + if (!ppsratecheck(&devctl_lasttime, &devctl_curtime, + rctl_devctl_rate_limit)) + continue; + buf = malloc(RCTL_LOG_BUFSIZE, M_RCTL, M_NOWAIT); if (buf == NULL) { printf("rctl_enforce: out of memory\n"); @@ -642,6 +653,9 @@ str2int64(const char *str, int64_t *value) if ((size_t)(end - str) != strlen(str)) return (EINVAL); + if (*value < 0) + return (ERANGE); + return (0); } @@ -1008,8 +1022,13 @@ rctl_string_to_rule(char *rulestr, struct rctl_rule **rulep) error = str2int64(amountstr, &rule->rr_amount); if (error != 0) goto out; - if (RACCT_IS_IN_MILLIONS(rule->rr_resource)) + if (RACCT_IS_IN_MILLIONS(rule->rr_resource)) { + if (rule->rr_amount > INT64_MAX / 1000000) { + error = ERANGE; + goto out; + } rule->rr_amount *= 1000000; + } } if (perstr == NULL || perstr[0] == '\0') diff --git a/sys/kern/kern_sendfile.c b/sys/kern/kern_sendfile.c index a2e1311..7ee7780 100644 --- a/sys/kern/kern_sendfile.c +++ b/sys/kern/kern_sendfile.c @@ -516,7 +516,7 @@ sendfile_getsock(struct thread *td, int s, struct file **sock_fp, int vn_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio, struct uio *trl_uio, off_t offset, size_t nbytes, off_t *sent, int flags, - int kflags, struct thread *td) + struct thread *td) { struct file *sock_fp; struct vnode *vp; @@ -534,7 +534,7 @@ vn_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio, so = NULL; m = mh = NULL; sfs = NULL; - sbytes = 0; + hdrlen = sbytes = 0; softerr = 0; error = sendfile_getobj(td, fp, &obj, &vp, &shmfd, &obj_size, &bsize); @@ -560,26 +560,6 @@ vn_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio, cv_init(&sfs->cv, "sendfile"); } - /* If headers are specified copy them into mbufs. */ - if (hdr_uio != NULL && hdr_uio->uio_resid > 0) { - hdr_uio->uio_td = td; - hdr_uio->uio_rw = UIO_WRITE; - /* - * In FBSD < 5.0 the nbytes to send also included - * the header. If compat is specified subtract the - * header size from nbytes. - */ - if (kflags & SFK_COMPAT) { - if (nbytes > hdr_uio->uio_resid) - nbytes -= hdr_uio->uio_resid; - else - nbytes = 0; - } - mh = m_uiotombuf(hdr_uio, M_WAITOK, 0, 0, 0); - hdrlen = m_length(mh, &mhtail); - } else - hdrlen = 0; - rem = nbytes ? omin(nbytes, obj_size - offset) : obj_size - offset; /* @@ -668,11 +648,20 @@ retry_space: SOCKBUF_UNLOCK(&so->so_snd); /* - * Reduce space in the socket buffer by the size of - * the header mbuf chain. - * hdrlen is set to 0 after the first loop. + * At the beginning of the first loop check if any headers + * are specified and copy them into mbufs. Reduce space in + * the socket buffer by the size of the header mbuf chain. + * Clear hdr_uio here and hdrlen at the end of the first loop. */ - space -= hdrlen; + if (hdr_uio != NULL && hdr_uio->uio_resid > 0) { + hdr_uio->uio_td = td; + hdr_uio->uio_rw = UIO_WRITE; + hdr_uio->uio_resid = min(hdr_uio->uio_resid, space); + mh = m_uiotombuf(hdr_uio, M_WAITOK, 0, 0, 0); + hdrlen = m_length(mh, &mhtail); + space -= hdrlen; + hdr_uio = NULL; + } if (vp != NULL) { error = vn_lock(vp, LK_SHARED); @@ -944,6 +933,19 @@ sendfile(struct thread *td, struct sendfile_args *uap, int compat) &hdr_uio); if (error != 0) goto out; +#ifdef COMPAT_FREEBSD4 + /* + * In FreeBSD < 5.0 the nbytes to send also included + * the header. If compat is specified subtract the + * header size from nbytes. + */ + if (compat) { + if (uap->nbytes > hdr_uio->uio_resid) + uap->nbytes -= hdr_uio->uio_resid; + else + uap->nbytes = 0; + } +#endif } if (hdtr.trailers != NULL) { error = copyinuio(hdtr.trailers, hdtr.trl_cnt, @@ -965,7 +967,7 @@ sendfile(struct thread *td, struct sendfile_args *uap, int compat) } error = fo_sendfile(fp, uap->s, hdr_uio, trl_uio, uap->offset, - uap->nbytes, &sbytes, uap->flags, compat ? SFK_COMPAT : 0, td); + uap->nbytes, &sbytes, uap->flags, td); fdrop(fp, td); if (uap->sbytes != NULL) diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c index 8c84773..c9932f56 100644 --- a/sys/kern/kern_synch.c +++ b/sys/kern/kern_synch.c @@ -162,15 +162,7 @@ _sleep(void *ident, struct lock_object *lock, int priority, else class = NULL; - if (cold || SCHEDULER_STOPPED()) { - /* - * During autoconfiguration, just return; - * don't run any other threads or panic below, - * in case this is the idle thread and already asleep. - * XXX: this used to do "s = splhigh(); splx(safepri); - * splx(s);" to give interrupts a chance, but there is - * no way to give interrupts a chance now. - */ + if (SCHEDULER_STOPPED()) { if (lock != NULL && priority & PDROP) class->lc_unlock(lock); return (0); @@ -264,17 +256,8 @@ msleep_spin_sbt(void *ident, struct mtx *mtx, const char *wmesg, KASSERT(p != NULL, ("msleep1")); KASSERT(ident != NULL && TD_IS_RUNNING(td), ("msleep")); - if (cold || SCHEDULER_STOPPED()) { - /* - * During autoconfiguration, just return; - * don't run any other threads or panic below, - * in case this is the idle thread and already asleep. - * XXX: this used to do "s = splhigh(); splx(safepri); - * splx(s);" to give interrupts a chance, but there is - * no way to give interrupts a chance now. - */ + if (SCHEDULER_STOPPED()) return (0); - } sleepq_lock(ident); CTR5(KTR_PROC, "msleep_spin: thread %ld (pid %ld, %s) on %s (%p)", @@ -438,8 +421,10 @@ mi_switch(int flags, struct thread *newtd) if (flags & SW_VOL) { td->td_ru.ru_nvcsw++; td->td_swvoltick = ticks; - } else + } else { td->td_ru.ru_nivcsw++; + td->td_swinvoltick = ticks; + } #ifdef SCHED_STATS SCHED_STAT_INC(sched_switch_stats[flags & SW_TYPE_MASK]); #endif diff --git a/sys/kern/pic_if.m b/sys/kern/pic_if.m index 2309f0c..3003c35 100644 --- a/sys/kern/pic_if.m +++ b/sys/kern/pic_if.m @@ -1,7 +1,6 @@ #- -# Copyright (c) 2012 Jakub Wojciech Klama <jceel@FreeBSD.org> -# Copyright (c) 2015 Svatopluk Kraus -# Copyright (c) 2015 Michal Meloun +# Copyright (c) 2015-2016 Svatopluk Kraus +# Copyright (c) 2015-2016 Michal Meloun # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -30,88 +29,132 @@ #include <sys/bus.h> #include <sys/cpuset.h> -#include <machine/frame.h> -#include <machine/intr.h> +#include <sys/resource.h> +#include <sys/intr.h> INTERFACE pic; CODE { - static int null_pic_bind(device_t dev, struct intr_irqsrc *isrc) + static int + dflt_pic_bind_intr(device_t dev, struct intr_irqsrc *isrc) { + return (EOPNOTSUPP); } - static void null_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) + static int + null_pic_alloc_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) + { + + return (0); + } + + static int + null_pic_release_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) + { + + return (0); + } + + static int + null_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) { - return; + + return (0); } - static void null_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) + static int + null_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) { - return; + + return (0); } - static void null_pic_init_secondary(device_t dev) + static void + null_pic_init_secondary(device_t dev) { - return; } - static void null_pic_ipi_send(device_t dev, cpuset_t cpus, u_int ipi) + static void + null_pic_ipi_send(device_t dev, cpuset_t cpus, u_int ipi) { - return; + } + + static int + dflt_pic_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc *isrc) + { + + return (EOPNOTSUPP); } }; -METHOD int register { +METHOD int alloc_intr { device_t dev; struct intr_irqsrc *isrc; - boolean_t *is_percpu; -}; + struct resource *res; + struct intr_map_data *data; +} DEFAULT null_pic_alloc_intr; -METHOD int unregister { +METHOD int bind_intr { device_t dev; struct intr_irqsrc *isrc; -}; +} DEFAULT dflt_pic_bind_intr; METHOD void disable_intr { device_t dev; struct intr_irqsrc *isrc; -} DEFAULT null_pic_disable_intr; +}; -METHOD void disable_source { +METHOD void enable_intr { device_t dev; struct intr_irqsrc *isrc; }; -METHOD void enable_source { +METHOD int map_intr { device_t dev; - struct intr_irqsrc *isrc; + struct intr_map_data *data; + struct intr_irqsrc **isrcp; }; -METHOD void enable_intr { +METHOD int release_intr { device_t dev; struct intr_irqsrc *isrc; -} DEFAULT null_pic_enable_intr; + struct resource *res; + struct intr_map_data *data; +} DEFAULT null_pic_release_intr; -METHOD void pre_ithread { +METHOD int setup_intr { device_t dev; struct intr_irqsrc *isrc; -}; + struct resource *res; + struct intr_map_data *data; +} DEFAULT null_pic_setup_intr; -METHOD void post_ithread { +METHOD int teardown_intr { device_t dev; struct intr_irqsrc *isrc; -}; + struct resource *res; + struct intr_map_data *data; +} DEFAULT null_pic_teardown_intr; METHOD void post_filter { device_t dev; struct intr_irqsrc *isrc; }; -METHOD int bind { +METHOD void post_ithread { device_t dev; struct intr_irqsrc *isrc; -} DEFAULT null_pic_bind; +}; + +METHOD void pre_ithread { + device_t dev; + struct intr_irqsrc *isrc; +}; METHOD void init_secondary { device_t dev; @@ -121,4 +164,11 @@ METHOD void ipi_send { device_t dev; struct intr_irqsrc *isrc; cpuset_t cpus; + u_int ipi; } DEFAULT null_pic_ipi_send; + +METHOD int ipi_setup { + device_t dev; + u_int ipi; + struct intr_irqsrc **isrcp; +} DEFAULT dflt_pic_ipi_setup; diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c index 8daa9f2..caf9202 100644 --- a/sys/kern/subr_bus.c +++ b/sys/kern/subr_bus.c @@ -839,6 +839,38 @@ sysctl_devctl_queue(SYSCTL_HANDLER_ARGS) return (0); } +/** + * @brief safely quotes strings that might have double quotes in them. + * + * The devctl protocol relies on quoted strings having matching quotes. + * This routine quotes any internal quotes so the resulting string + * is safe to pass to snprintf to construct, for example pnp info strings. + * Strings are always terminated with a NUL, but may be truncated if longer + * than @p len bytes after quotes. + * + * @param dst Buffer to hold the string. Must be at least @p len bytes long + * @param src Original buffer. + * @param len Length of buffer pointed to by @dst, including trailing NUL + */ +void +devctl_safe_quote(char *dst, const char *src, size_t len) +{ + char *walker = dst, *ep = dst + len - 1; + + if (len == 0) + return; + while (src != NULL && walker < ep) + { + if (*src == '"') { + if (ep - walker < 2) + break; + *walker++ = '\\'; + } + *walker++ = *src++; + } + *walker = '\0'; +} + /* End of /dev/devctl code */ static TAILQ_HEAD(,device) bus_data_devices; diff --git a/sys/kern/subr_counter.c b/sys/kern/subr_counter.c index ea2759c..5149f2d 100644 --- a/sys/kern/subr_counter.c +++ b/sys/kern/subr_counter.c @@ -94,3 +94,28 @@ sysctl_handle_counter_u64(SYSCTL_HANDLER_ARGS) return (0); } + +int +sysctl_handle_counter_u64_array(SYSCTL_HANDLER_ARGS) +{ + uint64_t *out; + int error; + + out = malloc(arg2 * sizeof(uint64_t), M_TEMP, M_WAITOK); + for (int i = 0; i < arg2; i++) + out[i] = counter_u64_fetch(((counter_u64_t *)arg1)[i]); + + error = SYSCTL_OUT(req, out, arg2 * sizeof(uint64_t)); + free(out, M_TEMP); + + if (error || !req->newptr) + return (error); + + /* + * Any write attempt to a counter zeroes it. + */ + for (int i = 0; i < arg2; i++) + counter_u64_zero(((counter_u64_t *)arg1)[i]); + + return (0); +} diff --git a/sys/kern/subr_intr.c b/sys/kern/subr_intr.c index 1c97fd4..96319ad 100644 --- a/sys/kern/subr_intr.c +++ b/sys/kern/subr_intr.c @@ -1,7 +1,6 @@ /*- - * Copyright (c) 2012-2014 Jakub Wojciech Klama <jceel@FreeBSD.org>. - * Copyright (c) 2015 Svatopluk Kraus - * Copyright (c) 2015 Michal Meloun + * Copyright (c) 2015-2016 Svatopluk Kraus + * Copyright (c) 2015-2016 Michal Meloun * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,8 +23,6 @@ * 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 <sys/cdefs.h> @@ -38,6 +35,7 @@ __FBSDID("$FreeBSD$"); * - to complete things for removable PICs */ +#include "opt_acpi.h" #include "opt_ddb.h" #include "opt_platform.h" @@ -52,6 +50,7 @@ __FBSDID("$FreeBSD$"); #include <sys/interrupt.h> #include <sys/conf.h> #include <sys/cpuset.h> +#include <sys/rman.h> #include <sys/sched.h> #include <sys/smp.h> #include <machine/atomic.h> @@ -112,6 +111,35 @@ u_int irq_next_free; #define IRQ_INVALID nitems(irq_sources) +/* + * XXX - All stuff around struct intr_dev_data is considered as temporary + * until better place for storing struct intr_map_data will be find. + * + * For now, there are two global interrupt numbers spaces: + * <0, NIRQ) ... interrupts without config data + * managed in irq_sources[] + * IRQ_DDATA_BASE + <0, 2 * NIRQ) ... interrupts with config data + * managed in intr_ddata_tab[] + * + * Read intr_ddata_lookup() to see how these spaces are worked with. + * Note that each interrupt number from second space duplicates some number + * from first space at this moment. An interrupt number from first space can + * be duplicated even multiple times in second space. + */ +struct intr_dev_data { + device_t idd_dev; + intptr_t idd_xref; + u_int idd_irq; + struct intr_map_data idd_data; + struct intr_irqsrc * idd_isrc; +}; + +static struct intr_dev_data *intr_ddata_tab[2 * NIRQ]; +static u_int intr_ddata_first_unused; + +#define IRQ_DDATA_BASE 10000 +CTASSERT(IRQ_DDATA_BASE > IRQ_INVALID); + #ifdef SMP static boolean_t irq_assign_cpu = FALSE; #endif @@ -173,12 +201,10 @@ static inline void isrc_increment_count(struct intr_irqsrc *isrc) { - /* - * XXX - It should be atomic for PPI interrupts. It was proven that - * the lost is measurable easily for timer PPI interrupts. - */ - isrc->isrc_count[0]++; - /*atomic_add_long(&isrc->isrc_count[0], 1);*/ + if (isrc->isrc_flags & INTR_ISRCF_PPI) + atomic_add_long(&isrc->isrc_count[0], 1); + else + isrc->isrc_count[0]++; } /* @@ -233,6 +259,16 @@ isrc_setup_counters(struct intr_irqsrc *isrc) isrc_update_name(isrc, NULL); } +/* + * Virtualization for interrupt source interrupt counters release. + */ +static void +isrc_release_counters(struct intr_irqsrc *isrc) +{ + + panic("%s: not implemented", __func__); +} + #ifdef SMP /* * Virtualization for interrupt source IPI counters setup. @@ -279,8 +315,8 @@ intr_irq_handler(struct trapframe *tf) * be called straight from the interrupt controller, when associated interrupt * source is learned. */ -void -intr_irq_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf) +int +intr_isrc_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf) { KASSERT(isrc != NULL, ("%s: no source", __func__)); @@ -293,57 +329,16 @@ intr_irq_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf) error = isrc->isrc_filter(isrc->isrc_arg, tf); PIC_POST_FILTER(isrc->isrc_dev, isrc); if (error == FILTER_HANDLED) - return; - } else + return (0); + } else #endif if (isrc->isrc_event != NULL) { if (intr_event_handle(isrc->isrc_event, tf) == 0) - return; + return (0); } isrc_increment_straycount(isrc); - PIC_DISABLE_SOURCE(isrc->isrc_dev, isrc); - - device_printf(isrc->isrc_dev, "stray irq <%s> disabled", - isrc->isrc_name); -} - -/* - * Allocate interrupt source. - */ -static struct intr_irqsrc * -isrc_alloc(u_int type, u_int extsize) -{ - struct intr_irqsrc *isrc; - - isrc = malloc(sizeof(*isrc) + extsize, M_INTRNG, M_WAITOK | M_ZERO); - isrc->isrc_irq = IRQ_INVALID; /* just to be safe */ - isrc->isrc_type = type; - isrc->isrc_nspc_type = INTR_IRQ_NSPC_NONE; - isrc->isrc_trig = INTR_TRIGGER_CONFORM; - isrc->isrc_pol = INTR_POLARITY_CONFORM; - CPU_ZERO(&isrc->isrc_cpu); - return (isrc); -} - -/* - * Free interrupt source. - */ -static void -isrc_free(struct intr_irqsrc *isrc) -{ - - free(isrc, M_INTRNG); -} - -void -intr_irq_set_name(struct intr_irqsrc *isrc, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vsnprintf(isrc->isrc_name, INTR_ISRC_NAMELEN, fmt, ap); - va_end(ap); + return (EINVAL); } /* @@ -356,8 +351,8 @@ intr_irq_set_name(struct intr_irqsrc *isrc, const char *fmt, ...) * immediately. However, if only one free handle left which is reused * constantly... */ -static int -isrc_alloc_irq_locked(struct intr_irqsrc *isrc) +static inline int +isrc_alloc_irq(struct intr_irqsrc *isrc) { u_int maxirqs, irq; @@ -383,46 +378,35 @@ found: isrc->isrc_irq = irq; irq_sources[irq] = isrc; - intr_irq_set_name(isrc, "irq%u", irq); - isrc_setup_counters(isrc); - irq_next_free = irq + 1; if (irq_next_free >= maxirqs) irq_next_free = 0; return (0); } -#ifdef notyet + /* * Free unique interrupt number (resource handle) from interrupt source. */ -static int +static inline int isrc_free_irq(struct intr_irqsrc *isrc) { - u_int maxirqs; - mtx_assert(&isrc_table_lock, MA_NOTOWNED); + mtx_assert(&isrc_table_lock, MA_OWNED); - maxirqs = nitems(irq_sources); - if (isrc->isrc_irq >= maxirqs) + if (isrc->isrc_irq >= nitems(irq_sources)) return (EINVAL); - - mtx_lock(&isrc_table_lock); - if (irq_sources[isrc->isrc_irq] != isrc) { - mtx_unlock(&isrc_table_lock); + if (irq_sources[isrc->isrc_irq] != isrc) return (EINVAL); - } irq_sources[isrc->isrc_irq] = NULL; isrc->isrc_irq = IRQ_INVALID; /* just to be safe */ - mtx_unlock(&isrc_table_lock); - return (0); } -#endif + /* * Lookup interrupt source by interrupt number (resource handle). */ -static struct intr_irqsrc * +static inline struct intr_irqsrc * isrc_lookup(u_int irq) { @@ -432,158 +416,159 @@ isrc_lookup(u_int irq) } /* - * Lookup interrupt source by namespace description. + * Initialize interrupt source and register it into global interrupt table. */ -static struct intr_irqsrc * -isrc_namespace_lookup(device_t dev, uint16_t type, uint16_t num) +int +intr_isrc_register(struct intr_irqsrc *isrc, device_t dev, u_int flags, + const char *fmt, ...) { - u_int irq; - struct intr_irqsrc *isrc; + int error; + va_list ap; - mtx_assert(&isrc_table_lock, MA_OWNED); + bzero(isrc, sizeof(struct intr_irqsrc)); + isrc->isrc_dev = dev; + isrc->isrc_irq = IRQ_INVALID; /* just to be safe */ + isrc->isrc_flags = flags; + + va_start(ap, fmt); + vsnprintf(isrc->isrc_name, INTR_ISRC_NAMELEN, fmt, ap); + va_end(ap); - for (irq = 0; irq < nitems(irq_sources); irq++) { - isrc = irq_sources[irq]; - if (isrc != NULL && isrc->isrc_dev == dev && - isrc->isrc_nspc_type == type && isrc->isrc_nspc_num == num) - return (isrc); + mtx_lock(&isrc_table_lock); + error = isrc_alloc_irq(isrc); + if (error != 0) { + mtx_unlock(&isrc_table_lock); + return (error); } - return (NULL); + /* + * Setup interrupt counters, but not for IPI sources. Those are setup + * later and only for used ones (up to INTR_IPI_COUNT) to not exhaust + * our counter pool. + */ + if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) + isrc_setup_counters(isrc); + mtx_unlock(&isrc_table_lock); + return (0); } /* - * Map interrupt source according to namespace into framework. If such mapping - * does not exist, create it. Return unique interrupt number (resource handle) - * associated with mapped interrupt source. + * Deregister interrupt source from global interrupt table. */ -u_int -intr_namespace_map_irq(device_t dev, uint16_t type, uint16_t num) +int +intr_isrc_deregister(struct intr_irqsrc *isrc) { - struct intr_irqsrc *isrc, *new_isrc; int error; - new_isrc = isrc_alloc(INTR_ISRCT_NAMESPACE, 0); - mtx_lock(&isrc_table_lock); - isrc = isrc_namespace_lookup(dev, type, num); - if (isrc != NULL) { - mtx_unlock(&isrc_table_lock); - isrc_free(new_isrc); - return (isrc->isrc_irq); /* already mapped */ - } + if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0) + isrc_release_counters(isrc); + error = isrc_free_irq(isrc); + mtx_unlock(&isrc_table_lock); + return (error); +} - error = isrc_alloc_irq_locked(new_isrc); - if (error != 0) { +static struct intr_dev_data * +intr_ddata_alloc(u_int extsize) +{ + struct intr_dev_data *ddata; + + ddata = malloc(sizeof(*ddata) + extsize, M_INTRNG, M_WAITOK | M_ZERO); + + mtx_lock(&isrc_table_lock); + if (intr_ddata_first_unused >= nitems(intr_ddata_tab)) { mtx_unlock(&isrc_table_lock); - isrc_free(new_isrc); - return (IRQ_INVALID); /* no space left */ + free(ddata, M_INTRNG); + return (NULL); } - - new_isrc->isrc_dev = dev; - new_isrc->isrc_nspc_type = type; - new_isrc->isrc_nspc_num = num; + intr_ddata_tab[intr_ddata_first_unused] = ddata; + ddata->idd_irq = IRQ_DDATA_BASE + intr_ddata_first_unused++; mtx_unlock(&isrc_table_lock); - - return (new_isrc->isrc_irq); + return (ddata); } -#ifdef FDT -/* - * Lookup interrupt source by FDT description. - */ static struct intr_irqsrc * -isrc_fdt_lookup(intptr_t xref, pcell_t *cells, u_int ncells) +intr_ddata_lookup(u_int irq, struct intr_map_data **datap) { - u_int irq, cellsize; + int error; struct intr_irqsrc *isrc; + struct intr_dev_data *ddata; - mtx_assert(&isrc_table_lock, MA_OWNED); + isrc = isrc_lookup(irq); + if (isrc != NULL) { + if (datap != NULL) + *datap = NULL; + return (isrc); + } - cellsize = ncells * sizeof(*cells); - for (irq = 0; irq < nitems(irq_sources); irq++) { - isrc = irq_sources[irq]; - if (isrc != NULL && isrc->isrc_type == INTR_ISRCT_FDT && - isrc->isrc_xref == xref && isrc->isrc_ncells == ncells && - memcmp(isrc->isrc_cells, cells, cellsize) == 0) - return (isrc); + if (irq < IRQ_DDATA_BASE) + return (NULL); + + irq -= IRQ_DDATA_BASE; + if (irq >= nitems(intr_ddata_tab)) + return (NULL); + + ddata = intr_ddata_tab[irq]; + if (ddata->idd_isrc == NULL) { + error = intr_map_irq(ddata->idd_dev, ddata->idd_xref, + &ddata->idd_data, &irq); + if (error != 0) + return (NULL); + ddata->idd_isrc = isrc_lookup(irq); } - return (NULL); + if (datap != NULL) + *datap = &ddata->idd_data; + return (ddata->idd_isrc); } +#ifdef DEV_ACPI /* - * Map interrupt source according to FDT data into framework. If such mapping + * Map interrupt source according to ACPI info into framework. If such mapping * does not exist, create it. Return unique interrupt number (resource handle) * associated with mapped interrupt source. */ u_int -intr_fdt_map_irq(phandle_t node, pcell_t *cells, u_int ncells) +intr_acpi_map_irq(device_t dev, u_int irq, enum intr_polarity pol, + enum intr_trigger trig) { - struct intr_irqsrc *isrc, *new_isrc; - u_int cellsize; - intptr_t xref; - int error; - - xref = (intptr_t)node; /* It's so simple for now. */ - - cellsize = ncells * sizeof(*cells); - new_isrc = isrc_alloc(INTR_ISRCT_FDT, cellsize); - - mtx_lock(&isrc_table_lock); - isrc = isrc_fdt_lookup(xref, cells, ncells); - if (isrc != NULL) { - mtx_unlock(&isrc_table_lock); - isrc_free(new_isrc); - return (isrc->isrc_irq); /* already mapped */ - } - - error = isrc_alloc_irq_locked(new_isrc); - if (error != 0) { - mtx_unlock(&isrc_table_lock); - isrc_free(new_isrc); - return (IRQ_INVALID); /* no space left */ - } - - new_isrc->isrc_xref = xref; - new_isrc->isrc_ncells = ncells; - memcpy(new_isrc->isrc_cells, cells, cellsize); - mtx_unlock(&isrc_table_lock); - - return (new_isrc->isrc_irq); + struct intr_dev_data *ddata; + + ddata = intr_ddata_alloc(0); + if (ddata == NULL) + return (0xFFFFFFFF); /* no space left */ + + ddata->idd_dev = dev; + ddata->idd_data.type = INTR_MAP_DATA_ACPI; + ddata->idd_data.acpi.irq = irq; + ddata->idd_data.acpi.pol = pol; + ddata->idd_data.acpi.trig = trig; + return (ddata->idd_irq); } #endif - +#ifdef FDT /* - * Register interrupt source into interrupt controller. + * Map interrupt source according to FDT data into framework. If such mapping + * does not exist, create it. Return unique interrupt number (resource handle) + * associated with mapped interrupt source. */ -static int -isrc_register(struct intr_irqsrc *isrc) +u_int +intr_fdt_map_irq(phandle_t node, pcell_t *cells, u_int ncells) { - struct intr_pic *pic; - boolean_t is_percpu; - int error; - - if (isrc->isrc_flags & INTR_ISRCF_REGISTERED) - return (0); - - if (isrc->isrc_dev == NULL) { - pic = pic_lookup(NULL, isrc->isrc_xref); - if (pic == NULL || pic->pic_dev == NULL) - return (ESRCH); - isrc->isrc_dev = pic->pic_dev; - } - - error = PIC_REGISTER(isrc->isrc_dev, isrc, &is_percpu); - if (error != 0) - return (error); + struct intr_dev_data *ddata; + u_int cellsize; - mtx_lock(&isrc_table_lock); - isrc->isrc_flags |= INTR_ISRCF_REGISTERED; - if (is_percpu) - isrc->isrc_flags |= INTR_ISRCF_PERCPU; - isrc_update_name(isrc, NULL); - mtx_unlock(&isrc_table_lock); - return (0); + cellsize = ncells * sizeof(*cells); + ddata = intr_ddata_alloc(cellsize); + if (ddata == NULL) + return (0xFFFFFFFF); /* no space left */ + + ddata->idd_xref = (intptr_t)node; + ddata->idd_data.type = INTR_MAP_DATA_FDT; + ddata->idd_data.fdt.ncells = ncells; + ddata->idd_data.fdt.cells = (pcell_t *)(ddata + 1); + memcpy(ddata->idd_data.fdt.cells, cells, cellsize); + return (ddata->idd_irq); } +#endif #ifdef INTR_SOLO /* @@ -678,7 +663,7 @@ intr_isrc_assign_cpu(void *arg, int cpu) * informed if the call is successfull. */ if (irq_assign_cpu) { - error = PIC_BIND(isrc->isrc_dev, isrc); + error = PIC_BIND_INTR(isrc->isrc_dev, isrc); if (error) { CPU_ZERO(&isrc->isrc_cpu); mtx_unlock(&isrc_table_lock); @@ -774,7 +759,7 @@ isrc_add_handler(struct intr_irqsrc *isrc, const char *name, /* * Lookup interrupt controller locked. */ -static struct intr_pic * +static inline struct intr_pic * pic_lookup_locked(device_t dev, intptr_t xref) { struct intr_pic *pic; @@ -801,7 +786,6 @@ pic_lookup(device_t dev, intptr_t xref) mtx_lock(&pic_list_lock); pic = pic_lookup_locked(dev, xref); mtx_unlock(&pic_list_lock); - return (pic); } @@ -871,7 +855,7 @@ intr_pic_register(device_t dev, intptr_t xref) * Unregister interrupt controller. */ int -intr_pic_unregister(device_t dev, intptr_t xref) +intr_pic_deregister(device_t dev, intptr_t xref) { panic("%s: not implemented", __func__); @@ -923,12 +907,73 @@ intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter, } int -intr_irq_add_handler(device_t dev, driver_filter_t filt, driver_intr_t hand, - void *arg, u_int irq, int flags, void **cookiep) +intr_map_irq(device_t dev, intptr_t xref, struct intr_map_data *data, + u_int *irqp) { - const char *name; + int error; + struct intr_irqsrc *isrc; + struct intr_pic *pic; + + if (data == NULL) + return (EINVAL); + + pic = pic_lookup(dev, xref); + if (pic == NULL || pic->pic_dev == NULL) + return (ESRCH); + + error = PIC_MAP_INTR(pic->pic_dev, data, &isrc); + if (error == 0) + *irqp = isrc->isrc_irq; + return (error); +} + +int +intr_alloc_irq(device_t dev, struct resource *res) +{ + struct intr_map_data *data; + struct intr_irqsrc *isrc; + + KASSERT(rman_get_start(res) == rman_get_end(res), + ("%s: more interrupts in resource", __func__)); + + isrc = intr_ddata_lookup(rman_get_start(res), &data); + if (isrc == NULL) + return (EINVAL); + + return (PIC_ALLOC_INTR(isrc->isrc_dev, isrc, res, data)); +} + +int +intr_release_irq(device_t dev, struct resource *res) +{ + struct intr_map_data *data; struct intr_irqsrc *isrc; + + KASSERT(rman_get_start(res) == rman_get_end(res), + ("%s: more interrupts in resource", __func__)); + + isrc = intr_ddata_lookup(rman_get_start(res), &data); + if (isrc == NULL) + return (EINVAL); + + return (PIC_RELEASE_INTR(isrc->isrc_dev, isrc, res, data)); +} + +int +intr_setup_irq(device_t dev, struct resource *res, driver_filter_t filt, + driver_intr_t hand, void *arg, int flags, void **cookiep) +{ int error; + struct intr_map_data *data; + struct intr_irqsrc *isrc; + const char *name; + + KASSERT(rman_get_start(res) == rman_get_end(res), + ("%s: more interrupts in resource", __func__)); + + isrc = intr_ddata_lookup(rman_get_start(res), &data); + if (isrc == NULL) + return (EINVAL); name = device_get_nameunit(dev); @@ -947,21 +992,7 @@ intr_irq_add_handler(device_t dev, driver_filter_t filt, driver_intr_t hand, debugf("irq %u cannot solo on %s\n", irq, name); return (EINVAL); } -#endif - isrc = isrc_lookup(irq); - if (isrc == NULL) { - debugf("irq %u without source on %s\n", irq, name); - return (EINVAL); - } - - error = isrc_register(isrc); - if (error != 0) { - debugf("irq %u map error %d on %s\n", irq, error, name); - return (error); - } - -#ifdef INTR_SOLO if (flags & INTR_SOLO) { error = iscr_setup_filter(isrc, name, (intr_irq_filter_t *)filt, arg, cookiep); @@ -978,24 +1009,32 @@ intr_irq_add_handler(device_t dev, driver_filter_t filt, driver_intr_t hand, return (error); mtx_lock(&isrc_table_lock); - isrc->isrc_handlers++; - if (isrc->isrc_handlers == 1) { - PIC_ENABLE_INTR(isrc->isrc_dev, isrc); - PIC_ENABLE_SOURCE(isrc->isrc_dev, isrc); + error = PIC_SETUP_INTR(isrc->isrc_dev, isrc, res, data); + if (error == 0) { + isrc->isrc_handlers++; + if (isrc->isrc_handlers == 1) + PIC_ENABLE_INTR(isrc->isrc_dev, isrc); } mtx_unlock(&isrc_table_lock); - return (0); + if (error != 0) + intr_event_remove_handler(*cookiep); + return (error); } int -intr_irq_remove_handler(device_t dev, u_int irq, void *cookie) +intr_teardown_irq(device_t dev, struct resource *res, void *cookie) { - struct intr_irqsrc *isrc; int error; + struct intr_map_data *data; + struct intr_irqsrc *isrc; - isrc = isrc_lookup(irq); + KASSERT(rman_get_start(res) == rman_get_end(res), + ("%s: more interrupts in resource", __func__)); + + isrc = intr_ddata_lookup(rman_get_start(res), &data); if (isrc == NULL || isrc->isrc_handlers == 0) return (EINVAL); + #ifdef INTR_SOLO if (isrc->isrc_filter != NULL) { if (isrc != cookie) @@ -1005,8 +1044,8 @@ intr_irq_remove_handler(device_t dev, u_int irq, void *cookie) isrc->isrc_filter = NULL; isrc->isrc_arg = NULL; isrc->isrc_handlers = 0; - PIC_DISABLE_SOURCE(isrc->isrc_dev, isrc); PIC_DISABLE_INTR(isrc->isrc_dev, isrc); + PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data); isrc_update_name(isrc, NULL); mtx_unlock(&isrc_table_lock); return (0); @@ -1019,10 +1058,9 @@ intr_irq_remove_handler(device_t dev, u_int irq, void *cookie) if (error == 0) { mtx_lock(&isrc_table_lock); isrc->isrc_handlers--; - if (isrc->isrc_handlers == 0) { - PIC_DISABLE_SOURCE(isrc->isrc_dev, isrc); + if (isrc->isrc_handlers == 0) PIC_DISABLE_INTR(isrc->isrc_dev, isrc); - } + PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data); intrcnt_updatename(isrc); mtx_unlock(&isrc_table_lock); } @@ -1030,36 +1068,16 @@ intr_irq_remove_handler(device_t dev, u_int irq, void *cookie) } int -intr_irq_config(u_int irq, enum intr_trigger trig, enum intr_polarity pol) +intr_describe_irq(device_t dev, struct resource *res, void *cookie, + const char *descr) { + int error; struct intr_irqsrc *isrc; - isrc = isrc_lookup(irq); - if (isrc == NULL) - return (EINVAL); - - if (isrc->isrc_handlers != 0) - return (EBUSY); /* interrrupt is enabled (active) */ + KASSERT(rman_get_start(res) == rman_get_end(res), + ("%s: more interrupts in resource", __func__)); - /* - * Once an interrupt is enabled, we do not change its configuration. - * A controller PIC_ENABLE_INTR() method is called when an interrupt - * is going to be enabled. In this method, a controller should setup - * the interrupt according to saved configuration parameters. - */ - isrc->isrc_trig = trig; - isrc->isrc_pol = pol; - - return (0); -} - -int -intr_irq_describe(u_int irq, void *cookie, const char *descr) -{ - struct intr_irqsrc *isrc; - int error; - - isrc = isrc_lookup(irq); + isrc = intr_ddata_lookup(rman_get_start(res), NULL); if (isrc == NULL || isrc->isrc_handlers == 0) return (EINVAL); #ifdef INTR_SOLO @@ -1084,11 +1102,14 @@ intr_irq_describe(u_int irq, void *cookie, const char *descr) #ifdef SMP int -intr_irq_bind(u_int irq, int cpu) +intr_bind_irq(device_t dev, struct resource *res, int cpu) { struct intr_irqsrc *isrc; - isrc = isrc_lookup(irq); + KASSERT(rman_get_start(res) == rman_get_end(res), + ("%s: more interrupts in resource", __func__)); + + isrc = intr_ddata_lookup(rman_get_start(res), NULL); if (isrc == NULL || isrc->isrc_handlers == 0) return (EINVAL); #ifdef INTR_SOLO @@ -1135,7 +1156,7 @@ intr_irq_shuffle(void *arg __unused) for (i = 0; i < NIRQ; i++) { isrc = irq_sources[i]; if (isrc == NULL || isrc->isrc_handlers == 0 || - isrc->isrc_flags & INTR_ISRCF_PERCPU) + isrc->isrc_flags & INTR_ISRCF_PPI) continue; if (isrc->isrc_event != NULL && @@ -1151,7 +1172,7 @@ intr_irq_shuffle(void *arg __unused) * for bound ISRC. The best thing we can do is to clear * isrc_cpu so inconsistency with ie_cpu will be detectable. */ - if (PIC_BIND(isrc->isrc_dev, isrc) != 0) + if (PIC_BIND_INTR(isrc->isrc_dev, isrc) != 0) CPU_ZERO(&isrc->isrc_cpu); } mtx_unlock(&isrc_table_lock); @@ -1196,6 +1217,7 @@ intr_pic_init_secondary(void) DB_SHOW_COMMAND(irqs, db_show_irqs) { u_int i, irqsum; + u_long num; struct intr_irqsrc *isrc; for (irqsum = 0, i = 0; i < NIRQ; i++) { @@ -1203,11 +1225,11 @@ DB_SHOW_COMMAND(irqs, db_show_irqs) if (isrc == NULL) continue; + num = isrc->isrc_count != NULL ? isrc->isrc_count[0] : 0; db_printf("irq%-3u <%s>: cpu %02lx%s cnt %lu\n", i, isrc->isrc_name, isrc->isrc_cpu.__bits[0], - isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "", - isrc->isrc_count[0]); - irqsum += isrc->isrc_count[0]; + isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "", num); + irqsum += num; } db_printf("irq total %u\n", irqsum); } diff --git a/sys/kern/subr_rman.c b/sys/kern/subr_rman.c index 41f1f34..9bbec64 100644 --- a/sys/kern/subr_rman.c +++ b/sys/kern/subr_rman.c @@ -159,7 +159,7 @@ rman_manage_region(struct rman *rm, rman_res_t start, rman_res_t end) struct resource_i *r, *s, *t; int rv = 0; - DPRINTF(("rman_manage_region: <%s> request: start %#lx, end %#lx\n", + DPRINTF(("rman_manage_region: <%s> request: start %#jx, end %#jx\n", rm->rm_descr, start, end)); if (start < rm->rm_start || end > rm->rm_end) return EINVAL; @@ -174,7 +174,7 @@ rman_manage_region(struct rman *rm, rman_res_t start, rman_res_t end) /* Skip entries before us. */ TAILQ_FOREACH(s, &rm->rm_list, r_link) { - if (s->r_end == ULONG_MAX) + if (s->r_end == ~0) break; if (s->r_end + 1 >= r->r_start) break; @@ -444,8 +444,8 @@ rman_reserve_resource_bound(struct rman *rm, rman_res_t start, rman_res_t end, rv = NULL; - DPRINTF(("rman_reserve_resource_bound: <%s> request: [%#lx, %#lx], " - "length %#lx, flags %u, device %s\n", rm->rm_descr, start, end, + DPRINTF(("rman_reserve_resource_bound: <%s> request: [%#jx, %#jx], " + "length %#jx, flags %x, device %s\n", rm->rm_descr, start, end, count, flags, dev == NULL ? "<null>" : device_get_nameunit(dev))); KASSERT((flags & RF_FIRSTSHARE) == 0, @@ -454,19 +454,29 @@ rman_reserve_resource_bound(struct rman *rm, rman_res_t start, rman_res_t end, mtx_lock(rm->rm_mtx); + r = TAILQ_FIRST(&rm->rm_list); + if (r == NULL) { + DPRINTF(("NULL list head\n")); + } else { + DPRINTF(("rman_reserve_resource_bound: trying %#jx <%#jx,%#jx>\n", + r->r_end, start, count-1)); + } for (r = TAILQ_FIRST(&rm->rm_list); r && r->r_end < start + count - 1; - r = TAILQ_NEXT(r, r_link)) + r = TAILQ_NEXT(r, r_link)) { ; + DPRINTF(("rman_reserve_resource_bound: tried %#jx <%#jx,%#jx>\n", + r->r_end, start, count-1)); + } if (r == NULL) { DPRINTF(("could not find a region\n")); goto out; } - amask = (1ul << RF_ALIGNMENT(flags)) - 1; - KASSERT(start <= ULONG_MAX - amask, - ("start (%#lx) + amask (%#lx) would wrap around", start, amask)); + amask = (1ull << RF_ALIGNMENT(flags)) - 1; + KASSERT(start <= RM_MAX_END - amask, + ("start (%#jx) + amask (%#jx) would wrap around", start, amask)); /* If bound is 0, bmask will also be 0 */ bmask = ~(bound - 1); @@ -474,18 +484,18 @@ rman_reserve_resource_bound(struct rman *rm, rman_res_t start, rman_res_t end, * First try to find an acceptable totally-unshared region. */ for (s = r; s; s = TAILQ_NEXT(s, r_link)) { - DPRINTF(("considering [%#lx, %#lx]\n", s->r_start, s->r_end)); + DPRINTF(("considering [%#jx, %#jx]\n", s->r_start, s->r_end)); /* * The resource list is sorted, so there is no point in * searching further once r_start is too large. */ if (s->r_start > end - (count - 1)) { - DPRINTF(("s->r_start (%#lx) + count - 1> end (%#lx)\n", + DPRINTF(("s->r_start (%#jx) + count - 1> end (%#jx)\n", s->r_start, end)); break; } - if (s->r_start > ULONG_MAX - amask) { - DPRINTF(("s->r_start (%#lx) + amask (%#lx) too large\n", + if (s->r_start > RM_MAX_END - amask) { + DPRINTF(("s->r_start (%#jx) + amask (%#jx) too large\n", s->r_start, amask)); break; } @@ -493,7 +503,7 @@ rman_reserve_resource_bound(struct rman *rm, rman_res_t start, rman_res_t end, DPRINTF(("region is allocated\n")); continue; } - rstart = ulmax(s->r_start, start); + rstart = ummax(s->r_start, start); /* * Try to find a region by adjusting to boundary and alignment * until both conditions are satisfied. This is not an optimal @@ -505,16 +515,16 @@ rman_reserve_resource_bound(struct rman *rm, rman_res_t start, rman_res_t end, rstart += bound - (rstart & ~bmask); } while ((rstart & amask) != 0 && rstart < end && rstart < s->r_end); - rend = ulmin(s->r_end, ulmax(rstart + count - 1, end)); + rend = ummin(s->r_end, ummax(rstart + count - 1, end)); if (rstart > rend) { DPRINTF(("adjusted start exceeds end\n")); continue; } - DPRINTF(("truncated region: [%#lx, %#lx]; size %#lx (requested %#lx)\n", + DPRINTF(("truncated region: [%#jx, %#jx]; size %#jx (requested %#jx)\n", rstart, rend, (rend - rstart + 1), count)); if ((rend - rstart + 1) >= count) { - DPRINTF(("candidate region: [%#lx, %#lx], size %#lx\n", + DPRINTF(("candidate region: [%#jx, %#jx], size %#jx\n", rstart, rend, (rend - rstart + 1))); if ((s->r_end - s->r_start + 1) == count) { DPRINTF(("candidate region is entire chunk\n")); @@ -545,7 +555,7 @@ rman_reserve_resource_bound(struct rman *rm, rman_res_t start, rman_res_t end, if (s->r_start < rv->r_start && s->r_end > rv->r_end) { DPRINTF(("splitting region in three parts: " - "[%#lx, %#lx]; [%#lx, %#lx]; [%#lx, %#lx]\n", + "[%#jx, %#jx]; [%#jx, %#jx]; [%#jx, %#jx]\n", s->r_start, rv->r_start - 1, rv->r_start, rv->r_end, rv->r_end + 1, s->r_end)); @@ -1032,8 +1042,8 @@ dump_rman_header(struct rman *rm) if (db_pager_quit) return; - db_printf("rman %p: %s (0x%lx-0x%lx full range)\n", - rm, rm->rm_descr, rm->rm_start, rm->rm_end); + db_printf("rman %p: %s (0x%jx-0x%jx full range)\n", + rm, rm->rm_descr, (rman_res_t)rm->rm_start, (rman_res_t)rm->rm_end); } static void @@ -1051,7 +1061,7 @@ dump_rman(struct rman *rm) devname = "nomatch"; } else devname = NULL; - db_printf(" 0x%lx-0x%lx (RID=%d) ", + db_printf(" 0x%jx-0x%jx (RID=%d) ", r->r_start, r->r_end, r->r_rid); if (devname != NULL) db_printf("(%s)\n", devname); diff --git a/sys/kern/subr_sleepqueue.c b/sys/kern/subr_sleepqueue.c index 12908f6..ef06c48 100644 --- a/sys/kern/subr_sleepqueue.c +++ b/sys/kern/subr_sleepqueue.c @@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$"); #include "opt_sleepqueue_profiling.h" #include "opt_ddb.h" #include "opt_sched.h" +#include "opt_stack.h" #include <sys/param.h> #include <sys/systm.h> @@ -75,6 +76,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sdt.h> #include <sys/signalvar.h> #include <sys/sleepqueue.h> +#include <sys/stack.h> #include <sys/sysctl.h> #include <vm/uma.h> @@ -83,6 +85,7 @@ __FBSDID("$FreeBSD$"); #include <ddb/ddb.h> #endif + /* * Constants for the hash table of sleep queue chains. * SC_TABLESIZE must be a power of two for SC_MASK to work properly. @@ -382,6 +385,8 @@ sleepq_set_timeout_sbt(void *wchan, sbintime_t sbt, sbintime_t pr, MPASS(TD_ON_SLEEPQ(td)); MPASS(td->td_sleepqueue == NULL); MPASS(wchan != NULL); + if (cold) + panic("timed sleep before timers are working"); callout_reset_sbt_on(&td->td_slpcallout, sbt, pr, sleepq_timeout, td, PCPU_GET(cpuid), flags | C_DIRECT_EXEC); } @@ -1034,6 +1039,122 @@ sleepq_abort(struct thread *td, int intrval) return (sleepq_resume_thread(sq, td, 0)); } +/* + * Prints the stacks of all threads presently sleeping on wchan/queue to + * the sbuf sb. Sets count_stacks_printed to the number of stacks actually + * printed. Typically, this will equal the number of threads sleeping on the + * queue, but may be less if sb overflowed before all stacks were printed. + */ +#ifdef STACK +int +sleepq_sbuf_print_stacks(struct sbuf *sb, void *wchan, int queue, + int *count_stacks_printed) +{ + struct thread *td, *td_next; + struct sleepqueue *sq; + struct stack **st; + struct sbuf **td_infos; + int i, stack_idx, error, stacks_to_allocate; + bool finished, partial_print; + + error = 0; + finished = false; + partial_print = false; + + KASSERT(wchan != NULL, ("%s: invalid NULL wait channel", __func__)); + MPASS((queue >= 0) && (queue < NR_SLEEPQS)); + + stacks_to_allocate = 10; + for (i = 0; i < 3 && !finished ; i++) { + /* We cannot malloc while holding the queue's spinlock, so + * we do our mallocs now, and hope it is enough. If it + * isn't, we will free these, drop the lock, malloc more, + * and try again, up to a point. After that point we will + * give up and report ENOMEM. We also cannot write to sb + * during this time since the client may have set the + * SBUF_AUTOEXTEND flag on their sbuf, which could cause a + * malloc as we print to it. So we defer actually printing + * to sb until after we drop the spinlock. + */ + + /* Where we will store the stacks. */ + st = malloc(sizeof(struct stack *) * stacks_to_allocate, + M_TEMP, M_WAITOK); + for (stack_idx = 0; stack_idx < stacks_to_allocate; + stack_idx++) + st[stack_idx] = stack_create(); + + /* Where we will store the td name, tid, etc. */ + td_infos = malloc(sizeof(struct sbuf *) * stacks_to_allocate, + M_TEMP, M_WAITOK); + for (stack_idx = 0; stack_idx < stacks_to_allocate; + stack_idx++) + td_infos[stack_idx] = sbuf_new(NULL, NULL, + MAXCOMLEN + sizeof(struct thread *) * 2 + 40, + SBUF_FIXEDLEN); + + sleepq_lock(wchan); + sq = sleepq_lookup(wchan); + if (sq == NULL) { + /* This sleepq does not exist; exit and return ENOENT. */ + error = ENOENT; + finished = true; + sleepq_release(wchan); + goto loop_end; + } + + stack_idx = 0; + /* Save thread info */ + TAILQ_FOREACH_SAFE(td, &sq->sq_blocked[queue], td_slpq, + td_next) { + if (stack_idx >= stacks_to_allocate) + goto loop_end; + + /* Note the td_lock is equal to the sleepq_lock here. */ + stack_save_td(st[stack_idx], td); + + sbuf_printf(td_infos[stack_idx], "%d: %s %p", + td->td_tid, td->td_name, td); + + ++stack_idx; + } + + finished = true; + sleepq_release(wchan); + + /* Print the stacks */ + for (i = 0; i < stack_idx; i++) { + sbuf_finish(td_infos[i]); + sbuf_printf(sb, "--- thread %s: ---\n", sbuf_data(td_infos[i])); + stack_sbuf_print(sb, st[i]); + sbuf_printf(sb, "\n"); + + error = sbuf_error(sb); + if (error == 0) + *count_stacks_printed = stack_idx; + } + +loop_end: + if (!finished) + sleepq_release(wchan); + for (stack_idx = 0; stack_idx < stacks_to_allocate; + stack_idx++) + stack_destroy(st[stack_idx]); + for (stack_idx = 0; stack_idx < stacks_to_allocate; + stack_idx++) + sbuf_delete(td_infos[stack_idx]); + free(st, M_TEMP); + free(td_infos, M_TEMP); + stacks_to_allocate *= 10; + } + + if (!finished && error == 0) + error = ENOMEM; + + return (error); +} +#endif + #ifdef SLEEPQUEUE_PROFILING #define SLEEPQ_PROF_LOCATIONS 1024 #define SLEEPQ_SBUFSIZE 512 diff --git a/sys/kern/subr_smp.c b/sys/kern/subr_smp.c index 82349f8..9848136 100644 --- a/sys/kern/subr_smp.c +++ b/sys/kern/subr_smp.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); #include <sys/proc.h> #include <sys/bus.h> #include <sys/lock.h> +#include <sys/malloc.h> #include <sys/mutex.h> #include <sys/pcpu.h> #include <sys/sched.h> @@ -51,6 +52,10 @@ __FBSDID("$FreeBSD$"); #include "opt_sched.h" #ifdef SMP +MALLOC_DEFINE(M_TOPO, "toponodes", "SMP topology data"); +#endif + +#ifdef SMP volatile cpuset_t stopped_cpus; volatile cpuset_t started_cpus; volatile cpuset_t suspended_cpus; @@ -556,7 +561,7 @@ smp_rendezvous(void (* setup_func)(void *), smp_rendezvous_cpus(all_cpus, setup_func, action_func, teardown_func, arg); } -static struct cpu_group group[MAXCPU]; +static struct cpu_group group[MAXCPU * MAX_CACHE_LEVELS + 1]; struct cpu_group * smp_topo(void) @@ -616,6 +621,17 @@ smp_topo(void) } struct cpu_group * +smp_topo_alloc(u_int count) +{ + static u_int index; + u_int curr; + + curr = index; + index += count; + return (&group[curr]); +} + +struct cpu_group * smp_topo_none(void) { struct cpu_group *top; @@ -861,3 +877,233 @@ sysctl_kern_smp_active(SYSCTL_HANDLER_ARGS) return (error); } + +#ifdef SMP +void +topo_init_node(struct topo_node *node) +{ + + bzero(node, sizeof(*node)); + TAILQ_INIT(&node->children); +} + +void +topo_init_root(struct topo_node *root) +{ + + topo_init_node(root); + root->type = TOPO_TYPE_SYSTEM; +} + +struct topo_node * +topo_add_node_by_hwid(struct topo_node *parent, int hwid, + topo_node_type type, uintptr_t subtype) +{ + struct topo_node *node; + + TAILQ_FOREACH_REVERSE(node, &parent->children, + topo_children, siblings) { + if (node->hwid == hwid + && node->type == type && node->subtype == subtype) { + return (node); + } + } + + node = malloc(sizeof(*node), M_TOPO, M_WAITOK); + topo_init_node(node); + node->parent = parent; + node->hwid = hwid; + node->type = type; + node->subtype = subtype; + TAILQ_INSERT_TAIL(&parent->children, node, siblings); + parent->nchildren++; + + return (node); +} + +struct topo_node * +topo_find_node_by_hwid(struct topo_node *parent, int hwid, + topo_node_type type, uintptr_t subtype) +{ + + struct topo_node *node; + + TAILQ_FOREACH(node, &parent->children, siblings) { + if (node->hwid == hwid + && node->type == type && node->subtype == subtype) { + return (node); + } + } + + return (NULL); +} + +void +topo_promote_child(struct topo_node *child) +{ + struct topo_node *next; + struct topo_node *node; + struct topo_node *parent; + + parent = child->parent; + next = TAILQ_NEXT(child, siblings); + TAILQ_REMOVE(&parent->children, child, siblings); + TAILQ_INSERT_HEAD(&parent->children, child, siblings); + + while (next != NULL) { + node = next; + next = TAILQ_NEXT(node, siblings); + TAILQ_REMOVE(&parent->children, node, siblings); + TAILQ_INSERT_AFTER(&parent->children, child, node, siblings); + child = node; + } +} + +struct topo_node * +topo_next_node(struct topo_node *top, struct topo_node *node) +{ + struct topo_node *next; + + if ((next = TAILQ_FIRST(&node->children)) != NULL) + return (next); + + if ((next = TAILQ_NEXT(node, siblings)) != NULL) + return (next); + + while ((node = node->parent) != top) + if ((next = TAILQ_NEXT(node, siblings)) != NULL) + return (next); + + return (NULL); +} + +struct topo_node * +topo_next_nonchild_node(struct topo_node *top, struct topo_node *node) +{ + struct topo_node *next; + + if ((next = TAILQ_NEXT(node, siblings)) != NULL) + return (next); + + while ((node = node->parent) != top) + if ((next = TAILQ_NEXT(node, siblings)) != NULL) + return (next); + + return (NULL); +} + +void +topo_set_pu_id(struct topo_node *node, cpuid_t id) +{ + + KASSERT(node->type == TOPO_TYPE_PU, + ("topo_set_pu_id: wrong node type: %u", node->type)); + KASSERT(CPU_EMPTY(&node->cpuset) && node->cpu_count == 0, + ("topo_set_pu_id: cpuset already not empty")); + node->id = id; + CPU_SET(id, &node->cpuset); + node->cpu_count = 1; + node->subtype = 1; + + while ((node = node->parent) != NULL) { + if (CPU_ISSET(id, &node->cpuset)) + break; + CPU_SET(id, &node->cpuset); + node->cpu_count++; + } +} + +int +topo_analyze(struct topo_node *topo_root, int all, + int *pkg_count, int *cores_per_pkg, int *thrs_per_core) +{ + struct topo_node *pkg_node; + struct topo_node *core_node; + struct topo_node *pu_node; + int thrs_per_pkg; + int cpp_counter; + int tpc_counter; + int tpp_counter; + + *pkg_count = 0; + *cores_per_pkg = -1; + *thrs_per_core = -1; + thrs_per_pkg = -1; + pkg_node = topo_root; + while (pkg_node != NULL) { + if (pkg_node->type != TOPO_TYPE_PKG) { + pkg_node = topo_next_node(topo_root, pkg_node); + continue; + } + if (!all && CPU_EMPTY(&pkg_node->cpuset)) { + pkg_node = topo_next_nonchild_node(topo_root, pkg_node); + continue; + } + + (*pkg_count)++; + + cpp_counter = 0; + tpp_counter = 0; + core_node = pkg_node; + while (core_node != NULL) { + if (core_node->type == TOPO_TYPE_CORE) { + if (!all && CPU_EMPTY(&core_node->cpuset)) { + core_node = + topo_next_nonchild_node(pkg_node, + core_node); + continue; + } + + cpp_counter++; + + tpc_counter = 0; + pu_node = core_node; + while (pu_node != NULL) { + if (pu_node->type == TOPO_TYPE_PU && + (all || !CPU_EMPTY(&pu_node->cpuset))) + tpc_counter++; + pu_node = topo_next_node(core_node, + pu_node); + } + + if (*thrs_per_core == -1) + *thrs_per_core = tpc_counter; + else if (*thrs_per_core != tpc_counter) + return (0); + + core_node = topo_next_nonchild_node(pkg_node, + core_node); + } else { + /* PU node directly under PKG. */ + if (core_node->type == TOPO_TYPE_PU && + (all || !CPU_EMPTY(&core_node->cpuset))) + tpp_counter++; + core_node = topo_next_node(pkg_node, + core_node); + } + } + + if (*cores_per_pkg == -1) + *cores_per_pkg = cpp_counter; + else if (*cores_per_pkg != cpp_counter) + return (0); + if (thrs_per_pkg == -1) + thrs_per_pkg = tpp_counter; + else if (thrs_per_pkg != tpp_counter) + return (0); + + pkg_node = topo_next_nonchild_node(topo_root, pkg_node); + } + + KASSERT(*pkg_count > 0, + ("bug in topology or analysis")); + if (*cores_per_pkg == 0) { + KASSERT(*thrs_per_core == -1 && thrs_per_pkg > 0, + ("bug in topology or analysis")); + *thrs_per_core = thrs_per_pkg; + } + + return (1); +} +#endif /* SMP */ + diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c index fe79f52..75fb66e 100644 --- a/sys/kern/sys_generic.c +++ b/sys/kern/sys_generic.c @@ -89,12 +89,14 @@ __FBSDID("$FreeBSD$"); #define SYS_IOCTL_SMALL_SIZE 128 /* bytes */ #define SYS_IOCTL_SMALL_ALIGN 8 /* bytes */ -int iosize_max_clamp = 0; +#ifdef __LP64__ +static int iosize_max_clamp = 0; SYSCTL_INT(_debug, OID_AUTO, iosize_max_clamp, CTLFLAG_RW, &iosize_max_clamp, 0, "Clamp max i/o size to INT_MAX"); -int devfs_iosize_max_clamp = 1; +static int devfs_iosize_max_clamp = 1; SYSCTL_INT(_debug, OID_AUTO, devfs_iosize_max_clamp, CTLFLAG_RW, &devfs_iosize_max_clamp, 0, "Clamp max i/o size to INT_MAX for devices"); +#endif /* * Assert that the return value of read(2) and write(2) syscalls fits @@ -159,6 +161,24 @@ struct selfd { static uma_zone_t selfd_zone; static struct mtx_pool *mtxpool_select; +#ifdef __LP64__ +size_t +devfs_iosize_max(void) +{ + + return (devfs_iosize_max_clamp || SV_CURPROC_FLAG(SV_ILP32) ? + INT_MAX : SSIZE_MAX); +} + +size_t +iosize_max(void) +{ + + return (iosize_max_clamp || SV_CURPROC_FLAG(SV_ILP32) ? + INT_MAX : SSIZE_MAX); +} +#endif + #ifndef _SYS_SYSPROTO_H_ struct read_args { int fd; diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c index 44ddd81..3575028 100644 --- a/sys/kern/syscalls.c +++ b/sys/kern/syscalls.c @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/kern/syscalls.master 296572 2016-03-09 19:05:11Z jhb + * created from FreeBSD: head/sys/kern/syscalls.master 297167 2016-03-21 21:37:33Z jhb */ const char *syscallnames[] = { diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index f08f5e3..5675425 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -554,7 +554,7 @@ 312 AUE_SETRESGID STD { int setresgid(gid_t rgid, gid_t egid, \ gid_t sgid); } 313 AUE_NULL OBSOL signanosleep -314 AUE_NULL STD { int aio_return(struct aiocb *aiocbp); } +314 AUE_NULL STD { ssize_t aio_return(struct aiocb *aiocbp); } 315 AUE_NULL STD { int aio_suspend( \ struct aiocb * const * aiocbp, int nent, \ const struct timespec *timeout); } @@ -643,7 +643,7 @@ 358 AUE_EXTATTR_DELETE_FILE STD { int extattr_delete_file(const char *path, \ int attrnamespace, \ const char *attrname); } -359 AUE_NULL STD { int aio_waitcomplete( \ +359 AUE_NULL STD { ssize_t aio_waitcomplete( \ struct aiocb **aiocbp, \ struct timespec *timeout); } 360 AUE_GETRESUID STD { int getresuid(uid_t *ruid, uid_t *euid, \ diff --git a/sys/kern/systrace_args.c b/sys/kern/systrace_args.c index 6fd03f1..c2ea2b4 100644 --- a/sys/kern/systrace_args.c +++ b/sys/kern/systrace_args.c @@ -9796,7 +9796,7 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) /* aio_return */ case 314: if (ndx == 0 || ndx == 1) - p = "int"; + p = "ssize_t"; break; /* aio_suspend */ case 315: @@ -9972,7 +9972,7 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) /* aio_waitcomplete */ case 359: if (ndx == 0 || ndx == 1) - p = "int"; + p = "ssize_t"; break; /* getresuid */ case 360: diff --git a/sys/kern/uipc_mbuf.c b/sys/kern/uipc_mbuf.c index 23bfbff..62dfda6 100644 --- a/sys/kern/uipc_mbuf.c +++ b/sys/kern/uipc_mbuf.c @@ -47,6 +47,49 @@ __FBSDID("$FreeBSD$"); #include <sys/domain.h> #include <sys/protosw.h> #include <sys/uio.h> +#include <sys/sdt.h> + +SDT_PROBE_DEFINE5_XLATE(sdt, , , m__init, + "struct mbuf *", "mbufinfo_t *", + "uint32_t", "uint32_t", + "uint16_t", "uint16_t", + "uint32_t", "uint32_t", + "uint32_t", "uint32_t"); + +SDT_PROBE_DEFINE3_XLATE(sdt, , , m__gethdr, + "uint32_t", "uint32_t", + "uint16_t", "uint16_t", + "struct mbuf *", "mbufinfo_t *"); + +SDT_PROBE_DEFINE3_XLATE(sdt, , , m__get, + "uint32_t", "uint32_t", + "uint16_t", "uint16_t", + "struct mbuf *", "mbufinfo_t *"); + +SDT_PROBE_DEFINE4_XLATE(sdt, , , m__getcl, + "uint32_t", "uint32_t", + "uint16_t", "uint16_t", + "uint32_t", "uint32_t", + "struct mbuf *", "mbufinfo_t *"); + +SDT_PROBE_DEFINE3_XLATE(sdt, , , m__clget, + "struct mbuf *", "mbufinfo_t *", + "uint32_t", "uint32_t", + "uint32_t", "uint32_t"); + +SDT_PROBE_DEFINE4_XLATE(sdt, , , m__cljget, + "struct mbuf *", "mbufinfo_t *", + "uint32_t", "uint32_t", + "uint32_t", "uint32_t", + "void*", "void*"); + +SDT_PROBE_DEFINE(sdt, , , m__cljset); + +SDT_PROBE_DEFINE1_XLATE(sdt, , , m__free, + "struct mbuf *", "mbufinfo_t *"); + +SDT_PROBE_DEFINE1_XLATE(sdt, , , m__freem, + "struct mbuf *", "mbufinfo_t *"); #include <security/mac/mac_framework.h> @@ -1627,7 +1670,7 @@ m_unshare(struct mbuf *m0, int how) * don't know how to break up the non-contiguous memory when * doing DMA. */ - n = m_getcl(how, m->m_type, m->m_flags); + n = m_getcl(how, m->m_type, m->m_flags & M_COPYFLAGS); if (n == NULL) { m_freem(m0); return (NULL); @@ -1657,7 +1700,7 @@ m_unshare(struct mbuf *m0, int how) break; off += cc; - n = m_getcl(how, m->m_type, m->m_flags); + n = m_getcl(how, m->m_type, m->m_flags & M_COPYFLAGS); if (n == NULL) { m_freem(mfirst); m_freem(m0); diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index 35d17b1..d873217 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -358,7 +358,7 @@ sysctl_maxsockets(SYSCTL_HANDLER_ARGS) } SYSCTL_PROC(_kern_ipc, OID_AUTO, maxsockets, CTLTYPE_INT|CTLFLAG_RW, &maxsockets, 0, sysctl_maxsockets, "IU", - "Maximum number of sockets avaliable"); + "Maximum number of sockets available"); /* * Socket operation routines. These routines are called by the routines in diff --git a/sys/kern/vfs_aio.c b/sys/kern/vfs_aio.c index 27fa239..20df141 100644 --- a/sys/kern/vfs_aio.c +++ b/sys/kern/vfs_aio.c @@ -743,7 +743,7 @@ aio_process_rw(struct kaiocb *job) struct file *fp; struct uio auio; struct iovec aiov; - int cnt; + ssize_t cnt; int error; int oublock_st, oublock_end; int inblock_st, inblock_end; @@ -1173,7 +1173,7 @@ aio_qphysio(struct proc *p, struct kaiocb *job) struct cdevsw *csw; struct cdev *dev; struct kaioinfo *ki; - int error, ref, unmap, poff; + int error, ref, poff; vm_prot_t prot; cb = &job->uaiocb; @@ -1206,12 +1206,13 @@ aio_qphysio(struct proc *p, struct kaiocb *job) ki = p->p_aioinfo; poff = (vm_offset_t)cb->aio_buf & PAGE_MASK; - unmap = ((dev->si_flags & SI_UNMAPPED) && unmapped_buf_allowed); - if (unmap) { + if ((dev->si_flags & SI_UNMAPPED) && unmapped_buf_allowed) { if (cb->aio_nbytes > MAXPHYS) { error = -1; goto unref; } + + pbuf = NULL; } else { if (cb->aio_nbytes > MAXPHYS - poff) { error = -1; @@ -1221,17 +1222,14 @@ aio_qphysio(struct proc *p, struct kaiocb *job) error = -1; goto unref; } - } - job->bp = bp = g_alloc_bio(); - if (!unmap) { + job->pbuf = pbuf = (struct buf *)getpbuf(NULL); BUF_KERNPROC(pbuf); - } - - AIO_LOCK(ki); - if (!unmap) + AIO_LOCK(ki); ki->kaio_buffer_count++; - AIO_UNLOCK(ki); + AIO_UNLOCK(ki); + } + job->bp = bp = g_alloc_bio(); bp->bio_length = cb->aio_nbytes; bp->bio_bcount = cb->aio_nbytes; @@ -1245,17 +1243,18 @@ aio_qphysio(struct proc *p, struct kaiocb *job) prot = VM_PROT_READ; if (cb->aio_lio_opcode == LIO_READ) prot |= VM_PROT_WRITE; /* Less backwards than it looks */ - if ((job->npages = vm_fault_quick_hold_pages( - &curproc->p_vmspace->vm_map, + job->npages = vm_fault_quick_hold_pages(&curproc->p_vmspace->vm_map, (vm_offset_t)bp->bio_data, bp->bio_length, prot, job->pages, - sizeof(job->pages)/sizeof(job->pages[0]))) < 0) { + nitems(job->pages)); + if (job->npages < 0) { error = EFAULT; goto doerror; } - if (!unmap) { + if (pbuf != NULL) { pmap_qenter((vm_offset_t)pbuf->b_data, job->pages, job->npages); bp->bio_data = pbuf->b_data + poff; + atomic_add_int(&num_buf_aio, 1); } else { bp->bio_ma = job->pages; bp->bio_ma_n = job->npages; @@ -1264,20 +1263,16 @@ aio_qphysio(struct proc *p, struct kaiocb *job) bp->bio_flags |= BIO_UNMAPPED; } - if (!unmap) - atomic_add_int(&num_buf_aio, 1); - /* Perform transfer. */ csw->d_strategy(bp); dev_relthread(dev, ref); return (0); doerror: - AIO_LOCK(ki); - if (!unmap) + if (pbuf != NULL) { + AIO_LOCK(ki); ki->kaio_buffer_count--; - AIO_UNLOCK(ki); - if (pbuf) { + AIO_UNLOCK(ki); relpbuf(pbuf, NULL); job->pbuf = NULL; } @@ -1446,8 +1441,7 @@ aio_aqueue(struct thread *td, struct aiocb *ujob, struct aioliojob *lj, return (error); } - /* XXX: aio_nbytes is later casted to signed types. */ - if (job->uaiocb.aio_nbytes > INT_MAX) { + if (job->uaiocb.aio_nbytes > IOSIZE_MAX) { uma_zfree(aiocb_zone, job); return (EINVAL); } @@ -1788,7 +1782,7 @@ kern_aio_return(struct thread *td, struct aiocb *ujob, struct aiocb_ops *ops) struct proc *p = td->td_proc; struct kaiocb *job; struct kaioinfo *ki; - int status, error; + long status, error; ki = p->p_aioinfo; if (ki == NULL) @@ -2344,7 +2338,8 @@ kern_aio_waitcomplete(struct thread *td, struct aiocb **ujobp, struct kaioinfo *ki; struct kaiocb *job; struct aiocb *ujob; - int error, status, timo; + long error, status; + int timo; ops->store_aiocb(ujobp, NULL); diff --git a/sys/kern/vfs_export.c b/sys/kern/vfs_export.c index 9be0ece..9eb523c 100644 --- a/sys/kern/vfs_export.c +++ b/sys/kern/vfs_export.c @@ -256,10 +256,10 @@ vfs_free_addrlist_af(struct radix_node_head **prnh) rnh = *prnh; RADIX_NODE_HEAD_LOCK(rnh); - (*rnh->rnh_walktree)(&rnh->rh, vfs_free_netcred, &rnh->rh); + (*rnh->rnh_walktree)(&rnh->rh, vfs_free_netcred, rnh); RADIX_NODE_HEAD_UNLOCK(rnh); RADIX_NODE_HEAD_DESTROY(rnh); - free(rnh, M_RTABLE); + rn_detachhead((void **)prnh); prnh = NULL; } diff --git a/sys/kern/vfs_mountroot.c b/sys/kern/vfs_mountroot.c index cf24253..a33e376 100644 --- a/sys/kern/vfs_mountroot.c +++ b/sys/kern/vfs_mountroot.c @@ -89,6 +89,7 @@ __FBSDID("$FreeBSD$"); static int parse_mount(char **); static struct mntarg *parse_mountroot_options(struct mntarg *, const char *); static int sysctl_vfs_root_mount_hold(SYSCTL_HANDLER_ARGS); +static void vfs_mountroot_wait(void); static int vfs_mountroot_wait_if_neccessary(const char *fs, const char *dev); /* @@ -488,6 +489,8 @@ parse_dir_ask(char **conf) char *mnt; int error; + vfs_mountroot_wait(); + printf("\nLoader variables:\n"); parse_dir_ask_printenv("vfs.root.mountfrom"); parse_dir_ask_printenv("vfs.root.mountfrom.options"); diff --git a/sys/mips/atheros/apb.c b/sys/mips/atheros/apb.c index 8d230a7..abae9c2 100644 --- a/sys/mips/atheros/apb.c +++ b/sys/mips/atheros/apb.c @@ -178,7 +178,7 @@ apb_alloc_resource(device_t bus, device_t child, int type, int *rid, passthrough = (device_get_parent(child) != bus); rle = NULL; - dprintf("%s: entry (%p, %p, %d, %d, %p, %p, %ld, %d)\n", + dprintf("%s: entry (%p, %p, %d, %d, %p, %p, %jd, %d)\n", __func__, bus, child, type, *rid, (void *)(intptr_t)start, (void *)(intptr_t)end, count, flags); @@ -491,8 +491,8 @@ apb_print_all_resources(device_t dev) if (STAILQ_FIRST(rl)) retval += printf(" at"); - retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); - retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); return (retval); } diff --git a/sys/mips/beri/beri_simplebus.c b/sys/mips/beri/beri_simplebus.c index dd219a5..79302f6 100644 --- a/sys/mips/beri/beri_simplebus.c +++ b/sys/mips/beri/beri_simplebus.c @@ -239,8 +239,8 @@ simplebus_print_child(device_t dev, device_t child) rv = 0; rv += bus_print_child_header(dev, child); - rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); - rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); if ((ip = simplebus_get_interrupt_parent(child)) != NULL) rv += printf(" (%s)", device_get_nameunit(ip)); rv += bus_print_child_footer(dev, child); diff --git a/sys/mips/mips/mips_pic.c b/sys/mips/mips/mips_pic.c index 250f8cf..4e97c41 100644 --- a/sys/mips/mips/mips_pic.c +++ b/sys/mips/mips/mips_pic.c @@ -48,6 +48,8 @@ __FBSDID("$FreeBSD$"); #include <sys/mutex.h> #include <sys/smp.h> #include <sys/sched.h> +#include <sys/pmc.h> +#include <sys/pmckern.h> #include <machine/bus.h> #include <machine/hwfunc.h> @@ -217,8 +219,11 @@ mips_pic_intr(void *arg) KASSERT(i == 0, ("all interrupts handled")); #ifdef HWPMC_HOOKS - if (pmc_hook && (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN)) + if (pmc_hook && (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN)) { + struct trapframe *tf = PCPU_GET(curthread)->td_intr_frame; + pmc_hook(PCPU_GET(curthread), PMC_FN_USER_CALLCHAIN, tf); + } #endif return (FILTER_HANDLED); } diff --git a/sys/mips/mips/nexus.c b/sys/mips/mips/nexus.c index 617d842..89049f0 100644 --- a/sys/mips/mips/nexus.c +++ b/sys/mips/mips/nexus.c @@ -231,8 +231,8 @@ nexus_print_all_resources(device_t dev) if (STAILQ_FIRST(rl)) retval += printf(" at"); - retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); - retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); return (retval); } @@ -275,7 +275,7 @@ nexus_alloc_resource(device_t bus, device_t child, int type, int *rid, struct rman *rm; int isdefault, needactivate, passthrough; - dprintf("%s: entry (%p, %p, %d, %p, %p, %p, %ld, %d)\n", + dprintf("%s: entry (%p, %p, %d, %p, %p, %p, %jd, %d)\n", __func__, bus, child, type, rid, (void *)(intptr_t)start, (void *)(intptr_t)end, count, flags); dprintf("%s: requested rid is %d\n", __func__, *rid); @@ -350,7 +350,7 @@ nexus_set_resource(device_t dev, device_t child, int type, int rid, struct resource_list *rl = &ndev->nx_resources; struct resource_list_entry *rle; - dprintf("%s: entry (%p, %p, %d, %d, %p, %ld)\n", + dprintf("%s: entry (%p, %p, %d, %d, %p, %jd)\n", __func__, dev, child, type, rid, (void *)(intptr_t)start, count); rle = resource_list_add(rl, type, rid, start, start + count - 1, diff --git a/sys/mips/nlm/xlp_pci.c b/sys/mips/nlm/xlp_pci.c index fee23cd..863c6aa 100644 --- a/sys/mips/nlm/xlp_pci.c +++ b/sys/mips/nlm/xlp_pci.c @@ -433,7 +433,7 @@ mips_platform_pcib_setup_intr(device_t dev, device_t child, if (error) return error; if (rman_get_start(irq) != rman_get_end(irq)) { - device_printf(dev, "Interrupt allocation %lu != %lu\n", + device_printf(dev, "Interrupt allocation %ju != %ju\n", rman_get_start(irq), rman_get_end(irq)); return (EINVAL); } diff --git a/sys/mips/nlm/xlp_simplebus.c b/sys/mips/nlm/xlp_simplebus.c index 3ffb19a..413775b 100644 --- a/sys/mips/nlm/xlp_simplebus.c +++ b/sys/mips/nlm/xlp_simplebus.c @@ -209,7 +209,7 @@ xlp_simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid, if (j == sc->nranges && sc->nranges != 0) { if (bootverbose) device_printf(bus, "Could not map resource " - "%#lx-%#lx\n", start, end); + "%#jx-%#jx\n", start, end); return (NULL); } } @@ -235,7 +235,7 @@ xlp_simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid, } else { if (bootverbose) device_printf(bus, "Invalid MEM range" - "%#lx-%#lx\n", start, end); + "%#jx-%#jx\n", start, end); return (NULL); } break; diff --git a/sys/mips/rmi/iodi.c b/sys/mips/rmi/iodi.c index da0405d..d1f74e1 100644 --- a/sys/mips/rmi/iodi.c +++ b/sys/mips/rmi/iodi.c @@ -134,17 +134,17 @@ iodi_alloc_resource(device_t bus, device_t child, int type, int *rid, #ifdef DEBUG switch (type) { case SYS_RES_IRQ: - device_printf(bus, "IRQ resource - for %s %lx-%lx\n", + device_printf(bus, "IRQ resource - for %s %jx-%jx\n", device_get_nameunit(child), start, end); break; case SYS_RES_IOPORT: - device_printf(bus, "IOPORT resource - for %s %lx-%lx\n", + device_printf(bus, "IOPORT resource - for %s %jx-%jx\n", device_get_nameunit(child), start, end); break; case SYS_RES_MEMORY: - device_printf(bus, "MEMORY resource - for %s %lx-%lx\n", + device_printf(bus, "MEMORY resource - for %s %jx-%jx\n", device_get_nameunit(child), start, end); break; } diff --git a/sys/mips/rmi/xlr_pci.c b/sys/mips/rmi/xlr_pci.c index 194213b..53581a6 100644 --- a/sys/mips/rmi/xlr_pci.c +++ b/sys/mips/rmi/xlr_pci.c @@ -464,7 +464,7 @@ mips_platform_pci_setup_intr(device_t dev, device_t child, if (error) return error; if (rman_get_start(irq) != rman_get_end(irq)) { - device_printf(dev, "Interrupt allocation %lu != %lu\n", + device_printf(dev, "Interrupt allocation %ju != %ju\n", rman_get_start(irq), rman_get_end(irq)); return (EINVAL); } diff --git a/sys/mips/rt305x/obio.c b/sys/mips/rt305x/obio.c index ff7ba03..ac27230 100644 --- a/sys/mips/rt305x/obio.c +++ b/sys/mips/rt305x/obio.c @@ -587,8 +587,8 @@ obio_print_all_resources(device_t dev) if (STAILQ_FIRST(rl)) retval += printf(" at"); - retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); - retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); return (retval); } diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 8d9e499..e2c2205 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -250,6 +250,7 @@ SUBDIR= \ ${_nandfs} \ ${_nandsim} \ ${_ncr} \ + ${_nctgpio} \ ${_ncv} \ ${_ndis} \ netfpga10g \ @@ -367,6 +368,8 @@ SUBDIR= \ udf_iconv \ ufs \ unionfs \ + urtwn \ + ${_urtwnfw} \ usb \ utopia \ ${_vesa} \ @@ -484,6 +487,7 @@ _ispfw= ispfw _mwlfw= mwlfw _ralfw= ralfw _rtwnfw= rtwnfw +_urtwnfw= urtwnfw _sf= sf _ti= ti _txp= txp @@ -555,6 +559,7 @@ _ixv= ixv _linprocfs= linprocfs _linsysfs= linsysfs _linux= linux +_nctgpio= nctgpio _ndis= ndis _pccard= pccard .if ${MK_OFED} != "no" || defined(ALL_MODULES) diff --git a/sys/modules/nctgpio/Makefile b/sys/modules/nctgpio/Makefile new file mode 100644 index 0000000..88e133d --- /dev/null +++ b/sys/modules/nctgpio/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/nctgpio +KMOD= nctgpio +SRCS= nctgpio.c +SRCS+= device_if.h bus_if.h isa_if.h gpio_if.h opt_platform.h + +.include <bsd.kmod.mk> diff --git a/sys/modules/pflog/Makefile b/sys/modules/pflog/Makefile index 1889681..36656dc 100644 --- a/sys/modules/pflog/Makefile +++ b/sys/modules/pflog/Makefile @@ -7,14 +7,11 @@ SRCS= if_pflog.c \ opt_pf.h opt_inet.h opt_inet6.h opt_bpf.h SRCS+= bus_if.h device_if.h -.if defined(KERNBUILDDIR) -MKDEP+= -include ${KERNBUILDDIR}/opt_global.h -.else +.if !defined(KERNBUILDDIR) .if defined(VIMAGE) opt_global.h: echo "#define VIMAGE 1" >> ${.TARGET} CFLAGS+= -include opt_global.h -MKDEP+= -include opt_global.h .endif .endif diff --git a/sys/modules/pfsync/Makefile b/sys/modules/pfsync/Makefile index d38bde7..89af6f9 100644 --- a/sys/modules/pfsync/Makefile +++ b/sys/modules/pfsync/Makefile @@ -7,14 +7,11 @@ SRCS= if_pfsync.c \ opt_pf.h opt_inet.h opt_inet6.h SRCS+= bus_if.h device_if.h -.if defined(KERNBUILDDIR) -MKDEP+= -include ${KERNBUILDDIR}/opt_global.h -.else +.if !defined(KERNBUILDDIR) .if defined(VIMAGE) opt_global.h: echo "#define VIMAGE 1" >> ${.TARGET} CFLAGS+= -include opt_global.h -MKDEP+= -include opt_global.h .endif .endif diff --git a/sys/modules/usb/urtwn/Makefile b/sys/modules/urtwn/Makefile index 59fa910..a4fdc25 100644 --- a/sys/modules/usb/urtwn/Makefile +++ b/sys/modules/urtwn/Makefile @@ -1,8 +1,9 @@ # $FreeBSD$ -.PATH: ${.CURDIR}/../../../dev/usb/wlan +.PATH: ${.CURDIR}/../../dev/urtwn -.include <src.opts.mk> +SYSDIR?=${.CURDIR}/../.. +.include "${SYSDIR}/conf/kern.opts.mk" KMOD = if_urtwn SRCS = if_urtwn.c if_urtwnreg.h if_urtwnvar.h \ diff --git a/sys/modules/usb/urtwnfw/Makefile b/sys/modules/urtwnfw/Makefile index 611b411..611b411 100644 --- a/sys/modules/usb/urtwnfw/Makefile +++ b/sys/modules/urtwnfw/Makefile diff --git a/sys/modules/usb/urtwnfw/Makefile.inc b/sys/modules/urtwnfw/Makefile.inc index 17c78be..2a97a09 100644 --- a/sys/modules/usb/urtwnfw/Makefile.inc +++ b/sys/modules/urtwnfw/Makefile.inc @@ -11,5 +11,5 @@ FIRMWS= ${_FIRM}:${KMOD}:111 # FIRMWARE_LICENSE= realtek -${_FIRM}: ${.CURDIR}/../../../../contrib/dev/urtwn/${_FIRM}.uu +${_FIRM}: ${.CURDIR}/../../../contrib/dev/urtwn/${_FIRM}.uu uudecode -p $? > ${.TARGET} diff --git a/sys/modules/usb/urtwnfw/urtwnrtl8188eu/Makefile b/sys/modules/urtwnfw/urtwnrtl8188eu/Makefile index d3c974e..d3c974e 100644 --- a/sys/modules/usb/urtwnfw/urtwnrtl8188eu/Makefile +++ b/sys/modules/urtwnfw/urtwnrtl8188eu/Makefile diff --git a/sys/modules/usb/urtwnfw/urtwnrtl8192cT/Makefile b/sys/modules/urtwnfw/urtwnrtl8192cT/Makefile index ef49984..ef49984 100644 --- a/sys/modules/usb/urtwnfw/urtwnrtl8192cT/Makefile +++ b/sys/modules/urtwnfw/urtwnrtl8192cT/Makefile diff --git a/sys/modules/usb/urtwnfw/urtwnrtl8192cU/Makefile b/sys/modules/urtwnfw/urtwnrtl8192cU/Makefile index e9a932c..e9a932c 100644 --- a/sys/modules/usb/urtwnfw/urtwnrtl8192cU/Makefile +++ b/sys/modules/urtwnfw/urtwnrtl8192cU/Makefile diff --git a/sys/modules/usb/Makefile b/sys/modules/usb/Makefile index b236d04..72b123e 100644 --- a/sys/modules/usb/Makefile +++ b/sys/modules/usb/Makefile @@ -47,7 +47,6 @@ SUBDIR = usb SUBDIR += ${_dwc_otg} ehci ${_musb} ohci uhci xhci ${_uss820dci} ${_at91dci} \ ${_atmegadci} ${_avr32dci} ${_rsu} ${_rsufw} ${_saf1761otg} SUBDIR += ${_rum} ${_run} ${_runfw} ${_uath} upgt usie ural ${_zyd} ${_urtw} -SUBDIR += urtwn ${_urtwnfw} SUBDIR += atp uhid ukbd ums udbp ufm uep wsp ugold uled SUBDIR += ucom u3g uark ubsa ubser uchcom ucycom ufoma uftdi ugensa uipaq ulpt \ umct umcs umodem umoscom uplcom uslcom uvisor uvscom @@ -70,7 +69,6 @@ _rum= rum _uath= uath _zyd= zyd _kue= kue -_urtwnfw= urtwnfw _run= run _runfw= runfw _rsu= rsu diff --git a/sys/modules/wtap/Makefile b/sys/modules/wtap/Makefile index 68b905f..58f60fb 100644 --- a/sys/modules/wtap/Makefile +++ b/sys/modules/wtap/Makefile @@ -16,11 +16,8 @@ SRCS += visibility.c SRCS += opt_global.h -.if defined(KERNBUILDDIR) -MKDEP= -include ${KERNBUILDDIR}/opt_global.h -.else +.if !defined(KERNBUILDDIR) CFLAGS+= -include opt_global.h -MKDEP= -include opt_global.h opt_global.h: echo "#define VIMAGE 1" > ${.TARGET} diff --git a/sys/net/ethernet.h b/sys/net/ethernet.h index 438028d..52d8f61 100644 --- a/sys/net/ethernet.h +++ b/sys/net/ethernet.h @@ -333,6 +333,7 @@ struct ether_vlan_header { #define ETHERTYPE_SLOW 0x8809 /* 802.3ad link aggregation (LACP) */ #define ETHERTYPE_PPP 0x880B /* PPP (obsolete by PPPoE) */ #define ETHERTYPE_HITACHI 0x8820 /* Hitachi Cable (Optoelectronic Systems Laboratory) */ +#define ETHERTYPE_TEST 0x8822 /* Network Conformance Testing */ #define ETHERTYPE_MPLS 0x8847 /* MPLS Unicast */ #define ETHERTYPE_MPLS_MCAST 0x8848 /* MPLS Multicast */ #define ETHERTYPE_AXIS 0x8856 /* Axis Communications AB proprietary bootstrap/config */ diff --git a/sys/net/if_debug.c b/sys/net/if_debug.c index 1d198eb..fbcc2b0 100644 --- a/sys/net/if_debug.c +++ b/sys/net/if_debug.c @@ -67,6 +67,8 @@ if_show_ifnet(struct ifnet *ifp) IF_DB_PRINTF("%p", if_l2com); IF_DB_PRINTF("%p", if_vnet); IF_DB_PRINTF("%p", if_home_vnet); + IF_DB_PRINTF("%p", if_vlantrunk); + IF_DB_PRINTF("%p", if_bpf); IF_DB_PRINTF("%p", if_addr); IF_DB_PRINTF("%p", if_llsoftc); IF_DB_PRINTF("%p", if_label); diff --git a/sys/net/route.c b/sys/net/route.c index 45f479c..4b191d0 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -201,6 +201,16 @@ rt_tables_get_rnh(int table, int fam) return (*rt_tables_get_rnh_ptr(table, fam)); } +u_int +rt_tables_get_gen(int table, int fam) +{ + struct rib_head *rnh; + + rnh = *rt_tables_get_rnh_ptr(table, fam); + return (rnh->rnh_gen); +} + + /* * route initialization must occur before ip6_init2(), which happenas at * SI_ORDER_MIDDLE. @@ -1754,6 +1764,7 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, *ret_nrt = rt; RT_ADDREF(rt); } + rnh->rnh_gen++; /* Routing table updated */ RT_UNLOCK(rt); break; case RTM_CHANGE: diff --git a/sys/net/route.h b/sys/net/route.h index 6cc1e1c..e0ff6b4 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -98,6 +98,14 @@ struct rt_metrics { /* lle state is exported in rmx_state rt_metrics field */ #define rmx_state rmx_weight +/* + * Keep a generation count of routing table, incremented on route addition, + * so we can invalidate caches. This is accessed without a lock, as precision + * is not required. + */ +typedef volatile u_int rt_gen_t; /* tree generation (for adds) */ +#define RT_GEN(fibnum, af) rt_tables_get_gen(fibnum, af) + #define RT_DEFAULT_FIB 0 /* Explicitly mark fib=0 restricted cases */ #define RT_ALL_FIBS -1 /* Announce event for every fib */ #ifdef _KERNEL @@ -398,6 +406,20 @@ struct rt_addrinfo { } \ } while (0) +/* + * Validate a cached route based on a supplied cookie. If there is an + * out-of-date cache, simply free it. Update the generation number + * for the new allocation + */ +#define RT_VALIDATE(ro, cookiep, fibnum) do { \ + rt_gen_t cookie = RT_GEN(fibnum, (ro)->ro_dst.sa_family); \ + if (*(cookiep) != cookie && (ro)->ro_rt != NULL) { \ + RTFREE((ro)->ro_rt); \ + (ro)->ro_rt = NULL; \ + *(cookiep) = cookie; \ + } \ +} while (0) + struct ifmultiaddr; struct rib_head; @@ -415,6 +437,7 @@ int rt_setgate(struct rtentry *, struct sockaddr *, struct sockaddr *); void rt_maskedcopy(struct sockaddr *, struct sockaddr *, struct sockaddr *); struct rib_head *rt_table_init(int); void rt_table_destroy(struct rib_head *); +u_int rt_tables_get_gen(int table, int fam); int rtsock_addrmsg(int, struct ifaddr *, int); int rtsock_routemsg(int, struct ifnet *ifp, int, struct rtentry *, int); diff --git a/sys/net/route_var.h b/sys/net/route_var.h index 86fc8a9..a8ef56a 100644 --- a/sys/net/route_var.h +++ b/sys/net/route_var.h @@ -41,7 +41,7 @@ struct rib_head { rn_walktree_t *rnh_walktree; /* traverse tree */ rn_walktree_from_t *rnh_walktree_from; /* traverse tree below a */ rn_close_t *rnh_close; /*do something when the last ref drops*/ - u_int rnh_gen; /* generation counter */ + rt_gen_t rnh_gen; /* generation counter */ int rnh_multipath; /* multipath capable ? */ struct radix_node rnh_nodes[3]; /* empty tree for common case */ struct rwlock rib_lock; /* config/data path lock */ diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index d59ef70..810da1b 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -275,8 +275,6 @@ rts_attach(struct socket *so, int proto, struct thread *td) /* XXX */ rp = malloc(sizeof *rp, M_PCB, M_WAITOK | M_ZERO); - if (rp == NULL) - return ENOBUFS; so->so_pcb = (caddr_t)rp; so->so_fibnum = td->td_proc->p_fibnum; diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h index 0a25392..f5fdd5a 100644 --- a/sys/net80211/ieee80211.h +++ b/sys/net80211/ieee80211.h @@ -317,6 +317,27 @@ struct ieee80211_wme_param { } __packed; /* + * WME U-APSD qos info field defines + */ +#define WME_CAPINFO_UAPSD_EN 0x00000080 +#define WME_CAPINFO_UAPSD_VO 0x00000001 +#define WME_CAPINFO_UAPSD_VI 0x00000002 +#define WME_CAPINFO_UAPSD_BK 0x00000004 +#define WME_CAPINFO_UAPSD_BE 0x00000008 +#define WME_CAPINFO_UAPSD_ACFLAGS_SHIFT 0 +#define WME_CAPINFO_UAPSD_ACFLAGS_MASK 0xF +#define WME_CAPINFO_UAPSD_MAXSP_SHIFT 5 +#define WME_CAPINFO_UAPSD_MAXSP_MASK 0x3 +#define WME_CAPINFO_IE_OFFSET 8 +#define WME_UAPSD_MAXSP(_qosinfo) \ + (((_qosinfo) >> WME_CAPINFO_UAPSD_MAXSP_SHIFT) & \ + WME_CAPINFO_UAPSD_MAXSP_MASK) +#define WME_UAPSD_AC_ENABLED(_ac, _qosinfo) \ + ((1 << (3 - (_ac))) & ( \ + ((_qosinfo) >> WME_CAPINFO_UAPSD_ACFLAGS_SHIFT) & \ + WME_CAPINFO_UAPSD_ACFLAGS_MASK)) + +/* * Management Notification Frame */ struct ieee80211_mnf { @@ -346,6 +367,7 @@ struct ieee80211_action { #define IEEE80211_ACTION_CAT_MESH 13 /* Mesh */ #define IEEE80211_ACTION_CAT_SELF_PROT 15 /* Self-protected */ /* 16 - 125 reserved */ +#define IEEE80211_ACTION_VHT 21 #define IEEE80211_ACTION_CAT_VENDOR 127 /* Vendor Specific */ #define IEEE80211_ACTION_HT_TXCHWIDTH 0 /* recommended xmit chan width*/ @@ -695,6 +717,151 @@ struct ieee80211_ie_htinfo { #define IEEE80211_HTINFO_BASIC_STBCMCS_S 0 #define IEEE80211_HTINFO_DUALPROTECTED 0x80 + +/* + * 802.11ac definitions - 802.11ac-2013 . + */ + +/* VHT opmode bits */ +#define IEEE80211_VHT_OPMODE_CHANWIDTH_MASK 3 +#define IEEE80211_VHT_OPMODE_CHANWIDTH_20MHZ 0 +#define IEEE80211_VHT_OPMODE_CHANWIDTH_40MHZ 1 +#define IEEE80211_VHT_OPMODE_CHANWIDTH_80MHZ 2 +#define IEEE80211_VHT_OPMODE_CHANWIDTH_160MHZ 3 +#define IEEE80211_VHT_OPMODE_RX_NSS_MASK 0x70 +#define IEEE80211_VHT_OPMODE_RX_NSS_SHIFT 4 +#define IEEE80211_VHT_OPMODE_RX_NSS_TYPE_BF 0x80 + +/* + * Maximum length of A-MPDU that the STA can RX in VHT. + * Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets) + */ +#define IEEE80211_VHTCAP_MAX_AMPDU_8K 0 +#define IEEE80211_VHTCAP_MAX_AMPDU_16K 1 +#define IEEE80211_VHTCAP_MAX_AMPDU_32K 2 +#define IEEE80211_VHTCAP_MAX_AMPDU_64K 3 +#define IEEE80211_VHTCAP_MAX_AMPDU_128K 4 +#define IEEE80211_VHTCAP_MAX_AMPDU_256K 5 +#define IEEE80211_VHTCAP_MAX_AMPDU_512K 6 +#define IEEE80211_VHTCAP_MAX_AMPDU_1024K 7 + +/* + * VHT MCS information. + * + rx_highest/tx_highest: optional; maximum long GI VHT PPDU + * data rate. 1Mbit/sec units. + * + rx_mcs_map/tx_mcs_map: bitmap of per-stream supported MCS; + * 2 bits each. + */ +#define IEEE80211_VHT_MCS_SUPPORT_0_7 0 /* MCS0-7 */ +#define IEEE80211_VHT_MCS_SUPPORT_0_8 1 /* MCS0-8 */ +#define IEEE80211_VHT_MCS_SUPPORT_0_9 2 /* MCS0-9 */ +#define IEEE80211_VHT_MCS_NOT_SUPPORTED 3 /* not supported */ + +struct ieee80211_vht_mcs_info { + uint16_t rx_mcs_map; + uint16_t rx_highest; + uint16_t tx_mcs_map; + uint16_t tx_highest; +} __packed; + +/* VHT capabilities element: 802.11ac-2013 8.4.2.160 */ +struct ieee80211_ie_vhtcap { + uint8_t ie; + uint8_t len; + uint32_t vht_cap_info; + struct ieee80211_vht_mcs_info supp_mcs; +} __packed; + +#define IEEE80211_VHT_CHANWIDTH_USE_HT 0 /* Use HT IE for chw */ +#define IEEE80211_VHT_CHANWIDTH_80MHZ 1 /* 80MHz */ +#define IEEE80211_VHT_CHANWIDTH_160MHZ 2 /* 160MHz */ +#define IEEE80211_VHT_CHANWIDTH_80P80MHZ 3 /* 80+80MHz */ + +/* VHT operation IE - 802.11ac-2013 8.4.2.161 */ +struct ieee80211_ie_vht_operation { + uint8_t ie; + uint8_t len; + uint8_t chan_width; + uint8_t center_freq_seg1_idx; + uint8_t center_freq_seg2_idx; + uint16_t basic_mcs_set; +} __packed; + +/* 802.11ac VHT Capabilities */ +#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_3895 0x00000000 +#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_7991 0x00000001 +#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_11454 0x00000002 +#define IEEE80211_VHTCAP_MAX_MPDU_MASK 0x00000003 +#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160MHZ 0x00000004 +#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ 0x00000008 +#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK 0x0000000C +#define IEEE80211_VHTCAP_RXLDPC 0x00000010 +#define IEEE80211_VHTCAP_SHORT_GI_80 0x00000020 +#define IEEE80211_VHTCAP_SHORT_GI_160 0x00000040 +#define IEEE80211_VHTCAP_TXSTBC 0x00000080 +#define IEEE80211_VHTCAP_RXSTBC_1 0x00000100 +#define IEEE80211_VHTCAP_RXSTBC_2 0x00000200 +#define IEEE80211_VHTCAP_RXSTBC_3 0x00000300 +#define IEEE80211_VHTCAP_RXSTBC_4 0x00000400 +#define IEEE80211_VHTCAP_RXSTBC_MASK 0x00000700 +#define IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE 0x00000800 +#define IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE 0x00001000 +#define IEEE80211_VHTCAP_BEAMFORMEE_STS_SHIFT 13 +#define IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK \ + (7 << IEEE80211_VHTCAP_BEAMFORMEE_STS_SHIFT) +#define IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_SHIFT 16 +#define IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_MASK \ + (7 << IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_SHIFT) +#define IEEE80211_VHTCAP_MU_BEAMFORMER_CAPABLE 0x00080000 +#define IEEE80211_VHTCAP_MU_BEAMFORMEE_CAPABLE 0x00100000 +#define IEEE80211_VHTCAP_VHT_TXOP_PS 0x00200000 +#define IEEE80211_VHTCAP_HTC_VHT 0x00400000 +#define IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT 23 +#define IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK \ + (7 << IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT) +#define IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB 0x08000000 +#define IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB 0x0c000000 +#define IEEE80211_VHTCAP_RX_ANTENNA_PATTERN 0x10000000 +#define IEEE80211_VHTCAP_TX_ANTENNA_PATTERN 0x20000000 + +/* + * VHT Transmit Power Envelope element - 802.11ac-2013 8.4.2.164 + * + * This defines the maximum transmit power for various bandwidths. + */ +/* + * Count is how many elements follow and what they're for: + * + * 0 - 20 MHz + * 1 - 20+40 MHz + * 2 - 20+40+80 MHz + * 3 - 20+40+80+(160, 80+80) MHz + */ +#define IEEE80211_VHT_TXPWRENV_INFO_COUNT_SHIFT 0 +#define IEEE80211_VHT_TXPWRENV_INFO_COUNT_MASK 0x07 + +/* + * Unit is the tx power representation. It should be EIRP for now; + * other values are reserved. + */ +#define IEEE80211_VHT_TXPWRENV_UNIT_MASK 0x38 +#define IEEE80211_VHT_TXPWRENV_UNIT_SHIFT 3 + +/* This value is within the unit mask/shift above */ +#define IEEE80211_VHT_TXPWRENV_UNIT_EIRP 0 + +struct ieee80211_ie_vht_txpwrenv { + uint8_t ie; + uint8_t len; + uint8_t tx_info; + int8_t tx_elem[0]; /* TX power elements, 1/2 dB, signed */ +}; + +/* VHT action codes */ +#define WLAN_ACTION_VHT_COMPRESSED_BF 0 +#define WLAN_ACTION_VHT_GROUPID_MGMT 1 +#define WLAN_ACTION_VHT_OPMODE_NOTIF 2 + /* * Management information element payloads. */ @@ -709,6 +876,8 @@ enum { IEEE80211_ELEMID_IBSSPARMS = 6, IEEE80211_ELEMID_COUNTRY = 7, IEEE80211_ELEMID_BSSLOAD = 11, + IEEE80211_ELEMID_TSPEC = 13, + IEEE80211_ELEMID_TCLAS = 14, IEEE80211_ELEMID_CHALLENGE = 16, /* 17-31 reserved for challenge text extension */ IEEE80211_ELEMID_PWRCNSTR = 32, @@ -728,6 +897,17 @@ enum { IEEE80211_ELEMID_XRATES = 50, IEEE80211_ELEMID_APCHANREP = 51, IEEE80211_ELEMID_HTINFO = 61, + IEEE80211_ELEMID_SECCHAN_OFFSET = 62, + IEEE80211_ELEMID_RRM_ENACAPS = 70, + IEEE80211_ELEMID_MULTIBSSID = 71, + IEEE80211_ELEMID_COEX_2040 = 72, + IEEE80211_ELEMID_INTOL_CHN_REPORT = 73, + IEEE80211_ELEMID_OVERLAP_BSS_SCAN_PARAM = 74, + IEEE80211_ELEMID_TSF_REQ = 91, + IEEE80211_ELEMID_TSF_RESP = 92, + IEEE80211_ELEMID_WNM_SLEEP_MODE = 93, + IEEE80211_ELEMID_TIM_BCAST_REQ = 94, + IEEE80211_ELEMID_TIM_BCAST_RESP = 95, IEEE80211_ELEMID_TPC = 150, IEEE80211_ELEMID_CCKM = 156, IEEE80211_ELEMID_VENDOR = 221, /* vendor private */ @@ -751,7 +931,7 @@ enum { IEEE80211_ELEMID_MESHGANN = 125, IEEE80211_ELEMID_MESHRANN = 126, /* 127 Extended Capabilities */ - IEEE80211_ELEMID_MESHEXTCAP = 127, + IEEE80211_ELEMID_EXTCAP = 127, /* 128-129 reserved */ IEEE80211_ELEMID_MESHPREQ = 130, IEEE80211_ELEMID_MESHPREP = 131, @@ -760,6 +940,11 @@ enum { IEEE80211_ELEMID_MESHPXU = 137, IEEE80211_ELEMID_MESHPXUC = 138, IEEE80211_ELEMID_MESHAH = 60, /* XXX: remove */ + + /* 802.11ac */ + IEEE80211_ELEMID_VHT_CAP = 191, + IEEE80211_ELEMID_VHT_OPMODE = 192, + IEEE80211_ELEMID_VHT_PWR_ENV = 195, }; struct ieee80211_tim_ie { diff --git a/sys/net80211/ieee80211_amrr.c b/sys/net80211/ieee80211_amrr.c index f50334e..7460223 100644 --- a/sys/net80211/ieee80211_amrr.c +++ b/sys/net80211/ieee80211_amrr.c @@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/module.h> +#include <sys/sbuf.h> #include <sys/socket.h> #include <sys/sysctl.h> @@ -75,6 +76,7 @@ static void amrr_tx_update(const struct ieee80211vap *vap, const struct ieee80211_node *, void *, void *, void *); static void amrr_sysctlattach(struct ieee80211vap *, struct sysctl_ctx_list *, struct sysctl_oid *); +static void amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s); /* number of references from net80211 layer */ static int nrefs = 0; @@ -91,6 +93,7 @@ static const struct ieee80211_ratectl amrr = { .ir_tx_complete = amrr_tx_complete, .ir_tx_update = amrr_tx_update, .ir_setinterval = amrr_setinterval, + .ir_node_stats = amrr_node_stats, }; IEEE80211_RATECTL_MODULE(amrr, 1); IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr); @@ -410,3 +413,31 @@ amrr_sysctlattach(struct ieee80211vap *vap, "amrr_min_sucess_threshold", CTLFLAG_RW, &amrr->amrr_min_success_threshold, 0, ""); } + +static void +amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s) +{ + int rate; + struct ieee80211_amrr_node *amn = ni->ni_rctls; + struct ieee80211_rateset *rs; + + /* XXX TODO: check locking? */ + + /* XXX TODO: this should be a method */ + if (amrr_node_is_11n(ni)) { + rs = (struct ieee80211_rateset *) &ni->ni_htrates; + rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; + sbuf_printf(s, "rate: MCS %d\n", rate); + } else { + rs = &ni->ni_rates; + rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; + sbuf_printf(s, "rate: %d Mbit\n", rate / 2); + } + + sbuf_printf(s, "ticks: %d\n", amn->amn_ticks); + sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt); + sbuf_printf(s, "success: %u\n", amn->amn_success); + sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold); + sbuf_printf(s, "recovery: %u\n", amn->amn_recovery); + sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt); +} diff --git a/sys/net80211/ieee80211_dfs.c b/sys/net80211/ieee80211_dfs.c index 232ceb7..4478d9e 100644 --- a/sys/net80211/ieee80211_dfs.c +++ b/sys/net80211/ieee80211_dfs.c @@ -245,7 +245,7 @@ dfs_timeout(void *arg) for (i = 0; i < ic->ic_nchans; i++) { c = &ic->ic_channels[i]; if (IEEE80211_IS_CHAN_RADAR(c)) { - if (time_after_eq(now, dfs->nol_event[i]+NOL_TIMEOUT)) { + if (ieee80211_time_after_eq(now, dfs->nol_event[i]+NOL_TIMEOUT)) { c->ic_state &= ~IEEE80211_CHANSTATE_RADAR; if (c->ic_state & IEEE80211_CHANSTATE_NORADAR) { /* diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h index 0566da7..62f3335 100644 --- a/sys/net80211/ieee80211_freebsd.h +++ b/sys/net80211/ieee80211_freebsd.h @@ -251,10 +251,10 @@ void ieee80211_vap_destroy(struct ieee80211vap *); #define ticks_to_msecs(t) (1000*(t) / hz) #define ticks_to_secs(t) ((t) / hz) -#define time_after(a,b) ((long)(b) - (long)(a) < 0) -#define time_before(a,b) time_after(b,a) -#define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0) -#define time_before_eq(a,b) time_after_eq(b,a) +#define ieee80211_time_after(a,b) ((long)(b) - (long)(a) < 0) +#define ieee80211_time_before(a,b) ieee80211_time_after(b,a) +#define ieee80211_time_after_eq(a,b) ((long)(a) - (long)(b) >= 0) +#define ieee80211_time_before_eq(a,b) ieee80211_time_after_eq(b,a) struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen); diff --git a/sys/net80211/ieee80211_ht.c b/sys/net80211/ieee80211_ht.c index 1a21734..be380e9 100644 --- a/sys/net80211/ieee80211_ht.c +++ b/sys/net80211/ieee80211_ht.c @@ -1406,7 +1406,7 @@ ieee80211_ht_timeout(struct ieee80211com *ic) IEEE80211_LOCK_ASSERT(ic); if ((ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR) && - time_after(ticks, ic->ic_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) { + ieee80211_time_after(ticks, ic->ic_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) { #if 0 IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, "%s", "time out non-HT STA present on channel"); diff --git a/sys/net80211/ieee80211_hwmp.c b/sys/net80211/ieee80211_hwmp.c index 6bbc32c..0c920d8 100644 --- a/sys/net80211/ieee80211_hwmp.c +++ b/sys/net80211/ieee80211_hwmp.c @@ -260,7 +260,7 @@ ieee80211_hwmp_init(void) } SYSINIT(wlan_hwmp, SI_SUB_DRIVERS, SI_ORDER_SECOND, ieee80211_hwmp_init, NULL); -void +static void hwmp_vattach(struct ieee80211vap *vap) { struct ieee80211_hwmp_state *hs; @@ -279,7 +279,7 @@ hwmp_vattach(struct ieee80211vap *vap) vap->iv_hwmp = hs; } -void +static void hwmp_vdetach(struct ieee80211vap *vap) { struct ieee80211_hwmp_state *hs = vap->iv_hwmp; @@ -289,7 +289,7 @@ hwmp_vdetach(struct ieee80211vap *vap) vap->iv_hwmp = NULL; } -int +static int hwmp_newstate(struct ieee80211vap *vap, enum ieee80211_state ostate, int arg) { enum ieee80211_state nstate = vap->iv_state; diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index 3b4379f..5c2f9e5 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -603,9 +603,10 @@ ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m, case IEEE80211_ELEMID_MESHCONF: scan->meshconf = frm; break; - case IEEE80211_ELEMID_MESHEXTCAP: - break; #endif + /* Extended capabilities; nothing handles it for now */ + case IEEE80211_ELEMID_EXTCAP: + break; case IEEE80211_ELEMID_VENDOR: if (iswpaoui(frm)) scan->wpa = frm; diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h index ee98026..8c606c1 100644 --- a/sys/net80211/ieee80211_ioctl.h +++ b/sys/net80211/ieee80211_ioctl.h @@ -246,7 +246,10 @@ struct ieee80211_stats { uint32_t is_ampdu_bar_tx_retry; /* A-MPDU BAR frames TX rtry */ uint32_t is_ampdu_bar_tx_fail; /* A-MPDU BAR frames TX fail */ - uint32_t is_spare[7]; + uint32_t is_ff_encapfail; /* failed FF encap */ + uint32_t is_amsdu_encapfail; /* failed A-MSDU encap */ + + uint32_t is_spare[5]; }; /* diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 6ac3655..520687b 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -2638,7 +2638,7 @@ ieee80211_erp_timeout(struct ieee80211com *ic) IEEE80211_LOCK_ASSERT(ic); if ((ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) && - time_after(ticks, ic->ic_lastnonerp + IEEE80211_NONERP_PRESENT_AGE)) { + ieee80211_time_after(ticks, ic->ic_lastnonerp + IEEE80211_NONERP_PRESENT_AGE)) { #if 0 IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, "%s", "age out non-ERP sta present on channel"); diff --git a/sys/net80211/ieee80211_power.c b/sys/net80211/ieee80211_power.c index 587e48f..4493ed6 100644 --- a/sys/net80211/ieee80211_power.c +++ b/sys/net80211/ieee80211_power.c @@ -623,7 +623,7 @@ ieee80211_sta_ps_timer_check(struct ieee80211vap *vap) /* If we've done any data within our idle interval, bail */ /* XXX hard-coded to one second for now, ew! */ - if (time_after(ic->ic_lastdata + 500, ticks)) + if (ieee80211_time_after(ic->ic_lastdata + 500, ticks)) goto out; /* diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index 3bc3141..2feb59a 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -1531,7 +1531,7 @@ beacon_miss(void *arg, int npending) IEEE80211_LOCK(ic); TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { /* - * We only pass events through for sta vap's in RUN state; + * We only pass events through for sta vap's in RUN+ state; * may be too restrictive but for now this saves all the * handlers duplicating these checks. */ @@ -1550,7 +1550,7 @@ beacon_swmiss(void *arg, int npending) struct ieee80211com *ic = vap->iv_ic; IEEE80211_LOCK(ic); - if (vap->iv_state == IEEE80211_S_RUN) { + if (vap->iv_state >= IEEE80211_S_RUN) { /* XXX Call multiple times if npending > zero? */ vap->iv_bmiss(vap); } @@ -1570,8 +1570,7 @@ ieee80211_swbmiss(void *arg) IEEE80211_LOCK_ASSERT(ic); - /* XXX sleep state? */ - KASSERT(vap->iv_state == IEEE80211_S_RUN, + KASSERT(vap->iv_state >= IEEE80211_S_RUN, ("wrong state %d", vap->iv_state)); if (ic->ic_flags & IEEE80211_F_SCAN) { diff --git a/sys/net80211/ieee80211_ratectl.c b/sys/net80211/ieee80211_ratectl.c index 3eff898..e9a0e2f 100644 --- a/sys/net80211/ieee80211_ratectl.c +++ b/sys/net80211/ieee80211_ratectl.c @@ -28,6 +28,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/kernel.h> +#include <sys/sbuf.h> #include <sys/systm.h> #include <sys/socket.h> #include <sys/malloc.h> @@ -68,12 +69,52 @@ ieee80211_ratectl_unregister(int type) ratectls[type] = NULL; } +static void +ieee80211_ratectl_sysctl_stats_node_iter(void *arg, struct ieee80211_node *ni) +{ + + struct sbuf *sb = (struct sbuf *) arg; + sbuf_printf(sb, "MAC: %6D\n", ni->ni_macaddr, ":"); + ieee80211_ratectl_node_stats(ni, sb); + sbuf_printf(sb, "\n"); +} + +static int +ieee80211_ratectl_sysctl_stats(SYSCTL_HANDLER_ARGS) +{ + struct ieee80211vap *vap = arg1; + struct ieee80211com *ic = vap->iv_ic; + struct sbuf sb; + int error; + + error = sysctl_wire_old_buffer(req, 0); + if (error) + return (error); + sbuf_new_for_sysctl(&sb, NULL, 8, req); + sbuf_clear_flags(&sb, SBUF_INCLUDENUL); + + IEEE80211_LOCK(ic); + ieee80211_iterate_nodes(&ic->ic_sta, + ieee80211_ratectl_sysctl_stats_node_iter, + &sb); + IEEE80211_UNLOCK(ic); + + error = sbuf_finish(&sb); + sbuf_delete(&sb); + return (error); +} + void ieee80211_ratectl_init(struct ieee80211vap *vap) { if (vap->iv_rate == ratectls[IEEE80211_RATECTL_NONE]) ieee80211_ratectl_set(vap, IEEE80211_RATECTL_AMRR); vap->iv_rate->ir_init(vap); + + /* Attach generic stats sysctl */ + SYSCTL_ADD_PROC(vap->iv_sysctl, SYSCTL_CHILDREN(vap->iv_oid), OID_AUTO, + "rate_stats", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, vap, + 0, ieee80211_ratectl_sysctl_stats, "A", "ratectl node stats"); } void diff --git a/sys/net80211/ieee80211_ratectl.h b/sys/net80211/ieee80211_ratectl.h index 5603509..9765fe7 100644 --- a/sys/net80211/ieee80211_ratectl.h +++ b/sys/net80211/ieee80211_ratectl.h @@ -53,6 +53,7 @@ struct ieee80211_ratectl { const struct ieee80211_node *, void *, void *, void *); void (*ir_setinterval)(const struct ieee80211vap *, int); + void (*ir_node_stats)(struct ieee80211_node *ni, struct sbuf *s); }; void ieee80211_ratectl_register(int, const struct ieee80211_ratectl *); @@ -115,3 +116,13 @@ ieee80211_ratectl_setinterval(const struct ieee80211vap *vap, int msecs) return; vap->iv_rate->ir_setinterval(vap, msecs); } + +static __inline void +ieee80211_ratectl_node_stats(struct ieee80211_node *ni, struct sbuf *s) +{ + const struct ieee80211vap *vap = ni->ni_vap; + + if (vap->iv_rate->ir_node_stats == NULL) + return; + vap->iv_rate->ir_node_stats(ni, s); +} diff --git a/sys/net80211/ieee80211_scan_sta.c b/sys/net80211/ieee80211_scan_sta.c index 07df92f..233c436 100644 --- a/sys/net80211/ieee80211_scan_sta.c +++ b/sys/net80211/ieee80211_scan_sta.c @@ -1323,7 +1323,7 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) * XXX deauth current ap */ if (curRate < roamRate || curRssi < roamRssi) { - if (time_after(ticks, ic->ic_lastscan + vap->iv_scanvalid)) { + if (ieee80211_time_after(ticks, ic->ic_lastscan + vap->iv_scanvalid)) { /* * Scan cache contents are too old; force a scan now * if possible so we have current state to make a @@ -1333,7 +1333,7 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) * XXX force immediate switch on scan complete */ if (!IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) && - time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)) + ieee80211_time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)) ieee80211_bg_scan(vap, 0); return; } diff --git a/sys/net80211/ieee80211_scan_sw.c b/sys/net80211/ieee80211_scan_sw.c index fc62f24..5875980 100644 --- a/sys/net80211/ieee80211_scan_sw.c +++ b/sys/net80211/ieee80211_scan_sw.c @@ -304,7 +304,7 @@ ieee80211_swscan_check_scan(const struct ieee80211_scanner *scan, } if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 && (flags & IEEE80211_SCAN_FLUSH) == 0 && - time_before(ticks, ic->ic_lastscan + vap->iv_scanvalid)) { + ieee80211_time_before(ticks, ic->ic_lastscan + vap->iv_scanvalid)) { /* * We're not currently scanning and the cache is * deemed hot enough to consult. Lock out others @@ -674,7 +674,7 @@ end: if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) || (ss_priv->ss_iflags & ISCAN_ABORT) || - time_after(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) { + ieee80211_time_after(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) { ss_priv->ss_iflags &= ~ISCAN_RUNNING; scan_end(ss, scandone); return; @@ -686,7 +686,7 @@ end: /* * Watch for truncation due to the scan end time. */ - if (time_after(ticks + ss->ss_maxdwell, ss_priv->ss_scanend)) + if (ieee80211_time_after(ticks + ss->ss_maxdwell, ss_priv->ss_scanend)) maxdwell = ss_priv->ss_scanend - ticks; else maxdwell = ss->ss_maxdwell; @@ -807,7 +807,7 @@ scan_end(struct ieee80211_scan_state *ss, int scandone) if ((ss_priv->ss_iflags & ISCAN_CANCEL) == 0 && !ss->ss_ops->scan_end(ss, vap) && (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 && - time_before(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) { + ieee80211_time_before(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: done, restart " "[ticks %u, dwell min %lu scanend %lu]\n", @@ -923,7 +923,7 @@ ieee80211_swscan_add_scan(struct ieee80211vap *vap, * the timer so we'll switch to the next channel. */ if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 && - time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) { + ieee80211_time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: chan %3d%c min dwell met (%u > %lu)\n", __func__, diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c index ee14ffd..16d801c 100644 --- a/sys/net80211/ieee80211_sta.c +++ b/sys/net80211/ieee80211_sta.c @@ -206,6 +206,24 @@ sta_authretry(struct ieee80211vap *vap, struct ieee80211_node *ni, int reason) } } +static void +sta_swbmiss_start(struct ieee80211vap *vap) +{ + + if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) { + /* + * Start s/w beacon miss timer for devices w/o + * hardware support. We fudge a bit here since + * we're doing this in software. + */ + vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS( + 2 * vap->iv_bmissthreshold * vap->iv_bss->ni_intval); + vap->iv_swbmiss_count = 0; + callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, + ieee80211_swbmiss, vap); + } +} + /* * IEEE80211_M_STA vap state machine handler. * This routine handles the main states in the 802.11 protocol. @@ -341,12 +359,13 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) break; } break; + case IEEE80211_S_SLEEP: case IEEE80211_S_RUN: switch (arg & 0xff) { case IEEE80211_FC0_SUBTYPE_AUTH: IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, 2); - vap->iv_state = ostate; /* stay RUN */ + vap->iv_state = IEEE80211_S_RUN; /* stay RUN */ break; case IEEE80211_FC0_SUBTYPE_DEAUTH: ieee80211_sta_leave(ni); @@ -418,19 +437,8 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) goto invalid; } ieee80211_sync_curchan(ic); - if (ostate != IEEE80211_S_RUN && - (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)) { - /* - * Start s/w beacon miss timer for devices w/o - * hardware support. We fudge a bit here since - * we're doing this in software. - */ - vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS( - 2 * vap->iv_bmissthreshold * ni->ni_intval); - vap->iv_swbmiss_count = 0; - callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, - ieee80211_swbmiss, vap); - } + if (ostate != IEEE80211_S_RUN) + sta_swbmiss_start(vap); /* * When 802.1x is not in use mark the port authorized * at this point so traffic can flow. @@ -450,6 +458,7 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) goto invalid; break; case IEEE80211_S_SLEEP: + sta_swbmiss_start(vap); vap->iv_sta_ps(vap, 1); break; default: @@ -1244,7 +1253,7 @@ contbgscan(struct ieee80211vap *vap) return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) && (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 && vap->iv_state == IEEE80211_S_RUN && /* XXX? */ - time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)); + ieee80211_time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)); } /* @@ -1265,8 +1274,8 @@ startbgscan(struct ieee80211vap *vap) #ifdef IEEE80211_SUPPORT_SUPERG !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) && #endif - time_after(ticks, ic->ic_lastscan + vap->iv_bgscanintvl) && - time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)); + ieee80211_time_after(ticks, ic->ic_lastscan + vap->iv_bgscanintvl) && + ieee80211_time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)); } static void diff --git a/sys/net80211/ieee80211_wds.c b/sys/net80211/ieee80211_wds.c index 03d9c60..7fe91cc 100644 --- a/sys/net80211/ieee80211_wds.c +++ b/sys/net80211/ieee80211_wds.c @@ -288,9 +288,11 @@ ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m) /* * Encapsulate the packet in prep for transmission. */ + IEEE80211_TX_LOCK(ic); mcopy = ieee80211_encap(vap, ni, mcopy); if (mcopy == NULL) { /* NB: stat+msg handled in ieee80211_encap */ + IEEE80211_TX_UNLOCK(ic); ieee80211_free_node(ni); continue; } @@ -298,6 +300,7 @@ ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m) mcopy->m_pkthdr.rcvif = (void *) ni; err = ieee80211_parent_xmitpkt(ic, mcopy); + IEEE80211_TX_UNLOCK(ic); if (!err) { if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1); diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index ec42e67..50e5d6e 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -1298,6 +1298,11 @@ in_pcbfree(struct inpcb *inp) if (inp->inp_moptions != NULL) inp_freemoptions(inp->inp_moptions); #endif + if (inp->inp_route.ro_rt) { + RTFREE(inp->inp_route.ro_rt); + inp->inp_route.ro_rt = (struct rtentry *)NULL; + } + inp->inp_vflag = 0; inp->inp_flags2 |= INP_FREED; crfree(inp->inp_cred); @@ -2225,6 +2230,23 @@ in_pcbremlists(struct inpcb *inp) } /* + * Check for alternatives when higher level complains + * about service problems. For now, invalidate cached + * routing information. If the route was created dynamically + * (by a redirect), time to try a default gateway again. + */ +void +in_losing(struct inpcb *inp) +{ + + if (inp->inp_route.ro_rt) { + RTFREE(inp->inp_route.ro_rt); + inp->inp_route.ro_rt = (struct rtentry *)NULL; + } + return; +} + +/* * A set label operation has occurred at the socket layer, propagate the * label change into the in_pcb for the socket. */ diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index 54eab88..9c77a51 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -42,6 +42,7 @@ #include <sys/_lock.h> #include <sys/_mutex.h> #include <sys/_rwlock.h> +#include <net/route.h> #ifdef _KERNEL #include <sys/lock.h> @@ -238,8 +239,14 @@ struct inpcb { #define inp_zero_size offsetof(struct inpcb, inp_gencnt) inp_gen_t inp_gencnt; /* (c) generation count */ struct llentry *inp_lle; /* cached L2 information */ - struct rtentry *inp_rt; /* cached L3 information */ struct rwlock inp_lock; + rt_gen_t inp_rt_cookie; /* generation for route entry */ + union { /* cached L3 information */ + struct route inpu_route; + struct route_in6 inpu_route6; + } inp_rtu; +#define inp_route inp_rtu.inpu_route +#define inp_route6 inp_rtu.inpu_route6 }; #define inp_fport inp_inc.inc_fport #define inp_lport inp_inc.inc_lport @@ -722,6 +729,7 @@ void in_pcbrehash_mbuf(struct inpcb *, struct mbuf *); int in_pcbrele(struct inpcb *); int in_pcbrele_rlocked(struct inpcb *); int in_pcbrele_wlocked(struct inpcb *); +void in_losing(struct inpcb *); void in_pcbsetsolabel(struct socket *so); int in_getpeeraddr(struct socket *so, struct sockaddr **nam); int in_getsockaddr(struct socket *so, struct sockaddr **nam); diff --git a/sys/netinet/in_pcbgroup.c b/sys/netinet/in_pcbgroup.c index 2d3c136..04a7699 100644 --- a/sys/netinet/in_pcbgroup.c +++ b/sys/netinet/in_pcbgroup.c @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include <sys/mbuf.h> #include <sys/mutex.h> #include <sys/smp.h> +#include <sys/socket.h> #include <sys/socketvar.h> #include <net/rss_config.h> diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index fa225fc..afdf1a7 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -282,17 +282,36 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, int flags, gw = dst = (struct sockaddr_in *)&ro->ro_dst; fibnum = (inp != NULL) ? inp->inp_inc.inc_fibnum : M_GETFIB(m); rte = ro->ro_rt; - /* - * The address family should also be checked in case of sharing - * the cache with IPv6. - */ - if (rte == NULL || dst->sin_family != AF_INET) { + if (rte == NULL) { bzero(dst, sizeof(*dst)); dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr = ip->ip_dst; } again: + /* + * Validate route against routing table additions; + * a better/more specific route might have been added. + */ + if (inp) + RT_VALIDATE(ro, &inp->inp_rt_cookie, fibnum); + /* + * If there is a cached route, + * check that it is to the same destination + * and is still up. If not, free it and try again. + * The address family should also be checked in case of sharing the + * cache with IPv6. + * Also check whether routing cache needs invalidation. + */ + rte = ro->ro_rt; + if (rte && ((rte->rt_flags & RTF_UP) == 0 || + rte->rt_ifp == NULL || + !RT_LINK_IS_UP(rte->rt_ifp) || + dst->sin_family != AF_INET || + dst->sin_addr.s_addr != ip->ip_dst.s_addr)) { + RTFREE(rte); + rte = ro->ro_rt = (struct rtentry *)NULL; + } ia = NULL; have_ia_ref = 0; /* diff --git a/sys/netinet/sctp_cc_functions.c b/sys/netinet/sctp_cc_functions.c index 0bddcfd..0ad3e33 100644 --- a/sys/netinet/sctp_cc_functions.c +++ b/sys/netinet/sctp_cc_functions.c @@ -1526,13 +1526,13 @@ sctp_rtt_rtcc_calculated(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_hs_raise_drop { int32_t cwnd; - int32_t increase; - int32_t drop_percent; + int8_t increase; + int8_t drop_percent; }; #define SCTP_HS_TABLE_SIZE 73 -struct sctp_hs_raise_drop sctp_cwnd_adjust[SCTP_HS_TABLE_SIZE] = { +static const struct sctp_hs_raise_drop sctp_cwnd_adjust[SCTP_HS_TABLE_SIZE] = { {38, 1, 50}, /* 0 */ {118, 2, 44}, /* 1 */ {221, 3, 41}, /* 2 */ @@ -1632,7 +1632,7 @@ sctp_hs_cwnd_increase(struct sctp_tcb *stcb, struct sctp_nets *net) } } net->last_hs_used = indx; - incr = ((sctp_cwnd_adjust[indx].increase) << 10); + incr = (((int32_t) sctp_cwnd_adjust[indx].increase) << 10); net->cwnd += incr; } sctp_enforce_cwnd_limit(&stcb->asoc, net); @@ -1658,7 +1658,7 @@ sctp_hs_cwnd_decrease(struct sctp_tcb *stcb, struct sctp_nets *net) } else { /* drop by the proper amount */ net->ssthresh = net->cwnd - (int)((net->cwnd / 100) * - sctp_cwnd_adjust[net->last_hs_used].drop_percent); + (int32_t) sctp_cwnd_adjust[net->last_hs_used].drop_percent); net->cwnd = net->ssthresh; /* now where are we */ indx = net->last_hs_used; @@ -2323,7 +2323,7 @@ sctp_htcp_cwnd_update_after_ecn_echo(struct sctp_tcb *stcb, } } -struct sctp_cc_functions sctp_cc_functions[] = { +const struct sctp_cc_functions sctp_cc_functions[] = { { .sctp_set_initial_cc_param = sctp_set_initial_cc_param, .sctp_cwnd_update_after_sack = sctp_cwnd_update_after_sack, diff --git a/sys/netinet/sctp_constants.h b/sys/netinet/sctp_constants.h index 5837f6f..497dde6 100644 --- a/sys/netinet/sctp_constants.h +++ b/sys/netinet/sctp_constants.h @@ -68,6 +68,8 @@ __FBSDID("$FreeBSD$"); /* Largest length of a chunk */ #define SCTP_MAX_CHUNK_LENGTH 0xffff +/* Largest length of an error cause */ +#define SCTP_MAX_CAUSE_LENGTH 0xffff /* Number of addresses where we just skip the counting */ #define SCTP_COUNT_LIMIT 40 diff --git a/sys/netinet/sctp_indata.c b/sys/netinet/sctp_indata.c index 75c1ac5..cbc7fb5 100644 --- a/sys/netinet/sctp_indata.c +++ b/sys/netinet/sctp_indata.c @@ -1948,7 +1948,7 @@ finish_express_del: return (1); } -int8_t sctp_map_lookup_tab[256] = { +static const int8_t sctp_map_lookup_tab[256] = { 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, @@ -2495,7 +2495,7 @@ sctp_process_data(struct mbuf **mm, int iphlen, int *offset, int length, if (op_err != NULL) { cause = mtod(op_err, struct sctp_gen_error_cause *); cause->code = htons(SCTP_CAUSE_UNRECOG_CHUNK); - cause->length = htons(chk_length + sizeof(struct sctp_gen_error_cause)); + cause->length = htons((uint16_t) (chk_length + sizeof(struct sctp_gen_error_cause))); SCTP_BUF_LEN(op_err) = sizeof(struct sctp_gen_error_cause); SCTP_BUF_NEXT(op_err) = SCTP_M_COPYM(m, *offset, chk_length, M_NOWAIT); if (SCTP_BUF_NEXT(op_err) != NULL) { @@ -2688,7 +2688,7 @@ sctp_process_segment_range(struct sctp_tcb *stcb, struct sctp_tmit_chunk **p_tp1 sctp_misc_ints(SCTP_FLIGHT_LOG_DOWN_GAP, tp1->whoTo->flight_size, tp1->book_size, - (uintptr_t) tp1->whoTo, + (uint32_t) (uintptr_t) tp1->whoTo, tp1->rec.data.TSN_seq); } sctp_flight_size_decrease(tp1); @@ -2897,7 +2897,7 @@ sctp_check_for_revoked(struct sctp_tcb *stcb, sctp_misc_ints(SCTP_FLIGHT_LOG_UP_REVOKE, tp1->whoTo->flight_size, tp1->book_size, - (uintptr_t) tp1->whoTo, + (uint32_t) (uintptr_t) tp1->whoTo, tp1->rec.data.TSN_seq); } sctp_flight_size_increase(tp1); @@ -3211,7 +3211,7 @@ sctp_strike_gap_ack_chunks(struct sctp_tcb *stcb, struct sctp_association *asoc, sctp_misc_ints(SCTP_FLIGHT_LOG_DOWN_RSND, (tp1->whoTo ? (tp1->whoTo->flight_size) : 0), tp1->book_size, - (uintptr_t) tp1->whoTo, + (uint32_t) (uintptr_t) tp1->whoTo, tp1->rec.data.TSN_seq); } if (tp1->whoTo) { @@ -3523,7 +3523,7 @@ sctp_window_probe_recovery(struct sctp_tcb *stcb, sctp_misc_ints(SCTP_FLIGHT_LOG_DWN_WP_FWD, tp1->whoTo ? tp1->whoTo->flight_size : 0, tp1->book_size, - (uintptr_t) tp1->whoTo, + (uint32_t) (uintptr_t) tp1->whoTo, tp1->rec.data.TSN_seq); return; } @@ -3542,7 +3542,7 @@ sctp_window_probe_recovery(struct sctp_tcb *stcb, sctp_misc_ints(SCTP_FLIGHT_LOG_DOWN_WP, tp1->whoTo->flight_size, tp1->book_size, - (uintptr_t) tp1->whoTo, + (uint32_t) (uintptr_t) tp1->whoTo, tp1->rec.data.TSN_seq); } } @@ -3661,7 +3661,7 @@ sctp_express_handle_sack(struct sctp_tcb *stcb, uint32_t cumack, sctp_misc_ints(SCTP_FLIGHT_LOG_DOWN_CA, tp1->whoTo->flight_size, tp1->book_size, - (uintptr_t) tp1->whoTo, + (uint32_t) (uintptr_t) tp1->whoTo, tp1->rec.data.TSN_seq); } sctp_flight_size_decrease(tp1); @@ -4302,7 +4302,7 @@ sctp_handle_sack(struct mbuf *m, int offset_seg, int offset_dup, sctp_misc_ints(SCTP_FLIGHT_LOG_DOWN_CA, tp1->whoTo->flight_size, tp1->book_size, - (uintptr_t) tp1->whoTo, + (uint32_t) (uintptr_t) tp1->whoTo, tp1->rec.data.TSN_seq); } sctp_flight_size_decrease(tp1); @@ -4575,7 +4575,7 @@ sctp_handle_sack(struct mbuf *m, int offset_seg, int offset_dup, sctp_misc_ints(SCTP_FLIGHT_LOG_UP_REVOKE, tp1->whoTo->flight_size, tp1->book_size, - (uintptr_t) tp1->whoTo, + (uint32_t) (uintptr_t) tp1->whoTo, tp1->rec.data.TSN_seq); } sctp_flight_size_increase(tp1); diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c index a205f2f..c555be2 100644 --- a/sys/netinet/sctp_input.c +++ b/sys/netinet/sctp_input.c @@ -2358,7 +2358,7 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset, SCTP_RTT_FROM_NON_DATA); #if defined(INET) || defined(INET6) if (((*netp)->port == 0) && (port != 0)) { - sctp_pathmtu_adjustment(stcb, (*netp)->mtu - sizeof(struct udphdr)); + sctp_pathmtu_adjustment(stcb, (uint16_t) ((*netp)->mtu - sizeof(struct udphdr))); } (*netp)->port = port; #endif @@ -3339,7 +3339,7 @@ process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc, sctp_misc_ints(SCTP_FLIGHT_LOG_DOWN_PDRP, tp1->whoTo->flight_size, tp1->book_size, - (uintptr_t) stcb, + (uint32_t) (uintptr_t) stcb, tp1->rec.data.TSN_seq); } if (tp1->sent < SCTP_DATAGRAM_RESEND) { @@ -5596,7 +5596,7 @@ process_control_chunks: len = min(SCTP_SIZE32(chk_length), (uint32_t) (length - *offset)); cause = mtod(op_err, struct sctp_gen_error_cause *); cause->code = htons(SCTP_CAUSE_UNRECOG_CHUNK); - cause->length = htons(len + sizeof(struct sctp_gen_error_cause)); + cause->length = htons((uint16_t) (len + sizeof(struct sctp_gen_error_cause))); SCTP_BUF_LEN(op_err) = sizeof(struct sctp_gen_error_cause); SCTP_BUF_NEXT(op_err) = SCTP_M_COPYM(m, *offset, len, M_NOWAIT); if (SCTP_BUF_NEXT(op_err) != NULL) { @@ -6033,7 +6033,9 @@ trigger_send: if (!TAILQ_EMPTY(&stcb->asoc.control_send_queue)) { cnt_ctrl_ready = stcb->asoc.ctrl_queue_cnt - stcb->asoc.ecn_echo_cnt_onq; } - if (cnt_ctrl_ready || stcb->asoc.trigger_reset || + if (!TAILQ_EMPTY(&stcb->asoc.asconf_send_queue) || + cnt_ctrl_ready || + stcb->asoc.trigger_reset || ((un_sent) && (stcb->asoc.peers_rwnd > 0 || (stcb->asoc.peers_rwnd <= 0 && stcb->asoc.total_flight == 0)))) { diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index a900106..8eda473 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -67,7 +67,7 @@ struct sack_track { struct sctp_gap_ack_block gaps[SCTP_MAX_GAPS_INARRAY]; }; -struct sack_track sack_array[256] = { +const struct sack_track sack_array[256] = { {0, 0, 0, 0, /* 0x00 */ {{0, 0}, {0, 0}, @@ -3508,7 +3508,7 @@ sctp_find_cmsg(int c_type, void *data, struct mbuf *control, size_t cpsize) return (found); } /* It is exactly what we want. Copy it out. */ - m_copydata(control, at + CMSG_ALIGN(sizeof(cmh)), cpsize, (caddr_t)data); + m_copydata(control, at + CMSG_ALIGN(sizeof(cmh)), (int)cpsize, (caddr_t)data); return (1); } else { struct sctp_sndrcvinfo *sndrcvinfo; @@ -4186,7 +4186,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, udp = (struct udphdr *)((caddr_t)ip + sizeof(struct ip)); udp->uh_sport = htons(SCTP_BASE_SYSCTL(sctp_udp_tunneling_port)); udp->uh_dport = port; - udp->uh_ulen = htons(packet_length - sizeof(struct ip)); + udp->uh_ulen = htons((uint16_t) (packet_length - sizeof(struct ip))); if (V_udp_cksum) { udp->uh_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, udp->uh_ulen + htons(IPPROTO_UDP)); } else { @@ -4414,7 +4414,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } else { ip6h->ip6_nxt = IPPROTO_SCTP; } - ip6h->ip6_plen = (packet_length - sizeof(struct ip6_hdr)); + ip6h->ip6_plen = (uint16_t) (packet_length - sizeof(struct ip6_hdr)); ip6h->ip6_dst = sin6->sin6_addr; /* @@ -4533,7 +4533,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, udp = (struct udphdr *)((caddr_t)ip6h + sizeof(struct ip6_hdr)); udp->uh_sport = htons(SCTP_BASE_SYSCTL(sctp_udp_tunneling_port)); udp->uh_dport = port; - udp->uh_ulen = htons(packet_length - sizeof(struct ip6_hdr)); + udp->uh_ulen = htons((uint16_t) (packet_length - sizeof(struct ip6_hdr))); udp->uh_sum = 0; sctphdr = (struct sctphdr *)((caddr_t)udp + sizeof(struct udphdr)); } else { @@ -6479,10 +6479,10 @@ error_out: } } /* get the new end of length */ - len = M_TRAILINGSPACE(*endofchain); + len = (int)M_TRAILINGSPACE(*endofchain); } else { /* how much is left at the end? */ - len = M_TRAILINGSPACE(*endofchain); + len = (int)M_TRAILINGSPACE(*endofchain); } /* Find the end of the data, for appending */ cp = (mtod((*endofchain), caddr_t)+SCTP_BUF_LEN((*endofchain))); @@ -6636,7 +6636,7 @@ sctp_sendall_iterator(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, ph = mtod(m, struct sctp_paramhdr *); ph->param_type = htons(SCTP_CAUSE_USER_INITIATED_ABT); - ph->param_length = htons(sizeof(struct sctp_paramhdr) + ca->sndlen); + ph->param_length = htons((uint16_t) (sizeof(struct sctp_paramhdr) + ca->sndlen)); } /* * We add one here to keep the assoc from dis-appearing on @@ -6814,7 +6814,7 @@ sctp_copy_out_all(struct uio *uio, int len) left = len; SCTP_BUF_LEN(ret) = 0; /* save space for the data chunk header */ - cancpy = M_TRAILINGSPACE(ret); + cancpy = (int)M_TRAILINGSPACE(ret); willcpy = min(cancpy, left); at = ret; while (left > 0) { @@ -6835,7 +6835,7 @@ sctp_copy_out_all(struct uio *uio, int len) } at = SCTP_BUF_NEXT(at); SCTP_BUF_LEN(at) = 0; - cancpy = M_TRAILINGSPACE(at); + cancpy = (int)M_TRAILINGSPACE(at); willcpy = min(cancpy, left); } } @@ -6869,7 +6869,7 @@ sctp_sendall(struct sctp_inpcb *inp, struct uio *uio, struct mbuf *m, ca->sndrcv.sinfo_flags &= ~SCTP_SENDALL; /* get length and mbuf chain */ if (uio) { - ca->sndlen = uio->uio_resid; + ca->sndlen = (int)uio->uio_resid; ca->m = sctp_copy_out_all(uio, ca->sndlen); if (ca->m == NULL) { SCTP_FREE(ca, SCTP_M_COPYAL); @@ -7019,7 +7019,7 @@ all_done: sctp_misc_ints(SCTP_FLIGHT_LOG_UP, data_list[i]->whoTo->flight_size, data_list[i]->book_size, - (uintptr_t) data_list[i]->whoTo, + (uint32_t) (uintptr_t) data_list[i]->whoTo, data_list[i]->rec.data.TSN_seq); } sctp_flight_size_increase(data_list[i]); @@ -7516,7 +7516,7 @@ dont_do_it: goto out_of; } sctp_snd_sb_alloc(stcb, sizeof(struct sctp_data_chunk)); - chk->book_size = chk->send_size = (to_move + sizeof(struct sctp_data_chunk)); + chk->book_size = chk->send_size = (uint16_t) (to_move + sizeof(struct sctp_data_chunk)); chk->book_size_scale = 0; chk->sent = SCTP_DATAGRAM_UNSENT; @@ -7551,7 +7551,7 @@ dont_do_it: chk->rec.data.TSN_seq = atomic_fetchadd_int(&asoc->sending_seq, 1); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_AT_SEND_2_OUTQ) { sctp_misc_ints(SCTP_STRMOUT_LOG_SEND, - (uintptr_t) stcb, sp->length, + (uint32_t) (uintptr_t) stcb, sp->length, (uint32_t) ((chk->rec.data.stream_number << 16) | chk->rec.data.stream_seq), chk->rec.data.TSN_seq); } @@ -8943,6 +8943,8 @@ sctp_queue_op_err(struct sctp_tcb *stcb, struct mbuf *op_err) chk->asoc = &stcb->asoc; chk->data = op_err; chk->whoTo = NULL; + chk->rec.chunk_id.id = SCTP_OPERATION_ERROR; + chk->rec.chunk_id.can_take_data = 0; hdr = mtod(op_err, struct sctp_chunkhdr *); hdr->chunk_type = SCTP_OPERATION_ERROR; hdr->chunk_flags = 0; @@ -9827,7 +9829,7 @@ one_chunk_around: sctp_misc_ints(SCTP_FLIGHT_LOG_UP_RSND, data_list[i]->whoTo->flight_size, data_list[i]->book_size, - (uintptr_t) data_list[i]->whoTo, + (uint32_t) (uintptr_t) data_list[i]->whoTo, data_list[i]->rec.data.TSN_seq); } sctp_flight_size_increase(data_list[i]); @@ -10288,7 +10290,7 @@ sctp_fill_in_rest: space_needed = (sizeof(struct sctp_forward_tsn_chunk) + (cnt_of_skipped * sizeof(struct sctp_strseq))); - cnt_of_space = M_TRAILINGSPACE(chk->data); + cnt_of_space = (unsigned int)M_TRAILINGSPACE(chk->data); if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { ovh = SCTP_MIN_OVERHEAD; @@ -10414,7 +10416,7 @@ sctp_send_sack(struct sctp_tcb *stcb, int so_locked struct sctp_sack_chunk *sack; struct sctp_nr_sack_chunk *nr_sack; struct sctp_gap_ack_block *gap_descriptor; - struct sack_track *selector; + const struct sack_track *selector; int mergeable = 0; int offset; caddr_t limit; @@ -10546,7 +10548,7 @@ sctp_send_sack(struct sctp_tcb *stcb, int so_locked } /* ok, lets go through and fill it in */ SCTP_BUF_RESV_UF(a_chk->data, SCTP_MIN_OVERHEAD); - space = M_TRAILINGSPACE(a_chk->data); + space = (unsigned int)M_TRAILINGSPACE(a_chk->data); if (space > (a_chk->whoTo->mtu - SCTP_MIN_OVERHEAD)) { space = (a_chk->whoTo->mtu - SCTP_MIN_OVERHEAD); } @@ -10760,9 +10762,9 @@ sctp_send_sack(struct sctp_tcb *stcb, int so_locked * queue. */ if (type == SCTP_SELECTIVE_ACK) { - a_chk->send_size = sizeof(struct sctp_sack_chunk) + + a_chk->send_size = (uint16_t) (sizeof(struct sctp_sack_chunk) + (num_gap_blocks + num_nr_gap_blocks) * sizeof(struct sctp_gap_ack_block) + - num_dups * sizeof(int32_t); + num_dups * sizeof(int32_t)); SCTP_BUF_LEN(a_chk->data) = a_chk->send_size; sack->sack.cum_tsn_ack = htonl(asoc->cumulative_tsn); sack->sack.a_rwnd = htonl(asoc->my_rwnd); @@ -10772,9 +10774,9 @@ sctp_send_sack(struct sctp_tcb *stcb, int so_locked sack->ch.chunk_flags = flags; sack->ch.chunk_length = htons(a_chk->send_size); } else { - a_chk->send_size = sizeof(struct sctp_nr_sack_chunk) + + a_chk->send_size = (uint16_t) (sizeof(struct sctp_nr_sack_chunk) + (num_gap_blocks + num_nr_gap_blocks) * sizeof(struct sctp_gap_ack_block) + - num_dups * sizeof(int32_t); + num_dups * sizeof(int32_t)); SCTP_BUF_LEN(a_chk->data) = a_chk->send_size; nr_sack->nr_sack.cum_tsn_ack = htonl(asoc->cumulative_tsn); nr_sack->nr_sack.a_rwnd = htonl(asoc->my_rwnd); @@ -11082,10 +11084,10 @@ sctp_send_resp_msg(struct sockaddr *src, struct sockaddr *dst, udp->uh_sport = htons(SCTP_BASE_SYSCTL(sctp_udp_tunneling_port)); udp->uh_dport = port; udp->uh_sum = 0; - udp->uh_ulen = htons(sizeof(struct udphdr) + + udp->uh_ulen = htons((uint16_t) (sizeof(struct udphdr) + sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr) + - cause_len + padding_len); + cause_len + padding_len)); len += sizeof(struct udphdr); shout = (struct sctphdr *)((caddr_t)shout + sizeof(struct udphdr)); } else { @@ -11108,7 +11110,7 @@ sctp_send_resp_msg(struct sockaddr *src, struct sockaddr *dst, } else { ch->chunk_flags = SCTP_HAD_NO_TCB; } - ch->chunk_length = htons(sizeof(struct sctp_chunkhdr) + cause_len); + ch->chunk_length = htons((uint16_t) (sizeof(struct sctp_chunkhdr) + cause_len)); len += sizeof(struct sctp_chunkhdr); len += cause_len + padding_len; @@ -11157,7 +11159,7 @@ sctp_send_resp_msg(struct sockaddr *src, struct sockaddr *dst, #endif #ifdef INET6 case AF_INET6: - ip6->ip6_plen = len - sizeof(struct ip6_hdr); + ip6->ip6_plen = (uint16_t) (len - sizeof(struct ip6_hdr)); if (port) { #if defined(SCTP_WITH_NO_CSUM) SCTP_STAT_INCR(sctps_sendnocrc); @@ -11484,7 +11486,7 @@ jump_out: * Len is already adjusted to size minus overhead above take * out the pkt_drop chunk itself from it. */ - chk->send_size = len - sizeof(struct sctp_pktdrop_chunk); + chk->send_size = (uint16_t) (len - sizeof(struct sctp_pktdrop_chunk)); len = chk->send_size; } else { /* no truncation needed */ @@ -11624,7 +11626,7 @@ sctp_add_stream_reset_out(struct sctp_tcb *stcb, struct sctp_tmit_chunk *chk, if (number_entries > SCTP_MAX_STREAMS_AT_ONCE_RESET) { number_entries = SCTP_MAX_STREAMS_AT_ONCE_RESET; } - len = (sizeof(struct sctp_stream_reset_out_request) + (sizeof(uint16_t) * number_entries)); + len = (uint16_t) (sizeof(struct sctp_stream_reset_out_request) + (sizeof(uint16_t) * number_entries)); req_out->ph.param_type = htons(SCTP_STR_RESET_OUT_REQUEST); req_out->ph.param_length = htons(len); req_out->request_seq = htonl(seq); @@ -11681,7 +11683,7 @@ sctp_add_stream_reset_in(struct sctp_tmit_chunk *chk, /* get to new offset for the param. */ req_in = (struct sctp_stream_reset_in_request *)((caddr_t)ch + len); /* now how long will this param be? */ - len = (sizeof(struct sctp_stream_reset_in_request) + (sizeof(uint16_t) * number_entries)); + len = (uint16_t) (sizeof(struct sctp_stream_reset_in_request) + (sizeof(uint16_t) * number_entries)); req_in->ph.param_type = htons(SCTP_STR_RESET_IN_REQUEST); req_in->ph.param_length = htons(len); req_in->request_seq = htonl(seq); @@ -12313,7 +12315,7 @@ sctp_copy_it_in(struct sctp_tcb *stcb, (void)SCTP_GETTIME_TIMEVAL(&sp->ts); sp->stream = srcv->sinfo_stream; - sp->length = min(uio->uio_resid, max_send_len); + sp->length = (uint32_t) min(uio->uio_resid, max_send_len); if ((sp->length == (uint32_t) uio->uio_resid) && ((user_marks_eor == 0) || (srcv->sinfo_flags & SCTP_EOF) || @@ -12472,7 +12474,7 @@ sctp_lower_sosend(struct socket *so, SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL); return (EINVAL); } - sndlen = uio->uio_resid; + sndlen = (unsigned int)uio->uio_resid; } else { top = SCTP_HEADER_TO_CHAIN(i_pak); sndlen = SCTP_HEADER_LEN(i_pak); @@ -12871,7 +12873,7 @@ sctp_lower_sosend(struct socket *so, /* now move forward the data pointer */ ph = mtod(mm, struct sctp_paramhdr *); ph->param_type = htons(SCTP_CAUSE_USER_INITIATED_ABT); - ph->param_length = htons(sizeof(struct sctp_paramhdr) + tot_out); + ph->param_length = htons((uint16_t) (sizeof(struct sctp_paramhdr) + tot_out)); ph++; SCTP_BUF_LEN(mm) = tot_out + sizeof(struct sctp_paramhdr); if (top == NULL) { @@ -13291,7 +13293,7 @@ skip_preblock: min(SCTP_BASE_SYSCTL(sctp_add_more_threshold), SCTP_SB_LIMIT_SND(so)))) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_BLK_LOGGING_ENABLE) { sctp_log_block(SCTP_BLOCK_LOG_INTO_BLK, - asoc, uio->uio_resid); + asoc, (size_t)uio->uio_resid); } be.error = 0; stcb->block_entry = &be; diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c index 7e3218d..12aa145 100644 --- a/sys/netinet/sctp_pcb.c +++ b/sys/netinet/sctp_pcb.c @@ -5357,6 +5357,7 @@ void sctp_add_local_addr_ep(struct sctp_inpcb *inp, struct sctp_ifa *ifa, uint32_t action) { struct sctp_laddr *laddr; + struct sctp_tcb *stcb; int fnd, error = 0; fnd = 0; @@ -5402,6 +5403,9 @@ sctp_add_local_addr_ep(struct sctp_inpcb *inp, struct sctp_ifa *ifa, uint32_t ac default: break; } + LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { + sctp_add_local_addr_restricted(stcb, ifa); + } } return; } diff --git a/sys/netinet/sctp_ss_functions.c b/sys/netinet/sctp_ss_functions.c index a2568ad..c4cbb05 100644 --- a/sys/netinet/sctp_ss_functions.c +++ b/sys/netinet/sctp_ss_functions.c @@ -822,7 +822,7 @@ default_again: return (strq); } -struct sctp_ss_functions sctp_ss_functions[] = { +const struct sctp_ss_functions sctp_ss_functions[] = { /* SCTP_SS_DEFAULT */ { .sctp_ss_init = sctp_ss_default_init, diff --git a/sys/netinet/sctp_timer.c b/sys/netinet/sctp_timer.c index dec02de..9fa37e7 100644 --- a/sys/netinet/sctp_timer.c +++ b/sys/netinet/sctp_timer.c @@ -662,7 +662,7 @@ start_again: sctp_misc_ints(SCTP_FLIGHT_LOG_DOWN_RSND_TO, chk->whoTo->flight_size, chk->book_size, - (uintptr_t) chk->whoTo, + (uint32_t) (uintptr_t) chk->whoTo, chk->rec.data.TSN_seq); } sctp_flight_size_decrease(chk); @@ -790,7 +790,7 @@ start_again: sctp_misc_ints(SCTP_FLIGHT_LOG_UP, chk->whoTo->flight_size, chk->book_size, - (uintptr_t) chk->whoTo, + (uint32_t) (uintptr_t) chk->whoTo, chk->rec.data.TSN_seq); } sctp_flight_size_increase(chk); diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c index d11bf20..a75f025 100644 --- a/sys/netinet/sctp_usrreq.c +++ b/sys/netinet/sctp_usrreq.c @@ -54,8 +54,8 @@ __FBSDID("$FreeBSD$"); -extern struct sctp_cc_functions sctp_cc_functions[]; -extern struct sctp_ss_functions sctp_ss_functions[]; +extern const struct sctp_cc_functions sctp_cc_functions[]; +extern const struct sctp_ss_functions sctp_ss_functions[]; void sctp_init(void) @@ -132,7 +132,7 @@ sctp_pathmtu_adjustment(struct sctp_tcb *stcb, uint16_t nxtsz) sctp_misc_ints(SCTP_FLIGHT_LOG_DOWN_PMTU, chk->whoTo->flight_size, chk->book_size, - (uintptr_t) chk->whoTo, + (uint32_t) (uintptr_t) chk->whoTo, chk->rec.data.TSN_seq); } /* Clear any time so NO RTT is being done */ @@ -1413,7 +1413,7 @@ sctp_do_connect_x(struct socket *so, struct sctp_inpcb *inp, void *optval, int creat_lock_on = 0; struct sctp_tcb *stcb = NULL; struct sockaddr *sa; - int num_v6 = 0, num_v4 = 0, *totaddrp, totaddr; + unsigned int num_v6 = 0, num_v4 = 0, *totaddrp, totaddr; uint32_t vrf_id; int bad_addresses = 0; sctp_assoc_t *a_id; @@ -1449,10 +1449,10 @@ sctp_do_connect_x(struct socket *so, struct sctp_inpcb *inp, void *optval, error = EFAULT; goto out_now; } - totaddrp = (int *)optval; + totaddrp = (unsigned int *)optval; totaddr = *totaddrp; sa = (struct sockaddr *)(totaddrp + 1); - stcb = sctp_connectx_helper_find(inp, sa, &totaddr, &num_v4, &num_v6, &error, (optsize - sizeof(int)), &bad_addresses); + stcb = sctp_connectx_helper_find(inp, sa, &totaddr, &num_v4, &num_v6, &error, (unsigned int)(optsize - sizeof(int)), &bad_addresses); if ((stcb != NULL) || bad_addresses) { /* Already have or am bring up an association */ SCTP_ASOC_CREATE_UNLOCK(inp); @@ -1903,7 +1903,8 @@ flags_out: case SCTP_GET_ASSOC_ID_LIST: { struct sctp_assoc_ids *ids; - unsigned int at, limit; + uint32_t at; + size_t limit; SCTP_CHECK_AND_CAST(ids, optval, struct sctp_assoc_ids, *optsize); SCTP_INP_RLOCK(inp); @@ -1919,6 +1920,11 @@ flags_out: LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { if (at < limit) { ids->gaids_assoc_id[at++] = sctp_get_associd(stcb); + if (at == 0) { + error = EINVAL; + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, error); + break; + } } else { error = EINVAL; SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, error); @@ -5810,6 +5816,10 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, __func__); continue; } + if ((sctp_is_addr_restricted(stcb, laddr->ifa)) && + (!sctp_is_addr_pending(stcb, laddr->ifa))) { + continue; + } if (laddr->ifa == ifa) { found = 1; break; @@ -5862,7 +5872,7 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); error = EINVAL; } - sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_SOCKOPT, SCTP_SO_LOCKED); + sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_SOCKOPT, SCTP_SO_LOCKED); out_of_it: SCTP_TCB_UNLOCK(stcb); } else { diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index 4115256..9434f06 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -58,8 +58,8 @@ __FBSDID("$FreeBSD$"); #define KTR_SCTP KTR_SUBSYS #endif -extern struct sctp_cc_functions sctp_cc_functions[]; -extern struct sctp_ss_functions sctp_ss_functions[]; +extern const struct sctp_cc_functions sctp_cc_functions[]; +extern const struct sctp_ss_functions sctp_ss_functions[]; void sctp_sblog(struct sockbuf *sb, struct sctp_tcb *stcb, int from, int incr) @@ -503,7 +503,7 @@ sctp_wakeup_log(struct sctp_tcb *stcb, uint32_t wake_cnt, int from) } void -sctp_log_block(uint8_t from, struct sctp_association *asoc, int sendlen) +sctp_log_block(uint8_t from, struct sctp_association *asoc, size_t sendlen) { struct sctp_cwnd_log sctp_clog; @@ -513,7 +513,7 @@ sctp_log_block(uint8_t from, struct sctp_association *asoc, int sendlen) sctp_clog.x.blk.stream_qcnt = (uint16_t) asoc->stream_queue_cnt; sctp_clog.x.blk.chunks_on_oque = (uint16_t) asoc->chunks_on_out_queue; sctp_clog.x.blk.flight_size = (uint16_t) (asoc->total_flight / 1024); - sctp_clog.x.blk.sndlen = sendlen; + sctp_clog.x.blk.sndlen = (uint32_t) sendlen; SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_BLOCK, from, @@ -2679,7 +2679,8 @@ sctp_notify_assoc_change(uint16_t state, struct sctp_tcb *stcb, struct mbuf *m_notify; struct sctp_assoc_change *sac; struct sctp_queued_to_read *control; - size_t notif_len, abort_len; + unsigned int notif_len; + uint16_t abort_len; unsigned int i; #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) @@ -2691,7 +2692,7 @@ sctp_notify_assoc_change(uint16_t state, struct sctp_tcb *stcb, return; } if (sctp_stcb_is_feature_on(stcb->sctp_ep, stcb, SCTP_PCB_FLAGS_RECVASSOCEVNT)) { - notif_len = sizeof(struct sctp_assoc_change); + notif_len = (unsigned int)sizeof(struct sctp_assoc_change); if (abort != NULL) { abort_len = ntohs(abort->ch.chunk_length); } else { @@ -2705,7 +2706,7 @@ sctp_notify_assoc_change(uint16_t state, struct sctp_tcb *stcb, m_notify = sctp_get_mbuf_for_msg(notif_len, 0, M_NOWAIT, 1, MT_DATA); if (m_notify == NULL) { /* Retry with smaller value. */ - notif_len = sizeof(struct sctp_assoc_change); + notif_len = (unsigned int)sizeof(struct sctp_assoc_change); m_notify = sctp_get_mbuf_for_msg(notif_len, 0, M_NOWAIT, 1, MT_DATA); if (m_notify == NULL) { goto set_error; @@ -3570,7 +3571,8 @@ sctp_notify_remote_error(struct sctp_tcb *stcb, uint16_t error, struct sctp_erro struct mbuf *m_notify; struct sctp_remote_error *sre; struct sctp_queued_to_read *control; - size_t notif_len, chunk_len; + unsigned int notif_len; + uint16_t chunk_len; if ((stcb == NULL) || sctp_stcb_is_feature_off(stcb->sctp_ep, stcb, SCTP_PCB_FLAGS_RECVPEERERR)) { @@ -3581,11 +3583,11 @@ sctp_notify_remote_error(struct sctp_tcb *stcb, uint16_t error, struct sctp_erro } else { chunk_len = 0; } - notif_len = sizeof(struct sctp_remote_error) + chunk_len; + notif_len = (unsigned int)(sizeof(struct sctp_remote_error) + chunk_len); m_notify = sctp_get_mbuf_for_msg(notif_len, 0, M_NOWAIT, 1, MT_DATA); if (m_notify == NULL) { /* Retry with smaller value. */ - notif_len = sizeof(struct sctp_remote_error); + notif_len = (unsigned int)sizeof(struct sctp_remote_error); m_notify = sctp_get_mbuf_for_msg(notif_len, 0, M_NOWAIT, 1, MT_DATA); if (m_notify == NULL) { return; @@ -4739,19 +4741,23 @@ sctp_generate_cause(uint16_t code, char *info) { struct mbuf *m; struct sctp_gen_error_cause *cause; - size_t info_len, len; + size_t info_len; + uint16_t len; if ((code == 0) || (info == NULL)) { return (NULL); } info_len = strlen(info); - len = sizeof(struct sctp_paramhdr) + info_len; + if (info_len > (SCTP_MAX_CAUSE_LENGTH - sizeof(struct sctp_paramhdr))) { + return (NULL); + } + len = (uint16_t) (sizeof(struct sctp_paramhdr) + info_len); m = sctp_get_mbuf_for_msg(len, 0, M_NOWAIT, 1, MT_DATA); if (m != NULL) { SCTP_BUF_LEN(m) = len; cause = mtod(m, struct sctp_gen_error_cause *); cause->code = htons(code); - cause->length = htons((uint16_t) len); + cause->length = htons(len); memcpy(cause->info, info, info_len); } return (m); @@ -4762,15 +4768,15 @@ sctp_generate_no_user_data_cause(uint32_t tsn) { struct mbuf *m; struct sctp_error_no_user_data *no_user_data_cause; - size_t len; + uint16_t len; - len = sizeof(struct sctp_error_no_user_data); + len = (uint16_t) sizeof(struct sctp_error_no_user_data); m = sctp_get_mbuf_for_msg(len, 0, M_NOWAIT, 1, MT_DATA); if (m != NULL) { SCTP_BUF_LEN(m) = len; no_user_data_cause = mtod(m, struct sctp_error_no_user_data *); no_user_data_cause->cause.code = htons(SCTP_CAUSE_NO_USER_DATA); - no_user_data_cause->cause.length = htons((uint16_t) len); + no_user_data_cause->cause.length = htons(len); no_user_data_cause->tsn = tsn; /* tsn is passed in as NBO */ } return (m); @@ -5295,7 +5301,7 @@ sctp_sorecvmsg(struct socket *so, uint32_t rwnd_req = 0; int hold_sblock = 0; int hold_rlock = 0; - int slen = 0; + ssize_t slen = 0; uint32_t held_length = 0; int sockbuf_lock = 0; @@ -5340,11 +5346,11 @@ sctp_sorecvmsg(struct socket *so, in_eeor_mode = sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXPLICIT_EOR); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_RECV_RWND_LOGGING_ENABLE) { sctp_misc_ints(SCTP_SORECV_ENTER, - rwnd_req, in_eeor_mode, so->so_rcv.sb_cc, uio->uio_resid); + rwnd_req, in_eeor_mode, so->so_rcv.sb_cc, (uint32_t) uio->uio_resid); } if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_RECV_RWND_LOGGING_ENABLE) { sctp_misc_ints(SCTP_SORECV_ENTERPL, - rwnd_req, block_allowed, so->so_rcv.sb_cc, uio->uio_resid); + rwnd_req, block_allowed, so->so_rcv.sb_cc, (uint32_t) uio->uio_resid); } error = sblock(&so->so_rcv, (block_allowed ? SBL_WAIT : 0)); if (error) { @@ -6219,13 +6225,13 @@ out: if (stcb) { sctp_misc_ints(SCTP_SORECV_DONE, freed_so_far, - ((uio) ? (slen - uio->uio_resid) : slen), + (uint32_t) ((uio) ? (slen - uio->uio_resid) : slen), stcb->asoc.my_rwnd, so->so_rcv.sb_cc); } else { sctp_misc_ints(SCTP_SORECV_DONE, freed_so_far, - ((uio) ? (slen - uio->uio_resid) : slen), + (uint32_t) ((uio) ? (slen - uio->uio_resid) : slen), 0, so->so_rcv.sb_cc); } @@ -6452,30 +6458,30 @@ out_now: struct sctp_tcb * sctp_connectx_helper_find(struct sctp_inpcb *inp, struct sockaddr *addr, - int *totaddr, int *num_v4, int *num_v6, int *error, - int limit, int *bad_addr) + unsigned int *totaddr, + unsigned int *num_v4, unsigned int *num_v6, int *error, + unsigned int limit, int *bad_addr) { struct sockaddr *sa; struct sctp_tcb *stcb = NULL; - size_t incr, at, i; + unsigned int incr, at, i; at = incr = 0; sa = addr; - *error = *num_v6 = *num_v4 = 0; /* account and validate addresses */ - for (i = 0; i < (size_t)*totaddr; i++) { + for (i = 0; i < *totaddr; i++) { switch (sa->sa_family) { #ifdef INET case AF_INET: - (*num_v4) += 1; - incr = sizeof(struct sockaddr_in); if (sa->sa_len != incr) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; *bad_addr = 1; return (NULL); } + (*num_v4) += 1; + incr = (unsigned int)sizeof(struct sockaddr_in); break; #endif #ifdef INET6 @@ -6491,14 +6497,14 @@ sctp_connectx_helper_find(struct sctp_inpcb *inp, struct sockaddr *addr, *bad_addr = 1; return (NULL); } - (*num_v6) += 1; - incr = sizeof(struct sockaddr_in6); if (sa->sa_len != incr) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; *bad_addr = 1; return (NULL); } + (*num_v6) += 1; + incr = (unsigned int)sizeof(struct sockaddr_in6); break; } #endif @@ -6507,7 +6513,7 @@ sctp_connectx_helper_find(struct sctp_inpcb *inp, struct sockaddr *addr, /* we are done */ break; } - if (i == (size_t)*totaddr) { + if (i == *totaddr) { break; } SCTP_INP_INCR_REF(inp); @@ -6518,7 +6524,7 @@ sctp_connectx_helper_find(struct sctp_inpcb *inp, struct sockaddr *addr, } else { SCTP_INP_DECR_REF(inp); } - if ((at + incr) > (size_t)limit) { + if ((at + incr) > limit) { *totaddr = i; break; } diff --git a/sys/netinet/sctputil.h b/sys/netinet/sctputil.h index 354d40e..2d32792 100644 --- a/sys/netinet/sctputil.h +++ b/sys/netinet/sctputil.h @@ -220,7 +220,8 @@ sctp_connectx_helper_add(struct sctp_tcb *stcb, struct sockaddr *addr, struct sctp_tcb * sctp_connectx_helper_find(struct sctp_inpcb *inp, struct sockaddr *addr, - int *totaddr, int *num_v4, int *num_v6, int *error, int limit, int *bad_addr); + unsigned int *totaddr, unsigned int *num_v4, unsigned int *num_v6, + int *error, unsigned int limit, int *bad_addr); int sctp_is_there_an_abort_here(struct mbuf *, int, uint32_t *); @@ -376,7 +377,7 @@ void sctp_log_closing(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int16_t loc void sctp_log_lock(struct sctp_inpcb *inp, struct sctp_tcb *stcb, uint8_t from); void sctp_log_maxburst(struct sctp_tcb *stcb, struct sctp_nets *, int, int, uint8_t); -void sctp_log_block(uint8_t, struct sctp_association *, int); +void sctp_log_block(uint8_t, struct sctp_association *, size_t); void sctp_log_rwnd(uint8_t, uint32_t, uint32_t, uint32_t); void sctp_log_rwnd_set(uint8_t, uint32_t, uint32_t, uint32_t, uint32_t); int sctp_fill_stat_log(void *, size_t *); diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index 102430d..7134ce4 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -235,16 +235,39 @@ VNET_DEFINE(struct inpcbhead, tcb); VNET_DEFINE(struct inpcbinfo, tcbinfo); /* - * TCP statistics are stored in an "array" of counter(9)s. + * TCP statistics are stored in an array of counter(9)s, which size matches + * size of struct tcpstat. TCP running connection count is a regular array. */ VNET_PCPUSTAT_DEFINE(struct tcpstat, tcpstat); -VNET_PCPUSTAT_SYSINIT(tcpstat); SYSCTL_VNET_PCPUSTAT(_net_inet_tcp, TCPCTL_STATS, stats, struct tcpstat, tcpstat, "TCP statistics (struct tcpstat, netinet/tcp_var.h)"); +VNET_DEFINE(counter_u64_t, tcps_states[TCP_NSTATES]); +SYSCTL_COUNTER_U64_ARRAY(_net_inet_tcp, TCPCTL_STATES, states, CTLFLAG_RD | + CTLFLAG_VNET, &VNET_NAME(tcps_states)[0], TCP_NSTATES, + "TCP connection counts by TCP state"); + +static void +tcp_vnet_init(const void *unused) +{ + + COUNTER_ARRAY_ALLOC(VNET(tcps_states), TCP_NSTATES, M_WAITOK); + VNET_PCPUSTAT_ALLOC(tcpstat, M_WAITOK); +} +VNET_SYSINIT(tcp_vnet_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, + tcp_vnet_init, NULL); #ifdef VIMAGE -VNET_PCPUSTAT_SYSUNINIT(tcpstat); +static void +tcp_vnet_uninit(const void *unused) +{ + + COUNTER_ARRAY_FREE(VNET(tcps_states), TCP_NSTATES); + VNET_PCPUSTAT_FREE(tcpstat); +} +VNET_SYSUNINIT(tcp_vnet_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, + tcp_vnet_uninit, NULL); #endif /* VIMAGE */ + /* * Kernel module interface for updating tcpstat. The argument is an index * into tcpstat treated as an array. diff --git a/sys/netinet/tcp_lro.c b/sys/netinet/tcp_lro.c index 7067abe..bd323bf 100644 --- a/sys/netinet/tcp_lro.c +++ b/sys/netinet/tcp_lro.c @@ -67,6 +67,8 @@ static MALLOC_DEFINE(M_LRO, "LRO", "LRO control structures"); #define TCP_LRO_INVALID_CSUM 0x0000 #endif +static void tcp_lro_rx_done(struct lro_ctrl *lc); + int tcp_lro_init(struct lro_ctrl *lc) { @@ -91,8 +93,8 @@ tcp_lro_init_args(struct lro_ctrl *lc, struct ifnet *ifp, lc->lro_ackcnt_lim = TCP_LRO_ACKCNT_MAX; lc->lro_length_lim = TCP_LRO_LENGTH_MAX; lc->ifp = ifp; - SLIST_INIT(&lc->lro_free); - SLIST_INIT(&lc->lro_active); + LIST_INIT(&lc->lro_free); + LIST_INIT(&lc->lro_active); /* compute size to allocate */ size = (lro_mbufs * sizeof(struct mbuf *)) + @@ -111,7 +113,7 @@ tcp_lro_init_args(struct lro_ctrl *lc, struct ifnet *ifp, /* setup linked list */ for (i = 0; i != lro_entries; i++) - SLIST_INSERT_HEAD(&lc->lro_free, le + i, next); + LIST_INSERT_HEAD(&lc->lro_free, le + i, next); return (0); } @@ -123,11 +125,11 @@ tcp_lro_free(struct lro_ctrl *lc) unsigned x; /* reset LRO free list */ - SLIST_INIT(&lc->lro_free); + LIST_INIT(&lc->lro_free); /* free active mbufs, if any */ - while ((le = SLIST_FIRST(&lc->lro_active)) != NULL) { - SLIST_REMOVE_HEAD(&lc->lro_active, next); + while ((le = LIST_FIRST(&lc->lro_active)) != NULL) { + LIST_REMOVE(le, next); m_freem(le->m_head); } @@ -226,20 +228,31 @@ tcp_lro_rx_csum_fixup(struct lro_entry *le, void *l3hdr, struct tcphdr *th, } #endif +static void +tcp_lro_rx_done(struct lro_ctrl *lc) +{ + struct lro_entry *le; + + while ((le = LIST_FIRST(&lc->lro_active)) != NULL) { + LIST_REMOVE(le, next); + tcp_lro_flush(lc, le); + } +} + void tcp_lro_flush_inactive(struct lro_ctrl *lc, const struct timeval *timeout) { struct lro_entry *le, *le_tmp; struct timeval tv; - if (SLIST_EMPTY(&lc->lro_active)) + if (LIST_EMPTY(&lc->lro_active)) return; getmicrotime(&tv); timevalsub(&tv, timeout); - SLIST_FOREACH_SAFE(le, &lc->lro_active, next, le_tmp) { + LIST_FOREACH_SAFE(le, &lc->lro_active, next, le_tmp) { if (timevalcmp(&tv, &le->mtime, >=)) { - SLIST_REMOVE(&lc->lro_active, le, lro_entry, next); + LIST_REMOVE(le, next); tcp_lro_flush(lc, le); } } @@ -335,7 +348,7 @@ tcp_lro_flush(struct lro_ctrl *lc, struct lro_entry *le) lc->lro_queued += le->append_cnt + 1; lc->lro_flushed++; bzero(le, sizeof(*le)); - SLIST_INSERT_HEAD(&lc->lro_free, le, next); + LIST_INSERT_HEAD(&lc->lro_free, le, next); } static int @@ -362,13 +375,12 @@ done: void tcp_lro_flush_all(struct lro_ctrl *lc) { - struct lro_entry *le; uint32_t hashtype; uint32_t flowid; unsigned x; /* check if no mbufs to flush */ - if (__predict_false(lc->lro_mbuf_count == 0)) + if (lc->lro_mbuf_count == 0) goto done; /* sort all mbufs according to stream */ @@ -390,10 +402,7 @@ tcp_lro_flush_all(struct lro_ctrl *lc) hashtype = M_HASHTYPE_GET(mb); /* flush active streams */ - while ((le = SLIST_FIRST(&lc->lro_active)) != NULL) { - SLIST_REMOVE_HEAD(&lc->lro_active, next); - tcp_lro_flush(lc, le); - } + tcp_lro_rx_done(lc); } #ifdef TCP_LRO_RESET_SEQUENCE /* reset sequence number */ @@ -409,10 +418,8 @@ tcp_lro_flush_all(struct lro_ctrl *lc) } done: /* flush active streams */ - while ((le = SLIST_FIRST(&lc->lro_active)) != NULL) { - SLIST_REMOVE_HEAD(&lc->lro_active, next); - tcp_lro_flush(lc, le); - } + tcp_lro_rx_done(lc); + lc->lro_mbuf_count = 0; } @@ -586,7 +593,7 @@ tcp_lro_rx(struct lro_ctrl *lc, struct mbuf *m, uint32_t csum) seq = ntohl(th->th_seq); /* Try to find a matching previous segment. */ - SLIST_FOREACH(le, &lc->lro_active, next) { + LIST_FOREACH(le, &lc->lro_active, next) { if (le->eh_type != eh_type) continue; if (le->source_port != th->th_sport || @@ -613,7 +620,7 @@ tcp_lro_rx(struct lro_ctrl *lc, struct mbuf *m, uint32_t csum) /* Flush now if appending will result in overflow. */ if (le->p_len > (lc->lro_length_lim - tcp_data_len)) { - SLIST_REMOVE(&lc->lro_active, le, lro_entry, next); + LIST_REMOVE(le, next); tcp_lro_flush(lc, le); break; } @@ -622,7 +629,7 @@ tcp_lro_rx(struct lro_ctrl *lc, struct mbuf *m, uint32_t csum) if (__predict_false(seq != le->next_seq || (tcp_data_len == 0 && le->ack_seq == th->th_ack))) { /* Out of order packet or duplicate ACK. */ - SLIST_REMOVE(&lc->lro_active, le, lro_entry, next); + LIST_REMOVE(le, next); tcp_lro_flush(lc, le); return (TCP_LRO_CANNOT); } @@ -655,8 +662,7 @@ tcp_lro_rx(struct lro_ctrl *lc, struct mbuf *m, uint32_t csum) * be further delayed. */ if (le->append_cnt >= lc->lro_ackcnt_lim) { - SLIST_REMOVE(&lc->lro_active, le, lro_entry, - next); + LIST_REMOVE(le, next); tcp_lro_flush(lc, le); } return (0); @@ -680,7 +686,7 @@ tcp_lro_rx(struct lro_ctrl *lc, struct mbuf *m, uint32_t csum) * overflow, pro-actively flush now. */ if (le->p_len > (lc->lro_length_lim - lc->ifp->if_mtu)) { - SLIST_REMOVE(&lc->lro_active, le, lro_entry, next); + LIST_REMOVE(le, next); tcp_lro_flush(lc, le); } else getmicrotime(&le->mtime); @@ -689,13 +695,13 @@ tcp_lro_rx(struct lro_ctrl *lc, struct mbuf *m, uint32_t csum) } /* Try to find an empty slot. */ - if (SLIST_EMPTY(&lc->lro_free)) - return (TCP_LRO_CANNOT); + if (LIST_EMPTY(&lc->lro_free)) + return (TCP_LRO_NO_ENTRIES); /* Start a new segment chain. */ - le = SLIST_FIRST(&lc->lro_free); - SLIST_REMOVE_HEAD(&lc->lro_free, next); - SLIST_INSERT_HEAD(&lc->lro_active, le, next); + le = LIST_FIRST(&lc->lro_free); + LIST_REMOVE(le, next); + LIST_INSERT_HEAD(&lc->lro_active, le, next); getmicrotime(&le->mtime); /* Start filling in details. */ diff --git a/sys/netinet/tcp_lro.h b/sys/netinet/tcp_lro.h index 3fc627c..b81a950 100644 --- a/sys/netinet/tcp_lro.h +++ b/sys/netinet/tcp_lro.h @@ -41,9 +41,8 @@ #define TCP_LRO_SEQUENCE(mb) \ (mb)->m_pkthdr.PH_loc.thirtytwo[0] -struct lro_entry -{ - SLIST_ENTRY(lro_entry) next; +struct lro_entry { + LIST_ENTRY(lro_entry) next; struct mbuf *m_head; struct mbuf *m_tail; union { @@ -72,7 +71,7 @@ struct lro_entry uint16_t timestamp; /* flag, not a TCP hdr field. */ struct timeval mtime; }; -SLIST_HEAD(lro_head, lro_entry); +LIST_HEAD(lro_head, lro_entry); #define le_ip4 leip.ip4 #define le_ip6 leip.ip6 @@ -110,6 +109,7 @@ void tcp_lro_flush_all(struct lro_ctrl *); int tcp_lro_rx(struct lro_ctrl *, struct mbuf *, uint32_t); void tcp_lro_queue_mbuf(struct lro_ctrl *, struct mbuf *); +#define TCP_LRO_NO_ENTRIES -2 #define TCP_LRO_CANNOT -1 #define TCP_LRO_NOT_SUPPORTED 1 diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c index 96a8700..2043fc9 100644 --- a/sys/netinet/tcp_output.c +++ b/sys/netinet/tcp_output.c @@ -1379,9 +1379,6 @@ send: #endif #ifdef INET { - struct route ro; - - bzero(&ro, sizeof(ro)); ip->ip_len = htons(m->m_pkthdr.len); #ifdef INET6 if (tp->t_inpcb->inp_vflag & INP_IPV6PROTO) @@ -1412,13 +1409,12 @@ send: tcp_pcap_add(th, m, &(tp->t_outpkts)); #endif - error = ip_output(m, tp->t_inpcb->inp_options, &ro, + error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route, ((so->so_options & SO_DONTROUTE) ? IP_ROUTETOIF : 0), 0, tp->t_inpcb); - if (error == EMSGSIZE && ro.ro_rt != NULL) - mtu = ro.ro_rt->rt_mtu; - RO_RTFREE(&ro); + if (error == EMSGSIZE && tp->t_inpcb->inp_route.ro_rt != NULL) + mtu = tp->t_inpcb->inp_route.ro_rt->rt_mtu; } #endif /* INET */ @@ -1652,7 +1648,7 @@ tcp_setpersist(struct tcpcb *tp) int tcp_addoptions(struct tcpopt *to, u_char *optp) { - u_int mask, optlen = 0; + u_int32_t mask, optlen = 0; for (mask = 1; mask < TOF_MAXOPT; mask <<= 1) { if ((to->to_flags & mask) != mask) diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index ff218dd..33b16ad 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -1542,7 +1542,7 @@ tcp_close(struct tcpcb *tp) #endif in_pcbdrop(inp); TCPSTAT_INC(tcps_closed); - TCPSTAT_DEC(tcps_states[tp->t_state]); + TCPSTATES_DEC(tp->t_state); KASSERT(inp->inp_socket != NULL, ("tcp_close: inp_socket NULL")); so = inp->inp_socket; soisdisconnected(so); @@ -1632,6 +1632,10 @@ tcp_notify(struct inpcb *inp, int error) if (tp->t_state == TCPS_ESTABLISHED && (error == EHOSTUNREACH || error == ENETUNREACH || error == EHOSTDOWN)) { + if (inp->inp_route.ro_rt) { + RTFREE(inp->inp_route.ro_rt); + inp->inp_route.ro_rt = (struct rtentry *)NULL; + } return (inp); } else if (tp->t_state < TCPS_ESTABLISHED && tp->t_rxtshift > 3 && tp->t_softerror) { @@ -1665,7 +1669,7 @@ tcp_pcblist(SYSCTL_HANDLER_ARGS) */ if (req->oldptr == NULL) { n = V_tcbinfo.ipi_count + - TCPSTAT_FETCH(tcps_states[TCPS_SYN_RECEIVED]); + counter_u64_fetch(VNET(tcps_states)[TCPS_SYN_RECEIVED]); n += imax(n / 8, 10); req->oldidx = 2 * (sizeof xig) + n * sizeof(struct xtcpcb); return (0); @@ -1682,7 +1686,7 @@ tcp_pcblist(SYSCTL_HANDLER_ARGS) n = V_tcbinfo.ipi_count; INP_LIST_RUNLOCK(&V_tcbinfo); - m = TCPSTAT_FETCH(tcps_states[TCPS_SYN_RECEIVED]); + m = counter_u64_fetch(VNET(tcps_states)[TCPS_SYN_RECEIVED]); error = sysctl_wire_old_buffer(req, 2 * (sizeof xig) + (n + m) * sizeof(struct xtcpcb)); @@ -1702,8 +1706,6 @@ tcp_pcblist(SYSCTL_HANDLER_ARGS) return (error); inp_list = malloc(n * sizeof *inp_list, M_TEMP, M_WAITOK); - if (inp_list == NULL) - return (ENOMEM); INP_INFO_WLOCK(&V_tcbinfo); for (inp = LIST_FIRST(V_tcbinfo.ipi_listhead), i = 0; @@ -1926,11 +1928,11 @@ tcp_ctlinput(int cmd, struct sockaddr *sa, void *vip) else if (V_icmp_may_rst && (cmd == PRC_UNREACH_ADMIN_PROHIB || cmd == PRC_UNREACH_PORT || cmd == PRC_TIMXCEED_INTRANS) && ip) notify = tcp_drop_syn_sent; - /* - * Redirects don't need to be handled up here. - */ - else if (PRC_IS_REDIRECT(cmd)) + else if (PRC_IS_REDIRECT(cmd)) { + /* signal EHOSTDOWN, as it flushes the cached route */ + in_pcbnotifyall(&V_tcbinfo, faddr, EHOSTDOWN, notify); return; + } /* * Hostdead is ugly because it goes linearly through all PCBs. * XXX: We never get this from ICMP, otherwise it makes an @@ -2986,8 +2988,8 @@ tcp_state_change(struct tcpcb *tp, int newstate) int pstate = tp->t_state; #endif - TCPSTAT_DEC(tcps_states[tp->t_state]); - TCPSTAT_INC(tcps_states[newstate]); + TCPSTATES_DEC(tp->t_state); + TCPSTATES_INC(newstate); tp->t_state = newstate; TCP_PROBE6(state__change, NULL, tp, NULL, tp, NULL, pstate); } diff --git a/sys/netinet/tcp_syncache.c b/sys/netinet/tcp_syncache.c index 0ff7318..b898c49 100644 --- a/sys/netinet/tcp_syncache.c +++ b/sys/netinet/tcp_syncache.c @@ -351,7 +351,7 @@ syncache_insert(struct syncache *sc, struct syncache_head *sch) SCH_UNLOCK(sch); - TCPSTAT_INC(tcps_states[TCPS_SYN_RECEIVED]); + TCPSTATES_INC(TCPS_SYN_RECEIVED); TCPSTAT_INC(tcps_sc_added); } @@ -365,7 +365,7 @@ syncache_drop(struct syncache *sc, struct syncache_head *sch) SCH_LOCK_ASSERT(sch); - TCPSTAT_DEC(tcps_states[TCPS_SYN_RECEIVED]); + TCPSTATES_DEC(TCPS_SYN_RECEIVED); TAILQ_REMOVE(&sch->sch_bucket, sc, sc_hash); sch->sch_length--; @@ -1003,7 +1003,7 @@ syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th, * sonewconn->tcp_usr_attach in TCPS_CLOSED state, then * syncache_socket() will change it to TCPS_SYN_RECEIVED. */ - TCPSTAT_DEC(tcps_states[TCPS_SYN_RECEIVED]); + TCPSTATES_DEC(TCPS_SYN_RECEIVED); TAILQ_REMOVE(&sch->sch_bucket, sc, sc_hash); sch->sch_length--; #ifdef TCP_OFFLOAD diff --git a/sys/netinet/tcp_timer.c b/sys/netinet/tcp_timer.c index 046feba..172c394 100644 --- a/sys/netinet/tcp_timer.c +++ b/sys/netinet/tcp_timer.c @@ -786,7 +786,9 @@ tcp_timer_rexmt(void * xtp) #ifdef INET6 if ((tp->t_inpcb->inp_vflag & INP_IPV6) != 0) in6_losing(tp->t_inpcb); + else #endif + in_losing(tp->t_inpcb); tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT); tp->t_srtt = 0; } diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c index c98de24..ff36ce7 100644 --- a/sys/netinet/tcp_timewait.c +++ b/sys/netinet/tcp_timewait.c @@ -660,7 +660,7 @@ tcp_tw_2msl_stop(struct tcptw *tw, int reuse) if (!reuse) uma_zfree(V_tcptw_zone, tw); - TCPSTAT_DEC(tcps_states[TCPS_TIME_WAIT]); + TCPSTATES_DEC(TCPS_TIME_WAIT); } struct tcptw * diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index 09493f0..6a3cde6 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -1883,7 +1883,7 @@ tcp_attach(struct socket *so) tp->t_state = TCPS_CLOSED; INP_WUNLOCK(inp); INP_INFO_RUNLOCK(&V_tcbinfo); - TCPSTAT_INC(tcps_states[TCPS_CLOSED]); + TCPSTATES_INC(TCPS_CLOSED); return (0); } diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h index 2f23881..d60ad9f 100644 --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -364,7 +364,7 @@ struct tcpcb { * options in tcp_addoptions. */ struct tcpopt { - u_int64_t to_flags; /* which options are present */ + u_int32_t to_flags; /* which options are present */ #define TOF_MSS 0x0001 /* maximum segment size */ #define TOF_SCALE 0x0002 /* window scaling */ #define TOF_SACKPERM 0x0004 /* SACK permitted */ @@ -588,9 +588,6 @@ struct tcpstat { uint64_t tcps_sig_err_sigopt; /* No signature expected by socket */ uint64_t tcps_sig_err_nosigopt; /* No signature provided by segment */ - /* Running connection count. */ - uint64_t tcps_states[TCP_NSTATES]; - uint64_t _pad[12]; /* 6 UTO, 6 TBD */ }; @@ -609,9 +606,6 @@ VNET_PCPUSTAT_DECLARE(struct tcpstat, tcpstat); /* tcp statistics */ #define TCPSTAT_ADD(name, val) \ VNET_PCPUSTAT_ADD(struct tcpstat, tcpstat, name, (val)) #define TCPSTAT_INC(name) TCPSTAT_ADD(name, 1) -#define TCPSTAT_DEC(name) TCPSTAT_ADD(name, -1) -#define TCPSTAT_FETCH(name) VNET_PCPUSTAT_FETCH(struct tcpstat, tcpstat, \ - name) /* * Kernel module consumers must use this accessor macro. @@ -621,6 +615,13 @@ void kmod_tcpstat_inc(int statnum); kmod_tcpstat_inc(offsetof(struct tcpstat, name) / sizeof(uint64_t)) /* + * Running TCP connection count by state. + */ +VNET_DECLARE(counter_u64_t, tcps_states[TCP_NSTATES]); +#define TCPSTATES_INC(state) counter_u64_add(VNET(tcps_states)[state], 1) +#define TCPSTATES_DEC(state) counter_u64_add(VNET(tcps_states)[state], -1) + +/* * TCP specific helper hook point identifiers. */ #define HHOOK_TCP_EST_IN 0 @@ -678,6 +679,7 @@ struct xtcpcb { #define TCPCTL_V6MSSDFLT 13 /* MSS default for IPv6 */ #define TCPCTL_SACK 14 /* Selective Acknowledgement,rfc 2018 */ #define TCPCTL_DROP 15 /* drop tcp connection */ +#define TCPCTL_STATES 16 /* connection counts by TCP state */ #ifdef _KERNEL #ifdef SYSCTL_DECL diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index f474a54..56aa56f 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -740,6 +740,11 @@ udp_notify(struct inpcb *inp, int errno) * or a write lock, but a read lock is sufficient. */ INP_LOCK_ASSERT(inp); + if ((errno == EHOSTUNREACH || errno == ENETUNREACH || + errno == EHOSTDOWN) && inp->inp_route.ro_rt) { + RTFREE(inp->inp_route.ro_rt); + inp->inp_route.ro_rt = (struct rtentry *)NULL; + } inp->inp_socket->so_error = errno; sorwakeup(inp->inp_socket); @@ -761,11 +766,11 @@ udp_common_ctlinput(int cmd, struct sockaddr *sa, void *vip, if (sa->sa_family != AF_INET || faddr.s_addr == INADDR_ANY) return; - /* - * Redirects don't need to be handled up here. - */ - if (PRC_IS_REDIRECT(cmd)) + if (PRC_IS_REDIRECT(cmd)) { + /* signal EHOSTDOWN, as it flushes the cached route */ + in_pcbnotifyall(&V_udbinfo, faddr, EHOSTDOWN, udp_notify); return; + } /* * Hostdead is ugly because it goes linearly through all PCBs. @@ -1116,7 +1121,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr, int error = 0; int ipflags; u_short fport, lport; - int unlock_udbinfo; + int unlock_udbinfo, unlock_inp; u_char tos; uint8_t pr; uint16_t cscov = 0; @@ -1137,7 +1142,15 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr, } src.sin_family = 0; - INP_RLOCK(inp); + sin = (struct sockaddr_in *)addr; + if (sin == NULL || + (inp->inp_laddr.s_addr == INADDR_ANY && inp->inp_lport == 0)) { + INP_WLOCK(inp); + unlock_inp = UH_WLOCKED; + } else { + INP_RLOCK(inp); + unlock_inp = UH_RLOCKED; + } tos = inp->inp_ip_tos; if (control != NULL) { /* @@ -1145,7 +1158,10 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr, * stored in a single mbuf. */ if (control->m_next) { - INP_RUNLOCK(inp); + if (unlock_inp == UH_WLOCKED) + INP_WUNLOCK(inp); + else + INP_RUNLOCK(inp); m_freem(control); m_freem(m); return (EINVAL); @@ -1220,7 +1236,10 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr, m_freem(control); } if (error) { - INP_RUNLOCK(inp); + if (unlock_inp == UH_WLOCKED) + INP_WUNLOCK(inp); + else + INP_RUNLOCK(inp); m_freem(m); return (error); } @@ -1246,8 +1265,6 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr, sin = (struct sockaddr_in *)addr; if (sin != NULL && (inp->inp_laddr.s_addr == INADDR_ANY && inp->inp_lport == 0)) { - INP_RUNLOCK(inp); - INP_WLOCK(inp); INP_HASH_WLOCK(pcbinfo); unlock_udbinfo = UH_WLOCKED; } else if ((sin != NULL && ( @@ -1514,9 +1531,10 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr, else if (unlock_udbinfo == UH_RLOCKED) INP_HASH_RUNLOCK(pcbinfo); UDP_PROBE(send, NULL, inp, &ui->ui_i, inp, &ui->ui_u); - error = ip_output(m, inp->inp_options, NULL, ipflags, + error = ip_output(m, inp->inp_options, + (unlock_inp == UH_WLOCKED ? &inp->inp_route : NULL), ipflags, inp->inp_moptions, inp); - if (unlock_udbinfo == UH_WLOCKED) + if (unlock_inp == UH_WLOCKED) INP_WUNLOCK(inp); else INP_RUNLOCK(inp); diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 393bb7e..797093a 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -2367,7 +2367,7 @@ in6_lltable_dump_entry(struct lltable *llt, struct llentry *lle, sdl->sdl_alen = ifp->if_addrlen; sdl->sdl_index = ifp->if_index; sdl->sdl_type = ifp->if_type; - bcopy(&lle->ll_addr, LLADDR(sdl), ifp->if_addrlen); + bcopy(lle->ll_addr, LLADDR(sdl), ifp->if_addrlen); if (lle->la_expire != 0) ndpc.rtm.rtm_rmx.rmx_expire = lle->la_expire + lle->lle_remtime / hz + diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c index 64981b3..471dd82 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -890,3 +890,29 @@ in6_purgemaddrs(struct ifnet *ifp) IN6_MULTI_UNLOCK(); } + +void +in6_ifattach_destroy(void) +{ + + callout_drain(&V_in6_tmpaddrtimer_ch); +} + +static void +in6_ifattach_init(void *dummy) +{ + + /* Timer for regeneranation of temporary addresses randomize ID. */ + callout_init(&V_in6_tmpaddrtimer_ch, 0); + callout_reset(&V_in6_tmpaddrtimer_ch, + (V_ip6_temp_preferred_lifetime - V_ip6_desync_factor - + V_ip6_temp_regen_advance) * hz, + in6_tmpaddrtimer, curvnet); +} + +/* + * Cheat. + * This must be after route_init(), which is now SI_ORDER_THIRD. + */ +SYSINIT(in6_ifattach_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, + in6_ifattach_init, NULL); diff --git a/sys/netinet6/in6_ifattach.h b/sys/netinet6/in6_ifattach.h index af62731..a5a82c0 100644 --- a/sys/netinet6/in6_ifattach.h +++ b/sys/netinet6/in6_ifattach.h @@ -35,6 +35,7 @@ #ifdef _KERNEL void in6_ifattach(struct ifnet *, struct ifnet *); +void in6_ifattach_destroy(void); void in6_ifdetach(struct ifnet *); int in6_get_tmpifid(struct ifnet *, u_int8_t *, const u_int8_t *, int); void in6_tmpaddrtimer(void *); diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index a779b8d..98f4cd3 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -827,9 +827,10 @@ void in6_losing(struct inpcb *in6p) { - /* - * We don't store route pointers in the routing table anymore - */ + if (in6p->inp_route6.ro_rt) { + RTFREE(in6p->inp_route6.ro_rt); + in6p->inp_route6.ro_rt = (struct rtentry *)NULL; + } return; } @@ -840,9 +841,11 @@ in6_losing(struct inpcb *in6p) struct inpcb * in6_rtchange(struct inpcb *inp, int errno) { - /* - * We don't store route pointers in the routing table anymore - */ + + if (inp->inp_route6.ro_rt) { + RTFREE(inp->inp_route6.ro_rt); + inp->inp_route6.ro_rt = (struct rtentry *)NULL; + } return inp; } diff --git a/sys/netinet6/in6_pcbgroup.c b/sys/netinet6/in6_pcbgroup.c index 694de99..4a68432 100644 --- a/sys/netinet6/in6_pcbgroup.c +++ b/sys/netinet6/in6_pcbgroup.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/mbuf.h> +#include <sys/socket.h> #include <net/rss_config.h> diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c index 4029402..40b4723 100644 --- a/sys/netinet6/in6_src.c +++ b/sys/netinet6/in6_src.c @@ -226,9 +226,6 @@ in6_selectsrc(uint32_t fibnum, struct sockaddr_in6 *dstsock, */ if (opts && (pi = opts->ip6po_pktinfo) && !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr)) { - struct sockaddr_in6 srcsock; - struct in6_ifaddr *ia6; - /* get the outgoing interface */ if ((error = in6_selectif(dstsock, opts, mopts, &ifp, oifp, fibnum)) @@ -242,33 +239,36 @@ in6_selectsrc(uint32_t fibnum, struct sockaddr_in6 *dstsock, * the interface must be specified; otherwise, ifa_ifwithaddr() * will fail matching the address. */ - bzero(&srcsock, sizeof(srcsock)); - srcsock.sin6_family = AF_INET6; - srcsock.sin6_len = sizeof(srcsock); - srcsock.sin6_addr = pi->ipi6_addr; + tmp = pi->ipi6_addr; if (ifp) { - error = in6_setscope(&srcsock.sin6_addr, ifp, NULL); + error = in6_setscope(&tmp, ifp, &odstzone); if (error) return (error); } if (cred != NULL && (error = prison_local_ip6(cred, - &srcsock.sin6_addr, (inp != NULL && - (inp->inp_flags & IN6P_IPV6_V6ONLY) != 0))) != 0) + &tmp, (inp->inp_flags & IN6P_IPV6_V6ONLY) != 0)) != 0) return (error); - ia6 = (struct in6_ifaddr *)ifa_ifwithaddr( - (struct sockaddr *)&srcsock); - if (ia6 == NULL || - (ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) { - if (ia6 != NULL) - ifa_free(&ia6->ia_ifa); - return (EADDRNOTAVAIL); - } - pi->ipi6_addr = srcsock.sin6_addr; /* XXX: this overrides pi */ + /* + * If IPV6_BINDANY socket option is set, we allow to specify + * non local addresses as source address in IPV6_PKTINFO + * ancillary data. + */ + if ((inp->inp_flags & INP_BINDANY) == 0) { + ia = in6ifa_ifwithaddr(&tmp, odstzone); + if (ia == NULL || (ia->ia6_flags & (IN6_IFF_ANYCAST | + IN6_IFF_NOTREADY))) { + if (ia != NULL) + ifa_free(&ia->ia_ifa); + return (EADDRNOTAVAIL); + } + bcopy(&ia->ia_addr.sin6_addr, srcp, sizeof(*srcp)); + ifa_free(&ia->ia_ifa); + } else + bcopy(&tmp, srcp, sizeof(*srcp)); + pi->ipi6_addr = tmp; /* XXX: this overrides pi */ if (ifpp) *ifpp = ifp; - bcopy(&ia6->ia_addr.sin6_addr, srcp, sizeof(*srcp)); - ifa_free(&ia6->ia_ifa); return (0); } diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index e6c16a9..6b49b26 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -156,9 +156,6 @@ static struct netisr_handler ip6_direct_nh = { }; #endif -VNET_DECLARE(struct callout, in6_tmpaddrtimer_ch); -#define V_in6_tmpaddrtimer_ch VNET(in6_tmpaddrtimer_ch) - VNET_DEFINE(struct pfil_head, inet6_pfil_hook); VNET_PCPUSTAT_DEFINE(struct ip6stat, ip6stat); @@ -170,7 +167,6 @@ VNET_PCPUSTAT_SYSUNINIT(ip6stat); struct rmlock in6_ifaddr_lock; RM_SYSINIT(in6_ifaddr_lock, &in6_ifaddr_lock, "in6_ifaddr_lock"); -static void ip6_init2(void *); static int ip6_hopopts_input(u_int32_t *, u_int32_t *, struct mbuf **, int *); #ifdef PULLDOWN_TEST static struct mbuf *ip6_pullexthdr(struct mbuf *, size_t, int); @@ -331,40 +327,11 @@ ip6_destroy() } hashdestroy(V_in6_ifaddrhashtbl, M_IFADDR, V_in6_ifaddrhmask); nd6_destroy(); - callout_drain(&V_in6_tmpaddrtimer_ch); + in6_ifattach_destroy(); } #endif static int -ip6_init2_vnet(const void *unused __unused) -{ - - /* nd6_timer_init */ - callout_init(&V_nd6_timer_ch, 0); - callout_reset(&V_nd6_timer_ch, hz, nd6_timer, curvnet); - - /* timer for regeneranation of temporary addresses randomize ID */ - callout_init(&V_in6_tmpaddrtimer_ch, 0); - callout_reset(&V_in6_tmpaddrtimer_ch, - (V_ip6_temp_preferred_lifetime - V_ip6_desync_factor - - V_ip6_temp_regen_advance) * hz, - in6_tmpaddrtimer, curvnet); - - return (0); -} - -static void -ip6_init2(void *dummy) -{ - - ip6_init2_vnet(NULL); -} - -/* cheat */ -/* This must be after route_init(), which is now SI_ORDER_THIRD */ -SYSINIT(netinet6init2, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, ip6_init2, NULL); - -static int ip6_input_hbh(struct mbuf *m, uint32_t *plen, uint32_t *rtalert, int *off, int *nxt, int *ours) { diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index 3a47285..7895139 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -546,7 +546,18 @@ again: /* adjust pointer */ ip6 = mtod(m, struct ip6_hdr *); - if (ro->ro_rt && fwd_tag == NULL) { + /* + * Validate route against routing table additions; + * a better/more specific route might have been added. + * Make sure address family is set in route. + */ + if (inp) { + ro->ro_dst.sin6_family = AF_INET6; + RT_VALIDATE((struct route *)ro, &inp->inp_rt_cookie, fibnum); + } + if (ro->ro_rt && fwd_tag == NULL && (ro->ro_rt->rt_flags & RTF_UP) && + ro->ro_dst.sin6_family == AF_INET6 && + IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, &ip6->ip6_dst)) { rt = ro->ro_rt; ifp = ro->ro_rt->rt_ifp; } else { @@ -939,7 +950,8 @@ passout: m->m_pkthdr.len); ifa_free(&ia6->ia_ifa); } - error = nd6_output_ifp(ifp, origifp, m, dst, NULL); + error = nd6_output_ifp(ifp, origifp, m, dst, + (struct route *)ro); goto done; } @@ -1038,7 +1050,8 @@ sendorfree: counter_u64_add(ia->ia_ifa.ifa_obytes, m->m_pkthdr.len); } - error = nd6_output_ifp(ifp, origifp, m, dst, NULL); + error = nd6_output_ifp(ifp, origifp, m, dst, + (struct route *)ro); } else m_freem(m); } diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 60693e1..8b1da24 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -127,7 +127,7 @@ static int nd6_is_new_addr_neighbor(const struct sockaddr_in6 *, static void nd6_setmtu0(struct ifnet *, struct nd_ifinfo *); static void nd6_slowtimo(void *); static int regen_tmpaddr(struct in6_ifaddr *); -static void nd6_free(struct llentry *, int); +static void nd6_free(struct llentry **, int); static void nd6_free_redirect(const struct llentry *); static void nd6_llinfo_timer(void *); static void nd6_llinfo_settimer_locked(struct llentry *, long); @@ -142,6 +142,7 @@ static VNET_DEFINE(struct callout, nd6_slowtimo_ch); #define V_nd6_slowtimo_ch VNET(nd6_slowtimo_ch) VNET_DEFINE(struct callout, nd6_timer_ch); +#define V_nd6_timer_ch VNET(nd6_timer_ch) static void nd6_lle_event(void *arg __unused, struct llentry *lle, int evt) @@ -213,11 +214,14 @@ nd6_init(void) /* initialization of the default router list */ TAILQ_INIT(&V_nd_defrouter); - /* start timer */ + /* Start timers. */ callout_init(&V_nd6_slowtimo_ch, 0); callout_reset(&V_nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz, nd6_slowtimo, curvnet); + callout_init(&V_nd6_timer_ch, 0); + callout_reset(&V_nd6_timer_ch, hz, nd6_timer, curvnet); + nd6_dad_init(); if (IS_DEFAULT_VNET(curvnet)) { lle_event_eh = EVENTHANDLER_REGISTER(lle_event, nd6_lle_event, @@ -723,12 +727,16 @@ nd6_llinfo_timer(void *arg) struct llentry *ln; struct in6_addr *dst, *pdst, *psrc, src; struct ifnet *ifp; - struct nd_ifinfo *ndi = NULL; + struct nd_ifinfo *ndi; int do_switch, send_ns; long delay; KASSERT(arg != NULL, ("%s: arg NULL", __func__)); ln = (struct llentry *)arg; + ifp = lltable_get_ifp(ln->lle_tbl); + CURVNET_SET(ifp->if_vnet); + + ND6_RLOCK(); LLE_WLOCK(ln); if (callout_pending(&ln->lle_timer)) { /* @@ -748,10 +756,10 @@ nd6_llinfo_timer(void *arg) * would have been 1. */ LLE_WUNLOCK(ln); + ND6_RUNLOCK(); + CURVNET_RESTORE(); return; } - ifp = ln->lle_tbl->llt_ifp; - CURVNET_SET(ifp->if_vnet); ndi = ND_IFINFO(ifp); send_ns = 0; dst = &ln->r_l3addr.addr6; @@ -773,8 +781,7 @@ nd6_llinfo_timer(void *arg) } if (ln->la_flags & LLE_DELETED) { - nd6_free(ln, 0); - ln = NULL; + nd6_free(&ln, 0); goto done; } @@ -799,9 +806,7 @@ nd6_llinfo_timer(void *arg) ln->la_hold = m0; clear_llinfo_pqueue(ln); } - EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_TIMEDOUT); - nd6_free(ln, 0); - ln = NULL; + nd6_free(&ln, 0); if (m != NULL) icmp6_error2(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0, ifp); @@ -830,12 +835,8 @@ nd6_llinfo_timer(void *arg) * GC timer has ended and entry hasn't been used. * Run Garbage collector (RFC 4861, 5.3) */ - if (!ND6_LLINFO_PERMANENT(ln)) { - EVENTHANDLER_INVOKE(lle_event, ln, - LLENTRY_EXPIRED); - nd6_free(ln, 1); - ln = NULL; - } + if (!ND6_LLINFO_PERMANENT(ln)) + nd6_free(&ln, 1); break; } @@ -857,9 +858,7 @@ nd6_llinfo_timer(void *arg) ln->la_asked++; send_ns = 1; } else { - EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_EXPIRED); - nd6_free(ln, 0); - ln = NULL; + nd6_free(&ln, 0); } break; default: @@ -867,6 +866,8 @@ nd6_llinfo_timer(void *arg) __func__, ln->ln_state); } done: + if (ln != NULL) + ND6_RUNLOCK(); if (send_ns != 0) { nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000); psrc = nd6_llinfo_get_holdsrc(ln, &src); @@ -1363,12 +1364,27 @@ nd6_is_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp) * Set noinline to be dtrace-friendly */ static __noinline void -nd6_free(struct llentry *ln, int gc) +nd6_free(struct llentry **lnp, int gc) { - struct nd_defrouter *dr; struct ifnet *ifp; + struct llentry *ln; + struct nd_defrouter *dr; + + ln = *lnp; + *lnp = NULL; LLE_WLOCK_ASSERT(ln); + ND6_RLOCK_ASSERT(); + + ifp = lltable_get_ifp(ln->lle_tbl); + if ((ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) != 0) + dr = defrouter_lookup_locked(&ln->r_l3addr.addr6, ifp); + else + dr = NULL; + ND6_RUNLOCK(); + + if ((ln->la_flags & LLE_DELETED) == 0) + EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_EXPIRED); /* * we used to have pfctlinput(PRC_HOSTDEAD) here. @@ -1378,11 +1394,7 @@ nd6_free(struct llentry *ln, int gc) /* cancel timer */ nd6_llinfo_settimer_locked(ln, -1); - dr = NULL; - ifp = ln->lle_tbl->llt_ifp; if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) { - dr = defrouter_lookup(&ln->r_l3addr.addr6, ifp); - if (dr != NULL && dr->expire && ln->ln_state == ND6_LLINFO_STALE && gc) { /* diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index 4c83467..3b1aa96 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -355,9 +355,6 @@ VNET_DECLARE(struct rwlock, nd6_lock); #define nd6log(x) do { if (V_nd6_debug) log x; } while (/*CONSTCOND*/ 0) -VNET_DECLARE(struct callout, nd6_timer_ch); -#define V_nd6_timer_ch VNET(nd6_timer_ch) - /* nd6_rtr.c */ VNET_DECLARE(int, nd6_defifindex); VNET_DECLARE(int, ip6_desync_factor); /* seconds */ @@ -459,7 +456,7 @@ void defrouter_reset(void); void defrouter_select(void); void defrouter_ref(struct nd_defrouter *); void defrouter_rele(struct nd_defrouter *); -void defrouter_remove(struct nd_defrouter *); +bool defrouter_remove(struct in6_addr *, struct ifnet *); void defrouter_unlink(struct nd_defrouter *, struct nd_drhead *); void defrouter_del(struct nd_defrouter *); void prelist_remove(struct nd_prefix *); diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index d528575..d621b52 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -857,30 +857,19 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) * Remove the sender from the Default Router List and * update the Destination Cache entries. */ - struct nd_defrouter *dr; struct ifnet *nd6_ifp; nd6_ifp = lltable_get_ifp(ln->lle_tbl); - ND6_WLOCK(); - dr = defrouter_lookup_locked(&ln->r_l3addr.addr6, - nd6_ifp); - if (dr != NULL) { - /* releases the ND lock */ - defrouter_remove(dr); - dr = NULL; - } else { - ND6_WUNLOCK(); - if ((ND_IFINFO(nd6_ifp)->flags & ND6_IFF_ACCEPT_RTADV) != 0) { - /* - * Even if the neighbor is not in the default - * router list, the neighbor may be used - * as a next hop for some destinations - * (e.g. redirect case). So we must - * call rt6_flush explicitly. - */ - rt6_flush(&ip6->ip6_src, ifp); - } - } + if (!defrouter_remove(&ln->r_l3addr.addr6, nd6_ifp) && + (ND_IFINFO(nd6_ifp)->flags & + ND6_IFF_ACCEPT_RTADV) != 0) + /* + * Even if the neighbor is not in the default + * router list, the neighbor may be used as a + * next hop for some destinations (e.g. redirect + * case). So we must call rt6_flush explicitly. + */ + rt6_flush(&ip6->ip6_src, ifp); } ln->ln_router = is_router; } diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index 294c90a..6fee830 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -627,22 +627,26 @@ defrouter_reset(void) } /* - * Remove a router from the global list and free it. - * - * The ND lock must be held and is released before returning. The caller must - * hold a reference on the router object. + * Look up a matching default router list entry and remove it. Returns true if a + * matching entry was found, false otherwise. */ -void -defrouter_remove(struct nd_defrouter *dr) +bool +defrouter_remove(struct in6_addr *addr, struct ifnet *ifp) { + struct nd_defrouter *dr; - ND6_WLOCK_ASSERT(); - KASSERT(dr->refcnt >= 2, ("unexpected refcount 0x%x", dr->refcnt)); + ND6_WLOCK(); + dr = defrouter_lookup_locked(addr, ifp); + if (dr == NULL) { + ND6_WUNLOCK(); + return (false); + } defrouter_unlink(dr, NULL); ND6_WUNLOCK(); defrouter_del(dr); defrouter_rele(dr); + return (true); } /* @@ -850,14 +854,14 @@ defrtrlist_update(struct nd_defrouter *new) struct nd_defrouter *dr, *n; int oldpref; - ND6_WLOCK(); - if ((dr = defrouter_lookup_locked(&new->rtaddr, new->ifp)) != NULL) { - if (new->rtlifetime == 0) { - /* releases the ND lock */ - defrouter_remove(dr); - return (NULL); - } + if (new->rtlifetime == 0) { + defrouter_remove(&new->rtaddr, new->ifp); + return (NULL); + } + ND6_WLOCK(); + dr = defrouter_lookup_locked(&new->rtaddr, new->ifp); + if (dr != NULL) { oldpref = rtpref(dr); /* override */ @@ -881,25 +885,17 @@ defrtrlist_update(struct nd_defrouter *new) */ TAILQ_REMOVE(&V_nd_defrouter, dr, dr_entry); n = dr; - goto insert; - } - - /* entry does not exist */ - if (new->rtlifetime == 0) { - ND6_WUNLOCK(); - return (NULL); - } - - n = malloc(sizeof(*n), M_IP6NDP, M_NOWAIT | M_ZERO); - if (n == NULL) { - ND6_WUNLOCK(); - return (NULL); + } else { + n = malloc(sizeof(*n), M_IP6NDP, M_NOWAIT | M_ZERO); + if (n == NULL) { + ND6_WUNLOCK(); + return (NULL); + } + memcpy(n, new, sizeof(*n)); + /* Initialize with an extra reference for the caller. */ + refcount_init(&n->refcnt, 2); } - memcpy(n, new, sizeof(*n)); - /* Initialize with an extra reference for the caller. */ - refcount_init(&n->refcnt, 2); -insert: /* * Insert the new router in the Default Router List; * The Default Router List should be in the descending order diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c index 9768df5..ebcf6d6 100644 --- a/sys/netinet6/udp6_usrreq.c +++ b/sys/netinet6/udp6_usrreq.c @@ -876,8 +876,8 @@ udp6_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr6, UDP_PROBE(send, NULL, inp, ip6, inp, udp6); UDPSTAT_INC(udps_opackets); - error = ip6_output(m, optp, NULL, flags, inp->in6p_moptions, - NULL, inp); + error = ip6_output(m, optp, &inp->inp_route6, flags, + inp->in6p_moptions, NULL, inp); break; case AF_INET: error = EAFNOSUPPORT; diff --git a/sys/netipsec/ipsec_output.c b/sys/netipsec/ipsec_output.c index 1523e0b..e0523d4 100644 --- a/sys/netipsec/ipsec_output.c +++ b/sys/netipsec/ipsec_output.c @@ -441,7 +441,7 @@ ipsec_encap(struct mbuf **mp, struct secasindex *saidx) setdf = V_ip4_ipsec_dfbit; break; default:/* propagate to outer header */ - setdf = (ip->ip_off & ntohs(IP_DF)) != 0; + setdf = (ip->ip_off & htons(IP_DF)) != 0; } itos = ip->ip_tos; break; diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 283dddc9..0220286 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -6192,11 +6192,13 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) * We do need to be careful about bridges. If the * net.link.bridge.pfil_bridge sysctl is set we can be filtering on a * bridge, so if the input interface is a bridge member and the output - * interface is its bridge we're not actually forwarding but bridging. + * interface is its bridge or a member of the same bridge we're not + * actually forwarding but bridging. */ - if (dir == PF_OUT && m->m_pkthdr.rcvif && ifp != m->m_pkthdr.rcvif - && (m->m_pkthdr.rcvif->if_bridge == NULL - || m->m_pkthdr.rcvif->if_bridge != ifp->if_softc)) + if (dir == PF_OUT && m->m_pkthdr.rcvif && ifp != m->m_pkthdr.rcvif && + (m->m_pkthdr.rcvif->if_bridge == NULL || + (m->m_pkthdr.rcvif->if_bridge != ifp->if_softc && + m->m_pkthdr.rcvif->if_bridge != ifp->if_bridge))) fwdir = PF_FWD; if (!V_pf_status.running) diff --git a/sys/nfs/bootp_subr.c b/sys/nfs/bootp_subr.c index dd76d7e..51c76e7 100644 --- a/sys/nfs/bootp_subr.c +++ b/sys/nfs/bootp_subr.c @@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/endian.h> #include <sys/jail.h> #include <sys/kernel.h> #include <sys/sockio.h> @@ -158,6 +159,7 @@ struct bootpc_ifcontext { int dhcpquerytype; /* dhcp type sent */ struct in_addr dhcpserver; int gotdhcpserver; + uint16_t mtu; }; #define TAG_MAXLEN 1024 @@ -199,6 +201,7 @@ struct bootpc_globalcontext { #define TAG_ROUTERS 3 /* Routers (in order of preference) */ #define TAG_HOSTNAME 12 /* Client host name */ #define TAG_ROOT 17 /* Root path */ +#define TAG_INTF_MTU 26 /* Interface MTU Size (RFC2132) */ /* DHCP specific tags */ #define TAG_OVERLOAD 52 /* Option Overload */ @@ -273,7 +276,7 @@ static int bootpc_call(struct bootpc_globalcontext *gctx, static void bootpc_fakeup_interface(struct bootpc_ifcontext *ifctx, struct thread *td); -static int bootpc_adjust_interface(struct bootpc_ifcontext *ifctx, +static void bootpc_adjust_interface(struct bootpc_ifcontext *ifctx, struct bootpc_globalcontext *gctx, struct thread *td); static void bootpc_decode_reply(struct nfsv3_diskless *nd, @@ -1008,33 +1011,41 @@ bootpc_shutdown_interface(struct bootpc_ifcontext *ifctx, struct thread *td) panic("%s: SIOCDIFADDR, error=%d", __func__, error); } -static int +static void bootpc_adjust_interface(struct bootpc_ifcontext *ifctx, struct bootpc_globalcontext *gctx, struct thread *td) { int error; - struct sockaddr_in defdst; - struct sockaddr_in defmask; struct sockaddr_in *sin; struct ifreq *ifr; struct in_aliasreq *ifra; struct sockaddr_in *myaddr; struct sockaddr_in *netmask; - struct sockaddr_in *gw; ifr = &ifctx->ireq; ifra = &ifctx->iareq; myaddr = &ifctx->myaddr; netmask = &ifctx->netmask; - gw = &ifctx->gw; if (bootpc_ifctx_isresolved(ifctx) == 0) { /* Shutdown interfaces where BOOTP failed */ bootpc_shutdown_interface(ifctx, td); - return (0); + return; } - printf("Adjusted interface %s\n", ifctx->ireq.ifr_name); + printf("Adjusted interface %s", ifctx->ireq.ifr_name); + + /* Do BOOTP interface options */ + if (ifctx->mtu != 0) { + printf(" (MTU=%d%s)", ifctx->mtu, + (ifctx->mtu > 1514) ? "/JUMBO" : ""); + ifr->ifr_mtu = ifctx->mtu; + error = ifioctl(bootp_so, SIOCSIFMTU, (caddr_t) ifr, td); + if (error != 0) + panic("%s: SIOCSIFMTU, error=%d", __func__, error); + } + printf("\n"); + /* * Do enough of ifconfig(8) so that the chosen interface * can talk to the servers. (just set the address) @@ -1054,24 +1065,48 @@ bootpc_adjust_interface(struct bootpc_ifcontext *ifctx, error = ifioctl(bootp_so, SIOCAIFADDR, (caddr_t)ifra, td); if (error != 0) panic("%s: SIOCAIFADDR, error=%d", __func__, error); +} + +static void +bootpc_add_default_route(struct bootpc_ifcontext *ifctx) +{ + int error; + struct sockaddr_in defdst; + struct sockaddr_in defmask; - /* Add new default route */ + if (ifctx->gw.sin_addr.s_addr == htonl(INADDR_ANY)) + return; - if (ifctx->gotgw != 0 || gctx->gotgw == 0) { - clear_sinaddr(&defdst); - clear_sinaddr(&defmask); - /* XXX MRT just table 0 */ - error = rtrequest_fib(RTM_ADD, - (struct sockaddr *) &defdst, (struct sockaddr *) gw, - (struct sockaddr *) &defmask, - (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL, RT_DEFAULT_FIB); - if (error != 0) { - printf("%s: RTM_ADD, error=%d\n", __func__, error); - return (error); - } + clear_sinaddr(&defdst); + clear_sinaddr(&defmask); + + error = rtrequest_fib(RTM_ADD, (struct sockaddr *)&defdst, + (struct sockaddr *) &ifctx->gw, (struct sockaddr *)&defmask, + (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL, RT_DEFAULT_FIB); + if (error != 0) { + printf("%s: RTM_ADD, error=%d\n", __func__, error); } +} + +static void +bootpc_remove_default_route(struct bootpc_ifcontext *ifctx) +{ + int error; + struct sockaddr_in defdst; + struct sockaddr_in defmask; + + if (ifctx->gw.sin_addr.s_addr == htonl(INADDR_ANY)) + return; + + clear_sinaddr(&defdst); + clear_sinaddr(&defmask); - return (0); + error = rtrequest_fib(RTM_DELETE, (struct sockaddr *)&defdst, + (struct sockaddr *) &ifctx->gw, (struct sockaddr *)&defmask, + (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL, RT_DEFAULT_FIB); + if (error != 0) { + printf("%s: RTM_DELETE, error=%d\n", __func__, error); + } } static int @@ -1463,6 +1498,8 @@ bootpc_decode_reply(struct nfsv3_diskless *nd, struct bootpc_ifcontext *ifctx, if (p == NULL) { p = bootpc_tag(&gctx->tag, &ifctx->reply, ifctx->replylen, TAG_ROOT); + if (p != NULL) + ifctx->gotrootpath = 1; } #ifdef ROOTDEVNAME if ((p == NULL || (boothowto & RB_DFLTROOT) != 0) && @@ -1482,7 +1519,6 @@ bootpc_decode_reply(struct nfsv3_diskless *nd, struct bootpc_ifcontext *ifctx, } printf("rootfs %s ", p); gctx->gotrootpath = 1; - ifctx->gotrootpath = 1; gctx->setrootfs = ifctx; p = bootpc_tag(&gctx->tag, &ifctx->reply, @@ -1522,6 +1558,11 @@ bootpc_decode_reply(struct nfsv3_diskless *nd, struct bootpc_ifcontext *ifctx, p[i] = '\0'; } + p = bootpc_tag(&gctx->tag, &ifctx->reply, ifctx->replylen, + TAG_INTF_MTU); + if (p != NULL) { + ifctx->mtu = be16dec(p); + } printf("\n"); @@ -1533,10 +1574,6 @@ bootpc_decode_reply(struct nfsv3_diskless *nd, struct bootpc_ifcontext *ifctx, else ifctx->netmask.sin_addr.s_addr = htonl(IN_CLASSC_NET); } - if (ifctx->gotgw == 0) { - /* Use proxyarp */ - ifctx->gw.sin_addr.s_addr = ifctx->myaddr.sin_addr.s_addr; - } } void @@ -1728,9 +1765,11 @@ retry: kern_setenv("boot.netif.name", ifctx->ifp->if_xname); + bootpc_add_default_route(ifctx); error = md_mount(&nd->root_saddr, nd->root_hostnam, nd->root_fh, &nd->root_fhsize, &nd->root_args, td); + bootpc_remove_default_route(ifctx); if (error != 0) { if (gctx->any_root_overrides == 0) panic("nfs_boot: mount root, error=%d", error); @@ -1748,6 +1787,7 @@ retry: ifctx->myaddr.sin_addr.s_addr | ~ ifctx->netmask.sin_addr.s_addr; bcopy(&ifctx->netmask, &nd->myif.ifra_mask, sizeof(ifctx->netmask)); + bcopy(&ifctx->gw, &nd->mygateway, sizeof(ifctx->gw)); out: while((ifctx = STAILQ_FIRST(&gctx->interfaces)) != NULL) { diff --git a/sys/nfs/nfs_diskless.c b/sys/nfs/nfs_diskless.c index 864aae0..e765c2e 100644 --- a/sys/nfs/nfs_diskless.c +++ b/sys/nfs/nfs_diskless.c @@ -154,6 +154,7 @@ nfs_parse_options(const char *envopts, struct nfs_args *nd) * boot.netif.netmask netmask on boot interface * boot.netif.gateway default gateway (optional) * boot.netif.hwaddr hardware address of boot interface + * boot.netif.mtu interface mtu from bootp/dhcp (optional) * boot.nfsroot.server IP address of root filesystem server * boot.nfsroot.path path of the root filesystem on server * boot.nfsroot.nfshandle NFS handle for root filesystem on server diff --git a/sys/ofed/drivers/infiniband/core/iwcm.c b/sys/ofed/drivers/infiniband/core/iwcm.c index a90f907..7e27584 100644 --- a/sys/ofed/drivers/infiniband/core/iwcm.c +++ b/sys/ofed/drivers/infiniband/core/iwcm.c @@ -79,7 +79,6 @@ struct iwcm_listen_work { static LIST_HEAD(listen_port_list); static DEFINE_MUTEX(listen_port_mutex); -static DEFINE_MUTEX(dequeue_mutex); struct listen_port_info { struct list_head list; @@ -455,7 +454,6 @@ iw_so_event_handler(struct work_struct *_work) kfree(work); return; } - mutex_lock(&dequeue_mutex); /* Dequeue & process all new 'so' connection requests for this cmid */ while ((so = dequeue_socket(work->cm_id->so)) != NULL) { @@ -475,7 +473,6 @@ iw_so_event_handler(struct work_struct *_work) } } err: - mutex_unlock(&dequeue_mutex); kfree(work); #endif return; @@ -487,7 +484,6 @@ iw_so_upcall(struct socket *parent_so, void *arg, int waitflag) struct socket *so; struct iw_cm_id *cm_id = arg; - mutex_lock(&dequeue_mutex); /* check whether iw_so_event_handler() already dequeued this 'so' */ so = TAILQ_FIRST(&parent_so->so_comp); if (!so) @@ -500,7 +496,6 @@ iw_so_upcall(struct socket *parent_so, void *arg, int waitflag) INIT_WORK(&work->work, iw_so_event_handler); queue_work(iwcm_wq, &work->work); - mutex_unlock(&dequeue_mutex); return SU_OK; } diff --git a/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib.h b/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib.h index eb269a4..acf3d54 100644 --- a/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib.h @@ -322,6 +322,8 @@ struct ipoib_dev_priv { unsigned long flags; + int gone; + struct mutex vlan_mutex; struct rb_root path_tree; diff --git a/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c b/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c index 5e69a52..9a68b23 100644 --- a/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -258,6 +258,10 @@ ipoib_ioctl(struct ifnet *ifp, u_long command, caddr_t data) struct ifreq *ifr = (struct ifreq *) data; int error = 0; + /* check if detaching */ + if (priv == NULL || priv->gone != 0) + return (ENXIO); + switch (command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { @@ -794,6 +798,7 @@ ipoib_detach(struct ipoib_dev_priv *priv) dev = priv->dev; if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags)) { + priv->gone = 1; bpfdetach(dev); if_detach(dev); if_free(dev); diff --git a/sys/ofed/drivers/net/mlx4/en_port.c b/sys/ofed/drivers/net/mlx4/en_port.c index e4dab0a..2a1c4ba 100644 --- a/sys/ofed/drivers/net/mlx4/en_port.c +++ b/sys/ofed/drivers/net/mlx4/en_port.c @@ -60,10 +60,11 @@ int mlx4_SET_VLAN_FLTR(struct mlx4_dev *dev, struct mlx4_en_priv *priv) memset(filter, 0, sizeof(*filter)); for (i = VLAN_FLTR_SIZE - 1; i >= 0; i--) { entry = 0; - for (j = 0; j < 32; j++) + for (j = 0; j < 32; j++) { if (test_bit(index, priv->active_vlans)) entry |= 1 << j; - index++; + index++; + } filter->entry[i] = cpu_to_be32(entry); } err = mlx4_cmd(dev, mailbox->dma, priv->port, 0, MLX4_CMD_SET_VLAN_FLTR, diff --git a/sys/ofed/drivers/net/mlx4/en_rx.c b/sys/ofed/drivers/net/mlx4/en_rx.c index 7c5b759..b29096d 100644 --- a/sys/ofed/drivers/net/mlx4/en_rx.c +++ b/sys/ofed/drivers/net/mlx4/en_rx.c @@ -561,9 +561,6 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud struct mbuf *mb; struct mlx4_cq *mcq = &cq->mcq; struct mlx4_cqe *buf = cq->buf; -#ifdef INET - struct lro_entry *queued; -#endif int index; unsigned int length; int polled = 0; @@ -616,7 +613,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud goto next; } - mb->m_pkthdr.flowid = cq->ring; + /* forward Toeplitz compatible hash value */ + mb->m_pkthdr.flowid = be32_to_cpu(cqe->immed_rss_invalid); M_HASHTYPE_SET(mb, M_HASHTYPE_OPAQUE); mb->m_pkthdr.rcvif = dev; if (be32_to_cpu(cqe->vlan_my_qpn) & @@ -668,10 +666,7 @@ next: /* Flush all pending IP reassembly sessions */ out: #ifdef INET - while ((queued = SLIST_FIRST(&ring->lro.lro_active)) != NULL) { - SLIST_REMOVE_HEAD(&ring->lro.lro_active, next); - tcp_lro_flush(&ring->lro, queued); - } + tcp_lro_flush_all(&ring->lro); #endif AVG_PERF_COUNTER(priv->pstats.rx_coal_avg, polled); mcq->cons_index = cons_index; diff --git a/sys/ofed/drivers/net/mlx4/en_tx.c b/sys/ofed/drivers/net/mlx4/en_tx.c index 56bb5b2..4358aa6 100644 --- a/sys/ofed/drivers/net/mlx4/en_tx.c +++ b/sys/ofed/drivers/net/mlx4/en_tx.c @@ -1060,7 +1060,7 @@ mlx4_en_transmit(struct ifnet *dev, struct mbuf *m) /* Compute which queue to use */ if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) { - i = m->m_pkthdr.flowid % priv->tx_ring_num; + i = (m->m_pkthdr.flowid % 128) % priv->tx_ring_num; } else { i = mlx4_en_select_queue(dev, m); diff --git a/sys/pc98/pc98/canbus.c b/sys/pc98/pc98/canbus.c index 8c3ee4e..0c54ceb 100644 --- a/sys/pc98/pc98/canbus.c +++ b/sys/pc98/pc98/canbus.c @@ -433,9 +433,9 @@ print_all_resources(device_t dev) if (STAILQ_FIRST(rl)) retval += printf(" at"); - retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx"); - retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#lx"); - retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#jx"); + retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#jx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); return retval; } diff --git a/sys/powerpc/include/bus.h b/sys/powerpc/include/bus.h index 658c4eb..cb6dc77 100644 --- a/sys/powerpc/include/bus.h +++ b/sys/powerpc/include/bus.h @@ -79,9 +79,14 @@ #define BUS_SPACE_MAXADDR 0xFFFFFFFFFFFFFFFFUL #define BUS_SPACE_MAXSIZE 0xFFFFFFFFFFFFFFFFUL #else +#ifdef BOOKE +#define BUS_SPACE_MAXADDR 0xFFFFFFFFFULL +#define BUS_SPACE_MAXSIZE 0xFFFFFFFFUL +#else #define BUS_SPACE_MAXADDR 0xFFFFFFFFUL #define BUS_SPACE_MAXSIZE 0xFFFFFFFFUL #endif +#endif #define BUS_SPACE_MAP_CACHEABLE 0x01 #define BUS_SPACE_MAP_LINEAR 0x02 diff --git a/sys/powerpc/mpc85xx/fsl_sdhc.c b/sys/powerpc/mpc85xx/fsl_sdhc.c index 174a8e6..29bedd8 100644 --- a/sys/powerpc/mpc85xx/fsl_sdhc.c +++ b/sys/powerpc/mpc85xx/fsl_sdhc.c @@ -126,7 +126,7 @@ static devclass_t fsl_sdhc_devclass; DRIVER_MODULE(sdhci_fsl, simplebus, fsl_sdhc_driver, fsl_sdhc_devclass, 0, 0); DRIVER_MODULE(mmc, sdhci_fsl, mmc_driver, mmc_devclass, NULL, NULL); - +MODULE_DEPEND(sdhci_fsl, mmc, 1, 1, 1); /***************************************************************************** * Private methods diff --git a/sys/powerpc/mpc85xx/lbc.c b/sys/powerpc/mpc85xx/lbc.c index a94a841..37e3ed0 100644 --- a/sys/powerpc/mpc85xx/lbc.c +++ b/sys/powerpc/mpc85xx/lbc.c @@ -718,8 +718,8 @@ lbc_alloc_resource(device_t bus, device_t child, int type, int *rid, res = rman_reserve_resource(rm, start, end, count, flags, child); if (res == NULL) { - device_printf(bus, "failed to reserve resource %#lx - %#lx " - "(%#lx)\n", start, end, count); + device_printf(bus, "failed to reserve resource %#jx - %#jx " + "(%#jx)\n", start, end, count); return (NULL); } @@ -749,8 +749,8 @@ lbc_print_child(device_t dev, device_t child) rv = 0; rv += bus_print_child_header(dev, child); - rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); - rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); rv += bus_print_child_footer(dev, child); return (rv); diff --git a/sys/powerpc/mpc85xx/pci_mpc85xx.c b/sys/powerpc/mpc85xx/pci_mpc85xx.c index 4397ac0..5a141b6 100644 --- a/sys/powerpc/mpc85xx/pci_mpc85xx.c +++ b/sys/powerpc/mpc85xx/pci_mpc85xx.c @@ -58,12 +58,11 @@ __FBSDID("$FreeBSD$"); #include <dev/ofw/ofw_pci.h> #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofwpci.h> #include <dev/pci/pcivar.h> #include <dev/pci/pcireg.h> #include <dev/pci/pcib_private.h> -#include <powerpc/ofw/ofw_pci.h> - #include "ofw_bus_if.h" #include "pcib_if.h" diff --git a/sys/powerpc/ofw/ofw_pci.c b/sys/powerpc/ofw/ofw_pci.c index 0ca5bc0..20aa81f 100644 --- a/sys/powerpc/ofw/ofw_pci.c +++ b/sys/powerpc/ofw/ofw_pci.c @@ -384,10 +384,10 @@ ofw_pci_activate_resource(device_t bus, device_t child, int type, int rid, } if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { struct ofw_pci_range *rp; - vm_offset_t start; + vm_paddr_t start; int space; - start = (vm_offset_t)rman_get_start(res); + start = (vm_paddr_t)rman_get_start(res); /* * Map this through the ranges list @@ -416,8 +416,8 @@ ofw_pci_activate_resource(device_t bus, device_t child, int type, int rid, } if (bootverbose) - printf("ofw_pci mapdev: start %zx, len %ld\n", start, - rman_get_size(res)); + printf("ofw_pci mapdev: start %jx, len %jd\n", + (rman_res_t)start, rman_get_size(res)); p = pmap_mapdev(start, (vm_size_t)rman_get_size(res)); if (p == NULL) diff --git a/sys/powerpc/powermac/cpcht.c b/sys/powerpc/powermac/cpcht.c index 765d946..3145689 100644 --- a/sys/powerpc/powermac/cpcht.c +++ b/sys/powerpc/powermac/cpcht.c @@ -51,7 +51,7 @@ __FBSDID("$FreeBSD$"); #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> -#include <powerpc/ofw/ofw_pci.h> +#include <dev/ofw/ofwpci.h> #include <vm/vm.h> #include <vm/pmap.h> diff --git a/sys/powerpc/powermac/grackle.c b/sys/powerpc/powermac/grackle.c index 95d59a1..3d35fea 100644 --- a/sys/powerpc/powermac/grackle.c +++ b/sys/powerpc/powermac/grackle.c @@ -35,11 +35,13 @@ __FBSDID("$FreeBSD$"); #include <sys/conf.h> #include <sys/kernel.h> #include <sys/proc.h> +#include <sys/rman.h> #include <dev/ofw/openfirm.h> #include <dev/ofw/ofw_pci.h> #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofwpci.h> #include <dev/pci/pcivar.h> #include <dev/pci/pcireg.h> @@ -50,9 +52,6 @@ __FBSDID("$FreeBSD$"); #include <machine/pio.h> #include <machine/resource.h> -#include <sys/rman.h> - -#include <powerpc/ofw/ofw_pci.h> #include <powerpc/powermac/gracklevar.h> #include <vm/vm.h> diff --git a/sys/powerpc/powermac/macgpio.c b/sys/powerpc/powermac/macgpio.c index 6b67514..c72f8d7 100644 --- a/sys/powerpc/powermac/macgpio.c +++ b/sys/powerpc/powermac/macgpio.c @@ -236,7 +236,7 @@ macgpio_print_child(device_t dev, device_t child) printf(" addr 0x%02x", dinfo->gpio_num); /* should not happen */ resource_list_print_type(&dinfo->mdi_resources, "irq", SYS_RES_IRQ, - "%ld"); + "%jd"); retval += bus_print_child_footer(dev, child); return (retval); @@ -258,7 +258,7 @@ macgpio_probe_nomatch(device_t dev, device_t child) if (dinfo->gpio_num >= 0) printf(" gpio %d",dinfo->gpio_num); resource_list_print_type(&dinfo->mdi_resources, "irq", - SYS_RES_IRQ, "%ld"); + SYS_RES_IRQ, "%jd"); printf(" (no driver attached)\n"); } } diff --git a/sys/powerpc/powermac/macio.c b/sys/powerpc/powermac/macio.c index 285d627..7a15b85 100644 --- a/sys/powerpc/powermac/macio.c +++ b/sys/powerpc/powermac/macio.c @@ -447,8 +447,8 @@ macio_print_child(device_t dev, device_t child) retval += bus_print_child_header(dev, child); - retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); - retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); retval += bus_print_child_footer(dev, child); @@ -470,8 +470,8 @@ macio_probe_nomatch(device_t dev, device_t child) if ((type = ofw_bus_get_type(child)) == NULL) type = "(unknown)"; device_printf(dev, "<%s, %s>", type, ofw_bus_get_name(child)); - resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); - resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); printf(" (no driver attached)\n"); } } diff --git a/sys/powerpc/powermac/uninorth.c b/sys/powerpc/powermac/uninorth.c index e34c9d8..2d32a49 100644 --- a/sys/powerpc/powermac/uninorth.c +++ b/sys/powerpc/powermac/uninorth.c @@ -425,8 +425,8 @@ unin_chip_print_child(device_t dev, device_t child) retval += bus_print_child_header(dev, child); - retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); - retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); retval += bus_print_child_footer(dev, child); @@ -447,8 +447,8 @@ unin_chip_probe_nomatch(device_t dev, device_t child) if ((type = ofw_bus_get_type(child)) == NULL) type = "(unknown)"; device_printf(dev, "<%s, %s>", type, ofw_bus_get_name(child)); - resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); - resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); printf(" (no driver attached)\n"); } } @@ -591,7 +591,7 @@ unin_chip_activate_resource(device_t bus, device_t child, int type, int rid, start = (vm_offset_t) rman_get_start(res); if (bootverbose) - printf("unin mapdev: start %zx, len %ld\n", start, + printf("unin mapdev: start %zx, len %jd\n", start, rman_get_size(res)); p = pmap_mapdev(start, (vm_size_t) rman_get_size(res)); diff --git a/sys/powerpc/powermac/uninorthpci.c b/sys/powerpc/powermac/uninorthpci.c index 9da06ff..6149af5 100644 --- a/sys/powerpc/powermac/uninorthpci.c +++ b/sys/powerpc/powermac/uninorthpci.c @@ -32,11 +32,13 @@ __FBSDID("$FreeBSD$"); #include <sys/bus.h> #include <sys/conf.h> #include <sys/kernel.h> +#include <sys/rman.h> #include <dev/ofw/openfirm.h> #include <dev/ofw/ofw_pci.h> #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofwpci.h> #include <dev/pci/pcivar.h> #include <dev/pci/pcireg.h> @@ -47,9 +49,6 @@ __FBSDID("$FreeBSD$"); #include <machine/pio.h> #include <machine/resource.h> -#include <sys/rman.h> - -#include <powerpc/ofw/ofw_pci.h> #include <powerpc/powermac/uninorthvar.h> #include <vm/vm.h> diff --git a/sys/powerpc/powermac/uninorthvar.h b/sys/powerpc/powermac/uninorthvar.h index e08478d..e570da0 100644 --- a/sys/powerpc/powermac/uninorthvar.h +++ b/sys/powerpc/powermac/uninorthvar.h @@ -30,7 +30,7 @@ #include <dev/ofw/ofw_bus_subr.h> #include <dev/ofw/ofw_pci.h> -#include <powerpc/ofw/ofw_pci.h> +#include <dev/ofw/ofwpci.h> struct uninorth_softc { struct ofw_pci_softc pci_sc; diff --git a/sys/powerpc/powerpc/nexus.c b/sys/powerpc/powerpc/nexus.c index 1ad118c..f5a3da8 100644 --- a/sys/powerpc/powerpc/nexus.c +++ b/sys/powerpc/powerpc/nexus.c @@ -203,7 +203,7 @@ nexus_activate_resource(device_t bus __unused, device_t child __unused, start = (vm_paddr_t) rman_get_start(r); if (bootverbose) - printf("nexus mapdev: start %jx, len %ld\n", + printf("nexus mapdev: start %jx, len %jd\n", (uintmax_t)start, rman_get_size(r)); p = pmap_mapdev(start, (vm_size_t) rman_get_size(r)); diff --git a/sys/powerpc/ps3/ps3bus.c b/sys/powerpc/ps3/ps3bus.c index bbb40c8..37ea0b7 100644 --- a/sys/powerpc/ps3/ps3bus.c +++ b/sys/powerpc/ps3/ps3bus.c @@ -480,9 +480,9 @@ ps3bus_print_child(device_t dev, device_t child) retval += bus_print_child_header(dev, child); retval += resource_list_print_type(&dinfo->resources, "mem", - SYS_RES_MEMORY, "%#lx"); + SYS_RES_MEMORY, "%#jx"); retval += resource_list_print_type(&dinfo->resources, "irq", - SYS_RES_IRQ, "%ld"); + SYS_RES_IRQ, "%jd"); retval += bus_print_child_footer(dev, child); diff --git a/sys/powerpc/pseries/rtas_pci.c b/sys/powerpc/pseries/rtas_pci.c index bb72b71..873168e 100644 --- a/sys/powerpc/pseries/rtas_pci.c +++ b/sys/powerpc/pseries/rtas_pci.c @@ -32,11 +32,13 @@ __FBSDID("$FreeBSD$"); #include <sys/bus.h> #include <sys/conf.h> #include <sys/kernel.h> +#include <sys/rman.h> #include <dev/ofw/openfirm.h> #include <dev/ofw/ofw_pci.h> #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofwpci.h> #include <dev/pci/pcivar.h> #include <dev/pci/pcireg.h> @@ -48,12 +50,9 @@ __FBSDID("$FreeBSD$"); #include <machine/resource.h> #include <machine/rtas.h> -#include <sys/rman.h> - #include <vm/vm.h> #include <vm/pmap.h> -#include <powerpc/ofw/ofw_pci.h> #include <powerpc/pseries/plpar_iommu.h> #include "pcib_if.h" diff --git a/sys/powerpc/pseries/vdevice.c b/sys/powerpc/pseries/vdevice.c index 2323bc7..72b76c7 100644 --- a/sys/powerpc/pseries/vdevice.c +++ b/sys/powerpc/pseries/vdevice.c @@ -180,7 +180,7 @@ vdevice_print_child(device_t dev, device_t child) retval += bus_print_child_header(dev, child); - retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); retval += bus_print_child_footer(dev, child); diff --git a/sys/powerpc/psim/iobus.c b/sys/powerpc/psim/iobus.c index 741b62a..ce4a93a 100644 --- a/sys/powerpc/psim/iobus.c +++ b/sys/powerpc/psim/iobus.c @@ -253,7 +253,7 @@ iobus_print_child(device_t dev, device_t child) retval += bus_print_child_header(dev, child); retval += printf(" offset 0x%x", dinfo->id_reg[1]); - retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); retval += bus_print_child_footer(dev, child); diff --git a/sys/rpc/rpc_generic.c b/sys/rpc/rpc_generic.c index 28aa849..80d658d 100644 --- a/sys/rpc/rpc_generic.c +++ b/sys/rpc/rpc_generic.c @@ -390,15 +390,11 @@ __rpc_uaddr2taddr_af(int af, const char *uaddr) } ret = (struct netbuf *)malloc(sizeof *ret, M_RPC, M_WAITOK); - if (ret == NULL) - goto out; switch (af) { case AF_INET: sin = (struct sockaddr_in *)malloc(sizeof *sin, M_RPC, M_WAITOK); - if (sin == NULL) - goto out; memset(sin, 0, sizeof *sin); sin->sin_family = AF_INET; sin->sin_port = htons(port); @@ -415,8 +411,6 @@ __rpc_uaddr2taddr_af(int af, const char *uaddr) case AF_INET6: sin6 = (struct sockaddr_in6 *)malloc(sizeof *sin6, M_RPC, M_WAITOK); - if (sin6 == NULL) - goto out; memset(sin6, 0, sizeof *sin6); sin6->sin6_family = AF_INET6; sin6->sin6_port = htons(port); @@ -433,8 +427,6 @@ __rpc_uaddr2taddr_af(int af, const char *uaddr) case AF_LOCAL: sun = (struct sockaddr_un *)malloc(sizeof *sun, M_RPC, M_WAITOK); - if (sun == NULL) - goto out; memset(sun, 0, sizeof *sun); sun->sun_family = AF_LOCAL; strncpy(sun->sun_path, addrstr, sizeof(sun->sun_path) - 1); diff --git a/sys/rpc/svc.c b/sys/rpc/svc.c index f725d53..b436c18 100644 --- a/sys/rpc/svc.c +++ b/sys/rpc/svc.c @@ -560,7 +560,7 @@ svc_loss_reg(SVCXPRT *xprt, void (*dispatch)(SVCXPRT *)) mtx_unlock(&pool->sp_lock); return (TRUE); } - s = malloc(sizeof (struct svc_callout), M_RPC, M_NOWAIT); + s = malloc(sizeof(struct svc_loss_callout), M_RPC, M_NOWAIT); if (s == NULL) { mtx_unlock(&pool->sp_lock); return (FALSE); diff --git a/sys/sparc64/central/central.c b/sys/sparc64/central/central.c index 15876f0..9b16364 100644 --- a/sys/sparc64/central/central.c +++ b/sys/sparc64/central/central.c @@ -295,5 +295,5 @@ central_print_res(struct central_devinfo *cdi) { return (resource_list_print_type(&cdi->cdi_rl, "mem", SYS_RES_MEMORY, - "%#lx")); + "%#jx")); } diff --git a/sys/sparc64/ebus/ebus.c b/sys/sparc64/ebus/ebus.c index a53b20b..49abe18 100644 --- a/sys/sparc64/ebus/ebus.c +++ b/sys/sparc64/ebus/ebus.c @@ -721,8 +721,8 @@ ebus_print_res(struct ebus_devinfo *edi) retval = 0; retval += resource_list_print_type(&edi->edi_rl, "addr", SYS_RES_MEMORY, - "%#lx"); + "%#jx"); retval += resource_list_print_type(&edi->edi_rl, "irq", SYS_RES_IRQ, - "%ld"); + "%jd"); return (retval); } diff --git a/sys/sparc64/fhc/fhc.c b/sys/sparc64/fhc/fhc.c index b9d8bc6..85aa67d 100644 --- a/sys/sparc64/fhc/fhc.c +++ b/sys/sparc64/fhc/fhc.c @@ -529,7 +529,7 @@ fhc_print_res(struct fhc_devinfo *fdi) rv = 0; rv += resource_list_print_type(&fdi->fdi_rl, "mem", SYS_RES_MEMORY, - "%#lx"); - rv += resource_list_print_type(&fdi->fdi_rl, "irq", SYS_RES_IRQ, "%ld"); + "%#jx"); + rv += resource_list_print_type(&fdi->fdi_rl, "irq", SYS_RES_IRQ, "%jd"); return (rv); } diff --git a/sys/sparc64/pci/apb.c b/sys/sparc64/pci/apb.c index ba3643c..55f1313 100644 --- a/sys/sparc64/pci/apb.c +++ b/sys/sparc64/pci/apb.c @@ -136,7 +136,7 @@ apb_map_print(uint8_t map, rman_res_t scale) for (first = 1, i = 0; i < 8; i++) { if ((map & (1 << i)) != 0) { - printf("%s0x%lx-0x%lx", first ? "" : ", ", + printf("%s0x%jx-0x%jx", first ? "" : ", ", i * scale, (i + 1) * scale - 1); first = 0; } @@ -253,26 +253,26 @@ apb_alloc_resource(device_t dev, device_t child, int type, int *rid, case SYS_RES_IOPORT: if (!apb_checkrange(sc->sc_iomap, APB_IO_SCALE, start, end)) { device_printf(dev, "device %s requested unsupported " - "I/O range 0x%lx-0x%lx\n", + "I/O range 0x%jx-0x%jx\n", device_get_nameunit(child), start, end); return (NULL); } if (bootverbose) device_printf(sc->sc_bsc.ops_pcib_sc.dev, "device " - "%s requested decoded I/O range 0x%lx-0x%lx\n", + "%s requested decoded I/O range 0x%jx-0x%jx\n", device_get_nameunit(child), start, end); break; case SYS_RES_MEMORY: if (!apb_checkrange(sc->sc_memmap, APB_MEM_SCALE, start, end)) { device_printf(dev, "device %s requested unsupported " - "memory range 0x%lx-0x%lx\n", + "memory range 0x%jx-0x%jx\n", device_get_nameunit(child), start, end); return (NULL); } if (bootverbose) device_printf(sc->sc_bsc.ops_pcib_sc.dev, "device " - "%s requested decoded memory range 0x%lx-0x%lx\n", + "%s requested decoded memory range 0x%jx-0x%jx\n", device_get_nameunit(child), start, end); break; } diff --git a/sys/sparc64/sbus/dma_sbus.c b/sys/sparc64/sbus/dma_sbus.c index cf22569..4a83c92 100644 --- a/sys/sparc64/sbus/dma_sbus.c +++ b/sys/sparc64/sbus/dma_sbus.c @@ -409,7 +409,7 @@ dma_print_res(struct dma_devinfo *ddi) rv = 0; rv += resource_list_print_type(&ddi->ddi_rl, "mem", SYS_RES_MEMORY, - "%#lx"); - rv += resource_list_print_type(&ddi->ddi_rl, "irq", SYS_RES_IRQ, "%ld"); + "%#jx"); + rv += resource_list_print_type(&ddi->ddi_rl, "irq", SYS_RES_IRQ, "%jd"); return (rv); } diff --git a/sys/sparc64/sbus/sbus.c b/sys/sparc64/sbus/sbus.c index af46c77..65e44ff 100644 --- a/sys/sparc64/sbus/sbus.c +++ b/sys/sparc64/sbus/sbus.c @@ -929,8 +929,8 @@ sbus_print_res(struct sbus_devinfo *sdi) rv = 0; rv += resource_list_print_type(&sdi->sdi_rl, "mem", SYS_RES_MEMORY, - "%#lx"); + "%#jx"); rv += resource_list_print_type(&sdi->sdi_rl, "irq", SYS_RES_IRQ, - "%ld"); + "%jd"); return (rv); } diff --git a/sys/sparc64/sparc64/nexus.c b/sys/sparc64/sparc64/nexus.c index 3e099bd..3e72b6c 100644 --- a/sys/sparc64/sparc64/nexus.c +++ b/sys/sparc64/sparc64/nexus.c @@ -605,8 +605,8 @@ nexus_print_res(struct nexus_devinfo *ndi) rv = 0; rv += resource_list_print_type(&ndi->ndi_rl, "mem", SYS_RES_MEMORY, - "%#lx"); + "%#jx"); rv += resource_list_print_type(&ndi->ndi_rl, "irq", SYS_RES_IRQ, - "%ld"); + "%jd"); return (rv); } diff --git a/sys/sparc64/sparc64/upa.c b/sys/sparc64/sparc64/upa.c index 504d001..34bc50f 100644 --- a/sys/sparc64/sparc64/upa.c +++ b/sys/sparc64/sparc64/upa.c @@ -588,8 +588,8 @@ upa_print_res(struct upa_devinfo *udi) rv = 0; rv += resource_list_print_type(&udi->udi_rl, "mem", SYS_RES_MEMORY, - "%#lx"); + "%#jx"); rv += resource_list_print_type(&udi->udi_rl, "irq", SYS_RES_IRQ, - "%ld"); + "%jd"); return (rv); } diff --git a/sys/sys/_types.h b/sys/sys/_types.h index 3b3c16d..16039a6 100644 --- a/sys/sys/_types.h +++ b/sys/sys/_types.h @@ -112,6 +112,6 @@ typedef union { __int64_t _mbstateL; /* for alignment */ } __mbstate_t; -typedef unsigned long __rman_res_t; +typedef __uintmax_t __rman_res_t; #endif /* !_SYS__TYPES_H_ */ diff --git a/sys/sys/aio.h b/sys/sys/aio.h index 25ffb28..0abee2d 100644 --- a/sys/sys/aio.h +++ b/sys/sys/aio.h @@ -238,7 +238,7 @@ int aio_suspend(const struct aiocb * const[], int, const struct timespec *); int aio_mlock(struct aiocb *); #ifdef __BSD_VISIBLE -int aio_waitcomplete(struct aiocb **, struct timespec *); +ssize_t aio_waitcomplete(struct aiocb **, struct timespec *); #endif int aio_fsync(int op, struct aiocb *aiocbp); diff --git a/sys/sys/bus.h b/sys/sys/bus.h index 5f0df65..e0297cc 100644 --- a/sys/sys/bus.h +++ b/sys/sys/bus.h @@ -141,6 +141,7 @@ void devctl_notify(const char *__system, const char *__subsystem, const char *__type, const char *__data); void devctl_queue_data_f(char *__data, int __flags); void devctl_queue_data(char *__data); +void devctl_safe_quote(char *__dst, const char *__src, size_t len); /** * Device name parsers. Hook to allow device enumerators to map diff --git a/sys/sys/fail.h b/sys/sys/fail.h index e011459..bd2eab1 100644 --- a/sys/sys/fail.h +++ b/sys/sys/fail.h @@ -37,6 +37,11 @@ #include <sys/linker_set.h> #include <sys/queue.h> #include <sys/sysctl.h> +#include <sys/condvar.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/systm.h> /** * Failpoint return codes, used internally. @@ -49,7 +54,8 @@ enum fail_point_return_code { }; struct fail_point_entry; -TAILQ_HEAD(fail_point_entries, fail_point_entry); +struct fail_point_setting; + /** * Internal failpoint structure, tracking all the current details of the * failpoint. This structure is the core component shared between the @@ -57,22 +63,42 @@ TAILQ_HEAD(fail_point_entries, fail_point_entry); * @ingroup failpoint_private */ struct fail_point { - const char *fp_name; /**< name of fail point */ - const char *fp_location; /**< file:line of fail point */ - struct fail_point_entries fp_entries; /**< list of entries */ + const char *fp_name; /* name of fail point */ + const char *fp_location; /* file:line of fail point */ + volatile int fp_ref_cnt; /** + * protects fp_setting: while holding + * a ref, fp_setting points to an + * unfreed fail_point_setting + */ + struct fail_point_setting * volatile fp_setting; int fp_flags; - void (*fp_sleep_fn)(void *); /**< Function to call at end of - * sleep for sleep failpoints */ - void *fp_sleep_arg; /**< Arg for sleep_fn */ + + /**< Function to call before sleep or pause */ + void (*fp_pre_sleep_fn)(void *); + /**< Arg for fp_pre_sleep_fn */ + void *fp_pre_sleep_arg; + + /**< Function to call after waking from sleep or pause */ + void (*fp_post_sleep_fn)(void *); + /**< Arg for fp_post_sleep_fn */ + void *fp_post_sleep_arg; }; #define FAIL_POINT_DYNAMIC_NAME 0x01 /**< Must free name on destroy */ +/**< Use timeout path for sleep instead of msleep */ +#define FAIL_POINT_USE_TIMEOUT_PATH 0x02 +/**< If fail point is set to sleep, replace the sleep call with delay */ +#define FAIL_POINT_NONSLEEPABLE 0x04 + +#define FAIL_POINT_CV_DESC "fp cv no iterators" +#define FAIL_POINT_IS_OFF(fp) (__predict_true((fp)->fp_setting == NULL) || \ + __predict_true(fail_point_is_off(fp))) __BEGIN_DECLS /* Private failpoint eval function -- use fail_point_eval() instead. */ enum fail_point_return_code fail_point_eval_nontrivial(struct fail_point *, - int *ret); + int *ret); /** * @addtogroup failpoint @@ -86,26 +112,62 @@ enum fail_point_return_code fail_point_eval_nontrivial(struct fail_point *, void fail_point_init(struct fail_point *, const char *fmt, ...) __printflike(2, 3); +/* Return true iff this fail point is set to off, false otherwise */ +bool fail_point_is_off(struct fail_point *fp); + +/** + * Set the pre-sleep function for a fail point + * If fp_post_sleep_fn is specified, then FAIL_POINT_SLEEP will result in a + * (*fp->fp_pre_sleep_fn)(fp->fp_pre_sleep_arg) call by the thread. + */ +static inline void +fail_point_sleep_set_pre_func(struct fail_point *fp, void (*sleep_fn)(void *)) +{ + fp->fp_pre_sleep_fn = sleep_fn; +} + +static inline void +fail_point_sleep_set_pre_arg(struct fail_point *fp, void *sleep_arg) +{ + fp->fp_pre_sleep_arg = sleep_arg; +} + /** - * Set the sleep function for a fail point - * If sleep_fn is specified, then FAIL_POINT_SLEEP will result in a - * (*fp->sleep_fn)(fp->sleep_arg) call by the timer thread. Otherwise, - * if sleep_fn is NULL (default), then FAIL_POINT_SLEEP will result in the - * fail_point_eval() call sleeping. + * Set the post-sleep function. This will be passed to timeout if we take + * the timeout path. This must be set if you sleep using the timeout path. */ -static __inline void -fail_point_sleep_set_func(struct fail_point *fp, void (*sleep_fn)(void *)) +static inline void +fail_point_sleep_set_post_func(struct fail_point *fp, void (*sleep_fn)(void *)) { - fp->fp_sleep_fn = sleep_fn; + fp->fp_post_sleep_fn = sleep_fn; } +static inline void +fail_point_sleep_set_post_arg(struct fail_point *fp, void *sleep_arg) +{ + fp->fp_post_sleep_arg = sleep_arg; +} /** - * Set the argument for the sleep function for a fail point + * If the FAIL_POINT_USE_TIMEOUT flag is set on a failpoint, then + * FAIL_POINT_SLEEP will result in a call to timeout instead of + * msleep. Note that if you sleep while this flag is set, you must + * set fp_post_sleep_fn or an error will occur upon waking. */ -static __inline void -fail_point_sleep_set_arg(struct fail_point *fp, void *sleep_arg) +static inline void +fail_point_use_timeout_path(struct fail_point *fp, bool use_timeout, + void (*post_sleep_fn)(void *)) { - fp->fp_sleep_arg = sleep_arg; + KASSERT(!use_timeout || post_sleep_fn != NULL || + (post_sleep_fn == NULL && fp->fp_post_sleep_fn != NULL), + ("Setting fp to use timeout, but not setting post_sleep_fn\n")); + + if (use_timeout) + fp->fp_flags |= FAIL_POINT_USE_TIMEOUT_PATH; + else + fp->fp_flags &= ~FAIL_POINT_USE_TIMEOUT_PATH; + + if (post_sleep_fn != NULL) + fp->fp_post_sleep_fn = post_sleep_fn; } /** @@ -116,33 +178,64 @@ void fail_point_destroy(struct fail_point *); /** * Evaluate a failpoint. */ -static __inline enum fail_point_return_code +static inline enum fail_point_return_code fail_point_eval(struct fail_point *fp, int *ret) { - if (TAILQ_EMPTY(&fp->fp_entries)) { + if (__predict_true(fp->fp_setting == NULL)) return (FAIL_POINT_RC_CONTINUE); - } return (fail_point_eval_nontrivial(fp, ret)); } __END_DECLS /* Declare a fail_point and its sysctl in a function. */ -#define _FAIL_POINT_NAME(name) _fail_point_##name -#define _FAIL_POINT_LOCATION() "(" __FILE__ ":" __XSTRING(__LINE__) ")" +#define _FAIL_POINT_NAME(name) _fail_point_##name +#define _FAIL_POINT_LOCATION() "(" __FILE__ ":" __XSTRING(__LINE__) ")" +#define _FAIL_POINT_INIT(parent, name, flags) \ + static struct fail_point _FAIL_POINT_NAME(name) = { \ + .fp_name = #name, \ + .fp_location = _FAIL_POINT_LOCATION(), \ + .fp_ref_cnt = 0, \ + .fp_setting = NULL, \ + .fp_flags = (flags), \ + .fp_pre_sleep_fn = NULL, \ + .fp_pre_sleep_arg = NULL, \ + .fp_post_sleep_fn = NULL, \ + .fp_post_sleep_arg = NULL, \ + }; \ + SYSCTL_OID(parent, OID_AUTO, name, \ + CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, \ + &_FAIL_POINT_NAME(name), 0, fail_point_sysctl, \ + "A", ""); \ + SYSCTL_OID(parent, OID_AUTO, status_##name, \ + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, \ + &_FAIL_POINT_NAME(name), 0, \ + fail_point_sysctl_status, "A", ""); +#define _FAIL_POINT_EVAL(name, cond, code...) \ + int RETURN_VALUE; \ + \ + if (__predict_false(cond && \ + fail_point_eval(&_FAIL_POINT_NAME(name), &RETURN_VALUE))) { \ + \ + code; \ + \ + } + /** - * Instantiate a failpoint which returns "value" from the function when triggered. - * @param parent The parent sysctl under which to locate the sysctl + * Instantiate a failpoint which returns "RETURN_VALUE" from the function + * when triggered. + * @param parent The parent sysctl under which to locate the fp's sysctl * @param name The name of the failpoint in the sysctl tree (and printouts) - * @return Instantly returns the return("value") specified in the + * @return Instantly returns the RETURN_VALUE specified in the * failpoint, if triggered. */ #define KFAIL_POINT_RETURN(parent, name) \ KFAIL_POINT_CODE(parent, name, return RETURN_VALUE) /** - * Instantiate a failpoint which returns (void) from the function when triggered. + * Instantiate a failpoint which returns (void) from the function when + * triggered. * @param parent The parent sysctl under which to locate the sysctl * @param name The name of the failpoint in the sysctl tree (and printouts) * @return Instantly returns void, if triggered in the failpoint. @@ -153,7 +246,8 @@ __END_DECLS /** * Instantiate a failpoint which sets an error when triggered. * @param parent The parent sysctl under which to locate the sysctl - * @param name The name of the failpoint in the sysctl tree (and printouts) + * @param name The name of the failpoint in the sysctl tree (and + * printouts) * @param error_var A variable to set to the failpoint's specified * return-value when triggered */ @@ -164,7 +258,8 @@ __END_DECLS * Instantiate a failpoint which sets an error and then goes to a * specified label in the function when triggered. * @param parent The parent sysctl under which to locate the sysctl - * @param name The name of the failpoint in the sysctl tree (and printouts) + * @param name The name of the failpoint in the sysctl tree (and + * printouts) * @param error_var A variable to set to the failpoint's specified * return-value when triggered * @param label The location to goto when triggered. @@ -173,39 +268,81 @@ __END_DECLS KFAIL_POINT_CODE(parent, name, (error_var) = RETURN_VALUE; goto label) /** + * Instantiate a failpoint which sets its pre- and post-sleep callback + * mechanisms. + * @param parent The parent sysctl under which to locate the sysctl + * @param name The name of the failpoint in the sysctl tree (and + * printouts) + * @param pre_func Function pointer to the pre-sleep function, which will be + * called directly before going to sleep. + * @param pre_arg Argument to the pre-sleep function + * @param post_func Function pointer to the pot-sleep function, which will be + * called directly before going to sleep. + * @param post_arg Argument to the post-sleep function + */ +#define KFAIL_POINT_SLEEP_CALLBACKS(parent, name, pre_func, pre_arg, \ + post_func, post_arg) \ + KFAIL_POINT_CODE_SLEEP_CALLBACKS(parent, name, pre_func, \ + pre_arg, post_func, post_arg, return RETURN_VALUE) + +/** + * Instantiate a failpoint which runs arbitrary code when triggered, and sets + * its pre- and post-sleep callback mechanisms + * @param parent The parent sysctl under which to locate the sysctl + * @param name The name of the failpoint in the sysctl tree (and + * printouts) + * @param pre_func Function pointer to the pre-sleep function, which will be + * called directly before going to sleep. + * @param pre_arg Argument to the pre-sleep function + * @param post_func Function pointer to the pot-sleep function, which will be + * called directly before going to sleep. + * @param post_arg Argument to the post-sleep function + * @param code The arbitrary code to run when triggered. Can reference + * "RETURN_VALUE" if desired to extract the specified + * user return-value when triggered. Note that this is + * implemented with a do-while loop so be careful of + * break and continue statements. + */ +#define KFAIL_POINT_CODE_SLEEP_CALLBACKS(parent, name, pre_func, pre_arg, \ + post_func, post_arg, code...) \ + do { \ + _FAIL_POINT_INIT(parent, name) \ + _FAIL_POINT_NAME(name).fp_pre_sleep_fn = pre_func; \ + _FAIL_POINT_NAME(name).fp_pre_sleep_arg = pre_arg; \ + _FAIL_POINT_NAME(name).fp_post_sleep_fn = post_func; \ + _FAIL_POINT_NAME(name).fp_post_sleep_arg = post_arg; \ + _FAIL_POINT_EVAL(name, true, code) \ + } while (0) + + +/** * Instantiate a failpoint which runs arbitrary code when triggered. * @param parent The parent sysctl under which to locate the sysctl * @param name The name of the failpoint in the sysctl tree - * (and printouts) + * (and printouts) * @param code The arbitrary code to run when triggered. Can reference * "RETURN_VALUE" if desired to extract the specified * user return-value when triggered. Note that this is * implemented with a do-while loop so be careful of * break and continue statements. */ -#define KFAIL_POINT_CODE(parent, name, code) \ -do { \ - int RETURN_VALUE; \ - static struct fail_point _FAIL_POINT_NAME(name) = { \ - #name, \ - _FAIL_POINT_LOCATION(), \ - TAILQ_HEAD_INITIALIZER(_FAIL_POINT_NAME(name).fp_entries), \ - 0, \ - NULL, NULL, \ - }; \ - SYSCTL_OID(parent, OID_AUTO, name, \ - CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, \ - &_FAIL_POINT_NAME(name), 0, fail_point_sysctl, \ - "A", ""); \ - \ - if (__predict_false( \ - fail_point_eval(&_FAIL_POINT_NAME(name), &RETURN_VALUE))) { \ - \ - code; \ - \ - } \ -} while (0) +#define KFAIL_POINT_CODE(parent, name, code...) \ + do { \ + _FAIL_POINT_INIT(parent, name, 0) \ + _FAIL_POINT_EVAL(name, true, code) \ + } while (0) + +#define KFAIL_POINT_CODE_FLAGS(parent, name, flags, code...) \ + do { \ + _FAIL_POINT_INIT(parent, name, flags) \ + _FAIL_POINT_EVAL(name, true, code) \ + } while (0) +#define KFAIL_POINT_CODE_COND(parent, name, cond, flags, code...) \ + do { \ + _FAIL_POINT_INIT(parent, name, flags) \ + _FAIL_POINT_EVAL(name, cond, code) \ + } while (0) /** * @} @@ -214,6 +351,7 @@ do { \ #ifdef _KERNEL int fail_point_sysctl(SYSCTL_HANDLER_ARGS); +int fail_point_sysctl_status(SYSCTL_HANDLER_ARGS); /* The fail point sysctl tree. */ SYSCTL_DECL(_debug_fail_point); diff --git a/sys/sys/file.h b/sys/sys/file.h index 524c1ce..c30eb05 100644 --- a/sys/sys/file.h +++ b/sys/sys/file.h @@ -112,7 +112,7 @@ typedef int fo_chown_t(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred, struct thread *td); typedef int fo_sendfile_t(struct file *fp, int sockfd, struct uio *hdr_uio, struct uio *trl_uio, off_t offset, size_t nbytes, - off_t *sent, int flags, int kflags, struct thread *td); + off_t *sent, int flags, struct thread *td); typedef int fo_seek_t(struct file *fp, off_t offset, int whence, struct thread *td); typedef int fo_fill_kinfo_t(struct file *fp, struct kinfo_file *kif, @@ -376,11 +376,11 @@ fo_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred, static __inline int fo_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio, struct uio *trl_uio, off_t offset, size_t nbytes, off_t *sent, int flags, - int kflags, struct thread *td) + struct thread *td) { return ((*fp->f_ops->fo_sendfile)(fp, sockfd, hdr_uio, trl_uio, offset, - nbytes, sent, flags, kflags, td)); + nbytes, sent, flags, td)); } static __inline int diff --git a/sys/sys/intr.h b/sys/sys/intr.h index 5ef2379..04325c1 100644 --- a/sys/sys/intr.h +++ b/sys/sys/intr.h @@ -1,7 +1,6 @@ -/* $NetBSD: intr.h,v 1.7 2003/06/16 20:01:00 thorpej Exp $ */ - /*- - * Copyright (c) 1997 Mark Brinicombe. + * Copyright (c) 2015-2016 Svatopluk Kraus + * Copyright (c) 2015-2016 Michal Meloun * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -12,28 +11,20 @@ * 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 Mark Brinicombe - * for the NetBSD Project. - * 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 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 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) + * 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. * * $FreeBSD$ - * */ #ifndef _SYS_INTR_H_ @@ -41,6 +32,37 @@ #include <sys/systm.h> +enum intr_map_data_type { + INTR_MAP_DATA_ACPI, + INTR_MAP_DATA_FDT, +}; + +#ifdef DEV_ACPI +struct intr_map_data_acpi { + u_int irq; + enum intr_polarity pol; + enum intr_trigger trig; +}; +#endif +#ifdef FDT +struct intr_map_data_fdt { + u_int ncells; + pcell_t *cells; +}; +#endif + +struct intr_map_data { + enum intr_map_data_type type; + union { +#ifdef DEV_ACPI + struct intr_map_data_acpi acpi; +#endif +#ifdef FDT + struct intr_map_data_fdt fdt; +#endif + }; +}; + #ifdef notyet #define INTR_SOLO INTR_MD1 typedef int intr_irq_filter_t(void *arg, struct trapframe *tf); @@ -50,30 +72,16 @@ typedef int intr_irq_filter_t(void *arg); #define INTR_ISRC_NAMELEN (MAXCOMLEN + 1) -typedef void intr_ipi_filter_t(void *arg); - -enum intr_isrc_type { - INTR_ISRCT_NAMESPACE, - INTR_ISRCT_FDT -}; - -#define INTR_ISRCF_REGISTERED 0x01 /* registered in a controller */ -#define INTR_ISRCF_PERCPU 0x02 /* per CPU interrupt */ +#define INTR_ISRCF_IPI 0x01 /* IPI interrupt */ +#define INTR_ISRCF_PPI 0x02 /* PPI interrupt */ #define INTR_ISRCF_BOUND 0x04 /* bound to a CPU */ /* Interrupt source definition. */ struct intr_irqsrc { device_t isrc_dev; /* where isrc is mapped */ - intptr_t isrc_xref; /* device reference key */ - uintptr_t isrc_data; /* device data for isrc */ u_int isrc_irq; /* unique identificator */ - enum intr_isrc_type isrc_type; /* how is isrc decribed */ u_int isrc_flags; char isrc_name[INTR_ISRC_NAMELEN]; - uint16_t isrc_nspc_type; - uint16_t isrc_nspc_num; - enum intr_trigger isrc_trig; - enum intr_polarity isrc_pol; cpuset_t isrc_cpu; /* on which CPUs is enabled */ u_int isrc_index; u_long * isrc_count; @@ -81,47 +89,46 @@ struct intr_irqsrc { struct intr_event * isrc_event; #ifdef INTR_SOLO intr_irq_filter_t * isrc_filter; -#endif - intr_ipi_filter_t * isrc_ipifilter; void * isrc_arg; -#ifdef FDT - u_int isrc_ncells; - pcell_t isrc_cells[]; /* leave it last */ #endif }; -void intr_irq_set_name(struct intr_irqsrc *isrc, const char *fmt, ...) - __printflike(2, 3); +/* Intr interface for PIC. */ +int intr_isrc_deregister(struct intr_irqsrc *); +int intr_isrc_register(struct intr_irqsrc *, device_t, u_int, const char *, ...) + __printflike(4, 5); -void intr_irq_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf); - -#define INTR_IRQ_NSPC_NONE 0 -#define INTR_IRQ_NSPC_PLAIN 1 -#define INTR_IRQ_NSPC_IRQ 2 -#define INTR_IRQ_NSPC_IPI 3 +int intr_isrc_dispatch(struct intr_irqsrc *, struct trapframe *); +u_int intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask); -u_int intr_namespace_map_irq(device_t dev, uint16_t type, uint16_t num); -#ifdef FDT -u_int intr_fdt_map_irq(phandle_t, pcell_t *, u_int); -#endif +int intr_pic_register(device_t, intptr_t); +int intr_pic_deregister(device_t, intptr_t); +int intr_pic_claim_root(device_t, intptr_t, intr_irq_filter_t *, void *, u_int); extern device_t intr_irq_root_dev; -int intr_pic_register(device_t dev, intptr_t xref); -int intr_pic_unregister(device_t dev, intptr_t xref); -int intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter, - void *arg, u_int ipicount); +/* Intr interface for BUS. */ +int intr_map_irq(device_t, intptr_t, struct intr_map_data *, u_int *); -int intr_irq_add_handler(device_t dev, driver_filter_t, driver_intr_t, void *, - u_int, int, void **); -int intr_irq_remove_handler(device_t dev, u_int, void *); -int intr_irq_config(u_int, enum intr_trigger, enum intr_polarity); -int intr_irq_describe(u_int, void *, const char *); +int intr_alloc_irq(device_t, struct resource *); +int intr_release_irq(device_t, struct resource *); -u_int intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask); +int intr_setup_irq(device_t, struct resource *, driver_filter_t, driver_intr_t, + void *, int, void **); +int intr_teardown_irq(device_t, struct resource *, void *); + +int intr_describe_irq(device_t, struct resource *, void *, const char *); + +#ifdef DEV_ACPI +u_int intr_acpi_map_irq(device_t, u_int, enum intr_polarity, + enum intr_trigger); +#endif +#ifdef FDT +u_int intr_fdt_map_irq(phandle_t, pcell_t *, u_int); +#endif #ifdef SMP -int intr_irq_bind(u_int, int); +int intr_bind_irq(device_t, struct resource *, int); void intr_pic_init_secondary(void); diff --git a/sys/sys/libkern.h b/sys/sys/libkern.h index efbaa4a..b1b7257 100644 --- a/sys/sys/libkern.h +++ b/sys/sys/libkern.h @@ -65,6 +65,16 @@ static __inline u_quad_t uqmax(u_quad_t a, u_quad_t b) { return (a > b ? a : b); static __inline u_quad_t uqmin(u_quad_t a, u_quad_t b) { return (a < b ? a : b); } static __inline u_long ulmax(u_long a, u_long b) { return (a > b ? a : b); } static __inline u_long ulmin(u_long a, u_long b) { return (a < b ? a : b); } +static __inline __uintmax_t ummax(__uintmax_t a, __uintmax_t b) +{ + + return (a > b ? a : b); +} +static __inline __uintmax_t ummin(__uintmax_t a, __uintmax_t b) +{ + + return (a < b ? a : b); +} static __inline off_t omax(off_t a, off_t b) { return (a > b ? a : b); } static __inline off_t omin(off_t a, off_t b) { return (a < b ? a : b); } diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h index 0b9fb2b..4a48d47 100644 --- a/sys/sys/mbuf.h +++ b/sys/sys/mbuf.h @@ -44,6 +44,32 @@ #endif #endif +#ifdef _KERNEL +#include <sys/sdt.h> + +#define MBUF_PROBE1(probe, arg0) \ + SDT_PROBE1(sdt, , , probe, arg0) +#define MBUF_PROBE2(probe, arg0, arg1) \ + SDT_PROBE2(sdt, , , probe, arg0, arg1) +#define MBUF_PROBE3(probe, arg0, arg1, arg2) \ + SDT_PROBE3(sdt, , , probe, arg0, arg1, arg2) +#define MBUF_PROBE4(probe, arg0, arg1, arg2, arg3) \ + SDT_PROBE4(sdt, , , probe, arg0, arg1, arg2, arg3) +#define MBUF_PROBE5(probe, arg0, arg1, arg2, arg3, arg4) \ + SDT_PROBE5(sdt, , , probe, arg0, arg1, arg2, arg3, arg4) + +SDT_PROBE_DECLARE(sdt, , , m__init); +SDT_PROBE_DECLARE(sdt, , , m__gethdr); +SDT_PROBE_DECLARE(sdt, , , m__get); +SDT_PROBE_DECLARE(sdt, , , m__getcl); +SDT_PROBE_DECLARE(sdt, , , m__clget); +SDT_PROBE_DECLARE(sdt, , , m__cljget); +SDT_PROBE_DECLARE(sdt, , , m__cljset); +SDT_PROBE_DECLARE(sdt, , , m__free); +SDT_PROBE_DECLARE(sdt, , , m__freem); + +#endif /* _KERNEL */ + /* * Mbufs are of a single size, MSIZE (sys/param.h), which includes overhead. * An mbuf may add a single "mbuf cluster" of size MCLBYTES (also in @@ -672,42 +698,52 @@ m_init(struct mbuf *m, int how, short type, int flags) m->m_len = 0; m->m_flags = flags; m->m_type = type; - if (flags & M_PKTHDR) { - if ((error = m_pkthdr_init(m, how)) != 0) - return (error); - } + if (flags & M_PKTHDR) + error = m_pkthdr_init(m, how); + else + error = 0; - return (0); + MBUF_PROBE5(m__init, m, how, type, flags, error); + return (error); } static __inline struct mbuf * m_get(int how, short type) { + struct mbuf *m; struct mb_args args; args.flags = 0; args.type = type; - return (uma_zalloc_arg(zone_mbuf, &args, how)); + m = uma_zalloc_arg(zone_mbuf, &args, how); + MBUF_PROBE3(m__get, how, type, m); + return (m); } static __inline struct mbuf * m_gethdr(int how, short type) { + struct mbuf *m; struct mb_args args; args.flags = M_PKTHDR; args.type = type; - return (uma_zalloc_arg(zone_mbuf, &args, how)); + m = uma_zalloc_arg(zone_mbuf, &args, how); + MBUF_PROBE3(m__gethdr, how, type, m); + return (m); } static __inline struct mbuf * m_getcl(int how, short type, int flags) { + struct mbuf *m; struct mb_args args; args.flags = flags; args.type = type; - return (uma_zalloc_arg(zone_pack, &args, how)); + m = uma_zalloc_arg(zone_pack, &args, how); + MBUF_PROBE4(m__getcl, how, type, flags, m); + return (m); } /* @@ -747,6 +783,7 @@ m_cljset(struct mbuf *m, void *cl, int type) m->m_ext.ext_flags = EXT_FLAG_EMBREF; m->m_ext.ext_count = 1; m->m_flags |= M_EXT; + MBUF_PROBE3(m__cljset, m, cl, type); } static __inline void @@ -1122,6 +1159,7 @@ m_free(struct mbuf *m) { struct mbuf *n = m->m_next; + MBUF_PROBE1(m__free, m); if ((m->m_flags & (M_PKTHDR|M_NOFREE)) == (M_PKTHDR|M_NOFREE)) m_tag_delete_chain(m, NULL); if (m->m_flags & M_EXT) diff --git a/sys/sys/osd.h b/sys/sys/osd.h index 14316ae..820e0f4 100644 --- a/sys/sys/osd.h +++ b/sys/sys/osd.h @@ -59,6 +59,10 @@ int osd_register(u_int type, osd_destructor_t destructor, void osd_deregister(u_int type, u_int slot); int osd_set(u_int type, struct osd *osd, u_int slot, void *value); +void *osd_reserve(u_int slot); +int osd_set_reserved(u_int type, struct osd *osd, u_int slot, void *rsv, + void *value); +void osd_free_reserved(void *rsv); void *osd_get(u_int type, struct osd *osd, u_int slot); void osd_del(u_int type, struct osd *osd, u_int slot); int osd_call(u_int type, u_int method, void *obj, void *data); @@ -71,6 +75,8 @@ void osd_exit(u_int type, struct osd *osd); osd_deregister(OSD_THREAD, (slot)) #define osd_thread_set(td, slot, value) \ osd_set(OSD_THREAD, &(td)->td_osd, (slot), (value)) +#define osd_thread_set_reserved(td, slot, rsv, value) \ + osd_set_reserved(OSD_THREAD, &(td)->td_osd, (slot), (rsv), (value)) #define osd_thread_get(td, slot) \ osd_get(OSD_THREAD, &(td)->td_osd, (slot)) #define osd_thread_del(td, slot) do { \ @@ -88,6 +94,8 @@ void osd_exit(u_int type, struct osd *osd); osd_deregister(OSD_JAIL, (slot)) #define osd_jail_set(pr, slot, value) \ osd_set(OSD_JAIL, &(pr)->pr_osd, (slot), (value)) +#define osd_jail_set_reserved(pr, slot, rsv, value) \ + osd_set_reserved(OSD_JAIL, &(pr)->pr_osd, (slot), (rsv), (value)) #define osd_jail_get(pr, slot) \ osd_get(OSD_JAIL, &(pr)->pr_osd, (slot)) #define osd_jail_del(pr, slot) \ diff --git a/sys/sys/param.h b/sys/sys/param.h index 9108576..987453c 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -58,7 +58,7 @@ * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1100102 /* Master, propagated to newvers */ +#define __FreeBSD_version 1100104 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, diff --git a/sys/sys/proc.h b/sys/sys/proc.h index f9ca6d9..2d1769e 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -162,6 +162,7 @@ struct pargs { */ struct cpuset; struct filecaps; +struct filemon; struct kaioinfo; struct kaudit_record; struct kdtrace_proc; @@ -253,6 +254,7 @@ struct thread { int td_slptick; /* (t) Time at sleep. */ int td_blktick; /* (t) Time spent blocked. */ int td_swvoltick; /* (t) Time at last SW_VOL switch. */ + int td_swinvoltick; /* (t) Time at last SW_INVOL switch. */ u_int td_cow; /* (*) Number of copy-on-write faults */ struct rusage td_ru; /* (t) rusage information. */ struct rusage_ext td_rux; /* (t) Internal rusage information. */ @@ -580,6 +582,7 @@ struct proc { struct procdesc *p_procdesc; /* (e) Process descriptor, if any. */ u_int p_treeflag; /* (e) P_TREE flags */ int p_pendingexits; /* (c) Count of pending thread exits. */ + struct filemon *p_filemon; /* (c) filemon-specific data. */ /* End area that is zeroed on creation. */ #define p_endzero p_magic diff --git a/sys/sys/racct.h b/sys/sys/racct.h index 3075856..8d1f2fa 100644 --- a/sys/sys/racct.h +++ b/sys/sys/racct.h @@ -98,7 +98,7 @@ extern int racct_enable; /* * Resource usage can drop, as opposed to only grow. When the process - * terminates, its resource usage is freed from the respective + * terminates, its resource usage is subtracted from the respective * per-credential racct containers. */ #define RACCT_IS_RECLAIMABLE(X) (racct_types[X] & RACCT_RECLAIMABLE) @@ -126,8 +126,7 @@ extern int racct_enable; * When a process terminates, its resource usage is not automatically * subtracted from per-credential racct containers. Instead, the resource * usage of per-credential racct containers decays in time. - * Resource usage can olso drop for such resource. - * So far, the only such resource is RACCT_PCTCPU. + * Resource usage can also drop for such resource. */ #define RACCT_IS_DECAYING(X) (racct_types[X] & RACCT_DECAYING) diff --git a/sys/sys/sleepqueue.h b/sys/sys/sleepqueue.h index cdb7a39..d59dc7e 100644 --- a/sys/sys/sleepqueue.h +++ b/sys/sys/sleepqueue.h @@ -107,5 +107,11 @@ int sleepq_type(void *wchan); void sleepq_wait(void *wchan, int pri); int sleepq_wait_sig(void *wchan, int pri); +#ifdef STACK +struct sbuf; +int sleepq_sbuf_print_stacks(struct sbuf *sb, void *wchan, int queue, + int *count_stacks_printed); +#endif + #endif /* _KERNEL */ #endif /* !_SYS_SLEEPQUEUE_H_ */ diff --git a/sys/sys/smp.h b/sys/sys/smp.h index 58b1754..904b9f7 100644 --- a/sys/sys/smp.h +++ b/sys/sys/smp.h @@ -17,9 +17,52 @@ #ifndef LOCORE #include <sys/cpuset.h> +#include <sys/queue.h> /* - * Topology of a NUMA or HTT system. + * Types of nodes in the topological tree. + */ +typedef enum { + /* No node has this type; can be used in topo API calls. */ + TOPO_TYPE_DUMMY, + /* Processing unit aka computing unit aka logical CPU. */ + TOPO_TYPE_PU, + /* Physical subdivision of a package. */ + TOPO_TYPE_CORE, + /* CPU L1/L2/L3 cache. */ + TOPO_TYPE_CACHE, + /* Package aka chip, equivalent to socket. */ + TOPO_TYPE_PKG, + /* NUMA node. */ + TOPO_TYPE_NODE, + /* Other logical or physical grouping of PUs. */ + /* E.g. PUs on the same dye, or PUs sharing an FPU. */ + TOPO_TYPE_GROUP, + /* The whole system. */ + TOPO_TYPE_SYSTEM +} topo_node_type; + +/* Hardware indenitifier of a topology component. */ +typedef unsigned int hwid_t; +/* Logical CPU idenitifier. */ +typedef int cpuid_t; + +/* A node in the topology. */ +struct topo_node { + struct topo_node *parent; + TAILQ_HEAD(topo_children, topo_node) children; + TAILQ_ENTRY(topo_node) siblings; + cpuset_t cpuset; + topo_node_type type; + uintptr_t subtype; + hwid_t hwid; + cpuid_t id; + int nchildren; + int cpu_count; +}; + +/* + * Scheduling topology of a NUMA or SMP system. * * The top level topology is an array of pointers to groups. Each group * contains a bitmask of cpus in its group or subgroups. It may also @@ -52,6 +95,8 @@ typedef struct cpu_group *cpu_group_t; #define CG_SHARE_L2 2 #define CG_SHARE_L3 3 +#define MAX_CACHE_LEVELS CG_SHARE_L3 + /* * Behavior modifiers for load balancing and affinity. */ @@ -60,10 +105,29 @@ typedef struct cpu_group *cpu_group_t; #define CG_FLAG_THREAD (CG_FLAG_HTT | CG_FLAG_SMT) /* Any threading. */ /* - * Convenience routines for building topologies. + * Convenience routines for building and traversing topologies. */ #ifdef SMP +void topo_init_node(struct topo_node *node); +void topo_init_root(struct topo_node *root); +struct topo_node * topo_add_node_by_hwid(struct topo_node *parent, int hwid, + topo_node_type type, uintptr_t subtype); +struct topo_node * topo_find_node_by_hwid(struct topo_node *parent, int hwid, + topo_node_type type, uintptr_t subtype); +void topo_promote_child(struct topo_node *child); +struct topo_node * topo_next_node(struct topo_node *top, + struct topo_node *node); +struct topo_node * topo_next_nonchild_node(struct topo_node *top, + struct topo_node *node); +void topo_set_pu_id(struct topo_node *node, cpuid_t id); +int topo_analyze(struct topo_node *topo_root, int all, int *pkg_count, + int *cores_per_pkg, int *thrs_per_core); + +#define TOPO_FOREACH(i, root) \ + for (i = root; i != NULL; i = topo_next_node(root, i)) + struct cpu_group *smp_topo(void); +struct cpu_group *smp_topo_alloc(u_int count); struct cpu_group *smp_topo_none(void); struct cpu_group *smp_topo_1level(int l1share, int l1count, int l1flags); struct cpu_group *smp_topo_2level(int l2share, int l2count, int l1share, diff --git a/sys/sys/socket.h b/sys/sys/socket.h index c20b075..649ba00 100644 --- a/sys/sys/socket.h +++ b/sys/sys/socket.h @@ -594,7 +594,6 @@ struct sf_hdtr { #define SF_FLAGS(rh, flags) (((rh) << 16) | (flags)) #ifdef _KERNEL -#define SFK_COMPAT 0x00000001 #define SF_READAHEAD(flags) ((flags) >> 16) #endif /* _KERNEL */ diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h index fef33b8..8a32a64 100644 --- a/sys/sys/syscall.h +++ b/sys/sys/syscall.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/kern/syscalls.master 296572 2016-03-09 19:05:11Z jhb + * created from FreeBSD: head/sys/kern/syscalls.master 297167 2016-03-21 21:37:33Z jhb */ #define SYS_syscall 0 diff --git a/sys/sys/syscall.mk b/sys/sys/syscall.mk index 3f62141..a264838 100644 --- a/sys/sys/syscall.mk +++ b/sys/sys/syscall.mk @@ -1,7 +1,7 @@ # FreeBSD system call object files. # DO NOT EDIT-- this file is automatically generated. # $FreeBSD$ -# created from FreeBSD: head/sys/kern/syscalls.master 296572 2016-03-09 19:05:11Z jhb +# created from FreeBSD: head/sys/kern/syscalls.master 297167 2016-03-21 21:37:33Z jhb MIASM = \ syscall.o \ exit.o \ diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index 7c1e79c..eb5eef0 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -204,6 +204,7 @@ int sysctl_handle_long(SYSCTL_HANDLER_ARGS); int sysctl_handle_string(SYSCTL_HANDLER_ARGS); int sysctl_handle_opaque(SYSCTL_HANDLER_ARGS); int sysctl_handle_counter_u64(SYSCTL_HANDLER_ARGS); +int sysctl_handle_counter_u64_array(SYSCTL_HANDLER_ARGS); int sysctl_handle_uma_zone_max(SYSCTL_HANDLER_ARGS); int sysctl_handle_uma_zone_cur(SYSCTL_HANDLER_ARGS); @@ -640,11 +641,34 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry); #define SYSCTL_ADD_COUNTER_U64(ctx, parent, nbr, name, access, ptr, descr) \ ({ \ + counter_u64_t *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U64); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_U64 | CTLFLAG_MPSAFE | (access), \ - ptr, 0, sysctl_handle_counter_u64, "QU", __DESCR(descr)); \ + __ptr, 0, sysctl_handle_counter_u64, "QU", __DESCR(descr)); \ +}) + +/* Oid for an array of counter(9)s. The pointer and length must be non zero. */ +#define SYSCTL_COUNTER_U64_ARRAY(parent, nbr, name, access, ptr, len, descr) \ + SYSCTL_OID(parent, nbr, name, \ + CTLTYPE_OPAQUE | CTLFLAG_MPSAFE | (access), \ + (ptr), (len), sysctl_handle_counter_u64_array, "S", descr); \ + CTASSERT((((access) & CTLTYPE) == 0 || \ + ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_OPAQUE) && \ + sizeof(counter_u64_t) == sizeof(*(ptr)) && \ + sizeof(uint64_t) == sizeof(**(ptr))) + +#define SYSCTL_ADD_COUNTER_U64_ARRAY(ctx, parent, nbr, name, access, \ + ptr, len, descr) \ +({ \ + counter_u64_t *__ptr = (ptr); \ + CTASSERT(((access) & CTLTYPE) == 0 || \ + ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_OPAQUE); \ + sysctl_add_oid(ctx, parent, nbr, name, \ + CTLTYPE_OPAQUE | CTLFLAG_MPSAFE | (access), \ + __ptr, len, sysctl_handle_counter_u64_array, "S", \ + __DESCR(descr)); \ }) /* Oid for an opaque object. Specified by a pointer and a length. */ diff --git a/sys/sys/sysproto.h b/sys/sys/sysproto.h index e3151f6..6d2357a 100644 --- a/sys/sys/sysproto.h +++ b/sys/sys/sysproto.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/kern/syscalls.master 296572 2016-03-09 19:05:11Z jhb + * created from FreeBSD: head/sys/kern/syscalls.master 297167 2016-03-21 21:37:33Z jhb */ #ifndef _SYS_SYSPROTO_H_ diff --git a/sys/sys/systm.h b/sys/sys/systm.h index 026a03c..9c6e450 100644 --- a/sys/sys/systm.h +++ b/sys/sys/systm.h @@ -148,10 +148,14 @@ extern char **kenvp; extern const void *zero_region; /* address space maps to a zeroed page */ extern int unmapped_buf_allowed; -extern int iosize_max_clamp; -extern int devfs_iosize_max_clamp; -#define IOSIZE_MAX (iosize_max_clamp ? INT_MAX : SSIZE_MAX) -#define DEVFS_IOSIZE_MAX (devfs_iosize_max_clamp ? INT_MAX : SSIZE_MAX) + +#ifdef __LP64__ +#define IOSIZE_MAX iosize_max() +#define DEVFS_IOSIZE_MAX devfs_iosize_max() +#else +#define IOSIZE_MAX SSIZE_MAX +#define DEVFS_IOSIZE_MAX SSIZE_MAX +#endif /* * General function declarations. @@ -403,6 +407,11 @@ struct cdev; dev_t dev2udev(struct cdev *x); const char *devtoname(struct cdev *cdev); +#ifdef __LP64__ +size_t devfs_iosize_max(void); +size_t iosize_max(void); +#endif + int poll_no_poll(int events); /* XXX: Should be void nanodelay(u_int nsec); */ diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c index 19c00de..799efe3 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -2270,8 +2270,6 @@ ffs_blkfree_cg(ump, fs, devvp, bno, size, inum, dephd) bdwrite(bp); } -TASKQUEUE_DEFINE_THREAD(ffs_trim); - struct ffs_blkfree_trim_params { struct task task; struct ufsmount *ump; @@ -2294,6 +2292,7 @@ ffs_blkfree_trim_task(ctx, pending) ffs_blkfree_cg(tp->ump, tp->ump->um_fs, tp->devvp, tp->bno, tp->size, tp->inum, tp->pdephd); vn_finished_secondary_write(UFSTOVFS(tp->ump)); + atomic_add_int(&tp->ump->um_trim_inflight, -1); free(tp, M_TEMP); } @@ -2306,7 +2305,7 @@ ffs_blkfree_trim_completed(bip) tp = bip->bio_caller2; g_destroy_bio(bip); TASK_INIT(&tp->task, 0, ffs_blkfree_trim_task, tp); - taskqueue_enqueue(taskqueue_ffs_trim, &tp->task); + taskqueue_enqueue(tp->ump->um_trim_tq, &tp->task); } void @@ -2350,6 +2349,7 @@ ffs_blkfree(ump, fs, devvp, bno, size, inum, vtype, dephd) * reordering, TRIM might be issued after we reuse the block * and write some new data into it. */ + atomic_add_int(&ump->um_trim_inflight, 1); tp = malloc(sizeof(struct ffs_blkfree_trim_params), M_TEMP, M_WAITOK); tp->ump = ump; tp->devvp = devvp; diff --git a/sys/ufs/ffs/ffs_softdep.c b/sys/ufs/ffs/ffs_softdep.c index 04ea39c..bedc8e1 100644 --- a/sys/ufs/ffs/ffs_softdep.c +++ b/sys/ufs/ffs/ffs_softdep.c @@ -1937,9 +1937,9 @@ softdep_waitidle(struct mount *mp, int flags __unused) vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); error = VOP_FSYNC(devvp, MNT_WAIT, td); VOP_UNLOCK(devvp, 0); + ACQUIRE_LOCK(ump); if (error != 0) break; - ACQUIRE_LOCK(ump); } ump->softdep_req = 0; if (i == SU_WAITIDLE_RETRIES && error == 0 && ump->softdep_deps != 0) { diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c index a82ef61..b89dc33 100644 --- a/sys/ufs/ffs/ffs_vfsops.c +++ b/sys/ufs/ffs/ffs_vfsops.c @@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$"); #include <sys/namei.h> #include <sys/priv.h> #include <sys/proc.h> +#include <sys/taskqueue.h> #include <sys/kernel.h> #include <sys/vnode.h> #include <sys/mount.h> @@ -1005,6 +1006,12 @@ ffs_mountfs(devvp, mp, td) mp->mnt_stat.f_mntonname); ump->um_candelete = 0; } + if (ump->um_candelete) { + ump->um_trim_tq = taskqueue_create("trim", M_WAITOK, + taskqueue_thread_enqueue, &ump->um_trim_tq); + taskqueue_start_threads(&ump->um_trim_tq, 1, PVFS, + "%s trim", mp->mnt_stat.f_mntonname); + } } ump->um_mountp = mp; @@ -1260,6 +1267,12 @@ ffs_unmount(mp, mntflags) } if (susp) vfs_write_resume(mp, VR_START_WRITE); + if (ump->um_trim_tq != NULL) { + while (ump->um_trim_inflight != 0) + pause("ufsutr", hz); + taskqueue_drain_all(ump->um_trim_tq); + taskqueue_free(ump->um_trim_tq); + } DROP_GIANT(); g_topology_lock(); if (ump->um_fsckpid > 0) { diff --git a/sys/ufs/ufs/ufs_extattr.c b/sys/ufs/ufs/ufs_extattr.c index 209d93e..b521a42 100644 --- a/sys/ufs/ufs/ufs_extattr.c +++ b/sys/ufs/ufs/ufs_extattr.c @@ -597,8 +597,6 @@ ufs_extattr_enable(struct ufsmount *ump, int attrnamespace, attribute = malloc(sizeof(struct ufs_extattr_list_entry), M_UFS_EXTATTR, M_WAITOK); - if (attribute == NULL) - return (ENOMEM); if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { error = EOPNOTSUPP; diff --git a/sys/ufs/ufs/ufsmount.h b/sys/ufs/ufs/ufsmount.h index 7148b76..838c1e3 100644 --- a/sys/ufs/ufs/ufsmount.h +++ b/sys/ufs/ufs/ufsmount.h @@ -50,6 +50,7 @@ MALLOC_DECLARE(M_UFSMNT); struct buf; struct inode; struct nameidata; +struct taskqueue; struct timeval; struct ucred; struct uio; @@ -85,11 +86,15 @@ struct ufsmount { int64_t um_savedmaxfilesize; /* XXX - limit maxfilesize */ int um_candelete; /* devvp supports TRIM */ int um_writesuspended; /* suspension in progress */ - int (*um_balloc)(struct vnode *, off_t, int, struct ucred *, int, struct buf **); + u_int um_trim_inflight; + struct taskqueue *um_trim_tq; + int (*um_balloc)(struct vnode *, off_t, int, struct ucred *, + int, struct buf **); int (*um_blkatoff)(struct vnode *, off_t, char **, struct buf **); int (*um_truncate)(struct vnode *, off_t, int, struct ucred *); int (*um_update)(struct vnode *, int); - int (*um_valloc)(struct vnode *, int, struct ucred *, struct vnode **); + int (*um_valloc)(struct vnode *, int, struct ucred *, + struct vnode **); int (*um_vfree)(struct vnode *, ino_t, int); void (*um_ifree)(struct ufsmount *, struct inode *); int (*um_rdonly)(struct inode *); diff --git a/sys/x86/include/apicreg.h b/sys/x86/include/apicreg.h index 35630c7..d3cfaaf 100644 --- a/sys/x86/include/apicreg.h +++ b/sys/x86/include/apicreg.h @@ -399,10 +399,11 @@ typedef struct IOAPIC ioapic_t; #define APIC_LVTT_VECTOR 0x000000ff #define APIC_LVTT_DS 0x00001000 #define APIC_LVTT_M 0x00010000 -#define APIC_LVTT_TM 0x00020000 +#define APIC_LVTT_TM 0x00060000 # define APIC_LVTT_TM_ONE_SHOT 0x00000000 # define APIC_LVTT_TM_PERIODIC 0x00020000 - +# define APIC_LVTT_TM_TSCDLT 0x00040000 +# define APIC_LVTT_TM_RSRV 0x00060000 /* APIC timer current count */ #define APIC_TIMER_MAX_COUNT 0xffffffff diff --git a/sys/x86/include/specialreg.h b/sys/x86/include/specialreg.h index 5f2c010..c2d2c59 100644 --- a/sys/x86/include/specialreg.h +++ b/sys/x86/include/specialreg.h @@ -457,6 +457,7 @@ #define MSR_DRAM_ENERGY_STATUS 0x619 #define MSR_PP0_ENERGY_STATUS 0x639 #define MSR_PP1_ENERGY_STATUS 0x641 +#define MSR_TSC_DEADLINE 0x6e0 /* Writes are not serializing */ /* * VMX MSRs @@ -478,7 +479,8 @@ #define MSR_VMX_TRUE_ENTRY_CTLS 0x490 /* - * X2APIC MSRs + * X2APIC MSRs. + * Writes are not serializing. */ #define MSR_APIC_000 0x800 #define MSR_APIC_ID 0x802 diff --git a/sys/x86/include/x86_var.h b/sys/x86/include/x86_var.h index c349913..46ce1a0 100644 --- a/sys/x86/include/x86_var.h +++ b/sys/x86/include/x86_var.h @@ -86,6 +86,13 @@ struct fpreg; struct dbreg; struct dumperinfo; +/* + * The interface type of the interrupt handler entry point cannot be + * expressed in C. Use simplest non-variadic function type as an + * approximation. + */ +typedef void alias_for_inthand_t(void); + void *alloc_fpusave(int flags); void busdma_swi(void); bool cpu_mwait_usable(void); diff --git a/sys/i386/i386/autoconf.c b/sys/x86/x86/autoconf.c index 029ed2e..813fe4c 100644 --- a/sys/i386/i386/autoconf.c +++ b/sys/x86/x86/autoconf.c @@ -65,7 +65,9 @@ __FBSDID("$FreeBSD$"); #include <net/ethernet.h> #include <netinet/in.h> +#ifdef PC98 #include <machine/bootinfo.h> +#endif #include <machine/md_var.h> #ifdef DEV_ISA @@ -92,7 +94,7 @@ configure_first(dummy) void *dummy; { - /* nexus0 is the top of the i386 device tree */ + /* nexus0 is the top of the x86 device tree */ device_add_child(root_bus, "nexus", 0); } @@ -101,13 +103,6 @@ configure(dummy) void *dummy; { - /* - * Enable interrupts on the processor. The interrupts are still - * disabled in the interrupt controllers until interrupt handlers - * are registered. - */ - enable_intr(); - /* initialize new bus architecture */ root_bus_configure(); diff --git a/sys/x86/x86/intr_machdep.c b/sys/x86/x86/intr_machdep.c index 35ec992..6678887 100644 --- a/sys/x86/x86/intr_machdep.c +++ b/sys/x86/x86/intr_machdep.c @@ -393,6 +393,21 @@ intr_init(void *dummy __unused) } SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL); +static void +intr_init_final(void *dummy __unused) +{ + + /* + * Enable interrupts on the BSP after all of the interrupt + * controllers are initialized. Device interrupts are still + * disabled in the interrupt controllers until interrupt + * handlers are registered. Interrupts are enabled on each AP + * after their first context switch. + */ + enable_intr(); +} +SYSINIT(intr_init_final, SI_SUB_INTR, SI_ORDER_ANY, intr_init_final, NULL); + #ifndef DEV_ATPIC /* Initialize the two 8259A's to a known-good shutdown state. */ void diff --git a/sys/x86/x86/io_apic.c b/sys/x86/x86/io_apic.c index 44609e6..1a2cc3c 100644 --- a/sys/x86/x86/io_apic.c +++ b/sys/x86/x86/io_apic.c @@ -987,14 +987,6 @@ apic_add_resource(device_t dev, int rid, vm_paddr_t base, size_t length) { int error; -#ifdef PAE - /* - * Resources use long's to track resources, so we can't - * include memory regions above 4GB. - */ - if (base >= ~0ul) - return; -#endif error = bus_set_resource(dev, SYS_RES_MEMORY, rid, base, length); if (error) panic("apic_add_resource: resource %d failed set with %d", rid, diff --git a/sys/x86/x86/local_apic.c b/sys/x86/x86/local_apic.c index 7fc6278..82efae3 100644 --- a/sys/x86/x86/local_apic.c +++ b/sys/x86/x86/local_apic.c @@ -56,6 +56,7 @@ __FBSDID("$FreeBSD$"); #include <vm/pmap.h> #include <x86/apicreg.h> +#include <machine/clock.h> #include <machine/cpufunc.h> #include <machine/cputypes.h> #include <machine/frame.h> @@ -94,6 +95,13 @@ CTASSERT(IPI_STOP < APIC_SPURIOUS_INT); #define IRQ_DTRACE_RET (NUM_IO_INTS + 3) #define IRQ_EVTCHN (NUM_IO_INTS + 4) +enum lat_timer_mode { + LAT_MODE_UNDEF = 0, + LAT_MODE_PERIODIC = 1, + LAT_MODE_ONESHOT = 2, + LAT_MODE_DEADLINE = 3, +}; + /* * Support for local APICs. Local APICs manage interrupts on each * individual processor as opposed to I/O APICs which receive interrupts @@ -119,9 +127,10 @@ struct lapic { u_int la_cluster_id:2; u_int la_present:1; u_long *la_timer_count; - u_long la_timer_period; - u_int la_timer_mode; - uint32_t lvt_timer_cache; + uint64_t la_timer_period; + enum lat_timer_mode la_timer_mode; + uint32_t lvt_timer_base; + uint32_t lvt_timer_last; /* Include IDT_SYSCALL to make indexing easier. */ int la_ioint_irqs[APIC_NUM_IOINTS + 1]; } static lapics[MAX_APIC_ID + 1]; @@ -160,13 +169,19 @@ volatile char *lapic_map; vm_paddr_t lapic_paddr; int x2apic_mode; int lapic_eoi_suppression; +static int lapic_timer_tsc_deadline; static u_long lapic_timer_divisor; static struct eventtimer lapic_et; +#ifdef SMP +static uint64_t lapic_ipi_wait_mult; +#endif SYSCTL_NODE(_hw, OID_AUTO, apic, CTLFLAG_RD, 0, "APIC options"); SYSCTL_INT(_hw_apic, OID_AUTO, x2apic_mode, CTLFLAG_RD, &x2apic_mode, 0, ""); SYSCTL_INT(_hw_apic, OID_AUTO, eoi_suppression, CTLFLAG_RD, &lapic_eoi_suppression, 0, ""); +SYSCTL_INT(_hw_apic, OID_AUTO, timer_tsc_deadline, CTLFLAG_RD, + &lapic_timer_tsc_deadline, 0, ""); static uint32_t lapic_read32(enum LAPIC_REGISTERS reg) @@ -256,10 +271,10 @@ native_lapic_enable_x2apic(void) static void lapic_enable(void); static void lapic_resume(struct pic *pic, bool suspend_cancelled); -static void lapic_timer_oneshot(struct lapic *, - u_int count, int enable_int); -static void lapic_timer_periodic(struct lapic *, - u_int count, int enable_int); +static void lapic_timer_oneshot(struct lapic *); +static void lapic_timer_oneshot_nointr(struct lapic *, uint32_t); +static void lapic_timer_periodic(struct lapic *); +static void lapic_timer_deadline(struct lapic *); static void lapic_timer_stop(struct lapic *); static void lapic_timer_set_divisor(u_int divisor); static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value); @@ -391,6 +406,9 @@ lvt_mode(struct lapic *la, u_int pin, uint32_t value) static void native_lapic_init(vm_paddr_t addr) { +#ifdef SMP + uint64_t r, r1, r2, rx; +#endif uint32_t ver; u_int regs[4]; int i, arat; @@ -450,7 +468,14 @@ native_lapic_init(vm_paddr_t addr) if (!arat) { lapic_et.et_flags |= ET_FLAGS_C3STOP; lapic_et.et_quality -= 200; + } else if ((cpu_feature & CPUID_TSC) != 0 && + (cpu_feature2 & CPUID2_TSCDLT) != 0 && + tsc_is_invariant && tsc_freq != 0) { + lapic_timer_tsc_deadline = 1; + TUNABLE_INT_FETCH("hw.lapic_tsc_deadline", + &lapic_timer_tsc_deadline); } + lapic_et.et_frequency = 0; /* We don't know frequency yet, so trying to guess. */ lapic_et.et_min_period = 0x00001000LL; @@ -484,6 +509,38 @@ native_lapic_init(vm_paddr_t addr) TUNABLE_INT_FETCH("hw.lapic_eoi_suppression", &lapic_eoi_suppression); } + +#ifdef SMP +#define LOOPS 1000000 + /* + * Calibrate the busy loop waiting for IPI ack in xAPIC mode. + * lapic_ipi_wait_mult contains the number of iterations which + * approximately delay execution for 1 microsecond (the + * argument to native_lapic_ipi_wait() is in microseconds). + * + * We assume that TSC is present and already measured. + * Possible TSC frequency jumps are irrelevant to the + * calibration loop below, the CPU clock management code is + * not yet started, and we do not enter sleep states. + */ + KASSERT((cpu_feature & CPUID_TSC) != 0 && tsc_freq != 0, + ("TSC not initialized")); + r = rdtsc(); + for (rx = 0; rx < LOOPS; rx++) { + (void)lapic_read_icr_lo(); + ia32_pause(); + } + r = rdtsc() - r; + r1 = tsc_freq * LOOPS; + r2 = r * 1000000; + lapic_ipi_wait_mult = r1 >= r2 ? r1 / r2 : 1; + if (bootverbose) { + printf("LAPIC: ipi_wait() us multiplier %ju (r %ju tsc %ju)\n", + (uintmax_t)lapic_ipi_wait_mult, (uintmax_t)r, + (uintmax_t)tsc_freq); + } +#undef LOOPS +#endif /* SMP */ } /* @@ -604,23 +661,35 @@ native_lapic_setup(int boot) } /* Program timer LVT and setup handler. */ - la->lvt_timer_cache = lvt_mode(la, APIC_LVT_TIMER, + la->lvt_timer_base = lvt_mode(la, APIC_LVT_TIMER, lapic_read32(LAPIC_LVT_TIMER)); - lapic_write32(LAPIC_LVT_TIMER, la->lvt_timer_cache); + la->lvt_timer_last = la->lvt_timer_base; + lapic_write32(LAPIC_LVT_TIMER, la->lvt_timer_base); if (boot) { snprintf(buf, sizeof(buf), "cpu%d:timer", PCPU_GET(cpuid)); intrcnt_add(buf, &la->la_timer_count); } /* Setup the timer if configured. */ - if (la->la_timer_mode != 0) { + if (la->la_timer_mode != LAT_MODE_UNDEF) { KASSERT(la->la_timer_period != 0, ("lapic%u: zero divisor", lapic_id())); - lapic_timer_set_divisor(lapic_timer_divisor); - if (la->la_timer_mode == 1) - lapic_timer_periodic(la, la->la_timer_period, 1); - else - lapic_timer_oneshot(la, la->la_timer_period, 1); + switch (la->la_timer_mode) { + case LAT_MODE_PERIODIC: + lapic_timer_set_divisor(lapic_timer_divisor); + lapic_timer_periodic(la); + break; + case LAT_MODE_ONESHOT: + lapic_timer_set_divisor(lapic_timer_divisor); + lapic_timer_oneshot(la); + break; + case LAT_MODE_DEADLINE: + lapic_timer_deadline(la); + break; + default: + panic("corrupted la_timer_mode %p %d", la, + la->la_timer_mode); + } } /* Program error LVT and clear any existing errors. */ @@ -722,46 +791,75 @@ native_lapic_disable_pmc(void) #endif } +static void +lapic_calibrate_initcount(struct eventtimer *et, struct lapic *la) +{ + u_long value; + + /* Start off with a divisor of 2 (power on reset default). */ + lapic_timer_divisor = 2; + /* Try to calibrate the local APIC timer. */ + do { + lapic_timer_set_divisor(lapic_timer_divisor); + lapic_timer_oneshot_nointr(la, APIC_TIMER_MAX_COUNT); + DELAY(1000000); + value = APIC_TIMER_MAX_COUNT - lapic_read32(LAPIC_CCR_TIMER); + if (value != APIC_TIMER_MAX_COUNT) + break; + lapic_timer_divisor <<= 1; + } while (lapic_timer_divisor <= 128); + if (lapic_timer_divisor > 128) + panic("lapic: Divisor too big"); + if (bootverbose) { + printf("lapic: Divisor %lu, Frequency %lu Hz\n", + lapic_timer_divisor, value); + } + et->et_frequency = value; +} + +static void +lapic_calibrate_deadline(struct eventtimer *et, struct lapic *la __unused) +{ + + et->et_frequency = tsc_freq; + if (bootverbose) { + printf("lapic: deadline tsc mode, Frequency %ju Hz\n", + (uintmax_t)et->et_frequency); + } +} + static int lapic_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { struct lapic *la; - u_long value; la = &lapics[PCPU_GET(apic_id)]; if (et->et_frequency == 0) { - /* Start off with a divisor of 2 (power on reset default). */ - lapic_timer_divisor = 2; - /* Try to calibrate the local APIC timer. */ - do { - lapic_timer_set_divisor(lapic_timer_divisor); - lapic_timer_oneshot(la, APIC_TIMER_MAX_COUNT, 0); - DELAY(1000000); - value = APIC_TIMER_MAX_COUNT - - lapic_read32(LAPIC_CCR_TIMER); - if (value != APIC_TIMER_MAX_COUNT) - break; - lapic_timer_divisor <<= 1; - } while (lapic_timer_divisor <= 128); - if (lapic_timer_divisor > 128) - panic("lapic: Divisor too big"); - if (bootverbose) - printf("lapic: Divisor %lu, Frequency %lu Hz\n", - lapic_timer_divisor, value); - et->et_frequency = value; + if (lapic_timer_tsc_deadline) + lapic_calibrate_deadline(et, la); + else + lapic_calibrate_initcount(et, la); et->et_min_period = (0x00000002LLU << 32) / et->et_frequency; et->et_max_period = (0xfffffffeLLU << 32) / et->et_frequency; } - if (la->la_timer_mode == 0) - lapic_timer_set_divisor(lapic_timer_divisor); if (period != 0) { - la->la_timer_mode = 1; - la->la_timer_period = ((uint32_t)et->et_frequency * period) >> 32; - lapic_timer_periodic(la, la->la_timer_period, 1); + if (la->la_timer_mode == LAT_MODE_UNDEF) + lapic_timer_set_divisor(lapic_timer_divisor); + la->la_timer_mode = LAT_MODE_PERIODIC; + la->la_timer_period = ((uint32_t)et->et_frequency * period) >> + 32; + lapic_timer_periodic(la); + } else if (lapic_timer_tsc_deadline) { + la->la_timer_mode = LAT_MODE_DEADLINE; + la->la_timer_period = (et->et_frequency * first) >> 32; + lapic_timer_deadline(la); } else { - la->la_timer_mode = 2; - la->la_timer_period = ((uint32_t)et->et_frequency * first) >> 32; - lapic_timer_oneshot(la, la->la_timer_period, 1); + if (la->la_timer_mode == LAT_MODE_UNDEF) + lapic_timer_set_divisor(lapic_timer_divisor); + la->la_timer_mode = LAT_MODE_ONESHOT; + la->la_timer_period = ((uint32_t)et->et_frequency * first) >> + 32; + lapic_timer_oneshot(la); } return (0); } @@ -769,10 +867,11 @@ lapic_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period) static int lapic_et_stop(struct eventtimer *et) { - struct lapic *la = &lapics[PCPU_GET(apic_id)]; + struct lapic *la; - la->la_timer_mode = 0; + la = &lapics[PCPU_GET(apic_id)]; lapic_timer_stop(la); + la->la_timer_mode = LAT_MODE_UNDEF; return (0); } @@ -1071,42 +1170,76 @@ lapic_timer_set_divisor(u_int divisor) } static void -lapic_timer_oneshot(struct lapic *la, u_int count, int enable_int) +lapic_timer_oneshot(struct lapic *la) { uint32_t value; - value = la->lvt_timer_cache; - value &= ~APIC_LVTT_TM; + value = la->lvt_timer_base; + value &= ~(APIC_LVTT_TM | APIC_LVT_M); value |= APIC_LVTT_TM_ONE_SHOT; - if (enable_int) - value &= ~APIC_LVT_M; + la->lvt_timer_last = value; lapic_write32(LAPIC_LVT_TIMER, value); - lapic_write32(LAPIC_ICR_TIMER, count); + lapic_write32(LAPIC_ICR_TIMER, la->la_timer_period); } static void -lapic_timer_periodic(struct lapic *la, u_int count, int enable_int) +lapic_timer_oneshot_nointr(struct lapic *la, uint32_t count) { uint32_t value; - value = la->lvt_timer_cache; + value = la->lvt_timer_base; value &= ~APIC_LVTT_TM; - value |= APIC_LVTT_TM_PERIODIC; - if (enable_int) - value &= ~APIC_LVT_M; + value |= APIC_LVTT_TM_ONE_SHOT | APIC_LVT_M; + la->lvt_timer_last = value; lapic_write32(LAPIC_LVT_TIMER, value); lapic_write32(LAPIC_ICR_TIMER, count); } static void -lapic_timer_stop(struct lapic *la) +lapic_timer_periodic(struct lapic *la) { uint32_t value; - value = la->lvt_timer_cache; - value &= ~APIC_LVTT_TM; - value |= APIC_LVT_M; + value = la->lvt_timer_base; + value &= ~(APIC_LVTT_TM | APIC_LVT_M); + value |= APIC_LVTT_TM_PERIODIC; + la->lvt_timer_last = value; lapic_write32(LAPIC_LVT_TIMER, value); + lapic_write32(LAPIC_ICR_TIMER, la->la_timer_period); +} + +static void +lapic_timer_deadline(struct lapic *la) +{ + uint32_t value; + + value = la->lvt_timer_base; + value &= ~(APIC_LVTT_TM | APIC_LVT_M); + value |= APIC_LVTT_TM_TSCDLT; + if (value != la->lvt_timer_last) { + la->lvt_timer_last = value; + lapic_write32_nofence(LAPIC_LVT_TIMER, value); + if (!x2apic_mode) + mfence(); + } + wrmsr(MSR_TSC_DEADLINE, la->la_timer_period + rdtsc()); +} + +static void +lapic_timer_stop(struct lapic *la) +{ + uint32_t value; + + if (la->la_timer_mode == LAT_MODE_DEADLINE) { + wrmsr(MSR_TSC_DEADLINE, 0); + mfence(); + } else { + value = la->lvt_timer_base; + value &= ~APIC_LVTT_TM; + value |= APIC_LVT_M; + la->lvt_timer_last = value; + lapic_write32(LAPIC_LVT_TIMER, value); + } } void @@ -1621,31 +1754,25 @@ SYSINIT(apic_setup_io, SI_SUB_INTR, SI_ORDER_THIRD, apic_setup_io, NULL); * private to the MD code. The public interface for the rest of the * kernel is defined in mp_machdep.c. */ + +/* + * Wait delay microseconds for IPI to be sent. If delay is -1, we + * wait forever. + */ static int native_lapic_ipi_wait(int delay) { - int x; + uint64_t rx; /* LAPIC_ICR.APIC_DELSTAT_MASK is undefined in x2APIC mode */ if (x2apic_mode) return (1); - /* - * Wait delay microseconds for IPI to be sent. If delay is - * -1, we wait forever. - */ - if (delay == -1) { - while ((lapic_read_icr_lo() & APIC_DELSTAT_MASK) != - APIC_DELSTAT_IDLE) - ia32_pause(); - return (1); - } - - for (x = 0; x < delay; x += 5) { + for (rx = 0; delay == -1 || rx < lapic_ipi_wait_mult * delay; rx++) { if ((lapic_read_icr_lo() & APIC_DELSTAT_MASK) == APIC_DELSTAT_IDLE) return (1); - DELAY(5); + ia32_pause(); } return (0); } diff --git a/sys/x86/x86/mp_x86.c b/sys/x86/x86/mp_x86.c index 9594ff3..90051c8 100644 --- a/sys/x86/x86/mp_x86.c +++ b/sys/x86/x86/mp_x86.c @@ -133,19 +133,28 @@ volatile int aps_ready = 0; * the APs. */ struct cpu_info cpu_info[MAX_APIC_ID + 1]; -int cpu_apic_ids[MAXCPU]; int apic_cpuids[MAX_APIC_ID + 1]; +int cpu_apic_ids[MAXCPU]; /* Holds pending bitmap based IPIs per CPU */ volatile u_int cpu_ipi_pending[MAXCPU]; -int cpu_logical; /* logical cpus per core */ -int cpu_cores; /* cores per package */ - static void release_aps(void *dummy); -static u_int hyperthreading_cpus; /* logical cpus sharing L1 cache */ static int hyperthreading_allowed = 1; +SYSCTL_INT(_machdep, OID_AUTO, hyperthreading_allowed, CTLFLAG_RDTUN, + &hyperthreading_allowed, 0, "Use Intel HTT logical CPUs"); + +static struct topo_node topo_root; + +static int pkg_id_shift; +static int core_id_shift; +static int disabled_cpus; + +struct cache_info { + int id_shift; + int present; +} static caches[MAX_CACHE_LEVELS]; void mem_range_AP_init(void) @@ -155,60 +164,125 @@ mem_range_AP_init(void) mem_range_softc.mr_op->initAP(&mem_range_softc); } -static void -topo_probe_amd(void) +/* + * Round up to the next power of two, if necessary, and then + * take log2. + * Returns -1 if argument is zero. + */ +static __inline int +mask_width(u_int x) { - int core_id_bits; - int id; - /* AMD processors do not support HTT. */ - cpu_logical = 1; + return (fls(x << (1 - powerof2(x))) - 1); +} - if ((amd_feature2 & AMDID2_CMP) == 0) { - cpu_cores = 1; - return; - } +static int +add_deterministic_cache(int type, int level, int share_count) +{ - core_id_bits = (cpu_procinfo2 & AMDID_COREID_SIZE) >> - AMDID_COREID_SIZE_SHIFT; - if (core_id_bits == 0) { - cpu_cores = (cpu_procinfo2 & AMDID_CMP_CORES) + 1; - return; + if (type == 0) + return (0); + if (type > 3) { + printf("unexpected cache type %d\n", type); + return (1); + } + if (type == 2) /* ignore instruction cache */ + return (1); + if (level == 0 || level > MAX_CACHE_LEVELS) { + printf("unexpected cache level %d\n", type); + return (1); } - /* Fam 10h and newer should get here. */ - for (id = 0; id <= MAX_APIC_ID; id++) { - /* Check logical CPU availability. */ - if (!cpu_info[id].cpu_present || cpu_info[id].cpu_disabled) - continue; - /* Check if logical CPU has the same package ID. */ - if ((id >> core_id_bits) != (boot_cpu_id >> core_id_bits)) - continue; - cpu_cores++; + if (caches[level - 1].present) { + printf("WARNING: multiple entries for L%u data cache\n", level); + printf("%u => %u\n", caches[level - 1].id_shift, + mask_width(share_count)); + } + caches[level - 1].id_shift = mask_width(share_count); + caches[level - 1].present = 1; + + if (caches[level - 1].id_shift > pkg_id_shift) { + printf("WARNING: L%u data cache covers more " + "APIC IDs than a package\n", level); + printf("%u > %u\n", caches[level - 1].id_shift, pkg_id_shift); + caches[level - 1].id_shift = pkg_id_shift; } + if (caches[level - 1].id_shift < core_id_shift) { + printf("WARNING: L%u data cache covers less " + "APIC IDs than a core\n", level); + printf("%u < %u\n", caches[level - 1].id_shift, core_id_shift); + caches[level - 1].id_shift = core_id_shift; + } + + return (1); } -/* - * Round up to the next power of two, if necessary, and then - * take log2. - * Returns -1 if argument is zero. - */ -static __inline int -mask_width(u_int x) +static void +topo_probe_amd(void) { + u_int p[4]; + int level; + int share_count; + int type; + int i; - return (fls(x << (1 - powerof2(x))) - 1); + /* No multi-core capability. */ + if ((amd_feature2 & AMDID2_CMP) == 0) + return; + + /* For families 10h and newer. */ + pkg_id_shift = (cpu_procinfo2 & AMDID_COREID_SIZE) >> + AMDID_COREID_SIZE_SHIFT; + + /* For 0Fh family. */ + if (pkg_id_shift == 0) + pkg_id_shift = + mask_width((cpu_procinfo2 & AMDID_CMP_CORES) + 1); + + if ((amd_feature2 & AMDID2_TOPOLOGY) != 0) { + for (i = 0; ; i++) { + cpuid_count(0x8000001d, i, p); + type = p[0] & 0x1f; + level = (p[0] >> 5) & 0x7; + share_count = 1 + ((p[0] >> 14) & 0xfff); + + if (!add_deterministic_cache(type, level, share_count)) + break; + } + } else { + if (cpu_exthigh >= 0x80000005) { + cpuid_count(0x80000005, 0, p); + if (((p[2] >> 24) & 0xff) != 0) { + caches[0].id_shift = 0; + caches[0].present = 1; + } + } + if (cpu_exthigh >= 0x80000006) { + cpuid_count(0x80000006, 0, p); + if (((p[2] >> 16) & 0xffff) != 0) { + caches[1].id_shift = 0; + caches[1].present = 1; + } + if (((p[3] >> 18) & 0x3fff) != 0) { + + /* + * TODO: Account for dual-node processors + * where each node within a package has its own + * L3 cache. + */ + caches[2].id_shift = pkg_id_shift; + caches[2].present = 1; + } + } + } } static void -topo_probe_0x4(void) +topo_probe_intel_0x4(void) { u_int p[4]; - int pkg_id_bits; - int core_id_bits; int max_cores; int max_logical; - int id; /* Both zero and one here mean one logical processor per package. */ max_logical = (cpu_feature & CPUID_HTT) != 0 ? @@ -216,180 +290,432 @@ topo_probe_0x4(void) if (max_logical <= 1) return; - /* - * Because of uniformity assumption we examine only - * those logical processors that belong to the same - * package as BSP. Further, we count number of - * logical processors that belong to the same core - * as BSP thus deducing number of threads per core. - */ if (cpu_high >= 0x4) { cpuid_count(0x04, 0, p); max_cores = ((p[0] >> 26) & 0x3f) + 1; } else max_cores = 1; - core_id_bits = mask_width(max_logical/max_cores); - if (core_id_bits < 0) - return; - pkg_id_bits = core_id_bits + mask_width(max_cores); - - for (id = 0; id <= MAX_APIC_ID; id++) { - /* Check logical CPU availability. */ - if (!cpu_info[id].cpu_present || cpu_info[id].cpu_disabled) - continue; - /* Check if logical CPU has the same package ID. */ - if ((id >> pkg_id_bits) != (boot_cpu_id >> pkg_id_bits)) - continue; - cpu_cores++; - /* Check if logical CPU has the same package and core IDs. */ - if ((id >> core_id_bits) == (boot_cpu_id >> core_id_bits)) - cpu_logical++; - } - - KASSERT(cpu_cores >= 1 && cpu_logical >= 1, - ("topo_probe_0x4 couldn't find BSP")); - cpu_cores /= cpu_logical; - hyperthreading_cpus = cpu_logical; + core_id_shift = mask_width(max_logical/max_cores); + KASSERT(core_id_shift >= 0, + ("intel topo: max_cores > max_logical\n")); + pkg_id_shift = core_id_shift + mask_width(max_cores); } static void -topo_probe_0xb(void) +topo_probe_intel_0xb(void) { u_int p[4]; int bits; - int cnt; - int i; - int logical; int type; - int x; + int i; + + /* Fall back if CPU leaf 11 doesn't really exist. */ + cpuid_count(0x0b, 0, p); + if (p[1] == 0) { + topo_probe_intel_0x4(); + return; + } /* We only support three levels for now. */ - for (i = 0; i < 3; i++) { + for (i = 0; ; i++) { cpuid_count(0x0b, i, p); - /* Fall back if CPU leaf 11 doesn't really exist. */ - if (i == 0 && p[1] == 0) { - topo_probe_0x4(); - return; - } - bits = p[0] & 0x1f; - logical = p[1] &= 0xffff; type = (p[2] >> 8) & 0xff; - if (type == 0 || logical == 0) + + if (type == 0) break; - /* - * Because of uniformity assumption we examine only - * those logical processors that belong to the same - * package as BSP. - */ - for (cnt = 0, x = 0; x <= MAX_APIC_ID; x++) { - if (!cpu_info[x].cpu_present || - cpu_info[x].cpu_disabled) - continue; - if (x >> bits == boot_cpu_id >> bits) - cnt++; - } + + /* TODO: check for duplicate (re-)assignment */ if (type == CPUID_TYPE_SMT) - cpu_logical = cnt; + core_id_shift = bits; else if (type == CPUID_TYPE_CORE) - cpu_cores = cnt; + pkg_id_shift = bits; + else + printf("unknown CPU level type %d\n", type); + } + + if (pkg_id_shift < core_id_shift) { + printf("WARNING: core covers more APIC IDs than a package\n"); + core_id_shift = pkg_id_shift; } - if (cpu_logical == 0) - cpu_logical = 1; - cpu_cores /= cpu_logical; +} + +static void +topo_probe_intel_caches(void) +{ + u_int p[4]; + int level; + int share_count; + int type; + int i; + + if (cpu_high < 0x4) { + /* + * Available cache level and sizes can be determined + * via CPUID leaf 2, but that requires a huge table of hardcoded + * values, so for now just assume L1 and L2 caches potentially + * shared only by HTT processing units, if HTT is present. + */ + caches[0].id_shift = pkg_id_shift; + caches[0].present = 1; + caches[1].id_shift = pkg_id_shift; + caches[1].present = 1; + return; + } + + for (i = 0; ; i++) { + cpuid_count(0x4, i, p); + type = p[0] & 0x1f; + level = (p[0] >> 5) & 0x7; + share_count = 1 + ((p[0] >> 14) & 0xfff); + + if (!add_deterministic_cache(type, level, share_count)) + break; + } +} + +static void +topo_probe_intel(void) +{ + + /* + * See Intel(R) 64 Architecture Processor + * Topology Enumeration article for details. + * + * Note that 0x1 <= cpu_high < 4 case should be + * compatible with topo_probe_intel_0x4() logic when + * CPUID.1:EBX[23:16] > 0 (cpu_cores will be 1) + * or it should trigger the fallback otherwise. + */ + if (cpu_high >= 0xb) + topo_probe_intel_0xb(); + else if (cpu_high >= 0x1) + topo_probe_intel_0x4(); + + topo_probe_intel_caches(); } /* - * Both topology discovery code and code that consumes topology - * information assume top-down uniformity of the topology. - * That is, all physical packages must be identical and each - * core in a package must have the same number of threads. * Topology information is queried only on BSP, on which this * code runs and for which it can query CPUID information. - * Then topology is extrapolated on all packages using the - * uniformity assumption. + * Then topology is extrapolated on all packages using an + * assumption that APIC ID to hardware component ID mapping is + * homogenious. + * That doesn't necesserily imply that the topology is uniform. */ void topo_probe(void) { static int cpu_topo_probed = 0; + struct x86_topo_layer { + int type; + int subtype; + int id_shift; + } topo_layers[MAX_CACHE_LEVELS + 3]; + struct topo_node *parent; + struct topo_node *node; + int layer; + int nlayers; + int node_id; + int i; if (cpu_topo_probed) return; CPU_ZERO(&logical_cpus_mask); + if (mp_ncpus <= 1) - cpu_cores = cpu_logical = 1; + ; /* nothing */ else if (cpu_vendor_id == CPU_VENDOR_AMD) topo_probe_amd(); - else if (cpu_vendor_id == CPU_VENDOR_INTEL) { - /* - * See Intel(R) 64 Architecture Processor - * Topology Enumeration article for details. - * - * Note that 0x1 <= cpu_high < 4 case should be - * compatible with topo_probe_0x4() logic when - * CPUID.1:EBX[23:16] > 0 (cpu_cores will be 1) - * or it should trigger the fallback otherwise. - */ - if (cpu_high >= 0xb) - topo_probe_0xb(); - else if (cpu_high >= 0x1) - topo_probe_0x4(); - } + else if (cpu_vendor_id == CPU_VENDOR_INTEL) + topo_probe_intel(); + + KASSERT(pkg_id_shift >= core_id_shift, + ("bug in APIC topology discovery")); + + nlayers = 0; + bzero(topo_layers, sizeof(topo_layers)); + + topo_layers[nlayers].type = TOPO_TYPE_PKG; + topo_layers[nlayers].id_shift = pkg_id_shift; + if (bootverbose) + printf("Package ID shift: %u\n", topo_layers[nlayers].id_shift); + nlayers++; /* - * Fallback: assume each logical CPU is in separate - * physical package. That is, no multi-core, no SMT. + * Consider all caches to be within a package/chip + * and "in front" of all sub-components like + * cores and hardware threads. */ - if (cpu_cores == 0 || cpu_logical == 0) - cpu_cores = cpu_logical = 1; + for (i = MAX_CACHE_LEVELS - 1; i >= 0; --i) { + if (caches[i].present) { + KASSERT(caches[i].id_shift <= pkg_id_shift, + ("bug in APIC topology discovery")); + KASSERT(caches[i].id_shift >= core_id_shift, + ("bug in APIC topology discovery")); + + topo_layers[nlayers].type = TOPO_TYPE_CACHE; + topo_layers[nlayers].subtype = i + 1; + topo_layers[nlayers].id_shift = caches[i].id_shift; + if (bootverbose) + printf("L%u cache ID shift: %u\n", + topo_layers[nlayers].subtype, + topo_layers[nlayers].id_shift); + nlayers++; + } + } + + if (pkg_id_shift > core_id_shift) { + topo_layers[nlayers].type = TOPO_TYPE_CORE; + topo_layers[nlayers].id_shift = core_id_shift; + if (bootverbose) + printf("Core ID shift: %u\n", + topo_layers[nlayers].id_shift); + nlayers++; + } + + topo_layers[nlayers].type = TOPO_TYPE_PU; + topo_layers[nlayers].id_shift = 0; + nlayers++; + + topo_init_root(&topo_root); + for (i = 0; i <= MAX_APIC_ID; ++i) { + if (!cpu_info[i].cpu_present) + continue; + + parent = &topo_root; + for (layer = 0; layer < nlayers; ++layer) { + node_id = i >> topo_layers[layer].id_shift; + parent = topo_add_node_by_hwid(parent, node_id, + topo_layers[layer].type, + topo_layers[layer].subtype); + } + } + + parent = &topo_root; + for (layer = 0; layer < nlayers; ++layer) { + node_id = boot_cpu_id >> topo_layers[layer].id_shift; + node = topo_find_node_by_hwid(parent, node_id, + topo_layers[layer].type, + topo_layers[layer].subtype); + topo_promote_child(node); + parent = node; + } + cpu_topo_probed = 1; } -struct cpu_group * -cpu_topo(void) +/* + * Assign logical CPU IDs to local APICs. + */ +void +assign_cpu_ids(void) { - int cg_flags; + struct topo_node *node; + u_int smt_mask; + + smt_mask = (1u << core_id_shift) - 1; /* - * Determine whether any threading flags are - * necessry. + * Assign CPU IDs to local APIC IDs and disable any CPUs + * beyond MAXCPU. CPU 0 is always assigned to the BSP. */ - topo_probe(); - if (cpu_logical > 1 && hyperthreading_cpus) - cg_flags = CG_FLAG_HTT; - else if (cpu_logical > 1) - cg_flags = CG_FLAG_SMT; + mp_ncpus = 0; + TOPO_FOREACH(node, &topo_root) { + if (node->type != TOPO_TYPE_PU) + continue; + + if ((node->hwid & smt_mask) != (boot_cpu_id & smt_mask)) + cpu_info[node->hwid].cpu_hyperthread = 1; + + if (resource_disabled("lapic", node->hwid)) { + if (node->hwid != boot_cpu_id) + cpu_info[node->hwid].cpu_disabled = 1; + else + printf("Cannot disable BSP, APIC ID = %d\n", + node->hwid); + } + + if (!hyperthreading_allowed && + cpu_info[node->hwid].cpu_hyperthread) + cpu_info[node->hwid].cpu_disabled = 1; + + if (mp_ncpus >= MAXCPU) + cpu_info[node->hwid].cpu_disabled = 1; + + if (cpu_info[node->hwid].cpu_disabled) { + disabled_cpus++; + continue; + } + + cpu_apic_ids[mp_ncpus] = node->hwid; + apic_cpuids[node->hwid] = mp_ncpus; + topo_set_pu_id(node, mp_ncpus); + mp_ncpus++; + } + + KASSERT(mp_maxid >= mp_ncpus - 1, + ("%s: counters out of sync: max %d, count %d", __func__, mp_maxid, + mp_ncpus)); +} + +/* + * Print various information about the SMP system hardware and setup. + */ +void +cpu_mp_announce(void) +{ + struct topo_node *node; + const char *hyperthread; + int pkg_count; + int cores_per_pkg; + int thrs_per_core; + + printf("FreeBSD/SMP: "); + if (topo_analyze(&topo_root, 1, &pkg_count, + &cores_per_pkg, &thrs_per_core)) { + printf("%d package(s)", pkg_count); + if (cores_per_pkg > 0) + printf(" x %d core(s)", cores_per_pkg); + if (thrs_per_core > 1) + printf(" x %d hardware threads", thrs_per_core); + } else { + printf("Non-uniform topology"); + } + printf("\n"); + + if (disabled_cpus) { + printf("FreeBSD/SMP Online: "); + if (topo_analyze(&topo_root, 0, &pkg_count, + &cores_per_pkg, &thrs_per_core)) { + printf("%d package(s)", pkg_count); + if (cores_per_pkg > 0) + printf(" x %d core(s)", cores_per_pkg); + if (thrs_per_core > 1) + printf(" x %d hardware threads", thrs_per_core); + } else { + printf("Non-uniform topology"); + } + printf("\n"); + } + + if (!bootverbose) + return; + + TOPO_FOREACH(node, &topo_root) { + switch (node->type) { + case TOPO_TYPE_PKG: + printf("Package HW ID = %u (%#x)\n", + node->hwid, node->hwid); + break; + case TOPO_TYPE_CORE: + printf("\tCore HW ID = %u (%#x)\n", + node->hwid, node->hwid); + break; + case TOPO_TYPE_PU: + if (cpu_info[node->hwid].cpu_hyperthread) + hyperthread = "/HT"; + else + hyperthread = ""; + + if (node->subtype == 0) + printf("\t\tCPU (AP%s): APIC ID: %u (%#x)" + "(disabled)\n", hyperthread, node->hwid, + node->hwid); + else if (node->id == 0) + printf("\t\tCPU0 (BSP): APIC ID: %u (%#x)\n", + node->hwid, node->hwid); + else + printf("\t\tCPU%u (AP%s): APIC ID: %u (%#x)\n", + node->id, hyperthread, node->hwid, + node->hwid); + break; + default: + /* ignored */ + break; + } + } +} + +static void +x86topo_add_sched_group(struct topo_node *root, struct cpu_group *cg_root) +{ + struct topo_node *node; + int nchildren; + int ncores; + int i; + + KASSERT(root->type == TOPO_TYPE_SYSTEM || root->type == TOPO_TYPE_CACHE, + ("x86topo_add_sched_group: bad type: %u", root->type)); + CPU_COPY(&root->cpuset, &cg_root->cg_mask); + cg_root->cg_count = root->cpu_count; + if (root->type == TOPO_TYPE_SYSTEM) + cg_root->cg_level = CG_SHARE_NONE; else - cg_flags = 0; - if (mp_ncpus % (cpu_cores * cpu_logical) != 0) { - printf("WARNING: Non-uniform processors.\n"); - printf("WARNING: Using suboptimal topology.\n"); - return (smp_topo_none()); + cg_root->cg_level = root->subtype; + + ncores = 0; + node = root; + while (node != NULL) { + if (node->type != TOPO_TYPE_CORE) { + node = topo_next_node(root, node); + continue; + } + + ncores++; + node = topo_next_nonchild_node(root, node); } - /* - * No multi-core or hyper-threaded. - */ - if (cpu_logical * cpu_cores == 1) + + if (cg_root->cg_level != CG_SHARE_NONE && + root->cpu_count > 1 && ncores < 2) + cg_root->cg_flags = CG_FLAG_SMT; + + nchildren = 0; + node = root; + while (node != NULL) { + if (node->type != TOPO_TYPE_CACHE || + (root->type != TOPO_TYPE_SYSTEM && + CPU_CMP(&node->cpuset, &root->cpuset) == 0)) { + node = topo_next_node(root, node); + continue; + } + nchildren++; + node = topo_next_nonchild_node(root, node); + } + + cg_root->cg_child = smp_topo_alloc(nchildren); + cg_root->cg_children = nchildren; + + node = root; + i = 0; + while (node != NULL) { + if (node->type != TOPO_TYPE_CACHE || + (root->type != TOPO_TYPE_SYSTEM && + CPU_CMP(&node->cpuset, &root->cpuset) == 0)) { + node = topo_next_node(root, node); + continue; + } + cg_root->cg_child[i].cg_parent = cg_root; + x86topo_add_sched_group(node, &cg_root->cg_child[i]); + i++; + node = topo_next_nonchild_node(root, node); + } +} + +struct cpu_group * +cpu_topo(void) +{ + struct cpu_group *cg_root; + + if (mp_ncpus <= 1) return (smp_topo_none()); - /* - * Only HTT no multi-core. - */ - if (cpu_logical > 1 && cpu_cores == 1) - return (smp_topo_1level(CG_SHARE_L1, cpu_logical, cg_flags)); - /* - * Only multi-core no HTT. - */ - if (cpu_cores > 1 && cpu_logical == 1) - return (smp_topo_1level(CG_SHARE_L2, cpu_cores, cg_flags)); - /* - * Both HTT and multi-core. - */ - return (smp_topo_2level(CG_SHARE_L2, cpu_cores, - CG_SHARE_L1, cpu_logical, cg_flags)); + + cg_root = smp_topo_alloc(1); + x86topo_add_sched_group(&topo_root, cg_root); + return (cg_root); } @@ -445,47 +771,9 @@ cpu_mp_probe(void) } /* - * Print various information about the SMP system hardware and setup. + * AP CPU's call this to initialize themselves. */ void -cpu_mp_announce(void) -{ - const char *hyperthread; - int i; - - printf("FreeBSD/SMP: %d package(s) x %d core(s)", - mp_ncpus / (cpu_cores * cpu_logical), cpu_cores); - if (hyperthreading_cpus > 1) - printf(" x %d HTT threads", cpu_logical); - else if (cpu_logical > 1) - printf(" x %d SMT threads", cpu_logical); - printf("\n"); - - /* List active CPUs first. */ - printf(" cpu0 (BSP): APIC ID: %2d\n", boot_cpu_id); - for (i = 1; i < mp_ncpus; i++) { - if (cpu_info[cpu_apic_ids[i]].cpu_hyperthread) - hyperthread = "/HT"; - else - hyperthread = ""; - printf(" cpu%d (AP%s): APIC ID: %2d\n", i, hyperthread, - cpu_apic_ids[i]); - } - - /* List disabled CPUs last. */ - for (i = 0; i <= MAX_APIC_ID; i++) { - if (!cpu_info[i].cpu_present || !cpu_info[i].cpu_disabled) - continue; - if (cpu_info[i].cpu_hyperthread) - hyperthread = "/HT"; - else - hyperthread = ""; - printf(" cpu (AP%s): APIC ID: %2d (disabled)\n", hyperthread, - i); - } -} - -void init_secondary_tail(void) { u_int cpuid; @@ -546,8 +834,7 @@ init_secondary_tail(void) printf("SMP: AP CPU #%d Launched!\n", cpuid); /* Determine if we are a logical CPU. */ - /* XXX Calculation depends on cpu_logical being a power of 2, e.g. 2 */ - if (cpu_logical > 1 && PCPU_GET(apic_id) % cpu_logical != 0) + if (cpu_info[PCPU_GET(apic_id)].cpu_hyperthread) CPU_SET(cpuid, &logical_cpus_mask); if (bootverbose) @@ -612,85 +899,13 @@ set_interrupt_apic_ids(void) continue; /* Don't let hyperthreads service interrupts. */ - if (cpu_logical > 1 && - apic_id % cpu_logical != 0) + if (cpu_info[apic_id].cpu_hyperthread) continue; intr_add_cpu(i); } } -/* - * Assign logical CPU IDs to local APICs. - */ -void -assign_cpu_ids(void) -{ - u_int i; - - TUNABLE_INT_FETCH("machdep.hyperthreading_allowed", - &hyperthreading_allowed); - - /* Check for explicitly disabled CPUs. */ - for (i = 0; i <= MAX_APIC_ID; i++) { - if (!cpu_info[i].cpu_present || cpu_info[i].cpu_bsp) - continue; - - if (hyperthreading_cpus > 1 && i % hyperthreading_cpus != 0) { - cpu_info[i].cpu_hyperthread = 1; - - /* - * Don't use HT CPU if it has been disabled by a - * tunable. - */ - if (hyperthreading_allowed == 0) { - cpu_info[i].cpu_disabled = 1; - continue; - } - } - - /* Don't use this CPU if it has been disabled by a tunable. */ - if (resource_disabled("lapic", i)) { - cpu_info[i].cpu_disabled = 1; - continue; - } - } - - if (hyperthreading_allowed == 0 && hyperthreading_cpus > 1) { - hyperthreading_cpus = 0; - cpu_logical = 1; - } - - /* - * Assign CPU IDs to local APIC IDs and disable any CPUs - * beyond MAXCPU. CPU 0 is always assigned to the BSP. - * - * To minimize confusion for userland, we attempt to number - * CPUs such that all threads and cores in a package are - * grouped together. For now we assume that the BSP is always - * the first thread in a package and just start adding APs - * starting with the BSP's APIC ID. - */ - mp_ncpus = 1; - cpu_apic_ids[0] = boot_cpu_id; - apic_cpuids[boot_cpu_id] = 0; - for (i = boot_cpu_id + 1; i != boot_cpu_id; - i == MAX_APIC_ID ? i = 0 : i++) { - if (!cpu_info[i].cpu_present || cpu_info[i].cpu_bsp || - cpu_info[i].cpu_disabled) - continue; - - if (mp_ncpus < MAXCPU) { - cpu_apic_ids[mp_ncpus] = i; - apic_cpuids[i] = mp_ncpus; - mp_ncpus++; - } else - cpu_info[i].cpu_disabled = 1; - } - KASSERT(mp_maxid >= mp_ncpus - 1, - ("%s: counters out of sync: max %d, count %d", __func__, mp_maxid, - mp_ncpus)); -} #ifdef COUNT_XINVLTLB_HITS u_int xhits_gbl[MAXCPU]; @@ -910,7 +1125,7 @@ ipi_all_but_self(u_int ipi) } int -ipi_nmi_handler() +ipi_nmi_handler(void) { u_int cpuid; diff --git a/sys/x86/x86/nexus.c b/sys/x86/x86/nexus.c index 39eeb82..8b49d41 100644 --- a/sys/x86/x86/nexus.c +++ b/sys/x86/x86/nexus.c @@ -301,9 +301,9 @@ nexus_print_all_resources(device_t dev) if (STAILQ_FIRST(rl)) retval += printf(" at"); - retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx"); - retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#lx"); - retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#jx"); + retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#jx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); return retval; } |