From laffer1 at midnightbsd.org Fri Mar 6 11:44:57 2020 From: laffer1 at midnightbsd.org (laffer1 at midnightbsd.org) Date: Fri, 6 Mar 2020 11:44:57 -0500 (EST) Subject: [Midnightbsd-cvs] src [12394] trunk/sys/arm/xscale: sync with freebsd 11 Message-ID: <202003061644.026Givch009520@stargazer.midnightbsd.org> Revision: 12394 http://svnweb.midnightbsd.org/src/?rev=12394 Author: laffer1 Date: 2020-03-06 11:44:57 -0500 (Fri, 06 Mar 2020) Log Message: ----------- sync with freebsd 11 Modified Paths: -------------- trunk/sys/arm/xscale/i8134x/crb_machdep.c trunk/sys/arm/xscale/i8134x/files.crb trunk/sys/arm/xscale/i8134x/files.i81342 trunk/sys/arm/xscale/i8134x/i81342.c trunk/sys/arm/xscale/i8134x/i81342_mcu.c trunk/sys/arm/xscale/i8134x/i81342_pci.c trunk/sys/arm/xscale/i8134x/i81342_space.c trunk/sys/arm/xscale/i8134x/i81342reg.h trunk/sys/arm/xscale/i8134x/i81342var.h trunk/sys/arm/xscale/i8134x/iq81342_7seg.c trunk/sys/arm/xscale/i8134x/iq81342reg.h trunk/sys/arm/xscale/i8134x/iq81342var.h trunk/sys/arm/xscale/i8134x/obio.c trunk/sys/arm/xscale/i8134x/obiovar.h trunk/sys/arm/xscale/i8134x/std.crb trunk/sys/arm/xscale/i8134x/std.i81342 trunk/sys/arm/xscale/i8134x/uart_bus_i81342.c trunk/sys/arm/xscale/i8134x/uart_cpu_i81342.c trunk/sys/arm/xscale/ixp425/avila_ata.c trunk/sys/arm/xscale/ixp425/avila_gpio.c trunk/sys/arm/xscale/ixp425/avila_led.c trunk/sys/arm/xscale/ixp425/avila_machdep.c trunk/sys/arm/xscale/ixp425/cambria_exp_space.c trunk/sys/arm/xscale/ixp425/cambria_fled.c trunk/sys/arm/xscale/ixp425/cambria_gpio.c trunk/sys/arm/xscale/ixp425/cambria_led.c trunk/sys/arm/xscale/ixp425/files.avila trunk/sys/arm/xscale/ixp425/files.ixp425 trunk/sys/arm/xscale/ixp425/if_npe.c trunk/sys/arm/xscale/ixp425/if_npereg.h trunk/sys/arm/xscale/ixp425/ixdp425_pci.c trunk/sys/arm/xscale/ixp425/ixdp425reg.h trunk/sys/arm/xscale/ixp425/ixp425.c trunk/sys/arm/xscale/ixp425/ixp425_a4x_io.S trunk/sys/arm/xscale/ixp425/ixp425_a4x_space.c trunk/sys/arm/xscale/ixp425/ixp425_iic.c trunk/sys/arm/xscale/ixp425/ixp425_intr.h trunk/sys/arm/xscale/ixp425/ixp425_mem.c trunk/sys/arm/xscale/ixp425/ixp425_npe.c trunk/sys/arm/xscale/ixp425/ixp425_npereg.h trunk/sys/arm/xscale/ixp425/ixp425_npevar.h trunk/sys/arm/xscale/ixp425/ixp425_pci.c trunk/sys/arm/xscale/ixp425/ixp425_pci_asm.S trunk/sys/arm/xscale/ixp425/ixp425_pci_space.c trunk/sys/arm/xscale/ixp425/ixp425_qmgr.c trunk/sys/arm/xscale/ixp425/ixp425_qmgr.h trunk/sys/arm/xscale/ixp425/ixp425_space.c trunk/sys/arm/xscale/ixp425/ixp425_timer.c trunk/sys/arm/xscale/ixp425/ixp425_wdog.c trunk/sys/arm/xscale/ixp425/ixp425reg.h trunk/sys/arm/xscale/ixp425/ixp425var.h trunk/sys/arm/xscale/ixp425/std.avila trunk/sys/arm/xscale/ixp425/std.ixp425 trunk/sys/arm/xscale/ixp425/std.ixp435 trunk/sys/arm/xscale/ixp425/uart_bus_ixp425.c trunk/sys/arm/xscale/ixp425/uart_cpu_ixp425.c trunk/sys/arm/xscale/pxa/files.pxa trunk/sys/arm/xscale/pxa/if_smc_smi.c trunk/sys/arm/xscale/pxa/pxa_gpio.c trunk/sys/arm/xscale/pxa/pxa_icu.c trunk/sys/arm/xscale/pxa/pxa_machdep.c trunk/sys/arm/xscale/pxa/pxa_obio.c trunk/sys/arm/xscale/pxa/pxa_smi.c trunk/sys/arm/xscale/pxa/pxa_space.c trunk/sys/arm/xscale/pxa/pxa_timer.c trunk/sys/arm/xscale/pxa/pxareg.h trunk/sys/arm/xscale/pxa/pxavar.h trunk/sys/arm/xscale/pxa/std.pxa trunk/sys/arm/xscale/pxa/uart_bus_pxa.c trunk/sys/arm/xscale/pxa/uart_cpu_pxa.c trunk/sys/arm/xscale/std.xscale trunk/sys/arm/xscale/std.xscale-be Added Paths: ----------- trunk/sys/arm/xscale/i8134x/i80321_timer.c trunk/sys/arm/xscale/i8134x/i80321_wdog.c trunk/sys/arm/xscale/i8134x/i80321reg.h trunk/sys/arm/xscale/i8134x/i80321var.h Modified: trunk/sys/arm/xscale/i8134x/crb_machdep.c =================================================================== --- trunk/sys/arm/xscale/i8134x/crb_machdep.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/crb_machdep.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -39,7 +39,7 @@ * * machdep.c * - * Machine dependant functions for kernel setup + * Machine dependent functions for kernel setup * * This file needs a lot of work. * @@ -47,8 +47,10 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/i8134x/crb_machdep.c 278727 2015-02-13 22:32:02Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/i8134x/crb_machdep.c 331890 2018-04-02 22:02:49Z gonzo $"); +#include "opt_kstack_pages.h" + #define _ARM32_BUS_DMA_PRIVATE #include #include @@ -71,6 +73,7 @@ #include #include #include +#include #include #include @@ -79,7 +82,6 @@ #include #include #include -#include #include #include #include @@ -91,7 +93,7 @@ #include -#include /* For i80321_calibrate_delay() */ +#include /* For i80321_calibrate_delay() */ #include #include @@ -119,13 +121,11 @@ struct pv_addr kernelstack; /* Static device mappings. */ -static const struct arm_devmap_entry iq81342_devmap[] = { +static const struct devmap_entry iq81342_devmap[] = { { IOP34X_VADDR, IOP34X_HWADDR, IOP34X_SIZE, - VM_PROT_READ|VM_PROT_WRITE, - PTE_DEVICE, }, { /* @@ -132,25 +132,19 @@ * Cheat and map a whole section, this will bring * both PCI-X and PCI-E outbound I/O */ - IOP34X_PCIX_OIOBAR_VADDR &~ (0x100000 - 1), - IOP34X_PCIX_OIOBAR &~ (0x100000 - 1), + rounddown2(IOP34X_PCIX_OIOBAR_VADDR, 0x100000), + rounddown2(IOP34X_PCIX_OIOBAR, 0x100000), 0x100000, - VM_PROT_READ|VM_PROT_WRITE, - PTE_DEVICE, }, { IOP34X_PCE1_VADDR, IOP34X_PCE1, IOP34X_PCE1_SIZE, - VM_PROT_READ|VM_PROT_WRITE, - PTE_DEVICE, }, { 0, 0, 0, - 0, - 0, } }; @@ -224,7 +218,7 @@ valloc_pages(irqstack, IRQ_STACK_SIZE); valloc_pages(abtstack, ABT_STACK_SIZE); valloc_pages(undstack, UND_STACK_SIZE); - valloc_pages(kernelstack, KSTACK_PAGES); + valloc_pages(kernelstack, kstack_pages); valloc_pages(msgbufpv, round_page(msgbufsize) / PAGE_SIZE); /* * Now we start construction of the L1 page table @@ -234,8 +228,8 @@ l1pagetable = kernel_l1pt.pv_va; /* Map the L2 pages tables in the L1 page table */ - pmap_link_l2pt(l1pagetable, ARM_VECTORS_HIGH & ~(0x00100000 - 1), - &kernel_pt_table[KERNEL_PT_SYS]); + pmap_link_l2pt(l1pagetable, rounddown2(ARM_VECTORS_HIGH, 0x00100000), + &kernel_pt_table[KERNEL_PT_SYS]); pmap_map_chunk(l1pagetable, KERNBASE, SDRAM_START, 0x100000, VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); @@ -243,11 +237,10 @@ 0x100000, VM_PROT_READ|VM_PROT_WRITE, PTE_PAGETABLE); pmap_map_chunk(l1pagetable, KERNBASE + 0x200000, SDRAM_START + 0x200000, - (((uint32_t)(lastaddr) - KERNBASE - 0x200000) + L1_S_SIZE) & ~(L1_S_SIZE - 1), - VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); - freemem_after = ((int)lastaddr + PAGE_SIZE) & ~(PAGE_SIZE - 1); - afterkern = round_page(((vm_offset_t)lastaddr + L1_S_SIZE) & ~(L1_S_SIZE - - 1)); + rounddown2(((uint32_t)(lastaddr) - KERNBASE - 0x200000) + L1_S_SIZE, L1_S_SIZE), + VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + freemem_after = rounddown2((int)lastaddr + PAGE_SIZE, PAGE_SIZE); + afterkern = round_page(rounddown2((vm_offset_t)lastaddr + L1_S_SIZE, L1_S_SIZE)); for (i = 0; i < KERNEL_PT_AFKERNEL_NUM; i++) { pmap_link_l2pt(l1pagetable, afterkern + i * 0x00100000, &kernel_pt_table[KERNEL_PT_AFKERNEL + i]); @@ -257,7 +250,7 @@ /* Map the vector page. */ pmap_map_entry(l1pagetable, ARM_VECTORS_HIGH, systempage.pv_pa, VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); - arm_devmap_bootstrap(l1pagetable, iq81342_devmap); + devmap_bootstrap(l1pagetable, iq81342_devmap); /* * Give the XScale global cache clean code an appropriately * sized chunk of unmapped VA space starting at 0xff000000 @@ -266,7 +259,7 @@ xscale_cache_clean_addr = 0xff000000U; cpu_domains((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT); - setttb(kernel_l1pt.pv_pa); + cpu_setttb(kernel_l1pt.pv_pa); cpu_tlb_flushID(); cpu_domains(DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)); /* @@ -283,7 +276,7 @@ /* * We must now clean the cache again.... * Cleaning may be done by reading new data to displace any - * dirty data in the cache. This will have happened in setttb() + * dirty data in the cache. This will have happened in cpu_setttb() * but since we are boot strapping the addresses used for the read * may have just been remapped and thus the cache could be out * of sync. A re-clean after the switch will cure this. @@ -291,10 +284,10 @@ * this problem will not occur after initarm(). */ cpu_idcache_wbinv_all(); - cpu_setup(""); + cpu_setup(); i80321_calibrate_delay(); - i81342_sdram_bounds(obio_bs_tag, IOP34X_VADDR, &memstart, &memsize); + i81342_sdram_bounds(arm_base_bs_tag, IOP34X_VADDR, &memstart, &memsize); physmem = memsize / PAGE_SIZE; cninit(); /* Set stack for exception handlers */ @@ -307,7 +300,7 @@ pmap_curmaxkvaddr = afterkern + PAGE_SIZE; - vm_max_kernel_address = 0xd0000000; + vm_max_kernel_address = 0xe0000000; pmap_bootstrap(pmap_curmaxkvaddr, &kernel_l1pt); msgbufp = (void*)msgbufpv.pv_va; msgbufinit(msgbufp, msgbufsize); @@ -324,6 +317,10 @@ * Prepare the list of physical memory available to the vm subsystem. */ arm_physmem_hardware_region(SDRAM_START, memsize); + arm_physmem_exclude_region(freemem_pt, abp->abp_physaddr - + freemem_pt, EXFLAG_NOALLOC); + arm_physmem_exclude_region(freemempos, abp->abp_physaddr - 0x100000 - + freemempos, EXFLAG_NOALLOC); arm_physmem_exclude_region(abp->abp_physaddr, virtual_avail - KERNVIRTADDR, EXFLAG_NOALLOC); arm_physmem_init_kernel_globals(); Modified: trunk/sys/arm/xscale/i8134x/files.crb =================================================================== --- trunk/sys/arm/xscale/i8134x/files.crb 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/files.crb 2020-03-06 16:44:57 UTC (rev 12394) @@ -1,3 +1,3 @@ -# $FreeBSD: stable/10/sys/arm/xscale/i8134x/files.crb 172297 2007-09-22 16:25:43Z cognet $ +# $FreeBSD: stable/11/sys/arm/xscale/i8134x/files.crb 172297 2007-09-22 16:25:43Z cognet $ arm/xscale/i8134x/crb_machdep.c standard arm/xscale/i8134x/iq81342_7seg.c optional 7seg Modified: trunk/sys/arm/xscale/i8134x/files.i81342 =================================================================== --- trunk/sys/arm/xscale/i8134x/files.i81342 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/files.i81342 2020-03-06 16:44:57 UTC (rev 12394) @@ -1,10 +1,7 @@ -# $FreeBSD: stable/10/sys/arm/xscale/i8134x/files.i81342 278727 2015-02-13 22:32:02Z ian $ +# $FreeBSD: stable/11/sys/arm/xscale/i8134x/files.i81342 295199 2016-02-03 08:59:12Z mmel $ arm/arm/bus_space_base.c standard -arm/arm/bus_space_generic.c standard -arm/arm/cpufunc_asm_xscale.S standard -arm/arm/cpufunc_asm_xscale_c3.S standard -arm/xscale/i80321/i80321_timer.c standard -arm/xscale/i80321/i80321_wdog.c optional iopwdog +arm/xscale/i8134x/i80321_timer.c standard +arm/xscale/i8134x/i80321_wdog.c optional iopwdog arm/xscale/i8134x/i81342.c standard arm/xscale/i8134x/i81342_mcu.c standard arm/xscale/i8134x/i81342_pci.c optional pci Added: trunk/sys/arm/xscale/i8134x/i80321_timer.c =================================================================== --- trunk/sys/arm/xscale/i8134x/i80321_timer.c (rev 0) +++ trunk/sys/arm/xscale/i8134x/i80321_timer.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -0,0 +1,485 @@ +/* $MidnightBSD$ */ +/* $NetBSD: i80321_timer.c,v 1.7 2003/07/27 04:52:28 thorpej Exp $ */ + +/*- + * Copyright (c) 2001, 2002 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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. + */ + +/* + * Timer/clock support for the Intel i80321 I/O processor. + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/i8134x/i80321_timer.c 308325 2016-11-05 04:30:44Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CPU_XSCALE_81342 +#define ICU_INT_TIMER0 (8) /* XXX: Can't include i81342reg.h because + definitions overrides the ones from i80321reg.h + */ +#endif +#include "opt_timer.h" + +void (*i80321_hardclock_hook)(void) = NULL; +struct i80321_timer_softc { + device_t dev; +} timer_softc; + + +static unsigned i80321_timer_get_timecount(struct timecounter *tc); + + +static uint32_t counts_per_hz; + +#if defined(XSCALE_DISABLE_CCNT) || defined(CPU_XSCALE_81342) +static uint32_t offset; +static uint32_t last = -1; +#endif + +static int ticked = 0; + +#ifndef COUNTS_PER_SEC +#define COUNTS_PER_SEC 200000000 /* 200MHz */ +#endif + +#define COUNTS_PER_USEC (COUNTS_PER_SEC / 1000000) + +static struct timecounter i80321_timer_timecounter = { + i80321_timer_get_timecount, /* get_timecount */ + NULL, /* no poll_pps */ + ~0u, /* counter_mask */ +#if defined(XSCALE_DISABLE_CCNT) || defined(CPU_XSCALE_81342) + COUNTS_PER_SEC, +#else + COUNTS_PER_SEC * 3, /* frequency */ +#endif + "i80321 timer", /* name */ + 1000 /* quality */ +}; + +static int +i80321_timer_probe(device_t dev) +{ + + device_set_desc(dev, "i80321 timer"); + return (0); +} + +static int +i80321_timer_attach(device_t dev) +{ + timer_softc.dev = dev; + + return (0); +} + +static device_method_t i80321_timer_methods[] = { + DEVMETHOD(device_probe, i80321_timer_probe), + DEVMETHOD(device_attach, i80321_timer_attach), + {0, 0}, +}; + +static driver_t i80321_timer_driver = { + "itimer", + i80321_timer_methods, + sizeof(struct i80321_timer_softc), +}; +static devclass_t i80321_timer_devclass; + +DRIVER_MODULE(itimer, iq, i80321_timer_driver, i80321_timer_devclass, 0, 0); + +int clockhandler(void *); + + +static __inline uint32_t +tmr1_read(void) +{ + uint32_t rv; + +#ifdef CPU_XSCALE_81342 + __asm __volatile("mrc p6, 0, %0, c1, c9, 0" +#else + __asm __volatile("mrc p6, 0, %0, c1, c1, 0" +#endif + : "=r" (rv)); + return (rv); +} + +static __inline void +tmr1_write(uint32_t val) +{ + + +#ifdef CPU_XSCALE_81342 + __asm __volatile("mcr p6, 0, %0, c1, c9, 0" +#else + __asm __volatile("mcr p6, 0, %0, c1, c1, 0" +#endif + : + : "r" (val)); +} + +static __inline uint32_t +tcr1_read(void) +{ + uint32_t rv; + +#ifdef CPU_XSCALE_81342 + __asm __volatile("mrc p6, 0, %0, c3, c9, 0" +#else + __asm __volatile("mrc p6, 0, %0, c3, c1, 0" +#endif + : "=r" (rv)); + return (rv); +} +static __inline void +tcr1_write(uint32_t val) +{ + +#ifdef CPU_XSCALE_81342 + __asm __volatile("mcr p6, 0, %0, c3, c9, 0" +#else + __asm __volatile("mcr p6, 0, %0, c3, c1, 0" +#endif + : + : "r" (val)); +} + +static __inline void +trr1_write(uint32_t val) +{ + +#ifdef CPU_XSCALE_81342 + __asm __volatile("mcr p6, 0, %0, c5, c9, 0" +#else + __asm __volatile("mcr p6, 0, %0, c5, c1, 0" +#endif + : + : "r" (val)); +} + +static __inline uint32_t +tmr0_read(void) +{ + uint32_t rv; + +#ifdef CPU_XSCALE_81342 + __asm __volatile("mrc p6, 0, %0, c0, c9, 0" +#else + __asm __volatile("mrc p6, 0, %0, c0, c1, 0" +#endif + : "=r" (rv)); + return (rv); +} + +static __inline void +tmr0_write(uint32_t val) +{ + +#ifdef CPU_XSCALE_81342 + __asm __volatile("mcr p6, 0, %0, c0, c9, 0" +#else + __asm __volatile("mcr p6, 0, %0, c0, c1, 0" +#endif + : + : "r" (val)); +} + +static __inline uint32_t +tcr0_read(void) +{ + uint32_t rv; + +#ifdef CPU_XSCALE_81342 + __asm __volatile("mrc p6, 0, %0, c2, c9, 0" +#else + __asm __volatile("mrc p6, 0, %0, c2, c1, 0" +#endif + : "=r" (rv)); + return (rv); +} +static __inline void +tcr0_write(uint32_t val) +{ + +#ifdef CPU_XSCALE_81342 + __asm __volatile("mcr p6, 0, %0, c2, c9, 0" +#else + __asm __volatile("mcr p6, 0, %0, c2, c1, 0" +#endif + : + : "r" (val)); +} + +static __inline void +trr0_write(uint32_t val) +{ + +#ifdef CPU_XSCALE_81342 + __asm __volatile("mcr p6, 0, %0, c4, c9, 0" +#else + __asm __volatile("mcr p6, 0, %0, c4, c1, 0" +#endif + : + : "r" (val)); +} + +static __inline void +tisr_write(uint32_t val) +{ + +#ifdef CPU_XSCALE_81342 + __asm __volatile("mcr p6, 0, %0, c6, c9, 0" +#else + __asm __volatile("mcr p6, 0, %0, c6, c1, 0" +#endif + : + : "r" (val)); +} + +static __inline uint32_t +tisr_read(void) +{ + int ret; + +#ifdef CPU_XSCALE_81342 + __asm __volatile("mrc p6, 0, %0, c6, c9, 0" : "=r" (ret)); +#else + __asm __volatile("mrc p6, 0, %0, c6, c1, 0" : "=r" (ret)); +#endif + return (ret); +} + +static unsigned +i80321_timer_get_timecount(struct timecounter *tc) +{ +#if defined(XSCALE_DISABLE_CCNT) || defined(CPU_XSCALE_81342) + uint32_t cur = tcr0_read(); + + if (cur > last && last != -1) { + offset += counts_per_hz; + if (ticked > 0) + ticked--; + } + if (ticked) { + offset += ticked * counts_per_hz; + ticked = 0; + } + return (counts_per_hz - cur + offset); +#else + uint32_t ret; + + __asm __volatile("mrc p14, 0, %0, c1, c0, 0\n" + : "=r" (ret)); + return (ret); +#endif +} + +/* + * i80321_calibrate_delay: + * + * Calibrate the delay loop. + */ +void +i80321_calibrate_delay(void) +{ + + /* + * Just use hz=100 for now -- we'll adjust it, if necessary, + * in cpu_initclocks(). + */ + counts_per_hz = COUNTS_PER_SEC / 100; + + tmr0_write(0); /* stop timer */ + tisr_write(TISR_TMR0); /* clear interrupt */ + trr0_write(counts_per_hz); /* reload value */ + tcr0_write(counts_per_hz); /* current value */ + + tmr0_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_CSEL_CORE); +} + +/* + * cpu_initclocks: + * + * Initialize the clock and get them going. + */ +void +cpu_initclocks(void) +{ + u_int oldirqstate; + struct resource *irq; + int rid = 0; + void *ihl; + device_t dev = timer_softc.dev; + + if (hz < 50 || COUNTS_PER_SEC % hz) { + printf("Cannot get %d Hz clock; using 100 Hz\n", hz); + hz = 100; + } + tick = 1000000 / hz; /* number of microseconds between interrupts */ + + /* + * We only have one timer available; stathz and profhz are + * always left as 0 (the upper-layer clock code deals with + * this situation). + */ + if (stathz != 0) + printf("Cannot get %d Hz statclock\n", stathz); + stathz = 0; + + if (profhz != 0) + printf("Cannot get %d Hz profclock\n", profhz); + profhz = 0; + + /* Report the clock frequency. */ + + oldirqstate = disable_interrupts(PSR_I); + + irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, +#ifdef CPU_XSCALE_81342 + ICU_INT_TIMER0, ICU_INT_TIMER0, +#else + ICU_INT_TMR0, ICU_INT_TMR0, +#endif + 1, RF_ACTIVE); + if (!irq) + panic("Unable to setup the clock irq handler.\n"); + else + bus_setup_intr(dev, irq, INTR_TYPE_CLK, clockhandler, NULL, + NULL, &ihl); + tmr0_write(0); /* stop timer */ + tisr_write(TISR_TMR0); /* clear interrupt */ + + counts_per_hz = COUNTS_PER_SEC / hz; + + trr0_write(counts_per_hz); /* reload value */ + tcr0_write(counts_per_hz); /* current value */ + tmr0_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_CSEL_CORE); + + tc_init(&i80321_timer_timecounter); + restore_interrupts(oldirqstate); + rid = 0; +#if !defined(XSCALE_DISABLE_CCNT) && !defined(CPU_XSCALE_81342) + /* Enable the clock count register. */ + __asm __volatile("mrc p14, 0, %0, c0, c0, 0\n" : "=r" (rid)); + rid &= ~(1 << 3); + rid |= (1 << 2) | 1; + __asm __volatile("mcr p14, 0, %0, c0, c0, 0\n" + : : "r" (rid)); +#endif +} + + +/* + * DELAY: + * + * Delay for at least N microseconds. + */ +void +DELAY(int n) +{ + uint32_t cur, last, delta, usecs; + + /* + * This works by polling the timer and counting the + * number of microseconds that go by. + */ + last = tcr0_read(); + delta = usecs = 0; + + while (n > usecs) { + cur = tcr0_read(); + + /* Check to see if the timer has wrapped around. */ + if (last < cur) + delta += (last + (counts_per_hz - cur)); + else + delta += (last - cur); + + last = cur; + + if (delta >= COUNTS_PER_USEC) { + usecs += delta / COUNTS_PER_USEC; + delta %= COUNTS_PER_USEC; + } + } +} + +/* + * clockhandler: + * + * Handle the hardclock interrupt. + */ +int +clockhandler(void *arg) +{ + struct trapframe *frame = arg; + + ticked++; + tisr_write(TISR_TMR0); + hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); + + if (i80321_hardclock_hook != NULL) + (*i80321_hardclock_hook)(); + return (FILTER_HANDLED); +} + +void +cpu_startprofclock(void) +{ +} + +void +cpu_stopprofclock(void) +{ + +} Property changes on: trunk/sys/arm/xscale/i8134x/i80321_timer.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/xscale/i8134x/i80321_wdog.c =================================================================== --- trunk/sys/arm/xscale/i8134x/i80321_wdog.c (rev 0) +++ trunk/sys/arm/xscale/i8134x/i80321_wdog.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -0,0 +1,154 @@ +/* $MidnightBSD$ */ +/* $NetBSD: i80321_wdog.c,v 1.6 2003/07/15 00:24:54 lukem Exp $ */ + +/*- + * Copyright (c) 2005 Olivier Houchard + * Copyright (c) 2002 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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. + */ + +/* + * Watchdog timer support for the Intel i80321 I/O processor. + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/i8134x/i80321_wdog.c 308325 2016-11-05 04:30:44Z mmel $"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + + +struct iopwdog_softc { + device_t dev; + int armed; + int wdog_period; +}; + +static __inline void +wdtcr_write(uint32_t val) +{ + +#ifdef CPU_XSCALE_81342 + __asm __volatile("mcr p6, 0, %0, c7, c9, 0" +#else + __asm __volatile("mcr p6, 0, %0, c7, c1, 0" +#endif + : + : "r" (val)); +} + +static void +iopwdog_tickle(void *arg) +{ + struct iopwdog_softc *sc = arg; + + if (!sc->armed) + return; + wdtcr_write(WDTCR_ENABLE1); + wdtcr_write(WDTCR_ENABLE2); +} + +static int +iopwdog_probe(device_t dev) +{ + struct iopwdog_softc *sc = device_get_softc(dev); + char buf[128]; + + /* + * XXX Should compute the period based on processor speed. + * For a 600MHz XScale core, the wdog must be tickled approx. + * every 7 seconds. + */ + + sc->wdog_period = 7; + sprintf(buf, "i80321 Watchdog, must be tickled every %d seconds", + sc->wdog_period); + device_set_desc_copy(dev, buf); + + return (0); +} + +static void +iopwdog_watchdog_fn(void *private, u_int cmd, int *error) +{ + struct iopwdog_softc *sc = private; + + cmd &= WD_INTERVAL; + if (cmd > 0 && cmd <= 63 + && (uint64_t)1<wdog_period * 1000000000) { + /* Valid value -> Enable watchdog */ + iopwdog_tickle(sc); + sc->armed = 1; + *error = 0; + } else { + /* Can't disable this watchdog! */ + if (sc->armed) + *error = EOPNOTSUPP; + } +} + +static int +iopwdog_attach(device_t dev) +{ + struct iopwdog_softc *sc = device_get_softc(dev); + + sc->dev = dev; + sc->armed = 0; + EVENTHANDLER_REGISTER(watchdog_list, iopwdog_watchdog_fn, sc, 0); + return (0); +} + +static device_method_t iopwdog_methods[] = { + DEVMETHOD(device_probe, iopwdog_probe), + DEVMETHOD(device_attach, iopwdog_attach), + {0, 0}, +}; + +static driver_t iopwdog_driver = { + "iopwdog", + iopwdog_methods, + sizeof(struct iopwdog_softc), +}; +static devclass_t iopwdog_devclass; + +DRIVER_MODULE(iopwdog, iq, iopwdog_driver, iopwdog_devclass, 0, 0); Property changes on: trunk/sys/arm/xscale/i8134x/i80321_wdog.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/xscale/i8134x/i80321reg.h =================================================================== --- trunk/sys/arm/xscale/i8134x/i80321reg.h (rev 0) +++ trunk/sys/arm/xscale/i8134x/i80321reg.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -0,0 +1,456 @@ +/* $MidnightBSD$ */ +/* $NetBSD: i80321reg.h,v 1.14 2003/12/19 10:08:11 gavan Exp $ */ + +/*- + * Copyright (c) 2002 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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: stable/11/sys/arm/xscale/i8134x/i80321reg.h 295201 2016-02-03 10:39:29Z mmel $ + * + */ + +#ifndef _ARM_XSCALE_I80321REG_H_ +#define _ARM_XSCALE_I80321REG_H_ + +/* + * Register definitions for the Intel 80321 (``Verde'') I/O processor, + * based on the XScale core. + */ + +/* + * Base i80321 memory map: + * + * 0x0000.0000 - 0x7fff.ffff ATU Outbound Direct Addressing Window + * 0x8000.0000 - 0x9001.ffff ATU Outbound Translation Windows + * 0x9002.0000 - 0xffff.dfff External Memory + * 0xffff.e000 - 0xffff.e8ff Peripheral Memory Mapped Registers + * 0xffff.e900 - 0xffff.ffff Reserved + */ + +#define VERDE_OUT_DIRECT_WIN_BASE 0x00000000UL +#define VERDE_OUT_DIRECT_WIN_SIZE 0x80000000UL + +#define VERDE_OUT_XLATE_MEM_WIN_SIZE 0x04000000UL +#define VERDE_OUT_XLATE_IO_WIN_SIZE 0x00010000UL + +#define VERDE_OUT_XLATE_MEM_WIN0_BASE 0x80000000UL +#define VERDE_OUT_XLATE_MEM_WIN1_BASE 0x84000000UL + +#define VERDE_OUT_XLATE_IO_WIN0_BASE 0x90000000UL + +#define VERDE_EXTMEM_BASE 0x90020000UL + +#define VERDE_PMMR_BASE 0xffffe000UL +#define VERDE_PMMR_SIZE 0x00001700UL + +/* + * Peripheral Memory Mapped Registers. Defined as offsets + * from the VERDE_PMMR_BASE. + */ +#define VERDE_ATU_BASE 0x0100 +#define VERDE_ATU_SIZE 0x0100 + +#define VERDE_MU_BASE 0x0300 +#define VERDE_MU_SIZE 0x0100 + +#define VERDE_DMA_BASE 0x0400 +#define VERDE_DMA_BASE0 (VERDE_DMA_BASE + 0x00) +#define VERDE_DMA_BASE1 (VERDE_DMA_BASE + 0x40) +#define VERDE_DMA_SIZE 0x0100 +#define VERDE_DMA_CHSIZE 0x0040 + +#define VERDE_MCU_BASE 0x0500 +#define VERDE_MCU_SIZE 0x0100 + +#define VERDE_PBIU_BASE 0x0680 +#define VERDE_PBIU_SIZE 0x0080 + +#define VERDE_I2C_BASE 0x1680 +#define VERDE_I2C_BASE0 (VERDE_I2C_BASE + 0x00) +#define VERDE_I2C_BASE1 (VERDE_I2C_BASE + 0x20) +#define VERDE_I2C_SIZE 0x0080 +#define VERDE_I2C_CHSIZE 0x0020 + +/* + * Address Translation Unit + */ + /* 0x00 - 0x38 -- PCI configuration space header */ +#define ATU_IALR0 0x40 /* Inbound ATU Limit 0 */ +#define ATU_IATVR0 0x44 /* Inbound ATU Xlate Value 0 */ +#define ATU_ERLR 0x48 /* Expansion ROM Limit */ +#define ATU_ERTVR 0x4c /* Expansion ROM Xlate Value */ +#define ATU_IALR1 0x50 /* Inbound ATU Limit 1 */ +#define ATU_IALR2 0x54 /* Inbound ATU Limit 2 */ +#define ATU_IATVR2 0x58 /* Inbound ATU Xlate Value 2 */ +#define ATU_OIOWTVR 0x5c /* Outbound I/O Window Xlate Value */ +#define ATU_OMWTVR0 0x60 /* Outbound Mem Window Xlate Value 0 */ +#define ATU_OUMWTVR0 0x64 /* Outbound Mem Window Xlate Value 0 Upper */ +#define ATU_OMWTVR1 0x68 /* Outbound Mem Window Xlate Value 1 */ +#define ATU_OUMWTVR1 0x6c /* Outbound Mem Window Xlate Value 1 Upper */ +#define ATU_OUDWTVR 0x78 /* Outbound Mem Direct Xlate Value Upper */ +#define ATU_ATUCR 0x80 /* ATU Configuration */ +#define ATU_PCSR 0x84 /* PCI Configuration and Status */ +#define ATU_ATUISR 0x88 /* ATU Interrupt Status */ +#define ATU_ATUIMR 0x8c /* ATU Interrupt Mask */ +#define ATU_IABAR3 0x90 /* Inbound ATU Base Address 3 */ +#define ATU_IAUBAR3 0x94 /* Inbound ATU Base Address 3 Upper */ +#define ATU_IALR3 0x98 /* Inbound ATU Limit 3 */ +#define ATU_IATVR3 0x9c /* Inbound ATU Xlate Value 3 */ +#define ATU_OCCAR 0xa4 /* Outbound Configuration Cycle Address */ +#define ATU_OCCDR 0xac /* Outbound Configuration Cycle Data */ +#define ATU_MSI_PORT 0xb4 /* MSI port */ +#define ATU_PDSCR 0xbc /* PCI Bus Drive Strength Control */ +#define ATU_PCI_X_CAP_ID 0xe0 /* (1) */ +#define ATU_PCI_X_NEXT 0xe1 /* (1) */ +#define ATU_PCIXCMD 0xe2 /* PCI-X Command Register (2) */ +#define ATU_PCIXSR 0xe4 /* PCI-X Status Register */ + +#define ATUCR_DRC_ALIAS (1U << 19) +#define ATUCR_DAU2GXEN (1U << 18) +#define ATUCR_P_SERR_MA (1U << 16) +#define ATUCR_DTS (1U << 15) +#define ATUCR_P_SERR_DIE (1U << 9) +#define ATUCR_DAE (1U << 8) +#define ATUCR_BIST_IE (1U << 3) +#define ATUCR_OUT_EN (1U << 1) + +#define PCSR_DAAAPE (1U << 18) +#define PCSR_PCI_X_CAP (3U << 16) +#define PCSR_PCI_X_CAP_BORING (0 << 16) +#define PCSR_PCI_X_CAP_66 (1U << 16) +#define PCSR_PCI_X_CAP_100 (2U << 16) +#define PCSR_PCI_X_CAP_133 (3U << 16) +#define PCSR_OTQB (1U << 15) +#define PCSR_IRTQB (1U << 14) +#define PCSR_DTV (1U << 12) +#define PCSR_BUS66 (1U << 10) +#define PCSR_BUS64 (1U << 8) +#define PCSR_RIB (1U << 5) +#define PCSR_RPB (1U << 4) +#define PCSR_CCR (1U << 2) +#define PCSR_CPR (1U << 1) + +#define ATUISR_IMW1BU (1U << 14) +#define ATUISR_ISCEM (1U << 13) +#define ATUISR_RSCEM (1U << 12) +#define ATUISR_PST (1U << 11) +#define ATUISR_P_SERR_ASRT (1U << 10) +#define ATUISR_DPE (1U << 9) +#define ATUISR_BIST (1U << 8) +#define ATUISR_IBMA (1U << 7) +#define ATUISR_P_SERR_DET (1U << 4) +#define ATUISR_PMA (1U << 3) +#define ATUISR_PTAM (1U << 2) +#define ATUISR_PTAT (1U << 1) +#define ATUISR_PMPE (1U << 0) + +#define ATUIMR_IMW1BU (1U << 11) +#define ATUIMR_ISCEM (1U << 10) +#define ATUIMR_RSCEM (1U << 9) +#define ATUIMR_PST (1U << 8) +#define ATUIMR_DPE (1U << 7) +#define ATUIMR_P_SERR_ASRT (1U << 6) +#define ATUIMR_PMA (1U << 5) +#define ATUIMR_PTAM (1U << 4) +#define ATUIMR_PTAT (1U << 3) +#define ATUIMR_PMPE (1U << 2) +#define ATUIMR_IE_SERR_EN (1U << 1) +#define ATUIMR_ECC_TAE (1U << 0) + +#define PCIXCMD_MOST_1 (0 << 4) +#define PCIXCMD_MOST_2 (1 << 4) +#define PCIXCMD_MOST_3 (2 << 4) +#define PCIXCMD_MOST_4 (3 << 4) +#define PCIXCMD_MOST_8 (4 << 4) +#define PCIXCMD_MOST_12 (5 << 4) +#define PCIXCMD_MOST_16 (6 << 4) +#define PCIXCMD_MOST_32 (7 << 4) +#define PCIXCMD_MOST_MASK (7 << 4) +#define PCIXCMD_MMRBC_512 (0 << 2) +#define PCIXCMD_MMRBC_1024 (1 << 2) +#define PCIXCMD_MMRBC_2048 (2 << 2) +#define PCIXCMD_MMRBC_4096 (3 << 2) +#define PCIXCMD_MMRBC_MASK (3 << 2) +#define PCIXCMD_ERO (1U << 1) +#define PCIXCMD_DPERE (1U << 0) + +#define PCIXSR_RSCEM (1U << 29) +#define PCIXSR_DMCRS_MASK (7 << 26) +#define PCIXSR_DMOST_MASK (7 << 23) +#define PCIXSR_COMPLEX (1U << 20) +#define PCIXSR_USC (1U << 19) +#define PCIXSR_SCD (1U << 18) +#define PCIXSR_133_CAP (1U << 17) +#define PCIXSR_32PCI (1U << 16) /* 0 = 32, 1 = 64 */ +#define PCIXSR_BUSNO(x) (((x) & 0xff00) >> 8) +#define PCIXSR_DEVNO(x) (((x) & 0xf8) >> 3) +#define PCIXSR_FUNCNO(x) ((x) & 0x7) + +/* + * Memory Controller Unit + */ +#define MCU_SDIR 0x00 /* DDR SDRAM Init. Register */ +#define MCU_SDCR 0x04 /* DDR SDRAM Control Register */ +#define MCU_SDBR 0x08 /* SDRAM Base Register */ +#define MCU_SBR0 0x0c /* SDRAM Boundary 0 */ +#define MCU_SBR1 0x10 /* SDRAM Boundary 1 */ +#define MCU_ECCR 0x34 /* ECC Control Register */ +#define MCU_ELOG0 0x38 /* ECC Log 0 */ +#define MCU_ELOG1 0x3c /* ECC Log 1 */ +#define MCU_ECAR0 0x40 /* ECC address 0 */ +#define MCU_ECAR1 0x44 /* ECC address 1 */ +#define MCU_ECTST 0x48 /* ECC test register */ +#define MCU_MCISR 0x4c /* MCU Interrupt Status Register */ +#define MCU_RFR 0x50 /* Refresh Frequency Register */ +#define MCU_DBUDSR 0x54 /* Data Bus Pull-up Drive Strength */ +#define MCU_DBDDSR 0x58 /* Data Bus Pull-down Drive Strength */ +#define MCU_CUDSR 0x5c /* Clock Pull-up Drive Strength */ +#define MCU_CDDSR 0x60 /* Clock Pull-down Drive Strength */ +#define MCU_CEUDSR 0x64 /* Clock En Pull-up Drive Strength */ +#define MCU_CEDDSR 0x68 /* Clock En Pull-down Drive Strength */ +#define MCU_CSUDSR 0x6c /* Chip Sel Pull-up Drive Strength */ +#define MCU_CSDDSR 0x70 /* Chip Sel Pull-down Drive Strength */ +#define MCU_REUDSR 0x74 /* Rx En Pull-up Drive Strength */ +#define MCU_REDDSR 0x78 /* Rx En Pull-down Drive Strength */ +#define MCU_ABUDSR 0x7c /* Addr Bus Pull-up Drive Strength */ +#define MCU_ABDDSR 0x80 /* Addr Bus Pull-down Drive Strength */ +#define MCU_DSDR 0x84 /* Data Strobe Delay Register */ +#define MCU_REDR 0x88 /* Rx Enable Delay Register */ + +#define SDCR_DIMMTYPE (1U << 1) /* 0 = unbuf, 1 = reg */ +#define SDCR_BUSWIDTH (1U << 2) /* 0 = 64, 1 = 32 */ + +#define SBRx_TECH (1U << 31) +#define SBRx_BOUND 0x0000003f + +#define ECCR_SBERE (1U << 0) +#define ECCR_MBERE (1U << 1) +#define ECCR_SBECE (1U << 2) +#define ECCR_ECCEN (1U << 3) + +#define ELOGx_SYNDROME 0x000000ff +#define ELOGx_ERRTYPE (1U << 8) /* 1 = multi-bit */ +#define ELOGx_RW (1U << 12) /* 1 = write error */ + /* + * Dev ID Func Requester + * 2 0 XScale core + * 2 1 ATU + * 13 0 DMA channel 0 + * 13 1 DMA channel 1 + * 26 0 ATU + */ +#define ELOGx_REQ_DEV(x) (((x) >> 19) & 0x1f) +#define ELOGx_REQ_FUNC(x) (((x) >> 16) & 0x3) + +#define MCISR_ECC_ERR0 (1U << 0) +#define MCISR_ECC_ERR1 (1U << 1) +#define MCISR_ECC_ERRN (1U << 2) + +/* + * Timers + * + * The i80321 timer registers are available in both memory-mapped + * and coprocessor spaces. Most of the registers are read-only + * if memory-mapped, so we access them via coprocessor space. + * + * TMR0 cp6 c0,1 0xffffe7e0 + * TMR1 cp6 c1,1 0xffffe7e4 + * TCR0 cp6 c2,1 0xffffe7e8 + * TCR1 cp6 c3,1 0xffffe7ec + * TRR0 cp6 c4,1 0xffffe7f0 + * TRR1 cp6 c5,1 0xffffe7f4 + * TISR cp6 c6,1 0xffffe7f8 + * WDTCR cp6 c7,1 0xffffe7fc + */ + +#define TMRx_TC (1U << 0) +#define TMRx_ENABLE (1U << 1) +#define TMRx_RELOAD (1U << 2) +#define TMRx_CSEL_CORE (0 << 4) +#define TMRx_CSEL_CORE_div4 (1 << 4) +#define TMRx_CSEL_CORE_div8 (2 << 4) +#define TMRx_CSEL_CORE_div16 (3 << 4) + +#define TISR_TMR0 (1U << 0) +#define TISR_TMR1 (1U << 1) + +#define WDTCR_ENABLE1 0x1e1e1e1e +#define WDTCR_ENABLE2 0xe1e1e1e1 + +/* + * Interrupt Controller Unit. + * + * INTCTL cp6 c0,0 0xffffe7d0 + * INTSTR cp6 c4,0 0xffffe7d4 + * IINTSRC cp6 c8,0 0xffffe7d8 + * FINTSRC cp6 c9,0 0xffffe7dc + * PIRSR 0xffffe1ec + */ + +#define ICU_PIRSR 0x01ec +#define ICU_GPOE 0x07c4 +#define ICU_GPID 0x07c8 +#define ICU_GPOD 0x07cc + +/* + * NOTE: WE USE THE `bitXX' BITS TO INDICATE PENDING SOFTWARE + * INTERRUPTS. See i80321_icu.c + */ +#define ICU_INT_HPI 31 /* high priority interrupt */ +#define ICU_INT_XINT0 27 /* external interrupts */ +#define ICU_INT_XINT(x) ((x) + ICU_INT_XINT0) +#define ICU_INT_bit26 26 + +/* CPU_XSCALE_80321 */ +#define ICU_INT_SSP 25 /* SSP serial port */ + +#define ICU_INT_MUE 24 /* msg unit error */ + +/* CPU_XSCALE_80321 */ +#define ICU_INT_AAUE 23 /* AAU error */ + +#define ICU_INT_bit22 22 +#define ICU_INT_DMA1E 21 /* DMA Ch 1 error */ +#define ICU_INT_DMA0E 20 /* DMA Ch 0 error */ +#define ICU_INT_MCUE 19 /* memory controller error */ +#define ICU_INT_ATUE 18 /* ATU error */ +#define ICU_INT_BIUE 17 /* bus interface unit error */ +#define ICU_INT_PMU 16 /* XScale PMU */ +#define ICU_INT_PPM 15 /* peripheral PMU */ +#define ICU_INT_BIST 14 /* ATU Start BIST */ +#define ICU_INT_MU 13 /* messaging unit */ +#define ICU_INT_I2C1 12 /* i2c unit 1 */ +#define ICU_INT_I2C0 11 /* i2c unit 0 */ +#define ICU_INT_TMR1 10 /* timer 1 */ +#define ICU_INT_TMR0 9 /* timer 0 */ +#define ICU_INT_CPPM 8 /* core processor PMU */ + +/* CPU_XSCALE_80321 */ +#define ICU_INT_AAU_EOC 7 /* AAU end-of-chain */ +#define ICU_INT_AAU_EOT 6 /* AAU end-of-transfer */ + +#define ICU_INT_bit5 5 +#define ICU_INT_bit4 4 +#define ICU_INT_DMA1_EOC 3 /* DMA1 end-of-chain */ +#define ICU_INT_DMA1_EOT 2 /* DMA1 end-of-transfer */ +#define ICU_INT_DMA0_EOC 1 /* DMA0 end-of-chain */ +#define ICU_INT_DMA0_EOT 0 /* DMA0 end-of-transfer */ + +/* CPU_XSCALE_80321 */ +#define ICU_INT_HWMASK (0xffffffff & \ + ~((1 << ICU_INT_bit26) | \ + (1 << ICU_INT_bit22) | \ + (1 << ICU_INT_bit5) | \ + (1 << ICU_INT_bit4))) + +/* + * Peripheral Bus Interface Unit + */ + +#define PBIU_PBCR 0x00 /* PBIU Control Register */ +#define PBIU_PBBAR0 0x08 /* PBIU Base Address Register 0 */ +#define PBIU_PBLR0 0x0c /* PBIU Limit Register 0 */ +#define PBIU_PBBAR1 0x10 /* PBIU Base Address Register 1 */ +#define PBIU_PBLR1 0x14 /* PBIU Limit Register 1 */ +#define PBIU_PBBAR2 0x18 /* PBIU Base Address Register 2 */ +#define PBIU_PBLR2 0x1c /* PBIU Limit Register 2 */ +#define PBIU_PBBAR3 0x20 /* PBIU Base Address Register 3 */ +#define PBIU_PBLR3 0x24 /* PBIU Limit Register 3 */ +#define PBIU_PBBAR4 0x28 /* PBIU Base Address Register 4 */ +#define PBIU_PBLR4 0x2c /* PBIU Limit Register 4 */ +#define PBIU_PBBAR5 0x30 /* PBIU Base Address Register 5 */ +#define PBIU_PBLR5 0x34 /* PBIU Limit Register 5 */ +#define PBIU_DSCR 0x38 /* PBIU Drive Strength Control Reg. */ +#define PBIU_MBR0 0x40 /* PBIU Memory-less Boot Reg. 0 */ +#define PBIU_MBR1 0x60 /* PBIU Memory-less Boot Reg. 1 */ +#define PBIU_MBR2 0x64 /* PBIU Memory-less Boot Reg. 2 */ + +#define PBIU_PBCR_PBIEN (1 << 0) +#define PBIU_PBCR_PBI100 (1 << 1) +#define PBIU_PBCR_PBI66 (2 << 1) +#define PBIU_PBCR_PBI33 (3 << 1) +#define PBIU_PBCR_PBBEN (1 << 3) + +#define PBIU_PBARx_WIDTH8 (0 << 0) +#define PBIU_PBARx_WIDTH16 (1 << 0) +#define PBIU_PBARx_WIDTH32 (2 << 0) +#define PBIU_PBARx_ADWAIT4 (0 << 2) +#define PBIU_PBARx_ADWAIT8 (1 << 2) +#define PBIU_PBARx_ADWAIT12 (2 << 2) +#define PBIU_PBARx_ADWAIT16 (3 << 2) +#define PBIU_PBARx_ADWAIT20 (4 << 2) +#define PBIU_PBARx_RCWAIT1 (0 << 6) +#define PBIU_PBARx_RCWAIT4 (1 << 6) +#define PBIU_PBARx_RCWAIT8 (2 << 6) +#define PBIU_PBARx_RCWAIT12 (3 << 6) +#define PBIU_PBARx_RCWAIT16 (4 << 6) +#define PBIU_PBARx_RCWAIT20 (5 << 6) +#define PBIU_PBARx_FWE (1 << 9) +#define PBIU_BASE_MASK 0xfffff000U + +#define PBIU_PBLRx_SIZE(x) (~((x) - 1)) + +/* + * Messaging Unit + */ +#define MU_IMR0 0x0010 /* MU Inbound Message Register 0 */ +#define MU_IMR1 0x0014 /* MU Inbound Message Register 1 */ +#define MU_OMR0 0x0018 /* MU Outbound Message Register 0 */ +#define MU_OMR1 0x001c /* MU Outbound Message Register 1 */ +#define MU_IDR 0x0020 /* MU Inbound Doorbell Register */ +#define MU_IISR 0x0024 /* MU Inbound Interrupt Status Reg */ +#define MU_IIMR 0x0028 /* MU Inbound Interrupt Mask Reg */ +#define MU_ODR 0x002c /* MU Outbound Doorbell Register */ +#define MU_OISR 0x0030 /* MU Outbound Interrupt Status Reg */ +#define MU_OIMR 0x0034 /* MU Outbound Interrupt Mask Reg */ +#define MU_MUCR 0x0050 /* MU Configuration Register */ +#define MU_QBAR 0x0054 /* MU Queue Base Address Register */ +#define MU_IFHPR 0x0060 /* MU Inbound Free Head Pointer Reg */ +#define MU_IFTPR 0x0064 /* MU Inbound Free Tail Pointer Reg */ +#define MU_IPHPR 0x0068 /* MU Inbound Post Head Pointer Reg */ +#define MU_IPTPR 0x006c /* MU Inbound Post Tail Pointer Reg */ +#define MU_OFHPR 0x0070 /* MU Outbound Free Head Pointer Reg */ +#define MU_OFTPR 0x0074 /* MU Outbound Free Tail Pointer Reg */ +#define MU_OPHPR 0x0078 /* MU Outbound Post Head Pointer Reg */ +#define MU_OPTPR 0x007c /* MU Outbound Post Tail Pointer Reg */ +#define MU_IAR 0x0080 /* MU Index Address Register */ + +#define MU_IIMR_IRI (1 << 6) /* Index Register Interrupt */ +#define MU_IIMR_OFQFI (1 << 5) /* Outbound Free Queue Full Int. */ +#define MU_IIMR_IPQI (1 << 4) /* Inbound Post Queue Interrupt */ +#define MU_IIMR_EDI (1 << 3) /* Error Doorbell Interrupt */ +#define MU_IIMR_IDI (1 << 2) /* Inbound Doorbell Interrupt */ +#define MU_IIMR_IM1I (1 << 1) /* Inbound Message 1 Interrupt */ +#define MU_IIMR_IM0I (1 << 0) /* Inbound Message 0 Interrupt */ + +#endif /* _ARM_XSCALE_I80321REG_H_ */ Property changes on: trunk/sys/arm/xscale/i8134x/i80321reg.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/xscale/i8134x/i80321var.h =================================================================== --- trunk/sys/arm/xscale/i8134x/i80321var.h (rev 0) +++ trunk/sys/arm/xscale/i8134x/i80321var.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -0,0 +1,138 @@ +/* $MidnightBSD$ */ +/* $NetBSD: i80321var.h,v 1.8 2003/10/06 16:06:06 thorpej Exp $ */ + +/*- + * Copyright (c) 2002, 2003 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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: stable/11/sys/arm/xscale/i8134x/i80321var.h 295199 2016-02-03 08:59:12Z mmel $ + * + */ + +#ifndef _ARM_XSCALE_I80321VAR_H_ +#define _ARM_XSCALE_I80321VAR_H_ + +#include +#include +#include + +extern struct bus_space i80321_bs_tag; + +struct i80321_softc { + device_t dev; + bus_space_tag_t sc_st; + bus_space_handle_t sc_sh; + /* Handles for the various subregions. */ + bus_space_handle_t sc_atu_sh; + bus_space_handle_t sc_mcu_sh; + int sc_is_host; + + /* + * We expect the board-specific front-end to have already mapped + * the PCI I/O space .. it is only 64K, and I/O mappings tend to + * be smaller than a page size, so it's generally more efficient + * to map them all into virtual space in one fell swoop. + */ + vm_offset_t sc_iow_vaddr; /* I/O window vaddr */ + + /* + * Variables that define the Inbound windows. The base address of + * 0-2 are configured by a host via BARs. The xlate variable + * defines the start of the local address space that it maps to. + * The size variable defines the byte size. + * + * The first 3 windows are for incoming PCI memory read/write + * cycles from a host. The 4th window, not configured by the + * host (as it outside the normal BAR range) is the inbound + * window for PCI devices controlled by the i80321. + */ + struct { + uint32_t iwin_base_hi; + uint32_t iwin_base_lo; + uint32_t iwin_xlate; + uint32_t iwin_size; + } sc_iwin[4]; + + /* + * Variables that define the Outbound windows. + */ + struct { + uint32_t owin_xlate_lo; + uint32_t owin_xlate_hi; + } sc_owin[2]; + + /* + * This is the PCI address that the Outbound I/O + * window maps to. + */ + uint32_t sc_ioout_xlate; + + /* Bus space, DMA, and PCI tags for the PCI bus (private devices). */ + struct bus_space sc_pci_iot; + struct bus_space sc_pci_memt; + + /* GPIO state */ + uint8_t sc_gpio_dir; /* GPIO pin direction (1 == output) */ + uint8_t sc_gpio_val; /* GPIO output pin value */ + struct rman sc_irq_rman; + +}; + + +struct i80321_pci_softc { + device_t sc_dev; + bus_space_tag_t sc_st; + bus_space_handle_t sc_atu_sh; + bus_space_tag_t sc_pciio; + bus_space_tag_t sc_pcimem; + int sc_busno; + struct rman sc_mem_rman; + struct rman sc_io_rman; + struct rman sc_irq_rman; + uint32_t sc_mem; + uint32_t sc_io; +}; + +void i80321_sdram_bounds(bus_space_tag_t, bus_space_handle_t, + vm_paddr_t *, vm_size_t *); + +void i80321_attach(struct i80321_softc *); +void i80321_calibrate_delay(void); + +void i80321_bs_init(bus_space_tag_t, void *); +void i80321_io_bs_init(bus_space_tag_t, void *); +void i80321_mem_bs_init(bus_space_tag_t, void *); +extern int machdep_pci_route_interrupt(device_t pcib, device_t dev, int pin); + + +#endif /* _ARM_XSCALE_I80321VAR_H_ */ Property changes on: trunk/sys/arm/xscale/i8134x/i80321var.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Modified: trunk/sys/arm/xscale/i8134x/i81342.c =================================================================== --- trunk/sys/arm/xscale/i8134x/i81342.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/i81342.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -26,7 +26,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/i8134x/i81342.c 278613 2015-02-12 03:50:33Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/i8134x/i81342.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -410,7 +410,7 @@ static struct resource * i81342_alloc_resource(device_t dev, device_t child, int type, int *rid, - u_long start, u_long end, u_long count, u_int flags) + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct i81342_softc *sc = device_get_softc(dev); struct resource *rv; Modified: trunk/sys/arm/xscale/i8134x/i81342_mcu.c =================================================================== --- trunk/sys/arm/xscale/i8134x/i81342_mcu.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/i81342_mcu.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -26,7 +26,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/i8134x/i81342_mcu.c 236987 2012-06-13 04:38:09Z imp $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/i8134x/i81342_mcu.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include Modified: trunk/sys/arm/xscale/i8134x/i81342_pci.c =================================================================== --- trunk/sys/arm/xscale/i8134x/i81342_pci.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/i81342_pci.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -26,7 +26,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/i8134x/i81342_pci.c 259329 2013-12-13 20:43:11Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/i8134x/i81342_pci.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -122,8 +122,8 @@ memstart | PCI_MAPREG_MEM_PREFETCHABLE_MASK | PCI_MAPREG_MEM_TYPE_64BIT); bus_space_write_4(sc->sc_st, sc->sc_atu_sh, ATU_IAUBAR1, 0); - bus_space_write_4(sc->sc_st, sc->sc_atu_sh, ATU_IALR1, ~(memsize - 1) - &~(0xfff)); + bus_space_write_4(sc->sc_st, sc->sc_atu_sh, ATU_IALR1, + rounddown2(~(0xfff), memsize)); bus_space_write_4(sc->sc_st, sc->sc_atu_sh, ATU_IATVR1, memstart); bus_space_write_4(sc->sc_st, sc->sc_atu_sh, ATU_IAUTVR1, 0); @@ -210,7 +210,7 @@ } bus_space_write_4(sc->sc_st, sc->sc_atu_sh, ATU_ISR, bus_space_read_4(sc->sc_st, sc->sc_atu_sh, ATU_ISR) & ATUX_ISR_ERRMSK); - device_add_child(dev, "pci", busno); + device_add_child(dev, "pci", -1); return (bus_generic_attach(dev)); } @@ -329,7 +329,7 @@ static struct resource * i81342_pci_alloc_resource(device_t bus, device_t child, int type, int *rid, - u_long start, u_long end, u_long count, u_int flags) + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct i81342_pci_softc *sc = device_get_softc(bus); struct resource *rv; @@ -384,7 +384,7 @@ i81342_pci_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { - u_long p; + bus_space_handle_t p; int error; if (type == SYS_RES_MEMORY) { Modified: trunk/sys/arm/xscale/i8134x/i81342_space.c =================================================================== --- trunk/sys/arm/xscale/i8134x/i81342_space.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/i81342_space.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -41,7 +41,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/i8134x/i81342_space.c 278727 2015-02-13 22:32:02Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/i8134x/i81342_space.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -179,13 +179,13 @@ tmp = tmp->next; } addr = allocable; - endaddr = ((addr + size) &~ (0x1000000 - 1)) + 0x1000000; + endaddr = rounddown2(addr + size, 0x1000000) + 0x1000000; if (endaddr >= IOP34X_VADDR) panic("PCI virtual memory exhausted"); allocable = endaddr; tmp = malloc(sizeof(*tmp), M_DEVBUF, M_WAITOK); tmp->next = NULL; - paddr = bpa &~ (0x100000 - 1); + paddr = rounddown2(bpa, 0x100000); tmp->paddr = paddr; tmp->vaddr = addr; tmp->size = 0; Modified: trunk/sys/arm/xscale/i8134x/i81342reg.h =================================================================== --- trunk/sys/arm/xscale/i8134x/i81342reg.h 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/i81342reg.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -25,7 +25,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ -/* $FreeBSD: stable/10/sys/arm/xscale/i8134x/i81342reg.h 261455 2014-02-04 03:36:42Z eadler $ */ +/* $FreeBSD: stable/11/sys/arm/xscale/i8134x/i81342reg.h 331722 2018-03-29 02:50:57Z eadler $ */ #ifndef I83142_REG_H_ #define I83142_REG_H_ @@ -185,7 +185,7 @@ #define ATU_IATVR2 0x005c /* Inbound ATU Translate Value Register 2 */ #define ATU_IAUTVR2 0x0060 /* Inbound ATU Upper Translate Value Register 2*/ #define ATU_ERLR 0x0064 /* Expansion ROM Limit Register */ -#define ATU_ERTVR 0x0068 /* Expansion ROM Translater Value Register */ +#define ATU_ERTVR 0x0068 /* Expansion ROM Translator Value Register */ #define ATU_ERUTVR 0x006c /* Expansion ROM Upper Translate Value Register*/ #define ATU_CR 0x0070 /* ATU Configuration Register */ #define ATU_CR_OUT_EN (1 << 1) Modified: trunk/sys/arm/xscale/i8134x/i81342var.h =================================================================== --- trunk/sys/arm/xscale/i8134x/i81342var.h 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/i81342var.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -25,7 +25,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ -/* $FreeBSD: stable/10/sys/arm/xscale/i8134x/i81342var.h 171626 2007-07-27 14:50:57Z cognet $ */ +/* $FreeBSD: stable/11/sys/arm/xscale/i8134x/i81342var.h 331722 2018-03-29 02:50:57Z eadler $ */ #ifndef I81342VAR_H_ #define I81342VAR_H_ Modified: trunk/sys/arm/xscale/i8134x/iq81342_7seg.c =================================================================== --- trunk/sys/arm/xscale/i8134x/iq81342_7seg.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/iq81342_7seg.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -41,7 +41,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/i8134x/iq81342_7seg.c 236987 2012-06-13 04:38:09Z imp $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/i8134x/iq81342_7seg.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include Modified: trunk/sys/arm/xscale/i8134x/iq81342reg.h =================================================================== --- trunk/sys/arm/xscale/i8134x/iq81342reg.h 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/iq81342reg.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -25,7 +25,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ -/* $FreeBSD: stable/10/sys/arm/xscale/i8134x/iq81342reg.h 172297 2007-09-22 16:25:43Z cognet $ */ +/* $FreeBSD: stable/11/sys/arm/xscale/i8134x/iq81342reg.h 331722 2018-03-29 02:50:57Z eadler $ */ #ifndef _IQ81342REG_H_ #define _IQ81342REG_H_ Modified: trunk/sys/arm/xscale/i8134x/iq81342var.h =================================================================== --- trunk/sys/arm/xscale/i8134x/iq81342var.h 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/iq81342var.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -25,7 +25,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ -/* $FreeBSD: stable/10/sys/arm/xscale/i8134x/iq81342var.h 172297 2007-09-22 16:25:43Z cognet $ */ +/* $FreeBSD: stable/11/sys/arm/xscale/i8134x/iq81342var.h 331722 2018-03-29 02:50:57Z eadler $ */ #ifndef _IQ81342VAR_H_ #define _IQ81342VAR_H_ Modified: trunk/sys/arm/xscale/i8134x/obio.c =================================================================== --- trunk/sys/arm/xscale/i8134x/obio.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/obio.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -42,7 +42,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/i8134x/obio.c 278727 2015-02-13 22:32:02Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/i8134x/obio.c 331890 2018-04-02 22:02:49Z gonzo $"); #include #include @@ -57,8 +57,6 @@ #include #include -bus_space_tag_t obio_bs_tag; - static int obio_probe(device_t dev) { @@ -70,8 +68,7 @@ { struct obio_softc *sc = device_get_softc(dev); - obio_bs_tag = arm_base_bs_tag; - sc->oba_st = obio_bs_tag; + sc->oba_st = arm_base_bs_tag; sc->oba_rman.rm_type = RMAN_ARRAY; sc->oba_rman.rm_descr = "OBIO I/O"; if (rman_init(&sc->oba_rman) != 0 || @@ -92,7 +89,7 @@ static struct resource * obio_alloc_resource(device_t bus, device_t child, int type, int *rid, - u_long start, u_long end, u_long count, u_int flags) + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct resource *rv; struct rman *rm; Modified: trunk/sys/arm/xscale/i8134x/obiovar.h =================================================================== --- trunk/sys/arm/xscale/i8134x/obiovar.h 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/obiovar.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -35,7 +35,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/xscale/i8134x/obiovar.h 278727 2015-02-13 22:32:02Z ian $ + * $FreeBSD: stable/11/sys/arm/xscale/i8134x/obiovar.h 331890 2018-04-02 22:02:49Z gonzo $ * */ @@ -51,6 +51,5 @@ struct rman oba_irq_rman; }; -extern bus_space_tag_t obio_bs_tag; #endif /* _IQ80321_OBIOVAR_H_ */ Modified: trunk/sys/arm/xscale/i8134x/std.crb =================================================================== --- trunk/sys/arm/xscale/i8134x/std.crb 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/std.crb 2020-03-06 16:44:57 UTC (rev 12394) @@ -1,5 +1,5 @@ #CRB board configuration -#$FreeBSD: stable/10/sys/arm/xscale/i8134x/std.crb 171626 2007-07-27 14:50:57Z cognet $ +#$FreeBSD: stable/11/sys/arm/xscale/i8134x/std.crb 171626 2007-07-27 14:50:57Z cognet $ include "../xscale/i8134x/std.i81342" files "../xscale/i8134x/files.crb" makeoptions KERNPHYSADDR=0x00200000 Modified: trunk/sys/arm/xscale/i8134x/std.i81342 =================================================================== --- trunk/sys/arm/xscale/i8134x/std.i81342 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/std.i81342 2020-03-06 16:44:57 UTC (rev 12394) @@ -1,5 +1,5 @@ #XScale i81342 generic configuration -#$FreeBSD: stable/10/sys/arm/xscale/i8134x/std.i81342 239362 2012-08-18 05:48:19Z andrew $ +#$FreeBSD: stable/11/sys/arm/xscale/i8134x/std.i81342 239362 2012-08-18 05:48:19Z andrew $ files "../xscale/i8134x/files.i81342" include "../xscale/std.xscale-be" cpu CPU_XSCALE_81342 Modified: trunk/sys/arm/xscale/i8134x/uart_bus_i81342.c =================================================================== --- trunk/sys/arm/xscale/i8134x/uart_bus_i81342.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/uart_bus_i81342.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -24,7 +24,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/i8134x/uart_bus_i81342.c 171626 2007-07-27 14:50:57Z cognet $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/i8134x/uart_bus_i81342.c 340145 2018-11-04 23:28:56Z mmacy $"); #include #include @@ -75,8 +75,8 @@ sc->sc_sysdev = SLIST_FIRST(&uart_sysdevs); bcopy(&sc->sc_sysdev->bas, &sc->sc_bas, sizeof(sc->sc_bas)); } - sc->sc_rres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_rrid, - 0, ~0, uart_getrange(sc->sc_class), RF_ACTIVE); + sc->sc_rres = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, + &sc->sc_rrid, uart_getrange(sc->sc_class), RF_ACTIVE); sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres); sc->sc_bas.bst = rman_get_bustag(sc->sc_rres); @@ -84,7 +84,7 @@ 0x40 | 0x10); bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); - err = uart_bus_probe(dev, 2, 33334000, 0, device_get_unit(dev)); + err = uart_bus_probe(dev, 2, 0, 33334000, 0, device_get_unit(dev), 0); sc->sc_rxfifosz = sc->sc_txfifosz = 1; return (err); } Modified: trunk/sys/arm/xscale/i8134x/uart_cpu_i81342.c =================================================================== --- trunk/sys/arm/xscale/i8134x/uart_cpu_i81342.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/i8134x/uart_cpu_i81342.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -26,7 +26,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/i8134x/uart_cpu_i81342.c 278727 2015-02-13 22:32:02Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/i8134x/uart_cpu_i81342.c 331890 2018-04-02 22:02:49Z gonzo $"); #include #include @@ -55,7 +55,7 @@ di->ops = uart_getops(&uart_ns8250_class); di->bas.chan = 0; - di->bas.bst = obio_bs_tag; + di->bas.bst = arm_base_bs_tag; di->bas.regshft = 2; di->bas.rclk = 33334000; di->baudrate = 115200; @@ -62,7 +62,7 @@ di->databits = 8; di->stopbits = 1; di->parity = UART_PARITY_NONE; - uart_bus_space_io = obio_bs_tag; + uart_bus_space_io = arm_base_bs_tag; uart_bus_space_mem = NULL; di->bas.bsh = IOP34X_UART0_VADDR; return (0); Modified: trunk/sys/arm/xscale/ixp425/avila_ata.c =================================================================== --- trunk/sys/arm/xscale/ixp425/avila_ata.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/avila_ata.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -29,7 +29,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/avila_ata.c 305615 2016-09-08 15:06:28Z pfg $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/avila_ata.c 331722 2018-03-29 02:50:57Z eadler $"); /* * Compact Flash Support for the Avila Gateworks XScale boards. @@ -54,8 +54,6 @@ #include #include -#include -#include #include #include #include @@ -283,12 +281,12 @@ static struct resource * ata_avila_alloc_resource(device_t dev, device_t child, int type, int *rid, - u_long start, u_long end, u_long count, u_int flags) + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { 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 */ Modified: trunk/sys/arm/xscale/ixp425/avila_gpio.c =================================================================== --- trunk/sys/arm/xscale/ixp425/avila_gpio.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/avila_gpio.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -33,7 +33,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/avila_gpio.c 278786 2015-02-14 21:16:19Z loos $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/avila_gpio.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -50,6 +50,7 @@ #include #include #include +#include #include "gpio_if.h" @@ -61,6 +62,7 @@ struct avila_gpio_softc { device_t sc_dev; + device_t sc_busdev; bus_space_tag_t sc_iot; bus_space_handle_t sc_gpio_ioh; uint32_t sc_valid; @@ -117,6 +119,7 @@ /* * GPIO interface */ +static device_t avila_gpio_get_bus(device_t); static int avila_gpio_pin_max(device_t dev, int *maxpin); static int avila_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); static int avila_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t @@ -163,6 +166,16 @@ } } +static device_t +avila_gpio_get_bus(device_t dev) +{ + struct avila_gpio_softc *sc; + + sc = device_get_softc(dev); + + return (sc->sc_busdev); +} + static int avila_gpio_pin_max(device_t dev, int *maxpin) { @@ -311,10 +324,11 @@ sc->sc_valid |= 1 << p->pin; } - device_add_child(dev, "gpioc", -1); - device_add_child(dev, "gpiobus", -1); + sc->sc_busdev = gpiobus_attach_bus(dev); + if (sc->sc_busdev == NULL) + return (ENXIO); - return (bus_generic_attach(dev)); + return (0); #undef N } @@ -322,7 +336,7 @@ avila_gpio_detach(device_t dev) { - bus_generic_detach(dev); + gpiobus_detach_bus(dev); return(0); } @@ -333,6 +347,7 @@ DEVMETHOD(device_detach, avila_gpio_detach), /* GPIO protocol */ + DEVMETHOD(gpio_get_bus, avila_gpio_get_bus), DEVMETHOD(gpio_pin_max, avila_gpio_pin_max), DEVMETHOD(gpio_pin_getname, avila_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, avila_gpio_pin_getflags), @@ -345,15 +360,11 @@ }; static driver_t gpio_avila_driver = { - "gpio_avila", + "gpio", gpio_avila_methods, sizeof(struct avila_gpio_softc), }; static devclass_t gpio_avila_devclass; -extern devclass_t gpiobus_devclass, gpioc_devclass; -extern driver_t gpiobus_driver, gpioc_driver; DRIVER_MODULE(gpio_avila, ixp, gpio_avila_driver, gpio_avila_devclass, 0, 0); -DRIVER_MODULE(gpiobus, gpio_avila, gpiobus_driver, gpiobus_devclass, 0, 0); -DRIVER_MODULE(gpioc, gpio_avila, gpioc_driver, gpioc_devclass, 0, 0); MODULE_VERSION(gpio_avila, 1); Modified: trunk/sys/arm/xscale/ixp425/avila_led.c =================================================================== --- trunk/sys/arm/xscale/ixp425/avila_led.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/avila_led.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -24,7 +24,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/avila_led.c 215319 2010-11-14 20:41:22Z thompsa $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/avila_led.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include Modified: trunk/sys/arm/xscale/ixp425/avila_machdep.c =================================================================== --- trunk/sys/arm/xscale/ixp425/avila_machdep.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/avila_machdep.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -39,7 +39,7 @@ * * machdep.c * - * Machine dependant functions for kernel setup + * Machine dependent functions for kernel setup * * This file needs a lot of work. * @@ -47,8 +47,10 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/avila_machdep.c 294683 2016-01-24 21:04:06Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/avila_machdep.c 331722 2018-03-29 02:50:57Z eadler $"); +#include "opt_kstack_pages.h" + #define _ARM32_BUS_DMA_PRIVATE #include #include @@ -71,6 +73,7 @@ #include #include #include +#include #include #include #include @@ -80,7 +83,6 @@ #include #include #include -#include #include #include #include @@ -116,81 +118,65 @@ struct pv_addr minidataclean; /* Static device mappings. */ -static const struct arm_devmap_entry ixp425_devmap[] = { +static const struct devmap_entry ixp425_devmap[] = { /* Physical/Virtual address for I/O space */ - { IXP425_IO_VBASE, IXP425_IO_HWBASE, IXP425_IO_SIZE, - VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + { IXP425_IO_VBASE, IXP425_IO_HWBASE, IXP425_IO_SIZE, }, /* Expansion Bus */ - { IXP425_EXP_VBASE, IXP425_EXP_HWBASE, IXP425_EXP_SIZE, - VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + { IXP425_EXP_VBASE, IXP425_EXP_HWBASE, IXP425_EXP_SIZE, }, /* CFI Flash on the Expansion Bus */ { IXP425_EXP_BUS_CS0_VBASE, IXP425_EXP_BUS_CS0_HWBASE, - IXP425_EXP_BUS_CS0_SIZE, VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + IXP425_EXP_BUS_CS0_SIZE, }, /* IXP425 PCI Configuration */ - { IXP425_PCI_VBASE, IXP425_PCI_HWBASE, IXP425_PCI_SIZE, - VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + { IXP425_PCI_VBASE, IXP425_PCI_HWBASE, IXP425_PCI_SIZE, }, /* SDRAM Controller */ - { IXP425_MCU_VBASE, IXP425_MCU_HWBASE, IXP425_MCU_SIZE, - VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + { IXP425_MCU_VBASE, IXP425_MCU_HWBASE, IXP425_MCU_SIZE, }, /* PCI Memory Space */ - { IXP425_PCI_MEM_VBASE, IXP425_PCI_MEM_HWBASE, IXP425_PCI_MEM_SIZE, - VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + { IXP425_PCI_MEM_VBASE, IXP425_PCI_MEM_HWBASE, IXP425_PCI_MEM_SIZE, }, /* Q-Mgr Memory Space */ - { IXP425_QMGR_VBASE, IXP425_QMGR_HWBASE, IXP425_QMGR_SIZE, - VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + { IXP425_QMGR_VBASE, IXP425_QMGR_HWBASE, IXP425_QMGR_SIZE, }, { 0 }, }; /* Static device mappings. */ -static const struct arm_devmap_entry ixp435_devmap[] = { +static const struct devmap_entry ixp435_devmap[] = { /* Physical/Virtual address for I/O space */ - { IXP425_IO_VBASE, IXP425_IO_HWBASE, IXP425_IO_SIZE, - VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + { IXP425_IO_VBASE, IXP425_IO_HWBASE, IXP425_IO_SIZE, }, - { IXP425_EXP_VBASE, IXP425_EXP_HWBASE, IXP425_EXP_SIZE, - VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + { IXP425_EXP_VBASE, IXP425_EXP_HWBASE, IXP425_EXP_SIZE, }, /* IXP425 PCI Configuration */ - { IXP425_PCI_VBASE, IXP425_PCI_HWBASE, IXP425_PCI_SIZE, - VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + { IXP425_PCI_VBASE, IXP425_PCI_HWBASE, IXP425_PCI_SIZE, }, /* DDRII Controller NB: mapped same place as IXP425 */ - { IXP425_MCU_VBASE, IXP435_MCU_HWBASE, IXP425_MCU_SIZE, - VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + { IXP425_MCU_VBASE, IXP435_MCU_HWBASE, IXP425_MCU_SIZE, }, /* PCI Memory Space */ - { IXP425_PCI_MEM_VBASE, IXP425_PCI_MEM_HWBASE, IXP425_PCI_MEM_SIZE, - VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + { IXP425_PCI_MEM_VBASE, IXP425_PCI_MEM_HWBASE, IXP425_PCI_MEM_SIZE, }, /* Q-Mgr Memory Space */ - { IXP425_QMGR_VBASE, IXP425_QMGR_HWBASE, IXP425_QMGR_SIZE, - VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + { IXP425_QMGR_VBASE, IXP425_QMGR_HWBASE, IXP425_QMGR_SIZE, }, /* CFI Flash on the Expansion Bus */ { IXP425_EXP_BUS_CS0_VBASE, IXP425_EXP_BUS_CS0_HWBASE, - IXP425_EXP_BUS_CS0_SIZE, VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + IXP425_EXP_BUS_CS0_SIZE, }, /* USB1 Memory Space */ - { IXP435_USB1_VBASE, IXP435_USB1_HWBASE, IXP435_USB1_SIZE, - VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + { IXP435_USB1_VBASE, IXP435_USB1_HWBASE, IXP435_USB1_SIZE, }, /* USB2 Memory Space */ - { IXP435_USB2_VBASE, IXP435_USB2_HWBASE, IXP435_USB2_SIZE, - VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + { IXP435_USB2_VBASE, IXP435_USB2_HWBASE, IXP435_USB2_SIZE, }, /* GPS Memory Space */ - { CAMBRIA_GPS_VBASE, CAMBRIA_GPS_HWBASE, CAMBRIA_GPS_SIZE, - VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + { CAMBRIA_GPS_VBASE, CAMBRIA_GPS_HWBASE, CAMBRIA_GPS_SIZE, }, /* RS485 Memory Space */ - { CAMBRIA_RS485_VBASE, CAMBRIA_RS485_HWBASE, CAMBRIA_RS485_SIZE, - VM_PROT_READ|VM_PROT_WRITE, PTE_DEVICE, }, + { CAMBRIA_RS485_VBASE, CAMBRIA_RS485_HWBASE, CAMBRIA_RS485_SIZE, }, { 0 } }; @@ -294,7 +280,7 @@ valloc_pages(irqstack, IRQ_STACK_SIZE); valloc_pages(abtstack, ABT_STACK_SIZE); valloc_pages(undstack, UND_STACK_SIZE); - valloc_pages(kernelstack, KSTACK_PAGES); + valloc_pages(kernelstack, kstack_pages); alloc_pages(minidataclean.pv_pa, 1); valloc_pages(msgbufpv, round_page(msgbufsize) / PAGE_SIZE); @@ -306,7 +292,7 @@ l1pagetable = kernel_l1pt.pv_va; /* Map the L2 pages tables in the L1 page table */ - pmap_link_l2pt(l1pagetable, ARM_VECTORS_HIGH & ~(0x00100000 - 1), + pmap_link_l2pt(l1pagetable, rounddown2(ARM_VECTORS_HIGH, 0x00100000), &kernel_pt_table[KERNEL_PT_SYS]); pmap_link_l2pt(l1pagetable, IXP425_IO_VBASE, &kernel_pt_table[KERNEL_PT_IO]); @@ -341,9 +327,9 @@ pmap_map_entry(l1pagetable, ARM_VECTORS_HIGH, systempage.pv_pa, VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); if (cpu_is_ixp43x()) - arm_devmap_bootstrap(l1pagetable, ixp435_devmap); + devmap_bootstrap(l1pagetable, ixp435_devmap); else - arm_devmap_bootstrap(l1pagetable, ixp425_devmap); + devmap_bootstrap(l1pagetable, ixp425_devmap); /* * Give the XScale global cache clean code an appropriately * sized chunk of unmapped VA space starting at 0xff000000 @@ -352,7 +338,7 @@ xscale_cache_clean_addr = 0xff000000U; cpu_domains((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT); - setttb(kernel_l1pt.pv_pa); + cpu_setttb(kernel_l1pt.pv_pa); cpu_tlb_flushID(); cpu_domains(DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)); @@ -369,7 +355,7 @@ /* * We must now clean the cache again.... * Cleaning may be done by reading new data to displace any - * dirty data in the cache. This will have happened in setttb() + * dirty data in the cache. This will have happened in cpu_setttb() * but since we are boot strapping the addresses used for the read * may have just been remapped and thus the cache could be out * of sync. A re-clean after the switch will cure this. @@ -377,7 +363,7 @@ * this problem will not occur after initarm(). */ cpu_idcache_wbinv_all(); - cpu_setup(""); + cpu_setup(); /* ready to setup the console (XXX move earlier if possible) */ cninit(); @@ -397,7 +383,7 @@ arm_vector_init(ARM_VECTORS_HIGH, ARM_VEC_ALL); pmap_curmaxkvaddr = afterkern + PAGE_SIZE; - vm_max_kernel_address = 0xd0000000; + vm_max_kernel_address = 0xe0000000; pmap_bootstrap(pmap_curmaxkvaddr, &kernel_l1pt); msgbufp = (void*)msgbufpv.pv_va; msgbufinit(msgbufp, msgbufsize); @@ -414,6 +400,10 @@ * Prepare the list of physical memory available to the vm subsystem. */ arm_physmem_hardware_region(PHYSADDR, memsize); + arm_physmem_exclude_region(freemem_pt, abp->abp_physaddr - + freemem_pt, EXFLAG_NOALLOC); + arm_physmem_exclude_region(freemempos, abp->abp_physaddr - 0x100000 - + freemempos, EXFLAG_NOALLOC); arm_physmem_exclude_region(abp->abp_physaddr, virtual_avail - KERNVIRTADDR, EXFLAG_NOALLOC); arm_physmem_init_kernel_globals(); Modified: trunk/sys/arm/xscale/ixp425/cambria_exp_space.c =================================================================== --- trunk/sys/arm/xscale/ixp425/cambria_exp_space.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/cambria_exp_space.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -37,7 +37,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/cambria_exp_space.c 278727 2015-02-13 22:32:02Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/cambria_exp_space.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include Modified: trunk/sys/arm/xscale/ixp425/cambria_fled.c =================================================================== --- trunk/sys/arm/xscale/ixp425/cambria_fled.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/cambria_fled.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -24,7 +24,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/cambria_fled.c 205705 2010-03-26 18:49:43Z rpaulo $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/cambria_fled.c 331722 2018-03-29 02:50:57Z eadler $"); /* * Cambria Front Panel LED sitting on the I2C bus. */ Modified: trunk/sys/arm/xscale/ixp425/cambria_gpio.c =================================================================== --- trunk/sys/arm/xscale/ixp425/cambria_gpio.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/cambria_gpio.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -33,7 +33,7 @@ * The Cambria PLD does not set the i2c ack bit after each write, if we used the * regular iicbus interface it would abort the xfer after the address byte * times out and not write our latch. To get around this we grab the iicbus and - * then do our own bit banging. This is a comprimise to changing all the iicbb + * then do our own bit banging. This is a compromise to changing all the iicbb * device methods to allow a flag to be passed down and is similir to how Linux * does it. * @@ -40,7 +40,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/cambria_gpio.c 278786 2015-02-14 21:16:19Z loos $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/cambria_gpio.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -57,6 +57,7 @@ #include #include +#include #include #include @@ -80,6 +81,7 @@ #define GPIO_PINS 5 struct cambria_gpio_softc { device_t sc_dev; + device_t sc_busdev; bus_space_tag_t sc_iot; bus_space_handle_t sc_gpio_ioh; struct mtx sc_mtx; @@ -120,6 +122,7 @@ /* * GPIO interface */ +static device_t cambria_gpio_get_bus(device_t); static int cambria_gpio_pin_max(device_t dev, int *maxpin); static int cambria_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); static int cambria_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t @@ -262,6 +265,16 @@ return (0); } +static device_t +cambria_gpio_get_bus(device_t dev) +{ + struct cambria_gpio_softc *sc; + + sc = device_get_softc(dev); + + return (sc->sc_busdev); +} + static int cambria_gpio_pin_max(device_t dev, int *maxpin) { @@ -439,10 +452,13 @@ cambria_gpio_pin_setflags(dev, pin, p->flags); } - device_add_child(dev, "gpioc", -1); - device_add_child(dev, "gpiobus", -1); + sc->sc_busdev = gpiobus_attach_bus(dev); + if (sc->sc_busdev == NULL) { + mtx_destroy(&sc->sc_mtx); + return (ENXIO); + } - return (bus_generic_attach(dev)); + return (0); } static int @@ -452,8 +468,7 @@ KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized")); - bus_generic_detach(dev); - + gpiobus_detach_bus(dev); mtx_destroy(&sc->sc_mtx); return(0); @@ -465,6 +480,7 @@ DEVMETHOD(device_detach, cambria_gpio_detach), /* GPIO protocol */ + DEVMETHOD(gpio_get_bus, cambria_gpio_get_bus), DEVMETHOD(gpio_pin_max, cambria_gpio_pin_max), DEVMETHOD(gpio_pin_getname, cambria_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, cambria_gpio_pin_getflags), @@ -477,16 +493,12 @@ }; static driver_t cambria_gpio_driver = { - "gpio_cambria", + "gpio", cambria_gpio_methods, sizeof(struct cambria_gpio_softc), }; static devclass_t cambria_gpio_devclass; -extern devclass_t gpiobus_devclass, gpioc_devclass; -extern driver_t gpiobus_driver, gpioc_driver; DRIVER_MODULE(gpio_cambria, iicbus, cambria_gpio_driver, cambria_gpio_devclass, 0, 0); -DRIVER_MODULE(gpiobus, gpio_cambria, gpiobus_driver, gpiobus_devclass, 0, 0); -DRIVER_MODULE(gpioc, gpio_cambria, gpioc_driver, gpioc_devclass, 0, 0); MODULE_VERSION(gpio_cambria, 1); MODULE_DEPEND(gpio_cambria, iicbus, 1, 1, 1); Modified: trunk/sys/arm/xscale/ixp425/cambria_led.c =================================================================== --- trunk/sys/arm/xscale/ixp425/cambria_led.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/cambria_led.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -24,7 +24,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/cambria_led.c 194015 2009-06-11 17:05:13Z avg $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/cambria_led.c 331722 2018-03-29 02:50:57Z eadler $"); /* * Gateworks Cambria Octal LED Latch driver. Modified: trunk/sys/arm/xscale/ixp425/files.avila =================================================================== --- trunk/sys/arm/xscale/ixp425/files.avila 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/files.avila 2020-03-06 16:44:57 UTC (rev 12394) @@ -1,4 +1,4 @@ -#$FreeBSD: stable/10/sys/arm/xscale/ixp425/files.avila 215142 2010-11-11 20:18:33Z thompsa $ +#$FreeBSD: stable/11/sys/arm/xscale/ixp425/files.avila 215142 2010-11-11 20:18:33Z thompsa $ arm/xscale/ixp425/avila_machdep.c standard arm/xscale/ixp425/avila_ata.c optional avila_ata arm/xscale/ixp425/avila_led.c optional avila_led Modified: trunk/sys/arm/xscale/ixp425/files.ixp425 =================================================================== --- trunk/sys/arm/xscale/ixp425/files.ixp425 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/files.ixp425 2020-03-06 16:44:57 UTC (rev 12394) @@ -1,6 +1,4 @@ -#$FreeBSD: stable/10/sys/arm/xscale/ixp425/files.ixp425 266311 2014-05-17 13:53:38Z ian $ -arm/arm/bus_space_generic.c standard -arm/arm/cpufunc_asm_xscale.S standard +#$FreeBSD: stable/11/sys/arm/xscale/ixp425/files.ixp425 291133 2015-11-21 15:30:08Z andrew $ arm/xscale/ixp425/ixp425.c standard arm/xscale/ixp425/ixp425_mem.c standard arm/xscale/ixp425/ixp425_space.c standard Modified: trunk/sys/arm/xscale/ixp425/if_npe.c =================================================================== --- trunk/sys/arm/xscale/ixp425/if_npe.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/if_npe.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -24,7 +24,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/if_npe.c 266406 2014-05-18 16:07:35Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/if_npe.c 331722 2018-03-29 02:50:57Z eadler $"); /* * Intel XScale NPE Ethernet driver. @@ -256,9 +256,8 @@ "IXP4XX NPE driver parameters"); static int npe_debug = 0; -SYSCTL_INT(_hw_npe, OID_AUTO, debug, CTLFLAG_RW, &npe_debug, +SYSCTL_INT(_hw_npe, OID_AUTO, debug, CTLFLAG_RWTUN, &npe_debug, 0, "IXP4XX NPE network interface debug msgs"); -TUNABLE_INT("hw.npe.debug", &npe_debug); #define DPRINTF(sc, fmt, ...) do { \ if (sc->sc_debug) device_printf(sc->sc_dev, fmt, __VA_ARGS__); \ } while (0) @@ -266,18 +265,15 @@ if (sc->sc_debug >= n) device_printf(sc->sc_dev, fmt, __VA_ARGS__);\ } while (0) static int npe_tickinterval = 3; /* npe_tick frequency (secs) */ -SYSCTL_INT(_hw_npe, OID_AUTO, tickinterval, CTLFLAG_RD, &npe_tickinterval, +SYSCTL_INT(_hw_npe, OID_AUTO, tickinterval, CTLFLAG_RDTUN, &npe_tickinterval, 0, "periodic work interval (secs)"); -TUNABLE_INT("hw.npe.tickinterval", &npe_tickinterval); static int npe_rxbuf = 64; /* # rx buffers to allocate */ -SYSCTL_INT(_hw_npe, OID_AUTO, rxbuf, CTLFLAG_RD, &npe_rxbuf, +SYSCTL_INT(_hw_npe, OID_AUTO, rxbuf, CTLFLAG_RDTUN, &npe_rxbuf, 0, "rx buffers allocated"); -TUNABLE_INT("hw.npe.rxbuf", &npe_rxbuf); static int npe_txbuf = 128; /* # tx buffers to allocate */ -SYSCTL_INT(_hw_npe, OID_AUTO, txbuf, CTLFLAG_RD, &npe_txbuf, +SYSCTL_INT(_hw_npe, OID_AUTO, txbuf, CTLFLAG_RDTUN, &npe_txbuf, 0, "tx buffers allocated"); -TUNABLE_INT("hw.npe.txbuf", &npe_txbuf); static int unit2npeid(int unit) @@ -290,7 +286,7 @@ }; /* XXX check feature register instead */ return (unit < 3 ? npeidmap[ - (cpu_id() & CPU_ID_CPU_MASK) == CPU_ID_IXP435][unit] : -1); + (cpu_ident() & CPU_ID_CPU_MASK) == CPU_ID_IXP435][unit] : -1); } static int @@ -686,7 +682,8 @@ /* MAC */ if (!override_addr(dev, "mac", &macbase)) macbase = npeconfig[sc->sc_npeid].macbase; - device_printf(sc->sc_dev, "MAC at 0x%x\n", macbase); + if (bootverbose) + device_printf(sc->sc_dev, "MAC at 0x%x\n", macbase); if (bus_space_map(sc->sc_iot, macbase, IXP425_REG_SIZE, 0, &sc->sc_ioh)) { device_printf(dev, "cannot map mac registers 0x%x:0x%x\n", macbase, IXP425_REG_SIZE); @@ -698,7 +695,8 @@ phy = npeconfig[sc->sc_npeid].phy; if (!override_addr(dev, "mii", &miibase)) miibase = npeconfig[sc->sc_npeid].miibase; - device_printf(sc->sc_dev, "MII at 0x%x\n", miibase); + if (bootverbose) + device_printf(sc->sc_dev, "MII at 0x%x\n", miibase); if (miibase != macbase) { /* * PHY is mapped through a different MAC, setup an @@ -909,20 +907,18 @@ be32toh(ns->RxOverrunDiscards) + be32toh(ns->RxUnderflowEntryDiscards); - ifp->if_oerrors += - be32toh(ns->dot3StatsInternalMacTransmitErrors) - + be32toh(ns->dot3StatsCarrierSenseErrors) - + be32toh(ns->TxVLANIdFilterDiscards) - ; - ifp->if_ierrors += be32toh(ns->dot3StatsFCSErrors) - + be32toh(ns->dot3StatsInternalMacReceiveErrors) - + be32toh(ns->RxOverrunDiscards) - + be32toh(ns->RxUnderflowEntryDiscards) - ; - ifp->if_collisions += - be32toh(ns->dot3StatsSingleCollisionFrames) - + be32toh(ns->dot3StatsMultipleCollisionFrames) - ; + if_inc_counter(ifp, IFCOUNTER_OERRORS, + be32toh(ns->dot3StatsInternalMacTransmitErrors) + + be32toh(ns->dot3StatsCarrierSenseErrors) + + be32toh(ns->TxVLANIdFilterDiscards)); + if_inc_counter(ifp, IFCOUNTER_IERRORS, + be32toh(ns->dot3StatsFCSErrors) + + be32toh(ns->dot3StatsInternalMacReceiveErrors) + + be32toh(ns->RxOverrunDiscards) + + be32toh(ns->RxUnderflowEntryDiscards)); + if_inc_counter(ifp, IFCOUNTER_COLLISIONS, + be32toh(ns->dot3StatsSingleCollisionFrames) + + be32toh(ns->dot3StatsMultipleCollisionFrames)); #undef NPEADD #undef MIBADD } @@ -1002,7 +998,7 @@ * We're no longer busy, so clear the busy flag and call the * start routine to xmit more packets. */ - ifp->if_opackets += td->count; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, td->count); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->npe_watchdog_timer = 0; npestart_locked(ifp); @@ -1141,7 +1137,7 @@ mrx->m_pkthdr.len = mrx->m_len; mrx->m_pkthdr.rcvif = ifp; - ifp->if_ipackets++; + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); ifp->if_input(ifp, mrx); rx_npkts++; } else { @@ -1470,7 +1466,7 @@ return; device_printf(sc->sc_dev, "watchdog timeout\n"); - sc->sc_ifp->if_oerrors++; + if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); npeinit_locked(sc); } Modified: trunk/sys/arm/xscale/ixp425/if_npereg.h =================================================================== --- trunk/sys/arm/xscale/ixp425/if_npereg.h 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/if_npereg.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -27,7 +27,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD: stable/10/sys/arm/xscale/ixp425/if_npereg.h 236987 2012-06-13 04:38:09Z imp $ + * $FreeBSD: stable/11/sys/arm/xscale/ixp425/if_npereg.h 331722 2018-03-29 02:50:57Z eadler $ */ /* Modified: trunk/sys/arm/xscale/ixp425/ixdp425_pci.c =================================================================== --- trunk/sys/arm/xscale/ixp425/ixdp425_pci.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixdp425_pci.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -34,7 +34,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/ixdp425_pci.c 229125 2011-12-31 15:53:34Z marius $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/ixdp425_pci.c 331722 2018-03-29 02:50:57Z eadler $"); #define _ARM32_BUS_DMA_PRIVATE #include Modified: trunk/sys/arm/xscale/ixp425/ixdp425reg.h =================================================================== --- trunk/sys/arm/xscale/ixp425/ixdp425reg.h 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixdp425reg.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -33,7 +33,7 @@ * SUCH DAMAGE. */ -/* $FreeBSD: stable/10/sys/arm/xscale/ixp425/ixdp425reg.h 164763 2006-11-30 06:30:01Z kevlo $ */ +/* $FreeBSD: stable/11/sys/arm/xscale/ixp425/ixdp425reg.h 331722 2018-03-29 02:50:57Z eadler $ */ #ifndef _IXDP425REG_H_ #define _IXDP425REG_H_ /* GPIOs */ Modified: trunk/sys/arm/xscale/ixp425/ixp425.c =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -35,7 +35,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425.c 278613 2015-02-12 03:50:33Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425.c 331722 2018-03-29 02:50:57Z eadler $"); #include "opt_ddb.h" @@ -474,7 +474,7 @@ }; int i; - for (i = 0; i < sizeof hwvtrans / sizeof *hwvtrans; i++) { + for (i = 0; i < nitems(hwvtrans); i++) { if (hwbase >= hwvtrans[i].hwbase && hwbase + size <= hwvtrans[i].hwbase + hwvtrans[i].size) return &hwvtrans[i]; @@ -497,7 +497,7 @@ static struct resource * ixp425_alloc_resource(device_t dev, device_t child, int type, int *rid, - u_long start, u_long end, u_long count, u_int flags) + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct ixp425_softc *sc = device_get_softc(dev); const struct hwvtrans *vtrans; @@ -534,7 +534,7 @@ (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" : ""); @@ -543,7 +543,7 @@ 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; } @@ -550,7 +550,7 @@ 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; } @@ -587,7 +587,7 @@ 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); } Modified: trunk/sys/arm/xscale/ixp425/ixp425_a4x_io.S =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425_a4x_io.S 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425_a4x_io.S 2020-03-06 16:44:57 UTC (rev 12394) @@ -44,7 +44,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425_a4x_io.S 275767 2014-12-14 16:28:53Z andrew $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425_a4x_io.S 275519 2014-12-05 19:04:08Z andrew $"); /* * bus_space I/O functions with offset*4 Modified: trunk/sys/arm/xscale/ixp425/ixp425_a4x_space.c =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425_a4x_space.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425_a4x_space.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -44,7 +44,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425_a4x_space.c 278727 2015-02-13 22:32:02Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425_a4x_space.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include Modified: trunk/sys/arm/xscale/ixp425/ixp425_iic.c =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425_iic.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425_iic.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -25,7 +25,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425_iic.c 236987 2012-06-13 04:38:09Z imp $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425_iic.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include Modified: trunk/sys/arm/xscale/ixp425/ixp425_intr.h =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425_intr.h 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425_intr.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -35,7 +35,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425_intr.h 186352 2008-12-20 03:26:09Z sam $ + * $FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425_intr.h 331722 2018-03-29 02:50:57Z eadler $ * */ @@ -47,7 +47,6 @@ #ifndef _LOCORE #include -#include #include Modified: trunk/sys/arm/xscale/ixp425/ixp425_mem.c =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425_mem.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425_mem.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -37,7 +37,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425_mem.c 266406 2014-05-18 16:07:35Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425_mem.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include Modified: trunk/sys/arm/xscale/ixp425/ixp425_npe.c =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425_npe.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425_npe.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -58,7 +58,7 @@ * SUCH DAMAGE. */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425_npe.c 249582 2013-04-17 11:40:10Z gabor $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425_npe.c 331722 2018-03-29 02:50:57Z eadler $"); /* * Intel XScale Network Processing Engine (NPE) support. @@ -93,8 +93,6 @@ #include #include -#include -#include #include #include #include @@ -182,9 +180,8 @@ } IxNpeDlNpeMgrStateInfoBlock; static int npe_debug = 0; -SYSCTL_INT(_debug, OID_AUTO, ixp425npe, CTLFLAG_RW, &npe_debug, +SYSCTL_INT(_debug, OID_AUTO, ixp425npe, CTLFLAG_RWTUN, &npe_debug, 0, "IXP4XX NPE debug msgs"); -TUNABLE_INT("debug.ixp425npe", &npe_debug); #define DPRINTF(dev, fmt, ...) do { \ if (npe_debug) device_printf(dev, fmt, __VA_ARGS__); \ } while (0) @@ -509,7 +506,7 @@ /* * If download was successful, store image Id in list of - * currently loaded images. If a critical error occured + * currently loaded images. If a critical error occurred * during download, record that the NPE has an invalid image */ mtx_lock(&sc->sc_mtx); @@ -866,7 +863,7 @@ while (npe_checkbits(sc, IX_NPEDL_REG_OFFSET_STAT, IX_NPEDL_MASK_STAT_IFNE)) { /* - * Step execution of the NPE intruction to read inFIFO using + * Step execution of the NPE instruction to read inFIFO using * the Debug Executing Context stack. */ error = npe_cpu_step(sc, IX_NPEDL_INSTR_RD_FIFO, 0, 0); @@ -1309,7 +1306,7 @@ ((regVal & IX_NPEDL_MASK_IMMED_INSTR_COPROC_DATA) << IX_NPEDL_DISPLACE_IMMED_INSTR_COPROC_DATA); - /* step execution of NPE intruction using Debug ECS */ + /* step execution of NPE instruction using Debug ECS */ error = npe_cpu_step(sc, npeInstruction, ctxtNum, IX_NPEDL_WR_INSTR_LDUR); } Modified: trunk/sys/arm/xscale/ixp425/ixp425_npereg.h =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425_npereg.h 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425_npereg.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -27,7 +27,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425_npereg.h 236987 2012-06-13 04:38:09Z imp $ + * $FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425_npereg.h 331722 2018-03-29 02:50:57Z eadler $ */ /*- @@ -126,7 +126,7 @@ /* * Reset value for Mailbox (MBST) register - * NOTE that if used, it should be complemented with an NPE intruction + * NOTE that if used, it should be complemented with an NPE instruction * to clear the Mailbox at the NPE side as well */ #define IX_NPEDL_REG_RESET_MBST 0x0000F0F0 Modified: trunk/sys/arm/xscale/ixp425/ixp425_npevar.h =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425_npevar.h 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425_npevar.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -22,7 +22,7 @@ * (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: stable/10/sys/arm/xscale/ixp425/ixp425_npevar.h 186420 2008-12-23 04:51:46Z sam $ + * $FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425_npevar.h 331722 2018-03-29 02:50:57Z eadler $ */ #ifndef _IXP425_NPEVAR_H_ Modified: trunk/sys/arm/xscale/ixp425/ixp425_pci.c =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425_pci.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425_pci.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -35,7 +35,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425_pci.c 278613 2015-02-12 03:50:33Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425_pci.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -137,14 +137,6 @@ NULL, NULL, &sc->sc_dmat)) panic("couldn't create the PCI dma tag !"); /* - * The PCI bus can only address 64MB. However, due to the way our - * implementation of busdma works, busdma can't tell if a device - * is a PCI device or not. So defaults to the PCI dma tag, which - * restrict the DMA'able memory to the first 64MB, and explicitely - * create less restrictive tags for non-PCI devices. - */ - arm_root_dma_tag = sc->sc_dmat; - /* * Initialize the bus space tags. */ ixp425_io_bs_init(&sc->sc_pci_iot, sc); @@ -278,7 +270,7 @@ static struct resource * ixppcib_alloc_resource(device_t bus, device_t child, int type, int *rid, - u_long start, u_long end, u_long count, u_int flags) + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct ixppcib_softc *sc = device_get_softc(bus); struct rman *rmanp; @@ -321,9 +313,12 @@ ixppcib_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { - struct ixppcib_softc *sc = device_get_softc(bus); + int error; + error = rman_activate_resource(r); + if (error) + return (error); switch (type) { case SYS_RES_IOPORT: rman_set_bustag(r, &sc->sc_pci_iot); @@ -336,7 +331,7 @@ break; } - return (rman_activate_resource(r)); + return (0); } static int @@ -359,6 +354,14 @@ return (ENXIO); } +static bus_dma_tag_t +ixppcib_get_dma_tag(device_t bus, device_t child) +{ + struct ixppcib_softc *sc = device_get_softc(bus); + + return (sc->sc_dmat); +} + static void ixppcib_conf_setup(struct ixppcib_softc *sc, int bus, int slot, int func, int reg) @@ -457,7 +460,7 @@ DEVMETHOD(bus_activate_resource, ixppcib_activate_resource), DEVMETHOD(bus_deactivate_resource, ixppcib_deactivate_resource), DEVMETHOD(bus_release_resource, ixppcib_release_resource), - /* DEVMETHOD(bus_get_dma_tag, ixppcib_get_dma_tag), */ + DEVMETHOD(bus_get_dma_tag, ixppcib_get_dma_tag), /* pcib interface */ DEVMETHOD(pcib_maxslots, ixppcib_maxslots), Modified: trunk/sys/arm/xscale/ixp425/ixp425_pci_asm.S =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425_pci_asm.S 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425_pci_asm.S 2020-03-06 16:44:57 UTC (rev 12394) @@ -35,7 +35,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425_pci_asm.S 275767 2014-12-14 16:28:53Z andrew $ + * $FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425_pci_asm.S 275519 2014-12-05 19:04:08Z andrew $ * */ Modified: trunk/sys/arm/xscale/ixp425/ixp425_pci_space.c =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425_pci_space.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425_pci_space.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -35,7 +35,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425_pci_space.c 278727 2015-02-13 22:32:02Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425_pci_space.c 331722 2018-03-29 02:50:57Z eadler $"); /* * bus_space PCI functions for ixp425 Modified: trunk/sys/arm/xscale/ixp425/ixp425_qmgr.c =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425_qmgr.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425_qmgr.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -58,7 +58,7 @@ * SUCH DAMAGE. */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425_qmgr.c 236987 2012-06-13 04:38:09Z imp $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425_qmgr.c 331722 2018-03-29 02:50:57Z eadler $"); /* * Intel XScale Queue Manager support. @@ -85,8 +85,6 @@ #include #include -#include -#include #include #include #include @@ -160,10 +158,9 @@ uint32_t aqmFreeSramAddress; /* SRAM free space */ }; -static int qmgr_debug = 0; -SYSCTL_INT(_debug, OID_AUTO, qmgr, CTLFLAG_RW, &qmgr_debug, +static int qmgr_debug; +SYSCTL_INT(_debug, OID_AUTO, qmgr, CTLFLAG_RWTUN, &qmgr_debug, 0, "IXP4XX Q-Manager debug msgs"); -TUNABLE_INT("debug.qmgr", &qmgr_debug); #define DPRINTF(dev, fmt, ...) do { \ if (qmgr_debug) printf(fmt, __VA_ARGS__); \ } while (0) @@ -357,7 +354,7 @@ if (cb == NULL) { /* Reset to dummy callback */ qi->cb = dummyCallback; - qi->cbarg = 0; + qi->cbarg = NULL; } else { qi->cb = cb; qi->cbarg = cbarg; @@ -423,7 +420,7 @@ return ENOSPC; } /* - * No overflow occured : someone is draining the queue + * No overflow occurred : someone is draining the queue * and the current counter needs to be * updated from the current number of entries in the queue */ Modified: trunk/sys/arm/xscale/ixp425/ixp425_qmgr.h =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425_qmgr.h 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425_qmgr.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -27,7 +27,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425_qmgr.h 236987 2012-06-13 04:38:09Z imp $ + * $FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425_qmgr.h 331722 2018-03-29 02:50:57Z eadler $ */ /*- Modified: trunk/sys/arm/xscale/ixp425/ixp425_space.c =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425_space.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425_space.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -35,7 +35,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425_space.c 278727 2015-02-13 22:32:02Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425_space.c 331722 2018-03-29 02:50:57Z eadler $"); /* * bus_space I/O functions for ixp425 Modified: trunk/sys/arm/xscale/ixp425/ixp425_timer.c =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425_timer.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425_timer.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -35,7 +35,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425_timer.c 278613 2015-02-12 03:50:33Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425_timer.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -50,7 +50,6 @@ #include #include #include -#include #include #include #include Modified: trunk/sys/arm/xscale/ixp425/ixp425_wdog.c =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425_wdog.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425_wdog.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -23,7 +23,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425_wdog.c 259329 2013-12-13 20:43:11Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425_wdog.c 331722 2018-03-29 02:50:57Z eadler $"); /* * IXP4XX Watchdog Timer Support. @@ -39,8 +39,6 @@ #include #include -#include -#include #include #include Modified: trunk/sys/arm/xscale/ixp425/ixp425reg.h =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425reg.h 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425reg.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425reg.h 261455 2014-02-04 03:36:42Z eadler $ + * $FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425reg.h 331722 2018-03-29 02:50:57Z eadler $ * */ Modified: trunk/sys/arm/xscale/ixp425/ixp425var.h =================================================================== --- trunk/sys/arm/xscale/ixp425/ixp425var.h 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/ixp425var.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -33,7 +33,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/xscale/ixp425/ixp425var.h 229125 2011-12-31 15:53:34Z marius $ + * $FreeBSD: stable/11/sys/arm/xscale/ixp425/ixp425var.h 331722 2018-03-29 02:50:57Z eadler $ * */ Modified: trunk/sys/arm/xscale/ixp425/std.avila =================================================================== --- trunk/sys/arm/xscale/ixp425/std.avila 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/std.avila 2020-03-06 16:44:57 UTC (rev 12394) @@ -1,4 +1,4 @@ -#$FreeBSD: stable/10/sys/arm/xscale/ixp425/std.avila 266110 2014-05-15 02:41:23Z ian $ +#$FreeBSD: stable/11/sys/arm/xscale/ixp425/std.avila 292591 2015-12-22 09:08:21Z andrew $ # # Gateworks GW23XX board configuration @@ -13,7 +13,6 @@ # be remapped away from 0. # options PHYSADDR=0x00000000 -options KERNPHYSADDR=0x00200000 makeoptions KERNPHYSADDR=0x00200000 options KERNVIRTADDR=0xc0200000 # Used in ldscript.arm makeoptions KERNVIRTADDR=0xc0200000 Modified: trunk/sys/arm/xscale/ixp425/std.ixp425 =================================================================== --- trunk/sys/arm/xscale/ixp425/std.ixp425 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/std.ixp425 2020-03-06 16:44:57 UTC (rev 12394) @@ -1,5 +1,5 @@ #XScale IXP425 generic configuration -#$FreeBSD: stable/10/sys/arm/xscale/ixp425/std.ixp425 239362 2012-08-18 05:48:19Z andrew $ +#$FreeBSD: stable/11/sys/arm/xscale/ixp425/std.ixp425 239362 2012-08-18 05:48:19Z andrew $ files "../xscale/ixp425/files.ixp425" include "../xscale/std.xscale-be" cpu CPU_XSCALE_IXP425 Modified: trunk/sys/arm/xscale/ixp425/std.ixp435 =================================================================== --- trunk/sys/arm/xscale/ixp425/std.ixp435 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/std.ixp435 2020-03-06 16:44:57 UTC (rev 12394) @@ -1,5 +1,5 @@ #XScale IXP435 generic configuration -#$FreeBSD: stable/10/sys/arm/xscale/ixp425/std.ixp435 239362 2012-08-18 05:48:19Z andrew $ +#$FreeBSD: stable/11/sys/arm/xscale/ixp425/std.ixp435 239362 2012-08-18 05:48:19Z andrew $ files "../xscale/ixp425/files.ixp425" include "../xscale/std.xscale-be" Modified: trunk/sys/arm/xscale/ixp425/uart_bus_ixp425.c =================================================================== --- trunk/sys/arm/xscale/ixp425/uart_bus_ixp425.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/uart_bus_ixp425.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -24,7 +24,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/uart_bus_ixp425.c 194668 2009-06-22 22:46:37Z sam $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/uart_bus_ixp425.c 340145 2018-11-04 23:28:56Z mmacy $"); #include #include @@ -79,5 +79,5 @@ if (bootverbose) device_printf(dev, "rclk %u\n", rclk); - return uart_bus_probe(dev, 0, rclk, 0, 0); + return uart_bus_probe(dev, 0, 0, rclk, 0, 0, 0); } Modified: trunk/sys/arm/xscale/ixp425/uart_cpu_ixp425.c =================================================================== --- trunk/sys/arm/xscale/ixp425/uart_cpu_ixp425.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/ixp425/uart_cpu_ixp425.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -26,7 +26,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/ixp425/uart_cpu_ixp425.c 170109 2007-05-29 18:10:42Z jhay $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/uart_cpu_ixp425.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include Modified: trunk/sys/arm/xscale/pxa/files.pxa =================================================================== --- trunk/sys/arm/xscale/pxa/files.pxa 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/pxa/files.pxa 2020-03-06 16:44:57 UTC (rev 12394) @@ -1,8 +1,5 @@ -# $FreeBSD: stable/10/sys/arm/xscale/pxa/files.pxa 266311 2014-05-17 13:53:38Z ian $ +# $FreeBSD: stable/11/sys/arm/xscale/pxa/files.pxa 291133 2015-11-21 15:30:08Z andrew $ -arm/arm/bus_space_generic.c standard -arm/arm/cpufunc_asm_xscale.S standard - arm/xscale/pxa/pxa_gpio.c standard arm/xscale/pxa/pxa_icu.c standard arm/xscale/pxa/pxa_machdep.c standard Modified: trunk/sys/arm/xscale/pxa/if_smc_smi.c =================================================================== --- trunk/sys/arm/xscale/pxa/if_smc_smi.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/pxa/if_smc_smi.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -26,7 +26,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/pxa/if_smc_smi.c 259342 2013-12-13 22:08:31Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/pxa/if_smc_smi.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include Modified: trunk/sys/arm/xscale/pxa/pxa_gpio.c =================================================================== --- trunk/sys/arm/xscale/pxa/pxa_gpio.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/pxa/pxa_gpio.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -24,7 +24,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/pxa/pxa_gpio.c 179595 2008-06-06 05:08:09Z benno $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/pxa/pxa_gpio.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -332,7 +332,7 @@ } int -pxa_gpio_get_next_irq() +pxa_gpio_get_next_irq(void) { struct pxa_gpio_softc *sc; int gpio; Modified: trunk/sys/arm/xscale/pxa/pxa_icu.c =================================================================== --- trunk/sys/arm/xscale/pxa/pxa_icu.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/pxa/pxa_icu.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -24,7 +24,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/pxa/pxa_icu.c 278613 2015-02-12 03:50:33Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/pxa/pxa_icu.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -172,7 +172,7 @@ } uint32_t -pxa_icu_get_icip() +pxa_icu_get_icip(void) { return (bus_space_read_4(pxa_icu_softc->pi_bst, @@ -188,7 +188,7 @@ } uint32_t -pxa_icu_get_icfp() +pxa_icu_get_icfp(void) { return (bus_space_read_4(pxa_icu_softc->pi_bst, @@ -204,7 +204,7 @@ } uint32_t -pxa_icu_get_icmr() +pxa_icu_get_icmr(void) { return (bus_space_read_4(pxa_icu_softc->pi_bst, @@ -220,7 +220,7 @@ } uint32_t -pxa_icu_get_iclr() +pxa_icu_get_iclr(void) { return (bus_space_read_4(pxa_icu_softc->pi_bst, @@ -236,7 +236,7 @@ } uint32_t -pxa_icu_get_icpr() +pxa_icu_get_icpr(void) { return (bus_space_read_4(pxa_icu_softc->pi_bst, @@ -244,7 +244,7 @@ } void -pxa_icu_idle_enable() +pxa_icu_idle_enable(void) { bus_space_write_4(pxa_icu_softc->pi_bst, @@ -252,7 +252,7 @@ } void -pxa_icu_idle_disable() +pxa_icu_idle_disable(void) { bus_space_write_4(pxa_icu_softc->pi_bst, Modified: trunk/sys/arm/xscale/pxa/pxa_machdep.c =================================================================== --- trunk/sys/arm/xscale/pxa/pxa_machdep.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/pxa/pxa_machdep.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -39,7 +39,7 @@ * * machdep.c * - * Machine dependant functions for kernel setup + * Machine dependent functions for kernel setup * * This file needs a lot of work. * @@ -47,9 +47,10 @@ */ #include "opt_ddb.h" +#include "opt_kstack_pages.h" #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/pxa/pxa_machdep.c 266386 2014-05-18 00:32:35Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/pxa/pxa_machdep.c 331722 2018-03-29 02:50:57Z eadler $"); #define _ARM32_BUS_DMA_PRIVATE #include @@ -73,6 +74,7 @@ #include #include #include +#include #include #include @@ -81,7 +83,6 @@ #include #include #include -#include #include #include #include @@ -120,7 +121,7 @@ uint32_t *, uint32_t *); /* Static device mappings. */ -static const struct arm_devmap_entry pxa_devmap[] = { +static const struct devmap_entry pxa_devmap[] = { /* * Map the on-board devices up into the KVA region so we don't muck * up user-space. @@ -129,10 +130,8 @@ PXA2X0_PERIPH_START + PXA2X0_PERIPH_OFFSET, PXA2X0_PERIPH_START, PXA250_PERIPH_END - PXA2X0_PERIPH_START, - VM_PROT_READ|VM_PROT_WRITE, - PTE_DEVICE, }, - { 0, 0, 0, 0, 0, } + { 0, 0, 0, } }; #define SDRAM_START 0xa0000000 @@ -206,7 +205,7 @@ valloc_pages(irqstack, IRQ_STACK_SIZE); valloc_pages(abtstack, ABT_STACK_SIZE); valloc_pages(undstack, UND_STACK_SIZE); - valloc_pages(kernelstack, KSTACK_PAGES); + valloc_pages(kernelstack, kstack_pages); alloc_pages(minidataclean.pv_pa, 1); valloc_pages(msgbufpv, round_page(msgbufsize) / PAGE_SIZE); /* @@ -224,8 +223,8 @@ l1pagetable = kernel_l1pt.pv_va; /* Map the L2 pages tables in the L1 page table */ - pmap_link_l2pt(l1pagetable, ARM_VECTORS_HIGH & ~(0x00100000 - 1), - &kernel_pt_table[KERNEL_PT_SYS]); + pmap_link_l2pt(l1pagetable, rounddown2(ARM_VECTORS_HIGH, 0x00100000), + &kernel_pt_table[KERNEL_PT_SYS]); #if 0 /* XXXBJR: What is this? Don't know if there's an analogue. */ pmap_link_l2pt(l1pagetable, IQ80321_IOPXS_VBASE, &kernel_pt_table[KERNEL_PT_IOPXS]); @@ -237,11 +236,10 @@ pmap_map_chunk(l1pagetable, KERNBASE + 0x100000, SDRAM_START + 0x100000, 0x100000, VM_PROT_READ|VM_PROT_WRITE, PTE_PAGETABLE); pmap_map_chunk(l1pagetable, KERNBASE + 0x200000, SDRAM_START + 0x200000, - (((uint32_t)(lastaddr) - KERNBASE - 0x200000) + L1_S_SIZE) & ~(L1_S_SIZE - 1), - VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); - freemem_after = ((int)lastaddr + PAGE_SIZE) & ~(PAGE_SIZE - 1); - afterkern = round_page(((vm_offset_t)lastaddr + L1_S_SIZE) & - ~(L1_S_SIZE - 1)); + rounddown2(((uint32_t)(lastaddr) - KERNBASE - 0x200000) + L1_S_SIZE, L1_S_SIZE), + VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + freemem_after = rounddown2((int)lastaddr + PAGE_SIZE, PAGE_SIZE); + afterkern = round_page(rounddown2((vm_offset_t)lastaddr + L1_S_SIZE, L1_S_SIZE)); for (i = 0; i < KERNEL_PT_AFKERNEL_NUM; i++) { pmap_link_l2pt(l1pagetable, afterkern + i * 0x00100000, &kernel_pt_table[KERNEL_PT_AFKERNEL + i]); @@ -257,7 +255,7 @@ /* Map the vector page. */ pmap_map_entry(l1pagetable, ARM_VECTORS_HIGH, systempage.pv_pa, VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); - arm_devmap_bootstrap(l1pagetable, pxa_devmap); + devmap_bootstrap(l1pagetable, pxa_devmap); /* * Give the XScale global cache clean code an appropriately @@ -267,7 +265,7 @@ xscale_cache_clean_addr = 0xff000000U; cpu_domains((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT); - setttb(kernel_l1pt.pv_pa); + cpu_setttb(kernel_l1pt.pv_pa); cpu_tlb_flushID(); cpu_domains(DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)); @@ -284,7 +282,7 @@ /* * We must now clean the cache again.... * Cleaning may be done by reading new data to displace any - * dirty data in the cache. This will have happened in setttb() + * dirty data in the cache. This will have happened in cpu_setttb() * but since we are boot strapping the addresses used for the read * may have just been remapped and thus the cache could be out * of sync. A re-clean after the switch will cure this. @@ -292,7 +290,7 @@ * this problem will not occur after initarm(). */ cpu_idcache_wbinv_all(); - cpu_setup(""); + cpu_setup(); /* * Sort out bus_space for on-board devices. @@ -316,7 +314,7 @@ arm_vector_init(ARM_VECTORS_HIGH, ARM_VEC_ALL); pmap_curmaxkvaddr = afterkern + PAGE_SIZE; - vm_max_kernel_address = 0xd0000000; + vm_max_kernel_address = 0xe0000000; pmap_bootstrap(pmap_curmaxkvaddr, &kernel_l1pt); msgbufp = (void*)msgbufpv.pv_va; msgbufinit(msgbufp, msgbufsize); @@ -336,6 +334,10 @@ if (memsize[j] > 0) arm_physmem_hardware_region(memstart[j], memsize[j]); } + arm_physmem_exclude_region(freemem_pt, abp->abp_physaddr - + freemem_pt, EXFLAG_NOALLOC); + arm_physmem_exclude_region(freemempos, abp->abp_physaddr - 0x100000 - + freemempos, EXFLAG_NOALLOC); arm_physmem_exclude_region(abp->abp_physaddr, virtual_avail - KERNVIRTADDR, EXFLAG_NOALLOC); arm_physmem_init_kernel_globals(); Modified: trunk/sys/arm/xscale/pxa/pxa_obio.c =================================================================== --- trunk/sys/arm/xscale/pxa/pxa_obio.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/pxa/pxa_obio.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -24,7 +24,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/pxa/pxa_obio.c 265999 2014-05-14 01:35:43Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/pxa/pxa_obio.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -51,7 +51,7 @@ static struct resource_list * pxa_get_resource_list(device_t, device_t); static struct resource * pxa_alloc_resource(device_t, device_t, int, - int *, u_long, u_long, u_long, u_int); + int *, rman_res_t, rman_res_t, rman_res_t, u_int); static int pxa_release_resource(device_t, device_t, int, int, struct resource *); static int pxa_activate_resource(device_t, device_t, @@ -58,7 +58,7 @@ int, int, struct resource *); static struct resource * pxa_alloc_gpio_irq(device_t, device_t, int, - int *, u_long, u_long, u_long, u_int); + int *, rman_res_t, rman_res_t, rman_res_t, u_int); struct obio_device { const char *od_name; @@ -160,9 +160,9 @@ 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); @@ -225,7 +225,7 @@ static struct resource * pxa_alloc_resource(device_t dev, device_t child, int type, int *rid, - u_long start, u_long end, u_long count, u_int flags) + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct obio_softc *sc; struct obio_device *od; @@ -352,7 +352,7 @@ static struct resource * pxa_alloc_gpio_irq(device_t dev, device_t child, int type, int *rid, - u_long start, u_long end, u_long count, u_int flags) + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct obio_softc *sc; struct obio_device *od; @@ -391,7 +391,7 @@ } 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); Modified: trunk/sys/arm/xscale/pxa/pxa_smi.c =================================================================== --- trunk/sys/arm/xscale/pxa/pxa_smi.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/pxa/pxa_smi.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -24,7 +24,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/pxa/pxa_smi.c 241885 2012-10-22 13:06:09Z eadler $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/pxa/pxa_smi.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -71,7 +71,7 @@ static int pxa_smi_read_ivar(device_t, device_t, int, uintptr_t *); static struct resource * pxa_smi_alloc_resource(device_t, device_t, - int, int *, u_long, u_long, u_long, u_int); + int, int *, rman_res_t, rman_res_t, rman_res_t, u_int); static int pxa_smi_release_resource(device_t, device_t, int, int, struct resource *); static int pxa_smi_activate_resource(device_t, device_t, @@ -145,9 +145,9 @@ 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); @@ -177,7 +177,7 @@ static struct resource * pxa_smi_alloc_resource(device_t dev, device_t child, int type, int *rid, - u_long start, u_long end, u_long count, u_int flags) + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct pxa_smi_softc *sc; struct smi_ivars *smid; Modified: trunk/sys/arm/xscale/pxa/pxa_space.c =================================================================== --- trunk/sys/arm/xscale/pxa/pxa_space.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/pxa/pxa_space.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -41,7 +41,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/pxa/pxa_space.c 278727 2015-02-13 22:32:02Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/pxa/pxa_space.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -177,7 +177,7 @@ bus_space_tag_t obio_tag = NULL; void -pxa_obio_tag_init() +pxa_obio_tag_init(void) { bcopy(&_base_tag, &_obio_tag, sizeof(struct bus_space)); Modified: trunk/sys/arm/xscale/pxa/pxa_timer.c =================================================================== --- trunk/sys/arm/xscale/pxa/pxa_timer.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/pxa/pxa_timer.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -24,7 +24,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/pxa/pxa_timer.c 278613 2015-02-12 03:50:33Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/pxa/pxa_timer.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -236,7 +236,7 @@ } uint32_t -pxa_timer_get_oscr() +pxa_timer_get_oscr(void) { return (bus_space_read_4(timer_softc->pt_bst, @@ -252,7 +252,7 @@ } uint32_t -pxa_timer_get_ossr() +pxa_timer_get_ossr(void) { return (bus_space_read_4(timer_softc->pt_bst, @@ -268,7 +268,7 @@ } void -pxa_timer_watchdog_enable() +pxa_timer_watchdog_enable(void) { bus_space_write_4(timer_softc->pt_bst, @@ -276,7 +276,7 @@ } void -pxa_timer_watchdog_disable() +pxa_timer_watchdog_disable(void) { bus_space_write_4(timer_softc->pt_bst, Modified: trunk/sys/arm/xscale/pxa/pxareg.h =================================================================== --- trunk/sys/arm/xscale/pxa/pxareg.h 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/pxa/pxareg.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -33,7 +33,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/xscale/pxa/pxareg.h 266311 2014-05-17 13:53:38Z ian $ + * $FreeBSD: stable/11/sys/arm/xscale/pxa/pxareg.h 331722 2018-03-29 02:50:57Z eadler $ */ Modified: trunk/sys/arm/xscale/pxa/pxavar.h =================================================================== --- trunk/sys/arm/xscale/pxa/pxavar.h 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/pxa/pxavar.h 2020-03-06 16:44:57 UTC (rev 12394) @@ -35,7 +35,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/xscale/pxa/pxavar.h 179595 2008-06-06 05:08:09Z benno $ + * $FreeBSD: stable/11/sys/arm/xscale/pxa/pxavar.h 331722 2018-03-29 02:50:57Z eadler $ * */ Modified: trunk/sys/arm/xscale/pxa/std.pxa =================================================================== --- trunk/sys/arm/xscale/pxa/std.pxa 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/pxa/std.pxa 2020-03-06 16:44:57 UTC (rev 12394) @@ -1,5 +1,5 @@ # XScale PXA generic configuration -# $FreeBSD: stable/10/sys/arm/xscale/pxa/std.pxa 266175 2014-05-15 19:09:31Z ian $ +# $FreeBSD: stable/11/sys/arm/xscale/pxa/std.pxa 261642 2014-02-08 22:21:38Z ian $ files "../xscale/pxa/files.pxa" include "../xscale/std.xscale" makeoptions KERNPHYSADDR=0xa0200000 Modified: trunk/sys/arm/xscale/pxa/uart_bus_pxa.c =================================================================== --- trunk/sys/arm/xscale/pxa/uart_bus_pxa.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/pxa/uart_bus_pxa.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -24,7 +24,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/pxa/uart_bus_pxa.c 234004 2012-04-07 23:47:08Z stas $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/pxa/uart_bus_pxa.c 340145 2018-11-04 23:28:56Z mmacy $"); #include #include @@ -98,7 +98,7 @@ sc = device_get_softc(dev); sc->sc_class = &uart_ns8250_class; - return(uart_bus_probe(dev, 2, PXA2X0_COM_FREQ, 0, 0)); + return(uart_bus_probe(dev, 2, 0, PXA2X0_COM_FREQ, 0, 0, 0)); } DRIVER_MODULE(uart, pxa, uart_pxa_driver, uart_devclass, 0, 0); Modified: trunk/sys/arm/xscale/pxa/uart_cpu_pxa.c =================================================================== --- trunk/sys/arm/xscale/pxa/uart_cpu_pxa.c 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/pxa/uart_cpu_pxa.c 2020-03-06 16:44:57 UTC (rev 12394) @@ -26,7 +26,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/pxa/uart_cpu_pxa.c 179595 2008-06-06 05:08:09Z benno $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/pxa/uart_cpu_pxa.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include Modified: trunk/sys/arm/xscale/std.xscale =================================================================== --- trunk/sys/arm/xscale/std.xscale 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/std.xscale 2020-03-06 16:44:57 UTC (rev 12394) @@ -1,3 +1,3 @@ -# $FreeBSD: stable/10/sys/arm/xscale/std.xscale 239362 2012-08-18 05:48:19Z andrew $ +# $FreeBSD: stable/11/sys/arm/xscale/std.xscale 239362 2012-08-18 05:48:19Z andrew $ options ARM_CACHE_LOCK_ENABLE options NO_EVENTTIMERS Modified: trunk/sys/arm/xscale/std.xscale-be =================================================================== --- trunk/sys/arm/xscale/std.xscale-be 2020-02-16 22:48:41 UTC (rev 12393) +++ trunk/sys/arm/xscale/std.xscale-be 2020-03-06 16:44:57 UTC (rev 12394) @@ -1,5 +1,5 @@ #Big-Endian XScale generic configuration -#$FreeBSD: stable/10/sys/arm/xscale/std.xscale-be 239362 2012-08-18 05:48:19Z andrew $ +#$FreeBSD: stable/11/sys/arm/xscale/std.xscale-be 239362 2012-08-18 05:48:19Z andrew $ include "../xscale/std.xscale" machine arm armeb From laffer1 at midnightbsd.org Fri Mar 6 12:05:09 2020 From: laffer1 at midnightbsd.org (laffer1 at midnightbsd.org) Date: Fri, 6 Mar 2020 12:05:09 -0500 (EST) Subject: [Midnightbsd-cvs] src [12395] trunk/sys/arm/samsung/exynos: sync with freebsd 11 Message-ID: <202003061705.026H59J7013499@stargazer.midnightbsd.org> Revision: 12395 http://svnweb.midnightbsd.org/src/?rev=12395 Author: laffer1 Date: 2020-03-06 12:05:09 -0500 (Fri, 06 Mar 2020) Log Message: ----------- sync with freebsd 11 Modified Paths: -------------- trunk/sys/arm/samsung/exynos/chrome_ec.c trunk/sys/arm/samsung/exynos/chrome_ec.h trunk/sys/arm/samsung/exynos/chrome_kb.c trunk/sys/arm/samsung/exynos/chrome_kb.h trunk/sys/arm/samsung/exynos/exynos5_combiner.c trunk/sys/arm/samsung/exynos/exynos5_combiner.h trunk/sys/arm/samsung/exynos/exynos5_common.c trunk/sys/arm/samsung/exynos/exynos5_common.h trunk/sys/arm/samsung/exynos/exynos5_ehci.c trunk/sys/arm/samsung/exynos/exynos5_fimd.c trunk/sys/arm/samsung/exynos/exynos5_i2c.c trunk/sys/arm/samsung/exynos/exynos5_machdep.c trunk/sys/arm/samsung/exynos/exynos5_mct.c trunk/sys/arm/samsung/exynos/exynos5_mp.c trunk/sys/arm/samsung/exynos/exynos5_pad.c trunk/sys/arm/samsung/exynos/exynos5_pad.h trunk/sys/arm/samsung/exynos/exynos_uart.c trunk/sys/arm/samsung/exynos/exynos_uart.h trunk/sys/arm/samsung/exynos/files.exynos5 trunk/sys/arm/samsung/exynos/std.exynos5250 trunk/sys/arm/samsung/exynos/std.exynos5420 Added Paths: ----------- trunk/sys/arm/samsung/exynos/chrome_ec_spi.c trunk/sys/arm/samsung/exynos/exynos5_pmu.c trunk/sys/arm/samsung/exynos/exynos5_pmu.h trunk/sys/arm/samsung/exynos/exynos5_spi.c trunk/sys/arm/samsung/exynos/exynos5_usb_phy.c trunk/sys/arm/samsung/exynos/exynos5_xhci.c Modified: trunk/sys/arm/samsung/exynos/chrome_ec.c =================================================================== --- trunk/sys/arm/samsung/exynos/chrome_ec.c 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/chrome_ec.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -30,7 +30,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/samsung/exynos/chrome_ec.c 266341 2014-05-17 19:37:04Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/chrome_ec.c 297793 2016-04-10 23:07:00Z pfg $"); #include #include @@ -44,13 +44,11 @@ #include #include -#include #include #include #include #include -#include #include #include @@ -61,12 +59,11 @@ #include -/* TODO: export to DTS */ -#define OUR_GPIO 177 -#define EC_GPIO 168 - struct ec_softc { device_t dev; + int have_arbitrator; + pcell_t our_gpio; + pcell_t ec_gpio; }; struct ec_softc *ec_sc; @@ -83,17 +80,24 @@ device_t gpio_dev; int status; + if (sc->our_gpio == 0 || sc->ec_gpio == 0) { + device_printf(sc->dev, "i2c arbitrator is not configured\n"); + return (1); + } + gpio_dev = devclass_get_device(devclass_find("gpio"), 0); - if (gpio_dev == NULL) { + if (gpio_dev == NULL) { device_printf(sc->dev, "cant find gpio_dev\n"); return (1); } /* Say we want the bus */ - GPIO_PIN_SET(gpio_dev, OUR_GPIO, GPIO_PIN_LOW); + GPIO_PIN_SET(gpio_dev, sc->our_gpio, GPIO_PIN_LOW); + /* TODO: insert a delay to allow EC to react. */ + /* Check EC decision */ - GPIO_PIN_GET(gpio_dev, EC_GPIO, &status); + GPIO_PIN_GET(gpio_dev, sc->ec_gpio, &status); if (status == 1) { /* Okay. We have bus */ @@ -109,13 +113,18 @@ { device_t gpio_dev; + if (sc->our_gpio == 0 || sc->ec_gpio == 0) { + device_printf(sc->dev, "i2c arbitrator is not configured\n"); + return (1); + } + gpio_dev = devclass_get_device(devclass_find("gpio"), 0); - if (gpio_dev == NULL) { + if (gpio_dev == NULL) { device_printf(sc->dev, "cant find gpio_dev\n"); return (1); } - GPIO_PIN_SET(gpio_dev, OUR_GPIO, GPIO_PIN_HIGH); + GPIO_PIN_SET(gpio_dev, sc->our_gpio, GPIO_PIN_HIGH); return (0); } @@ -155,7 +164,7 @@ int i; msg_dout = malloc(dout_len + 4, M_DEVBUF, M_NOWAIT); - msg_dinp = malloc(dinp_len + 4, M_DEVBUF, M_NOWAIT); + msg_dinp = malloc(dinp_len + 3, M_DEVBUF, M_NOWAIT); if (ec_sc == NULL) return (-1); @@ -168,13 +177,13 @@ for (i = 0; i < dout_len; i++) { msg_dout[i + 3] = dout[i]; - }; + } fill_checksum(msg_dout, dout_len + 3); struct iic_msg msgs[] = { { 0x1e, IIC_M_WR, dout_len + 4, msg_dout, }, - { 0x1e, IIC_M_RD, dinp_len + 4, msg_dinp, }, + { 0x1e, IIC_M_RD, dinp_len + 3, msg_dinp, }, }; ret = iicbus_transfer(sc->dev, msgs, 2); @@ -186,8 +195,8 @@ } for (i = 0; i < dinp_len; i++) { - dinp[i] = msg_dinp[i + 3]; - }; + dinp[i] = msg_dinp[i + 2]; + } free(msg_dout, M_DEVBUF); free(msg_dinp, M_DEVBUF); @@ -204,12 +213,34 @@ data_in[2] = 0x20; data_in[3] = 0x10; - ec_command(EC_CMD_MKBP_STATE, data_in, 4, + ec_command(EC_CMD_HELLO, data_in, 4, data_out, 4); return (0); } +static void +configure_i2c_arbitrator(struct ec_softc *sc) +{ + phandle_t arbitrator; + + /* TODO: look for compatible entry instead of hard-coded path */ + arbitrator = OF_finddevice("/i2c-arbitrator"); + if (arbitrator > 0 && + OF_hasprop(arbitrator, "freebsd,our-gpio") && + OF_hasprop(arbitrator, "freebsd,ec-gpio")) { + sc->have_arbitrator = 1; + OF_getencprop(arbitrator, "freebsd,our-gpio", + &sc->our_gpio, sizeof(sc->our_gpio)); + OF_getencprop(arbitrator, "freebsd,ec-gpio", + &sc->ec_gpio, sizeof(sc->ec_gpio)); + } else { + sc->have_arbitrator = 0; + sc->our_gpio = 0; + sc->ec_gpio = 0; + } +} + static int ec_attach(device_t dev) { @@ -220,6 +251,8 @@ ec_sc = sc; + configure_i2c_arbitrator(sc); + /* * Claim the bus. * @@ -228,7 +261,7 @@ * */ - if (bus_claim(sc) != 0) { + if (sc->have_arbitrator && bus_claim(sc) != 0) { return (ENXIO); } @@ -242,7 +275,9 @@ sc = device_get_softc(dev); - bus_release(sc); + if (sc->have_arbitrator) { + bus_release(sc); + } return (0); } Modified: trunk/sys/arm/samsung/exynos/chrome_ec.h =================================================================== --- trunk/sys/arm/samsung/exynos/chrome_ec.h 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/chrome_ec.h 2020-03-06 17:05:09 UTC (rev 12395) @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/samsung/exynos/chrome_ec.h 266341 2014-05-17 19:37:04Z ian $ + * $FreeBSD: stable/11/sys/arm/samsung/exynos/chrome_ec.h 269369 2014-08-01 06:20:25Z br $ */ #define EC_CMD_HELLO 0x01 @@ -31,6 +31,8 @@ #define EC_CMD_GET_VERSION 0x02 #define EC_CMD_MKBP_STATE 0x60 #define EC_CMD_VERSION0 0xdc +#define EC_CMD_RESEND_RESPONSE 0xdb +#define EC_CMD_GET_COMMS_STATUS 0x09 int ec_command(uint8_t cmd, uint8_t *dout, uint8_t dout_len, uint8_t *dinp, uint8_t dinp_len); Added: trunk/sys/arm/samsung/exynos/chrome_ec_spi.c =================================================================== --- trunk/sys/arm/samsung/exynos/chrome_ec_spi.c (rev 0) +++ trunk/sys/arm/samsung/exynos/chrome_ec_spi.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -0,0 +1,229 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2014 Ruslan Bukin
+ * 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 EXPREC OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNEC 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 BUSINEC 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. + */ + +/* + * Samsung Chromebook Embedded Controller (EC) + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/chrome_ec_spi.c 297793 2016-04-10 23:07:00Z pfg $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "spibus_if.h" +#include "gpio_if.h" + +#include + +struct ec_softc { + device_t dev; + device_t dev_gpio; +}; + +struct ec_softc *ec_sc; + +#define EC_SPI_CS 200 + +static int +assert_cs(struct ec_softc *sc, int enable) +{ + /* Get the GPIO device */ + sc->dev_gpio = devclass_get_device(devclass_find("gpio"), 0); + if (sc->dev_gpio == NULL) { + device_printf(sc->dev, "Error: failed to get the GPIO dev\n"); + return (1); + } + + GPIO_PIN_SETFLAGS(sc->dev_gpio, EC_SPI_CS, GPIO_PIN_OUTPUT); + + if (enable) { + GPIO_PIN_SET(sc->dev_gpio, EC_SPI_CS, GPIO_PIN_LOW); + } else { + GPIO_PIN_SET(sc->dev_gpio, EC_SPI_CS, GPIO_PIN_HIGH); + } + + return (0); +} + +static int +ec_probe(device_t dev) +{ + + device_set_desc(dev, "Chromebook Embedded Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +fill_checksum(uint8_t *data_out, int len) +{ + int res; + int i; + + res = 0; + for (i = 0; i < len; i++) { + res += data_out[i]; + } + + data_out[len] = (res & 0xff); + + return (0); +} + +int +ec_command(uint8_t cmd, uint8_t *dout, uint8_t dout_len, + uint8_t *dinp, uint8_t dinp_len) +{ + struct spi_command spi_cmd; + struct ec_softc *sc; + uint8_t *msg_dout; + uint8_t *msg_dinp; + int ret; + int i; + + memset(&spi_cmd, 0, sizeof(spi_cmd)); + + msg_dout = malloc(dout_len + 4, M_DEVBUF, M_NOWAIT | M_ZERO); + msg_dinp = malloc(dinp_len + 4, M_DEVBUF, M_NOWAIT | M_ZERO); + + spi_cmd.tx_cmd = msg_dout; + spi_cmd.rx_cmd = msg_dinp; + + if (ec_sc == NULL) + return (-1); + + sc = ec_sc; + + msg_dout[0] = EC_CMD_VERSION0; + msg_dout[1] = cmd; + msg_dout[2] = dout_len; + + for (i = 0; i < dout_len; i++) { + msg_dout[i + 3] = dout[i]; + } + + fill_checksum(msg_dout, dout_len + 3); + + assert_cs(sc, 1); + spi_cmd.rx_cmd_sz = spi_cmd.tx_cmd_sz = dout_len + 4; + ret = SPIBUS_TRANSFER(device_get_parent(sc->dev), sc->dev, &spi_cmd); + + /* Wait 0xec */ + for (i = 0; i < 1000; i++) { + DELAY(10); + msg_dout[0] = 0xff; + spi_cmd.rx_cmd_sz = spi_cmd.tx_cmd_sz = 1; + SPIBUS_TRANSFER(device_get_parent(sc->dev), sc->dev, &spi_cmd); + if (msg_dinp[0] == 0xec) + break; + } + + /* Get the rest */ + for (i = 0; i < (dout_len + 4); i++) + msg_dout[i] = 0xff; + spi_cmd.rx_cmd_sz = spi_cmd.tx_cmd_sz = dout_len + 4 - 1; + ret = SPIBUS_TRANSFER(device_get_parent(sc->dev), sc->dev, &spi_cmd); + assert_cs(sc, 0); + + if (ret != 0) { + device_printf(sc->dev, "spibus_transfer returned %d\n", ret); + free(msg_dout, M_DEVBUF); + free(msg_dinp, M_DEVBUF); + return (-1); + } + + for (i = 0; i < dinp_len; i++) { + dinp[i] = msg_dinp[i + 2]; + } + + free(msg_dout, M_DEVBUF); + free(msg_dinp, M_DEVBUF); + + return (0); +} + +static int +ec_attach(device_t dev) +{ + struct ec_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + ec_sc = sc; + + return (0); +} + +static int +ec_detach(device_t dev) +{ + struct ec_softc *sc; + + sc = device_get_softc(dev); + + return (0); +} + +static device_method_t ec_methods[] = { + DEVMETHOD(device_probe, ec_probe), + DEVMETHOD(device_attach, ec_attach), + DEVMETHOD(device_detach, ec_detach), + { 0, 0 } +}; + +static driver_t ec_driver = { + "chrome_ec", + ec_methods, + sizeof(struct ec_softc), +}; + +static devclass_t ec_devclass; + +DRIVER_MODULE(chrome_ec, spibus, ec_driver, ec_devclass, 0, 0); +MODULE_VERSION(chrome_ec, 1); +MODULE_DEPEND(chrome_ec, spibus, 1, 1, 1); Property changes on: trunk/sys/arm/samsung/exynos/chrome_ec_spi.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Modified: trunk/sys/arm/samsung/exynos/chrome_kb.c =================================================================== --- trunk/sys/arm/samsung/exynos/chrome_kb.c 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/chrome_kb.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -30,7 +30,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/samsung/exynos/chrome_kb.c 266352 2014-05-17 20:52:10Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/chrome_kb.c 356020 2019-12-22 19:06:45Z kevans $"); #include #include @@ -47,7 +47,6 @@ #include #include -#include #include #include #include @@ -54,11 +53,9 @@ #include #include -#include #include #include -#include #include #include @@ -106,9 +103,6 @@ #define CKB_FLAG_POLLING 0x2 #define KBD_DRIVER_NAME "ckbd" -/* TODO: take interrupt from DTS */ -#define KB_GPIO_INT 146 - struct ckb_softc { keyboard_t sc_kbd; keymap_t sc_keymap; @@ -131,8 +125,11 @@ int flag; int rows; int cols; + int gpio; device_t dev; + device_t gpio_dev; struct thread *sc_poll_thread; + uint16_t *keymap; uint8_t *scan_local; uint8_t *scan; @@ -199,7 +196,7 @@ ckb_intr(keyboard_t *kbd, void *arg) { - return (0); + return (0); } /* lock the access to the keyboard, not used */ @@ -207,7 +204,7 @@ ckb_lock(keyboard_t *kbd, int lock) { - return (1); + return (1); } /* clear the internal state of the keyboard */ @@ -258,12 +255,12 @@ if (sc->sc_flags & CKB_FLAG_POLLING) { return (1); - }; + } for (i = 0; i < sc->cols; i++) if (sc->scan_local[i] != sc->scan[i]) { return (1); - }; + } if (sc->sc_repeating) return (1); @@ -309,20 +306,33 @@ return (0); } -int scantokey(int i, int j); - -int -scantokey(int i, int j) +static uint16_t +keymap_read(struct ckb_softc *sc, int col, int row) { - int k; - for (k = 0; k < KEYMAP_LEN; k++) - if ((keymap[k].col == i) && (keymap[k].row == j)) - return (keymap[k].key); + KASSERT(sc->keymap != NULL, ("keymap_read: no keymap")); + if (col >= 0 && col < sc->cols && + row >= 0 && row < sc->rows) { + return sc->keymap[row * sc->cols + col]; + } return (0); } +static int +keymap_write(struct ckb_softc *sc, int col, int row, uint16_t key) +{ + + KASSERT(sc->keymap != NULL, ("keymap_write: no keymap")); + if (col >= 0 && col < sc->cols && + row >= 0 && row < sc->rows) { + sc->keymap[row * sc->cols + col] = key; + return (0); + } + + return (-1); +} + /* read char from the keyboard */ static uint32_t ckb_read_char_locked(keyboard_t *kbd, int wait) @@ -332,6 +342,7 @@ uint16_t key; int oldbit; int newbit; + int status; sc = kbd->kb_data; @@ -345,11 +356,25 @@ callout_reset(&sc->sc_repeat_callout, hz / 10, ckb_repeat, sc); return (sc->sc_repeat_key); - }; + } if (sc->sc_flags & CKB_FLAG_POLLING) { - /* TODO */ - }; + for (;;) { + GPIO_PIN_GET(sc->gpio_dev, sc->gpio, &status); + if (status == 0) { + if (ec_command(EC_CMD_MKBP_STATE, sc->scan, + sc->cols, + sc->scan, sc->cols)) { + return (NOKEY); + } + break; + } + if (!wait) { + return (NOKEY); + } + DELAY(1000); + } + } for (i = 0; i < sc->cols; i++) { for (j = 0; j < sc->rows; j++) { @@ -359,10 +384,10 @@ if (oldbit == newbit) continue; - key = scantokey(i,j); + key = keymap_read(sc, i, j); if (key == 0) { continue; - }; + } if (newbit > 0) { /* key pressed */ @@ -638,9 +663,7 @@ .clear_state = &ckb_clear_state, .get_state = &ckb_get_state, .set_state = &ckb_set_state, - .get_fkeystr = &genkbd_get_fkeystr, .poll = &ckb_poll, - .diag = &genkbd_diag, }; static int @@ -652,29 +675,111 @@ KEYBOARD_DRIVER(ckbd, ckbdsw, dummy_kbd_configure); +/* + * Parses 'keymap' into sc->keymap. + * Requires sc->cols and sc->rows to be set. + */ static int +parse_keymap(struct ckb_softc *sc, pcell_t *keymap, size_t len) +{ + int i; + + sc->keymap = malloc(sc->cols * sc->rows * sizeof(sc->keymap[0]), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (sc->keymap == NULL) { + return (ENOMEM); + } + + for (i = 0; i < len; i++) { + /* + * Return value is ignored, we just write whatever fits into + * specified number of rows and columns and silently ignore + * everything else. + * Keymap entries follow this format: 0xRRCCKKKK + * RR - row number, CC - column number, KKKK - key code + */ + keymap_write(sc, (keymap[i] >> 16) & 0xff, + (keymap[i] >> 24) & 0xff, + keymap[i] & 0xffff); + } + + return (0); +} + +/* Allocates a new array for keymap and returns it in 'keymap'. */ +static int +read_keymap(phandle_t node, const char *prop, pcell_t **keymap, size_t *len) +{ + + if ((*len = OF_getproplen(node, prop)) <= 0) { + return (ENXIO); + } + if ((*keymap = malloc(*len, M_DEVBUF, M_NOWAIT)) == NULL) { + return (ENOMEM); + } + if (OF_getencprop(node, prop, *keymap, *len) != *len) { + return (ENXIO); + } + return (0); +} + +static int parse_dts(struct ckb_softc *sc) { phandle_t node; pcell_t dts_value; - int len; + pcell_t *keymap; + int len, ret; + const char *keymap_prop = NULL; if ((node = ofw_bus_get_node(sc->dev)) == -1) return (ENXIO); - if ((len = OF_getproplen(node, "keypad,num-rows")) <= 0) + if ((len = OF_getproplen(node, "google,key-rows")) <= 0) return (ENXIO); - OF_getprop(node, "keypad,num-rows", &dts_value, len); - sc->rows = fdt32_to_cpu(dts_value); + OF_getencprop(node, "google,key-rows", &dts_value, len); + sc->rows = dts_value; - if ((len = OF_getproplen(node, "keypad,num-columns")) <= 0) + if ((len = OF_getproplen(node, "google,key-columns")) <= 0) return (ENXIO); - OF_getprop(node, "keypad,num-columns", &dts_value, len); - sc->cols = fdt32_to_cpu(dts_value); + OF_getencprop(node, "google,key-columns", &dts_value, len); + sc->cols = dts_value; - if ((sc->rows == 0) || (sc->cols == 0)) + if ((len = OF_getproplen(node, "freebsd,intr-gpio")) <= 0) return (ENXIO); + OF_getencprop(node, "freebsd,intr-gpio", &dts_value, len); + sc->gpio = dts_value; + if (OF_hasprop(node, "freebsd,keymap")) { + keymap_prop = "freebsd,keymap"; + device_printf(sc->dev, "using FreeBSD-specific keymap from FDT\n"); + } else if (OF_hasprop(node, "linux,keymap")) { + keymap_prop = "linux,keymap"; + device_printf(sc->dev, "using Linux keymap from FDT\n"); + } else { + device_printf(sc->dev, "using built-in keymap\n"); + } + + if (keymap_prop != NULL) { + if ((ret = read_keymap(node, keymap_prop, &keymap, &len))) { + device_printf(sc->dev, + "failed to read keymap from FDT: %d\n", ret); + return (ret); + } + ret = parse_keymap(sc, keymap, len); + free(keymap, M_DEVBUF); + if (ret) { + return (ret); + } + } else { + if ((ret = parse_keymap(sc, default_keymap, KEYMAP_LEN))) { + return (ret); + } + } + + if ((sc->rows == 0) || (sc->cols == 0) || (sc->gpio == 0)) + return (ENXIO); + return (0); } @@ -707,17 +812,23 @@ sc = device_get_softc(dev); sc->dev = dev; + sc->keymap = NULL; if ((error = parse_dts(sc)) != 0) return error; + sc->gpio_dev = devclass_get_device(devclass_find("gpio"), 0); + if (sc->gpio_dev == NULL) { + device_printf(sc->dev, "Can't find gpio device.\n"); + return (ENXIO); + } + #if 0 device_printf(sc->dev, "Keyboard matrix [%dx%d]\n", sc->cols, sc->rows); #endif - /* TODO: take interrupt from DTS */ - pad_setup_intr(KB_GPIO_INT, ckb_ec_intr, sc); + pad_setup_intr(sc->gpio, ckb_ec_intr, sc); kbd = &sc->sc_kbd; rid = 0; @@ -728,7 +839,7 @@ for (i = 0; i < sc->cols; i++) { sc->scan_local[i] = 0; sc->scan[i] = 0; - }; + } kbd_init_struct(kbd, KBD_DRIVER_NAME, KB_OTHER, device_get_unit(dev), 0, 0, 0); @@ -753,7 +864,7 @@ if (kbd_register(kbd) < 0) { return (ENXIO); - }; + } KBD_CONFIG_DONE(kbd); return (0); @@ -766,7 +877,8 @@ if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (ofw_bus_is_compatible(dev, "google,cros-ec-keyb")) { + if (ofw_bus_is_compatible(dev, "google,cros-ec-keyb") || + ofw_bus_is_compatible(dev, "google,mkbp-keyb")) { device_set_desc(dev, "Chrome EC Keyboard"); return (BUS_PROBE_DEFAULT); } @@ -774,9 +886,24 @@ return (ENXIO); } +static int +chrome_kb_detach(device_t dev) +{ + struct ckb_softc *sc; + + sc = device_get_softc(dev); + + if (sc->keymap != NULL) { + free(sc->keymap, M_DEVBUF); + } + + return 0; +} + static device_method_t chrome_kb_methods[] = { DEVMETHOD(device_probe, chrome_kb_probe), DEVMETHOD(device_attach, chrome_kb_attach), + DEVMETHOD(device_detach, chrome_kb_detach), { 0, 0 } }; Modified: trunk/sys/arm/samsung/exynos/chrome_kb.h =================================================================== --- trunk/sys/arm/samsung/exynos/chrome_kb.h 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/chrome_kb.h 2020-03-06 17:05:09 UTC (rev 12395) @@ -24,100 +24,96 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/samsung/exynos/chrome_kb.h 266341 2014-05-17 19:37:04Z ian $ + * $FreeBSD: stable/11/sys/arm/samsung/exynos/chrome_kb.h 266872 2014-05-30 06:45:50Z br $ */ +#include + void ckb_ec_intr(void *); -struct key { - uint8_t row; - uint8_t col; - uint8_t key; -}; - #define KEYMAP_LEN 75 -struct key keymap[KEYMAP_LEN] = { - { 0x00, 0x01, 0x7d }, /* lmeta */ - { 0x00, 0x02, 0x3b }, /* F1 */ - { 0x00, 0x03, 0x30 }, /* B */ - { 0x00, 0x04, 0x44 }, /* F10 */ - { 0x00, 0x06, 0x31 }, /* N */ - { 0x00, 0x08, 0x0d }, /* = */ - { 0x00, 0x0a, 0x64 }, /* ralt */ +pcell_t default_keymap[KEYMAP_LEN] = { + 0x0001007d, /* lmeta */ + 0x0002003b, /* F1 */ + 0x00030030, /* B */ + 0x00040044, /* F10 */ + 0x00060031, /* N */ + 0x0008000d, /* = */ + 0x000a0064, /* ralt */ - { 0x01, 0x01, 0x01 }, /* escape */ - { 0x01, 0x02, 0x3e }, /* F4 */ - { 0x01, 0x03, 0x22 }, /* G */ - { 0x01, 0x04, 0x41 }, /* F7 */ - { 0x01, 0x06, 0x23 }, /* H */ - { 0x01, 0x08, 0x28 }, /* ' */ - { 0x01, 0x09, 0x43 }, /* F9 */ - { 0x01, 0x0b, 0x0e }, /* backspace */ + 0x01010001, /* escape */ + 0x0102003e, /* F4 */ + 0x01030022, /* G */ + 0x01040041, /* F7 */ + 0x01060023, /* H */ + 0x01080028, /* ' */ + 0x01090043, /* F9 */ + 0x010b000e, /* backspace */ - { 0x02, 0x00, 0x1d }, /* lctrl */ - { 0x02, 0x01, 0x0f }, /* tab */ - { 0x02, 0x02, 0x3d }, /* F3 */ - { 0x02, 0x03, 0x14 }, /* t */ - { 0x02, 0x04, 0x40 }, /* F6 */ - { 0x02, 0x05, 0x1b }, /* ] */ - { 0x02, 0x06, 0x15 }, /* y */ - { 0x02, 0x07, 0x56 }, /* 102nd */ - { 0x02, 0x08, 0x1a }, /* [ */ - { 0x02, 0x09, 0x42 }, /* F8 */ + 0x0200001d, /* lctrl */ + 0x0201000f, /* tab */ + 0x0202003d, /* F3 */ + 0x02030014, /* t */ + 0x02040040, /* F6 */ + 0x0205001b, /* ] */ + 0x02060015, /* y */ + 0x02070056, /* 102nd */ + 0x0208001a, /* [ */ + 0x02090042, /* F8 */ - { 0x03, 0x01, 0x29 }, /* grave */ - { 0x03, 0x02, 0x3c }, /* F2 */ - { 0x03, 0x03, 0x06 }, /* 5 */ - { 0x03, 0x04, 0x3f }, /* F5 */ - { 0x03, 0x06, 0x07 }, /* 6 */ - { 0x03, 0x08, 0x0c }, /* - */ - { 0x03, 0x0b, 0x2b }, /* \ */ + 0x03010029, /* grave */ + 0x0302003c, /* F2 */ + 0x03030006, /* 5 */ + 0x0304003f, /* F5 */ + 0x03060007, /* 6 */ + 0x0308000c, /* - */ + 0x030b002b, /* \ */ - { 0x04, 0x00, 0x61 }, /* rctrl */ - { 0x04, 0x01, 0x1e }, /* a */ - { 0x04, 0x02, 0x20 }, /* d */ - { 0x04, 0x03, 0x21 }, /* f */ - { 0x04, 0x04, 0x1f }, /* s */ - { 0x04, 0x05, 0x25 }, /* k */ - { 0x04, 0x06, 0x24 }, /* j */ - { 0x04, 0x08, 0x27 }, /* ; */ - { 0x04, 0x09, 0x26 }, /* l */ - { 0x04, 0x0a, 0x2b }, /* \ */ - { 0x04, 0x0b, 0x1c }, /* enter */ + 0x04000061, /* rctrl */ + 0x0401001e, /* a */ + 0x04020020, /* d */ + 0x04030021, /* f */ + 0x0404001f, /* s */ + 0x04050025, /* k */ + 0x04060024, /* j */ + 0x04080027, /* ; */ + 0x04090026, /* l */ + 0x040a002b, /* \ */ + 0x040b001c, /* enter */ - { 0x05, 0x01, 0x2c }, /* z */ - { 0x05, 0x02, 0x2e }, /* c */ - { 0x05, 0x03, 0x2f }, /* v */ - { 0x05, 0x04, 0x2d }, /* x */ - { 0x05, 0x05, 0x33 }, /* , */ - { 0x05, 0x06, 0x32 }, /* m */ - { 0x05, 0x07, 0x2a }, /* lsh */ - { 0x05, 0x08, 0x35 }, /* / */ - { 0x05, 0x09, 0x34 }, /* . */ - { 0x05, 0x0B, 0x39 }, /* space */ + 0x0501002c, /* z */ + 0x0502002e, /* c */ + 0x0503002f, /* v */ + 0x0504002d, /* x */ + 0x05050033, /* , */ + 0x05060032, /* m */ + 0x0507002a, /* lsh */ + 0x05080035, /* / */ + 0x05090034, /* . */ + 0x050B0039, /* space */ - { 0x06, 0x01, 0x02 }, /* 1 */ - { 0x06, 0x02, 0x04 }, /* 3 */ - { 0x06, 0x03, 0x05 }, /* 4 */ - { 0x06, 0x04, 0x03 }, /* 2 */ - { 0x06, 0x05, 0x09 }, /* 8 */ - { 0x06, 0x06, 0x08 }, /* 7 */ - { 0x06, 0x08, 0x0b }, /* 0 */ - { 0x06, 0x09, 0x0a }, /* 9 */ - { 0x06, 0x0a, 0x38 }, /* lalt */ - { 0x06, 0x0b, 0x64 }, /* down */ - { 0x06, 0x0c, 0x62 }, /* right */ + 0x06010002, /* 1 */ + 0x06020004, /* 3 */ + 0x06030005, /* 4 */ + 0x06040003, /* 2 */ + 0x06050009, /* 8 */ + 0x06060008, /* 7 */ + 0x0608000b, /* 0 */ + 0x0609000a, /* 9 */ + 0x060a0038, /* lalt */ + 0x060b0064, /* down */ + 0x060c0062, /* right */ - { 0x07, 0x01, 0x10 }, /* q */ - { 0x07, 0x02, 0x12 }, /* e */ - { 0x07, 0x03, 0x13 }, /* r */ - { 0x07, 0x04, 0x11 }, /* w */ - { 0x07, 0x05, 0x17 }, /* i */ - { 0x07, 0x06, 0x16 }, /* u */ - { 0x07, 0x07, 0x36 }, /* rsh */ - { 0x07, 0x08, 0x19 }, /* p */ - { 0x07, 0x09, 0x18 }, /* o */ - { 0x07, 0x0b, 0x5F }, /* up */ - { 0x07, 0x0c, 0x61 }, /* left */ + 0x07010010, /* q */ + 0x07020012, /* e */ + 0x07030013, /* r */ + 0x07040011, /* w */ + 0x07050017, /* i */ + 0x07060016, /* u */ + 0x07070036, /* rsh */ + 0x07080019, /* p */ + 0x07090018, /* o */ + 0x070b005F, /* up */ + 0x070c0061, /* left */ }; Modified: trunk/sys/arm/samsung/exynos/exynos5_combiner.c =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_combiner.c 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/exynos5_combiner.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -29,9 +29,11 @@ * Samsung Exynos 5 Interrupt Combiner * Chapter 7, Exynos 5 Dual User's Manual Public Rev 1.00 */ - +#ifdef USB_GLOBAL_INCLUDE_FILE +#include USB_GLOBAL_INCLUDE_FILE +#else #include -__FBSDID("$FreeBSD: stable/10/sys/arm/samsung/exynos/exynos5_combiner.c 266341 2014-05-17 19:37:04Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_combiner.c 351675 2019-09-02 00:29:16Z emaste $"); #include #include @@ -44,21 +46,19 @@ #include #include -#include #include #include #include #include -#include #include #include +#endif #include #include #define NGRP 32 -#define ITABLE_LEN 24 #define IESR(n) (0x10 * n + 0x0) /* Interrupt enable set */ #define IECR(n) (0x10 * n + 0x4) /* Interrupt enable clear */ @@ -119,7 +119,7 @@ char *source_name; }; -static struct combiner_entry interrupt_table[ITABLE_LEN] = { +static struct combiner_entry interrupt_table[] = { { 63, 1, "EINT[15]" }, { 63, 0, "EINT[14]" }, { 62, 1, "EINT[13]" }, @@ -144,8 +144,116 @@ { 55, 4, "MCT_G1" }, { 55, 3, "MCT_G0" }, { 55, 0, "EINT[0]" }, + { 54, 7, "CPU_nCNTVIRQ[1]" }, + { 54, 6, "CPU_nCTIIRQ[1]" }, + { 54, 5, "CPU_nCNTPSIRQ[1]" }, + { 54, 4, "CPU_nPMUIRQ[1]" }, + { 54, 3, "CPU_nCNTPNSIRQ[1]" }, + { 54, 2, "CPU_PARITYFAILSCU[1]" }, + { 54, 1, "CPU_nCNTHPIRQ[1]" }, + { 54, 0, "PARITYFAIL[1]" }, + { 53, 1, "CPU_nIRQ[1]" }, + { 52, 0, "CPU_nIRQ[0]" }, + { 51, 7, "CPU_nRAMERRIRQ" }, + { 51, 6, "CPU_nAXIERRIRQ" }, + { 51, 4, "INT_COMB_ISP_GIC" }, + { 51, 3, "INT_COMB_IOP_GIC" }, + { 51, 2, "CCI_nERRORIRQ" }, + { 51, 1, "INT_COMB_ARMISP_GIC" }, + { 51, 0, "INT_COMB_ARMIOP_GIC" }, + { 50, 7, "DISP1[3]" }, + { 50, 6, "DISP1[2]" }, + { 50, 5, "DISP1[1]" }, + { 50, 4, "DISP1[0]" }, + { 49, 3, "SSCM_PULSE_IRQ_C2CIF[1]" }, + { 49, 2, "SSCM_PULSE_IRQ_C2CIF[0]" }, + { 49, 1, "SSCM_IRQ_C2CIF[1]" }, + { 49, 0, "SSCM_IRQ_C2CIF[0]" }, + { 48, 3, "PEREV_M1_CDREX" }, + { 48, 2, "PEREV_M0_CDREX" }, + { 48, 1, "PEREV_A1_CDREX" }, + { 48, 0, "PEREV_A0_CDREX" }, + { 47, 3, "MDMA0_ABORT" }, + /* 46 is fully reserved */ + { 45, 1, "MDMA1_ABORT" }, + /* 44 is fully reserved */ + { 43, 7, "SYSMMU_DRCISP[1]" }, + { 43, 6, "SYSMMU_DRCISP[0]" }, + { 43, 1, "SYSMMU_ODC[1]" }, + { 43, 0, "SYSMMU_ODC[0]" }, + { 42, 7, "SYSMMU_ISP[1]" }, + { 42, 6, "SYSMMU_ISP[0]" }, + { 42, 5, "SYSMMU_DIS0[1]" }, + { 42, 4, "SYSMMU_DIS0[0]" }, + { 42, 3, "DP1" }, + { 41, 5, "SYSMMU_DIS1[1]" }, + { 41, 4, "SYSMMU_DIS1[0]" }, + { 40, 6, "SYSMMU_MFCL[1]" }, + { 40, 5, "SYSMMU_MFCL[0]" }, + { 39, 5, "SYSMMU_TV_M0[1]" }, + { 39, 4, "SYSMMU_TV_M0[0]" }, + { 39, 3, "SYSMMU_MDMA1[1]" }, + { 39, 2, "SYSMMU_MDMA1[0]" }, + { 39, 1, "SYSMMU_MDMA0[1]" }, + { 39, 0, "SYSMMU_MDMA0[0]" }, + { 38, 7, "SYSMMU_SSS[1]" }, + { 38, 6, "SYSMMU_SSS[0]" }, + { 38, 5, "SYSMMU_RTIC[1]" }, + { 38, 4, "SYSMMU_RTIC[0]" }, + { 38, 3, "SYSMMU_MFCR[1]" }, + { 38, 2, "SYSMMU_MFCR[0]" }, + { 38, 1, "SYSMMU_ARM[1]" }, + { 38, 0, "SYSMMU_ARM[0]" }, + { 37, 7, "SYSMMU_3DNR[1]" }, + { 37, 6, "SYSMMU_3DNR[0]" }, + { 37, 5, "SYSMMU_MCUISP[1]" }, + { 37, 4, "SYSMMU_MCUISP[0]" }, + { 37, 3, "SYSMMU_SCALERCISP[1]" }, + { 37, 2, "SYSMMU_SCALERCISP[0]" }, + { 37, 1, "SYSMMU_FDISP[1]" }, + { 37, 0, "SYSMMU_FDISP[0]" }, + { 36, 7, "MCUIOP_CTIIRQ" }, + { 36, 6, "MCUIOP_PMUIRQ" }, + { 36, 5, "MCUISP_CTIIRQ" }, + { 36, 4, "MCUISP_PMUIRQ" }, + { 36, 3, "SYSMMU_JPEGX[1]" }, + { 36, 2, "SYSMMU_JPEGX[0]" }, + { 36, 1, "SYSMMU_ROTATOR[1]" }, + { 36, 0, "SYSMMU_ROTATOR[0]" }, + { 35, 7, "SYSMMU_SCALERPISP[1]" }, + { 35, 6, "SYSMMU_SCALERPISP[0]" }, + { 35, 5, "SYSMMU_FIMC_LITE0[1]" }, + { 35, 4, "SYSMMU_FIMC_LITE0[0]" }, + { 35, 3, "SYSMMU_DISP1_M0[1]" }, + { 35, 2, "SYSMMU_DISP1_M0[0]" }, + { 35, 1, "SYSMMU_FIMC_LITE2[1]" }, + { 35, 0, "SYSMMU_FIMC_LITE2[0]" }, + { 34, 7, "SYSMMU_GSCL3[1]" }, + { 34, 6, "SYSMMU_GSCL3[0]" }, + { 34, 5, "SYSMMU_GSCL2[1]" }, + { 34, 4, "SYSMMU_GSCL2[0]" }, + { 34, 3, "SYSMMU_GSCL1[1]" }, + { 34, 2, "SYSMMU_GSCL1[0]" }, + { 34, 1, "SYSMMU_GSCL0[1]" }, + { 34, 0, "SYSMMU_GSCL0[0]" }, + { 33, 7, "CPU_nCNTVIRQ[0]" }, + { 33, 6, "CPU_nCNTPSIRQ[0]" }, + { 33, 5, "CPU_nCNTPSNIRQ[0]" }, + { 33, 4, "CPU_nCNTHPIRQ[0]" }, + { 33, 3, "CPU_nCTIIRQ[0]" }, + { 33, 2, "CPU_nPMUIRQ[0]" }, + { 33, 1, "CPU_PARITYFAILSCU[0]" }, + { 33, 0, "CPU_PARITYFAIL0" }, + { 32, 7, "TZASC_XR1BXW" }, + { 32, 6, "TZASC_XR1BXR" }, + { 32, 5, "TZASC_XLBXW" }, + { 32, 4, "TZASC_XLBXR" }, + { 32, 3, "TZASC_DRBXW" }, + { 32, 2, "TZASC_DRBXR" }, + { 32, 1, "TZASC_CBXW" }, + { 32, 0, "TZASC_CBXR" }, - /* TODO: add groups 54-32 */ + { -1, -1, NULL }, }; struct combined_intr { @@ -207,13 +315,13 @@ sc = combiner_sc; if (sc == NULL) { - device_printf(sc->dev, "Error: combiner is not attached\n"); + printf("%s: error: combiner is not attached\n", __func__); return; } entry = NULL; - for (i = 0; i < ITABLE_LEN; i++) { + for (i = 0; i < NGRP && interrupt_table[i].bit != -1; i++) { if (strcmp(interrupt_table[i].source_name, source_name) == 0) { entry = &interrupt_table[i]; } @@ -247,6 +355,9 @@ combiner_probe(device_t dev) { + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + if (!ofw_bus_is_compatible(dev, "exynos,combiner")) return (ENXIO); Modified: trunk/sys/arm/samsung/exynos/exynos5_combiner.h =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_combiner.h 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/exynos5_combiner.h 2020-03-06 17:05:09 UTC (rev 12395) @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/samsung/exynos/exynos5_combiner.h 266341 2014-05-17 19:37:04Z ian $ + * $FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_combiner.h 263936 2014-03-30 15:22:36Z br $ */ void combiner_setup_intr(char *source_name, void (*ih)(void *), void *ih_user); Modified: trunk/sys/arm/samsung/exynos/exynos5_common.c =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_common.c 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/exynos5_common.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -26,7 +26,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/samsung/exynos/exynos5_common.c 266277 2014-05-17 00:53:12Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_common.c 314506 2017-03-01 19:55:04Z ian $"); #include #include @@ -50,10 +50,7 @@ while (1); } -struct fdt_fixup_entry fdt_fixup_table[] = { - { NULL, NULL } -}; - +#ifndef INTRNG static int fdt_pic_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, int *pol) @@ -72,3 +69,4 @@ &fdt_pic_decode_ic, NULL }; +#endif Modified: trunk/sys/arm/samsung/exynos/exynos5_common.h =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_common.h 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/exynos5_common.h 2020-03-06 17:05:09 UTC (rev 12395) @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/samsung/exynos/exynos5_common.h 266332 2014-05-17 17:54:38Z ian $ + * $FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_common.h 263426 2014-03-20 17:07:14Z br $ */ #define READ4(_sc, _reg) \ Modified: trunk/sys/arm/samsung/exynos/exynos5_ehci.c =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_ehci.c 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/exynos5_ehci.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -1,6 +1,6 @@ /* $MidnightBSD$ */ /*- - * Copyright (c) 2013 Ruslan Bukin
+ * Copyright (c) 2013-2014 Ruslan Bukin
* All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,8 +25,11 @@ * SUCH DAMAGE. */ +#ifdef USB_GLOBAL_INCLUDE_FILE +#include USB_GLOBAL_INCLUDE_FILE +#else #include -__FBSDID("$FreeBSD: stable/10/sys/arm/samsung/exynos/exynos5_ehci.c 278278 2015-02-05 20:03:02Z hselasky $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_ehci.c 346524 2019-04-22 04:56:41Z ian $"); #include "opt_bus.h" @@ -51,14 +54,16 @@ #include #include -#include - #include #include +#include +#include + #include "gpio_if.h" #include "opt_platform.h" +#endif /* GPIO control */ #define GPIO_OUTPUT 1 @@ -65,11 +70,6 @@ #define GPIO_INPUT 0 #define PIN_USB 161 -/* PWR control */ -#define EXYNOS5_PWR_USBHOST_PHY 0x708 -#define PHY_POWER_ON 1 -#define PHY_POWER_OFF 0 - /* SYSREG */ #define EXYNOS5_SYSREG_USB2_PHY 0x0 #define USB2_MODE_HOST 0x1 @@ -92,12 +92,10 @@ struct exynos_ehci_softc { device_t dev; ehci_softc_t base; - struct resource *res[5]; + struct resource *res[4]; bus_space_tag_t host_bst; - bus_space_tag_t pwr_bst; bus_space_tag_t sysreg_bst; bus_space_handle_t host_bsh; - bus_space_handle_t pwr_bsh; bus_space_handle_t sysreg_bsh; }; @@ -106,7 +104,6 @@ { 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_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; @@ -130,13 +127,13 @@ static driver_t ehci_driver = { "ehci", ehci_methods, - sizeof(ehci_softc_t) + sizeof(struct exynos_ehci_softc) }; static devclass_t ehci_devclass; -DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); -MODULE_DEPEND(ehci, usb, 1, 1, 1); +DRIVER_MODULE(exynos_ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); +MODULE_DEPEND(exynos_ehci, usb, 1, 1, 1); /* * Public methods @@ -181,9 +178,41 @@ } static int +reset_hsic_hub(struct exynos_ehci_softc *esc, phandle_t hub) +{ + device_t gpio_dev; + pcell_t pin; + + /* TODO: check that hub is compatible with "smsc,usb3503" */ + if (!OF_hasprop(hub, "freebsd,reset-gpio")) { + return (1); + } + + if (OF_getencprop(hub, "freebsd,reset-gpio", &pin, sizeof(pin)) < 0) { + device_printf(esc->dev, + "failed to decode reset GPIO pin number for HSIC hub\n"); + return (1); + } + + /* Get the GPIO device, we need this to give power to USB */ + gpio_dev = devclass_get_device(devclass_find("gpio"), 0); + if (gpio_dev == NULL) { + device_printf(esc->dev, "Cant find gpio device\n"); + return (1); + } + + GPIO_PIN_SET(gpio_dev, pin, GPIO_PIN_LOW); + DELAY(100); + GPIO_PIN_SET(gpio_dev, pin, GPIO_PIN_HIGH); + + return (0); +} + +static int phy_init(struct exynos_ehci_softc *esc) { int reg; + phandle_t hub; gpio_ctrl(esc, GPIO_INPUT, 1); @@ -192,8 +221,7 @@ EXYNOS5_SYSREG_USB2_PHY, USB2_MODE_HOST); /* Power ON phy */ - bus_space_write_4(esc->pwr_bst, esc->pwr_bsh, - EXYNOS5_PWR_USBHOST_PHY, PHY_POWER_ON); + usb2_phy_power_on(); reg = bus_space_read_4(esc->host_bst, esc->host_bsh, 0x0); reg &= ~(HOST_CTRL_CLK_MASK | @@ -213,6 +241,10 @@ reg &= ~(HOST_CTRL_RESET_LINK); bus_space_write_4(esc->host_bst, esc->host_bsh, 0x0, reg); + if ((hub = OF_finddevice("/hsichub")) != 0) { + reset_hsic_hub(esc, hub); + } + gpio_ctrl(esc, GPIO_OUTPUT, 1); return (0); @@ -248,13 +280,9 @@ esc->host_bst = rman_get_bustag(esc->res[1]); esc->host_bsh = rman_get_bushandle(esc->res[1]); - /* PWR registers */ - esc->pwr_bst = rman_get_bustag(esc->res[2]); - esc->pwr_bsh = rman_get_bushandle(esc->res[2]); - /* SYSREG */ - esc->sysreg_bst = rman_get_bustag(esc->res[3]); - esc->sysreg_bsh = rman_get_bushandle(esc->res[3]); + esc->sysreg_bst = rman_get_bustag(esc->res[2]); + esc->sysreg_bsh = rman_get_bushandle(esc->res[2]); /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), @@ -273,7 +301,7 @@ phy_init(esc); /* Setup interrupt handler */ - err = bus_setup_intr(dev, esc->res[4], INTR_TYPE_BIO | INTR_MPSAFE, + err = bus_setup_intr(dev, esc->res[3], INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { @@ -286,7 +314,7 @@ sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(dev, "Could not add USB device\n"); - err = bus_teardown_intr(dev, esc->res[4], + err = bus_teardown_intr(dev, esc->res[3], sc->sc_intr_hdl); if (err) device_printf(dev, "Could not tear down irq," @@ -307,7 +335,7 @@ device_delete_child(dev, sc->sc_bus.bdev); sc->sc_bus.bdev = NULL; - err = bus_teardown_intr(dev, esc->res[4], + err = bus_teardown_intr(dev, esc->res[3], sc->sc_intr_hdl); if (err) device_printf(dev, "Could not tear down irq," @@ -346,8 +374,8 @@ bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, EHCI_USBINTR, 0); - if (esc->res[4] && sc->sc_intr_hdl) { - err = bus_teardown_intr(dev, esc->res[4], + if (esc->res[3] && sc->sc_intr_hdl) { + err = bus_teardown_intr(dev, esc->res[3], sc->sc_intr_hdl); if (err) { device_printf(dev, "Could not tear down irq," Modified: trunk/sys/arm/samsung/exynos/exynos5_fimd.c =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_fimd.c 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/exynos5_fimd.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -31,7 +31,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/samsung/exynos/exynos5_fimd.c 266358 2014-05-17 21:26:33Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_fimd.c 269702 2014-08-08 06:29:30Z nwhitehorn $"); #include #include @@ -51,8 +51,8 @@ #include #include #include +#include -#include #include #include #include @@ -65,7 +65,6 @@ #include "gpio_if.h" #include -#include #include #include @@ -214,37 +213,37 @@ /* panel size */ if ((len = OF_getproplen(node, "panel-size")) <= 0) return (ENXIO); - OF_getprop(node, "panel-size", &dts_value, len); - panel->width = fdt32_to_cpu(dts_value[0]); - panel->height = fdt32_to_cpu(dts_value[1]); + OF_getencprop(node, "panel-size", dts_value, len); + panel->width = dts_value[0]; + panel->height = dts_value[1]; /* hsync */ if ((len = OF_getproplen(node, "panel-hsync")) <= 0) return (ENXIO); - OF_getprop(node, "panel-hsync", &dts_value, len); - panel->h_back_porch = fdt32_to_cpu(dts_value[0]); - panel->h_pulse_width = fdt32_to_cpu(dts_value[1]); - panel->h_front_porch = fdt32_to_cpu(dts_value[2]); + OF_getencprop(node, "panel-hsync", dts_value, len); + panel->h_back_porch = dts_value[0]; + panel->h_pulse_width = dts_value[1]; + panel->h_front_porch = dts_value[2]; /* vsync */ if ((len = OF_getproplen(node, "panel-vsync")) <= 0) return (ENXIO); - OF_getprop(node, "panel-vsync", &dts_value, len); - panel->v_back_porch = fdt32_to_cpu(dts_value[0]); - panel->v_pulse_width = fdt32_to_cpu(dts_value[1]); - panel->v_front_porch = fdt32_to_cpu(dts_value[2]); + OF_getencprop(node, "panel-vsync", dts_value, len); + panel->v_back_porch = dts_value[0]; + panel->v_pulse_width = dts_value[1]; + panel->v_front_porch = dts_value[2]; /* clk divider */ if ((len = OF_getproplen(node, "panel-clk-div")) <= 0) return (ENXIO); - OF_getprop(node, "panel-clk-div", &dts_value, len); - panel->clk_div = fdt32_to_cpu(dts_value[0]); + OF_getencprop(node, "panel-clk-div", dts_value, len); + panel->clk_div = dts_value[0]; /* backlight pin */ if ((len = OF_getproplen(node, "panel-backlight-pin")) <= 0) return (ENXIO); - OF_getprop(node, "panel-backlight-pin", &dts_value, len); - panel->backlight_pin = fdt32_to_cpu(dts_value[0]); + OF_getencprop(node, "panel-backlight-pin", dts_value, len); + panel->backlight_pin = dts_value[0]; return (0); } Modified: trunk/sys/arm/samsung/exynos/exynos5_i2c.c =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_i2c.c 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/exynos5_i2c.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -31,7 +31,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/samsung/exynos/exynos5_i2c.c 289666 2015-10-20 21:20:34Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_i2c.c 297793 2016-04-10 23:07:00Z pfg $"); #include #include @@ -48,13 +48,11 @@ #include "iicbus_if.h" -#include #include #include #include #include -#include #include #include @@ -295,7 +293,7 @@ mtx_unlock(&sc->mutex); return (IIC_ENOACK); - }; + } mtx_unlock(&sc->mutex); return (IIC_NOERR); @@ -373,6 +371,13 @@ mtx_lock(&sc->mutex); /* dummy read */ + clear_ipend(sc); + error = wait_for_iif(sc); + if (error) { + DPRINTF("cant i2c read: iif error\n"); + mtx_unlock(&sc->mutex); + return (error); + } READ1(sc, I2CDS); DPRINTF("Read "); @@ -383,7 +388,7 @@ reg = READ1(sc, I2CCON); reg &= ~(ACKGEN); WRITE1(sc, I2CCON, reg); - }; + } clear_ipend(sc); @@ -440,7 +445,7 @@ DPRINTF("cant i2c write: no ack\n"); mtx_unlock(&sc->mutex); return (IIC_ENOACK); - }; + } (*sent)++; } Modified: trunk/sys/arm/samsung/exynos/exynos5_machdep.c =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_machdep.c 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/exynos5_machdep.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -29,67 +29,57 @@ #include "opt_platform.h" #include -__FBSDID("$FreeBSD: stable/10/sys/arm/samsung/exynos/exynos5_machdep.c 266275 2014-05-16 23:49:40Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_machdep.c 331722 2018-03-29 02:50:57Z eadler $"); -#define _ARM32_BUS_DMA_PRIVATE #include #include #include +#include #include #include #include -#include #include +#include -#include - vm_offset_t -initarm_lastaddr(void) +platform_lastaddr(void) { - return (arm_devmap_lastaddr()); + return (devmap_lastaddr()); } void -initarm_early_init(void) +platform_probe_and_attach(void) { } void -initarm_gpio_init(void) +platform_gpio_init(void) { } void -initarm_late_init(void) +platform_late_init(void) { } int -initarm_devmap_init(void) +platform_devmap_init(void) { + /* CHIP ID */ + devmap_add_entry(0x10000000, 0x100000); + /* UART */ - arm_devmap_add_entry(0x12C00000, 0x100000); + devmap_add_entry(0x12C00000, 0x100000); - return (0); -} + /* DWMMC */ + devmap_add_entry(0x12200000, 0x100000); -struct arm32_dma_range * -bus_dma_get_range(void) -{ - - return (NULL); -} - -int -bus_dma_get_range_nb(void) -{ - return (0); } Modified: trunk/sys/arm/samsung/exynos/exynos5_mct.c =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_mct.c 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/exynos5_mct.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -30,7 +30,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/samsung/exynos/exynos5_mct.c 266332 2014-05-17 17:54:38Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_mct.c 269703 2014-08-08 06:30:17Z nwhitehorn $"); #include #include @@ -46,13 +46,11 @@ #include #include -#include #include #include #include #include -#include #define MCT_CTRL_START (1 << 8) #define MCT_CTRL (0x240) Modified: trunk/sys/arm/samsung/exynos/exynos5_mp.c =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_mp.c 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/exynos5_mp.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -1,6 +1,6 @@ /* $MidnightBSD$ */ /*- - * Copyright (c) 2013 Ruslan Bukin
+ * Copyright (c) 2013-2014 Ruslan Bukin
* All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,7 +26,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/samsung/exynos/exynos5_mp.c 266203 2014-05-16 00:14:50Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_mp.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include #include @@ -34,17 +34,41 @@ #include #include +#include +#include + +#include #include #include #include -#define EXYNOS_SYSRAM 0x02020000 +#define EXYNOS_CHIPID 0x10000000 -void -platform_mp_init_secondary(void) +#define EXYNOS5250_SOC_ID 0x43520000 +#define EXYNOS5420_SOC_ID 0xE5420000 +#define EXYNOS5_SOC_ID_MASK 0xFFFFF000 + +#define EXYNOS_SYSRAM 0x02020000 +#define EXYNOS5420_SYSRAM_NS (EXYNOS_SYSRAM + 0x53000 + 0x1c) + +#define EXYNOS_PMU_BASE 0x10040000 +#define CORE_CONFIG(n) (0x2000 + (0x80 * (n))) +#define CORE_STATUS(n) (CORE_CONFIG(n) + 0x4) +#define CORE_PWR_EN 0x3 + +static int +exynos_get_soc_id(void) { + bus_addr_t chipid; + int reg; - gic_init_secondary(); + if (bus_space_map(fdtbus_bs_tag, EXYNOS_CHIPID, + 0x1000, 0, &chipid) != 0) + panic("Couldn't map chipid\n"); + reg = bus_space_read_4(fdtbus_bs_tag, chipid, 0x0); + bus_space_unmap(fdtbus_bs_tag, chipid, 0x1000); + + return (reg & EXYNOS5_SOC_ID_MASK); } void @@ -51,40 +75,58 @@ platform_mp_setmaxid(void) { - mp_maxid = 1; -} + if (exynos_get_soc_id() == EXYNOS5420_SOC_ID) + mp_ncpus = 4; + else + mp_ncpus = 2; -int -platform_mp_probe(void) -{ - - mp_ncpus = 2; - return (1); + mp_maxid = mp_ncpus - 1; } void platform_mp_start_ap(void) { - bus_addr_t sysram; - int err; + bus_addr_t sysram, pmu; + int err, i, j; + int status; + int reg; - err = bus_space_map(fdtbus_bs_tag, EXYNOS_SYSRAM, 0x100, 0, &sysram); + err = bus_space_map(fdtbus_bs_tag, EXYNOS_PMU_BASE, 0x20000, 0, &pmu); if (err != 0) + panic("Couldn't map pmu\n"); + + if (exynos_get_soc_id() == EXYNOS5420_SOC_ID) + reg = EXYNOS5420_SYSRAM_NS; + else + reg = EXYNOS_SYSRAM; + + err = bus_space_map(fdtbus_bs_tag, reg, 0x100, 0, &sysram); + if (err != 0) panic("Couldn't map sysram\n"); + /* Give power to CPUs */ + for (i = 1; i < mp_ncpus; i++) { + bus_space_write_4(fdtbus_bs_tag, pmu, CORE_CONFIG(i), + CORE_PWR_EN); + + for (j = 10; j >= 0; j--) { + status = bus_space_read_4(fdtbus_bs_tag, pmu, + CORE_STATUS(i)); + if ((status & CORE_PWR_EN) == CORE_PWR_EN) + break; + DELAY(10); + if (j == 0) + printf("Can't power on CPU%d\n", i); + } + } + bus_space_write_4(fdtbus_bs_tag, sysram, 0x0, pmap_kextract((vm_offset_t)mpentry)); - cpu_idcache_wbinv_all(); - cpu_l2cache_wbinv_all(); + dcache_wbinv_poc_all(); - armv7_sev(); + dsb(); + sev(); bus_space_unmap(fdtbus_bs_tag, sysram, 0x100); + bus_space_unmap(fdtbus_bs_tag, pmu, 0x20000); } - -void -platform_ipi_send(cpuset_t cpus, u_int ipi) -{ - - pic_ipi_send(cpus, ipi); -} Modified: trunk/sys/arm/samsung/exynos/exynos5_pad.c =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_pad.c 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/exynos5_pad.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -29,9 +29,11 @@ * Samsung Exynos 5 Pad Control * Chapter 4, Exynos 5 Dual User's Manual Public Rev 1.00 */ - +#ifdef USB_GLOBAL_INCLUDE_FILE +#include USB_GLOBAL_INCLUDE_FILE +#else #include -__FBSDID("$FreeBSD: stable/10/sys/arm/samsung/exynos/exynos5_pad.c 278786 2015-02-14 21:16:19Z loos $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_pad.c 351675 2019-09-02 00:29:16Z emaste $"); #include #include @@ -46,17 +48,17 @@ #include #include -#include +#include #include #include #include #include -#include #include #include #include "gpio_if.h" +#endif #include #include @@ -66,11 +68,14 @@ #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) -#define NPORTS 4 -#define NGRP 40 -#define NGPIO 253 -#define NINTS 16 +#define MAX_PORTS 5 +#define MAX_NGPIO 253 +#define N_EXT_INTS 16 + +#define EXYNOS5250 1 +#define EXYNOS5420 2 + #define PIN_IN 0 #define PIN_OUT 1 @@ -82,6 +87,7 @@ /* * GPIO interface */ +static device_t pad_get_bus(device_t); static int pad_pin_max(device_t, int *); static int pad_pin_getcaps(device_t, uint32_t, uint32_t *); static int pad_pin_getname(device_t, uint32_t, char *); @@ -91,20 +97,37 @@ static int pad_pin_get(device_t, uint32_t, unsigned int *); static int pad_pin_toggle(device_t, uint32_t pin); +struct gpio_bank { + char *name; + uint32_t port; + uint32_t con; + uint32_t ngpio; + uint32_t ext_con; + uint32_t ext_flt_con; + uint32_t mask; + uint32_t pend; +}; + struct pad_softc { - struct resource *res[NPORTS+4]; - bus_space_tag_t bst[NPORTS]; - bus_space_handle_t bsh[NPORTS]; + struct resource *res[MAX_PORTS * 2]; + bus_space_tag_t bst[MAX_PORTS]; + bus_space_handle_t bsh[MAX_PORTS]; struct mtx sc_mtx; int gpio_npins; - struct gpio_pin gpio_pins[NGPIO]; - void *gpio_ih[NPORTS+4]; + struct gpio_pin gpio_pins[MAX_NGPIO]; + void *gpio_ih[MAX_PORTS]; device_t dev; + device_t busdev; + int model; + struct resource_spec *pad_spec; + struct gpio_bank *gpio_map; + struct interrupt_entry *interrupt_table; + int nports; }; struct pad_softc *gpio_sc; -static struct resource_spec pad_spec[] = { +static struct resource_spec pad_spec_5250[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { SYS_RES_MEMORY, 2, RF_ACTIVE }, @@ -116,6 +139,26 @@ { -1, 0 } }; +static struct resource_spec pad_spec_5420[] = { + { 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 }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 1, RF_ACTIVE }, + { SYS_RES_IRQ, 2, RF_ACTIVE }, + { SYS_RES_IRQ, 3, RF_ACTIVE }, + { SYS_RES_IRQ, 4, RF_ACTIVE }, + { -1, 0 } +}; + +static struct ofw_compat_data compat_data[] = { + {"samsung,exynos5420-padctrl", EXYNOS5420}, + {"samsung,exynos5250-padctrl", EXYNOS5250}, + {NULL, 0} +}; + struct pad_intr { uint32_t enabled; void (*ih) (void *); @@ -122,7 +165,7 @@ void *ih_user; }; -static struct pad_intr intr_map[NGPIO]; +static struct pad_intr intr_map[MAX_NGPIO]; struct interrupt_entry { int gpio_number; @@ -129,7 +172,7 @@ char *combiner_source_name; }; -struct interrupt_entry interrupt_table[NINTS] = { +struct interrupt_entry interrupt_table_5250[N_EXT_INTS] = { { 147, "EINT[15]" }, { 146, "EINT[14]" }, { 145, "EINT[13]" }, @@ -148,16 +191,23 @@ { 132, "EINT[0]" }, }; -struct gpio_bank { - char *name; - uint32_t port; - uint32_t con; - uint32_t ngpio; - uint32_t ext_int_grp; - uint32_t ext_con; - uint32_t ext_flt_con; - uint32_t mask; - uint32_t pend; +struct interrupt_entry interrupt_table_5420[N_EXT_INTS] = { + { 23, "EINT[15]" }, + { 22, "EINT[14]" }, + { 21, "EINT[13]" }, + { 20, "EINT[12]" }, + { 19, "EINT[11]" }, + { 18, "EINT[10]" }, + { 17, "EINT[9]" }, + { 16, "EINT[8]" }, + { 15, "EINT[7]" }, + { 14, "EINT[6]" }, + { 13, "EINT[5]" }, + { 12, "EINT[4]" }, + { 11, "EINT[3]" }, + { 10, "EINT[2]" }, + { 9, "EINT[1]" }, + { 8, "EINT[0]" }, }; /* @@ -164,57 +214,110 @@ * 253 multi-functional input/output ports */ -static struct gpio_bank gpio_map[] = { +static struct gpio_bank gpio_map_5250[] = { /* first 132 gpio */ - { "gpa0", 0, 0x000, 8, 1, 0x700, 0x800, 0x900, 0xA00 }, - { "gpa1", 0, 0x020, 6, 2, 0x704, 0x808, 0x904, 0xA04 }, - { "gpa2", 0, 0x040, 8, 3, 0x708, 0x810, 0x908, 0xA08 }, - { "gpb0", 0, 0x060, 5, 4, 0x70C, 0x818, 0x90C, 0xA0C }, - { "gpb1", 0, 0x080, 5, 5, 0x710, 0x820, 0x910, 0xA10 }, - { "gpb2", 0, 0x0A0, 4, 6, 0x714, 0x828, 0x914, 0xA14 }, - { "gpb3", 0, 0x0C0, 4, 7, 0x718, 0x830, 0x918, 0xA18 }, - { "gpc0", 0, 0x0E0, 7, 8, 0x71C, 0x838, 0x91C, 0xA1C }, - { "gpc1", 0, 0x100, 4, 9, 0x720, 0x840, 0x920, 0xA20 }, - { "gpc2", 0, 0x120, 7, 10, 0x724, 0x848, 0x924, 0xA24 }, - { "gpc3", 0, 0x140, 7, 11, 0x728, 0x850, 0x928, 0xA28 }, - { "gpd0", 0, 0x160, 4, 12, 0x72C, 0x858, 0x92C, 0xA2C }, - { "gpd1", 0, 0x180, 8, 13, 0x730, 0x860, 0x930, 0xA30 }, - { "gpy0", 0, 0x1A0, 6, 0, 0, 0, 0, 0 }, - { "gpy1", 0, 0x1C0, 4, 0, 0, 0, 0, 0 }, - { "gpy2", 0, 0x1E0, 6, 0, 0, 0, 0, 0 }, - { "gpy3", 0, 0x200, 8, 0, 0, 0, 0, 0 }, - { "gpy4", 0, 0x220, 8, 0, 0, 0, 0, 0 }, - { "gpy5", 0, 0x240, 8, 0, 0, 0, 0, 0 }, - { "gpy6", 0, 0x260, 8, 0, 0, 0, 0, 0 }, - { "gpc4", 0, 0x2E0, 7, 30, 0x734, 0x868, 0x934, 0xA34 }, + { "gpa0", 0, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, + { "gpa1", 0, 0x020, 6, 0x704, 0x808, 0x904, 0xA04 }, + { "gpa2", 0, 0x040, 8, 0x708, 0x810, 0x908, 0xA08 }, + { "gpb0", 0, 0x060, 5, 0x70C, 0x818, 0x90C, 0xA0C }, + { "gpb1", 0, 0x080, 5, 0x710, 0x820, 0x910, 0xA10 }, + { "gpb2", 0, 0x0A0, 4, 0x714, 0x828, 0x914, 0xA14 }, + { "gpb3", 0, 0x0C0, 4, 0x718, 0x830, 0x918, 0xA18 }, + { "gpc0", 0, 0x0E0, 7, 0x71C, 0x838, 0x91C, 0xA1C }, + { "gpc1", 0, 0x100, 4, 0x720, 0x840, 0x920, 0xA20 }, + { "gpc2", 0, 0x120, 7, 0x724, 0x848, 0x924, 0xA24 }, + { "gpc3", 0, 0x140, 7, 0x728, 0x850, 0x928, 0xA28 }, + { "gpd0", 0, 0x160, 4, 0x72C, 0x858, 0x92C, 0xA2C }, + { "gpd1", 0, 0x180, 8, 0x730, 0x860, 0x930, 0xA30 }, + { "gpy0", 0, 0x1A0, 6, 0, 0, 0, 0 }, + { "gpy1", 0, 0x1C0, 4, 0, 0, 0, 0 }, + { "gpy2", 0, 0x1E0, 6, 0, 0, 0, 0 }, + { "gpy3", 0, 0x200, 8, 0, 0, 0, 0 }, + { "gpy4", 0, 0x220, 8, 0, 0, 0, 0 }, + { "gpy5", 0, 0x240, 8, 0, 0, 0, 0 }, + { "gpy6", 0, 0x260, 8, 0, 0, 0, 0 }, + { "gpc4", 0, 0x2E0, 7, 0x734, 0x868, 0x934, 0xA34 }, /* next 32 */ - { "gpx0", 0, 0xC00, 8, 40, 0xE00, 0xE80, 0xF00, 0xF40 }, - { "gpx1", 0, 0xC20, 8, 41, 0xE04, 0xE88, 0xF04, 0xF44 }, - { "gpx2", 0, 0xC40, 8, 42, 0xE08, 0xE90, 0xF08, 0xF48 }, - { "gpx3", 0, 0xC60, 8, 43, 0xE0C, 0xE98, 0xF0C, 0xF4C }, + { "gpx0", 0, 0xC00, 8, 0xE00, 0xE80, 0xF00, 0xF40 }, + { "gpx1", 0, 0xC20, 8, 0xE04, 0xE88, 0xF04, 0xF44 }, + { "gpx2", 0, 0xC40, 8, 0xE08, 0xE90, 0xF08, 0xF48 }, + { "gpx3", 0, 0xC60, 8, 0xE0C, 0xE98, 0xF0C, 0xF4C }, - { "gpe0", 1, 0x000, 8, 14, 0x700, 0x800, 0x900, 0xA00 }, - { "gpe1", 1, 0x020, 2, 15, 0x704, 0x808, 0x904, 0xA04 }, - { "gpf0", 1, 0x040, 4, 16, 0x708, 0x810, 0x908, 0xA08 }, - { "gpf1", 1, 0x060, 4, 17, 0x70C, 0x818, 0x90C, 0xA0C }, - { "gpg0", 1, 0x080, 8, 18, 0x710, 0x820, 0x910, 0xA10 }, - { "gpg1", 1, 0x0A0, 8, 19, 0x714, 0x828, 0x914, 0xA14 }, - { "gpg2", 1, 0x0C0, 2, 20, 0x718, 0x830, 0x918, 0xA18 }, - { "gph0", 1, 0x0E0, 4, 21, 0x71C, 0x838, 0x91C, 0xA1C }, - { "gph1", 1, 0x100, 8, 22, 0x720, 0x840, 0x920, 0xA20 }, + { "gpe0", 1, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, + { "gpe1", 1, 0x020, 2, 0x704, 0x808, 0x904, 0xA04 }, + { "gpf0", 1, 0x040, 4, 0x708, 0x810, 0x908, 0xA08 }, + { "gpf1", 1, 0x060, 4, 0x70C, 0x818, 0x90C, 0xA0C }, + { "gpg0", 1, 0x080, 8, 0x710, 0x820, 0x910, 0xA10 }, + { "gpg1", 1, 0x0A0, 8, 0x714, 0x828, 0x914, 0xA14 }, + { "gpg2", 1, 0x0C0, 2, 0x718, 0x830, 0x918, 0xA18 }, + { "gph0", 1, 0x0E0, 4, 0x71C, 0x838, 0x91C, 0xA1C }, + { "gph1", 1, 0x100, 8, 0x720, 0x840, 0x920, 0xA20 }, - { "gpv0", 2, 0x000, 8, 60, 0x700, 0x800, 0x900, 0xA00 }, - { "gpv1", 2, 0x020, 8, 61, 0x704, 0x808, 0x904, 0xA04 }, - { "gpv2", 2, 0x060, 8, 62, 0x708, 0x810, 0x908, 0xA08 }, - { "gpv3", 2, 0x080, 8, 63, 0x70C, 0x818, 0x90C, 0xA0C }, - { "gpv4", 2, 0x0C0, 2, 64, 0x710, 0x820, 0x910, 0xA10 }, + { "gpv0", 2, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, + { "gpv1", 2, 0x020, 8, 0x704, 0x808, 0x904, 0xA04 }, + { "gpv2", 2, 0x060, 8, 0x708, 0x810, 0x908, 0xA08 }, + { "gpv3", 2, 0x080, 8, 0x70C, 0x818, 0x90C, 0xA0C }, + { "gpv4", 2, 0x0C0, 2, 0x710, 0x820, 0x910, 0xA10 }, - { "gpz", 3, 0x000, 7, 50, 0x700, 0x800, 0x900, 0xA00 }, + { "gpz", 3, 0x000, 7, 0x700, 0x800, 0x900, 0xA00 }, + + { NULL, -1, -1, -1, -1, -1, -1, -1 }, }; +static struct gpio_bank gpio_map_5420[] = { + /* First 40 */ + { "gpy7", 0, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, + { "gpx0", 0, 0xC00, 8, 0x704, 0xE80, 0xF00, 0xF40 }, + { "gpx1", 0, 0xC20, 8, 0x708, 0xE88, 0xF04, 0xF44 }, + { "gpx2", 0, 0xC40, 8, 0x70C, 0xE90, 0xF08, 0xF48 }, + { "gpx3", 0, 0xC60, 8, 0x710, 0xE98, 0xF0C, 0xF4C }, + + /* Next 85 */ + { "gpc0", 1, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, + { "gpc1", 1, 0x020, 8, 0x704, 0x808, 0x904, 0xA04 }, + { "gpc2", 1, 0x040, 7, 0x708, 0x810, 0x908, 0xA08 }, + { "gpc3", 1, 0x060, 4, 0x70C, 0x818, 0x90C, 0xA0C }, + { "gpc4", 1, 0x080, 2, 0x710, 0x820, 0x910, 0xA10 }, + { "gpd1", 1, 0x0A0, 8, 0x714, 0x828, 0x914, 0xA14 }, + { "gpy0", 1, 0x0C0, 6, 0x718, 0x830, 0x918, 0xA18 }, + { "gpy1", 1, 0x0E0, 4, 0x71C, 0x838, 0x91C, 0xA1C }, + { "gpy2", 1, 0x100, 6, 0x720, 0x840, 0x920, 0xA20 }, + { "gpy3", 1, 0x120, 8, 0x724, 0x848, 0x924, 0xA24 }, + { "gpy4", 1, 0x140, 8, 0x728, 0x850, 0x928, 0xA28 }, + { "gpy5", 1, 0x160, 8, 0x72C, 0x858, 0x92C, 0xA2C }, + { "gpy6", 1, 0x180, 8, 0x730, 0x860, 0x930, 0xA30 }, + + /* Next 46 */ + { "gpe0", 2, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, + { "gpe1", 2, 0x020, 2, 0x704, 0x808, 0x904, 0xA04 }, + { "gpf0", 2, 0x040, 6, 0x708, 0x810, 0x908, 0xA08 }, + { "gpf1", 2, 0x060, 8, 0x70C, 0x818, 0x90C, 0xA0C }, + { "gpg0", 2, 0x080, 8, 0x710, 0x820, 0x910, 0xA10 }, + { "gpg1", 2, 0x0A0, 8, 0x714, 0x828, 0x914, 0xA14 }, + { "gpg2", 2, 0x0C0, 2, 0x718, 0x830, 0x918, 0xA18 }, + { "gpj4", 2, 0x0E0, 4, 0x71C, 0x838, 0x91C, 0xA1C }, + + /* Next 54 */ + { "gpa0", 3, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, + { "gpa1", 3, 0x020, 6, 0x704, 0x808, 0x904, 0xA04 }, + { "gpa2", 3, 0x040, 8, 0x708, 0x810, 0x908, 0xA08 }, + { "gpb0", 3, 0x060, 5, 0x70C, 0x818, 0x90C, 0xA0C }, + { "gpb1", 3, 0x080, 5, 0x710, 0x820, 0x910, 0xA10 }, + { "gpb2", 3, 0x0A0, 4, 0x714, 0x828, 0x914, 0xA14 }, + { "gpb3", 3, 0x0C0, 8, 0x718, 0x830, 0x918, 0xA18 }, + { "gpb4", 3, 0x0E0, 2, 0x71C, 0x838, 0x91C, 0xA1C }, + { "gph0", 3, 0x100, 8, 0x720, 0x840, 0x920, 0xA20 }, + + /* Last 7 */ + { "gpz", 4, 0x000, 7, 0x700, 0x800, 0x900, 0xA00 }, + + { NULL, -1, -1, -1, -1, -1, -1, -1 }, +}; + static int -get_bank(int gpio_number, struct gpio_bank *bank, int *pin_shift) +get_bank(struct pad_softc *sc, int gpio_number, + struct gpio_bank *bank, int *pin_shift) { int ngpio; int i; @@ -221,17 +324,17 @@ int n; n = 0; - for (i = 0; i < NGRP; i++) { - ngpio = gpio_map[i].ngpio; + for (i = 0; sc->gpio_map[i].ngpio != -1; i++) { + ngpio = sc->gpio_map[i].ngpio; - if ((n + ngpio) >= gpio_number) { - *bank = gpio_map[i]; + if ((n + ngpio) > gpio_number) { + *bank = sc->gpio_map[i]; *pin_shift = (gpio_number - n); return (0); - }; + } n += ngpio; - }; + } return (-1); } @@ -261,16 +364,16 @@ sc = arg; n = 0; - for (i = 0; i < NGRP; i++) { + for (i = 0; sc->gpio_map[i].ngpio != -1; i++) { found = 0; - ngpio = gpio_map[i].ngpio; + ngpio = sc->gpio_map[i].ngpio; - if (gpio_map[i].pend == 0) { + if (sc->gpio_map[i].pend == 0) { n += ngpio; continue; } - reg = READ4(sc, gpio_map[i].port, gpio_map[i].pend); + reg = READ4(sc, sc->gpio_map[i].port, sc->gpio_map[i].pend); for (j = 0; j < ngpio; j++) { if (reg & (1 << j)) { @@ -287,7 +390,7 @@ if (found) { /* ACK */ - WRITE4(sc, gpio_map[i].port, gpio_map[i].pend, reg); + WRITE4(sc, sc->gpio_map[i].port, sc->gpio_map[i].pend, reg); } n += ngpio; @@ -308,17 +411,17 @@ sc = gpio_sc; if (sc == NULL) { - device_printf(sc->dev, "Error: pad is not attached\n"); + printf("%s: Error: pad is not attached\n", __func__); return (-1); } - if (get_bank(gpio_number, &bank, &pin_shift) != 0) + if (get_bank(sc, gpio_number, &bank, &pin_shift) != 0) return (-1); entry = NULL; - for (i = 0; i < NINTS; i++) - if (interrupt_table[i].gpio_number == gpio_number) - entry = &interrupt_table[i]; + for (i = 0; i < N_EXT_INTS; i++) + if (sc->interrupt_table[i].gpio_number == gpio_number) + entry = &(sc->interrupt_table[i]); if (entry == NULL) { device_printf(sc->dev, "Cant find interrupt source for %d\n", @@ -375,11 +478,12 @@ if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "exynos,pad")) - return (ENXIO); + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { + device_set_desc(dev, "Exynos Pad Control"); + return (BUS_PROBE_DEFAULT); + } - device_set_desc(dev, "Exynos Pad Control"); - return (BUS_PROBE_DEFAULT); + return (ENXIO); } static int @@ -392,40 +496,60 @@ int i; sc = device_get_softc(dev); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); - if (bus_alloc_resources(dev, pad_spec, sc->res)) { + sc->model = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + switch (sc->model) { + case EXYNOS5250: + sc->pad_spec = pad_spec_5250; + sc->gpio_map = gpio_map_5250; + sc->interrupt_table = interrupt_table_5250; + sc->gpio_npins = 253; + sc->nports = 4; + break; + case EXYNOS5420: + sc->pad_spec = pad_spec_5420; + sc->gpio_map = gpio_map_5420; + sc->interrupt_table = interrupt_table_5420; + sc->gpio_npins = 232; + sc->nports = 5; + break; + default: + goto fail; + } + + if (bus_alloc_resources(dev, sc->pad_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); - return (ENXIO); + goto fail; } /* Memory interface */ - for (i = 0; i < NPORTS; i++) { + for (i = 0; i < sc->nports; i++) { sc->bst[i] = rman_get_bustag(sc->res[i]); sc->bsh[i] = rman_get_bushandle(sc->res[i]); - }; + } sc->dev = dev; - sc->gpio_npins = NGPIO; gpio_sc = sc; - for (i = 0; i < NPORTS; i++) { - if ((bus_setup_intr(dev, sc->res[NPORTS + i], + for (i = 0; i < sc->nports; i++) { + if ((bus_setup_intr(dev, sc->res[sc->nports + i], INTR_TYPE_BIO | INTR_MPSAFE, port_intr, NULL, sc, &sc->gpio_ih[i]))) { device_printf(dev, "ERROR: Unable to register interrupt handler\n"); - return (ENXIO); + goto fail; } - }; + } for (i = 0; i < sc->gpio_npins; i++) { sc->gpio_pins[i].gp_pin = i; sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; - if (get_bank(i, &bank, &pin_shift) != 0) + if (get_bank(sc, i, &bank, &pin_shift) != 0) continue; pin_shift *= 4; @@ -441,18 +565,42 @@ snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "pad%d.%d", device_get_unit(dev), i); } + sc->busdev = gpiobus_attach_bus(dev); + if (sc->busdev == NULL) + goto fail; - device_add_child(dev, "gpioc", -1); - device_add_child(dev, "gpiobus", -1); + return (0); - return (bus_generic_attach(dev)); +fail: + for (i = 0; i < sc->nports; i++) { + if (sc->gpio_ih[i]) + bus_teardown_intr(dev, sc->res[sc->nports + i], + sc->gpio_ih[i]); + } + bus_release_resources(dev, sc->pad_spec, sc->res); + mtx_destroy(&sc->sc_mtx); + + return (ENXIO); } +static device_t +pad_get_bus(device_t dev) +{ + struct pad_softc *sc; + + sc = device_get_softc(dev); + + return (sc->busdev); +} + static int pad_pin_max(device_t dev, int *maxpin) { + struct pad_softc *sc; - *maxpin = NGPIO - 1; + sc = device_get_softc(dev); + + *maxpin = sc->gpio_npins - 1; return (0); } @@ -539,7 +687,7 @@ if (i >= sc->gpio_npins) return (EINVAL); - if (get_bank(pin, &bank, &pin_shift) != 0) + if (get_bank(sc, pin, &bank, &pin_shift) != 0) return (EINVAL); GPIO_LOCK(sc); @@ -570,7 +718,7 @@ if (i >= sc->gpio_npins) return (EINVAL); - if (get_bank(pin, &bank, &pin_shift) != 0) + if (get_bank(sc, pin, &bank, &pin_shift) != 0) return (EINVAL); GPIO_LOCK(sc); @@ -602,7 +750,7 @@ if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); - if (get_bank(pin->gp_pin, &bank, &pin_shift) != 0) + if (get_bank(sc, pin->gp_pin, &bank, &pin_shift) != 0) return; pin_shift *= 4; @@ -667,7 +815,7 @@ if (i >= sc->gpio_npins) return (EINVAL); - if (get_bank(pin, &bank, &pin_shift) != 0) + if (get_bank(sc, pin, &bank, &pin_shift) != 0) return (EINVAL); GPIO_LOCK(sc); @@ -686,6 +834,7 @@ DEVMETHOD(device_attach, pad_attach), /* GPIO protocol */ + DEVMETHOD(gpio_get_bus, pad_get_bus), DEVMETHOD(gpio_pin_max, pad_pin_max), DEVMETHOD(gpio_pin_getname, pad_pin_getname), DEVMETHOD(gpio_pin_getcaps, pad_pin_getcaps), Modified: trunk/sys/arm/samsung/exynos/exynos5_pad.h =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_pad.h 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/exynos5_pad.h 2020-03-06 17:05:09 UTC (rev 12395) @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/samsung/exynos/exynos5_pad.h 266341 2014-05-17 19:37:04Z ian $ + * $FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_pad.h 263936 2014-03-30 15:22:36Z br $ */ int pad_setup_intr(int gpio_number, void (*ih)(void *), void *ih_user); Added: trunk/sys/arm/samsung/exynos/exynos5_pmu.c =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_pmu.c (rev 0) +++ trunk/sys/arm/samsung/exynos/exynos5_pmu.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -0,0 +1,180 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2014 Ruslan Bukin
+ * 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. + */ + +/* + * Exynos 5 Power Management Unit (PMU) + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_pmu.c 269703 2014-08-08 06:30:17Z nwhitehorn $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define EXYNOS5250 1 +#define EXYNOS5420 2 + +/* PWR control */ +#define EXYNOS5_PWR_USBHOST_PHY 0x708 +#define EXYNOS5_USBDRD_PHY_CTRL 0x704 +#define EXYNOS5420_USBDRD1_PHY_CTRL 0x708 + +#define PHY_POWER_ON 1 +#define PHY_POWER_OFF 0 + +struct pmu_softc { + struct resource *res[1]; + bus_space_tag_t bst; + bus_space_handle_t bsh; + device_t dev; + int model; +}; + +struct pmu_softc *pmu_sc; + +static struct ofw_compat_data compat_data[] = { + {"samsung,exynos5420-pmu", EXYNOS5420}, + {"samsung,exynos5250-pmu", EXYNOS5250}, + {NULL, 0} +}; + +static struct resource_spec pmu_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static int +pmu_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, "Samsung Exynos 5 Power Management Unit"); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +int +usb2_phy_power_on(void) +{ + struct pmu_softc *sc; + + sc = pmu_sc; + if (sc == NULL) + return (-1); + + /* EHCI */ + WRITE4(sc, EXYNOS5_PWR_USBHOST_PHY, PHY_POWER_ON); + + return (0); +} + +int +usbdrd_phy_power_on(void) +{ + struct pmu_softc *sc; + + sc = pmu_sc; + if (sc == NULL) + return (-1); + + /* + * First XHCI controller (left-side USB port on chromebook2) + */ + WRITE4(sc, EXYNOS5_USBDRD_PHY_CTRL, PHY_POWER_ON); + + /* + * Second XHCI controller (right-side USB port on chrombook2) + * Only available on 5420. + */ + if (sc->model == EXYNOS5420) + WRITE4(sc, EXYNOS5420_USBDRD1_PHY_CTRL, PHY_POWER_ON); + + return (0); +} + +static int +pmu_attach(device_t dev) +{ + struct pmu_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->model = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + if (bus_alloc_resources(dev, pmu_spec, sc->res)) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + /* Memory interface */ + sc->bst = rman_get_bustag(sc->res[0]); + sc->bsh = rman_get_bushandle(sc->res[0]); + + pmu_sc = sc; + + return (0); +} + +static device_method_t pmu_methods[] = { + DEVMETHOD(device_probe, pmu_probe), + DEVMETHOD(device_attach, pmu_attach), + { 0, 0 } +}; + +static driver_t pmu_driver = { + "pmu", + pmu_methods, + sizeof(struct pmu_softc), +}; + +static devclass_t pmu_devclass; + +DRIVER_MODULE(pmu, simplebus, pmu_driver, pmu_devclass, 0, 0); Property changes on: trunk/sys/arm/samsung/exynos/exynos5_pmu.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/samsung/exynos/exynos5_pmu.h =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_pmu.h (rev 0) +++ trunk/sys/arm/samsung/exynos/exynos5_pmu.h 2020-03-06 17:05:09 UTC (rev 12395) @@ -0,0 +1,31 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2014 Ruslan Bukin
+ * 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: stable/11/sys/arm/samsung/exynos/exynos5_pmu.h 269369 2014-08-01 06:20:25Z br $ + */ + +int usb2_phy_power_on(void); +int usbdrd_phy_power_on(void); Property changes on: trunk/sys/arm/samsung/exynos/exynos5_pmu.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/samsung/exynos/exynos5_spi.c =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_spi.c (rev 0) +++ trunk/sys/arm/samsung/exynos/exynos5_spi.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -0,0 +1,237 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2014 Ruslan Bukin
+ * 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. + */ + +/* + * Exynos 5 Serial Peripheral Interface (SPI) + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_spi.c 331506 2018-03-24 23:23:31Z ian $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "spibus_if.h" + +#include +#include +#include + +#include +#include +#include + +#include + +#define CH_CFG 0x00 /* SPI configuration */ +#define SW_RST (1 << 5) /* Reset */ +#define RX_CH_ON (1 << 1) /* SPI Rx Channel On */ +#define TX_CH_ON (1 << 0) /* SPI Tx Channel On */ +#define MODE_CFG 0x08 /* FIFO control */ +#define CS_REG 0x0C /* slave selection control */ +#define NSSOUT (1 << 0) +#define SPI_INT_EN 0x10 /* interrupt enable */ +#define SPI_STATUS 0x14 /* SPI status */ +#define TX_FIFO_LVL_S 6 +#define TX_FIFO_LVL_M 0x1ff +#define RX_FIFO_LVL_S 15 +#define RX_FIFO_LVL_M 0x1ff +#define SPI_TX_DATA 0x18 /* Tx data */ +#define SPI_RX_DATA 0x1C /* Rx data */ +#define PACKET_CNT_REG 0x20 /* packet count */ +#define PENDING_CLR_REG 0x24 /* interrupt pending clear */ +#define SWAP_CFG 0x28 /* swap configuration */ +#define FB_CLK_SEL 0x2C /* feedback clock selection */ +#define FB_CLK_180 0x2 /* 180 degree phase lagging */ + +struct spi_softc { + struct resource *res[2]; + bus_space_tag_t bst; + bus_space_handle_t bsh; + device_t dev; +}; + +struct spi_softc *spi_sc; + +static struct resource_spec spi_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static int +spi_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "samsung,exynos5-spi")) + return (ENXIO); + + device_set_desc(dev, "Exynos 5 Serial Peripheral Interface (SPI)"); + return (BUS_PROBE_DEFAULT); +} + +static int +spi_attach(device_t dev) +{ + struct spi_softc *sc; + int reg; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (bus_alloc_resources(dev, spi_spec, sc->res)) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + /* Memory interface */ + sc->bst = rman_get_bustag(sc->res[0]); + sc->bsh = rman_get_bushandle(sc->res[0]); + + spi_sc = sc; + + WRITE4(sc, FB_CLK_SEL, FB_CLK_180); + + reg = READ4(sc, CH_CFG); + reg |= (RX_CH_ON | TX_CH_ON); + WRITE4(sc, CH_CFG, reg); + + device_add_child(dev, "spibus", 0); + return (bus_generic_attach(dev)); +} + +static int +spi_txrx(struct spi_softc *sc, uint8_t *out_buf, + uint8_t *in_buf, int bufsz, int cs) +{ + uint32_t reg; + uint32_t i; + + if (bufsz == 0) { + /* Nothing to transfer */ + return (0); + } + + /* Reset registers */ + reg = READ4(sc, CH_CFG); + reg |= SW_RST; + WRITE4(sc, CH_CFG, reg); + reg &= ~SW_RST; + WRITE4(sc, CH_CFG, reg); + + /* Assert CS */ + reg = READ4(sc, CS_REG); + reg &= ~NSSOUT; + WRITE4(sc, CS_REG, reg); + + for (i = 0; i < bufsz; i++) { + + /* TODO: Implement FIFO operation */ + + /* Wait all the data shifted out */ + while (READ4(sc, SPI_STATUS) & \ + (TX_FIFO_LVL_M << TX_FIFO_LVL_S)) + continue; + + WRITE1(sc, SPI_TX_DATA, out_buf[i]); + + /* Wait until no data available */ + while ((READ4(sc, SPI_STATUS) & \ + (RX_FIFO_LVL_M << RX_FIFO_LVL_S)) == 0) + continue; + + in_buf[i] = READ1(sc, SPI_RX_DATA); + } + + /* Deassert CS */ + reg = READ4(sc, CS_REG); + reg |= NSSOUT; + WRITE4(sc, CS_REG, reg); + + return (0); +} + +static int +spi_transfer(device_t dev, device_t child, struct spi_command *cmd) +{ + struct spi_softc *sc; + uint32_t cs; + + sc = device_get_softc(dev); + + KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, + ("%s: TX/RX command sizes should be equal", __func__)); + KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, + ("%s: TX/RX data sizes should be equal", __func__)); + + /* get the proper chip select */ + spibus_get_cs(child, &cs); + + cs &= ~SPIBUS_CS_HIGH; + + /* Command */ + spi_txrx(sc, cmd->tx_cmd, cmd->rx_cmd, cmd->tx_cmd_sz, cs); + + /* Data */ + spi_txrx(sc, cmd->tx_data, cmd->rx_data, cmd->tx_data_sz, cs); + + return (0); +} + +static device_method_t spi_methods[] = { + DEVMETHOD(device_probe, spi_probe), + DEVMETHOD(device_attach, spi_attach), + + /* SPI interface */ + DEVMETHOD(spibus_transfer, spi_transfer), + + { 0, 0 } +}; + +static driver_t spi_driver = { + "spi", + spi_methods, + sizeof(struct spi_softc), +}; + +static devclass_t spi_devclass; + +DRIVER_MODULE(spi, simplebus, spi_driver, spi_devclass, 0, 0); Property changes on: trunk/sys/arm/samsung/exynos/exynos5_spi.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/samsung/exynos/exynos5_usb_phy.c =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_usb_phy.c (rev 0) +++ trunk/sys/arm/samsung/exynos/exynos5_usb_phy.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -0,0 +1,272 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2014 Ruslan Bukin
+ * 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. + */ + +/* + * DWC3 USB 3.0 DRD (dual role device) PHY + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_usb_phy.c 299069 2016-05-04 15:48:59Z pfg $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "gpio_if.h" + +#define USB_DRD_LINKSYSTEM 0x04 +#define LINKSYSTEM_FLADJ_MASK (0x3f << 1) +#define LINKSYSTEM_FLADJ(x) ((x) << 1) +#define LINKSYSTEM_XHCI_VERSION_CTRL (1 << 27) +#define USB_DRD_PHYUTMI 0x08 +#define PHYUTMI_OTGDISABLE (1 << 6) +#define PHYUTMI_FORCESUSPEND (1 << 1) +#define PHYUTMI_FORCESLEEP (1 << 0) +#define USB_DRD_PHYPIPE 0x0c +#define USB_DRD_PHYCLKRST 0x10 +#define PHYCLKRST_PORTRESET (1 << 1) +#define PHYCLKRST_COMMONONN (1 << 0) +#define PHYCLKRST_EN_UTMISUSPEND (1 << 31) +#define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23) +#define PHYCLKRST_SSC_REFCLKSEL(x) ((x) << 23) +#define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21) +#define PHYCLKRST_SSC_RANGE(x) ((x) << 21) +#define PHYCLKRST_SSC_EN (1 << 20) +#define PHYCLKRST_REF_SSP_EN (1 << 19) +#define PHYCLKRST_REF_CLKDIV2 (1 << 18) +#define PHYCLKRST_MPLL_MLTPR_MASK (0x7f << 11) +#define PHYCLKRST_MPLL_MLTPR_100MHZ (0x19 << 11) +#define PHYCLKRST_MPLL_MLTPR_50M (0x32 << 11) +#define PHYCLKRST_MPLL_MLTPR_24MHZ (0x68 << 11) +#define PHYCLKRST_MPLL_MLTPR_20MHZ (0x7d << 11) +#define PHYCLKRST_MPLL_MLTPR_19200KHZ (0x02 << 11) +#define PHYCLKRST_FSEL_UTMI_MASK (0x7 << 5) +#define PHYCLKRST_FSEL_PIPE_MASK (0x7 << 8) +#define PHYCLKRST_FSEL(x) ((x) << 5) +#define PHYCLKRST_FSEL_9MHZ6 0x0 +#define PHYCLKRST_FSEL_10MHZ 0x1 +#define PHYCLKRST_FSEL_12MHZ 0x2 +#define PHYCLKRST_FSEL_19MHZ2 0x3 +#define PHYCLKRST_FSEL_20MHZ 0x4 +#define PHYCLKRST_FSEL_24MHZ 0x5 +#define PHYCLKRST_FSEL_50MHZ 0x7 +#define PHYCLKRST_RETENABLEN (1 << 4) +#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2) +#define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2) +#define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2) +#define USB_DRD_PHYREG0 0x14 +#define USB_DRD_PHYREG1 0x18 +#define USB_DRD_PHYPARAM0 0x1c +#define PHYPARAM0_REF_USE_PAD (1 << 31) +#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26) +#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26) +#define USB_DRD_PHYPARAM1 0x20 +#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) +#define PHYPARAM1_PCS_TXDEEMPH (0x1c) +#define USB_DRD_PHYTERM 0x24 +#define USB_DRD_PHYTEST 0x28 +#define PHYTEST_POWERDOWN_SSP (1 << 3) +#define PHYTEST_POWERDOWN_HSP (1 << 2) +#define USB_DRD_PHYADP 0x2c +#define USB_DRD_PHYUTMICLKSEL 0x30 +#define PHYUTMICLKSEL_UTMI_CLKSEL (1 << 2) +#define USB_DRD_PHYRESUME 0x34 +#define USB_DRD_LINKPORT 0x44 + +struct usb_phy_softc { + struct resource *res[1]; + bus_space_tag_t bst; + bus_space_handle_t bsh; + device_t dev; +}; + +static struct resource_spec usb_phy_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static int +usb_phy_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "samsung,exynos5420-usbdrd-phy")) + return (ENXIO); + + device_set_desc(dev, "Samsung Exynos 5 USB PHY"); + return (BUS_PROBE_DEFAULT); +} + +static int +vbus_on(struct usb_phy_softc *sc) +{ + pcell_t dts_value[3]; + device_t gpio_dev; + phandle_t node; + pcell_t pin; + int len; + + if ((node = ofw_bus_get_node(sc->dev)) == -1) + return (-1); + + /* Power pin */ + if ((len = OF_getproplen(node, "vbus-supply")) <= 0) + return (-1); + OF_getencprop(node, "vbus-supply", dts_value, len); + pin = dts_value[0]; + + gpio_dev = devclass_get_device(devclass_find("gpio"), 0); + if (gpio_dev == NULL) { + device_printf(sc->dev, "can't find gpio_dev\n"); + return (1); + } + + GPIO_PIN_SETFLAGS(gpio_dev, pin, GPIO_PIN_OUTPUT); + GPIO_PIN_SET(gpio_dev, pin, GPIO_PIN_HIGH); + + return (0); +} + +static int +usb3_phy_init(struct usb_phy_softc *sc) +{ + int reg; + + /* Reset USB 3.0 PHY */ + WRITE4(sc, USB_DRD_PHYREG0, 0); + + reg = READ4(sc, USB_DRD_PHYPARAM0); + /* PHY CLK src */ + reg &= ~(PHYPARAM0_REF_USE_PAD); + reg &= ~(PHYPARAM0_REF_LOSLEVEL_MASK); + reg |= (PHYPARAM0_REF_LOSLEVEL); + WRITE4(sc, USB_DRD_PHYPARAM0, reg); + WRITE4(sc, USB_DRD_PHYRESUME, 0); + + reg = (LINKSYSTEM_XHCI_VERSION_CTRL | + LINKSYSTEM_FLADJ(0x20)); + WRITE4(sc, USB_DRD_LINKSYSTEM, reg); + + reg = READ4(sc, USB_DRD_PHYPARAM1); + reg &= ~(PHYPARAM1_PCS_TXDEEMPH_MASK); + reg |= (PHYPARAM1_PCS_TXDEEMPH); + WRITE4(sc, USB_DRD_PHYPARAM1, reg); + + reg = READ4(sc, USB_DRD_PHYUTMICLKSEL); + reg |= (PHYUTMICLKSEL_UTMI_CLKSEL); + WRITE4(sc, USB_DRD_PHYUTMICLKSEL, reg); + + reg = READ4(sc, USB_DRD_PHYTEST); + reg &= ~(PHYTEST_POWERDOWN_HSP); + reg &= ~(PHYTEST_POWERDOWN_SSP); + WRITE4(sc, USB_DRD_PHYTEST, reg); + + WRITE4(sc, USB_DRD_PHYUTMI, PHYUTMI_OTGDISABLE); + + /* Clock */ + reg = (PHYCLKRST_REFCLKSEL_EXT_REFCLK); + reg |= (PHYCLKRST_FSEL(PHYCLKRST_FSEL_24MHZ)); + reg |= (PHYCLKRST_MPLL_MLTPR_24MHZ); + reg |= (PHYCLKRST_SSC_REFCLKSEL(0x88)); + reg |= (PHYCLKRST_RETENABLEN | + PHYCLKRST_REF_SSP_EN | /* Super speed */ + PHYCLKRST_SSC_EN | /* Spread spectrum */ + PHYCLKRST_COMMONONN | + PHYCLKRST_PORTRESET); + + WRITE4(sc, USB_DRD_PHYCLKRST, reg); + DELAY(50000); + reg &= ~PHYCLKRST_PORTRESET; + WRITE4(sc, USB_DRD_PHYCLKRST, reg); + + return (0); +} + +static int +usb_phy_attach(device_t dev) +{ + struct usb_phy_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (bus_alloc_resources(dev, usb_phy_spec, sc->res)) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + /* Memory interface */ + sc->bst = rman_get_bustag(sc->res[0]); + sc->bsh = rman_get_bushandle(sc->res[0]); + + vbus_on(sc); + + usbdrd_phy_power_on(); + + DELAY(100); + + usb3_phy_init(sc); + + return (0); +} + +static device_method_t usb_phy_methods[] = { + DEVMETHOD(device_probe, usb_phy_probe), + DEVMETHOD(device_attach, usb_phy_attach), + { 0, 0 } +}; + +static driver_t usb_phy_driver = { + "usb_phy", + usb_phy_methods, + sizeof(struct usb_phy_softc), +}; + +static devclass_t usb_phy_devclass; + +DRIVER_MODULE(usb_phy, simplebus, usb_phy_driver, usb_phy_devclass, 0, 0); Property changes on: trunk/sys/arm/samsung/exynos/exynos5_usb_phy.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/samsung/exynos/exynos5_xhci.c =================================================================== --- trunk/sys/arm/samsung/exynos/exynos5_xhci.c (rev 0) +++ trunk/sys/arm/samsung/exynos/exynos5_xhci.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -0,0 +1,314 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2014 Ruslan Bukin
+ * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_xhci.c 308401 2016-11-07 08:36:06Z hselasky $"); + +#include "opt_bus.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "opt_platform.h" + +#define GSNPSID 0x20 +#define GSNPSID_MASK 0xffff0000 +#define REVISION_MASK 0xffff +#define GCTL 0x10 +#define GCTL_PWRDNSCALE(n) ((n) << 19) +#define GCTL_U2RSTECN (1 << 16) +#define GCTL_CLK_BUS (0) +#define GCTL_CLK_PIPE (1) +#define GCTL_CLK_PIPEHALF (2) +#define GCTL_CLK_M (3) +#define GCTL_CLK_S (6) +#define GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12) +#define GCTL_PRTCAPDIR(n) ((n) << 12) +#define GCTL_PRTCAP_HOST 1 +#define GCTL_PRTCAP_DEVICE 2 +#define GCTL_PRTCAP_OTG 3 +#define GCTL_CORESOFTRESET (1 << 11) +#define GCTL_SCALEDOWN_MASK 3 +#define GCTL_SCALEDOWN_SHIFT 4 +#define GCTL_DISSCRAMBLE (1 << 3) +#define GCTL_DSBLCLKGTNG (1 << 0) +#define GHWPARAMS1 0x3c +#define GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24) +#define GHWPARAMS1_EN_PWROPT_NO 0 +#define GHWPARAMS1_EN_PWROPT_CLK 1 +#define GUSB2PHYCFG(n) (0x100 + (n * 0x04)) +#define GUSB2PHYCFG_PHYSOFTRST (1 << 31) +#define GUSB2PHYCFG_SUSPHY (1 << 6) +#define GUSB3PIPECTL(n) (0x1c0 + (n * 0x04)) +#define GUSB3PIPECTL_PHYSOFTRST (1 << 31) +#define GUSB3PIPECTL_SUSPHY (1 << 17) + +/* Forward declarations */ +static device_attach_t exynos_xhci_attach; +static device_detach_t exynos_xhci_detach; +static device_probe_t exynos_xhci_probe; + +struct exynos_xhci_softc { + device_t dev; + struct xhci_softc base; + struct resource *res[3]; + bus_space_tag_t bst; + bus_space_handle_t bsh; +}; + +static struct resource_spec exynos_xhci_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_MEMORY, 1, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static device_method_t xhci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, exynos_xhci_probe), + DEVMETHOD(device_attach, exynos_xhci_attach), + DEVMETHOD(device_detach, exynos_xhci_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + DEVMETHOD_END +}; + +/* kobj_class definition */ +static driver_t xhci_driver = { + "xhci", + xhci_methods, + sizeof(struct xhci_softc) +}; + +static devclass_t xhci_devclass; + +DRIVER_MODULE(xhci, simplebus, xhci_driver, xhci_devclass, 0, 0); +MODULE_DEPEND(xhci, usb, 1, 1, 1); + +/* + * Public methods + */ +static int +exynos_xhci_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_is_compatible(dev, "samsung,exynos5250-dwusb3") == 0) + return (ENXIO); + + device_set_desc(dev, "Exynos USB 3.0 controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +dwc3_init(struct exynos_xhci_softc *esc) +{ + int hwparams1; + int rev; + int reg; + + rev = READ4(esc, GSNPSID); + if ((rev & GSNPSID_MASK) != 0x55330000) { + printf("It is not DWC3 controller\n"); + return (-1); + } + + /* Reset controller */ + WRITE4(esc, GCTL, GCTL_CORESOFTRESET); + WRITE4(esc, GUSB3PIPECTL(0), GUSB3PIPECTL_PHYSOFTRST); + WRITE4(esc, GUSB2PHYCFG(0), GUSB2PHYCFG_PHYSOFTRST); + + DELAY(100000); + + reg = READ4(esc, GUSB3PIPECTL(0)); + reg &= ~(GUSB3PIPECTL_PHYSOFTRST); + WRITE4(esc, GUSB3PIPECTL(0), reg); + + reg = READ4(esc, GUSB2PHYCFG(0)); + reg &= ~(GUSB2PHYCFG_PHYSOFTRST); + WRITE4(esc, GUSB2PHYCFG(0), reg); + + reg = READ4(esc, GCTL); + reg &= ~GCTL_CORESOFTRESET; + WRITE4(esc, GCTL, reg); + + hwparams1 = READ4(esc, GHWPARAMS1); + + reg = READ4(esc, GCTL); + reg &= ~(GCTL_SCALEDOWN_MASK << GCTL_SCALEDOWN_SHIFT); + reg &= ~(GCTL_DISSCRAMBLE); + + if (GHWPARAMS1_EN_PWROPT(hwparams1) == \ + GHWPARAMS1_EN_PWROPT_CLK) + reg &= ~(GCTL_DSBLCLKGTNG); + + if ((rev & REVISION_MASK) < 0x190a) + reg |= (GCTL_U2RSTECN); + WRITE4(esc, GCTL, reg); + + /* Set host mode */ + reg = READ4(esc, GCTL); + reg &= ~(GCTL_PRTCAPDIR(GCTL_PRTCAP_OTG)); + reg |= GCTL_PRTCAPDIR(GCTL_PRTCAP_HOST); + WRITE4(esc, GCTL, reg); + + return (0); +} + +static int +exynos_xhci_attach(device_t dev) +{ + struct exynos_xhci_softc *esc = device_get_softc(dev); + bus_space_handle_t bsh; + int err; + + esc->dev = dev; + + if (bus_alloc_resources(dev, exynos_xhci_spec, esc->res)) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + /* XHCI registers */ + esc->base.sc_io_tag = rman_get_bustag(esc->res[0]); + bsh = rman_get_bushandle(esc->res[0]); + esc->base.sc_io_size = rman_get_size(esc->res[0]); + + /* DWC3 ctrl registers */ + esc->bst = rman_get_bustag(esc->res[1]); + esc->bsh = rman_get_bushandle(esc->res[1]); + + /* + * Set handle to USB related registers subregion used by + * generic XHCI driver. + */ + err = bus_space_subregion(esc->base.sc_io_tag, bsh, 0x0, + esc->base.sc_io_size, &esc->base.sc_io_hdl); + if (err != 0) { + device_printf(dev, "Subregion failed\n"); + bus_release_resources(dev, exynos_xhci_spec, esc->res); + return (ENXIO); + } + + if (xhci_init(&esc->base, dev, 0)) { + device_printf(dev, "Could not initialize softc\n"); + bus_release_resources(dev, exynos_xhci_spec, esc->res); + return (ENXIO); + } + + /* Setup interrupt handler */ + err = bus_setup_intr(dev, esc->res[2], INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (driver_intr_t *)xhci_interrupt, &esc->base, + &esc->base.sc_intr_hdl); + if (err) { + device_printf(dev, "Could not setup irq, %d\n", err); + esc->base.sc_intr_hdl = NULL; + goto error; + } + + /* Add USB device */ + esc->base.sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (esc->base.sc_bus.bdev == NULL) { + device_printf(dev, "Could not add USB device\n"); + goto error; + } + device_set_ivars(esc->base.sc_bus.bdev, &esc->base.sc_bus); + strlcpy(esc->base.sc_vendor, "Samsung", sizeof(esc->base.sc_vendor)); + + dwc3_init(esc); + + err = xhci_halt_controller(&esc->base); + if (err == 0) { + device_printf(dev, "Starting controller\n"); + err = xhci_start_controller(&esc->base); + } + if (err == 0) { + device_printf(dev, "Controller started\n"); + err = device_probe_and_attach(esc->base.sc_bus.bdev); + } + if (err != 0) + goto error; + return (0); + +error: + exynos_xhci_detach(dev); + return (ENXIO); +} + +static int +exynos_xhci_detach(device_t dev) +{ + struct exynos_xhci_softc *esc = device_get_softc(dev); + int err; + + /* During module unload there are lots of children leftover */ + device_delete_children(dev); + + xhci_halt_controller(&esc->base); + + if (esc->res[2] && esc->base.sc_intr_hdl) { + err = bus_teardown_intr(dev, esc->res[2], + esc->base.sc_intr_hdl); + if (err) { + device_printf(dev, "Could not tear down IRQ," + " %d\n", err); + return (err); + } + } + + bus_release_resources(dev, exynos_xhci_spec, esc->res); + + xhci_uninit(&esc->base); + + return (0); +} Property changes on: trunk/sys/arm/samsung/exynos/exynos5_xhci.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Modified: trunk/sys/arm/samsung/exynos/exynos_uart.c =================================================================== --- trunk/sys/arm/samsung/exynos/exynos_uart.c 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/exynos_uart.c 2020-03-06 17:05:09 UTC (rev 12395) @@ -28,7 +28,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/samsung/exynos/exynos_uart.c 283327 2015-05-23 20:54:25Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos_uart.c 356020 2019-12-22 19:06:45Z kevans $"); #include #include @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -381,6 +380,7 @@ .uc_ops = &uart_exynos4210_ops, .uc_range = 8, .uc_rclk = 0, + .uc_rshift = 0 }; static struct ofw_compat_data compat_data[] = { Modified: trunk/sys/arm/samsung/exynos/exynos_uart.h =================================================================== --- trunk/sys/arm/samsung/exynos/exynos_uart.h 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/exynos_uart.h 2020-03-06 17:05:09 UTC (rev 12395) @@ -32,7 +32,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/samsung/exynos/exynos_uart.h 283322 2015-05-23 19:50:14Z ian $ + * $FreeBSD: stable/11/sys/arm/samsung/exynos/exynos_uart.h 266944 2014-06-01 08:34:45Z br $ */ /* s3c2410-specific registers */ Modified: trunk/sys/arm/samsung/exynos/files.exynos5 =================================================================== --- trunk/sys/arm/samsung/exynos/files.exynos5 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/files.exynos5 2020-03-06 17:05:09 UTC (rev 12395) @@ -1,18 +1,7 @@ -# $FreeBSD: stable/10/sys/arm/samsung/exynos/files.exynos5 283322 2015-05-23 19:50:14Z ian $ +# $FreeBSD: stable/11/sys/arm/samsung/exynos/files.exynos5 291135 2015-11-21 16:23:56Z andrew $ kern/kern_clocksource.c standard -arm/arm/bus_space_generic.c standard -arm/arm/bus_space_asm_generic.S standard -arm/arm/cpufunc_asm_armv5.S standard -arm/arm/cpufunc_asm_arm10.S standard -arm/arm/cpufunc_asm_arm11.S standard -arm/arm/cpufunc_asm_armv7.S standard - -arm/arm/bus_space_base.c standard -arm/arm/gic.c standard -arm/arm/generic_timer.c standard - arm/samsung/exynos/exynos5_mct.c standard arm/samsung/exynos/exynos5_mp.c optional smp arm/samsung/exynos/exynos5_common.c standard @@ -21,11 +10,16 @@ arm/samsung/exynos/exynos5_pad.c optional gpio arm/samsung/exynos/exynos_uart.c optional uart arm/samsung/exynos/exynos5_ehci.c optional ehci +arm/samsung/exynos/exynos5_xhci.c optional xhci arm/samsung/exynos/exynos5_fimd.c optional vt arm/samsung/exynos/exynos5_i2c.c optional iicbus +arm/samsung/exynos/exynos5_usb_phy.c standard +arm/samsung/exynos/exynos5_pmu.c standard +arm/samsung/exynos/exynos5_spi.c optional exynos_spi # chromebook drivers -arm/samsung/exynos/chrome_ec.c optional chrome_ec +arm/samsung/exynos/chrome_ec.c optional chrome_ec_i2c +arm/samsung/exynos/chrome_ec_spi.c optional chrome_ec_spi arm/samsung/exynos/chrome_kb.c optional chrome_kb -#dev/sdhci/sdhci_fdt.c optional sdhci +dev/mmc/host/dwmmc.c optional dwmmc Modified: trunk/sys/arm/samsung/exynos/std.exynos5250 =================================================================== --- trunk/sys/arm/samsung/exynos/std.exynos5250 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/std.exynos5250 2020-03-06 17:05:09 UTC (rev 12395) @@ -1,21 +1,9 @@ -# $FreeBSD: stable/10/sys/arm/samsung/exynos/std.exynos5250 278601 2015-02-11 22:47:48Z ian $ +# $FreeBSD: stable/11/sys/arm/samsung/exynos/std.exynos5250 327658 2018-01-07 00:04:13Z ian $ -makeoption ARM_LITTLE_ENDIAN - cpu CPU_CORTEXA machine arm armv6 -makeoptions CONF_CFLAGS="-march=armv7a -Wa,-march=armv7a" +makeoptions CONF_CFLAGS="-march=armv7a" -options PHYSADDR=0x40000000 - -makeoptions KERNPHYSADDR=0x40f00000 -options KERNPHYSADDR=0x40f00000 - -makeoptions KERNVIRTADDR=0xc0f00000 -options KERNVIRTADDR=0xc0f00000 - -options ARM_L2_PIPT - options IPI_IRQ_START=0 options IPI_IRQ_END=15 Modified: trunk/sys/arm/samsung/exynos/std.exynos5420 =================================================================== --- trunk/sys/arm/samsung/exynos/std.exynos5420 2020-03-06 16:44:57 UTC (rev 12394) +++ trunk/sys/arm/samsung/exynos/std.exynos5420 2020-03-06 17:05:09 UTC (rev 12395) @@ -1,21 +1,9 @@ -# $FreeBSD: stable/10/sys/arm/samsung/exynos/std.exynos5420 278601 2015-02-11 22:47:48Z ian $ +# $FreeBSD: stable/11/sys/arm/samsung/exynos/std.exynos5420 327658 2018-01-07 00:04:13Z ian $ -makeoption ARM_LITTLE_ENDIAN - cpu CPU_CORTEXA machine arm armv6 -makeoptions CONF_CFLAGS="-march=armv7a -Wa,-march=armv7a" +makeoptions CONF_CFLAGS="-march=armv7a" -options PHYSADDR=0x20000000 - -makeoptions KERNPHYSADDR=0x20f00000 -options KERNPHYSADDR=0x20f00000 - -makeoptions KERNVIRTADDR=0xc0f00000 -options KERNVIRTADDR=0xc0f00000 - -options ARM_L2_PIPT - options IPI_IRQ_START=0 options IPI_IRQ_END=15 From laffer1 at midnightbsd.org Fri Mar 6 12:08:20 2020 From: laffer1 at midnightbsd.org (laffer1 at midnightbsd.org) Date: Fri, 6 Mar 2020 12:08:20 -0500 (EST) Subject: [Midnightbsd-cvs] src [12396] trunk/sys/arm/nvidia: sync with freebsd 11 Message-ID: <202003061708.026H8K2X013698@stargazer.midnightbsd.org> Revision: 12396 http://svnweb.midnightbsd.org/src/?rev=12396 Author: laffer1 Date: 2020-03-06 12:08:20 -0500 (Fri, 06 Mar 2020) Log Message: ----------- sync with freebsd 11 Added Paths: ----------- trunk/sys/arm/nvidia/ trunk/sys/arm/nvidia/as3722.c trunk/sys/arm/nvidia/as3722.h trunk/sys/arm/nvidia/as3722_gpio.c trunk/sys/arm/nvidia/as3722_regulators.c trunk/sys/arm/nvidia/as3722_rtc.c trunk/sys/arm/nvidia/drm2/ trunk/sys/arm/nvidia/drm2/hdmi.c trunk/sys/arm/nvidia/drm2/hdmi.h trunk/sys/arm/nvidia/drm2/tegra_bo.c trunk/sys/arm/nvidia/drm2/tegra_dc.c trunk/sys/arm/nvidia/drm2/tegra_dc_if.m trunk/sys/arm/nvidia/drm2/tegra_dc_reg.h trunk/sys/arm/nvidia/drm2/tegra_drm.h trunk/sys/arm/nvidia/drm2/tegra_drm_if.m trunk/sys/arm/nvidia/drm2/tegra_drm_subr.c trunk/sys/arm/nvidia/drm2/tegra_fb.c trunk/sys/arm/nvidia/drm2/tegra_hdmi.c trunk/sys/arm/nvidia/drm2/tegra_hdmi_reg.h trunk/sys/arm/nvidia/drm2/tegra_host1x.c trunk/sys/arm/nvidia/tegra124/ trunk/sys/arm/nvidia/tegra124/files.tegra124 trunk/sys/arm/nvidia/tegra124/std.tegra124 trunk/sys/arm/nvidia/tegra124/tegra124_car.c trunk/sys/arm/nvidia/tegra124/tegra124_car.h trunk/sys/arm/nvidia/tegra124/tegra124_clk_per.c trunk/sys/arm/nvidia/tegra124/tegra124_clk_pll.c trunk/sys/arm/nvidia/tegra124/tegra124_clk_super.c trunk/sys/arm/nvidia/tegra124/tegra124_coretemp.c trunk/sys/arm/nvidia/tegra124/tegra124_cpufreq.c trunk/sys/arm/nvidia/tegra124/tegra124_machdep.c trunk/sys/arm/nvidia/tegra124/tegra124_mp.c trunk/sys/arm/nvidia/tegra124/tegra124_mp.h trunk/sys/arm/nvidia/tegra124/tegra124_pmc.c trunk/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c trunk/sys/arm/nvidia/tegra_abpmisc.c trunk/sys/arm/nvidia/tegra_ahci.c trunk/sys/arm/nvidia/tegra_efuse.c trunk/sys/arm/nvidia/tegra_efuse.h trunk/sys/arm/nvidia/tegra_ehci.c trunk/sys/arm/nvidia/tegra_gpio.c trunk/sys/arm/nvidia/tegra_i2c.c trunk/sys/arm/nvidia/tegra_lic.c trunk/sys/arm/nvidia/tegra_mc.c trunk/sys/arm/nvidia/tegra_pcie.c trunk/sys/arm/nvidia/tegra_pinmux.c trunk/sys/arm/nvidia/tegra_pmc.h trunk/sys/arm/nvidia/tegra_rtc.c trunk/sys/arm/nvidia/tegra_sdhci.c trunk/sys/arm/nvidia/tegra_soctherm.c trunk/sys/arm/nvidia/tegra_soctherm_if.m trunk/sys/arm/nvidia/tegra_uart.c trunk/sys/arm/nvidia/tegra_usbphy.c trunk/sys/arm/nvidia/tegra_xhci.c Added: trunk/sys/arm/nvidia/as3722.c =================================================================== --- trunk/sys/arm/nvidia/as3722.c (rev 0) +++ trunk/sys/arm/nvidia/as3722.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,412 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/as3722.c 308335 2016-11-05 10:56:32Z mmel $"); + +/* + * AS3722 PMIC driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#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; +static DEFINE_CLASS_0(gpio, as3722_driver, as3722_methods, + sizeof(struct as3722_softc)); +EARLY_DRIVER_MODULE(as3722, iicbus, as3722_driver, as3722_devclass, + NULL, NULL, 74); Property changes on: trunk/sys/arm/nvidia/as3722.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/as3722.h =================================================================== --- trunk/sys/arm/nvidia/as3722.h (rev 0) +++ trunk/sys/arm/nvidia/as3722.h 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,324 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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. + * + * $FreeBSD: stable/11/sys/arm/nvidia/as3722.h 296936 2016-03-16 13:01:48Z mmel $ + */ + +#ifndef _AS3722_H_ + +#include + +#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_ */ Property changes on: trunk/sys/arm/nvidia/as3722.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/as3722_gpio.c =================================================================== --- trunk/sys/arm/nvidia/as3722_gpio.c (rev 0) +++ trunk/sys/arm/nvidia/as3722_gpio.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,578 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/as3722_gpio.c 299715 2016-05-14 05:00:17Z gonzo $"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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) + OF_prop_free(pins); + if (cfg.function != NULL) + OF_prop_free(cfg.function); + + 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 <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); +} Property changes on: trunk/sys/arm/nvidia/as3722_gpio.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/as3722_regulators.c =================================================================== --- trunk/sys/arm/nvidia/as3722_regulators.c (rev 0) +++ trunk/sys/arm/nvidia/as3722_regulators.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,718 @@ +/* $MidnightBSD$ */ +/*- + * Copyright 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/as3722_regulators.c 308328 2016-11-05 04:40:58Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include "as3722.h" + +MALLOC_DEFINE(M_AS3722_REG, "AS3722 regulator", "AS3722 power regulator"); + +#define DIV_ROUND_UP(n,d) howmany(n, 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, +}; + + +/* 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; +}; + +static struct regulator_range as3722_sd016_ranges[] = { + REG_RANGE_INIT(0x00, 0x00, 0, 0), + REG_RANGE_INIT(0x01, 0x5A, 610000, 10000), +}; + +static struct regulator_range as3722_sd0_lv_ranges[] = { + REG_RANGE_INIT(0x00, 0x00, 0, 0), + REG_RANGE_INIT(0x01, 0x6E, 410000, 10000), +}; + +static struct regulator_range as3722_sd_ranges[] = { + REG_RANGE_INIT(0x00, 0x00, 0, 0), + REG_RANGE_INIT(0x01, 0x40, 612500, 12500), + REG_RANGE_INIT(0x41, 0x70, 1425000, 25000), + REG_RANGE_INIT(0x71, 0x7F, 2650000, 50000), +}; + +static struct regulator_range as3722_ldo3_ranges[] = { + REG_RANGE_INIT(0x00, 0x00, 0, 0), + REG_RANGE_INIT(0x01, 0x2D, 620000, 20000), +}; + +static struct regulator_range as3722_ldo_ranges[] = { + REG_RANGE_INIT(0x00, 0x00, 0, 0), + REG_RANGE_INIT(0x01, 0x24, 825000, 25000), + REG_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 +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_sel8(sc->def->ranges, sc->def->nranges, + 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_sel8_to_volt(sc->def->ranges, sc->def->nranges, + sel, uvolt); + return (rv); +} Property changes on: trunk/sys/arm/nvidia/as3722_regulators.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/as3722_rtc.c =================================================================== --- trunk/sys/arm/nvidia/as3722_rtc.c (rev 0) +++ trunk/sys/arm/nvidia/as3722_rtc.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,116 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/as3722_rtc.c 296936 2016-03-16 13:01:48Z mmel $"); + +#include +#include +#include +#include +#include + +#include + +#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); +} Property changes on: trunk/sys/arm/nvidia/as3722_rtc.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/drm2/hdmi.c =================================================================== --- trunk/sys/arm/nvidia/drm2/hdmi.c (rev 0) +++ trunk/sys/arm/nvidia/drm2/hdmi.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,1230 @@ +/* $MidnightBSD$ */ +/* + * Copyright (C) 2012 Avionic Design GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/drm2/hdmi.c 310600 2016-12-26 14:36:05Z mmel $"); + +#include +#include + +#include + +#define EXPORT_SYMBOL(x) +#ifndef BIT +#define BIT(x) (1U << (x)) +#endif +#define hdmi_log(fmt, ...) printf(fmt, ##__VA_ARGS__) + +static uint8_t hdmi_infoframe_checksum(uint8_t *ptr, size_t size) +{ + uint8_t csum = 0; + size_t i; + + /* compute checksum */ + for (i = 0; i < size; i++) + csum += ptr[i]; + + return 256 - csum; +} + +static void hdmi_infoframe_set_checksum(void *buffer, size_t size) +{ + uint8_t *ptr = buffer; + + ptr[3] = hdmi_infoframe_checksum(buffer, size); +} + +/** + * hdmi_avi_infoframe_init() - initialize an HDMI AVI infoframe + * @frame: HDMI AVI infoframe + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame) +{ + memset(frame, 0, sizeof(*frame)); + + frame->type = HDMI_INFOFRAME_TYPE_AVI; + frame->version = 2; + frame->length = HDMI_AVI_INFOFRAME_SIZE; + + return 0; +} +EXPORT_SYMBOL(hdmi_avi_infoframe_init); + +/** + * hdmi_avi_infoframe_pack() - write HDMI AVI infoframe to binary buffer + * @frame: HDMI AVI infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Packs the information contained in the @frame structure into a binary + * representation that can be written into the corresponding controller + * registers. Also computes the checksum as required by section 5.3.5 of + * the HDMI 1.4 specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, + size_t size) +{ + uint8_t *ptr = buffer; + size_t length; + + length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; + + if (size < length) + return -ENOSPC; + + memset(buffer, 0, size); + + ptr[0] = frame->type; + ptr[1] = frame->version; + ptr[2] = frame->length; + ptr[3] = 0; /* checksum */ + + /* start infoframe payload */ + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + ptr[0] = ((frame->colorspace & 0x3) << 5) | (frame->scan_mode & 0x3); + + /* + * Data byte 1, bit 4 has to be set if we provide the active format + * aspect ratio + */ + if (frame->active_aspect & 0xf) + ptr[0] |= BIT(4); + + /* Bit 3 and 2 indicate if we transmit horizontal/vertical bar data */ + if (frame->top_bar || frame->bottom_bar) + ptr[0] |= BIT(3); + + if (frame->left_bar || frame->right_bar) + ptr[0] |= BIT(2); + + ptr[1] = ((frame->colorimetry & 0x3) << 6) | + ((frame->picture_aspect & 0x3) << 4) | + (frame->active_aspect & 0xf); + + ptr[2] = ((frame->extended_colorimetry & 0x7) << 4) | + ((frame->quantization_range & 0x3) << 2) | + (frame->nups & 0x3); + + if (frame->itc) + ptr[2] |= BIT(7); + + ptr[3] = frame->video_code & 0x7f; + + ptr[4] = ((frame->ycc_quantization_range & 0x3) << 6) | + ((frame->content_type & 0x3) << 4) | + (frame->pixel_repeat & 0xf); + + ptr[5] = frame->top_bar & 0xff; + ptr[6] = (frame->top_bar >> 8) & 0xff; + ptr[7] = frame->bottom_bar & 0xff; + ptr[8] = (frame->bottom_bar >> 8) & 0xff; + ptr[9] = frame->left_bar & 0xff; + ptr[10] = (frame->left_bar >> 8) & 0xff; + ptr[11] = frame->right_bar & 0xff; + ptr[12] = (frame->right_bar >> 8) & 0xff; + + hdmi_infoframe_set_checksum(buffer, length); + + return length; +} +EXPORT_SYMBOL(hdmi_avi_infoframe_pack); + +/** + * hdmi_spd_infoframe_init() - initialize an HDMI SPD infoframe + * @frame: HDMI SPD infoframe + * @vendor: vendor string + * @product: product string + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame, + const char *vendor, const char *product) +{ + memset(frame, 0, sizeof(*frame)); + + frame->type = HDMI_INFOFRAME_TYPE_SPD; + frame->version = 1; + frame->length = HDMI_SPD_INFOFRAME_SIZE; + + strncpy(frame->vendor, vendor, sizeof(frame->vendor)); + strncpy(frame->product, product, sizeof(frame->product)); + + return 0; +} +EXPORT_SYMBOL(hdmi_spd_infoframe_init); + +/** + * hdmi_spd_infoframe_pack() - write HDMI SPD infoframe to binary buffer + * @frame: HDMI SPD infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Packs the information contained in the @frame structure into a binary + * representation that can be written into the corresponding controller + * registers. Also computes the checksum as required by section 5.3.5 of + * the HDMI 1.4 specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer, + size_t size) +{ + uint8_t *ptr = buffer; + size_t length; + + length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; + + if (size < length) + return -ENOSPC; + + memset(buffer, 0, size); + + ptr[0] = frame->type; + ptr[1] = frame->version; + ptr[2] = frame->length; + ptr[3] = 0; /* checksum */ + + /* start infoframe payload */ + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + memcpy(ptr, frame->vendor, sizeof(frame->vendor)); + memcpy(ptr + 8, frame->product, sizeof(frame->product)); + + ptr[24] = frame->sdi; + + hdmi_infoframe_set_checksum(buffer, length); + + return length; +} +EXPORT_SYMBOL(hdmi_spd_infoframe_pack); + +/** + * hdmi_audio_infoframe_init() - initialize an HDMI audio infoframe + * @frame: HDMI audio infoframe + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame) +{ + memset(frame, 0, sizeof(*frame)); + + frame->type = HDMI_INFOFRAME_TYPE_AUDIO; + frame->version = 1; + frame->length = HDMI_AUDIO_INFOFRAME_SIZE; + + return 0; +} +EXPORT_SYMBOL(hdmi_audio_infoframe_init); + +/** + * hdmi_audio_infoframe_pack() - write HDMI audio infoframe to binary buffer + * @frame: HDMI audio infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Packs the information contained in the @frame structure into a binary + * representation that can be written into the corresponding controller + * registers. Also computes the checksum as required by section 5.3.5 of + * the HDMI 1.4 specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, + void *buffer, size_t size) +{ + unsigned char channels; + uint8_t *ptr = buffer; + size_t length; + + length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; + + if (size < length) + return -ENOSPC; + + memset(buffer, 0, size); + + if (frame->channels >= 2) + channels = frame->channels - 1; + else + channels = 0; + + ptr[0] = frame->type; + ptr[1] = frame->version; + ptr[2] = frame->length; + ptr[3] = 0; /* checksum */ + + /* start infoframe payload */ + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + ptr[0] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7); + ptr[1] = ((frame->sample_frequency & 0x7) << 2) | + (frame->sample_size & 0x3); + ptr[2] = frame->coding_type_ext & 0x1f; + ptr[3] = frame->channel_allocation; + ptr[4] = (frame->level_shift_value & 0xf) << 3; + + if (frame->downmix_inhibit) + ptr[4] |= BIT(7); + + hdmi_infoframe_set_checksum(buffer, length); + + return length; +} +EXPORT_SYMBOL(hdmi_audio_infoframe_pack); + +/** + * hdmi_vendor_infoframe_init() - initialize an HDMI vendor infoframe + * @frame: HDMI vendor infoframe + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame) +{ + memset(frame, 0, sizeof(*frame)); + + frame->type = HDMI_INFOFRAME_TYPE_VENDOR; + frame->version = 1; + + frame->oui = HDMI_IEEE_OUI; + + /* + * 0 is a valid value for s3d_struct, so we use a special "not set" + * value + */ + frame->s3d_struct = HDMI_3D_STRUCTURE_INVALID; + + return 0; +} +EXPORT_SYMBOL(hdmi_vendor_infoframe_init); + +/** + * hdmi_vendor_infoframe_pack() - write a HDMI vendor infoframe to binary buffer + * @frame: HDMI infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Packs the information contained in the @frame structure into a binary + * representation that can be written into the corresponding controller + * registers. Also computes the checksum as required by section 5.3.5 of + * the HDMI 1.4 specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, + void *buffer, size_t size) +{ + uint8_t *ptr = buffer; + size_t length; + + /* empty info frame */ + if (frame->vic == 0 && frame->s3d_struct == HDMI_3D_STRUCTURE_INVALID) + return -EINVAL; + + /* only one of those can be supplied */ + if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID) + return -EINVAL; + + /* for side by side (half) we also need to provide 3D_Ext_Data */ + if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) + frame->length = 6; + else + frame->length = 5; + + length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; + + if (size < length) + return -ENOSPC; + + memset(buffer, 0, size); + + ptr[0] = frame->type; + ptr[1] = frame->version; + ptr[2] = frame->length; + ptr[3] = 0; /* checksum */ + + /* HDMI OUI */ + ptr[4] = 0x03; + ptr[5] = 0x0c; + ptr[6] = 0x00; + + if (frame->vic) { + ptr[7] = 0x1 << 5; /* video format */ + ptr[8] = frame->vic; + } else { + ptr[7] = 0x2 << 5; /* video format */ + ptr[8] = (frame->s3d_struct & 0xf) << 4; + if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) + ptr[9] = (frame->s3d_ext_data & 0xf) << 4; + } + + hdmi_infoframe_set_checksum(buffer, length); + + return length; +} +EXPORT_SYMBOL(hdmi_vendor_infoframe_pack); + +/* + * hdmi_vendor_any_infoframe_pack() - write a vendor infoframe to binary buffer + */ +static ssize_t +hdmi_vendor_any_infoframe_pack(union hdmi_vendor_any_infoframe *frame, + void *buffer, size_t size) +{ + /* we only know about HDMI vendor infoframes */ + if (frame->any.oui != HDMI_IEEE_OUI) + return -EINVAL; + + return hdmi_vendor_infoframe_pack(&frame->hdmi, buffer, size); +} + +/** + * hdmi_infoframe_pack() - write a HDMI infoframe to binary buffer + * @frame: HDMI infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Packs the information contained in the @frame structure into a binary + * representation that can be written into the corresponding controller + * registers. Also computes the checksum as required by section 5.3.5 of + * the HDMI 1.4 specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t +hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, size_t size) +{ + ssize_t length; + + switch (frame->any.type) { + case HDMI_INFOFRAME_TYPE_AVI: + length = hdmi_avi_infoframe_pack(&frame->avi, buffer, size); + break; + case HDMI_INFOFRAME_TYPE_SPD: + length = hdmi_spd_infoframe_pack(&frame->spd, buffer, size); + break; + case HDMI_INFOFRAME_TYPE_AUDIO: + length = hdmi_audio_infoframe_pack(&frame->audio, buffer, size); + break; + case HDMI_INFOFRAME_TYPE_VENDOR: + length = hdmi_vendor_any_infoframe_pack(&frame->vendor, + buffer, size); + break; + default: + printf("Bad infoframe type %d\n", frame->any.type); + length = -EINVAL; + } + + return length; +} +EXPORT_SYMBOL(hdmi_infoframe_pack); + +static const char *hdmi_infoframe_type_get_name(enum hdmi_infoframe_type type) +{ + if (type < 0x80 || type > 0x9f) + return "Invalid"; + switch (type) { + case HDMI_INFOFRAME_TYPE_VENDOR: + return "Vendor"; + case HDMI_INFOFRAME_TYPE_AVI: + return "Auxiliary Video Information (AVI)"; + case HDMI_INFOFRAME_TYPE_SPD: + return "Source Product Description (SPD)"; + case HDMI_INFOFRAME_TYPE_AUDIO: + return "Audio"; + } + return "Reserved"; +} + +static void hdmi_infoframe_log_header(struct hdmi_any_infoframe *frame) +{ + hdmi_log("HDMI infoframe: %s, version %u, length %u\n", + hdmi_infoframe_type_get_name(frame->type), + frame->version, frame->length); +} + +static const char *hdmi_colorspace_get_name(enum hdmi_colorspace colorspace) +{ + switch (colorspace) { + case HDMI_COLORSPACE_RGB: + return "RGB"; + case HDMI_COLORSPACE_YUV422: + return "YCbCr 4:2:2"; + case HDMI_COLORSPACE_YUV444: + return "YCbCr 4:4:4"; + case HDMI_COLORSPACE_YUV420: + return "YCbCr 4:2:0"; + case HDMI_COLORSPACE_RESERVED4: + return "Reserved (4)"; + case HDMI_COLORSPACE_RESERVED5: + return "Reserved (5)"; + case HDMI_COLORSPACE_RESERVED6: + return "Reserved (6)"; + case HDMI_COLORSPACE_IDO_DEFINED: + return "IDO Defined"; + } + return "Invalid"; +} + +static const char *hdmi_scan_mode_get_name(enum hdmi_scan_mode scan_mode) +{ + switch (scan_mode) { + case HDMI_SCAN_MODE_NONE: + return "No Data"; + case HDMI_SCAN_MODE_OVERSCAN: + return "Overscan"; + case HDMI_SCAN_MODE_UNDERSCAN: + return "Underscan"; + case HDMI_SCAN_MODE_RESERVED: + return "Reserved"; + } + return "Invalid"; +} + +static const char *hdmi_colorimetry_get_name(enum hdmi_colorimetry colorimetry) +{ + switch (colorimetry) { + case HDMI_COLORIMETRY_NONE: + return "No Data"; + case HDMI_COLORIMETRY_ITU_601: + return "ITU601"; + case HDMI_COLORIMETRY_ITU_709: + return "ITU709"; + case HDMI_COLORIMETRY_EXTENDED: + return "Extended"; + } + return "Invalid"; +} + +static const char * +hdmi_picture_aspect_get_name(enum hdmi_picture_aspect picture_aspect) +{ + switch (picture_aspect) { + case HDMI_PICTURE_ASPECT_NONE: + return "No Data"; + case HDMI_PICTURE_ASPECT_4_3: + return "4:3"; + case HDMI_PICTURE_ASPECT_16_9: + return "16:9"; + case HDMI_PICTURE_ASPECT_RESERVED: + return "Reserved"; + } + return "Invalid"; +} + +static const char * +hdmi_active_aspect_get_name(enum hdmi_active_aspect active_aspect) +{ + if (active_aspect > 0xf) + return "Invalid"; + + switch (active_aspect) { + case HDMI_ACTIVE_ASPECT_16_9_TOP: + return "16:9 Top"; + case HDMI_ACTIVE_ASPECT_14_9_TOP: + return "14:9 Top"; + case HDMI_ACTIVE_ASPECT_16_9_CENTER: + return "16:9 Center"; + case HDMI_ACTIVE_ASPECT_PICTURE: + return "Same as Picture"; + case HDMI_ACTIVE_ASPECT_4_3: + return "4:3"; + case HDMI_ACTIVE_ASPECT_16_9: + return "16:9"; + case HDMI_ACTIVE_ASPECT_14_9: + return "14:9"; + case HDMI_ACTIVE_ASPECT_4_3_SP_14_9: + return "4:3 SP 14:9"; + case HDMI_ACTIVE_ASPECT_16_9_SP_14_9: + return "16:9 SP 14:9"; + case HDMI_ACTIVE_ASPECT_16_9_SP_4_3: + return "16:9 SP 4:3"; + } + return "Reserved"; +} + +static const char * +hdmi_extended_colorimetry_get_name(enum hdmi_extended_colorimetry ext_col) +{ + switch (ext_col) { + case HDMI_EXTENDED_COLORIMETRY_XV_YCC_601: + return "xvYCC 601"; + case HDMI_EXTENDED_COLORIMETRY_XV_YCC_709: + return "xvYCC 709"; + case HDMI_EXTENDED_COLORIMETRY_S_YCC_601: + return "sYCC 601"; + case HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601: + return "Adobe YCC 601"; + case HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB: + return "Adobe RGB"; + case HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM: + return "BT.2020 Constant Luminance"; + case HDMI_EXTENDED_COLORIMETRY_BT2020: + return "BT.2020"; + case HDMI_EXTENDED_COLORIMETRY_RESERVED: + return "Reserved"; + } + return "Invalid"; +} + +static const char * +hdmi_quantization_range_get_name(enum hdmi_quantization_range qrange) +{ + switch (qrange) { + case HDMI_QUANTIZATION_RANGE_DEFAULT: + return "Default"; + case HDMI_QUANTIZATION_RANGE_LIMITED: + return "Limited"; + case HDMI_QUANTIZATION_RANGE_FULL: + return "Full"; + case HDMI_QUANTIZATION_RANGE_RESERVED: + return "Reserved"; + } + return "Invalid"; +} + +static const char *hdmi_nups_get_name(enum hdmi_nups nups) +{ + switch (nups) { + case HDMI_NUPS_UNKNOWN: + return "Unknown Non-uniform Scaling"; + case HDMI_NUPS_HORIZONTAL: + return "Horizontally Scaled"; + case HDMI_NUPS_VERTICAL: + return "Vertically Scaled"; + case HDMI_NUPS_BOTH: + return "Horizontally and Vertically Scaled"; + } + return "Invalid"; +} + +static const char * +hdmi_ycc_quantization_range_get_name(enum hdmi_ycc_quantization_range qrange) +{ + switch (qrange) { + case HDMI_YCC_QUANTIZATION_RANGE_LIMITED: + return "Limited"; + case HDMI_YCC_QUANTIZATION_RANGE_FULL: + return "Full"; + } + return "Invalid"; +} + +static const char * +hdmi_content_type_get_name(enum hdmi_content_type content_type) +{ + switch (content_type) { + case HDMI_CONTENT_TYPE_GRAPHICS: + return "Graphics"; + case HDMI_CONTENT_TYPE_PHOTO: + return "Photo"; + case HDMI_CONTENT_TYPE_CINEMA: + return "Cinema"; + case HDMI_CONTENT_TYPE_GAME: + return "Game"; + } + return "Invalid"; +} + +/** + * hdmi_avi_infoframe_log() - log info of HDMI AVI infoframe + * @level: logging level + * @dev: device + * @frame: HDMI AVI infoframe + */ +static void hdmi_avi_infoframe_log(struct hdmi_avi_infoframe *frame) +{ + hdmi_infoframe_log_header((struct hdmi_any_infoframe *)frame); + + hdmi_log(" colorspace: %s\n", + hdmi_colorspace_get_name(frame->colorspace)); + hdmi_log(" scan mode: %s\n", + hdmi_scan_mode_get_name(frame->scan_mode)); + hdmi_log(" colorimetry: %s\n", + hdmi_colorimetry_get_name(frame->colorimetry)); + hdmi_log(" picture aspect: %s\n", + hdmi_picture_aspect_get_name(frame->picture_aspect)); + hdmi_log(" active aspect: %s\n", + hdmi_active_aspect_get_name(frame->active_aspect)); + hdmi_log(" itc: %s\n", frame->itc ? "IT Content" : "No Data"); + hdmi_log(" extended colorimetry: %s\n", + hdmi_extended_colorimetry_get_name(frame->extended_colorimetry)); + hdmi_log(" quantization range: %s\n", + hdmi_quantization_range_get_name(frame->quantization_range)); + hdmi_log(" nups: %s\n", hdmi_nups_get_name(frame->nups)); + hdmi_log(" video code: %u\n", frame->video_code); + hdmi_log(" ycc quantization range: %s\n", + hdmi_ycc_quantization_range_get_name(frame->ycc_quantization_range)); + hdmi_log(" hdmi content type: %s\n", + hdmi_content_type_get_name(frame->content_type)); + hdmi_log(" pixel repeat: %u\n", frame->pixel_repeat); + hdmi_log(" bar top %u, bottom %u, left %u, right %u\n", + frame->top_bar, frame->bottom_bar, + frame->left_bar, frame->right_bar); +} + +static const char *hdmi_spd_sdi_get_name(enum hdmi_spd_sdi sdi) +{ +; + switch (sdi) { + case HDMI_SPD_SDI_UNKNOWN: + return "Unknown"; + case HDMI_SPD_SDI_DSTB: + return "Digital STB"; + case HDMI_SPD_SDI_DVDP: + return "DVD Player"; + case HDMI_SPD_SDI_DVHS: + return "D-VHS"; + case HDMI_SPD_SDI_HDDVR: + return "HDD Videorecorder"; + case HDMI_SPD_SDI_DVC: + return "DVC"; + case HDMI_SPD_SDI_DSC: + return "DSC"; + case HDMI_SPD_SDI_VCD: + return "Video CD"; + case HDMI_SPD_SDI_GAME: + return "Game"; + case HDMI_SPD_SDI_PC: + return "PC General"; + case HDMI_SPD_SDI_BD: + return "Blu-Ray Disc (BD)"; + case HDMI_SPD_SDI_SACD: + return "Super Audio CD"; + case HDMI_SPD_SDI_HDDVD: + return "HD DVD"; + case HDMI_SPD_SDI_PMP: + return "PMP"; + } + return "Reserved"; +} + +/** + * hdmi_spd_infoframe_log() - log info of HDMI SPD infoframe + * @level: logging level + * @dev: device + * @frame: HDMI SPD infoframe + */ +static void hdmi_spd_infoframe_log(struct hdmi_spd_infoframe *frame) +{ + uint8_t buf[17]; + + hdmi_infoframe_log_header((struct hdmi_any_infoframe *)frame); + + memset(buf, 0, sizeof(buf)); + + strncpy(buf, frame->vendor, 8); + hdmi_log(" vendor: %s\n", buf); + strncpy(buf, frame->product, 16); + hdmi_log(" product: %s\n", buf); + hdmi_log(" source device information: %s (0x%x)\n", + hdmi_spd_sdi_get_name(frame->sdi), frame->sdi); +} + +static const char * +hdmi_audio_coding_type_get_name(enum hdmi_audio_coding_type coding_type) +{ + switch (coding_type) { + case HDMI_AUDIO_CODING_TYPE_STREAM: + return "Refer to Stream Header"; + case HDMI_AUDIO_CODING_TYPE_PCM: + return "PCM"; + case HDMI_AUDIO_CODING_TYPE_AC3: + return "AC-3"; + case HDMI_AUDIO_CODING_TYPE_MPEG1: + return "MPEG1"; + case HDMI_AUDIO_CODING_TYPE_MP3: + return "MP3"; + case HDMI_AUDIO_CODING_TYPE_MPEG2: + return "MPEG2"; + case HDMI_AUDIO_CODING_TYPE_AAC_LC: + return "AAC"; + case HDMI_AUDIO_CODING_TYPE_DTS: + return "DTS"; + case HDMI_AUDIO_CODING_TYPE_ATRAC: + return "ATRAC"; + case HDMI_AUDIO_CODING_TYPE_DSD: + return "One Bit Audio"; + case HDMI_AUDIO_CODING_TYPE_EAC3: + return "Dolby Digital +"; + case HDMI_AUDIO_CODING_TYPE_DTS_HD: + return "DTS-HD"; + case HDMI_AUDIO_CODING_TYPE_MLP: + return "MAT (MLP)"; + case HDMI_AUDIO_CODING_TYPE_DST: + return "DST"; + case HDMI_AUDIO_CODING_TYPE_WMA_PRO: + return "WMA PRO"; + case HDMI_AUDIO_CODING_TYPE_CXT: + return "Refer to CXT"; + } + return "Invalid"; +} + +static const char * +hdmi_audio_sample_size_get_name(enum hdmi_audio_sample_size sample_size) +{ + switch (sample_size) { + case HDMI_AUDIO_SAMPLE_SIZE_STREAM: + return "Refer to Stream Header"; + case HDMI_AUDIO_SAMPLE_SIZE_16: + return "16 bit"; + case HDMI_AUDIO_SAMPLE_SIZE_20: + return "20 bit"; + case HDMI_AUDIO_SAMPLE_SIZE_24: + return "24 bit"; + } + return "Invalid"; +} + +static const char * +hdmi_audio_sample_frequency_get_name(enum hdmi_audio_sample_frequency freq) +{ + switch (freq) { + case HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM: + return "Refer to Stream Header"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_32000: + return "32 kHz"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_44100: + return "44.1 kHz (CD)"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_48000: + return "48 kHz"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_88200: + return "88.2 kHz"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_96000: + return "96 kHz"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_176400: + return "176.4 kHz"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_192000: + return "192 kHz"; + } + return "Invalid"; +} + +static const char * +hdmi_audio_coding_type_ext_get_name(enum hdmi_audio_coding_type_ext ctx) +{ + + switch (ctx) { + case HDMI_AUDIO_CODING_TYPE_EXT_CT: + return "Refer to CT"; + case HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC: + return "HE AAC"; + case HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC_V2: + return "HE AAC v2"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG_SURROUND: + return "MPEG SURROUND"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC: + return "MPEG-4 HE AAC"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_V2: + return "MPEG-4 HE AAC v2"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC: + return "MPEG-4 AAC LC"; + case HDMI_AUDIO_CODING_TYPE_EXT_DRA: + return "DRA"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_SURROUND: + return "MPEG-4 HE AAC + MPEG Surround"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC_SURROUND: + return "MPEG-4 AAC LC + MPEG Surround"; + } + return "Reserved"; +} + +/** + * hdmi_audio_infoframe_log() - log info of HDMI AUDIO infoframe + * @level: logging level + * @dev: device + * @frame: HDMI AUDIO infoframe + */ +static void hdmi_audio_infoframe_log(struct hdmi_audio_infoframe *frame) +{ + hdmi_infoframe_log_header((struct hdmi_any_infoframe *)frame); + + if (frame->channels) + hdmi_log(" channels: %u\n", frame->channels - 1); + else + hdmi_log(" channels: Refer to stream header\n"); + hdmi_log(" coding type: %s\n", + hdmi_audio_coding_type_get_name(frame->coding_type)); + hdmi_log(" sample size: %s\n", + hdmi_audio_sample_size_get_name(frame->sample_size)); + hdmi_log(" sample frequency: %s\n", + hdmi_audio_sample_frequency_get_name(frame->sample_frequency)); + hdmi_log(" coding type ext: %s\n", + hdmi_audio_coding_type_ext_get_name(frame->coding_type_ext)); + hdmi_log(" channel allocation: 0x%x\n", + frame->channel_allocation); + hdmi_log(" level shift value: %u dB\n", + frame->level_shift_value); + hdmi_log(" downmix inhibit: %s\n", + frame->downmix_inhibit ? "Yes" : "No"); +} + +static const char * +hdmi_3d_structure_get_name(enum hdmi_3d_structure s3d_struct) +{ + if (s3d_struct < 0 || s3d_struct > 0xf) + return "Invalid"; + + switch (s3d_struct) { + case HDMI_3D_STRUCTURE_FRAME_PACKING: + return "Frame Packing"; + case HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE: + return "Field Alternative"; + case HDMI_3D_STRUCTURE_LINE_ALTERNATIVE: + return "Line Alternative"; + case HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL: + return "Side-by-side (Full)"; + case HDMI_3D_STRUCTURE_L_DEPTH: + return "L + Depth"; + case HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH: + return "L + Depth + Graphics + Graphics-depth"; + case HDMI_3D_STRUCTURE_TOP_AND_BOTTOM: + return "Top-and-Bottom"; + case HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF: + return "Side-by-side (Half)"; + default: + break; + } + return "Reserved"; +} + +/** + * hdmi_vendor_infoframe_log() - log info of HDMI VENDOR infoframe + * @level: logging level + * @dev: device + * @frame: HDMI VENDOR infoframe + */ +static void +hdmi_vendor_any_infoframe_log(union hdmi_vendor_any_infoframe *frame) +{ + struct hdmi_vendor_infoframe *hvf = &frame->hdmi; + + hdmi_infoframe_log_header((struct hdmi_any_infoframe *)frame); + + if (frame->any.oui != HDMI_IEEE_OUI) { + hdmi_log(" not a HDMI vendor infoframe\n"); + return; + } + if (hvf->vic == 0 && hvf->s3d_struct == HDMI_3D_STRUCTURE_INVALID) { + hdmi_log(" empty frame\n"); + return; + } + + if (hvf->vic) + hdmi_log(" HDMI VIC: %u\n", hvf->vic); + if (hvf->s3d_struct != HDMI_3D_STRUCTURE_INVALID) { + hdmi_log(" 3D structure: %s\n", + hdmi_3d_structure_get_name(hvf->s3d_struct)); + if (hvf->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) + hdmi_log(" 3D extension data: %d\n", + hvf->s3d_ext_data); + } +} + +/** + * hdmi_infoframe_log() - log info of HDMI infoframe + * @level: logging level + * @dev: device + * @frame: HDMI infoframe + */ +void hdmi_infoframe_log(union hdmi_infoframe *frame) +{ + switch (frame->any.type) { + case HDMI_INFOFRAME_TYPE_AVI: + hdmi_avi_infoframe_log(&frame->avi); + break; + case HDMI_INFOFRAME_TYPE_SPD: + hdmi_spd_infoframe_log(&frame->spd); + break; + case HDMI_INFOFRAME_TYPE_AUDIO: + hdmi_audio_infoframe_log(&frame->audio); + break; + case HDMI_INFOFRAME_TYPE_VENDOR: + hdmi_vendor_any_infoframe_log(&frame->vendor); + break; + } +} +EXPORT_SYMBOL(hdmi_infoframe_log); + +/** + * hdmi_avi_infoframe_unpack() - unpack binary buffer to a HDMI AVI infoframe + * @buffer: source buffer + * @frame: HDMI AVI infoframe + * + * Unpacks the information contained in binary @buffer into a structured + * @frame of the HDMI Auxiliary Video (AVI) information frame. + * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns 0 on success or a negative error code on failure. + */ +static int hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame, + void *buffer) +{ + uint8_t *ptr = buffer; + int ret; + + if (ptr[0] != HDMI_INFOFRAME_TYPE_AVI || + ptr[1] != 2 || + ptr[2] != HDMI_AVI_INFOFRAME_SIZE) + return -EINVAL; + + if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(AVI)) != 0) + return -EINVAL; + + ret = hdmi_avi_infoframe_init(frame); + if (ret) + return ret; + + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + frame->colorspace = (ptr[0] >> 5) & 0x3; + if (ptr[0] & 0x10) + frame->active_aspect = ptr[1] & 0xf; + if (ptr[0] & 0x8) { + frame->top_bar = (ptr[5] << 8) + ptr[6]; + frame->bottom_bar = (ptr[7] << 8) + ptr[8]; + } + if (ptr[0] & 0x4) { + frame->left_bar = (ptr[9] << 8) + ptr[10]; + frame->right_bar = (ptr[11] << 8) + ptr[12]; + } + frame->scan_mode = ptr[0] & 0x3; + + frame->colorimetry = (ptr[1] >> 6) & 0x3; + frame->picture_aspect = (ptr[1] >> 4) & 0x3; + frame->active_aspect = ptr[1] & 0xf; + + frame->itc = ptr[2] & 0x80 ? true : false; + frame->extended_colorimetry = (ptr[2] >> 4) & 0x7; + frame->quantization_range = (ptr[2] >> 2) & 0x3; + frame->nups = ptr[2] & 0x3; + + frame->video_code = ptr[3] & 0x7f; + frame->ycc_quantization_range = (ptr[4] >> 6) & 0x3; + frame->content_type = (ptr[4] >> 4) & 0x3; + + frame->pixel_repeat = ptr[4] & 0xf; + + return 0; +} + +/** + * hdmi_spd_infoframe_unpack() - unpack binary buffer to a HDMI SPD infoframe + * @buffer: source buffer + * @frame: HDMI SPD infoframe + * + * Unpacks the information contained in binary @buffer into a structured + * @frame of the HDMI Source Product Description (SPD) information frame. + * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns 0 on success or a negative error code on failure. + */ +static int hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe *frame, + void *buffer) +{ + uint8_t *ptr = buffer; + int ret; + + if (ptr[0] != HDMI_INFOFRAME_TYPE_SPD || + ptr[1] != 1 || + ptr[2] != HDMI_SPD_INFOFRAME_SIZE) { + return -EINVAL; + } + + if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(SPD)) != 0) + return -EINVAL; + + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + ret = hdmi_spd_infoframe_init(frame, ptr, ptr + 8); + if (ret) + return ret; + + frame->sdi = ptr[24]; + + return 0; +} + +/** + * hdmi_audio_infoframe_unpack() - unpack binary buffer to a HDMI AUDIO infoframe + * @buffer: source buffer + * @frame: HDMI Audio infoframe + * + * Unpacks the information contained in binary @buffer into a structured + * @frame of the HDMI Audio information frame. + * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns 0 on success or a negative error code on failure. + */ +static int hdmi_audio_infoframe_unpack(struct hdmi_audio_infoframe *frame, + void *buffer) +{ + uint8_t *ptr = buffer; + int ret; + + if (ptr[0] != HDMI_INFOFRAME_TYPE_AUDIO || + ptr[1] != 1 || + ptr[2] != HDMI_AUDIO_INFOFRAME_SIZE) { + return -EINVAL; + } + + if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(AUDIO)) != 0) + return -EINVAL; + + ret = hdmi_audio_infoframe_init(frame); + if (ret) + return ret; + + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + frame->channels = ptr[0] & 0x7; + frame->coding_type = (ptr[0] >> 4) & 0xf; + frame->sample_size = ptr[1] & 0x3; + frame->sample_frequency = (ptr[1] >> 2) & 0x7; + frame->coding_type_ext = ptr[2] & 0x1f; + frame->channel_allocation = ptr[3]; + frame->level_shift_value = (ptr[4] >> 3) & 0xf; + frame->downmix_inhibit = ptr[4] & 0x80 ? true : false; + + return 0; +} + +/** + * hdmi_vendor_infoframe_unpack() - unpack binary buffer to a HDMI vendor infoframe + * @buffer: source buffer + * @frame: HDMI Vendor infoframe + * + * Unpacks the information contained in binary @buffer into a structured + * @frame of the HDMI Vendor information frame. + * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns 0 on success or a negative error code on failure. + */ +static int +hdmi_vendor_any_infoframe_unpack(union hdmi_vendor_any_infoframe *frame, + void *buffer) +{ + uint8_t *ptr = buffer; + size_t length; + int ret; + uint8_t hdmi_video_format; + struct hdmi_vendor_infoframe *hvf = &frame->hdmi; + + if (ptr[0] != HDMI_INFOFRAME_TYPE_VENDOR || + ptr[1] != 1 || + (ptr[2] != 5 && ptr[2] != 6)) + return -EINVAL; + + length = ptr[2]; + + if (hdmi_infoframe_checksum(buffer, + HDMI_INFOFRAME_HEADER_SIZE + length) != 0) + return -EINVAL; + + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + /* HDMI OUI */ + if ((ptr[0] != 0x03) || + (ptr[1] != 0x0c) || + (ptr[2] != 0x00)) + return -EINVAL; + + hdmi_video_format = ptr[3] >> 5; + + if (hdmi_video_format > 0x2) + return -EINVAL; + + ret = hdmi_vendor_infoframe_init(hvf); + if (ret) + return ret; + + hvf->length = length; + + if (hdmi_video_format == 0x1) { + hvf->vic = ptr[4]; + } else if (hdmi_video_format == 0x2) { + hvf->s3d_struct = ptr[4] >> 4; + if (hvf->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) { + if (length == 6) + hvf->s3d_ext_data = ptr[5] >> 4; + else + return -EINVAL; + } + } + + return 0; +} + +/** + * hdmi_infoframe_unpack() - unpack binary buffer to a HDMI infoframe + * @buffer: source buffer + * @frame: HDMI infoframe + * + * Unpacks the information contained in binary buffer @buffer into a structured + * @frame of a HDMI infoframe. + * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer) +{ + int ret; + uint8_t *ptr = buffer; + + switch (ptr[0]) { + case HDMI_INFOFRAME_TYPE_AVI: + ret = hdmi_avi_infoframe_unpack(&frame->avi, buffer); + break; + case HDMI_INFOFRAME_TYPE_SPD: + ret = hdmi_spd_infoframe_unpack(&frame->spd, buffer); + break; + case HDMI_INFOFRAME_TYPE_AUDIO: + ret = hdmi_audio_infoframe_unpack(&frame->audio, buffer); + break; + case HDMI_INFOFRAME_TYPE_VENDOR: + ret = hdmi_vendor_any_infoframe_unpack(&frame->vendor, buffer); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} +EXPORT_SYMBOL(hdmi_infoframe_unpack); Property changes on: trunk/sys/arm/nvidia/drm2/hdmi.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/drm2/hdmi.h =================================================================== --- trunk/sys/arm/nvidia/drm2/hdmi.h (rev 0) +++ trunk/sys/arm/nvidia/drm2/hdmi.h 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,336 @@ +/* $MidnightBSD$ */ +/* + * Copyright (C) 2012 Avionic Design GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $FreeBSD: stable/11/sys/arm/nvidia/drm2/hdmi.h 310600 2016-12-26 14:36:05Z mmel $ + */ + +#ifndef _HDMI_H_ +#define _HDMI_H_ + + +enum hdmi_infoframe_type { + HDMI_INFOFRAME_TYPE_VENDOR = 0x81, + HDMI_INFOFRAME_TYPE_AVI = 0x82, + HDMI_INFOFRAME_TYPE_SPD = 0x83, + HDMI_INFOFRAME_TYPE_AUDIO = 0x84, +}; + +#define HDMI_IEEE_OUI 0x000c03 +#define HDMI_INFOFRAME_HEADER_SIZE 4 +#define HDMI_AVI_INFOFRAME_SIZE 13 +#define HDMI_SPD_INFOFRAME_SIZE 25 +#define HDMI_AUDIO_INFOFRAME_SIZE 10 + +#define HDMI_INFOFRAME_SIZE(type) \ + (HDMI_INFOFRAME_HEADER_SIZE + HDMI_ ## type ## _INFOFRAME_SIZE) + +struct hdmi_any_infoframe { + enum hdmi_infoframe_type type; + unsigned char version; + unsigned char length; +}; + +enum hdmi_colorspace { + HDMI_COLORSPACE_RGB, + HDMI_COLORSPACE_YUV422, + HDMI_COLORSPACE_YUV444, + HDMI_COLORSPACE_YUV420, + HDMI_COLORSPACE_RESERVED4, + HDMI_COLORSPACE_RESERVED5, + HDMI_COLORSPACE_RESERVED6, + HDMI_COLORSPACE_IDO_DEFINED, +}; + +enum hdmi_scan_mode { + HDMI_SCAN_MODE_NONE, + HDMI_SCAN_MODE_OVERSCAN, + HDMI_SCAN_MODE_UNDERSCAN, + HDMI_SCAN_MODE_RESERVED, +}; + +enum hdmi_colorimetry { + HDMI_COLORIMETRY_NONE, + HDMI_COLORIMETRY_ITU_601, + HDMI_COLORIMETRY_ITU_709, + HDMI_COLORIMETRY_EXTENDED, +}; + +enum hdmi_picture_aspect { + HDMI_PICTURE_ASPECT_NONE, + HDMI_PICTURE_ASPECT_4_3, + HDMI_PICTURE_ASPECT_16_9, + HDMI_PICTURE_ASPECT_RESERVED, +}; + +enum hdmi_active_aspect { + HDMI_ACTIVE_ASPECT_16_9_TOP = 2, + HDMI_ACTIVE_ASPECT_14_9_TOP = 3, + HDMI_ACTIVE_ASPECT_16_9_CENTER = 4, + HDMI_ACTIVE_ASPECT_PICTURE = 8, + HDMI_ACTIVE_ASPECT_4_3 = 9, + HDMI_ACTIVE_ASPECT_16_9 = 10, + HDMI_ACTIVE_ASPECT_14_9 = 11, + HDMI_ACTIVE_ASPECT_4_3_SP_14_9 = 13, + HDMI_ACTIVE_ASPECT_16_9_SP_14_9 = 14, + HDMI_ACTIVE_ASPECT_16_9_SP_4_3 = 15, +}; + +enum hdmi_extended_colorimetry { + HDMI_EXTENDED_COLORIMETRY_XV_YCC_601, + HDMI_EXTENDED_COLORIMETRY_XV_YCC_709, + HDMI_EXTENDED_COLORIMETRY_S_YCC_601, + HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601, + HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB, + + /* The following EC values are only defined in CEA-861-F. */ + HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM, + HDMI_EXTENDED_COLORIMETRY_BT2020, + HDMI_EXTENDED_COLORIMETRY_RESERVED, +}; + +enum hdmi_quantization_range { + HDMI_QUANTIZATION_RANGE_DEFAULT, + HDMI_QUANTIZATION_RANGE_LIMITED, + HDMI_QUANTIZATION_RANGE_FULL, + HDMI_QUANTIZATION_RANGE_RESERVED, +}; + +/* non-uniform picture scaling */ +enum hdmi_nups { + HDMI_NUPS_UNKNOWN, + HDMI_NUPS_HORIZONTAL, + HDMI_NUPS_VERTICAL, + HDMI_NUPS_BOTH, +}; + +enum hdmi_ycc_quantization_range { + HDMI_YCC_QUANTIZATION_RANGE_LIMITED, + HDMI_YCC_QUANTIZATION_RANGE_FULL, +}; + +enum hdmi_content_type { + HDMI_CONTENT_TYPE_GRAPHICS, + HDMI_CONTENT_TYPE_PHOTO, + HDMI_CONTENT_TYPE_CINEMA, + HDMI_CONTENT_TYPE_GAME, +}; + +struct hdmi_avi_infoframe { + enum hdmi_infoframe_type type; + unsigned char version; + unsigned char length; + enum hdmi_colorspace colorspace; + enum hdmi_scan_mode scan_mode; + enum hdmi_colorimetry colorimetry; + enum hdmi_picture_aspect picture_aspect; + enum hdmi_active_aspect active_aspect; + bool itc; + enum hdmi_extended_colorimetry extended_colorimetry; + enum hdmi_quantization_range quantization_range; + enum hdmi_nups nups; + unsigned char video_code; + enum hdmi_ycc_quantization_range ycc_quantization_range; + enum hdmi_content_type content_type; + unsigned char pixel_repeat; + unsigned short top_bar; + unsigned short bottom_bar; + unsigned short left_bar; + unsigned short right_bar; +}; + +int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame); +ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, + size_t size); + +enum hdmi_spd_sdi { + HDMI_SPD_SDI_UNKNOWN, + HDMI_SPD_SDI_DSTB, + HDMI_SPD_SDI_DVDP, + HDMI_SPD_SDI_DVHS, + HDMI_SPD_SDI_HDDVR, + HDMI_SPD_SDI_DVC, + HDMI_SPD_SDI_DSC, + HDMI_SPD_SDI_VCD, + HDMI_SPD_SDI_GAME, + HDMI_SPD_SDI_PC, + HDMI_SPD_SDI_BD, + HDMI_SPD_SDI_SACD, + HDMI_SPD_SDI_HDDVD, + HDMI_SPD_SDI_PMP, +}; + +struct hdmi_spd_infoframe { + enum hdmi_infoframe_type type; + unsigned char version; + unsigned char length; + char vendor[8]; + char product[16]; + enum hdmi_spd_sdi sdi; +}; + +int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame, + const char *vendor, const char *product); +ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer, + size_t size); + +enum hdmi_audio_coding_type { + HDMI_AUDIO_CODING_TYPE_STREAM, + HDMI_AUDIO_CODING_TYPE_PCM, + HDMI_AUDIO_CODING_TYPE_AC3, + HDMI_AUDIO_CODING_TYPE_MPEG1, + HDMI_AUDIO_CODING_TYPE_MP3, + HDMI_AUDIO_CODING_TYPE_MPEG2, + HDMI_AUDIO_CODING_TYPE_AAC_LC, + HDMI_AUDIO_CODING_TYPE_DTS, + HDMI_AUDIO_CODING_TYPE_ATRAC, + HDMI_AUDIO_CODING_TYPE_DSD, + HDMI_AUDIO_CODING_TYPE_EAC3, + HDMI_AUDIO_CODING_TYPE_DTS_HD, + HDMI_AUDIO_CODING_TYPE_MLP, + HDMI_AUDIO_CODING_TYPE_DST, + HDMI_AUDIO_CODING_TYPE_WMA_PRO, + HDMI_AUDIO_CODING_TYPE_CXT, +}; + +enum hdmi_audio_sample_size { + HDMI_AUDIO_SAMPLE_SIZE_STREAM, + HDMI_AUDIO_SAMPLE_SIZE_16, + HDMI_AUDIO_SAMPLE_SIZE_20, + HDMI_AUDIO_SAMPLE_SIZE_24, +}; + +enum hdmi_audio_sample_frequency { + HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM, + HDMI_AUDIO_SAMPLE_FREQUENCY_32000, + HDMI_AUDIO_SAMPLE_FREQUENCY_44100, + HDMI_AUDIO_SAMPLE_FREQUENCY_48000, + HDMI_AUDIO_SAMPLE_FREQUENCY_88200, + HDMI_AUDIO_SAMPLE_FREQUENCY_96000, + HDMI_AUDIO_SAMPLE_FREQUENCY_176400, + HDMI_AUDIO_SAMPLE_FREQUENCY_192000, +}; + +enum hdmi_audio_coding_type_ext { + /* Refer to Audio Coding Type (CT) field in Data Byte 1 */ + HDMI_AUDIO_CODING_TYPE_EXT_CT, + + /* + * The next three CXT values are defined in CEA-861-E only. + * They do not exist in older versions, and in CEA-861-F they are + * defined as 'Not in use'. + */ + HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC, + HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC_V2, + HDMI_AUDIO_CODING_TYPE_EXT_MPEG_SURROUND, + + /* The following CXT values are only defined in CEA-861-F. */ + HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC, + HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_V2, + HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC, + HDMI_AUDIO_CODING_TYPE_EXT_DRA, + HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_SURROUND, + HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC_SURROUND = 10, +}; + +struct hdmi_audio_infoframe { + enum hdmi_infoframe_type type; + unsigned char version; + unsigned char length; + unsigned char channels; + enum hdmi_audio_coding_type coding_type; + enum hdmi_audio_sample_size sample_size; + enum hdmi_audio_sample_frequency sample_frequency; + enum hdmi_audio_coding_type_ext coding_type_ext; + unsigned char channel_allocation; + unsigned char level_shift_value; + bool downmix_inhibit; + +}; + +int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame); +ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, + void *buffer, size_t size); + +enum hdmi_3d_structure { + HDMI_3D_STRUCTURE_INVALID = -1, + HDMI_3D_STRUCTURE_FRAME_PACKING = 0, + HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE, + HDMI_3D_STRUCTURE_LINE_ALTERNATIVE, + HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL, + HDMI_3D_STRUCTURE_L_DEPTH, + HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH, + HDMI_3D_STRUCTURE_TOP_AND_BOTTOM, + HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF = 8, +}; + + +struct hdmi_vendor_infoframe { + enum hdmi_infoframe_type type; + unsigned char version; + unsigned char length; + unsigned int oui; + uint8_t vic; + enum hdmi_3d_structure s3d_struct; + unsigned int s3d_ext_data; +}; + +int hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame); +ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, + void *buffer, size_t size); + +union hdmi_vendor_any_infoframe { + struct { + enum hdmi_infoframe_type type; + unsigned char version; + unsigned char length; + unsigned int oui; + } any; + struct hdmi_vendor_infoframe hdmi; +}; + +/** + * union hdmi_infoframe - overall union of all abstract infoframe representations + * @any: generic infoframe + * @avi: avi infoframe + * @spd: spd infoframe + * @vendor: union of all vendor infoframes + * @audio: audio infoframe + * + * This is used by the generic pack function. This works since all infoframes + * have the same header which also indicates which type of infoframe should be + * packed. + */ +union hdmi_infoframe { + struct hdmi_any_infoframe any; + struct hdmi_avi_infoframe avi; + struct hdmi_spd_infoframe spd; + union hdmi_vendor_any_infoframe vendor; + struct hdmi_audio_infoframe audio; +}; + +ssize_t +hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, size_t size); +int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer); +void hdmi_infoframe_log(union hdmi_infoframe *frame); + +#endif /* _HDMI_H */ Property changes on: trunk/sys/arm/nvidia/drm2/hdmi.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/drm2/tegra_bo.c =================================================================== --- trunk/sys/arm/nvidia/drm2/tegra_bo.c (rev 0) +++ trunk/sys/arm/nvidia/drm2/tegra_bo.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,370 @@ +/* $MidnightBSD$ */ +/*- + * 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_bo.c 310600 2016-12-26 14:36:05Z mmel $"); + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +static void +tegra_bo_destruct(struct tegra_bo *bo) +{ + vm_page_t m; + size_t size; + int i; + + if (bo->cdev_pager == NULL) + return; + + size = round_page(bo->gem_obj.size); + if (bo->vbase != 0) + pmap_qremove(bo->vbase, bo->npages); + + VM_OBJECT_WLOCK(bo->cdev_pager); + for (i = 0; i < bo->npages; i++) { + m = bo->m[i]; + cdev_pager_free_page(bo->cdev_pager, m); + vm_page_lock(m); + m->flags &= ~PG_FICTITIOUS; + vm_page_unwire(m, PQ_NONE); + vm_page_free(m); + vm_page_unlock(m); + } + VM_OBJECT_WUNLOCK(bo->cdev_pager); + + vm_object_deallocate(bo->cdev_pager); + if (bo->vbase != 0) + vmem_free(kmem_arena, bo->vbase, size); +} + +static void +tegra_bo_free_object(struct drm_gem_object *gem_obj) +{ + struct tegra_bo *bo; + + bo = container_of(gem_obj, struct tegra_bo, gem_obj); + drm_gem_free_mmap_offset(gem_obj); + drm_gem_object_release(gem_obj); + + tegra_bo_destruct(bo); + + free(bo->m, DRM_MEM_DRIVER); + free(bo, DRM_MEM_DRIVER); +} + +static int +tegra_bo_alloc_contig(size_t npages, u_long alignment, vm_memattr_t memattr, + vm_page_t **ret_page) +{ + vm_page_t m; + int pflags, tries, i; + vm_paddr_t low, high, boundary; + + low = 0; + high = -1UL; + boundary = 0; + pflags = VM_ALLOC_NORMAL | VM_ALLOC_NOOBJ | VM_ALLOC_NOBUSY | + VM_ALLOC_WIRED | VM_ALLOC_ZERO; + tries = 0; +retry: + m = vm_page_alloc_contig(NULL, 0, pflags, npages, low, high, alignment, + boundary, memattr); + if (m == NULL) { + if (tries < 3) { + if (!vm_page_reclaim_contig(pflags, npages, low, high, + alignment, boundary)) + VM_WAIT; + tries++; + goto retry; + } + return (ENOMEM); + } + + for (i = 0; i < npages; i++, m++) { + if ((m->flags & PG_ZERO) == 0) + pmap_zero_page(m); + m->valid = VM_PAGE_BITS_ALL; + (*ret_page)[i] = m; + } + + return (0); +} + +/* Initialize pager and insert all object pages to it*/ +static int +tegra_bo_init_pager(struct tegra_bo *bo) +{ + vm_page_t m; + size_t size; + int i; + + size = round_page(bo->gem_obj.size); + + bo->pbase = VM_PAGE_TO_PHYS(bo->m[0]); + if (vmem_alloc(kmem_arena, size, M_WAITOK | M_BESTFIT, &bo->vbase)) + return (ENOMEM); + + VM_OBJECT_WLOCK(bo->cdev_pager); + for (i = 0; i < bo->npages; i++) { + m = bo->m[i]; + /* + * XXX This is a temporary hack. + * We need pager suitable for paging (mmap) managed + * real (non-fictitious) pages. + * - managed pages are needed for clean module unload. + * - aliasing fictitious page to real one is bad, + * pmap cannot handle this situation without issues + * It expects that + * paddr = PHYS_TO_VM_PAGE(VM_PAGE_TO_PHYS(paddr)) + * for every single page passed to pmap. + */ + m->oflags &= ~VPO_UNMANAGED; + m->flags |= PG_FICTITIOUS; + if (vm_page_insert(m, bo->cdev_pager, i) != 0) + return (EINVAL); + } + VM_OBJECT_WUNLOCK(bo->cdev_pager); + + pmap_qenter(bo->vbase, bo->m, bo->npages); + return (0); +} + +/* Allocate memory for frame buffer */ +static int +tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo) +{ + size_t size; + int rv; + + size = bo->gem_obj.size; + + bo->npages = atop(size); + bo->m = malloc(sizeof(vm_page_t *) * bo->npages, DRM_MEM_DRIVER, + M_WAITOK | M_ZERO); + + rv = tegra_bo_alloc_contig(bo->npages, PAGE_SIZE, + VM_MEMATTR_WRITE_COMBINING, &(bo->m)); + if (rv != 0) { + DRM_WARNING("Cannot allocate memory for gem object.\n"); + return (rv); + } + rv = tegra_bo_init_pager(bo); + if (rv != 0) { + DRM_WARNING("Cannot initialize gem object pager.\n"); + return (rv); + } + return (0); +} + +int +tegra_bo_create(struct drm_device *drm, size_t size, struct tegra_bo **res_bo) +{ + struct tegra_bo *bo; + int rv; + + if (size <= 0) + return (-EINVAL); + + bo = malloc(sizeof(*bo), DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + + size = round_page(size); + rv = drm_gem_object_init(drm, &bo->gem_obj, size); + if (rv != 0) { + free(bo, DRM_MEM_DRIVER); + return (rv); + } + rv = drm_gem_create_mmap_offset(&bo->gem_obj); + if (rv != 0) { + drm_gem_object_release(&bo->gem_obj); + free(bo, DRM_MEM_DRIVER); + return (rv); + } + + bo->cdev_pager = cdev_pager_allocate(&bo->gem_obj, OBJT_MGTDEVICE, + drm->driver->gem_pager_ops, size, 0, 0, NULL); + rv = tegra_bo_alloc(drm, bo); + if (rv != 0) { + tegra_bo_free_object(&bo->gem_obj); + return (rv); + } + + *res_bo = bo; + return (0); +} + + + +static int +tegra_bo_create_with_handle(struct drm_file *file, struct drm_device *drm, + size_t size, uint32_t *handle, struct tegra_bo **res_bo) +{ + int rv; + struct tegra_bo *bo; + + rv = tegra_bo_create(drm, size, &bo); + if (rv != 0) + return (rv); + + rv = drm_gem_handle_create(file, &bo->gem_obj, handle); + if (rv != 0) { + tegra_bo_free_object(&bo->gem_obj); + drm_gem_object_release(&bo->gem_obj); + return (rv); + } + + drm_gem_object_unreference_unlocked(&bo->gem_obj); + + *res_bo = bo; + return (0); +} + +static int +tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm_dev, + struct drm_mode_create_dumb *args) +{ + struct tegra_drm *drm; + struct tegra_bo *bo; + int rv; + + drm = container_of(drm_dev, struct tegra_drm, drm_dev); + + args->pitch= (args->width * args->bpp + 7) / 8; + args->pitch = roundup(args->pitch, drm->pitch_align); + args->size = args->pitch * args->height; + rv = tegra_bo_create_with_handle(file, drm_dev, args->size, + &args->handle, &bo); + + return (rv); +} + +static int +tegra_bo_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *drm_dev, uint32_t handle, uint64_t *offset) +{ + struct drm_gem_object *gem_obj; + int rv; + + DRM_LOCK(drm_dev); + gem_obj = drm_gem_object_lookup(drm_dev, file_priv, handle); + if (gem_obj == NULL) { + device_printf(drm_dev->dev, "Object not found\n"); + DRM_UNLOCK(drm_dev); + return (-EINVAL); + } + rv = drm_gem_create_mmap_offset(gem_obj); + if (rv != 0) + goto fail; + + *offset = DRM_GEM_MAPPING_OFF(gem_obj->map_list.key) | + DRM_GEM_MAPPING_KEY; + + drm_gem_object_unreference(gem_obj); + DRM_UNLOCK(drm_dev); + return (0); + +fail: + drm_gem_object_unreference(gem_obj); + DRM_UNLOCK(drm_dev); + return (rv); +} + +static int +tegra_bo_dumb_destroy(struct drm_file *file_priv, struct drm_device *drm_dev, + unsigned int handle) +{ + int rv; + + rv = drm_gem_handle_delete(file_priv, handle); + return (rv); +} + +/* + * mmap support + */ +static int +tegra_gem_pager_fault(vm_object_t vm_obj, vm_ooffset_t offset, int prot, + vm_page_t *mres) +{ + +#ifdef DRM_PAGER_DEBUG + DRM_DEBUG("object %p offset %jd prot %d mres %p\n", + vm_obj, (intmax_t)offset, prot, mres); +#endif + return (VM_PAGER_FAIL); + +} + +static int +tegra_gem_pager_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot, + vm_ooffset_t foff, struct ucred *cred, u_short *color) +{ + + if (color != NULL) + *color = 0; + return (0); +} + +static void +tegra_gem_pager_dtor(void *handle) +{ + +} + +static struct cdev_pager_ops tegra_gem_pager_ops = { + .cdev_pg_fault = tegra_gem_pager_fault, + .cdev_pg_ctor = tegra_gem_pager_ctor, + .cdev_pg_dtor = tegra_gem_pager_dtor +}; + +/* Fill up relevant fields in drm_driver ops */ +void +tegra_bo_driver_register(struct drm_driver *drm_drv) +{ + drm_drv->gem_free_object = tegra_bo_free_object; + drm_drv->gem_pager_ops = &tegra_gem_pager_ops; + drm_drv->dumb_create = tegra_bo_dumb_create; + drm_drv->dumb_map_offset = tegra_bo_dumb_map_offset; + drm_drv->dumb_destroy = tegra_bo_dumb_destroy; +} Property changes on: trunk/sys/arm/nvidia/drm2/tegra_bo.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/drm2/tegra_dc.c =================================================================== --- trunk/sys/arm/nvidia/drm2/tegra_dc.c (rev 0) +++ trunk/sys/arm/nvidia/drm2/tegra_dc.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,1448 @@ +/* $MidnightBSD$ */ +/*- + * 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_dc.c 310600 2016-12-26 14:36:05Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "tegra_drm_if.h" +#include "tegra_dc_if.h" + +#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, 4 * (_r), (_v)) +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, 4 * (_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, "tegra_dc_wait", timeout); +#define LOCK_INIT(_sc) \ + mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_dc", 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) + + +#define SYNCPT_VBLANK0 26 +#define SYNCPT_VBLANK1 27 + +#define DC_MAX_PLANES 2 /* Maximum planes */ + +/* DRM Formats supported by DC */ +/* XXXX expand me */ +static uint32_t dc_plane_formats[] = { + DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_UYVY, + DRM_FORMAT_YUYV, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV422, +}; + + +/* Complete description of one window (plane) */ +struct dc_window { + /* Source (in framebuffer) rectangle, in pixels */ + u_int src_x; + u_int src_y; + u_int src_w; + u_int src_h; + + /* Destination (on display) rectangle, in pixels */ + u_int dst_x; + u_int dst_y; + u_int dst_w; + u_int dst_h; + + /* Parsed pixel format */ + u_int bits_per_pixel; + bool is_yuv; /* any YUV mode */ + bool is_yuv_planar; /* planar YUV mode */ + uint32_t color_mode; /* DC_WIN_COLOR_DEPTH */ + uint32_t swap; /* DC_WIN_BYTE_SWAP */ + uint32_t surface_kind; /* DC_WINBUF_SURFACE_KIND */ + uint32_t block_height; /* DC_WINBUF_SURFACE_KIND */ + + /* Parsed flipping, rotation is not supported for pitched modes */ + bool flip_x; /* inverted X-axis */ + bool flip_y; /* inverted Y-axis */ + bool transpose_xy; /* swap X and Y-axis */ + + /* Color planes base addresses and strides */ + bus_size_t base[3]; + uint32_t stride[3]; /* stride[2] isn't used by HW */ +}; + +struct dc_softc { + device_t dev; + struct resource *mem_res; + struct resource *irq_res; + void *irq_ih; + struct mtx mtx; + + clk_t clk_parent; + clk_t clk_dc; + hwreset_t hwreset_dc; + + int pitch_align; + + struct tegra_crtc tegra_crtc; + struct drm_pending_vblank_event *event; + struct drm_gem_object *cursor_gem; +}; + + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-dc", 1}, + {NULL, 0}, +}; + +/* Convert standard drm pixel format to tegra windows parameters. */ +static int +dc_parse_drm_format(struct tegra_fb *fb, struct dc_window *win) +{ + struct tegra_bo *bo; + uint32_t cm; + uint32_t sw; + bool is_yuv, is_yuv_planar; + int nplanes, i; + + switch (fb->drm_fb.pixel_format) { + case DRM_FORMAT_XBGR8888: + sw = BYTE_SWAP(NOSWAP); + cm = WIN_COLOR_DEPTH_R8G8B8A8; + is_yuv = false; + is_yuv_planar = false; + break; + + case DRM_FORMAT_XRGB8888: + sw = BYTE_SWAP(NOSWAP); + cm = WIN_COLOR_DEPTH_B8G8R8A8; + is_yuv = false; + is_yuv_planar = false; + break; + + case DRM_FORMAT_RGB565: + sw = BYTE_SWAP(NOSWAP); + cm = WIN_COLOR_DEPTH_B5G6R5; + is_yuv = false; + is_yuv_planar = false; + break; + + case DRM_FORMAT_UYVY: + sw = BYTE_SWAP(NOSWAP); + cm = WIN_COLOR_DEPTH_YCbCr422; + is_yuv = true; + is_yuv_planar = false; + break; + + case DRM_FORMAT_YUYV: + sw = BYTE_SWAP(SWAP2); + cm = WIN_COLOR_DEPTH_YCbCr422; + is_yuv = true; + is_yuv_planar = false; + break; + + case DRM_FORMAT_YUV420: + sw = BYTE_SWAP(NOSWAP); + cm = WIN_COLOR_DEPTH_YCbCr420P; + is_yuv = true; + is_yuv_planar = true; + break; + + case DRM_FORMAT_YUV422: + sw = BYTE_SWAP(NOSWAP); + cm = WIN_COLOR_DEPTH_YCbCr422P; + is_yuv = true; + is_yuv_planar = true; + break; + + default: + /* Unsupported format */ + return (-EINVAL); + } + + /* Basic check of arguments. */ + switch (fb->rotation) { + case 0: + case 180: + break; + + case 90: /* Rotation is supported only */ + case 270: /* for block linear surfaces */ + if (!fb->block_linear) + return (-EINVAL); + break; + + default: + return (-EINVAL); + } + /* XXX Add more checks (sizes, scaling...) */ + + if (win == NULL) + return (0); + + win->surface_kind = + fb->block_linear ? SURFACE_KIND_BL_16B2: SURFACE_KIND_PITCH; + win->block_height = fb->block_height; + switch (fb->rotation) { + case 0: /* (0,0,0) */ + win->transpose_xy = false; + win->flip_x = false; + win->flip_y = false; + break; + + case 90: /* (1,0,1) */ + win->transpose_xy = true; + win->flip_x = false; + win->flip_y = true; + break; + + case 180: /* (0,1,1) */ + win->transpose_xy = false; + win->flip_x = true; + win->flip_y = true; + break; + + case 270: /* (1,1,0) */ + win->transpose_xy = true; + win->flip_x = true; + win->flip_y = false; + break; + } + win->flip_x ^= fb->flip_x; + win->flip_y ^= fb->flip_y; + + win->color_mode = cm; + win->swap = sw; + win->bits_per_pixel = fb->drm_fb.bits_per_pixel; + win->is_yuv = is_yuv; + win->is_yuv_planar = is_yuv_planar; + + nplanes = drm_format_num_planes(fb->drm_fb.pixel_format); + for (i = 0; i < nplanes; i++) { + bo = fb->planes[i]; + win->base[i] = bo->pbase + fb->drm_fb.offsets[i]; + win->stride[i] = fb->drm_fb.pitches[i]; + } + return (0); +} + +/* + * Scaling functions. + * + * It's unclear if we want/must program the fractional portion + * (aka bias) of init_dda registers, mainly when mirrored axis + * modes are used. + * For now, we use 1.0 as recommended by TRM. + */ +static inline uint32_t +dc_scaling_init(uint32_t start) +{ + + return (1 << 12); +} + +static inline uint32_t +dc_scaling_incr(uint32_t src, uint32_t dst, uint32_t maxscale) +{ + uint32_t val; + + val = (src - 1) << 12 ; /* 4.12 fixed float */ + val /= (dst - 1); + if (val > (maxscale << 12)) + val = maxscale << 12; + return val; +} + +/* ------------------------------------------------------------------- + * + * HW Access. + * + */ + +/* + * Setup pixel clock. + * Minimal frequency is pixel clock, but output is free to select + * any higher. + */ +static int +dc_setup_clk(struct dc_softc *sc, struct drm_crtc *crtc, + struct drm_display_mode *mode, uint32_t *div) +{ + uint64_t pclk, freq; + struct tegra_drm_encoder *output; + struct drm_encoder *encoder; + long rv; + + pclk = mode->clock * 1000; + + /* Find attached encoder */ + output = NULL; + list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list, + head) { + if (encoder->crtc == crtc) { + output = container_of(encoder, struct tegra_drm_encoder, + encoder); + break; + } + } + if (output == NULL) + return (-ENODEV); + + if (output->setup_clock == NULL) + panic("Output have not setup_clock function.\n"); + rv = output->setup_clock(output, sc->clk_dc, pclk); + if (rv != 0) { + device_printf(sc->dev, "Cannot setup pixel clock: %llu\n", + pclk); + return (rv); + } + + rv = clk_get_freq(sc->clk_dc, &freq); + *div = (freq * 2 / pclk) - 2; + + DRM_DEBUG_KMS("frequency: %llu, DC divider: %u\n", freq, *div); + + return 0; +} + +static void +dc_setup_window(struct dc_softc *sc, unsigned int index, struct dc_window *win) +{ + uint32_t h_offset, v_offset, h_size, v_size, bpp; + uint32_t h_init_dda, v_init_dda, h_incr_dda, v_incr_dda; + uint32_t val; + +#ifdef DMR_DEBUG_WINDOW + printf("%s window: %d\n", __func__, index); + printf(" src: x: %d, y: %d, w: %d, h: %d\n", + win->src_x, win->src_y, win->src_w, win->src_h); + printf(" dst: x: %d, y: %d, w: %d, h: %d\n", + win->dst_x, win->dst_y, win->dst_w, win->dst_h); + printf(" bpp: %d, color_mode: %d, swap: %d\n", + win->bits_per_pixel, win->color_mode, win->swap); +#endif + + if (win->is_yuv) + bpp = win->is_yuv_planar ? 1 : 2; + else + bpp = (win->bits_per_pixel + 7) / 8; + + if (!win->transpose_xy) { + h_size = win->src_w * bpp; + v_size = win->src_h; + } else { + h_size = win->src_h * bpp; + v_size = win->src_w; + } + + h_offset = win->src_x * bpp;; + v_offset = win->src_y; + if (win->flip_x) { + h_offset += win->src_w * bpp - 1; + } + if (win->flip_y) + v_offset += win->src_h - 1; + + /* Adjust offsets for planar yuv modes */ + if (win->is_yuv_planar) { + h_offset &= ~1; + if (win->flip_x ) + h_offset |= 1; + v_offset &= ~1; + if (win->flip_y ) + v_offset |= 1; + } + + /* Setup scaling. */ + if (!win->transpose_xy) { + h_init_dda = dc_scaling_init(win->src_x); + v_init_dda = dc_scaling_init(win->src_y); + h_incr_dda = dc_scaling_incr(win->src_w, win->dst_w, 4); + v_incr_dda = dc_scaling_incr(win->src_h, win->dst_h, 15); + } else { + h_init_dda = dc_scaling_init(win->src_y); + v_init_dda = dc_scaling_init(win->src_x); + h_incr_dda = dc_scaling_incr(win->src_h, win->dst_h, 4); + v_incr_dda = dc_scaling_incr(win->src_w, win->dst_w, 15); + } +#ifdef DMR_DEBUG_WINDOW + printf("\n"); + printf(" bpp: %d, size: h: %d v: %d, offset: h:%d v: %d\n", + bpp, h_size, v_size, h_offset, v_offset); + printf(" init_dda: h: %d v: %d, incr_dda: h: %d v: %d\n", + h_init_dda, v_init_dda, h_incr_dda, v_incr_dda); +#endif + + LOCK(sc); + + /* Select target window */ + val = WINDOW_A_SELECT << index; + WR4(sc, DC_CMD_DISPLAY_WINDOW_HEADER, val); + + /* Sizes */ + WR4(sc, DC_WIN_POSITION, WIN_POSITION(win->dst_x, win->dst_y)); + WR4(sc, DC_WIN_SIZE, WIN_SIZE(win->dst_w, win->dst_h)); + WR4(sc, DC_WIN_PRESCALED_SIZE, WIN_PRESCALED_SIZE(h_size, v_size)); + + /* DDA */ + WR4(sc, DC_WIN_DDA_INCREMENT, + WIN_DDA_INCREMENT(h_incr_dda, v_incr_dda)); + WR4(sc, DC_WIN_H_INITIAL_DDA, h_init_dda); + WR4(sc, DC_WIN_V_INITIAL_DDA, v_init_dda); + + /* Color planes base addresses and strides */ + WR4(sc, DC_WINBUF_START_ADDR, win->base[0]); + if (win->is_yuv_planar) { + WR4(sc, DC_WINBUF_START_ADDR_U, win->base[1]); + WR4(sc, DC_WINBUF_START_ADDR_V, win->base[2]); + WR4(sc, DC_WIN_LINE_STRIDE, + win->stride[1] << 16 | win->stride[0]); + } else { + WR4(sc, DC_WIN_LINE_STRIDE, win->stride[0]); + } + + /* Offsets for rotation and axis flip */ + WR4(sc, DC_WINBUF_ADDR_H_OFFSET, h_offset); + WR4(sc, DC_WINBUF_ADDR_V_OFFSET, v_offset); + + /* Color format */ + WR4(sc, DC_WIN_COLOR_DEPTH, win->color_mode); + WR4(sc, DC_WIN_BYTE_SWAP, win->swap); + + /* Tiling */ + val = win->surface_kind; + if (win->surface_kind == SURFACE_KIND_BL_16B2) + val |= SURFACE_KIND_BLOCK_HEIGHT(win->block_height); + WR4(sc, DC_WINBUF_SURFACE_KIND, val); + + /* Color space coefs for YUV modes */ + if (win->is_yuv) { + WR4(sc, DC_WINC_CSC_YOF, 0x00f0); + WR4(sc, DC_WINC_CSC_KYRGB, 0x012a); + WR4(sc, DC_WINC_CSC_KUR, 0x0000); + WR4(sc, DC_WINC_CSC_KVR, 0x0198); + WR4(sc, DC_WINC_CSC_KUG, 0x039b); + WR4(sc, DC_WINC_CSC_KVG, 0x032f); + WR4(sc, DC_WINC_CSC_KUB, 0x0204); + WR4(sc, DC_WINC_CSC_KVB, 0x0000); + } + + val = WIN_ENABLE; + if (win->is_yuv) + val |= CSC_ENABLE; + else if (win->bits_per_pixel < 24) + val |= COLOR_EXPAND; + if (win->flip_y) + val |= V_DIRECTION; + if (win->flip_x) + val |= H_DIRECTION; + if (win->transpose_xy) + val |= SCAN_COLUMN; + WR4(sc, DC_WINC_WIN_OPTIONS, val); + +#ifdef DMR_DEBUG_WINDOW + /* Set underflow debug mode -> highlight missing pixels. */ + WR4(sc, DC_WINBUF_UFLOW_CTRL, UFLOW_CTR_ENABLE); + WR4(sc, DC_WINBUF_UFLOW_DBG_PIXEL, 0xFFFF0000); +#endif + + UNLOCK(sc); +} + +/* ------------------------------------------------------------------- + * + * Plane functions. + * + */ +static int +dc_plane_update(struct drm_plane *drm_plane, struct drm_crtc *drm_crtc, + struct drm_framebuffer *drm_fb, + int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h) +{ + struct tegra_plane *plane; + struct tegra_crtc *crtc; + struct tegra_fb *fb; + struct dc_softc *sc; + struct dc_window win; + int rv; + + plane = container_of(drm_plane, struct tegra_plane, drm_plane); + fb = container_of(drm_fb, struct tegra_fb, drm_fb); + crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); + sc = device_get_softc(crtc->dev); + + memset(&win, 0, sizeof(win)); + win.src_x = src_x >> 16; + win.src_y = src_y >> 16; + win.src_w = src_w >> 16; + win.src_h = src_h >> 16; + win.dst_x = crtc_x; + win.dst_y = crtc_y; + win.dst_w = crtc_w; + win.dst_h = crtc_h; + + rv = dc_parse_drm_format(fb, &win); + if (rv != 0) { + DRM_WARNING("unsupported pixel format %d\n", + fb->drm_fb.pixel_format); + return (rv); + } + + dc_setup_window(sc, plane->index, &win); + + WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_UPDATE << plane->index); + WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_ACT_REQ << plane->index); + + return (0); +} + +static int +dc_plane_disable(struct drm_plane *drm_plane) +{ + struct tegra_plane *plane; + struct tegra_crtc *crtc; + struct dc_softc *sc; + uint32_t val, idx; + + if (drm_plane->crtc == NULL) + return (0); + plane = container_of(drm_plane, struct tegra_plane, drm_plane); + crtc = container_of(drm_plane->crtc, struct tegra_crtc, drm_crtc); + + sc = device_get_softc(crtc->dev); + idx = plane->index; + + LOCK(sc); + + WR4(sc, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT << idx); + + val = RD4(sc, DC_WINC_WIN_OPTIONS); + val &= ~WIN_ENABLE; + WR4(sc, DC_WINC_WIN_OPTIONS, val); + + UNLOCK(sc); + + WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_UPDATE << idx); + WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_ACT_REQ << idx); + + return (0); +} + +static void +dc_plane_destroy(struct drm_plane *plane) +{ + + dc_plane_disable(plane); + drm_plane_cleanup(plane); + free(plane, DRM_MEM_KMS); +} + +static const struct drm_plane_funcs dc_plane_funcs = { + .update_plane = dc_plane_update, + .disable_plane = dc_plane_disable, + .destroy = dc_plane_destroy, +}; + +/* ------------------------------------------------------------------- + * + * CRTC helper functions. + * + */ +static void +dc_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + /* Empty function */ +} + +static bool +dc_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, + struct drm_display_mode *adjusted) +{ + + return (true); +} + +static int +dc_set_base(struct dc_softc *sc, int x, int y, struct tegra_fb *fb) +{ + struct dc_window win; + int rv; + + memset(&win, 0, sizeof(win)); + win.src_x = x; + win.src_y = y; + win.src_w = fb->drm_fb.width; + win.src_h = fb->drm_fb.height; + win.dst_x = x; + win.dst_y = y; + win.dst_w = fb->drm_fb.width; + win.dst_h = fb->drm_fb.height; + + rv = dc_parse_drm_format(fb, &win); + if (rv != 0) { + DRM_WARNING("unsupported pixel format %d\n", + fb->drm_fb.pixel_format); + return (rv); + } + dc_setup_window(sc, 0, &win); + + return (0); +} + +static int +dc_crtc_mode_set(struct drm_crtc *drm_crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct dc_softc *sc; + struct tegra_crtc *crtc; + struct tegra_fb *fb; + struct dc_window win; + uint32_t div, h_ref_to_sync, v_ref_to_sync; + int rv; + + crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); + sc = device_get_softc(crtc->dev); + fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb); + + + h_ref_to_sync = 1; + v_ref_to_sync = 1; + /* Setup timing */ + rv = dc_setup_clk(sc, drm_crtc, mode, &div); + if (rv != 0) { + device_printf(sc->dev, "Cannot set pixel clock\n"); + return (rv); + } + + /* Timing */ + WR4(sc, DC_DISP_DISP_TIMING_OPTIONS, 0); + + WR4(sc, DC_DISP_REF_TO_SYNC, + (v_ref_to_sync << 16) | + h_ref_to_sync); + + WR4(sc, DC_DISP_SYNC_WIDTH, + ((mode->vsync_end - mode->vsync_start) << 16) | + ((mode->hsync_end - mode->hsync_start) << 0)); + + WR4(sc, DC_DISP_BACK_PORCH, + ((mode->vtotal - mode->vsync_end) << 16) | + ((mode->htotal - mode->hsync_end) << 0)); + + WR4(sc, DC_DISP_FRONT_PORCH, + ((mode->vsync_start - mode->vdisplay) << 16) | + ((mode->hsync_start - mode->hdisplay) << 0)); + + WR4(sc, DC_DISP_DISP_ACTIVE, + (mode->vdisplay << 16) | mode->hdisplay); + + WR4(sc, DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT(DF1P1C)); + + WR4(sc,DC_DISP_DISP_CLOCK_CONTROL, + SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER(PCD1)); + + memset(&win, 0, sizeof(win)); + win.src_x = x; + win.src_y = y; + win.src_w = mode->hdisplay; + win.src_h = mode->vdisplay; + win.dst_x = x; + win.dst_y = y; + win.dst_w = mode->hdisplay; + win.dst_h = mode->vdisplay; + + rv = dc_parse_drm_format(fb, &win); + if (rv != 0) { + DRM_WARNING("unsupported pixel format %d\n", + drm_crtc->fb->pixel_format); + return (rv); + } + + dc_setup_window(sc, 0, &win); + + return (0); + +} + +static int +dc_crtc_mode_set_base(struct drm_crtc *drm_crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct dc_softc *sc; + struct tegra_crtc *crtc; + struct tegra_fb *fb; + int rv; + + crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); + fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb); + sc = device_get_softc(crtc->dev); + + rv = dc_set_base(sc, x, y, fb); + + /* Commit */ + WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE); + WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ); + return (rv); +} + + +static void +dc_crtc_prepare(struct drm_crtc *drm_crtc) +{ + + struct dc_softc *sc; + struct tegra_crtc *crtc; + uint32_t val; + + crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); + sc = device_get_softc(crtc->dev); + + WR4(sc, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL, SYNCPT_CNTRL_NO_STALL); + /* XXX allocate syncpoint from host1x */ + WR4(sc, DC_CMD_CONT_SYNCPT_VSYNC, SYNCPT_VSYNC_ENABLE | + (sc->tegra_crtc.nvidia_head == 0 ? SYNCPT_VBLANK0: SYNCPT_VBLANK1)); + + WR4(sc, DC_CMD_DISPLAY_POWER_CONTROL, + PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | + PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); + + val = RD4(sc, DC_CMD_DISPLAY_COMMAND); + val |= DISPLAY_CTRL_MODE(CTRL_MODE_C_DISPLAY); + WR4(sc, DC_CMD_DISPLAY_COMMAND, val); + + WR4(sc, DC_CMD_INT_MASK, + WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT); + + WR4(sc, DC_CMD_INT_ENABLE, + VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT); +} + +static void +dc_crtc_commit(struct drm_crtc *drm_crtc) +{ + struct dc_softc *sc; + struct tegra_crtc *crtc; + uint32_t val; + + crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); + sc = device_get_softc(crtc->dev); + + WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE); + + val = RD4(sc, DC_CMD_INT_MASK); + val |= FRAME_END_INT; + WR4(sc, DC_CMD_INT_MASK, val); + + val = RD4(sc, DC_CMD_INT_ENABLE); + val |= FRAME_END_INT; + WR4(sc, DC_CMD_INT_ENABLE, val); + + WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ); +} + +static void +dc_crtc_load_lut(struct drm_crtc *crtc) +{ + + /* empty function */ +} + +static const struct drm_crtc_helper_funcs dc_crtc_helper_funcs = { + .dpms = dc_crtc_dpms, + .mode_fixup = dc_crtc_mode_fixup, + .mode_set = dc_crtc_mode_set, + .mode_set_base = dc_crtc_mode_set_base, + .prepare = dc_crtc_prepare, + .commit = dc_crtc_commit, + .load_lut = dc_crtc_load_lut, +}; + +static int +drm_crtc_index(struct drm_crtc *crtc) +{ + int idx; + struct drm_crtc *tmp; + + idx = 0; + list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) { + if (tmp == crtc) + return (idx); + idx++; + } + panic("Cannot find CRTC"); +} + +/* ------------------------------------------------------------------- + * + * Exported functions (mainly vsync related). + * + * XXX revisit this -> convert to bus methods? + */ +int +tegra_dc_get_pipe(struct drm_crtc *drm_crtc) +{ + struct tegra_crtc *crtc; + + crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); + return (crtc->nvidia_head); +} + +void +tegra_dc_enable_vblank(struct drm_crtc *drm_crtc) +{ + struct dc_softc *sc; + struct tegra_crtc *crtc; + uint32_t val; + + crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); + sc = device_get_softc(crtc->dev); + + LOCK(sc); + val = RD4(sc, DC_CMD_INT_MASK); + val |= VBLANK_INT; + WR4(sc, DC_CMD_INT_MASK, val); + UNLOCK(sc); +} + +void +tegra_dc_disable_vblank(struct drm_crtc *drm_crtc) +{ + struct dc_softc *sc; + struct tegra_crtc *crtc; + uint32_t val; + + crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); + sc = device_get_softc(crtc->dev); + + LOCK(sc); + val = RD4(sc, DC_CMD_INT_MASK); + val &= ~VBLANK_INT; + WR4(sc, DC_CMD_INT_MASK, val); + UNLOCK(sc); +} + +static void +dc_finish_page_flip(struct dc_softc *sc) +{ + struct drm_crtc *drm_crtc; + struct drm_device *drm; + struct tegra_fb *fb; + struct tegra_bo *bo; + uint32_t base; + int idx; + + drm_crtc = &sc->tegra_crtc.drm_crtc; + drm = drm_crtc->dev; + fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb); + + mtx_lock(&drm->event_lock); + + if (sc->event == NULL) { + mtx_unlock(&drm->event_lock); + return; + } + + LOCK(sc); + /* Read active copy of WINBUF_START_ADDR */ + WR4(sc, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT); + WR4(sc, DC_CMD_STATE_ACCESS, READ_MUX); + base = RD4(sc, DC_WINBUF_START_ADDR); + WR4(sc, DC_CMD_STATE_ACCESS, 0); + UNLOCK(sc); + + /* Is already active */ + bo = tegra_fb_get_plane(fb, 0); + if (base == (bo->pbase + fb->drm_fb.offsets[0])) { + idx = drm_crtc_index(drm_crtc); + drm_send_vblank_event(drm, idx, sc->event); + drm_vblank_put(drm, idx); + sc->event = NULL; + } + + mtx_unlock(&drm->event_lock); +} + + +void +tegra_dc_cancel_page_flip(struct drm_crtc *drm_crtc, struct drm_file *file) +{ + struct dc_softc *sc; + struct tegra_crtc *crtc; + struct drm_device *drm; + + crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); + sc = device_get_softc(crtc->dev); + drm = drm_crtc->dev; + mtx_lock(&drm->event_lock); + + if ((sc->event != NULL) && (sc->event->base.file_priv == file)) { + sc->event->base.destroy(&sc->event->base); + drm_vblank_put(drm, drm_crtc_index(drm_crtc)); + sc->event = NULL; + } + mtx_unlock(&drm->event_lock); +} + +/* ------------------------------------------------------------------- + * + * CRTC functions. + * + */ +static int +dc_page_flip(struct drm_crtc *drm_crtc, struct drm_framebuffer *drm_fb, + struct drm_pending_vblank_event *event) +{ + struct dc_softc *sc; + struct tegra_crtc *crtc; + struct tegra_fb *fb; + struct drm_device *drm; + + crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); + sc = device_get_softc(crtc->dev); + fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb); + drm = drm_crtc->dev; + + if (sc->event != NULL) + return (-EBUSY); + + if (event != NULL) { + event->pipe = sc->tegra_crtc.nvidia_head; + sc->event = event; + drm_vblank_get(drm, event->pipe); + } + + dc_set_base(sc, drm_crtc->x, drm_crtc->y, fb); + drm_crtc->fb = drm_fb; + + /* Commit */ + WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE); + + return (0); +} + +static int +dc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file, + uint32_t handle, uint32_t width, uint32_t height) +{ + + struct dc_softc *sc; + struct tegra_crtc *crtc; + struct drm_gem_object *gem; + struct tegra_bo *bo; + int i; + uint32_t val, *src, *dst; + + crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); + sc = device_get_softc(crtc->dev); + + if (width != height) + return (-EINVAL); + + switch (width) { + case 32: + val = CURSOR_SIZE(C32x32); + break; + case 64: + val = CURSOR_SIZE(C64x64); + break; + case 128: + val = CURSOR_SIZE(C128x128); + break; + case 256: + val = CURSOR_SIZE(C256x256); + break; + default: + return (-EINVAL); + } + + bo = NULL; + gem = NULL; + if (handle != 0) { + gem = drm_gem_object_lookup(drm_crtc->dev, file, handle); + if (gem == NULL) + return (-ENOENT); + bo = container_of(gem, struct tegra_bo, gem_obj); + } + + if (sc->cursor_gem != NULL) { + drm_gem_object_unreference(sc->cursor_gem); + } + sc->cursor_gem = gem; + + if (bo != NULL) { + /* + * Copy cursor into cache and convert it from ARGB to RGBA. + * XXXX - this is broken by design - client can write to BO at + * any time. We can dedicate other window for cursor or switch + * to sw cursor in worst case. + */ + src = (uint32_t *)bo->vbase; + dst = (uint32_t *)crtc->cursor_vbase; + for (i = 0; i < width * height; i++) + dst[i] = (src[i] << 8) | (src[i] >> 24); + + val |= CURSOR_CLIP(CC_DISPLAY); + val |= CURSOR_START_ADDR(crtc->cursor_pbase); + WR4(sc, DC_DISP_CURSOR_START_ADDR, val); + + val = RD4(sc, DC_DISP_BLEND_CURSOR_CONTROL); + val &= ~CURSOR_DST_BLEND_FACTOR_SELECT(~0); + val &= ~CURSOR_SRC_BLEND_FACTOR_SELECT(~0); + val |= CURSOR_MODE_SELECT; + val |= CURSOR_DST_BLEND_FACTOR_SELECT(DST_NEG_K1_TIMES_SRC); + val |= CURSOR_SRC_BLEND_FACTOR_SELECT(SRC_BLEND_K1_TIMES_SRC); + val |= CURSOR_ALPHA(~0); + WR4(sc, DC_DISP_BLEND_CURSOR_CONTROL, val); + + val = RD4(sc, DC_DISP_DISP_WIN_OPTIONS); + val |= CURSOR_ENABLE; + WR4(sc, DC_DISP_DISP_WIN_OPTIONS, val); + } else { + val = RD4(sc, DC_DISP_DISP_WIN_OPTIONS); + val &= ~CURSOR_ENABLE; + WR4(sc, DC_DISP_DISP_WIN_OPTIONS, val); + } + + /* XXX This fixes cursor underflow issues, but why ? */ + WR4(sc, DC_DISP_CURSOR_UNDERFLOW_CTRL, CURSOR_UFLOW_CYA); + + WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | CURSOR_UPDATE ); + WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | CURSOR_ACT_REQ); + return (0); +} + +static int +dc_cursor_move(struct drm_crtc *drm_crtc, int x, int y) +{ + struct dc_softc *sc; + struct tegra_crtc *crtc; + + crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc); + sc = device_get_softc(crtc->dev); + WR4(sc, DC_DISP_CURSOR_POSITION, CURSOR_POSITION(x, y)); + + WR4(sc, DC_CMD_STATE_CONTROL, CURSOR_UPDATE); + WR4(sc, DC_CMD_STATE_CONTROL, CURSOR_ACT_REQ); + + return (0); +} + +static void +dc_destroy(struct drm_crtc *crtc) +{ + + drm_crtc_cleanup(crtc); + memset(crtc, 0, sizeof(*crtc)); +} + +static const struct drm_crtc_funcs dc_crtc_funcs = { + .page_flip = dc_page_flip, + .cursor_set = dc_cursor_set, + .cursor_move = dc_cursor_move, + .set_config = drm_crtc_helper_set_config, + .destroy = dc_destroy, +}; + +/* ------------------------------------------------------------------- + * + * Bus and infrastructure. + * + */ +static int +dc_init_planes(struct dc_softc *sc, struct tegra_drm *drm) +{ + int i, rv; + struct tegra_plane *plane; + + rv = 0; + for (i = 0; i < DC_MAX_PLANES; i++) { + plane = malloc(sizeof(*plane), DRM_MEM_KMS, M_WAITOK | M_ZERO); + plane->index = i + 1; + rv = drm_plane_init(&drm->drm_dev, &plane->drm_plane, + 1 << sc->tegra_crtc.nvidia_head, &dc_plane_funcs, + dc_plane_formats, nitems(dc_plane_formats), false); + if (rv != 0) { + free(plane, DRM_MEM_KMS); + return (rv); + } + } + return 0; +} + +static void +dc_display_enable(device_t dev, bool enable) +{ + struct dc_softc *sc; + uint32_t val; + + sc = device_get_softc(dev); + + /* Set display mode */ + val = enable ? CTRL_MODE_C_DISPLAY: CTRL_MODE_STOP; + WR4(sc, DC_CMD_DISPLAY_COMMAND, DISPLAY_CTRL_MODE(val)); + + /* and commit it*/ + WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE); + WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ); +} + +static void +dc_hdmi_enable(device_t dev, bool enable) +{ + struct dc_softc *sc; + uint32_t val; + + sc = device_get_softc(dev); + + val = RD4(sc, DC_DISP_DISP_WIN_OPTIONS); + if (enable) + val |= HDMI_ENABLE; + else + val &= ~HDMI_ENABLE; + WR4(sc, DC_DISP_DISP_WIN_OPTIONS, val); + +} + +static void +dc_setup_timing(device_t dev, int h_pulse_start) +{ + struct dc_softc *sc; + + sc = device_get_softc(dev); + + /* Setup display timing */ + WR4(sc, DC_DISP_DISP_TIMING_OPTIONS, VSYNC_H_POSITION(1)); + WR4(sc, DC_DISP_DISP_COLOR_CONTROL, + DITHER_CONTROL(DITHER_DISABLE) | BASE_COLOR_SIZE(SIZE_BASE888)); + + WR4(sc, DC_DISP_DISP_SIGNAL_OPTIONS0, H_PULSE2_ENABLE); + WR4(sc, DC_DISP_H_PULSE2_CONTROL, + PULSE_CONTROL_QUAL(QUAL_VACTIVE) | PULSE_CONTROL_LAST(LAST_END_A)); + + WR4(sc, DC_DISP_H_PULSE2_POSITION_A, + PULSE_START(h_pulse_start) | PULSE_END(h_pulse_start + 8)); +} + +static void +dc_intr(void *arg) +{ + struct dc_softc *sc; + uint32_t status; + + sc = arg; + + /* Confirm interrupt */ + status = RD4(sc, DC_CMD_INT_STATUS); + WR4(sc, DC_CMD_INT_STATUS, status); + if (status & VBLANK_INT) { + drm_handle_vblank(sc->tegra_crtc.drm_crtc.dev, + sc->tegra_crtc.nvidia_head); + dc_finish_page_flip(sc); + } +} + +static int +dc_init_client(device_t dev, device_t host1x, struct tegra_drm *drm) +{ + struct dc_softc *sc; + int rv; + + sc = device_get_softc(dev); + + if (drm->pitch_align < sc->pitch_align) + drm->pitch_align = sc->pitch_align; + + drm_crtc_init(&drm->drm_dev, &sc->tegra_crtc.drm_crtc, &dc_crtc_funcs); + drm_mode_crtc_set_gamma_size(&sc->tegra_crtc.drm_crtc, 256); + drm_crtc_helper_add(&sc->tegra_crtc.drm_crtc, &dc_crtc_helper_funcs); + + rv = dc_init_planes(sc, drm); + if (rv!= 0){ + device_printf(dev, "Cannot init planes\n"); + return (rv); + } + + WR4(sc, DC_CMD_INT_TYPE, + WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT); + + WR4(sc, DC_CMD_INT_POLARITY, + WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT); + + WR4(sc, DC_CMD_INT_ENABLE, 0); + WR4(sc, DC_CMD_INT_MASK, 0); + + rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, dc_intr, sc, &sc->irq_ih); + if (rv != 0) { + device_printf(dev, "Cannot register interrupt handler\n"); + return (rv); + } + + /* allocate memory for cursor cache */ + sc->tegra_crtc.cursor_vbase = kmem_alloc_contig(kernel_arena, + 256 * 256 * 4, M_WAITOK | M_ZERO, + 0, -1UL, PAGE_SIZE, 0, VM_MEMATTR_WRITE_COMBINING); + sc->tegra_crtc.cursor_pbase = vtophys(sc->tegra_crtc.cursor_vbase); + return (0); +} + +static int +dc_exit_client(device_t dev, device_t host1x, struct tegra_drm *drm) +{ + struct dc_softc *sc; + + sc = device_get_softc(dev); + + if (sc->irq_ih != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); + sc->irq_ih = NULL; + + return (0); +} + +static int +get_fdt_resources(struct dc_softc *sc, phandle_t node) +{ + int rv; + + rv = hwreset_get_by_ofw_name(sc->dev, 0, "dc", &sc->hwreset_dc); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'dc' reset\n"); + return (rv); + } + rv = clk_get_by_ofw_name(sc->dev, 0, "parent", &sc->clk_parent); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'parent' clock\n"); + return (rv); + } + rv = clk_get_by_ofw_name(sc->dev, 0, "dc", &sc->clk_dc); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'dc' clock\n"); + return (rv); + } + + rv = OF_getencprop(node, "nvidia,head", &sc->tegra_crtc.nvidia_head, + sizeof(sc->tegra_crtc.nvidia_head)); + if (rv <= 0) { + device_printf(sc->dev, + "Cannot get 'nvidia,head' property\n"); + return (rv); + } + return (0); +} + +static int +enable_fdt_resources(struct dc_softc *sc) +{ + int id, rv; + + rv = clk_set_parent_by_clk(sc->clk_dc, sc->clk_parent); + if (rv != 0) { + device_printf(sc->dev, "Cannot set parent for 'dc' clock\n"); + return (rv); + } + + id = (sc->tegra_crtc.nvidia_head == 0) ? + TEGRA_POWERGATE_DIS: TEGRA_POWERGATE_DISB; + rv = tegra_powergate_sequence_power_up(id, sc->clk_dc, sc->hwreset_dc); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'DIS' powergate\n"); + return (rv); + } + + return (0); +} + +static int +dc_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 Display Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +dc_attach(device_t dev) +{ + struct dc_softc *sc; + phandle_t node; + int rid, rv; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->tegra_crtc.dev = dev; + + node = ofw_bus_get_node(sc->dev); + LOCK_INIT(sc); + + 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; + } + + rv = get_fdt_resources(sc, node); + if (rv != 0) { + device_printf(dev, "Cannot parse FDT resources\n"); + goto fail; + } + rv = enable_fdt_resources(sc); + if (rv != 0) { + device_printf(dev, "Cannot enable FDT resources\n"); + goto fail; + } + + /* + * Tegra124 + * - 64 for RGB modes + * - 128 for YUV planar modes + * - 256 for block linear modes + */ + sc->pitch_align = 256; + + rv = TEGRA_DRM_REGISTER_CLIENT(device_get_parent(sc->dev), sc->dev); + if (rv != 0) { + device_printf(dev, "Cannot register DRM device\n"); + goto fail; + } + + return (bus_generic_attach(dev)); + +fail: + TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev); + if (sc->irq_ih != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); + if (sc->clk_parent != NULL) + clk_release(sc->clk_parent); + if (sc->clk_dc != NULL) + clk_release(sc->clk_dc); + if (sc->hwreset_dc != NULL) + hwreset_release(sc->hwreset_dc); + 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 (ENXIO); +} + +static int +dc_detach(device_t dev) +{ + struct dc_softc *sc; + + sc = device_get_softc(dev); + + TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev); + + if (sc->irq_ih != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); + if (sc->clk_parent != NULL) + clk_release(sc->clk_parent); + if (sc->clk_dc != NULL) + clk_release(sc->clk_dc); + if (sc->hwreset_dc != NULL) + hwreset_release(sc->hwreset_dc); + 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_dc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dc_probe), + DEVMETHOD(device_attach, dc_attach), + DEVMETHOD(device_detach, dc_detach), + + /* tegra drm interface */ + DEVMETHOD(tegra_drm_init_client, dc_init_client), + DEVMETHOD(tegra_drm_exit_client, dc_exit_client), + + /* tegra dc interface */ + DEVMETHOD(tegra_dc_display_enable, dc_display_enable), + DEVMETHOD(tegra_dc_hdmi_enable, dc_hdmi_enable), + DEVMETHOD(tegra_dc_setup_timing, dc_setup_timing), + + DEVMETHOD_END +}; + +static devclass_t tegra_dc_devclass; +DEFINE_CLASS_0(tegra_dc, tegra_dc_driver, tegra_dc_methods, + sizeof(struct dc_softc)); +DRIVER_MODULE(tegra_dc, host1x, tegra_dc_driver, tegra_dc_devclass, NULL, NULL); Property changes on: trunk/sys/arm/nvidia/drm2/tegra_dc.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/drm2/tegra_dc_if.m =================================================================== --- trunk/sys/arm/nvidia/drm2/tegra_dc_if.m (rev 0) +++ trunk/sys/arm/nvidia/drm2/tegra_dc_if.m 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,58 @@ +/* $MidnightBSD$ */ +#- +# 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. +# +# $FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_dc_if.m 310600 2016-12-26 14:36:05Z mmel $ +# + +#include + +INTERFACE tegra_dc; + + +METHOD void write_4{ + device_t dev; + bus_size_t offset; + uint32_t val; +}; +METHOD uint32_t read_4{ + device_t dev; + bus_size_t offset; +}; + +METHOD void display_enable{ + device_t dev; + bool enable; +}; + +METHOD void hdmi_enable{ + device_t dev; + bool enable; +}; + +METHOD void setup_timing{ + device_t dev; + int h_pulse_start; +}; Property changes on: trunk/sys/arm/nvidia/drm2/tegra_dc_if.m ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: trunk/sys/arm/nvidia/drm2/tegra_dc_reg.h =================================================================== --- trunk/sys/arm/nvidia/drm2/tegra_dc_reg.h (rev 0) +++ trunk/sys/arm/nvidia/drm2/tegra_dc_reg.h 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,401 @@ +/* $MidnightBSD$ */ +/*- + * Copyright 1992-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. + * + * $FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_dc_reg.h 310600 2016-12-26 14:36:05Z mmel $ + */ +#ifndef _TEGRA_DC_REG_H_ +#define _TEGRA_DC_REG_H_ + +/* + * !!! WARNING !!! + * Tegra manual uses registers index (and not register addreses). + * We follow the TRM notation and index is converted to offset in + * WR4 / RD4 macros + */ + +/* --------------------------- DC CMD -------------------------------------- */ +#define DC_CMD_GENERAL_INCR_SYNCPT 0x000 +#define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL 0x001 +#define SYNCPT_CNTRL_NO_STALL (1 << 8) +#define SYNCPT_CNTRL_SOFT_RESET (1 << 0) + +#define DC_CMD_GENERAL_INCR_SYNCPT_ERROR 0x002 +#define DC_CMD_WIN_A_INCR_SYNCPT 0x008 +#define DC_CMD_WIN_A_INCR_SYNCPT_CNTRL 0x009 +#define DC_CMD_WIN_A_INCR_SYNCPT_ERROR 0x00a +#define DC_CMD_WIN_B_INCR_SYNCPT 0x010 +#define DC_CMD_WIN_B_INCR_SYNCPT_CNTRL 0x011 +#define DC_CMD_WIN_B_INCR_SYNCPT_ERROR 0x012 +#define DC_CMD_WIN_C_INCR_SYNCPT 0x018 +#define DC_CMD_WIN_C_INCR_SYNCPT_CNTRL 0x019 +#define DC_CMD_WIN_C_INCR_SYNCPT_ERROR 0x01a +#define DC_CMD_CONT_SYNCPT_VSYNC 0x028 +#define SYNCPT_VSYNC_ENABLE (1 << 8) + +#define DC_CMD_CTXSW 0x030 +#define DC_CMD_DISPLAY_COMMAND_OPTION0 0x031 +#define DC_CMD_DISPLAY_COMMAND 0x032 +#define DISPLAY_CTRL_MODE(x) ((x) << 5) +#define CTRL_MODE_STOP 0 +#define CTRL_MODE_C_DISPLAY 1 +#define CTRL_MODE_NC_DISPLAY 2 + +#define DC_CMD_SIGNAL_RAISE 0x033 +#define DC_CMD_DISPLAY_POWER_CONTROL 0x036 +#define PM1_ENABLE (1 << 18) +#define PM0_ENABLE (1 << 16) +#define PW4_ENABLE (1 << 8) +#define PW3_ENABLE (1 << 6) +#define PW2_ENABLE (1 << 4) +#define PW1_ENABLE (1 << 2) +#define PW0_ENABLE (1 << 0) + +#define DC_CMD_INT_STATUS 0x037 +#define DC_CMD_INT_MASK 0x038 +#define DC_CMD_INT_ENABLE 0x039 +#define DC_CMD_INT_TYPE 0x03a +#define DC_CMD_INT_POLARITY 0x03b +#define WIN_T_UF_INT (1 << 25) +#define WIN_D_UF_INT (1 << 24) +#define HC_UF_INT (1 << 23) +#define CMU_LUT_CONFLICT_INT (1 << 22) +#define WIN_C_OF_INT (1 << 16) +#define WIN_B_OF_INT (1 << 15) +#define WIN_A_OF_INT (1 << 14) +#define SSF_INT (1 << 13) +#define MSF_INT (1 << 12) +#define WIN_C_UF_INT (1 << 10) +#define WIN_B_UF_INT (1 << 9) +#define WIN_A_UF_INT (1 << 8) +#define SPI_BUSY_INT (1 << 6) +#define V_PULSE2_INT (1 << 5) +#define V_PULSE3_INT (1 << 4) +#define HBLANK_INT (1 << 3) +#define VBLANK_INT (1 << 2) +#define FRAME_END_INT (1 << 1) + +#define DC_CMD_STATE_ACCESS 0x040 +#define WRITE_MUX (1 << 2) +#define READ_MUX (1 << 0) + +#define DC_CMD_STATE_CONTROL 0x041 +#define NC_HOST_TRIG (1 << 24) +#define CURSOR_UPDATE (1 << 15) +#define WIN_C_UPDATE (1 << 11) +#define WIN_B_UPDATE (1 << 10) +#define WIN_A_UPDATE (1 << 9) +#define WIN_UPDATE(x) (1 << (9 + (x))) +#define GENERAL_UPDATE (1 << 8) +#define CURSOR_ACT_REQ (1 << 7) +#define WIN_D_ACT_REQ (1 << 4) +#define WIN_C_ACT_REQ (1 << 3) +#define WIN_B_ACT_REQ (1 << 2) +#define WIN_A_ACT_REQ (1 << 1) +#define WIN_ACT_REQ(x) (1 << (1 + (x))) +#define GENERAL_ACT_REQ (1 << 0) + +#define DC_CMD_DISPLAY_WINDOW_HEADER 0x042 +#define WINDOW_D_SELECT (1 << 7) +#define WINDOW_C_SELECT (1 << 6) +#define WINDOW_B_SELECT (1 << 5) +#define WINDOW_A_SELECT (1 << 4) +#define WINDOW_SELECT(x) (1 << (4 + (x))) + +#define DC_CMD_REG_ACT_CONTROL 0x043 +#define DC_CMD_WIN_D_INCR_SYNCPT 0x04c +#define DC_CMD_WIN_D_INCR_SYNCPT_CNTRL 0x04d +#define DC_CMD_WIN_D_INCR_SYNCPT_ERROR 0x04e + +/* ---------------------------- DC COM ------------------------------------- */ + +/* --------------------------- DC DISP ------------------------------------- */ + +#define DC_DISP_DISP_SIGNAL_OPTIONS0 0x400 +#define M1_ENABLE (1 << 26) +#define M0_ENABLE (1 << 24) +#define V_PULSE2_ENABLE (1 << 18) +#define V_PULSE1_ENABLE (1 << 16) +#define V_PULSE0_ENABLE (1 << 14) +#define H_PULSE2_ENABLE (1 << 12) +#define H_PULSE1_ENABLE (1 << 10) +#define H_PULSE0_ENABLE (1 << 8) + +#define DC_DISP_DISP_SIGNAL_OPTIONS1 0x401 + +#define DC_DISP_DISP_WIN_OPTIONS 0x402 +#define HDMI_ENABLE (1 << 30) +#define DSI_ENABLE (1 << 29) +#define SOR1_TIMING_CYA (1 << 27) +#define SOR1_ENABLE (1 << 26) +#define SOR_ENABLE (1 << 25) +#define CURSOR_ENABLE (1 << 16) + +#define DC_DISP_DISP_TIMING_OPTIONS 0x405 +#define VSYNC_H_POSITION(x) (((x) & 0xfff) << 0) + +#define DC_DISP_REF_TO_SYNC 0x406 +#define DC_DISP_SYNC_WIDTH 0x407 +#define DC_DISP_BACK_PORCH 0x408 +#define DC_DISP_DISP_ACTIVE 0x409 +#define DC_DISP_FRONT_PORCH 0x40a +#define DC_DISP_H_PULSE0_CONTROL 0x40b +#define DC_DISP_H_PULSE0_POSITION_A 0x40c +#define DC_DISP_H_PULSE0_POSITION_B 0x40d +#define DC_DISP_H_PULSE0_POSITION_C 0x40e +#define DC_DISP_H_PULSE0_POSITION_D 0x40f +#define DC_DISP_H_PULSE1_CONTROL 0x410 +#define DC_DISP_H_PULSE1_POSITION_A 0x411 +#define DC_DISP_H_PULSE1_POSITION_B 0x412 +#define DC_DISP_H_PULSE1_POSITION_C 0x413 +#define DC_DISP_H_PULSE1_POSITION_D 0x414 +#define DC_DISP_H_PULSE2_CONTROL 0x415 +#define DC_DISP_H_PULSE2_POSITION_A 0x416 +#define DC_DISP_H_PULSE2_POSITION_B 0x417 +#define DC_DISP_H_PULSE2_POSITION_C 0x418 +#define DC_DISP_H_PULSE2_POSITION_D 0x419 +#define DC_DISP_V_PULSE0_CONTROL 0x41a +#define DC_DISP_V_PULSE0_POSITION_A 0x41b +#define DC_DISP_V_PULSE0_POSITION_B 0x41c +#define DC_DISP_V_PULSE0_POSITION_C 0x41d +#define DC_DISP_V_PULSE1_CONTROL 0x41e +#define DC_DISP_V_PULSE1_POSITION_A 0x41f +#define DC_DISP_V_PULSE1_POSITION_B 0x420 +#define DC_DISP_V_PULSE1_POSITION_C 0x421 +#define DC_DISP_V_PULSE2_CONTROL 0x422 +#define DC_DISP_V_PULSE2_POSITION_A 0x423 +#define DC_DISP_V_PULSE3_CONTROL 0x424 +#define PULSE_CONTROL_LAST(x) (((x) & 0x7f) << 8) +#define LAST_START_A 0 +#define LAST_END_A 1 +#define LAST_START_B 2 +#define LAST_END_B 3 +#define LAST_START_C 4 +#define LAST_END_C 5 +#define LAST_START_D 6 +#define LAST_END_D 7 +#define PULSE_CONTROL_QUAL(x) (((x) & 0x3) << 8) +#define QUAL_ALWAYS 0 +#define QUAL_VACTIVE 2 +#define QUAL_VACTIVE1 3 +#define PULSE_POLARITY (1 << 4) +#define PULSE_MODE (1 << 3) + +#define DC_DISP_V_PULSE3_POSITION_A 0x425 +#define PULSE_END(x) (((x) & 0xfff) << 16) +#define PULSE_START(x) (((x) & 0xfff) << 0) + + +#define DC_DISP_DISP_CLOCK_CONTROL 0x42e +#define PIXEL_CLK_DIVIDER(x) (((x) & 0xf) << 8) +#define PCD1 0 +#define PCD1H 1 +#define PCD2 2 +#define PCD3 3 +#define PCD4 4 +#define PCD6 5 +#define PCD8 6 +#define PCD9 7 +#define PCD12 8 +#define PCD16 9 +#define PCD18 10 +#define PCD24 11 +#define PCD13 12 +#define SHIFT_CLK_DIVIDER(x) ((x) & 0xff) + +#define DC_DISP_DISP_INTERFACE_CONTROL 0x42f +#define DISP_ORDER_BLUE_RED ( 1 << 9) +#define DISP_ALIGNMENT_LSB ( 1 << 8) +#define DISP_DATA_FORMAT(x) (((x) & 0xf) << 8) +#define DF1P1C 0 +#define DF1P2C24B 1 +#define DF1P2C18B 2 +#define DF1P2C16B 3 +#define DF1S 4 +#define DF2S 5 +#define DF3S 6 +#define DFSPI 7 +#define DF1P3C24B 8 +#define DF2P1C18B 9 +#define DFDUAL1P1C18B 10 + +#define DC_DISP_DISP_COLOR_CONTROL 0x430 +#define NON_BASE_COLOR (1 << 18) +#define BLANK_COLOR (1 << 17) +#define DISP_COLOR_SWAP (1 << 16) +#define ORD_DITHER_ROTATION(x) (((x) & 0x3) << 12) +#define DITHER_CONTROL(x) (((x) & 0x3) << 8) +#define DITHER_DISABLE 0 +#define DITHER_ORDERED 2 +#define DITHER_TEMPORAL 3 +#define BASE_COLOR_SIZE(x) (((x) & 0xF) << 0) +#define SIZE_BASE666 0 +#define SIZE_BASE111 1 +#define SIZE_BASE222 2 +#define SIZE_BASE333 3 +#define SIZE_BASE444 4 +#define SIZE_BASE555 5 +#define SIZE_BASE565 6 +#define SIZE_BASE332 7 +#define SIZE_BASE888 8 + +#define DC_DISP_CURSOR_START_ADDR 0x43e +#define CURSOR_CLIP(x) (((x) & 0x3) << 28) +#define CC_DISPLAY 0 +#define CC_WA 1 +#define CC_WB 2 +#define CC_WC 3 +#define CURSOR_SIZE(x) (((x) & 0x3) << 24) +#define C32x32 0 +#define C64x64 1 +#define C128x128 2 +#define C256x256 3 +#define CURSOR_START_ADDR(x) (((x) >> 10) & 0x3FFFFF) + +#define DC_DISP_CURSOR_POSITION 0x440 +#define CURSOR_POSITION(h, v) ((((h) & 0x3fff) << 0) | \ + (((v) & 0x3fff) << 16)) +#define DC_DISP_CURSOR_UNDERFLOW_CTRL 0x4eb +#define DC_DISP_BLEND_CURSOR_CONTROL 0x4f1 +#define CURSOR_MODE_SELECT (1 << 24) +#define CURSOR_DST_BLEND_FACTOR_SELECT(x) (((x) & 0x3) << 16) +#define DST_BLEND_ZERO 0 +#define DST_BLEND_K1 1 +#define DST_NEG_K1_TIMES_SRC 2 +#define CURSOR_SRC_BLEND_FACTOR_SELECT(x) (((x) & 0x3) << 8) +#define SRC_BLEND_K1 0 +#define SRC_BLEND_K1_TIMES_SRC 1 +#define CURSOR_ALPHA(x) (((x) & 0xFF) << 0) + +#define DC_DISP_CURSOR_UFLOW_DBG_PIXEL 0x4f3 +#define CURSOR_UFLOW_CYA (1 << 7) +#define CURSOR_UFLOW_CTRL_DBG_MODE (1 << 0) +/* --------------------------- DC WIN ------------------------------------- */ + +#define DC_WINC_COLOR_PALETTE 0x500 +#define DC_WINC_CSC_YOF 0x611 +#define DC_WINC_CSC_KYRGB 0x612 +#define DC_WINC_CSC_KUR 0x613 +#define DC_WINC_CSC_KVR 0x614 +#define DC_WINC_CSC_KUG 0x615 +#define DC_WINC_CSC_KVG 0x616 +#define DC_WINC_CSC_KUB 0x617 +#define DC_WINC_CSC_KVB 0x618 + +#define DC_WINC_WIN_OPTIONS 0x700 +#define H_FILTER_MODE (1U << 31) +#define WIN_ENABLE (1 << 30) +#define INTERLACE_ENABLE (1 << 23) +#define YUV_RANGE_EXPAND (1 << 22) +#define DV_ENABLE (1 << 20) +#define CSC_ENABLE (1 << 18) +#define CP_ENABLE (1 << 16) +#define V_FILTER_UV_ALIGN (1 << 14) +#define V_FILTER_OPTIMIZE (1 << 12) +#define V_FILTER_ENABLE (1 << 10) +#define H_FILTER_ENABLE (1 << 8) +#define COLOR_EXPAND (1 << 6) +#define SCAN_COLUMN (1 << 4) +#define V_DIRECTION (1 << 2) +#define H_DIRECTION (1 << 0) + +#define DC_WIN_BYTE_SWAP 0x701 +#define BYTE_SWAP(x) (((x) & 0x7) << 0) +#define NOSWAP 0 +#define SWAP2 1 +#define SWAP4 2 +#define SWAP4HW 3 +#define SWAP02 4 +#define SWAPLEFT 5 + +#define DC_WIN_COLOR_DEPTH 0x703 +#define WIN_COLOR_DEPTH_P8 3 +#define WIN_COLOR_DEPTH_B4G4R4A4 4 +#define WIN_COLOR_DEPTH_B5G5R5A 5 +#define WIN_COLOR_DEPTH_B5G6R5 6 +#define WIN_COLOR_DEPTH_AB5G5R5 7 +#define WIN_COLOR_DEPTH_B8G8R8A8 12 +#define WIN_COLOR_DEPTH_R8G8B8A8 13 +#define WIN_COLOR_DEPTH_YCbCr422 16 +#define WIN_COLOR_DEPTH_YUV422 17 +#define WIN_COLOR_DEPTH_YCbCr420P 18 +#define WIN_COLOR_DEPTH_YUV420P 19 +#define WIN_COLOR_DEPTH_YCbCr422P 20 +#define WIN_COLOR_DEPTH_YUV422P 21 +#define WIN_COLOR_DEPTH_YCbCr422R 22 +#define WIN_COLOR_DEPTH_YUV422R 23 +#define WIN_COLOR_DEPTH_YCbCr422RA 24 +#define WIN_COLOR_DEPTH_YUV422RA 25 + +#define DC_WIN_POSITION 0x704 +#define WIN_POSITION(h, v) ((((h) & 0x1fff) << 0) | \ + (((v) & 0x1fff) << 16)) + +#define DC_WIN_SIZE 0x705 +#define WIN_SIZE(h, v) ((((h) & 0x1fff) << 0) | \ + (((v) & 0x1fff) << 16)) + +#define DC_WIN_PRESCALED_SIZE 0x706 +#define WIN_PRESCALED_SIZE(h, v) ((((h) & 0x7fff) << 0) | \ + (((v) & 0x1fff) << 16)) + + +#define DC_WIN_H_INITIAL_DDA 0x707 +#define DC_WIN_V_INITIAL_DDA 0x708 +#define DC_WIN_DDA_INCREMENT 0x709 +#define WIN_DDA_INCREMENT(h, v) ((((h) & 0xffff) << 0) | \ + (((v) & 0xffff) << 16)) +#define DC_WIN_LINE_STRIDE 0x70a + +/* -------------------------- DC WINBUF ------------------------------------ */ + +#define DC_WINBUF_START_ADDR 0x800 +#define DC_WINBUF_START_ADDR_NS 0x801 +#define DC_WINBUF_START_ADDR_U 0x802 +#define DC_WINBUF_START_ADDR_U_NS 0x803 +#define DC_WINBUF_START_ADDR_V 0x804 +#define DC_WINBUF_START_ADDR_V_NS 0x805 +#define DC_WINBUF_ADDR_H_OFFSET 0x806 +#define DC_WINBUF_ADDR_H_OFFSET_NS 0x807 +#define DC_WINBUF_ADDR_V_OFFSET 0x808 +#define DC_WINBUF_ADDR_V_OFFSET_NS 0x809 +#define DC_WINBUF_UFLOW_STATUS 0x80a +#define DC_WINBUF_SURFACE_KIND 0x80b +#define SURFACE_KIND_BLOCK_HEIGHT(x) (((x) & 0x7) << 4) +#define SURFACE_KIND_PITCH 0 +#define SURFACE_KIND_TILED 1 +#define SURFACE_KIND_BL_16B2 2 +#define DC_WINBUF_SURFACE_WEIGHT 0x80c +#define DC_WINBUF_START_ADDR_HI 0x80d +#define DC_WINBUF_START_ADDR_HI_NS 0x80e +#define DC_WINBUF_START_ADDR_U_HI 0x80f +#define DC_WINBUF_START_ADDR_U_HI_NS 0x810 +#define DC_WINBUF_START_ADDR_V_HI 0x811 +#define DC_WINBUF_START_ADDR_V_HI_NS 0x812 +#define DC_WINBUF_UFLOW_CTRL 0x824 +#define UFLOW_CTR_ENABLE (1 << 0) +#define DC_WINBUF_UFLOW_DBG_PIXEL 0x825 + +#endif /* _TEGRA_DC_REG_H_ */ Property changes on: trunk/sys/arm/nvidia/drm2/tegra_dc_reg.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/drm2/tegra_drm.h =================================================================== --- trunk/sys/arm/nvidia/drm2/tegra_drm.h (rev 0) +++ trunk/sys/arm/nvidia/drm2/tegra_drm.h 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,126 @@ +/* $MidnightBSD$ */ +/*- + * Copyright 1992-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. + * + * $FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_drm.h 310600 2016-12-26 14:36:05Z mmel $ + */ +#ifndef _TEGRA_DRM_H_ +#define _TEGRA_DRM_H_ + +#include + +struct tegra_bo { + struct drm_gem_object gem_obj; + /* mapped memory buffer */ + vm_paddr_t pbase; + vm_offset_t vbase; + size_t npages; + vm_page_t *m; + vm_object_t cdev_pager; +}; + +struct tegra_plane { + struct drm_plane drm_plane; + int index; /* Window index */ +}; + +struct tegra_fb { + struct drm_framebuffer drm_fb; + struct drm_fb_helper fb_helper; + struct tegra_bo **planes; /* Attached planes */ + int nplanes; + + /* Surface and display geometry */ + bool block_linear; /* Surface_kind */ + uint32_t block_height; + int rotation; /* In degrees */ + bool flip_x; /* Inverted X-axis */ + bool flip_y; /* Inverted Y-axis */ +}; + +struct tegra_crtc { + struct drm_crtc drm_crtc; + device_t dev; + int nvidia_head; + vm_paddr_t cursor_pbase; /* Cursor buffer */ + vm_offset_t cursor_vbase; +}; + +struct tegra_drm_encoder { + device_t dev; + + void *panel; /* XXX For LVDS panel */ + device_t ddc; + struct edid *edid; + + gpio_pin_t gpio_hpd; + + struct drm_encoder encoder; + struct drm_connector connector; + int (*setup_clock)(struct tegra_drm_encoder *output, + clk_t clk, uint64_t pclk); +}; + +struct tegra_drm { + struct drm_device drm_dev; + struct tegra_fb *fb; /* Prime framebuffer */ + int pitch_align; +}; + +/* tegra_drm_subr.c */ +int tegra_drm_encoder_attach(struct tegra_drm_encoder *output, phandle_t node); +int tegra_drm_encoder_init(struct tegra_drm_encoder *output, + struct tegra_drm *drm); +int tegra_drm_encoder_exit(struct tegra_drm_encoder *output, + struct tegra_drm *drm); +enum drm_connector_status tegra_drm_connector_detect( + struct drm_connector *connector, bool force); +int tegra_drm_connector_get_modes(struct drm_connector *connector); +struct drm_encoder *tegra_drm_connector_best_encoder( + struct drm_connector *connector); + +/* tegra_dc.c */ +void tegra_dc_cancel_page_flip(struct drm_crtc *drm_crtc, + struct drm_file *file); +void tegra_dc_enable_vblank(struct drm_crtc *drm_crtc); +void tegra_dc_disable_vblank(struct drm_crtc *drm_crtc); +int tegra_dc_get_pipe(struct drm_crtc *drm_crtc); + +/* tegra_fb.c */ +struct fb_info *tegra_drm_fb_getinfo(struct drm_device *drm); +struct tegra_bo *tegra_fb_get_plane(struct tegra_fb *fb, int idx); +int tegra_drm_fb_create(struct drm_device *drm, struct drm_file *file, + struct drm_mode_fb_cmd2 *cmd, struct drm_framebuffer **fb_res); +int tegra_drm_fb_init(struct drm_device *drm); +void tegra_drm_fb_destroy(struct drm_device *drm); + + +/* tegra_bo.c */ +struct tegra_bo; +int tegra_bo_create(struct drm_device *drm, size_t size, + struct tegra_bo **res_bo); +void tegra_bo_driver_register(struct drm_driver *drm_drv); + +#endif /* _TEGRA_DRM_H_ */ Property changes on: trunk/sys/arm/nvidia/drm2/tegra_drm.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/drm2/tegra_drm_if.m =================================================================== --- trunk/sys/arm/nvidia/drm2/tegra_drm_if.m (rev 0) +++ trunk/sys/arm/nvidia/drm2/tegra_drm_if.m 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,69 @@ +/* $MidnightBSD$ */ +#- +# 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. +# +# $FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_drm_if.m 310600 2016-12-26 14:36:05Z mmel $ +# + + +INTERFACE tegra_drm; +HEADER { + struct tegra_drm; +}; + + +/** + * Register client to host1x + */ +METHOD int register_client{ + device_t host1x; + device_t client; +}; + +/** + * Deregister client to host1x + */ +METHOD int deregister_client{ + device_t host1x; + device_t client; +}; + +/** + * Call client init method + */ +METHOD int init_client{ + device_t client; + device_t host1x; + struct tegra_drm *drm; +}; + +/** + * Call client exit method + */ +METHOD int exit_client{ + device_t client; + device_t host1x; + struct tegra_drm *drm; +}; Property changes on: trunk/sys/arm/nvidia/drm2/tegra_drm_if.m ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: trunk/sys/arm/nvidia/drm2/tegra_drm_subr.c =================================================================== --- trunk/sys/arm/nvidia/drm2/tegra_drm_subr.c (rev 0) +++ trunk/sys/arm/nvidia/drm2/tegra_drm_subr.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,178 @@ +/* $MidnightBSD$ */ +/*- + * 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_drm_subr.c 310600 2016-12-26 14:36:05Z mmel $"); + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +int +tegra_drm_connector_get_modes(struct drm_connector *connector) +{ + struct tegra_drm_encoder *output; + struct edid *edid = NULL; + int rv; + + output = container_of(connector, struct tegra_drm_encoder, + connector); + + /* Panel is first */ + if (output->panel != NULL) { + /* XXX panel parsing */ + return (0); + } + + /* static EDID is second*/ + edid = output->edid; + + /* EDID from monitor is last */ + if (edid == NULL) + edid = drm_get_edid(connector, output->ddc); + + if (edid == NULL) + return (0); + + /* Process EDID */ + drm_mode_connector_update_edid_property(connector, edid); + rv = drm_add_edid_modes(connector, edid); + drm_edid_to_eld(connector, edid); + return (rv); +} + +struct drm_encoder * +tegra_drm_connector_best_encoder(struct drm_connector *connector) +{ + struct tegra_drm_encoder *output; + + output = container_of(connector, struct tegra_drm_encoder, + connector); + + return &(output->encoder); +} + +enum drm_connector_status +tegra_drm_connector_detect(struct drm_connector *connector, bool force) +{ + struct tegra_drm_encoder *output; + bool active; + int rv; + + output = container_of(connector, struct tegra_drm_encoder, + connector); + if (output->gpio_hpd == NULL) { + return ((output->panel != NULL) ? + connector_status_connected: + connector_status_disconnected); + } + + rv = gpio_pin_is_active(output->gpio_hpd, &active); + if (rv != 0) { + device_printf(output->dev, " GPIO read failed: %d\n", rv); + return (connector_status_unknown); + } + + return (active ? + connector_status_connected : connector_status_disconnected); +} + +int +tegra_drm_encoder_attach(struct tegra_drm_encoder *output, phandle_t node) +{ + int rv; + phandle_t ddc; + + /* XXX parse output panel here */ + + rv = OF_getencprop_alloc(node, "nvidia,edid", 1, + (void **)&output->edid); + + /* EDID exist but have invalid size */ + if ((rv >= 0) && (rv != sizeof(struct edid))) { + device_printf(output->dev, + "Malformed \"nvidia,edid\" property\n"); + if (output->edid != NULL) + free(output->edid, M_OFWPROP); + return (ENXIO); + } + + gpio_pin_get_by_ofw_property(output->dev, node, "nvidia,hpd-gpio", + &output->gpio_hpd); + ddc = 0; + OF_getencprop(node, "nvidia,ddc-i2c-bus", &ddc, sizeof(ddc)); + if (ddc > 0) + output->ddc = OF_device_from_xref(ddc); + if ((output->edid == NULL) && (output->ddc == NULL)) + return (ENXIO); + + if (output->gpio_hpd != NULL) { + output->connector.polled = +// DRM_CONNECTOR_POLL_HPD; + DRM_CONNECTOR_POLL_DISCONNECT | + DRM_CONNECTOR_POLL_CONNECT; + } + + return (0); +} + +int tegra_drm_encoder_init(struct tegra_drm_encoder *output, + struct tegra_drm *drm) +{ + + if (output->panel) { + /* attach panel */ + } + return (0); +} + +int tegra_drm_encoder_exit(struct tegra_drm_encoder *output, + struct tegra_drm *drm) +{ + + if (output->panel) { + /* detach panel */ + } + return (0); +} \ No newline at end of file Property changes on: trunk/sys/arm/nvidia/drm2/tegra_drm_subr.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/drm2/tegra_fb.c =================================================================== --- trunk/sys/arm/nvidia/drm2/tegra_fb.c (rev 0) +++ trunk/sys/arm/nvidia/drm2/tegra_fb.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,339 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_fb.c 310600 2016-12-26 14:36:05Z mmel $"); + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +static void +fb_destroy(struct drm_framebuffer *drm_fb) +{ + struct tegra_fb *fb; + struct tegra_bo *bo; + unsigned int i; + + fb = container_of(drm_fb, struct tegra_fb, drm_fb); + for (i = 0; i < fb->nplanes; i++) { + bo = fb->planes[i]; + if (bo != NULL) + drm_gem_object_unreference_unlocked(&bo->gem_obj); + } + + drm_framebuffer_cleanup(drm_fb); + free(fb->planes, DRM_MEM_DRIVER); +} + +static int +fb_create_handle(struct drm_framebuffer *drm_fb, struct drm_file *file, + unsigned int *handle) +{ + struct tegra_fb *fb; + int rv; + + fb = container_of(drm_fb, struct tegra_fb, drm_fb); + rv = drm_gem_handle_create(file, &fb->planes[0]->gem_obj, handle); + return (rv); +} + +/* XXX Probably not needed */ +static int +fb_dirty(struct drm_framebuffer *fb, struct drm_file *file_priv, +unsigned flags, unsigned color, struct drm_clip_rect *clips, unsigned num_clips) +{ + + return (0); +} + +static const struct drm_framebuffer_funcs fb_funcs = { + .destroy = fb_destroy, + .create_handle = fb_create_handle, + .dirty = fb_dirty, +}; + +static int +fb_alloc(struct drm_device *drm, struct drm_mode_fb_cmd2 *mode_cmd, + struct tegra_bo **planes, int num_planes, struct tegra_fb **res_fb) +{ + struct tegra_fb *fb; + int i; + int rv; + + fb = malloc(sizeof(*fb), DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + fb->planes = malloc(num_planes * sizeof(*fb->planes), DRM_MEM_DRIVER, + M_WAITOK | M_ZERO); + fb->nplanes = num_planes; + + drm_helper_mode_fill_fb_struct(&fb->drm_fb, mode_cmd); + for (i = 0; i < fb->nplanes; i++) + fb->planes[i] = planes[i]; + rv = drm_framebuffer_init(drm, &fb->drm_fb, &fb_funcs); + if (rv < 0) { + device_printf(drm->dev, + "Cannot initialize frame buffer %d\n", rv); + free(fb->planes, DRM_MEM_DRIVER); + return (rv); + } + *res_fb = fb; + return (0); +} + +static int +tegra_fb_probe(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + u_int bpp, size; + struct tegra_drm *drm; + struct tegra_fb *fb; + struct fb_info *info; + struct tegra_bo *bo; + struct drm_mode_fb_cmd2 mode_cmd; + struct drm_device *drm_dev; + int rv; + + if (helper->fb != NULL) + return (0); + + DRM_DEBUG_KMS("surface: %d x %d (bpp: %d)\n", sizes->surface_width, + sizes->surface_height, sizes->surface_bpp); + + drm_dev = helper->dev; + fb = container_of(helper, struct tegra_fb, fb_helper); + drm = container_of(drm_dev, struct tegra_drm, drm_dev); + bpp = (sizes->surface_bpp + 7) / 8; + + /* Create mode_cmd */ + memset(&mode_cmd, 0, sizeof(mode_cmd)); + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + mode_cmd.pitches[0] = roundup(sizes->surface_width * bpp, + drm->pitch_align); + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + size = mode_cmd.pitches[0] * mode_cmd.height; + + DRM_LOCK(drm_dev); + rv = tegra_bo_create(drm_dev, size, &bo); + DRM_UNLOCK(drm_dev); + if (rv != 0) + return (rv); + + info = framebuffer_alloc(); + if (info == NULL) { + device_printf(drm_dev->dev, + "Cannot allocate DRM framebuffer info.\n"); + rv = -ENOMEM; + goto err_object; + } + + rv = fb_alloc(drm_dev, &mode_cmd, &bo, 1, &fb); + if (rv != 0) { + device_printf(drm_dev->dev, + "Cannot allocate DRM framebuffer.\n"); + goto err_fb; + } + helper->fb = &fb->drm_fb; + helper->fbdev = info; + + /* Fill FB info */ + info->fb_vbase = bo->vbase; + info->fb_pbase = bo->pbase; + info->fb_size = size; + info->fb_bpp = sizes->surface_bpp; + drm_fb_helper_fill_fix(info, fb->drm_fb.pitches[0], fb->drm_fb.depth); + drm_fb_helper_fill_var(info, helper, fb->drm_fb.width, + fb->drm_fb.height); + + DRM_DEBUG_KMS("allocated %dx%d (s %dbits) fb size: %d, bo %p\n", + fb->drm_fb.width, fb->drm_fb.height, fb->drm_fb.depth, + size, bo); + return (1); +err_fb: + drm_gem_object_unreference_unlocked(&bo->gem_obj); + framebuffer_release(info); +err_object: + drm_gem_object_release(&bo->gem_obj); + return (rv); +} + +static struct drm_fb_helper_funcs fb_helper_funcs = { + .fb_probe = tegra_fb_probe, +}; + +/* + * Exported functions + */ +struct fb_info * +tegra_drm_fb_getinfo(struct drm_device *drm_dev) +{ + struct tegra_fb *fb; + struct tegra_drm *drm; + + drm = container_of(drm_dev, struct tegra_drm, drm_dev); + fb = drm->fb; + if (fb == NULL) + return (NULL); + return (fb->fb_helper.fbdev); +} + +struct tegra_bo * +tegra_fb_get_plane(struct tegra_fb *fb, int idx) +{ + + if (idx >= drm_format_num_planes(fb->drm_fb.pixel_format)) + return (NULL); + if (idx >= fb->nplanes) + return (NULL); + return (fb->planes[idx]); +} + +int +tegra_drm_fb_init(struct drm_device *drm_dev) +{ + struct tegra_fb *fb; + struct tegra_drm *drm; + int rv; + + drm = drm_dev->dev_private; + drm = container_of(drm_dev, struct tegra_drm, drm_dev); + fb = malloc(sizeof(*fb), DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + drm->fb = fb; + + fb->fb_helper.funcs = &fb_helper_funcs; + rv = drm_fb_helper_init(drm_dev, &fb->fb_helper, + drm_dev->mode_config.num_crtc, drm_dev->mode_config.num_connector); + if (rv != 0) { + device_printf(drm_dev->dev, + "Cannot initialize frame buffer %d\n", rv); + return (rv); + } + + rv = drm_fb_helper_single_add_all_connectors(&fb->fb_helper); + if (rv != 0) { + device_printf(drm_dev->dev, "Cannot add all connectors: %d\n", + rv); + goto err_fini; + } + + rv = drm_fb_helper_initial_config(&fb->fb_helper, 32); + if (rv != 0) { + device_printf(drm_dev->dev, + "Cannot set initial config: %d\n", rv); + goto err_fini; + } + /* XXXX Setup initial mode for FB */ + /* drm_fb_helper_set_par(fb->fb_helper.fbdev); */ + return 0; + +err_fini: + drm_fb_helper_fini(&fb->fb_helper); + return (rv); +} + +int +tegra_drm_fb_create(struct drm_device *drm, struct drm_file *file, + struct drm_mode_fb_cmd2 *cmd, struct drm_framebuffer **fb_res) +{ + int hsub, vsub, i; + int width, height, size, bpp; + struct tegra_bo *planes[4]; + struct drm_gem_object *gem_obj; + struct tegra_fb *fb; + int rv, nplanes; + + hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format); + vsub = drm_format_vert_chroma_subsampling(cmd->pixel_format); + + nplanes = drm_format_num_planes(cmd->pixel_format); + for (i = 0; i < nplanes; i++) { + width = cmd->width; + height = cmd->height; + if (i != 0) { + width /= hsub; + height /= vsub; + } + gem_obj = drm_gem_object_lookup(drm, file, cmd->handles[i]); + if (gem_obj == NULL) { + rv = -ENXIO; + goto fail; + } + + bpp = drm_format_plane_cpp(cmd->pixel_format, i); + size = (height - 1) * cmd->pitches[i] + + width * bpp + cmd->offsets[i]; + if (gem_obj->size < size) { + rv = -EINVAL; + goto fail; + } + planes[i] = container_of(gem_obj, struct tegra_bo, gem_obj); + } + + rv = fb_alloc(drm, cmd, planes, nplanes, &fb); + if (rv != 0) + goto fail; + + *fb_res = &fb->drm_fb; + return (0); + +fail: + while (i--) + drm_gem_object_unreference_unlocked(&planes[i]->gem_obj); + return (rv); +} + +void +tegra_drm_fb_destroy(struct drm_device *drm_dev) +{ + struct fb_info *info; + struct tegra_fb *fb; + struct tegra_drm *drm; + + drm = container_of(drm_dev, struct tegra_drm, drm_dev); + fb = drm->fb; + if (fb == NULL) + return; + info = fb->fb_helper.fbdev; + drm_framebuffer_remove(&fb->drm_fb); + framebuffer_release(info); + drm_fb_helper_fini(&fb->fb_helper); + drm_framebuffer_cleanup(&fb->drm_fb); + free(fb, DRM_MEM_DRIVER); + drm->fb = NULL; +} Property changes on: trunk/sys/arm/nvidia/drm2/tegra_fb.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/drm2/tegra_hdmi.c =================================================================== --- trunk/sys/arm/nvidia/drm2/tegra_hdmi.c (rev 0) +++ trunk/sys/arm/nvidia/drm2/tegra_hdmi.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,1327 @@ +/* $MidnightBSD$ */ +/*- + * 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_hdmi.c 310600 2016-12-26 14:36:05Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "tegra_dc_if.h" +#include "tegra_drm_if.h" + +#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, 4 * (_r), (_v)) +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, 4 * (_r)) + +/* HDA stream format verb. */ +#define AC_FMT_CHAN_GET(x) (((x) >> 0) & 0xf) +#define AC_FMT_CHAN_BITS_GET(x) (((x) >> 4) & 0x7) +#define AC_FMT_DIV_GET(x) (((x) >> 8) & 0x7) +#define AC_FMT_MUL_GET(x) (((x) >> 11) & 0x7) +#define AC_FMT_BASE_44K (1 << 14) +#define AC_FMT_TYPE_NON_PCM (1 << 15) + +#define HDMI_REKEY_DEFAULT 56 +#define HDMI_ELD_BUFFER_SIZE 96 + +#define HDMI_DC_CLOCK_MULTIPIER 2 + +struct audio_reg { + uint32_t audio_clk; + bus_size_t acr_reg; + bus_size_t nval_reg; + bus_size_t aval_reg; +}; + +static const struct audio_reg audio_regs[] = +{ + { + .audio_clk = 32000, + .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW, + .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0320, + .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320, + }, + { + .audio_clk = 44100, + .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW, + .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0441, + .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441, + }, + { + .audio_clk = 88200, + .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW, + .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0882, + .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882, + }, + { + .audio_clk = 176400, + .acr_reg = HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW, + .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_1764, + .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764, + }, + { + .audio_clk = 48000, + .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW, + .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0480, + .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480, + }, + { + .audio_clk = 96000, + .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW, + .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0960, + .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960, + }, + { + .audio_clk = 192000, + .acr_reg = HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW, + .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_1920, + .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920, + }, +}; + +struct tmds_config { + uint32_t pclk; + uint32_t pll0; + uint32_t pll1; + uint32_t drive_c; + uint32_t pe_c; + uint32_t peak_c; + uint32_t pad_ctls; +}; + +static const struct tmds_config tegra124_tmds_config[] = +{ + { /* 480p/576p / 25.2MHz/27MHz */ + .pclk = 27000000, + .pll0 = 0x01003010, + .pll1 = 0x00301B00, + .drive_c = 0x1F1F1F1F, + .pe_c = 0x00000000, + .peak_c = 0x03030303, + .pad_ctls = 0x800034BB, + }, + { /* 720p/1080i / 74.25MHz */ + .pclk = 74250000, + .pll0 = 0x01003110, + .pll1 = 0x00301500, + .drive_c = 0x2C2C2C2C, + .pe_c = 0x00000000, + .peak_c = 0x07070707, + .pad_ctls = 0x800034BB, + }, + { /* 1080p / 148.5MHz */ + .pclk = 148500000, + .pll0 = 0x01003310, + .pll1 = 0x00301500, + .drive_c = 0x33333333, + .pe_c = 0x00000000, + .peak_c = 0x0C0C0C0C, + .pad_ctls = 0x800034BB, + }, + { /* 2216p / 297MHz */ + .pclk = UINT_MAX, + .pll0 = 0x01003F10, + .pll1 = 0x00300F00, + .drive_c = 0x37373737, + .pe_c = 0x00000000, + .peak_c = 0x17171717, + .pad_ctls = 0x800036BB, + }, +}; + + +struct hdmi_softc { + device_t dev; + struct resource *mem_res; + struct resource *irq_res; + void *irq_ih; + + clk_t clk_parent; + clk_t clk_hdmi; + hwreset_t hwreset_hdmi; + regulator_t supply_hdmi; + regulator_t supply_pll; + regulator_t supply_vdd; + + uint64_t pclk; + boolean_t hdmi_mode; + + int audio_src_type; + int audio_freq; + int audio_chans; + + struct tegra_drm *drm; + struct tegra_drm_encoder output; + + const struct tmds_config *tmds_config; + int n_tmds_configs; +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-hdmi", 1}, + {NULL, 0}, +}; + +/* These functions have been copied from newer version of drm_edid.c */ +/* ELD Header Block */ +#define DRM_ELD_HEADER_BLOCK_SIZE 4 +#define DRM_ELD_BASELINE_ELD_LEN 2 /* in dwords! */ +static int drm_eld_size(const uint8_t *eld) +{ + return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4; +} + +static int +drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, + struct drm_display_mode *mode) +{ + int rv; + + if (!frame || !mode) + return -EINVAL; + + rv = hdmi_avi_infoframe_init(frame); + if (rv < 0) + return rv; + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + frame->pixel_repeat = 1; + + frame->video_code = drm_match_cea_mode(mode); + + frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE; +#ifdef FREEBSD_NOTYET + /* + * Populate picture aspect ratio from either + * user input (if specified) or from the CEA mode list. + */ + if (mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_4_3 || + mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_16_9) + frame->picture_aspect = mode->picture_aspect_ratio; + else if (frame->video_code > 0) + frame->picture_aspect = drm_get_cea_aspect_ratio( + frame->video_code); +#endif + + frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; + frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN; + + return 0; +} +/* --------------------------------------------------------------------- */ + +static int +hdmi_setup_clock(struct tegra_drm_encoder *output, clk_t clk, uint64_t pclk) +{ + struct hdmi_softc *sc; + uint64_t freq; + int rv; + + sc = device_get_softc(output->dev); + + /* Disable consumers clock for while. */ + rv = clk_disable(sc->clk_hdmi); + if (rv != 0) { + device_printf(sc->dev, "Cannot disable 'hdmi' clock\n"); + return (rv); + } + rv = clk_disable(clk); + if (rv != 0) { + device_printf(sc->dev, "Cannot disable display clock\n"); + return (rv); + } + + /* Set frequency for Display Controller PLL. */ + freq = HDMI_DC_CLOCK_MULTIPIER * pclk; + rv = clk_set_freq(sc->clk_parent, freq, 0); + if (rv != 0) { + device_printf(output->dev, + "Cannot set display pixel frequency\n"); + return (rv); + } + + /* Reparent display controller */ + rv = clk_set_parent_by_clk(clk, sc->clk_parent); + if (rv != 0) { + device_printf(output->dev, "Cannot set parent clock\n"); + return (rv); + + } + rv = clk_set_freq(clk, freq, 0); + if (rv != 0) { + device_printf(output->dev, + "Cannot set display controller frequency\n"); + return (rv); + } + rv = clk_set_freq(sc->clk_hdmi, pclk, 0); + if (rv != 0) { + device_printf(output->dev, + "Cannot set display controller frequency\n"); + return (rv); + } + + /* And reenable consumers clock. */ + rv = clk_enable(clk); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable display clock\n"); + return (rv); + } + rv = clk_enable(sc->clk_hdmi); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'hdmi' clock\n"); + return (rv); + } + + rv = clk_get_freq(clk, &freq); + if (rv != 0) { + device_printf(output->dev, + "Cannot get display controller frequency\n"); + return (rv); + } + + DRM_DEBUG_KMS("DC frequency: %llu\n", freq); + + return (0); +} + +/* ------------------------------------------------------------------- + * + * Infoframes. + * + */ +static void +avi_setup_infoframe(struct hdmi_softc *sc, struct drm_display_mode *mode) +{ + struct hdmi_avi_infoframe frame; + uint8_t buf[17], *hdr, *pb;; + ssize_t rv; + + rv = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode); + if (rv < 0) { + device_printf(sc->dev, "Cannot setup AVI infoframe: %zd\n", rv); + return; + } + rv = hdmi_avi_infoframe_pack(&frame, buf, sizeof(buf)); + if (rv < 0) { + device_printf(sc->dev, "Cannot pack AVI infoframe: %zd\n", rv); + return; + } + hdr = buf + 0; + pb = buf + 3; + WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER, + (hdr[2] << 16) | (hdr[1] << 8) | (hdr[0] << 0)); + WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW, + (pb[3] << 24) |(pb[2] << 16) | (pb[1] << 8) | (pb[0] << 0)); + WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH, + (pb[6] << 16) | (pb[5] << 8) | (pb[4] << 0)); + WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW, + (pb[10] << 24) |(pb[9] << 16) | (pb[8] << 8) | (pb[7] << 0)); + WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH, + (pb[13] << 16) | (pb[12] << 8) | (pb[11] << 0)); + + WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL, + AVI_INFOFRAME_CTRL_ENABLE); +} + +static void +audio_setup_infoframe(struct hdmi_softc *sc) +{ + struct hdmi_audio_infoframe frame; + uint8_t buf[14], *hdr, *pb; + ssize_t rv; + + + rv = hdmi_audio_infoframe_init(&frame); + frame.channels = sc->audio_chans; + rv = hdmi_audio_infoframe_pack(&frame, buf, sizeof(buf)); + if (rv < 0) { + device_printf(sc->dev, "Cannot pack audio infoframe\n"); + return; + } + hdr = buf + 0; + pb = buf + 3; + WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER, + (hdr[2] << 16) | (hdr[1] << 8) | (hdr[0] << 0)); + WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW, + (pb[3] << 24) |(pb[2] << 16) | (pb[1] << 8) | (pb[0] << 0)); + WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH, + (pb[5] << 8) | (pb[4] << 0)); + + WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL, + AUDIO_INFOFRAME_CTRL_ENABLE); +} + +/* ------------------------------------------------------------------- + * + * Audio + * + */ +static void +init_hda_eld(struct hdmi_softc *sc) +{ + size_t size; + int i ; + uint32_t val; + + size = drm_eld_size(sc->output.connector.eld); + for (i = 0; i < HDMI_ELD_BUFFER_SIZE; i++) { + val = i << 8; + if (i < size) + val |= sc->output.connector.eld[i]; + WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR, val); + } + WR4(sc,HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE, + SOR_AUDIO_HDA_PRESENSE_VALID | SOR_AUDIO_HDA_PRESENSE_PRESENT); +} + +static int +get_audio_regs(int freq, bus_size_t *acr_reg, bus_size_t *nval_reg, + bus_size_t *aval_reg) +{ + int i; + const struct audio_reg *reg; + + for (i = 0; i < nitems(audio_regs) ; i++) { + reg = audio_regs + i; + if (reg->audio_clk == freq) { + if (acr_reg != NULL) + *acr_reg = reg->acr_reg; + if (nval_reg != NULL) + *nval_reg = reg->nval_reg; + if (aval_reg != NULL) + *aval_reg = reg->aval_reg; + return (0); + } + } + return (ERANGE); +} + +#define FR_BITS 16 +#define TO_FFP(x) (((int64_t)(x)) << FR_BITS) +#define TO_INT(x) ((int)((x) >> FR_BITS)) +static int +get_hda_cts_n(uint32_t audio_freq_hz, uint32_t pixclk_freq_hz, + uint32_t *best_cts, uint32_t *best_n, uint32_t *best_a) +{ + int min_n; + int max_n; + int ideal_n; + int n; + int cts; + int aval; + int64_t err_f; + int64_t min_err_f; + int64_t cts_f; + int64_t aval_f; + int64_t half_f; /* constant 0.5 */ + bool better_n; + + /* + * All floats are in fixed I48.16 format. + * + * Ideal ACR interval is 1000 hz (1 ms); + * acceptable is 300 hz .. 1500 hz + */ + min_n = 128 * audio_freq_hz / 1500; + max_n = 128 * audio_freq_hz / 300; + ideal_n = 128 * audio_freq_hz / 1000; + min_err_f = TO_FFP(100); + half_f = TO_FFP(1) / 2; + + *best_n = 0; + *best_cts = 0; + *best_a = 0; + + for (n = min_n; n <= max_n; n++) { + cts_f = TO_FFP(pixclk_freq_hz); + cts_f *= n; + cts_f /= 128 * audio_freq_hz; + cts = TO_INT(cts_f + half_f); /* round */ + err_f = cts_f - TO_FFP(cts); + if (err_f < 0) + err_f = -err_f; + aval_f = TO_FFP(24000000); + aval_f *= n; + aval_f /= 128 * audio_freq_hz; + aval = TO_INT(aval_f); /* truncate */ + + better_n = abs(n - ideal_n) < abs((int)(*best_n) - ideal_n); + if (TO_FFP(aval) == aval_f && + (err_f < min_err_f || (err_f == min_err_f && better_n))) { + + min_err_f = err_f; + *best_n = (uint32_t)n; + *best_cts = (uint32_t)cts; + *best_a = (uint32_t)aval; + + if (err_f == 0 && n == ideal_n) + break; + } + } + return (0); +} +#undef FR_BITS +#undef TO_FFP +#undef TO_INT + +static int +audio_setup(struct hdmi_softc *sc) +{ + uint32_t val; + uint32_t audio_n; + uint32_t audio_cts; + uint32_t audio_aval; + uint64_t hdmi_freq; + bus_size_t aval_reg; + int rv; + + if (!sc->hdmi_mode) + return (ENOTSUP); + rv = get_audio_regs(sc->audio_freq, NULL, NULL, &aval_reg); + if (rv != 0) { + device_printf(sc->dev, "Unsupported audio frequency.\n"); + return (rv); + } + + rv = clk_get_freq(sc->clk_hdmi, &hdmi_freq); + if (rv != 0) { + device_printf(sc->dev, "Cannot get hdmi frequency: %d\n", rv); + return (rv); + } + + rv = get_hda_cts_n(sc->audio_freq, hdmi_freq, &audio_cts, &audio_n, + &audio_aval); + if (rv != 0) { + device_printf(sc->dev, "Cannot compute audio coefs: %d\n", rv); + return (rv); + } + + /* Audio infoframe. */ + audio_setup_infoframe(sc); + /* Setup audio source */ + WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_CNTRL0, + SOR_AUDIO_CNTRL0_SOURCE_SELECT(sc->audio_src_type) | + SOR_AUDIO_CNTRL0_INJECT_NULLSMPL); + + val = RD4(sc, HDMI_NV_PDISP_SOR_AUDIO_SPARE0); + val |= SOR_AUDIO_SPARE0_HBR_ENABLE; + WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_SPARE0, val); + + WR4(sc, HDMI_NV_PDISP_HDMI_ACR_CTRL, 0); + + WR4(sc, HDMI_NV_PDISP_AUDIO_N, + AUDIO_N_RESETF | + AUDIO_N_GENERATE_ALTERNATE | + AUDIO_N_VALUE(audio_n - 1)); + + WR4(sc, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH, + ACR_SUBPACK_N(audio_n) | ACR_ENABLE); + + WR4(sc, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW, + ACR_SUBPACK_CTS(audio_cts)); + + WR4(sc, HDMI_NV_PDISP_HDMI_SPARE, + SPARE_HW_CTS | SPARE_FORCE_SW_CTS | SPARE_CTS_RESET_VAL(1)); + + val = RD4(sc, HDMI_NV_PDISP_AUDIO_N); + val &= ~AUDIO_N_RESETF; + WR4(sc, HDMI_NV_PDISP_AUDIO_N, val); + + WR4(sc, aval_reg, audio_aval); + + return (0); +} + +static void +audio_disable(struct hdmi_softc *sc) { + uint32_t val; + + /* Disable audio */ + val = RD4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); + val &= ~GENERIC_CTRL_AUDIO; + WR4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL, val); + + /* Disable audio infoframes */ + val = RD4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); + val &= ~AUDIO_INFOFRAME_CTRL_ENABLE; + WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL, val); +} + +static void +audio_enable(struct hdmi_softc *sc) { + uint32_t val; + + if (!sc->hdmi_mode) + audio_disable(sc); + + /* Enable audio infoframes */ + val = RD4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); + val |= AUDIO_INFOFRAME_CTRL_ENABLE; + WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL, val); + + /* Enable audio */ + val = RD4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); + val |= GENERIC_CTRL_AUDIO; + WR4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL, val); +} + +/* ------------------------------------------------------------------- + * + * HDMI. + * + */ + /* Process format change notification from HDA */ +static void +hda_intr(struct hdmi_softc *sc) +{ + uint32_t val; + int rv; + + if (!sc->hdmi_mode) + return; + + val = RD4(sc, HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0); + if ((val & (1 << 30)) == 0) { + audio_disable(sc); + return; + } + + /* XXX Move this to any header */ + /* Keep in sync with HDA */ + sc->audio_freq = val & 0x00FFFFFF; + sc->audio_chans = (val >> 24) & 0x0f; + DRM_DEBUG_KMS("%d channel(s) at %dHz\n", sc->audio_chans, + sc->audio_freq); + + rv = audio_setup(sc); + if (rv != 0) { + audio_disable(sc); + return; + } + + audio_enable(sc); +} + +static void +tmds_init(struct hdmi_softc *sc, const struct tmds_config *tmds) +{ + + WR4(sc, HDMI_NV_PDISP_SOR_PLL0, tmds->pll0); + WR4(sc, HDMI_NV_PDISP_SOR_PLL1, tmds->pll1); + WR4(sc, HDMI_NV_PDISP_PE_CURRENT, tmds->pe_c); + WR4(sc, HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT, tmds->drive_c); + WR4(sc, HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT, tmds->peak_c); + WR4(sc, HDMI_NV_PDISP_SOR_PAD_CTLS0, tmds->pad_ctls); +} + +static int +hdmi_sor_start(struct hdmi_softc *sc, struct drm_display_mode *mode) +{ + int i; + uint32_t val; + + /* Enable TMDS macro */ + val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0); + val &= ~SOR_PLL0_PWR; + val &= ~SOR_PLL0_VCOPD; + val &= ~SOR_PLL0_PULLDOWN; + WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val); + DELAY(10); + + val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0); + val &= ~SOR_PLL0_PDBG; + WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val); + + WR4(sc, HDMI_NV_PDISP_SOR_PWR, SOR_PWR_SETTING_NEW); + WR4(sc, HDMI_NV_PDISP_SOR_PWR, 0); + + /* Wait until SOR is ready */ + for (i = 1000; i > 0; i--) { + val = RD4(sc, HDMI_NV_PDISP_SOR_PWR); + if ((val & SOR_PWR_SETTING_NEW) == 0) + break; + DELAY(10); + } + if (i <= 0) { + device_printf(sc->dev, "Timeouted while enabling SOR power.\n"); + return (ETIMEDOUT); + } + + val = SOR_STATE2_ASY_OWNER(ASY_OWNER_HEAD0) | + SOR_STATE2_ASY_SUBOWNER(SUBOWNER_BOTH) | + SOR_STATE2_ASY_CRCMODE(ASY_CRCMODE_COMPLETE) | + SOR_STATE2_ASY_PROTOCOL(ASY_PROTOCOL_SINGLE_TMDS_A); + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + val |= SOR_STATE2_ASY_HSYNCPOL_NEG; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + val |= SOR_STATE2_ASY_VSYNCPOL_NEG; + WR4(sc, HDMI_NV_PDISP_SOR_STATE2, val); + + WR4(sc, HDMI_NV_PDISP_SOR_STATE1, SOR_STATE1_ASY_ORMODE_NORMAL | + SOR_STATE1_ASY_HEAD_OPMODE(ASY_HEAD_OPMODE_AWAKE)); + + WR4(sc, HDMI_NV_PDISP_SOR_STATE0, 0); + WR4(sc, HDMI_NV_PDISP_SOR_STATE0, SOR_STATE0_UPDATE); + + val = RD4(sc, HDMI_NV_PDISP_SOR_STATE1); + val |= SOR_STATE1_ATTACHED; + WR4(sc, HDMI_NV_PDISP_SOR_STATE1, val); + + WR4(sc, HDMI_NV_PDISP_SOR_STATE0, 0); + + return 0; +} + +static int +hdmi_disable(struct hdmi_softc *sc) +{ + struct tegra_crtc *crtc; + device_t dc; + uint32_t val; + + dc = NULL; + if (sc->output.encoder.crtc != NULL) { + crtc = container_of(sc->output.encoder.crtc, struct tegra_crtc, + drm_crtc); + dc = crtc->dev; + } + + if (dc != NULL) { + TEGRA_DC_HDMI_ENABLE(dc, false); + TEGRA_DC_DISPLAY_ENABLE(dc, false); + } + audio_disable(sc); + val = RD4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); + val &= ~AVI_INFOFRAME_CTRL_ENABLE; + WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL, val); + + /* Disable interrupts */ + WR4(sc, HDMI_NV_PDISP_INT_ENABLE, 0); + WR4(sc, HDMI_NV_PDISP_INT_MASK, 0); + + return (0); +} + +static int +hdmi_enable(struct hdmi_softc *sc) +{ + uint64_t freq; + struct drm_display_mode *mode; + struct tegra_crtc *crtc; + uint32_t val, h_sync_width, h_back_porch, h_front_porch, h_pulse_start; + uint32_t h_max_ac_packet, div8_2; + device_t dc; + int i, rv; + + mode = &sc->output.encoder.crtc->mode; + crtc = container_of(sc->output.encoder.crtc, struct tegra_crtc, + drm_crtc); + dc = crtc->dev; + + /* Compute all timings first. */ + sc->pclk = mode->clock * 1000; + h_sync_width = mode->hsync_end - mode->hsync_start; + h_back_porch = mode->htotal - mode->hsync_end; + h_front_porch = mode->hsync_start - mode->hdisplay; + h_pulse_start = 1 + h_sync_width + h_back_porch - 10; + h_max_ac_packet = (h_sync_width + h_back_porch + h_front_porch - + HDMI_REKEY_DEFAULT - 18) / 32; + + /* Check if HDMI device is connected and detected. */ + if (sc->output.connector.edid_blob_ptr == NULL) { + sc->hdmi_mode = false; + } else { + sc->hdmi_mode = drm_detect_hdmi_monitor( + (struct edid *)sc->output.connector.edid_blob_ptr->data); + } + + /* Get exact HDMI pixel frequency. */ + rv = clk_get_freq(sc->clk_hdmi, &freq); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'hdmi' clock frequency\n"); + return (rv); + } + DRM_DEBUG_KMS("HDMI frequency: %llu Hz\n", freq); + + /* Wakeup SOR power */ + val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0); + val &= ~SOR_PLL0_PDBG; + WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val); + DELAY(10); + + val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0); + val &= ~SOR_PLL0_PWR; + WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val); + + /* Setup timings */ + TEGRA_DC_SETUP_TIMING(dc, h_pulse_start); + WR4(sc, HDMI_NV_PDISP_HDMI_VSYNC_WINDOW, + VSYNC_WINDOW_START(0x200) | VSYNC_WINDOW_END(0x210) | + VSYNC_WINDOW_ENABLE); + + /* Setup video source and adjust video range */ + val = 0; + if (crtc->nvidia_head != 0) + HDMI_SRC_DISPLAYB; + if ((mode->hdisplay != 640) || (mode->vdisplay != 480)) + val |= ARM_VIDEO_RANGE_LIMITED; + WR4(sc, HDMI_NV_PDISP_INPUT_CONTROL, val); + + /* Program SOR reference clock - it uses 8.2 fractional divisor */ + div8_2 = (freq * 4) / 1000000; + val = SOR_REFCLK_DIV_INT(div8_2 >> 2) | SOR_REFCLK_DIV_FRAC(div8_2); + WR4(sc, HDMI_NV_PDISP_SOR_REFCLK, val); + + /* Setup audio */ + if (sc->hdmi_mode) { + rv = audio_setup(sc); + if (rv != 0) + sc->hdmi_mode = false; + } + + /* Init HDA ELD */ + init_hda_eld(sc); + val = HDMI_CTRL_REKEY(HDMI_REKEY_DEFAULT); + val |= HDMI_CTRL_MAX_AC_PACKET(h_max_ac_packet); + if (sc->hdmi_mode) + val |= HDMI_CTRL_ENABLE; + WR4(sc, HDMI_NV_PDISP_HDMI_CTRL, val); + + /* Setup TMDS */ + for (i = 0; i < sc->n_tmds_configs; i++) { + if (sc->pclk <= sc->tmds_config[i].pclk) { + tmds_init(sc, sc->tmds_config + i); + break; + } + } + + /* Program sequencer. */ + WR4(sc, HDMI_NV_PDISP_SOR_SEQ_CTL, + SOR_SEQ_PU_PC(0) | SOR_SEQ_PU_PC_ALT(0) | + SOR_SEQ_PD_PC(8) | SOR_SEQ_PD_PC_ALT(8)); + + val = SOR_SEQ_INST_WAIT_TIME(1) | + SOR_SEQ_INST_WAIT_UNITS(WAIT_UNITS_VSYNC) | + SOR_SEQ_INST_HALT | + SOR_SEQ_INST_DRIVE_PWM_OUT_LO; + WR4(sc, HDMI_NV_PDISP_SOR_SEQ_INST(0), val); + WR4(sc, HDMI_NV_PDISP_SOR_SEQ_INST(8), val); + + val = RD4(sc,HDMI_NV_PDISP_SOR_CSTM); + val &= ~SOR_CSTM_LVDS_ENABLE; + val &= ~SOR_CSTM_ROTCLK(~0); + val |= SOR_CSTM_ROTCLK(2); + val &= ~SOR_CSTM_MODE(~0); + val |= SOR_CSTM_MODE(CSTM_MODE_TMDS); + val |= SOR_CSTM_PLLDIV; + WR4(sc, HDMI_NV_PDISP_SOR_CSTM, val); + + TEGRA_DC_DISPLAY_ENABLE(dc, false); + + rv = hdmi_sor_start(sc, mode); + if (rv != 0) + return (rv); + + TEGRA_DC_HDMI_ENABLE(dc, true); + TEGRA_DC_DISPLAY_ENABLE(dc, true); + + /* Enable HDA codec interrupt */ + WR4(sc, HDMI_NV_PDISP_INT_MASK, INT_CODEC_SCRATCH0); + WR4(sc, HDMI_NV_PDISP_INT_ENABLE, INT_CODEC_SCRATCH0); + + if (sc->hdmi_mode) { + avi_setup_infoframe(sc, mode); + audio_enable(sc); + } + + return (0); +} + +/* ------------------------------------------------------------------- + * + * DRM Interface. + * + */ +static enum drm_mode_status +hdmi_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct tegra_drm_encoder *output; + struct hdmi_softc *sc; + int rv; + uint64_t freq; + + output = container_of(connector, struct tegra_drm_encoder, + connector); + sc = device_get_softc(output->dev); + + freq = HDMI_DC_CLOCK_MULTIPIER * mode->clock * 1000; + rv = clk_test_freq(sc->clk_parent, freq, 0); + DRM_DEBUG_KMS("Test HDMI frequency: %u kHz, rv: %d\n", mode->clock, rv); + if (rv != 0) + return (MODE_NOCLOCK); + + return (MODE_OK); +} + + +static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = { + .get_modes = tegra_drm_connector_get_modes, + .mode_valid = hdmi_connector_mode_valid, + .best_encoder = tegra_drm_connector_best_encoder, +}; + +static const struct drm_connector_funcs hdmi_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = tegra_drm_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, +}; + +static const struct drm_encoder_funcs hdmi_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static void +hdmi_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + + /* Empty function. */ +} + +static bool +hdmi_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted) +{ + + return (true); +} + +static void +hdmi_encoder_prepare(struct drm_encoder *encoder) +{ + + /* Empty function. */ +} + +static void +hdmi_encoder_commit(struct drm_encoder *encoder) +{ + + /* Empty function. */ +} + +static void +hdmi_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, struct drm_display_mode *adjusted) +{ + struct tegra_drm_encoder *output; + struct hdmi_softc *sc; + int rv; + + output = container_of(encoder, struct tegra_drm_encoder, encoder); + sc = device_get_softc(output->dev); + rv = hdmi_enable(sc); + if (rv != 0) + device_printf(sc->dev, "Cannot enable HDMI port\n"); + +} + +static void +hdmi_encoder_disable(struct drm_encoder *encoder) +{ + struct tegra_drm_encoder *output; + struct hdmi_softc *sc; + int rv; + + output = container_of(encoder, struct tegra_drm_encoder, encoder); + sc = device_get_softc(output->dev); + if (sc == NULL) + return; + rv = hdmi_disable(sc); + if (rv != 0) + device_printf(sc->dev, "Cannot disable HDMI port\n"); +} + +static const struct drm_encoder_helper_funcs hdmi_encoder_helper_funcs = { + .dpms = hdmi_encoder_dpms, + .mode_fixup = hdmi_encoder_mode_fixup, + .prepare = hdmi_encoder_prepare, + .commit = hdmi_encoder_commit, + .mode_set = hdmi_encoder_mode_set, + .disable = hdmi_encoder_disable, +}; + +/* ------------------------------------------------------------------- + * + * Bus and infrastructure. + * + */ +static int +hdmi_init_client(device_t dev, device_t host1x, struct tegra_drm *drm) +{ + struct hdmi_softc *sc; + phandle_t node; + int rv; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(sc->dev); + sc->drm = drm; + sc->output.setup_clock = &hdmi_setup_clock; + + rv = tegra_drm_encoder_attach(&sc->output, node); + if (rv != 0) { + device_printf(dev, "Cannot attach output connector\n"); + return(ENXIO); + } + + /* Connect this encoder + connector to DRM. */ + drm_connector_init(&drm->drm_dev, &sc->output.connector, + &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); + + drm_connector_helper_add(&sc->output.connector, + &hdmi_connector_helper_funcs); + + sc->output.connector.dpms = DRM_MODE_DPMS_OFF; + + drm_encoder_init(&drm->drm_dev, &sc->output.encoder, + &hdmi_encoder_funcs, DRM_MODE_ENCODER_TMDS); + + drm_encoder_helper_add(&sc->output.encoder, &hdmi_encoder_helper_funcs); + + drm_mode_connector_attach_encoder(&sc->output.connector, + &sc->output.encoder); + + rv = tegra_drm_encoder_init(&sc->output, drm); + if (rv < 0) { + device_printf(sc->dev, "Unable to init HDMI output\n"); + return (rv); + } + sc->output.encoder.possible_crtcs = 0x3; + return (0); +} + +static int +hdmi_exit_client(device_t dev, device_t host1x, struct tegra_drm *drm) +{ + struct hdmi_softc *sc; + + sc = device_get_softc(dev); + tegra_drm_encoder_exit(&sc->output, drm); + return (0); +} + +static int +get_fdt_resources(struct hdmi_softc *sc, phandle_t node) +{ + int rv; + + rv = regulator_get_by_ofw_property(sc->dev, 0, "hdmi-supply", + &sc->supply_hdmi); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'hdmi' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev,0, "pll-supply", + &sc->supply_pll); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pll' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, 0, "vdd-supply", + &sc->supply_vdd); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'vdd' regulator\n"); + return (ENXIO); + } + + rv = hwreset_get_by_ofw_name(sc->dev, 0, "hdmi", &sc->hwreset_hdmi); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'hdmi' reset\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, 0, "parent", &sc->clk_parent); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'parent' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, 0, "hdmi", &sc->clk_hdmi); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'hdmi' clock\n"); + return (ENXIO); + } + + return (0); +} + +static int +enable_fdt_resources(struct hdmi_softc *sc) +{ + int rv; + + + rv = clk_set_parent_by_clk(sc->clk_hdmi, sc->clk_parent); + if (rv != 0) { + device_printf(sc->dev, + "Cannot set parent for 'hdmi' clock\n"); + return (rv); + } + + /* 594 MHz is arbitrarily selected value */ + rv = clk_set_freq(sc->clk_parent, 594000000, 0); + if (rv != 0) { + device_printf(sc->dev, + "Cannot set frequency for 'hdmi' parent clock\n"); + return (rv); + } + rv = clk_set_freq(sc->clk_hdmi, 594000000 / 4, 0); + if (rv != 0) { + device_printf(sc->dev, + "Cannot set frequency for 'hdmi' parent clock\n"); + return (rv); + } + + rv = regulator_enable(sc->supply_hdmi); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'hdmi' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_pll); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'pll' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_vdd); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'vdd' regulator\n"); + return (rv); + } + + rv = clk_enable(sc->clk_hdmi); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'hdmi' clock\n"); + return (rv); + } + + rv = hwreset_deassert(sc->hwreset_hdmi); + if (rv != 0) { + device_printf(sc->dev, "Cannot unreset 'hdmi' reset\n"); + return (rv); + } + return (0); +} + +static void +hdmi_intr(void *arg) +{ + struct hdmi_softc *sc; + uint32_t status; + + sc = arg; + + /* Confirm interrupt */ + status = RD4(sc, HDMI_NV_PDISP_INT_STATUS); + WR4(sc, HDMI_NV_PDISP_INT_STATUS, status); + + /* process audio verb from HDA */ + if (status & INT_CODEC_SCRATCH0) + hda_intr(sc); +} + +static int +hdmi_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 HDMI"); + return (BUS_PROBE_DEFAULT); +} + +static int +hdmi_attach(device_t dev) +{ + struct hdmi_softc *sc; + phandle_t node; + int rid, rv; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->output.dev = sc->dev; + node = ofw_bus_get_node(sc->dev); + + sc->audio_src_type = SOURCE_SELECT_AUTO; + sc->audio_freq = 44100; + sc->audio_chans = 2; + sc->hdmi_mode = false; + + sc->tmds_config = tegra124_tmds_config; + sc->n_tmds_configs = nitems(tegra124_tmds_config); + + 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; + } + + rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, hdmi_intr, sc, &sc->irq_ih); + if (rv != 0) { + device_printf(dev, + "WARNING: unable to register interrupt handler\n"); + goto fail; + } + + rv = get_fdt_resources(sc, node); + if (rv != 0) { + device_printf(dev, "Cannot parse FDT resources\n"); + goto fail; + } + rv = enable_fdt_resources(sc); + if (rv != 0) { + device_printf(dev, "Cannot enable FDT resources\n"); + goto fail; + } + + rv = TEGRA_DRM_REGISTER_CLIENT(device_get_parent(sc->dev), sc->dev); + if (rv != 0) { + device_printf(dev, "Cannot register DRM device\n"); + goto fail; + } + return (bus_generic_attach(dev)); + +fail: + TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev); + + if (sc->irq_ih != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); + if (sc->clk_parent != NULL) + clk_release(sc->clk_parent); + if (sc->clk_hdmi != NULL) + clk_release(sc->clk_hdmi); + if (sc->hwreset_hdmi != NULL) + hwreset_release(sc->hwreset_hdmi); + if (sc->supply_hdmi != NULL) + regulator_release(sc->supply_hdmi); + if (sc->supply_pll != NULL) + regulator_release(sc->supply_pll); + if (sc->supply_vdd != NULL) + regulator_release(sc->supply_vdd); + 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 +hdmi_detach(device_t dev) +{ + struct hdmi_softc *sc; + sc = device_get_softc(dev); + + TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev); + + if (sc->irq_ih != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); + if (sc->clk_parent != NULL) + clk_release(sc->clk_parent); + if (sc->clk_hdmi != NULL) + clk_release(sc->clk_hdmi); + if (sc->hwreset_hdmi != NULL) + hwreset_release(sc->hwreset_hdmi); + if (sc->supply_hdmi != NULL) + regulator_release(sc->supply_hdmi); + if (sc->supply_pll != NULL) + regulator_release(sc->supply_pll); + if (sc->supply_vdd != NULL) + regulator_release(sc->supply_vdd); + 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 (bus_generic_detach(dev)); +} + +static device_method_t tegra_hdmi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, hdmi_probe), + DEVMETHOD(device_attach, hdmi_attach), + DEVMETHOD(device_detach, hdmi_detach), + + /* tegra drm interface */ + DEVMETHOD(tegra_drm_init_client, hdmi_init_client), + DEVMETHOD(tegra_drm_exit_client, hdmi_exit_client), + + DEVMETHOD_END +}; + +static devclass_t tegra_hdmi_devclass; +DEFINE_CLASS_0(tegra_hdmi, tegra_hdmi_driver, tegra_hdmi_methods, + sizeof(struct hdmi_softc)); +DRIVER_MODULE(tegra_hdmi, host1x, tegra_hdmi_driver, +tegra_hdmi_devclass, 0, 0); Property changes on: trunk/sys/arm/nvidia/drm2/tegra_hdmi.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/drm2/tegra_hdmi_reg.h =================================================================== --- trunk/sys/arm/nvidia/drm2/tegra_hdmi_reg.h (rev 0) +++ trunk/sys/arm/nvidia/drm2/tegra_hdmi_reg.h 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,286 @@ +/* $MidnightBSD$ */ +/*- + * Copyright 1992-2016 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. + * + * $FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_hdmi_reg.h 310600 2016-12-26 14:36:05Z mmel $ + */ +#ifndef _TEGRA_HDMI_REG_H_ +#define _TEGRA_HDMI_REG_H_ + +/* + * !!! WARNING !!! + * Tegra manual uses registers index (and not register addreses). + * We follow the TRM notation and index is converted to offset in + * WR4 / RD4 macros + */ +#define HDMI_NV_PDISP_SOR_STATE0 0x001 +#define SOR_STATE0_UPDATE (1 << 0) + +#define HDMI_NV_PDISP_SOR_STATE1 0x002 +#define SOR_STATE1_ATTACHED (1 << 3) +#define SOR_STATE1_ASY_ORMODE_NORMAL (1 << 2) +#define SOR_STATE1_ASY_HEAD_OPMODE(x) (((x) & 0x3) << 0) +#define ASY_HEAD_OPMODE_SLEEP 0 +#define ASY_HEAD_OPMODE_SNOOZE 1 +#define ASY_HEAD_OPMODE_AWAKE 2 + +#define HDMI_NV_PDISP_SOR_STATE2 0x003 +#define SOR_STATE2_ASY_DEPOL_NEG (1 << 14) +#define SOR_STATE2_ASY_VSYNCPOL_NEG (1 << 13) +#define SOR_STATE2_ASY_HSYNCPOL_NEG (1 << 12) +#define SOR_STATE2_ASY_PROTOCOL(x) (((x) & 0xf) << 8) +#define ASY_PROTOCOL_SINGLE_TMDS_A 1 +#define ASY_PROTOCOL_CUSTOM 15 +#define SOR_STATE2_ASY_CRCMODE(x) (((x) & 0x3) << 6) +#define ASY_CRCMODE_ACTIVE 0 +#define ASY_CRCMODE_COMPLETE 1 +#define ASY_CRCMODE_NON_ACTIVE 2 +#define SOR_STATE2_ASY_SUBOWNER(x) (((x) & 0x3) << 4) +#define ASY_SUBOWNER_NONE 0 +#define ASY_SUBOWNER_SUBHEAD0 1 +#define ASY_SUBOWNER_SUBHEAD1 2 +#define SUBOWNER_BOTH 3 +#define SOR_STATE2_ASY_OWNER(x) (((x) & 0x3) << 0) +#define ASY_OWNER_NONE 0 +#define ASY_OWNER_HEAD0 1 + +#define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL 0x01e +#define AUDIO_INFOFRAME_CTRL_ENABLE (1 << 0) +#define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS 0x01f +#define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER 0x020 +#define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW 0x021 +#define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH 0x022 +#define INFOFRAME_HEADER_LEN(x) (((x) & 0x0f) << 16) +#define INFOFRAME_HEADER_VERSION(x) (((x) & 0xff) << 8) +#define INFOFRAME_HEADER_TYPE(x) (((x) & 0xff) << 0) + +#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL 0x023 +#define AVI_INFOFRAME_CTRL_ENABLE (1 << 0) +#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_STATUS 0x024 +#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER 0x025 +#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW 0x026 +#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH 0x027 +#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW 0x028 +#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH 0x029 + +#define HDMI_NV_PDISP_HDMI_GENERIC_CTRL 0x02a +#define GENERIC_CTRL_AUDIO (1 << 16) +#define GENERIC_CTRL_HBLANK (1 << 12) +#define GENERIC_CTRL_SINGLE (1 << 8) +#define GENERIC_CTRL_OTHER (1 << 4) +#define GENERIC_CTRL_ENABLE (1 << 0) +#define HDMI_NV_PDISP_HDMI_GENERIC_STATUS 0x02b +#define HDMI_NV_PDISP_HDMI_GENERIC_HEADER 0x02c +#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW 0x02d +#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH 0x02e +#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW 0x02f +#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH 0x030 +#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW 0x031 +#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH 0x032 +#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW 0x033 +#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH 0x034 + +#define HDMI_NV_PDISP_HDMI_ACR_CTRL 0x035 +#define HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW 0x036 +#define HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH 0x037 +#define HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW 0x038 +#define HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH 0x039 +#define HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW 0x03a +#define HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH 0x03b +#define HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW 0x03c +#define HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH 0x03d +#define HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW 0x03e +#define HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH 0x03f +#define HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW 0x040 +#define HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH 0x041 +#define HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW 0x042 +#define HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH 0x043 +#define ACR_ENABLE (1U << 31) +#define ACR_SUBPACK_CTS(x) (((x) & 0xffffff) << 8) +#define ACR_SUBPACK_N(x) (((x) & 0xffffff) << 0) + +#define HDMI_NV_PDISP_HDMI_CTRL 0x044 +#define HDMI_CTRL_ENABLE (1 << 30) +#define HDMI_CTRL_CA_SELECT (1 << 28) +#define HDMI_CTRL_SS_SELECT (1 << 27) +#define HDMI_CTRL_SF_SELECT (1 << 26) +#define HDMI_CTRL_CC_SELECT (1 << 25) +#define HDMI_CTRL_CT_SELECT (1 << 24) +#define HDMI_CTRL_MAX_AC_PACKET(x) (((x) & 0x1f) << 16) +#define HDMI_CTRL_SAMPLE_FLAT (1 << 12) +#define HDMI_CTRL_AUDIO_LAYOUT_SELECT (1 << 10) +#define HDMI_CTRL_AUDIO_LAYOUT (1 << 8) +#define HDMI_CTRL_REKEY(x) (((x) & 0x7f) << 0) + +#define HDMI_NV_PDISP_HDMI_VSYNC_WINDOW 0x046 +#define VSYNC_WINDOW_ENABLE (1U << 31) +#define VSYNC_WINDOW_START(x) (((x) & 0x3ff) << 16) +#define VSYNC_WINDOW_END(x) (((x) & 0x3ff) << 0) + +#define HDMI_NV_PDISP_HDMI_SPARE 0x04f +#define SPARE_ACR_PRIORITY (1U << 31) +#define SPARE_CTS_RESET_VAL(x) (((x) & 0x7) << 16) +#define SPARE_SUPRESS_SP_B (1 << 2) +#define SPARE_FORCE_SW_CTS (1 << 1) +#define SPARE_HW_CTS (1 << 0) + +#define HDMI_NV_PDISP_SOR_PWR 0x055 +#define SOR_PWR_SETTING_NEW (1U << 31) +#define SOR_PWR_SAFE_STATE_PU (1 << 16) +#define SOR_PWR_NORMAL_START_ALT (1 << 1) +#define SOR_PWR_NORMAL_STATE_PU (1 << 0) + +#define HDMI_NV_PDISP_SOR_PLL0 0x057 +#define SOR_PLL0_TX_REG_LOAD(x) (((x) & 0xf) << 28) +#define SOR_PLL0_ICHPMP(x) (((x) & 0xf) << 24) +#define SOR_PLL0_FILTER(x) (((x) & 0xf) << 16) +#define SOR_PLL0_BG_V17_S(x) (((x) & 0xf) << 12) +#define SOR_PLL0_VCOCAP(x) (((x) & 0xf) << 8) +#define SOR_PLL0_PULLDOWN (1 << 5) +#define SOR_PLL0_RESISTORSEL (1 << 4) +#define SOR_PLL0_PDPORT (1 << 3) +#define SOR_PLL0_VCOPD (1 << 2) +#define SOR_PLL0_PDBG (1 << 1) +#define SOR_PLL0_PWR (1 << 0) + +#define HDMI_NV_PDISP_SOR_PLL1 0x058 +#define SOR_PLL1_S_D_PIN_PE (1 << 30) +#define SOR_PLL1_HALF_FULL_PE (1 << 29) +#define SOR_PLL1_PE_EN (1 << 28) +#define SOR_PLL1_LOADADJ(x) (((x) & 0xf) << 20) +#define SOR_PLL1_TMDS_TERMADJ(x) (((x) & 0xf) << 9) +#define SOR_PLL1_TMDS_TERM (1 << 8) + +#define HDMI_NV_PDISP_SOR_CSTM 0x05a +#define SOR_CSTM_ROTAT(x) (((x) & 0xf) << 28) +#define SOR_CSTM_ROTCLK(x) (((x) & 0xf) << 24) +#define SOR_CSTM_PLLDIV (1 << 21) +#define SOR_CSTM_BALANCED (1 << 19) +#define SOR_CSTM_NEW_MODE (1 << 18) +#define SOR_CSTM_DUP_SYNC (1 << 17) +#define SOR_CSTM_LVDS_ENABLE (1 << 16) +#define SOR_CSTM_LINKACTB (1 << 15) +#define SOR_CSTM_LINKACTA (1 << 14) +#define SOR_CSTM_MODE(x) (((x) & 0x3) << 12) +#define CSTM_MODE_LVDS 0 +#define CSTM_MODE_TMDS 1 + +#define HDMI_NV_PDISP_SOR_SEQ_CTL 0x05f +#define SOR_SEQ_SWITCH (1 << 30) +#define SOR_SEQ_STATUS (1 << 28) +#define SOR_SEQ_PC(x) (((x) & 0xf) << 16) +#define SOR_SEQ_PD_PC_ALT(x) (((x) & 0xf) << 12) +#define SOR_SEQ_PD_PC(x) (((x) & 0xf) << 8) +#define SOR_SEQ_PU_PC_ALT(x) (((x) & 0xf) << 4) +#define SOR_SEQ_PU_PC(x) (((x) & 0xf) << 0) + +#define HDMI_NV_PDISP_SOR_SEQ_INST(x) (0x060 + (x)) +#define SOR_SEQ_INST_PLL_PULLDOWN (1U << 31) +#define SOR_SEQ_INST_POWERDOWN_MACRO (1 << 30) +#define SOR_SEQ_INST_ASSERT_PLL_RESETV (1 << 29) +#define SOR_SEQ_INST_BLANK_V (1 << 28) +#define SOR_SEQ_INST_BLANK_H (1 << 27) +#define SOR_SEQ_INST_BLANK_DE (1 << 26) +#define SOR_SEQ_INST_BLACK_DATA (1 << 25) +#define SOR_SEQ_INST_TRISTATE_IOS (1 << 24) +#define SOR_SEQ_INST_DRIVE_PWM_OUT_LO (1 << 23) +#define SOR_SEQ_INST_PIN_B_HIGH (1 << 22) +#define SOR_SEQ_INST_PIN_A_HIGH (1 << 21) +#define SOR_SEQ_INST_HALT (1 << 15) +#define SOR_SEQ_INST_WAIT_UNITS(x) (((x) & 0x3) << 12) +#define WAIT_UNITS_US 0 +#define WAIT_UNITS_MS 1 +#define WAIT_UNITS_VSYNC 2 +#define SOR_SEQ_INST_WAIT_TIME(x) (((x) & 0x3ff) << 0) + +#define HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT 0x07e + + +#define HDMI_NV_PDISP_AUDIO_N 0x08c +#define AUDIO_N_LOOKUP (1 << 28) +#define AUDIO_N_GENERATE_ALTERNATE (1 << 24) +#define AUDIO_N_RESETF (1 << 20) +#define AUDIO_N_VALUE(x) (((x) & 0xfffff) << 0) + +#define HDMI_NV_PDISP_SOR_REFCLK 0x095 +#define SOR_REFCLK_DIV_INT(x) (((x) & 0xff) << 8) +#define SOR_REFCLK_DIV_FRAC(x) (((x) & 0x03) << 6) + +#define HDMI_NV_PDISP_INPUT_CONTROL 0x097 +#define ARM_VIDEO_RANGE_LIMITED (1 << 1) +#define HDMI_SRC_DISPLAYB (1 << 0) + +#define HDMI_NV_PDISP_PE_CURRENT 0x099 +#define HDMI_NV_PDISP_SOR_AUDIO_CNTRL0 0x0ac +#define SOR_AUDIO_CNTRL0_INJECT_NULLSMPL (1 << 29) +#define SOR_AUDIO_CNTRL0_SOURCE_SELECT(x) (((x) & 0x03) << 20) +#define SOURCE_SELECT_AUTO 0 +#define SOURCE_SELECT_SPDIF 1 +#define SOURCE_SELECT_HDAL 2 +#define SOR_AUDIO_CNTRL0_AFIFO_FLUSH (1 << 12) + +#define HDMI_NV_PDISP_SOR_AUDIO_SPARE0 0x0ae +#define SOR_AUDIO_SPARE0_HBR_ENABLE (1 << 27) + +#define HDMI_NV_PDISP_SOR_AUDIO_NVAL_0320 0x0af +#define HDMI_NV_PDISP_SOR_AUDIO_NVAL_0441 0x0b0 +#define HDMI_NV_PDISP_SOR_AUDIO_NVAL_0882 0x0b1 +#define HDMI_NV_PDISP_SOR_AUDIO_NVAL_1764 0x0b2 +#define HDMI_NV_PDISP_SOR_AUDIO_NVAL_0480 0x0b3 +#define HDMI_NV_PDISP_SOR_AUDIO_NVAL_0960 0x0b4 +#define HDMI_NV_PDISP_SOR_AUDIO_NVAL_1920 0x0b5 +#define HDMI_NV_PDISP_SOR_AUDIO_HDA_SCRATCH0 0x0b6 +#define HDMI_NV_PDISP_SOR_AUDIO_HDA_SCRATCH1 0x0b7 +#define HDMI_NV_PDISP_SOR_AUDIO_HDA_SCRATCH2 0x0b8 +#define HDMI_NV_PDISP_SOR_AUDIO_HDA_SCRATCH3 0x0b9 +#define HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0 0x0ba +#define HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH1 0x0bb +#define HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR 0x0bc +#define HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE 0x0bd +#define SOR_AUDIO_HDA_PRESENSE_VALID (1 << 1) +#define SOR_AUDIO_HDA_PRESENSE_PRESENT (1 << 0) + +#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320 0x0bf +#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441 0x0c0 +#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882 0x0c1 +#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764 0x0c2 +#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480 0x0c3 +#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960 0x0c4 +#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920 0x0c5 +#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_DEFAULT 0x0c6 + +#define HDMI_NV_PDISP_INT_STATUS 0x0cc +#define INT_SCRATCH (1 << 3) +#define INT_CP_REQUEST (1 << 2) +#define INT_CODEC_SCRATCH1 (1 << 1) +#define INT_CODEC_SCRATCH0 (1 << 0) + +#define HDMI_NV_PDISP_INT_MASK 0x0cd +#define HDMI_NV_PDISP_INT_ENABLE 0x0ce +#define HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT 0x0d1 +#define HDMI_NV_PDISP_SOR_PAD_CTLS0 0x0d2 + + +#endif /* _TEGRA_HDMI_REG_H_ */ Property changes on: trunk/sys/arm/nvidia/drm2/tegra_hdmi_reg.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/drm2/tegra_host1x.c =================================================================== --- trunk/sys/arm/nvidia/drm2/tegra_host1x.c (rev 0) +++ trunk/sys/arm/nvidia/drm2/tegra_host1x.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,651 @@ +/* $MidnightBSD$ */ +/*- + * 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_host1x.c 310600 2016-12-26 14:36:05Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fb_if.h" +#include "tegra_drm_if.h" + +#define WR4(_sc, _r, _v) bus_rite_4((_sc)->mem_res, (_r), (_v)) +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) + +#define LOCK(_sc) sx_xlock(&(_sc)->lock) +#define UNLOCK(_sc) sx_xunlock(&(_sc)->lock) +#define SLEEP(_sc, timeout) sx_sleep(sc, &sc->lock, 0, "host1x", timeout); +#define LOCK_INIT(_sc) sx_init(&_sc->lock, "host1x") +#define LOCK_DESTROY(_sc) sx_destroy(&_sc->lock) +#define ASSERT_LOCKED(_sc) sx_assert(&_sc->lock, SA_LOCKED) +#define ASSERT_UNLOCKED(_sc) sx_assert(&_sc->lock, SA_UNLOCKED) + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-host1x", 1}, + {NULL, 0} +}; + +#define DRIVER_NAME "tegra" +#define DRIVER_DESC "NVIDIA Tegra TK1" +#define DRIVER_DATE "20151101" +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +struct client_info; +TAILQ_HEAD(client_list, client_info); +typedef struct client_list client_list_t; + +struct client_info { + TAILQ_ENTRY(client_info) list_e; + device_t client; + int activated; +}; + +struct host1x_softc { + struct simplebus_softc simplebus_sc; /* must be first */ + device_t dev; + struct sx lock; + int attach_done; + + struct resource *mem_res; + struct resource *syncpt_irq_res; + void *syncpt_irq_h; + struct resource *gen_irq_res; + void *gen_irq_h; + + clk_t clk; + hwreset_t reset; + struct intr_config_hook irq_hook; + + int drm_inited; + client_list_t clients; + + struct tegra_drm *tegra_drm; +}; + + +static void +host1x_output_poll_changed(struct drm_device *drm_dev) +{ + struct tegra_drm *drm; + + drm = container_of(drm_dev, struct tegra_drm, drm_dev); + if (drm->fb != NULL) + drm_fb_helper_hotplug_event(&drm->fb->fb_helper); +} + +static const struct drm_mode_config_funcs mode_config_funcs = { + .fb_create = tegra_drm_fb_create, + .output_poll_changed = host1x_output_poll_changed, +}; + + +static int +host1x_drm_init(struct host1x_softc *sc) +{ + struct client_info *entry; + int rv; + + LOCK(sc); + + TAILQ_FOREACH(entry, &sc->clients, list_e) { + if (entry->activated) + continue; + rv = TEGRA_DRM_INIT_CLIENT(entry->client, sc->dev, + sc->tegra_drm); + if (rv != 0) { + device_printf(sc->dev, + "Cannot init DRM client %s: %d\n", + device_get_name(entry->client), rv); + return (rv); + } + entry->activated = 1; + } + UNLOCK(sc); + + return (0); +} + +static int +host1x_drm_exit(struct host1x_softc *sc) +{ + struct client_info *entry; + int rv; +#ifdef FREEBSD_NOTYET + struct drm_device *dev, *tmp; +#endif + LOCK(sc); + if (!sc->drm_inited) { + UNLOCK(sc); + return (0); + } + TAILQ_FOREACH_REVERSE(entry, &sc->clients, client_list, list_e) { + if (!entry->activated) + continue; + rv = TEGRA_DRM_EXIT_CLIENT(entry->client, sc->dev, + sc->tegra_drm); + if (rv != 0) { + device_printf(sc->dev, + "Cannot exit DRM client %s: %d\n", + device_get_name(entry->client), rv); + } + entry->activated = 0; + } + +#ifdef FREEBSD_NOTYET + list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item) + drm_put_dev(dev); +#endif + sc->drm_inited = 0; + UNLOCK(sc); + + return (0); +} + +static int +host1x_drm_load(struct drm_device *drm_dev, unsigned long flags) +{ + struct host1x_softc *sc; + int rv; + + sc = device_get_softc(drm_dev->dev); + + drm_mode_config_init(drm_dev); + drm_dev->mode_config.min_width = 32; + drm_dev->mode_config.min_height = 32; + drm_dev->mode_config.max_width = 4096; + drm_dev->mode_config.max_height = 4096; + drm_dev->mode_config.funcs = &mode_config_funcs; + + rv = host1x_drm_init(sc); + if (rv != 0) + goto fail_host1x; + + drm_dev->irq_enabled = true; + drm_dev->max_vblank_count = 0xffffffff; + drm_dev->vblank_disable_allowed = true; + + rv = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc); + if (rv != 0) + goto fail_vblank; + + drm_mode_config_reset(drm_dev); + + rv = tegra_drm_fb_init(drm_dev); + if (rv != 0) + goto fail_fb; + drm_kms_helper_poll_init(drm_dev); + + return (0); + +fail_fb: + tegra_drm_fb_destroy(drm_dev); + drm_vblank_cleanup(drm_dev); +fail_vblank: + host1x_drm_exit(sc); +fail_host1x: + drm_mode_config_cleanup(drm_dev); + + return (rv); +} + +static int +host1x_drm_unload(struct drm_device *drm_dev) +{ + struct host1x_softc *sc; + int rv; + + sc = device_get_softc(drm_dev->dev); + + drm_kms_helper_poll_fini(drm_dev); + tegra_drm_fb_destroy(drm_dev); + drm_mode_config_cleanup(drm_dev); + + rv = host1x_drm_exit(sc); + if (rv < 0) + return (rv); + return (0); +} + +static int +host1x_drm_open(struct drm_device *drm_dev, struct drm_file *filp) +{ + + return (0); +} + +static void +tegra_drm_preclose(struct drm_device *drm, struct drm_file *file) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) + tegra_dc_cancel_page_flip(crtc, file); +} + +static void +host1x_drm_lastclose(struct drm_device *drm_dev) +{ + + struct tegra_drm *drm; + + drm = container_of(drm_dev, struct tegra_drm, drm_dev); + if (drm->fb != NULL) + drm_fb_helper_restore_fbdev_mode(&drm->fb->fb_helper); +} + +static int +host1x_drm_enable_vblank(struct drm_device *drm_dev, int pipe) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) { + if (pipe == tegra_dc_get_pipe(crtc)) { + tegra_dc_enable_vblank(crtc); + return (0); + } + } + return (-ENODEV); +} + +static void +host1x_drm_disable_vblank(struct drm_device *drm_dev, int pipe) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) { + if (pipe == tegra_dc_get_pipe(crtc)) { + tegra_dc_disable_vblank(crtc); + return; + } + } +} + + +static struct drm_ioctl_desc host1x_drm_ioctls[] = { +}; + + +struct drm_driver tegra_drm_driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, + .load = host1x_drm_load, + .unload = host1x_drm_unload, + .open = host1x_drm_open, + .preclose = tegra_drm_preclose, + .lastclose = host1x_drm_lastclose, + + .get_vblank_counter = drm_vblank_count, + .enable_vblank = host1x_drm_enable_vblank, + .disable_vblank = host1x_drm_disable_vblank, + + /* Fields filled by tegra_bo_driver_register() + .gem_free_object + .gem_pager_ops + .dumb_create + .dumb_map_offset + .dumb_destroy + */ + .ioctls = host1x_drm_ioctls, + .num_ioctls = nitems(host1x_drm_ioctls), + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; + +/* + * ----------------- Device methods ------------------------- + */ +static void +host1x_irq_hook(void *arg) +{ + struct host1x_softc *sc; + int rv; + + sc = arg; + config_intrhook_disestablish(&sc->irq_hook); + + tegra_bo_driver_register(&tegra_drm_driver); + rv = drm_get_platform_dev(sc->dev, &sc->tegra_drm->drm_dev, + &tegra_drm_driver); + if (rv != 0) { + device_printf(sc->dev, "drm_get_platform_dev(): %d\n", rv); + return; + } + + sc->drm_inited = 1; +} + +static struct fb_info * +host1x_fb_helper_getinfo(device_t dev) +{ + struct host1x_softc *sc; + + sc = device_get_softc(dev); + if (sc->tegra_drm == NULL) + return (NULL); + return (tegra_drm_fb_getinfo(&sc->tegra_drm->drm_dev)); +} + +static int +host1x_register_client(device_t dev, device_t client) +{ + struct host1x_softc *sc; + struct client_info *entry; + + sc = device_get_softc(dev); + + entry = malloc(sizeof(struct client_info), M_DEVBUF, M_WAITOK | M_ZERO); + entry->client = client; + entry->activated = 0; + + LOCK(sc); + TAILQ_INSERT_TAIL(&sc->clients, entry, list_e); + UNLOCK(sc); + + return (0); +} + +static int +host1x_deregister_client(device_t dev, device_t client) +{ + struct host1x_softc *sc; + struct client_info *entry; + + sc = device_get_softc(dev); + + LOCK(sc); + TAILQ_FOREACH(entry, &sc->clients, list_e) { + if (entry->client == client) { + if (entry->activated) + panic("Tegra DRM: Attempt to deregister " + "activated client"); + TAILQ_REMOVE(&sc->clients, entry, list_e); + free(entry, M_DEVBUF); + UNLOCK(sc); + return (0); + } + } + UNLOCK(sc); + + return (0); +} + +static void +host1x_gen_intr(void *arg) +{ + struct host1x_softc *sc; + + sc = (struct host1x_softc *)arg; + LOCK(sc); + UNLOCK(sc); +} + +static void +host1x_syncpt_intr(void *arg) +{ + struct host1x_softc *sc; + + sc = (struct host1x_softc *)arg; + LOCK(sc); + UNLOCK(sc); +} + +static void +host1x_new_pass(device_t dev) +{ + struct host1x_softc *sc; + int rv, rid; + phandle_t node; + + /* + * We attach during BUS_PASS_BUS (because we must overcome simplebus), + * but some of our FDT resources are not ready until BUS_PASS_DEFAULT + */ + sc = device_get_softc(dev); + if (sc->attach_done || bus_current_pass < BUS_PASS_DEFAULT) { + bus_generic_new_pass(dev); + return; + } + + sc->attach_done = 1; + node = ofw_bus_get_node(dev); + + /* Allocate our IRQ resource. */ + rid = 0; + sc->syncpt_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->syncpt_irq_res == NULL) { + device_printf(dev, "Cannot allocate interrupt.\n"); + rv = ENXIO; + goto fail; + } + rid = 1; + sc->gen_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->gen_irq_res == NULL) { + device_printf(dev, "Cannot allocate interrupt.\n"); + rv = ENXIO; + goto fail; + } + + /* FDT resources */ + rv = hwreset_get_by_ofw_name(sc->dev, 0, "host1x", &sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot get fuse reset\n"); + goto fail; + } + rv = clk_get_by_ofw_index(sc->dev, 0, 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; + } + rv = hwreset_deassert(sc->reset); + if (rv != 0) { + device_printf(sc->dev, "Cannot clear reset\n"); + goto fail; + } + + /* Setup interrupts */ + rv = bus_setup_intr(dev, sc->gen_irq_res, + INTR_TYPE_MISC | INTR_MPSAFE, NULL, host1x_gen_intr, + sc, &sc->gen_irq_h); + if (rv) { + device_printf(dev, "Cannot setup gen interrupt.\n"); + goto fail; + } + + rv = bus_setup_intr(dev, sc->syncpt_irq_res, + INTR_TYPE_MISC | INTR_MPSAFE, NULL, host1x_syncpt_intr, + sc, &sc->syncpt_irq_h); + if (rv) { + device_printf(dev, "Cannot setup syncpt interrupt.\n"); + goto fail; + } + + simplebus_init(dev, 0); + for (node = OF_child(node); node > 0; node = OF_peer(node)) + simplebus_add_device(dev, node, 0, NULL, -1, NULL); + + sc->irq_hook.ich_func = host1x_irq_hook; + sc->irq_hook.ich_arg = sc; + config_intrhook_establish(&sc->irq_hook); + bus_generic_new_pass(dev); + return; + +fail: + device_detach(dev); + return; +} + +static int +host1x_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 +host1x_attach(device_t dev) +{ + int rv, rid; + struct host1x_softc *sc; + + sc = device_get_softc(dev); + sc->tegra_drm = malloc(sizeof(struct tegra_drm), DRM_MEM_DRIVER, + M_WAITOK | M_ZERO); + + /* crosslink together all worlds */ + sc->dev = dev; + sc->tegra_drm->drm_dev.dev_private = &sc->tegra_drm; + sc->tegra_drm->drm_dev.dev = dev; + + TAILQ_INIT(&sc->clients); + + 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; + } + + return (bus_generic_attach(dev)); + +fail: + if (sc->tegra_drm != NULL) + free(sc->tegra_drm, DRM_MEM_DRIVER); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + LOCK_DESTROY(sc); + return (rv); +} + +static int +host1x_detach(device_t dev) +{ + struct host1x_softc *sc; + + sc = device_get_softc(dev); + + host1x_drm_exit(sc); + + if (sc->gen_irq_h != NULL) + bus_teardown_intr(dev, sc->gen_irq_res, sc->gen_irq_h); + if (sc->tegra_drm != NULL) + free(sc->tegra_drm, DRM_MEM_DRIVER); + if (sc->clk != NULL) + clk_release(sc->clk); + if (sc->reset != NULL) + hwreset_release(sc->reset); + if (sc->syncpt_irq_h != NULL) + bus_teardown_intr(dev, sc->syncpt_irq_res, sc->syncpt_irq_h); + if (sc->gen_irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 1, sc->gen_irq_res); + if (sc->syncpt_irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->syncpt_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 host1x_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, host1x_probe), + DEVMETHOD(device_attach, host1x_attach), + DEVMETHOD(device_detach, host1x_detach), + + /* Bus interface */ + DEVMETHOD(bus_new_pass, host1x_new_pass), + + /* Framebuffer service methods */ + DEVMETHOD(fb_getinfo, host1x_fb_helper_getinfo), + + /* tegra drm interface */ + DEVMETHOD(tegra_drm_register_client, host1x_register_client), + DEVMETHOD(tegra_drm_deregister_client, host1x_deregister_client), + + DEVMETHOD_END +}; + +static devclass_t host1x_devclass; +DEFINE_CLASS_1(host1x, host1x_driver, host1x_methods, + sizeof(struct host1x_softc), simplebus_driver); +EARLY_DRIVER_MODULE(host1x, simplebus, host1x_driver, + host1x_devclass, 0, 0, BUS_PASS_BUS); + +/* Bindings for fbd device. */ +extern devclass_t fbd_devclass; +extern driver_t fbd_driver; +DRIVER_MODULE(fbd, host1x, fbd_driver, fbd_devclass, 0, 0); + Property changes on: trunk/sys/arm/nvidia/drm2/tegra_host1x.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra124/files.tegra124 =================================================================== --- trunk/sys/arm/nvidia/tegra124/files.tegra124 (rev 0) +++ trunk/sys/arm/nvidia/tegra124/files.tegra124 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,72 @@ +# $MidnightBSD$ + +# +# Standard ARM support. +# +kern/kern_clocksource.c standard + +# +# 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_xhci.c optional xhci +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_mc.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 +# +# Firmware +# +tegra124_xusb_fw.c optional tegra124_xusb_fw \ + dependency "$S/arm/nvidia/tegra124/files.tegra124" \ + compile-with "${AWK} -f $S/tools/fw_stub.awk tegra124_xusb.fw:tegra124_xusb_fw -mtegra124_xusb_fw -c${.TARGET}" \ + no-implicit-rule before-depend local \ + clean "tegra124_xusb_fw.c" +tegra124_xusb.fwo optional tegra124_xusb_fw \ + dependency "tegra124_xusb.fw" \ + compile-with "${NORMAL_FWO}" \ + no-implicit-rule \ + clean "tegra124_xusb.fwo" +tegra124_xusb.fw optional tegra124_xusb_fw \ + dependency "$S/contrib/dev/nvidia/tegra124_xusb.bin.uu" \ + compile-with "${NORMAL_FW}" \ + no-obj no-implicit-rule \ + clean "tegra124_xusb.fw" +# +# 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 Property changes on: trunk/sys/arm/nvidia/tegra124/files.tegra124 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra124/std.tegra124 =================================================================== --- trunk/sys/arm/nvidia/tegra124/std.tegra124 (rev 0) +++ trunk/sys/arm/nvidia/tegra124/std.tegra124 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,11 @@ +# $MidnightBSD$ +cpu CPU_CORTEXA +machine arm armv6 +makeoptions CONF_CFLAGS="-march=armv7a" + +options INTRNG + +options IPI_IRQ_START=0 +options IPI_IRQ_END=15 + +files "../nvidia/tegra124/files.tegra124" Property changes on: trunk/sys/arm/nvidia/tegra124/std.tegra124 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra124/tegra124_car.c =================================================================== --- trunk/sys/arm/nvidia/tegra124/tegra124_car.c (rev 0) +++ trunk/sys/arm/nvidia/tegra124/tegra124_car.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,610 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_car.c 317013 2017-04-16 08:21:14Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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_xusb_hs) = {"xusb_ss_div2", "pllU_60"}; +PLIST(mux_xusb_ss) = {"pc_xusb_ss", "osc_div_clk"}; + + +/* Clocks ajusted online. */ +static struct clk_fixed_def fixed_clk_m = + FRATE(TEGRA124_CLK_CLK_M, "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), + + /* XUSB */ + FACT(TEGRA124_CLK_XUSB_SS_DIV2, "xusb_ss_div2", "xusb_ss", 1, 2), + +}; + + +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), + + /* USB. */ + MUX(TEGRA124_CLK_XUSB_HS_SRC, "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(TEGRA124_CLK_PLL_U_480M, "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 DEFINE_CLASS_0(car, tegra124_car_driver, tegra124_car_methods, + sizeof(struct tegra124_car_softc)); +EARLY_DRIVER_MODULE(tegra124_car, simplebus, tegra124_car_driver, + tegra124_car_devclass, NULL, NULL, BUS_PASS_TIMER); Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_car.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra124/tegra124_car.h =================================================================== --- trunk/sys/arm/nvidia/tegra124/tegra124_car.h (rev 0) +++ trunk/sys/arm/nvidia/tegra124/tegra124_car.h 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,338 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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. + * + * $FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_car.h 296936 2016-03-16 13:01:48Z mmel $ + */ + +#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 Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_car.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra124/tegra124_clk_per.c =================================================================== --- trunk/sys/arm/nvidia/tegra124/tegra124_clk_per.c (rev 0) +++ trunk/sys/arm/nvidia/tegra124/tegra124_clk_per.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,829 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_clk_per.c 317013 2017-04-16 08:21:14Z mmel $"); + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include "tegra124_car.h" + +/* The TEGRA124_CLK_XUSB_GATE is missing in current + * DT bindings, define it localy + */ +#ifdef TEGRA124_CLK_XUSB_GATE +#error "TEGRA124_CLK_XUSB_GATE is now defined, revisit XUSB code!" +#else +#define TEGRA124_CLK_XUSB_GATE 143 +#endif + +/* 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_c_p_clkm_mud_c2_c3_cud) = + {"pllM_out0", "pllC_out0", "pllP_out0", "clk_m", + "pllM_UD", "pllC2_out0", "pllC3_out0", "pllC_UD"}; + +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_enable_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", "pc_emc_2x", 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, "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_GATE, "xusb_gate", "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", W(27)), + GATE(DFLL_SOC, "dvfs_soc", "pc_dvfs_soc", W(27)), + GATE(XUSB_SS, "xusb_ss", "xusb_ss_mux", W(28)), + /* GATE(EMC_LATENCY, "emc_latency", "pc_emc_latency", W(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 bits / 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 DCF_IS_SATA 0x0006 +#define DCF_IS_VIC 0x0007 +#define DCF_IS_AUDIO 0x0008 +#define DCF_IS_SOR0 0x0009 +#define DCF_IS_EMC 0x000A + +/* 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(id, cn, pl, r, f) \ + PER_CLK(id, cn, pl, r, 8, 1, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV) + +/* Mux with fractional 16.1 divider. */ +#define CLK16_1(id, cn, pl, r, f) \ + PER_CLK(id, cn, pl, r, 16, 1, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV) +/* Mux with integer 16bits divider. */ +#define CLK16_0(id, cn, pl, r, f) \ + PER_CLK(id, cn, pl, r, 16, 0, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV) +/* Mux wihout divider. */ +#define CLK_0_0(id, cn, pl, r, f) \ + PER_CLK(id, cn, pl, r, 0, 0, (f) | DCF_HAVE_MUX) + +static struct periph_def periph_def[] = { + CLK_8_1(0, "pc_i2s1", mux_a_N_audio1_N_p_N_clkm, CLK_SOURCE_I2S1, DCF_HAVE_ENA), + CLK_8_1(0, "pc_i2s2", mux_a_N_audio2_N_p_N_clkm, CLK_SOURCE_I2S2, DCF_HAVE_ENA), + CLK_8_1(0, "pc_spdif_out", mux_a_N_audio_N_p_N_clkm, CLK_SOURCE_SPDIF_OUT, 0), + CLK_8_1(0, "pc_spdif_in", mux_p_c2_c_c3_m, CLK_SOURCE_SPDIF_IN, 0), + CLK_8_1(0, "pc_pwm", mux_p_c2_c_c3_clks_N_clkm, CLK_SOURCE_PWM, 0), + CLK_8_1(0, "pc_spi2", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI2, 0), + CLK_8_1(0, "pc_spi3", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI3, 0), + CLK16_0(0, "pc_i2c5", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C5, 0), + CLK16_0(0, "pc_i2c1", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C1, 0), + CLK_8_1(0, "pc_spi1", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI1, 0), + CLK_0_0(0, "pc_disp1", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_DISP1, 0), + CLK_0_0(0, "pc_disp2", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_DISP2, 0), + CLK_8_1(0, "pc_isp", mux_m_c_p_a_c2_c3_clkm_c4, CLK_SOURCE_ISP, 0), + CLK_8_1(0, "pc_vi", mux_m_c2_c_c3_p_N_a_c4, CLK_SOURCE_VI, DCF_IS_VI), + CLK_8_1(0, "pc_sdmmc1", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC1, 0), + CLK_8_1(0, "pc_sdmmc2", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC2, 0), + CLK_8_1(0, "pc_sdmmc4", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC4, 0), + CLK_8_1(0, "pc_vfir", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_VFIR, 0), + CLK_8_1(0, "pc_hsi", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HSI, 0), + CLK16_1(0, "pc_uarta", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTA, DCF_IS_UART), + CLK16_1(0, "pc_uartb", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTB, DCF_IS_UART), + CLK_8_1(0, "pc_host1x", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HOST1X, DCF_IS_HOST1X), + CLK_8_1(0, "pc_hdmi", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_HDMI, 0), + CLK16_0(0, "pc_i2c2", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C2, 0), + CLK_8_1(0, "pc_emc_2x", mux_m_c_p_clkm_mud_c2_c3_cud, CLK_SOURCE_EMC, DCF_IS_EMC), + CLK16_1(0, "pc_uartc", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTC, DCF_IS_UART), + CLK_8_1(0, "pc_vi_sensor", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_VI_SENSOR, 0), + CLK_8_1(0, "pc_spi4", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI4, 0), + CLK16_0(0, "pc_i2c3", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C3, 0), + CLK_8_1(0, "pc_sdmmc3", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC3, 0), + CLK16_1(0, "pc_uartd", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTD, DCF_IS_UART), + CLK_8_1(0, "pc_vde", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_VDE, 0), + CLK_8_1(0, "pc_owr", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_OWR, 0), + CLK_8_1(0, "pc_snor", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_NOR, 0), + CLK_8_1(0, "pc_csite", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_CSITE, 0), + CLK_8_1(0, "pc_i2s0", mux_a_N_audio0_N_p_N_clkm, CLK_SOURCE_I2S0, 0), +/* DTV xxx */ + CLK_8_1(0, "pc_msenc", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_MSENC, 0), + CLK_8_1(0, "pc_tsec", mux_p_c2_c_c3_m_a_clkm, CLK_SOURCE_TSEC, 0), +/* SPARE2 */ + + + CLK_8_1(0, "pc_mselect", mux_p_c2_c_c3_m_clks_clkm, CLK_SOURCE_MSELECT, 0), + CLK_8_1(0, "pc_tsensor", mux_p_c2_c_c3_clkm_N_clks, CLK_SOURCE_TSENSOR, 0), + CLK_8_1(0, "pc_i2s3", mux_a_N_audio3_N_p_N_clkm, CLK_SOURCE_I2S3, DCF_HAVE_ENA), + CLK_8_1(0, "pc_i2s4", mux_a_N_audio4_N_p_N_clkm, CLK_SOURCE_I2S4, DCF_HAVE_ENA), + CLK16_0(0, "pc_i2c4", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C4, 0), + CLK_8_1(0, "pc_spi5", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI5, 0), + CLK_8_1(0, "pc_spi6", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI6, 0), + CLK_8_1(0, "pc_audio", mux_sep_audio, CLK_SOURCE_AUDIO, DCF_IS_AUDIO), + CLK_8_1(0, "pc_dam0", mux_sep_audio, CLK_SOURCE_DAM0, DCF_IS_AUDIO), + CLK_8_1(0, "pc_dam1", mux_sep_audio, CLK_SOURCE_DAM1, DCF_IS_AUDIO), + CLK_8_1(0, "pc_dam2", mux_sep_audio, CLK_SOURCE_DAM2, DCF_IS_AUDIO), + CLK_8_1(0, "pc_hda2codec_2x", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HDA2CODEC_2X, 0), + CLK_8_1(0, "pc_actmon", mux_p_c2_c_c3_clks_N_clkm, CLK_SOURCE_ACTMON, 0), + CLK_8_1(0, "pc_extperiph1", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH1, 0), + CLK_8_1(0, "pc_extperiph2", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH2, 0), + CLK_8_1(0, "pc_extperiph3", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH3, 0), + CLK_8_1(0, "pc_i2c_slow", mux_p_c2_c_c3_clks_N_clkm, CLK_SOURCE_I2C_SLOW, 0), +/* SYS */ + CLK_8_1(0, "pc_sor0", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_SOR0, DCF_IS_SOR0), + CLK_8_1(0, "pc_sata_oob", mux_p_N_c_N_m_N_clkm, CLK_SOURCE_SATA_OOB, 0), + CLK_8_1(0, "pc_sata", mux_p_N_c_N_m_N_clkm, CLK_SOURCE_SATA, DCF_IS_SATA), + CLK_8_1(0, "pc_hda", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HDA, 0), + CLK_8_1(TEGRA124_CLK_XUSB_HOST_SRC, + "pc_xusb_core_host", mux_clkm_p_c2_c_c3_refre, CLK_SOURCE_XUSB_CORE_HOST, 0), + CLK_8_1(TEGRA124_CLK_XUSB_FALCON_SRC, + "pc_xusb_falcon", mux_clkm_p_c2_c_c3_refre, CLK_SOURCE_XUSB_FALCON, 0), + CLK_8_1(TEGRA124_CLK_XUSB_FS_SRC, + "pc_xusb_fs", mux_clkm_N_u48_N_p_N_u480, CLK_SOURCE_XUSB_FS, 0), + CLK_8_1(TEGRA124_CLK_XUSB_DEV_SRC, + "pc_xusb_core_dev", mux_clkm_p_c2_c_c3_refre, CLK_SOURCE_XUSB_CORE_DEV, 0), + CLK_8_1(TEGRA124_CLK_XUSB_SS_SRC, + "pc_xusb_ss", mux_clkm_refe_clks_u480_c_c2_c3_oscdiv, CLK_SOURCE_XUSB_SS, DCF_IS_XUSB_SS), + CLK_8_1(0, "pc_cilab", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_CILAB, 0), + CLK_8_1(0, "pc_cilcd", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_CILCD, 0), + CLK_8_1(0, "pc_cile", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_CILE, 0), + CLK_8_1(0, "pc_dsia_lp", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_DSIA_LP, 0), + CLK_8_1(0, "pc_dsib_lp", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_DSIB_LP, 0), + CLK_8_1(0, "pc_entropy", mux_p_clkm_clks_E, CLK_SOURCE_ENTROPY, 0), + CLK_8_1(0, "pc_dvfs_ref", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_DVFS_REF, DCF_HAVE_ENA), + CLK_8_1(0, "pc_dvfs_soc", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_DVFS_SOC, DCF_HAVE_ENA), + CLK_8_1(0, "pc_traceclkin", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_TRACECLKIN, 0), + CLK_8_1(0, "pc_adx", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_ADX, DCF_HAVE_ENA), + CLK_8_1(0, "pc_amx", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_AMX, DCF_HAVE_ENA), + CLK_8_1(0, "pc_emc_latency", mux_m_c_p_clkm_mud_c2_c3, CLK_SOURCE_EMC_LATENCY, 0), + CLK_8_1(0, "pc_soc_therm", mux_m_c_p_a_c2_c3, CLK_SOURCE_SOC_THERM, 0), + CLK_8_1(0, "pc_vi_sensor2", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_VI_SENSOR2, 0), + CLK16_0(0, "pc_i2c6", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C6, 0), + CLK_8_1(0, "pc_emc_dll", mux_m_c_p_clkm_mud_c2_c3, CLK_SOURCE_EMC_DLL, DCF_IS_EMC_DLL), + CLK_8_1(0, "pc_hdmi_audio", mux_p_c_c2_clkm, CLK_SOURCE_HDMI_AUDIO, 0), + CLK_8_1(0, "pc_clk72mhz", mux_p_c_c2_clkm, CLK_SOURCE_CLK72MHZ, 0), + CLK_8_1(0, "pc_adx1", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_ADX1, DCF_HAVE_ENA), + CLK_8_1(0, "pc_amx1", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_AMX1, DCF_HAVE_ENA), + CLK_8_1(0, "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 - 1); + + if (flags & CLK_SET_DRYRUN) { + if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) && + (*fout != (tmp / divider))) + return (ERANGE); + } else { + 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 <div_width) - 1; + sc->div_f_width = clkdef->div_f_width; + sc->div_f_mask = (1 <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_enable_reg), + ("Invalid clock index for enable: %d", idx)); + return (clk_enable_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"); + } + +} Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_clk_per.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra124/tegra124_clk_pll.c =================================================================== --- trunk/sys/arm/nvidia/tegra124/tegra124_clk_pll.c (rev 0) +++ trunk/sys/arm/nvidia/tegra124/tegra124_clk_pll.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,1148 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_clk_pll.c 317013 2017-04-16 08:21:14Z mmel $"); + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#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 5000 + +/* 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 pdiv_table pllrefe_map[] = { + {1, 0}, + {2, 1}, + {3, 2}, + {4, 3}, + {5, 4}, + {6, 5}, + {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, + .pdiv_table = pllrefe_map, + .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) - 1); + + while (tbl->divider != 0) { + if (p_div <= tbl->divider) + return (tbl->value); + tbl++; + } + return (0xFFFFFFFF); +} + +static uint32_t +reg_to_pdiv(struct pll_sc *sc, uint32_t reg) +{ + struct pdiv_table *tbl; + + tbl = sc->pdiv_table; + if (tbl == NULL) + return (1 << reg); + + while (tbl->divider) { + if (reg == tbl->value) + return (tbl->divider); + tbl++; + } + return (0); +} + +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)); +} + +/* + * PLLD2 is used as source for pixel clock for HDMI. + * We must be able to set it frequency very flexibly and + * precisely (within 5% tolerance limit allowed by HDMI specs). + * + * For this reason, it is necessary to search the full state space. + * Fortunately, thanks to early cycle terminations, performance + * is within acceptable limits. + */ +#define PLLD2_PFD_MIN 12000000 /* 12 MHz */ +#define PLLD2_PFD_MAX 38000000 /* 38 MHz */ +#define PLLD2_VCO_MIN 600000000 /* 600 MHz */ +#define PLLD2_VCO_MAX 1200000000 /* 1.2 GHz */ + +static int +plld2_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) +{ + uint32_t m, n, p; + uint32_t best_m, best_n, best_p; + uint64_t vco, pfd; + int64_t err, best_err; + struct mnp_bits *mnp_bits; + struct pdiv_table *tbl; + int p_idx, rv; + + mnp_bits = &sc->mnp_bits; + tbl = sc->pdiv_table; + best_err = INT64_MAX; + + for (p_idx = 0; tbl[p_idx].divider != 0; p_idx++) { + p = tbl[p_idx].divider; + + /* Check constraints */ + vco = *fout * p; + if (vco < PLLD2_VCO_MIN) + continue; + if (vco > PLLD2_VCO_MAX) + break; + + for (m = 1; m < (1 << mnp_bits->m_width); m++) { + n = (*fout * p * m + fin / 2) / fin; + + /* Check constraints */ + if (n == 0) + continue; + if (n >= (1 << mnp_bits->n_width)) + break; + vco = (fin * n) / m; + if (vco > PLLD2_VCO_MAX || vco < PLLD2_VCO_MIN) + continue; + pfd = fin / m; + if (pfd > PLLD2_PFD_MAX || vco < PLLD2_PFD_MIN) + continue; + + /* Constraints passed, save best result */ + err = *fout - vco / p; + if (err < 0) + err = -err; + if (err < best_err) { + best_err = err; + best_p = p; + best_m = m; + best_n = n; + } + if (err == 0) + goto done; + } + } +done: + /* + * HDMI specification allows 5% pixel clock tolerance, + * we will by a slightly stricter + */ + if (best_err > ((*fout * 100) / 4)) + return (ERANGE); + + if (flags & CLK_SET_DRYRUN) + return (0); + rv = pll_set_std(sc, fin, fout, flags, best_m, best_n, best_p); + /* XXXX Panic for rv == ERANGE ? */ + return (rv); +} + +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; + 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 +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); + } + + /* PLLX doesn't have bypass, disable it first. */ + RD4(sc, sc->base_reg, ®); + reg &= ~PLL_BASE_ENABLE; + WR4(sc, sc->base_reg, reg); + + /* 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 lock detection. */ + RD4(sc, sc->misc_reg, ®); + reg |= sc->lock_enable; + WR4(sc, sc->misc_reg, reg); + + /* Enable PLL. */ + RD4(sc, sc->base_reg, ®); + reg |= PLL_BASE_ENABLE; + WR4(sc, sc->base_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, ®); + + *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: %s requested freq: %llu, input freq: %llu\n", __func__, + clknode_get_name(clknode), *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); + } + if (sc->type == PLL_REFE) { + RD4(sc, sc->misc_reg, ®); + reg &= ~(1 << 29); /* Diasble lock override */ + 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); + +} Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_clk_pll.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra124/tegra124_clk_super.c =================================================================== --- trunk/sys/arm/nvidia/tegra124/tegra124_clk_super.c (rev 0) +++ trunk/sys/arm/nvidia/tegra124/tegra124_clk_super.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,266 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_clk_super.c 297576 2016-04-05 09:20:52Z mmel $"); + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#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 - 1) * 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"); + } + +} Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_clk_super.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra124/tegra124_coretemp.c =================================================================== --- trunk/sys/arm/nvidia/tegra124/tegra124_coretemp.c (rev 0) +++ trunk/sys/arm/nvidia/tegra124/tegra124_coretemp.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,273 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_coretemp.c 308374 2016-11-06 15:25:46Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#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]; + OF_prop_free(cells); + + 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) +{ + phandle_t root; + + root = OF_finddevice("/"); + if (!ofw_bus_node_is_compatible(root, "nvidia,tegra124")) + return; + 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 Thermal Sensor"); + 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 DEFINE_CLASS_0(tegra124_coretemp, tegra124_coretemp_driver, + tegra124_coretemp_methods, sizeof(struct tegra124_coretemp_softc)); +DRIVER_MODULE(tegra124_coretemp, cpu, tegra124_coretemp_driver, + tegra124_coretemp_devclass, NULL, NULL); Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_coretemp.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra124/tegra124_cpufreq.c =================================================================== --- trunk/sys/arm/nvidia/tegra124/tegra124_cpufreq.c (rev 0) +++ trunk/sys/arm/nvidia/tegra124/tegra124_cpufreq.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,599 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_cpufreq.c 308374 2016-11-06 15:25:46Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#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 = 1000000, /* XXX 0.9 V doesn't work on all boards */ + .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, + 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) roundup(val, div) +#define ROUND_DOWN(val, div) rounddown(val, 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); + } + + /* Switch supermux to PLLP first */ + rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_p); + if (rv != 0) { + device_printf(sc->dev, "Can't set parent to PLLP\n"); + return (rv); + } + + /* Set PLLX frequency */ + rv = clk_set_freq(sc->clk_pll_x, point->freq, CLK_SET_ROUND_DOWN); + if (rv != 0) { + device_printf(sc->dev, "Can't set CPU clock frequency\n"); + return (rv); + } + + rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_x); + if (rv != 0) { + device_printf(sc->dev, "Can't set parent to PLLX\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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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) +{ + phandle_t root; + + root = OF_finddevice("/"); + if (!ofw_bus_node_is_compatible(root, "nvidia,tegra124")) + return; + + if (device_get_unit(parent) != 0) + return; + 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) +{ + + 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 DEFINE_CLASS_0(tegra124_cpufreq, tegra124_cpufreq_driver, + tegra124_cpufreq_methods, sizeof(struct tegra124_cpufreq_softc)); +DRIVER_MODULE(tegra124_cpufreq, cpu, tegra124_cpufreq_driver, + tegra124_cpufreq_devclass, NULL, NULL); Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_cpufreq.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra124/tegra124_machdep.c =================================================================== --- trunk/sys/arm/nvidia/tegra124/tegra124_machdep.c (rev 0) +++ trunk/sys/arm/nvidia/tegra124/tegra124_machdep.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,157 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 "opt_platform.h" + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_machdep.c 331893 2018-04-02 23:19:07Z gonzo $"); + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#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) + +static vm_offset_t +tegra124_lastaddr(platform_t plat) +{ + + return (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) +{ + + devmap_add_entry(0x70000000, 0x01000000); + return (0); +} + +static void +tegra124_cpu_reset(platform_t plat) +{ + 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=0x70000000 + * option SOCDEV_VA=0x70000000 + * option EARLY_PRINTF + */ +#ifdef EARLY_PRINTF +static void +tegra124_early_putc(int c) +{ + + volatile uint32_t * UART_STAT_REG = (uint32_t *)(0x70006314); + volatile uint32_t * UART_TX_REG = (uint32_t *)(0x70006300); + const uint32_t UART_TXRDY = (1 << 6); + 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), + PLATFORMMETHOD(platform_cpu_reset, tegra124_cpu_reset), + +#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", 120); Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_machdep.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra124/tegra124_mp.c =================================================================== --- trunk/sys/arm/nvidia/tegra124/tegra124_mp.c (rev 0) +++ trunk/sys/arm/nvidia/tegra124/tegra124_mp.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,129 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_mp.c 307344 2016-10-15 08:27:54Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#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); + } + + } + dsb(); + sev(); + bus_space_unmap(fdtbus_bs_tag, pmc, PMC_SIZE); + bus_space_unmap(fdtbus_bs_tag, exvec, TEGRA_EXCEPTION_VECTORS_SIZE); +} Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_mp.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra124/tegra124_mp.h =================================================================== --- trunk/sys/arm/nvidia/tegra124/tegra124_mp.h (rev 0) +++ trunk/sys/arm/nvidia/tegra124/tegra124_mp.h 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,36 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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. + * + * $FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_mp.h 296936 2016-03-16 13:01:48Z mmel $ + */ + +#ifndef _TEGRA124_MP_H_ +#define _TEGRA124_MP_H_ + +void tegra124_mp_setmaxid(platform_t plat); +void tegra124_mp_start_ap(platform_t plat); + +#endif /*_TEGRA124_MP_H_*/ \ No newline at end of file Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_mp.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra124/tegra124_pmc.c =================================================================== --- trunk/sys/arm/nvidia/tegra124/tegra124_pmc.c (rev 0) +++ trunk/sys/arm/nvidia/tegra124/tegra124_pmc.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,563 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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. + * + * $FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_pmc.c 308335 2016-11-05 10:56:32Z mmel $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#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, 0, "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 devclass_t tegra124_pmc_devclass; +static DEFINE_CLASS_0(pmc, tegra124_pmc_driver, tegra124_pmc_methods, + sizeof(struct tegra124_pmc_softc)); +EARLY_DRIVER_MODULE(tegra124_pmc, simplebus, tegra124_pmc_driver, + tegra124_pmc_devclass, NULL, NULL, 70); Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_pmc.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c =================================================================== --- trunk/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c (rev 0) +++ trunk/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,1213 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c 332025 2018-04-04 13:23:06Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "phydev_if.h" + +/* FUSE calibration data. */ +#define FUSE_XUSB_CALIB 0x0F0 +#define FUSE_XUSB_CALIB_HS_CURR_LEVEL_123(x) (((x) >> 15) & 0x3F); +#define FUSE_XUSB_CALIB_HS_IREF_CAP(x) (((x) >> 13) & 0x03); +#define FUSE_XUSB_CALIB_HS_SQUELCH_LEVEL(x) (((x) >> 11) & 0x03); +#define FUSE_XUSB_CALIB_HS_TERM_RANGE_ADJ(x) (((x) >> 7) & 0x0F); +#define FUSE_XUSB_CALIB_HS_CURR_LEVEL_0(x) (((x) >> 0) & 0x3F); + + +/* Registers. */ +#define XUSB_PADCTL_USB2_PAD_MUX 0x004 + +#define XUSB_PADCTL_USB2_PORT_CAP 0x008 +#define USB2_PORT_CAP_ULPI_PORT_INTERNAL (1 << 25) +#define USB2_PORT_CAP_ULPI_PORT_CAP (1 << 24) +#define USB2_PORT_CAP_PORT_REVERSE_ID(p) (1 << (3 + (p) * 4)) +#define USB2_PORT_CAP_PORT_INTERNAL(p) (1 << (2 + (p) * 4)) +#define USB2_PORT_CAP_PORT_CAP(p, x) (((x) & 3) << ((p) * 4)) +#define USB2_PORT_CAP_PORT_CAP_OTG 0x3 +#define USB2_PORT_CAP_PORT_CAP_DEVICE 0x2 +#define USB2_PORT_CAP_PORT_CAP_HOST 0x1 +#define USB2_PORT_CAP_PORT_CAP_DISABLED 0x0 + +#define XUSB_PADCTL_SS_PORT_MAP 0x014 +#define SS_PORT_MAP_PORT_INTERNAL(p) (1 << (3 + (p) * 4)) +#define SS_PORT_MAP_PORT_MAP(p, x) (((x) & 7) << ((p) * 4)) + +#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 ELPG_PROGRAM_SSP_ELPG_VCORE_DOWN(x) (1 << (18 + (x) * 4)) +#define ELPG_PROGRAM_SSP_ELPG_CLAMP_EN_EARLY(x) (1 << (17 + (x) * 4)) +#define ELPG_PROGRAM_SSP_ELPG_CLAMP_EN(x) (1 << (16 + (x) * 4)) + +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040 +#define IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19) +#define IOPHY_PLL_P0_CTL1_REFCLK_SEL(x) (((x) & 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_IOPHY_USB3_PAD_CTL2(x) (0x058 + (x) * 4) +#define IOPHY_USB3_PAD_CTL2_CDR_CNTL(x) (((x) & 0x00FF) << 4) +#define IOPHY_USB3_PAD_CTL2_RX_EQ(x) (((x) & 0xFFFF) << 8) +#define IOPHY_USB3_PAD_CTL2_RX_WANDER(x) (((x) & 0x000F) << 4) +#define IOPHY_USB3_PAD_CTL2_RX_TERM_CNTL(x) (((x) & 0x0003) << 2) +#define IOPHY_USB3_PAD_CTL2_TX_TERM_CNTL(x) (((x) & 0x0003) << 0) + + +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4(x) (0x068 + (x) * 4) + +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0(x) (0x0A0 + (x) * 4) +#define USB2_OTG_PAD_CTL0_LSBIAS_SEL (1 << 23) +#define USB2_OTG_PAD_CTL0_DISCON_DETECT_METHOD (1 << 22) +#define USB2_OTG_PAD_CTL0_PD_ZI (1 << 21) +#define USB2_OTG_PAD_CTL0_PD2 (1 << 20) +#define USB2_OTG_PAD_CTL0_PD (1 << 19) +#define USB2_OTG_PAD_CTL0_TERM_EN (1 << 18) +#define USB2_OTG_PAD_CTL0_LS_LS_FSLEW(x) (((x) & 0x03) << 16) +#define USB2_OTG_PAD_CTL0_LS_RSLEW(x) (((x) & 0x03) << 14) +#define USB2_OTG_PAD_CTL0_FS_SLEW(x) (((x) & 0x03) << 12) +#define USB2_OTG_PAD_CTL0_HS_SLEW(x) (((x) & 0x3F) << 6) +#define USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(x) (((x) & 0x3F) << 0) + +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1(x) (0x0AC + (x) * 4) +#define USB2_OTG_PAD_CTL1_RPU_RANGE_ADJ(x) (((x) & 0x3) << 11) +#define USB2_OTG_PAD_CTL1_HS_IREF_CAP(x) (((x) & 0x3) << 9) +#define USB2_OTG_PAD_CTL1_SPARE(x) (((x) & 0x3) << 7) +#define USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(x) (((x) & 0xF) << 3) +#define USB2_OTG_PAD_CTL1_PD_DR (1 << 2) +#define USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP (1 << 1) +#define USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP (1 << 0) + +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x0B8 +#define USB2_BIAS_PAD_CTL0_ADJRPU(x) (((x) & 0x7) << 14) +#define USB2_BIAS_PAD_CTL0_PD_TRK (1 << 13) +#define USB2_BIAS_PAD_CTL0_PD (1 << 12) +#define USB2_BIAS_PAD_CTL0_TERM_OFFSETL(x) (((x) & 0x3) << 9) +#define USB2_BIAS_PAD_CTL0_VBUS_LEVEL(x) (((x) & 0x3) << 7) +#define USB2_BIAS_PAD_CTL0_HS_CHIRP_LEVEL(x) (((x) & 0x3) << 5) +#define USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(x) (((x) & 0x7) << 2) +#define USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(x) (((x) & 0x3) << 0) + +#define XUSB_PADCTL_HSIC_PAD0_CTL0 0x0C8 +#define HSIC_PAD0_CTL0_HSIC_OPT(x) (((x) & 0xF) << 16) +#define HSIC_PAD0_CTL0_TX_SLEWN(x) (((x) & 0xF) << 12) +#define HSIC_PAD0_CTL0_TX_SLEWP(x) (((x) & 0xF) << 8) +#define HSIC_PAD0_CTL0_TX_RTUNEN(x) (((x) & 0xF) << 4) +#define HSIC_PAD0_CTL0_TX_RTUNEP(x) (((x) & 0xF) << 0) + +#define XUSB_PADCTL_USB3_PAD_MUX 0x134 +#define USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x))) +#define USB3_PAD_MUX_SATA_IDDQ_DISABLE (1 << 6) + + +#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 + + +#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) + + +struct padctl_softc { + device_t dev; + struct resource *mem_res; + hwreset_t rst; + int phy_ena_cnt; + + /* Fuses calibration data */ + uint32_t hs_curr_level_0; + uint32_t hs_curr_level_123; + uint32_t hs_iref_cap; + uint32_t hs_term_range_adj; + uint32_t hs_squelch_level; + + uint32_t hs_curr_level_offset; +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-xusb-padctl", 1}, + {NULL, 0}, +}; + +/* Ports. */ +enum padctl_port_type { + PADCTL_PORT_USB2, + PADCTL_PORT_ULPI, + PADCTL_PORT_HSIC, + PADCTL_PORT_USB3, +}; + +struct padctl_lane; +struct padctl_port { + enum padctl_port_type type; + const char *name; + const char *base_name; + int idx; + int (*init)(struct padctl_softc *sc, + struct padctl_port *port); + + /* Runtime data. */ + bool enabled; + regulator_t supply_vbus; /* USB2, USB3 */ + bool internal; /* ULPI, USB2, USB3 */ + uint32_t companion; /* USB3 */ + struct padctl_lane *lane; +}; + +static int usb3_port_init(struct padctl_softc *sc, struct padctl_port *port); + +#define PORT(t, n, p, i) { \ + .type = t, \ + .name = n "-" #p, \ + .base_name = n, \ + .idx = p, \ + .init = i, \ +} +static struct padctl_port ports_tbl[] = { + PORT(PADCTL_PORT_USB2, "usb2", 0, NULL), + PORT(PADCTL_PORT_USB2, "usb2", 1, NULL), + PORT(PADCTL_PORT_USB2, "usb2", 2, NULL), + PORT(PADCTL_PORT_ULPI, "ulpi", 0, NULL), + PORT(PADCTL_PORT_HSIC, "hsic", 0, NULL), + PORT(PADCTL_PORT_HSIC, "hsic", 1, NULL), + PORT(PADCTL_PORT_USB3, "usb3", 0, usb3_port_init), + PORT(PADCTL_PORT_USB3, "usb3", 1, usb3_port_init), +}; + +/* Pads - a group of lannes. */ +enum padctl_pad_type { + PADCTL_PAD_USB2, + PADCTL_PAD_ULPI, + PADCTL_PAD_HSIC, + PADCTL_PAD_PCIE, + PADCTL_PAD_SATA, +}; + +struct padctl_lane; +struct padctl_pad { + const char *name; + enum padctl_pad_type type; + int (*powerup)(struct padctl_softc *sc, + struct padctl_lane *lane); + int (*powerdown)(struct padctl_softc *sc, + struct padctl_lane *lane); + /* Runtime data. */ + bool enabled; + struct padctl_lane *lanes[8]; /* Safe maximum value. */ + int nlanes; +}; + +static int usb2_powerup(struct padctl_softc *sc, struct padctl_lane *lane); +static int usb2_powerdown(struct padctl_softc *sc, struct padctl_lane *lane); +static int pcie_powerup(struct padctl_softc *sc, struct padctl_lane *lane); +static int pcie_powerdown(struct padctl_softc *sc, struct padctl_lane *lane); +static int sata_powerup(struct padctl_softc *sc, struct padctl_lane *lane); +static int sata_powerdown(struct padctl_softc *sc, struct padctl_lane *lane); + +#define PAD(n, t, u, d) { \ + .name = n, \ + .type = t, \ + .powerup = u, \ + .powerdown = d, \ +} +static struct padctl_pad pads_tbl[] = { + PAD("usb2", PADCTL_PAD_USB2, usb2_powerup, usb2_powerdown), + PAD("ulpi", PADCTL_PAD_ULPI, NULL, NULL), + PAD("hsic", PADCTL_PAD_HSIC, NULL, NULL), + PAD("pcie", PADCTL_PAD_PCIE, pcie_powerup, pcie_powerdown), + PAD("sata", PADCTL_PAD_SATA, sata_powerup, sata_powerdown), +}; + +/* Lanes. */ +static char *otg_mux[] = {"snps", "xusb", "uart", "rsvd"}; +static char *usb_mux[] = {"snps", "xusb"}; +static char *pci_mux[] = {"pcie", "usb3-ss", "sata", "rsvd"}; + +struct padctl_lane { + const char *name; + int idx; + bus_size_t reg; + uint32_t shift; + uint32_t mask; + char **mux; + int nmux; + /* Runtime data. */ + bool enabled; + struct padctl_pad *pad; + struct padctl_port *port; + int mux_idx; + +}; + +#define LANE(n, p, r, s, m, mx) { \ + .name = n "-" #p, \ + .idx = p, \ + .reg = r, \ + .shift = s, \ + .mask = m, \ + .mux = mx, \ + .nmux = nitems(mx), \ +} +static struct padctl_lane lanes_tbl[] = { + LANE("usb2", 0, XUSB_PADCTL_USB2_PAD_MUX, 0, 0x3, otg_mux), + LANE("usb2", 1, XUSB_PADCTL_USB2_PAD_MUX, 2, 0x3, otg_mux), + LANE("usb2", 2, XUSB_PADCTL_USB2_PAD_MUX, 4, 0x3, otg_mux), + LANE("ulpi", 0, XUSB_PADCTL_USB2_PAD_MUX, 12, 0x1, usb_mux), + LANE("hsic", 0, XUSB_PADCTL_USB2_PAD_MUX, 14, 0x1, usb_mux), + LANE("hsic", 1, XUSB_PADCTL_USB2_PAD_MUX, 15, 0x1, usb_mux), + LANE("pcie", 0, XUSB_PADCTL_USB3_PAD_MUX, 16, 0x3, pci_mux), + LANE("pcie", 1, XUSB_PADCTL_USB3_PAD_MUX, 18, 0x3, pci_mux), + LANE("pcie", 2, XUSB_PADCTL_USB3_PAD_MUX, 20, 0x3, pci_mux), + LANE("pcie", 3, XUSB_PADCTL_USB3_PAD_MUX, 22, 0x3, pci_mux), + LANE("pcie", 4, XUSB_PADCTL_USB3_PAD_MUX, 24, 0x3, pci_mux), + LANE("sata", 0, XUSB_PADCTL_USB3_PAD_MUX, 26, 0x3, pci_mux), +}; + +/* Define all possible mappings for USB3 port lanes */ +struct padctl_lane_map { + int port_idx; + enum padctl_pad_type pad_type; + int lane_idx; +}; + +#define LANE_MAP(pi, pt, li) { \ + .port_idx = pi, \ + .pad_type = pt, \ + .lane_idx = li, \ +} +static struct padctl_lane_map lane_map_tbl[] = { + LANE_MAP(0, PADCTL_PAD_PCIE, 0), /* port USB3-0 -> lane PCIE-0 */ + LANE_MAP(1, PADCTL_PAD_PCIE, 1), /* port USB3-1 -> lane PCIE-1 */ + /* -- or -- */ + LANE_MAP(1, PADCTL_PAD_SATA, 0), /* port USB3-1 -> lane SATA-0 */ +}; + + /* Phy class and methods. */ +static int xusbpadctl_phy_enable(struct phynode *phy, bool enable); +static phynode_method_t xusbpadctl_phynode_methods[] = { + PHYNODEMETHOD(phynode_enable, xusbpadctl_phy_enable), + PHYNODEMETHOD_END + +}; +DEFINE_CLASS_1(xusbpadctl_phynode, xusbpadctl_phynode_class, + xusbpadctl_phynode_methods, 0, phynode_class); + +static struct padctl_port *search_lane_port(struct padctl_softc *sc, + struct padctl_lane *lane); +/* ------------------------------------------------------------------------- + * + * PHY functions + */ +static int +usb3_port_init(struct padctl_softc *sc, struct padctl_port *port) +{ + uint32_t reg; + + reg = RD4(sc, XUSB_PADCTL_SS_PORT_MAP); + if (port->internal) + reg &= ~SS_PORT_MAP_PORT_INTERNAL(port->idx); + else + reg |= SS_PORT_MAP_PORT_INTERNAL(port->idx); + reg &= ~SS_PORT_MAP_PORT_MAP(port->idx, ~0); + reg |= SS_PORT_MAP_PORT_MAP(port->idx, port->companion); + WR4(sc, XUSB_PADCTL_SS_PORT_MAP, reg); + + reg = RD4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(port->idx)); + reg &= ~IOPHY_USB3_PAD_CTL2_CDR_CNTL(~0); + reg &= ~IOPHY_USB3_PAD_CTL2_RX_EQ(~0); + reg &= ~IOPHY_USB3_PAD_CTL2_RX_WANDER(~0); + reg |= IOPHY_USB3_PAD_CTL2_CDR_CNTL(0x24); + reg |= IOPHY_USB3_PAD_CTL2_RX_EQ(0xF070); + reg |= IOPHY_USB3_PAD_CTL2_RX_WANDER(0xF); + WR4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(port->idx), reg); + + WR4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL4(port->idx), + 0x002008EE); + + reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); + reg &= ~ELPG_PROGRAM_SSP_ELPG_VCORE_DOWN(port->idx); + WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); + reg &= ~ELPG_PROGRAM_SSP_ELPG_CLAMP_EN_EARLY(port->idx); + WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); + reg &= ~ELPG_PROGRAM_SSP_ELPG_CLAMP_EN(port->idx); + WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + return (0); +} + +static int +pcie_powerup(struct padctl_softc *sc, struct padctl_lane *lane) +{ + uint32_t reg; + int i; + + reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + reg &= ~IOPHY_PLL_P0_CTL1_REFCLK_SEL(~0); + WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); + DELAY(100); + + reg = RD4(sc, 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; + WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL2, reg); + DELAY(100); + + reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + reg |= IOPHY_PLL_P0_CTL1_PLL_RST; + WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); + DELAY(100); + + for (i = 100; i > 0; i--) { + reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + if (reg & IOPHY_PLL_P0_CTL1_PLL0_LOCKDET) + break; + DELAY(10); + } + if (i <= 0) { + device_printf(sc->dev, "Failed to power up PCIe phy\n"); + return (ETIMEDOUT); + } + reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); + reg |= USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx); + WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); + + return (0); +} + +static int +pcie_powerdown(struct padctl_softc *sc, struct padctl_lane *lane) +{ + uint32_t reg; + + reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); + reg &= ~USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx); + WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); + + reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + reg &= ~IOPHY_PLL_P0_CTL1_PLL_RST; + WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); + DELAY(100); + + return (0); + +} + +static int +sata_powerup(struct padctl_softc *sc, struct padctl_lane *lane) +{ + uint32_t reg; + int i; + + reg = RD4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); + reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD; + reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ; + WR4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg); + + reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg &= ~IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD; + reg &= ~IOPHY_PLL_S0_CTL1_PLL_IDDQ; + WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + + reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg |= IOPHY_PLL_S0_CTL1_PLL1_MODE; + WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + + reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L; + WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + + for (i = 100; i >= 0; i--) { + reg = RD4(sc, 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); + } + reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); + reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L; + WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); + + reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); + reg |= USB3_PAD_MUX_SATA_IDDQ_DISABLE; + WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); + + return (0); +} + +static int +sata_powerdown(struct padctl_softc *sc, struct padctl_lane *lane) +{ + uint32_t reg; + + reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); + reg &= ~USB3_PAD_MUX_SATA_IDDQ_DISABLE; + WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); + + reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg &= ~IOPHY_PLL_S0_CTL1_PLL_RST_L; + WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + DELAY(100); + + reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg &= ~IOPHY_PLL_S0_CTL1_PLL1_MODE; + WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + DELAY(100); + + reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg |= IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD; + reg |= IOPHY_PLL_S0_CTL1_PLL_IDDQ; + WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + DELAY(100); + + reg = RD4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); + reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD; + reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ; + WR4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg); + DELAY(100); + + return (0); +} + +static int +usb2_powerup(struct padctl_softc *sc, struct padctl_lane *lane) +{ + uint32_t reg; + struct padctl_port *port; + int rv; + + port = search_lane_port(sc, lane); + if (port == NULL) { + device_printf(sc->dev, "Cannot find port for lane: %s\n", + lane->name); + } + reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); + reg &= ~USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(~0); + reg &= ~USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(~0); + reg |= USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(sc->hs_squelch_level); + reg |= USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(5); + WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg); + + reg = RD4(sc, XUSB_PADCTL_USB2_PORT_CAP); + reg &= ~USB2_PORT_CAP_PORT_CAP(lane->idx, ~0); + reg |= USB2_PORT_CAP_PORT_CAP(lane->idx, USB2_PORT_CAP_PORT_CAP_HOST); + WR4(sc, XUSB_PADCTL_USB2_PORT_CAP, reg); + + reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx)); + reg &= ~USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(~0); + reg &= ~USB2_OTG_PAD_CTL0_HS_SLEW(~0); + reg &= ~USB2_OTG_PAD_CTL0_LS_RSLEW(~0); + reg &= ~USB2_OTG_PAD_CTL0_PD; + reg &= ~USB2_OTG_PAD_CTL0_PD2; + reg &= ~USB2_OTG_PAD_CTL0_PD_ZI; + + reg |= USB2_OTG_PAD_CTL0_HS_SLEW(14); + if (lane->idx == 0) { + reg |= USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(sc->hs_curr_level_0); + reg |= USB2_OTG_PAD_CTL0_LS_RSLEW(3); + } else { + reg |= USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(sc->hs_curr_level_123); + reg |= USB2_OTG_PAD_CTL0_LS_RSLEW(0); + } + WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx), reg); + + reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx)); + reg &= ~USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(~0); + reg &= ~USB2_OTG_PAD_CTL1_HS_IREF_CAP(~0); + reg &= ~USB2_OTG_PAD_CTL1_PD_DR; + reg &= ~USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP; + reg &= ~USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP; + + reg |= USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(sc->hs_term_range_adj); + reg |= USB2_OTG_PAD_CTL1_HS_IREF_CAP(sc->hs_iref_cap); + WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx), reg); + + if (port != NULL && port->supply_vbus != NULL) { + rv = regulator_enable(port->supply_vbus); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable vbus regulator\n"); + return (rv); + } + } + reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); + reg &= ~USB2_BIAS_PAD_CTL0_PD; + WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg); + + return (0); +} + +static int +usb2_powerdown(struct padctl_softc *sc, struct padctl_lane *lane) +{ + uint32_t reg; + struct padctl_port *port; + int rv; + + port = search_lane_port(sc, lane); + if (port == NULL) { + device_printf(sc->dev, "Cannot find port for lane: %s\n", + lane->name); + } + reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); + reg |= USB2_BIAS_PAD_CTL0_PD; + WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg); + + if (port != NULL && port->supply_vbus != NULL) { + rv = regulator_enable(port->supply_vbus); + if (rv != 0) { + device_printf(sc->dev, + "Cannot disable vbus regulator\n"); + return (rv); + } + } + return (0); +} + + +static int +phy_powerup(struct padctl_softc *sc) +{ + uint32_t reg; + + reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); + reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN; + WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); + reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN; + WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); + reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY; + WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + return (0); +} + +static int +phy_powerdown(struct padctl_softc *sc) +{ + uint32_t reg; + + reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); + reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY; + WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); + reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN; + WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); + reg |= ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN; + WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + return (0); +} + +static int +xusbpadctl_phy_enable(struct phynode *phy, bool enable) +{ + device_t dev; + intptr_t id; + struct padctl_softc *sc; + struct padctl_lane *lane; + struct padctl_pad *pad; + int rv; + + dev = phynode_get_device(phy); + id = phynode_get_id(phy); + sc = device_get_softc(dev); + + if (id < 0 || id >= nitems(lanes_tbl)) { + device_printf(dev, "Unknown phy: %d\n", id); + return (ENXIO); + } + lane = lanes_tbl + id; + if (!lane->enabled) { + device_printf(dev, "Lane is not enabled/configured: %s\n", + lane->name); + return (ENXIO); + } + pad = lane->pad; + if (enable) { + if (sc->phy_ena_cnt == 0) { + rv = phy_powerup(sc); + if (rv != 0) + return (rv); + } + sc->phy_ena_cnt++; + } + + if (enable) + rv = pad->powerup(sc, lane); + else + rv = pad->powerdown(sc, lane); + if (rv != 0) + return (rv); + + if (!enable) { + if (sc->phy_ena_cnt == 1) { + rv = phy_powerdown(sc); + if (rv != 0) + return (rv); + } + sc->phy_ena_cnt--; + } + + return (0); +} + +/* ------------------------------------------------------------------------- + * + * FDT processing + */ +static struct padctl_port * +search_port(struct padctl_softc *sc, char *port_name) +{ + int i; + + for (i = 0; i < nitems(ports_tbl); i++) { + if (strcmp(port_name, ports_tbl[i].name) == 0) + return (&ports_tbl[i]); + } + return (NULL); +} + +static struct padctl_port * +search_lane_port(struct padctl_softc *sc, struct padctl_lane *lane) +{ + int i; + + for (i = 0; i < nitems(ports_tbl); i++) { + if (!ports_tbl[i].enabled) + continue; + if (ports_tbl[i].lane == lane) + return (ports_tbl + i); + } + return (NULL); +} + +static struct padctl_lane * +search_lane(struct padctl_softc *sc, 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 struct padctl_lane * +search_pad_lane(struct padctl_softc *sc, enum padctl_pad_type type, int idx) +{ + int i; + + for (i = 0; i < nitems(lanes_tbl); i++) { + if (!lanes_tbl[i].enabled) + continue; + if (type == lanes_tbl[i].pad->type && idx == lanes_tbl[i].idx) + return (lanes_tbl + i); + } + return (NULL); +} + +static struct padctl_lane * +search_usb3_pad_lane(struct padctl_softc *sc, int idx) +{ + int i; + struct padctl_lane *lane, *tmp; + + lane = NULL; + for (i = 0; i < nitems(lane_map_tbl); i++) { + if (idx != lane_map_tbl[i].port_idx) + continue; + tmp = search_pad_lane(sc, lane_map_tbl[i].pad_type, + lane_map_tbl[i].lane_idx); + if (tmp == NULL) + continue; + if (strcmp(tmp->mux[tmp->mux_idx], "usb3-ss") != 0) + continue; + if (lane != NULL) { + device_printf(sc->dev, "Duplicated mappings found for" + " lanes: %s and %s\n", lane->name, tmp->name); + return (NULL); + } + lane = tmp; + } + return (lane); +} + +static struct padctl_pad * +search_pad(struct padctl_softc *sc, char *pad_name) +{ + int i; + + for (i = 0; i < nitems(pads_tbl); i++) { + if (strcmp(pad_name, pads_tbl[i].name) == 0) + return (pads_tbl + i); + } + return (NULL); +} + +static int +search_mux(struct padctl_softc *sc, 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 +config_lane(struct padctl_softc *sc, struct padctl_lane *lane) +{ + uint32_t reg; + + reg = RD4(sc, lane->reg); + reg &= ~(lane->mask << lane->shift); + reg |= (lane->mux_idx & lane->mask) << lane->shift; + WR4(sc, lane->reg, reg); + return (0); +} + +static int +process_lane(struct padctl_softc *sc, phandle_t node, struct padctl_pad *pad) +{ + struct padctl_lane *lane; + struct phynode *phynode; + struct phynode_init_def phy_init; + char *name; + char *function; + int rv; + + name = NULL; + function = NULL; + rv = OF_getprop_alloc(node, "name", 1, (void **)&name); + if (rv <= 0) { + device_printf(sc->dev, "Cannot read lane name.\n"); + return (ENXIO); + } + + lane = search_lane(sc, name); + if (lane == NULL) { + device_printf(sc->dev, "Unknown lane: %s\n", name); + rv = ENXIO; + goto end; + } + + /* Read function (mux) settings. */ + rv = OF_getprop_alloc(node, "nvidia,function", 1, (void **)&function); + if (rv <= 0) { + device_printf(sc->dev, "Cannot read lane function.\n"); + rv = ENXIO; + goto end; + } + + lane->mux_idx = search_mux(sc, lane, function); + if (lane->mux_idx == ~0) { + device_printf(sc->dev, "Unknown function %s for lane %s\n", + function, name); + rv = ENXIO; + goto end; + } + + rv = config_lane(sc, lane); + if (rv != 0) { + device_printf(sc->dev, "Cannot configure lane: %s: %d\n", + name, rv); + rv = ENXIO; + goto end; + } + lane->pad = pad; + lane->enabled = true; + pad->lanes[pad->nlanes++] = lane; + + /* Create and register phy. */ + bzero(&phy_init, sizeof(phy_init)); + phy_init.id = lane - lanes_tbl; + phy_init.ofw_node = node; + phynode = phynode_create(sc->dev, &xusbpadctl_phynode_class, &phy_init); + if (phynode == NULL) { + device_printf(sc->dev, "Cannot create phy\n"); + rv = ENXIO; + goto end; + } + if (phynode_register(phynode) == NULL) { + device_printf(sc->dev, "Cannot create phy\n"); + return (ENXIO); + } + + rv = 0; + +end: + if (name != NULL) + OF_prop_free(name); + if (function != NULL) + OF_prop_free(function); + return (rv); +} + +static int +process_pad(struct padctl_softc *sc, phandle_t node) +{ + struct padctl_pad *pad; + char *name; + int rv; + + name = NULL; + rv = OF_getprop_alloc(node, "name", 1, (void **)&name); + if (rv <= 0) { + device_printf(sc->dev, "Cannot read pad name.\n"); + return (ENXIO); + } + pad = search_pad(sc, name); + if (pad == NULL) { + device_printf(sc->dev, "Unknown pad: %s\n", name); + rv = ENXIO; + goto end; + } + + /* Read and process associated lanes. */ + node = ofw_bus_find_child(node, "lanes"); + if (node <= 0) { + device_printf(sc->dev, "Cannot find regulators subnode\n"); + rv = ENXIO; + goto end; + } + + for (node = OF_child(node); node != 0; node = OF_peer(node)) { + if (!fdt_is_enabled(node)) + continue; + + rv = process_lane(sc, node, pad); + if (rv != 0) + goto end; + } + pad->enabled = true; + rv = 0; +end: + if (name != NULL) + OF_prop_free(name); + return (rv); +} + +static int +process_port(struct padctl_softc *sc, phandle_t node) +{ + + struct padctl_port *port; + char *name; + int rv; + + name = NULL; + rv = OF_getprop_alloc(node, "name", 1, (void **)&name); + if (rv <= 0) { + device_printf(sc->dev, "Cannot read port name.\n"); + return (ENXIO); + } + + port = search_port(sc, name); + if (port == NULL) { + device_printf(sc->dev, "Unknown port: %s\n", name); + rv = ENXIO; + goto end; + } + + if (port->type == PADCTL_PORT_USB3) { + rv = OF_getencprop(node, "nvidia,usb2-companion", + &(port->companion), sizeof(port->companion)); + if (rv <= 0) { + device_printf(sc->dev, + "Missing 'nvidia,usb2-companion' property " + "for port: %s\n", name); + rv = ENXIO; + goto end; + } + } + + if (OF_hasprop(node, "vbus-supply")) { + rv = regulator_get_by_ofw_property(sc->dev, 0, + "vbus-supply", &port->supply_vbus); + if (rv <= 0) { + device_printf(sc->dev, + "Cannot get 'vbus-supply' regulator " + "for port: %s\n", name); + rv = ENXIO; + goto end; + } + } + + if (OF_hasprop(node, "nvidia,internal")) + port->internal = true; + /* Find assigned lane */ + if (port->lane == NULL) { + switch(port->type) { + /* Routing is fixed for USB2, ULPI AND HSIC. */ + case PADCTL_PORT_USB2: + port->lane = search_pad_lane(sc, PADCTL_PAD_USB2, + port->idx); + break; + case PADCTL_PORT_ULPI: + port->lane = search_pad_lane(sc, PADCTL_PAD_ULPI, + port->idx); + break; + case PADCTL_PORT_HSIC: + port->lane = search_pad_lane(sc, PADCTL_PAD_HSIC, + port->idx); + break; + case PADCTL_PORT_USB3: + port->lane = search_usb3_pad_lane(sc, port->idx); + break; + } + } + if (port->lane == NULL) { + device_printf(sc->dev, "Cannot find lane for port: %s\n", name); + rv = ENXIO; + goto end; + } + port->enabled = true; + rv = 0; +end: + if (name != NULL) + OF_prop_free(name); + return (rv); +} + +static int +parse_fdt(struct padctl_softc *sc, phandle_t base_node) +{ + phandle_t node; + int rv; + + rv = 0; + node = ofw_bus_find_child(base_node, "pads"); + + if (node <= 0) { + device_printf(sc->dev, "Cannot find pads subnode.\n"); + return (ENXIO); + } + for (node = OF_child(node); node != 0; node = OF_peer(node)) { + if (!fdt_is_enabled(node)) + continue; + rv = process_pad(sc, node); + if (rv != 0) + return (rv); + } + + node = ofw_bus_find_child(base_node, "ports"); + if (node <= 0) { + device_printf(sc->dev, "Cannot find ports subnode.\n"); + return (ENXIO); + } + for (node = OF_child(node); node != 0; node = OF_peer(node)) { + if (!fdt_is_enabled(node)) + continue; + rv = process_port(sc, node); + if (rv != 0) + return (rv); + } + + return (0); +} + +static void +load_calibration(struct padctl_softc *sc) +{ + uint32_t reg; + + /* All XUSB pad calibrations are packed into single dword.*/ + reg = tegra_fuse_read_4(FUSE_XUSB_CALIB); + sc->hs_curr_level_0 = FUSE_XUSB_CALIB_HS_CURR_LEVEL_0(reg); + sc->hs_curr_level_123 = FUSE_XUSB_CALIB_HS_CURR_LEVEL_123(reg); + sc->hs_iref_cap = FUSE_XUSB_CALIB_HS_IREF_CAP(reg); + sc->hs_squelch_level = FUSE_XUSB_CALIB_HS_SQUELCH_LEVEL(reg); + sc->hs_term_range_adj = FUSE_XUSB_CALIB_HS_TERM_RANGE_ADJ(reg); +} + +/* ------------------------------------------------------------------------- + * + * BUS functions + */ +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 padctl_softc * sc; + int i, rid, rv; + struct padctl_port *port; + phandle_t node; + + 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 == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + rv = hwreset_get_by_ofw_name(dev, 0, "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); + } + + load_calibration(sc); + + rv = parse_fdt(sc, node); + if (rv != 0) { + device_printf(dev, "Cannot parse fdt configuration: %d\n", rv); + return (rv); + } + for (i = 0; i < nitems(ports_tbl); i++) { + port = ports_tbl + i; + if (!port->enabled) + continue; + if (port->init == NULL) + continue; + rv = port->init(sc, port); + if (rv != 0) { + device_printf(dev, "Cannot init port '%s'\n", + port->name); + return (rv); + } + } + 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), + + DEVMETHOD_END +}; + +static devclass_t tegra_xusbpadctl_devclass; +static DEFINE_CLASS_0(xusbpadctl, tegra_xusbpadctl_driver, + tegra_xusbpadctl_methods, sizeof(struct padctl_softc)); +EARLY_DRIVER_MODULE(tegra_xusbpadctl, simplebus, tegra_xusbpadctl_driver, + tegra_xusbpadctl_devclass, NULL, NULL, 73); Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_abpmisc.c =================================================================== --- trunk/sys/arm/nvidia/tegra_abpmisc.c (rev 0) +++ trunk/sys/arm/nvidia/tegra_abpmisc.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,195 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_abpmisc.c 308335 2016-11-05 10:56:32Z mmel $"); + +/* + * SoC misc configuration and indentification driver. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#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 +}; + +static devclass_t tegra_abpmisc_devclass; +static DEFINE_CLASS_0(abpmisc, tegra_abpmisc_driver, tegra_abpmisc_methods, + sizeof(struct tegra_abpmisc_softc)); +EARLY_DRIVER_MODULE(tegra_abpmisc, simplebus, tegra_abpmisc_driver, + tegra_abpmisc_devclass, NULL, NULL, BUS_PASS_TIMER); Property changes on: trunk/sys/arm/nvidia/tegra_abpmisc.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_ahci.c =================================================================== --- trunk/sys/arm/nvidia/tegra_ahci.c (rev 0) +++ trunk/sys/arm/nvidia/tegra_ahci.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,626 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_ahci.c 332025 2018-04-04 13:23:06Z mmel $"); + +/* + * AHCI driver for Tegra SoCs. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "sata-0", &sc->phy); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'sata' phy\n"); + return (ENXIO); + } + + rv = clk_get_by_ofw_name(sc->dev, 0, "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, 0, "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, 0, "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, 0, "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->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)); +} + +static device_method_t tegra_ahci_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 DEFINE_CLASS_0(ahci, tegra_ahci_driver, tegra_ahci_methods, + sizeof(struct tegra_ahci_sc)); +DRIVER_MODULE(tegra_ahci, simplebus, tegra_ahci_driver, ahci_devclass, + NULL, NULL); Property changes on: trunk/sys/arm/nvidia/tegra_ahci.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_efuse.c =================================================================== --- trunk/sys/arm/nvidia/tegra_efuse.c (rev 0) +++ trunk/sys/arm/nvidia/tegra_efuse.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,368 @@ +/* $MidnightBSD$ */ +/*- + * 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_efuse.c 314506 2017-03-01 19:55:04Z ian $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + + +#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(void) +{ + 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, 0, "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, 0, "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 +}; + +static devclass_t tegra_efuse_devclass; +static DEFINE_CLASS_0(efuse, tegra_efuse_driver, tegra_efuse_methods, + sizeof(struct tegra_efuse_softc)); +EARLY_DRIVER_MODULE(tegra_efuse, simplebus, tegra_efuse_driver, + tegra_efuse_devclass, NULL, NULL, BUS_PASS_TIMER); Property changes on: trunk/sys/arm/nvidia/tegra_efuse.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_efuse.h =================================================================== --- trunk/sys/arm/nvidia/tegra_efuse.h (rev 0) +++ trunk/sys/arm/nvidia/tegra_efuse.h 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,62 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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. + * + * $FreeBSD: stable/11/sys/arm/nvidia/tegra_efuse.h 296936 2016-03-16 13:01:48Z mmel $ + */ + +#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_ */ Property changes on: trunk/sys/arm/nvidia/tegra_efuse.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_ehci.c =================================================================== --- trunk/sys/arm/nvidia/tegra_ehci.c (rev 0) +++ trunk/sys/arm/nvidia/tegra_ehci.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,319 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_ehci.c 332025 2018-04-04 13:23:06Z mmel $"); + +/* + * EHCI driver for Tegra SoCs. + */ +#include "opt_bus.h" +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, 0, "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, 0, "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, 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->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 | INTR_MPSAFE, + 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 devclass_t ehci_devclass; +static DEFINE_CLASS_0(ehci, ehci_driver, ehci_methods, + sizeof(struct tegra_ehci_softc)); +DRIVER_MODULE(tegra_ehci, simplebus, ehci_driver, ehci_devclass, NULL, NULL); +MODULE_DEPEND(tegra_ehci, usb, 1, 1, 1); Property changes on: trunk/sys/arm/nvidia/tegra_ehci.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_gpio.c =================================================================== --- trunk/sys/arm/nvidia/tegra_gpio.c (rev 0) +++ trunk/sys/arm/nvidia/tegra_gpio.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,893 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_gpio.c 308335 2016-11-05 10:56:32Z mmel $"); + +/* + * Tegra GPIO driver. + */ +#include "opt_platform.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "pic_if.h" + +#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->mtx) +#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) +#define GPIO_LOCK_INIT(_sc) mtx_init(&_sc->mtx, \ + device_get_nameunit(_sc->dev), "tegra_gpio", MTX_DEF) +#define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_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 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_LVL_DELTA (1 << 16) +#define GPIO_INT_LVL_EDGE (1 << 8) +#define GPIO_INT_LVL_HIGH (1 << 0) +#define GPIO_INT_LVL_MASK (GPIO_INT_LVL_DELTA | \ + GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH) +#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 6 */ + "CC", "DD", "EE" /* Bank 7 */ +}; + +struct tegra_gpio_irqsrc { + struct intr_irqsrc isrc; + u_int irq; + uint32_t cfgreg; +}; + +struct tegra_gpio_softc; +struct tegra_gpio_irq_cookie { + struct tegra_gpio_softc *sc; + int bank_num; +}; + +struct tegra_gpio_softc { + device_t dev; + device_t busdev; + struct mtx mtx; + struct resource *mem_res; + struct resource *irq_res[GPIO_NUM_BANKS]; + void *irq_ih[GPIO_NUM_BANKS]; + struct tegra_gpio_irq_cookie irq_cookies[GPIO_NUM_BANKS]; + int gpio_npins; + struct gpio_pin gpio_pins[NGPIO]; + struct tegra_gpio_irqsrc *isrcs; +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-gpio", 1}, + {NULL, 0} +}; + +/* -------------------------------------------------------------------------- + * + * GPIO + * + */ +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->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); +} + +/* -------------------------------------------------------------------------- + * + * Interrupts + * + */ +static inline void +intr_write_masked(struct tegra_gpio_softc *sc, bus_addr_t reg, + struct tegra_gpio_irqsrc *tgi, uint32_t val) +{ + uint32_t tmp; + int bit; + + bit = GPIO_BIT(tgi->irq); + tmp = 0x100 << bit; /* mask */ + tmp |= (val & 1) << bit; /* value */ + bus_write_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq), tmp); +} + +static inline void +intr_write_modify(struct tegra_gpio_softc *sc, bus_addr_t reg, + struct tegra_gpio_irqsrc *tgi, uint32_t val, uint32_t mask) +{ + uint32_t tmp; + int bit; + + bit = GPIO_BIT(tgi->irq); + GPIO_LOCK(sc); + tmp = bus_read_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq)); + tmp &= ~(mask << bit); + tmp |= val << bit; + bus_write_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq), tmp); + GPIO_UNLOCK(sc); +} + +static inline void +tegra_gpio_isrc_mask(struct tegra_gpio_softc *sc, + struct tegra_gpio_irqsrc *tgi, uint32_t val) +{ + + intr_write_masked(sc, GPIO_MSK_INT_ENB, tgi, val); +} + +static inline void +tegra_gpio_isrc_eoi(struct tegra_gpio_softc *sc, + struct tegra_gpio_irqsrc *tgi) +{ + + intr_write_masked(sc, GPIO_INT_CLR, tgi, 1); +} + +static inline bool +tegra_gpio_isrc_is_level(struct tegra_gpio_irqsrc *tgi) +{ + + return (tgi->cfgreg & GPIO_INT_LVL_EDGE); +} + +static int +tegra_gpio_intr(void *arg) +{ + u_int irq, i, j, val, basepin; + struct tegra_gpio_softc *sc; + struct trapframe *tf; + struct tegra_gpio_irqsrc *tgi; + struct tegra_gpio_irq_cookie *cookie; + + cookie = (struct tegra_gpio_irq_cookie *)arg; + sc = cookie->sc; + tf = curthread->td_intr_frame; + + for (i = 0; i < GPIO_REGS_IN_BANK; i++) { + basepin = cookie->bank_num * GPIO_REGS_IN_BANK * + GPIO_PINS_IN_REG + i * GPIO_PINS_IN_REG; + + val = bus_read_4(sc->mem_res, GPIO_INT_STA + + GPIO_REGNUM(basepin)); + val &= bus_read_4(sc->mem_res, GPIO_INT_ENB + + GPIO_REGNUM(basepin)); + /* Interrupt handling */ + for (j = 0; j < GPIO_PINS_IN_REG; j++) { + if ((val & (1 << j)) == 0) + continue; + irq = basepin + j; + tgi = &sc->isrcs[irq]; + if (!tegra_gpio_isrc_is_level(tgi)) + tegra_gpio_isrc_eoi(sc, tgi); + if (intr_isrc_dispatch(&tgi->isrc, tf) != 0) { + tegra_gpio_isrc_mask(sc, tgi, 0); + if (tegra_gpio_isrc_is_level(tgi)) + tegra_gpio_isrc_eoi(sc, tgi); + device_printf(sc->dev, + "Stray irq %u disabled\n", irq); + } + + } + } + + return (FILTER_HANDLED); +} + +static int +tegra_gpio_pic_attach(struct tegra_gpio_softc *sc) +{ + int error; + uint32_t irq; + const char *name; + + sc->isrcs = malloc(sizeof(*sc->isrcs) * sc->gpio_npins, M_DEVBUF, + M_WAITOK | M_ZERO); + + name = device_get_nameunit(sc->dev); + for (irq = 0; irq < sc->gpio_npins; irq++) { + sc->isrcs[irq].irq = irq; + sc->isrcs[irq].cfgreg = 0; + error = intr_isrc_register(&sc->isrcs[irq].isrc, + sc->dev, 0, "%s,%u", name, irq); + if (error != 0) + return (error); /* XXX deregister ISRCs */ + } + if (intr_pic_register(sc->dev, + OF_xref_from_node(ofw_bus_get_node(sc->dev))) == NULL) + return (ENXIO); + + return (0); +} + +static int +tegra_gpio_pic_detach(struct tegra_gpio_softc *sc) +{ + + /* + * There has not been established any procedure yet + * how to detach PIC from living system correctly. + */ + device_printf(sc->dev, "%s: not implemented yet\n", __func__); + return (EBUSY); +} + + +static void +tegra_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_gpio_softc *sc; + struct tegra_gpio_irqsrc *tgi; + + sc = device_get_softc(dev); + tgi = (struct tegra_gpio_irqsrc *)isrc; + tegra_gpio_isrc_mask(sc, tgi, 0); +} + +static void +tegra_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_gpio_softc *sc; + struct tegra_gpio_irqsrc *tgi; + + sc = device_get_softc(dev); + tgi = (struct tegra_gpio_irqsrc *)isrc; + tegra_gpio_isrc_mask(sc, tgi, 1); +} + +static int +tegra_gpio_pic_map_fdt(struct tegra_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->gpio_npins) + 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. + */ + if (cells[1] == 1) + reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH; + else if (cells[1] == 2) + reg = GPIO_INT_LVL_EDGE; + else if (cells[1] == 3) + reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_DELTA; + else if (cells[1] == 4) + reg = GPIO_INT_LVL_HIGH; + else if (cells[1] == 8) + reg = 0; + else + return (EINVAL); + + *irqp = cells[0]; + if (regp != NULL) + *regp = reg; + return (0); +} + + +static int +tegra_gpio_pic_map_gpio(struct tegra_gpio_softc *sc, u_int gpio_pin_num, + u_int gpio_pin_flags, u_int intr_mode, u_int *irqp, uint32_t *regp) +{ + + uint32_t reg; + + if (gpio_pin_num >= sc->gpio_npins) + return (EINVAL); + switch (intr_mode) { + case GPIO_INTR_CONFORM: + case GPIO_INTR_LEVEL_LOW: + reg = 0; + break; + case GPIO_INTR_LEVEL_HIGH: + reg = GPIO_INT_LVL_HIGH; + break; + case GPIO_INTR_EDGE_RISING: + reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH; + break; + case GPIO_INTR_EDGE_FALLING: + reg = GPIO_INT_LVL_EDGE; + break; + case GPIO_INTR_EDGE_BOTH: + reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_DELTA; + break; + default: + return (EINVAL); + } + *irqp = gpio_pin_num; + if (regp != NULL) + *regp = reg; + return (0); +} + +static int +tegra_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + int rv; + u_int irq; + struct tegra_gpio_softc *sc; + + sc = device_get_softc(dev); + + if (data->type == INTR_MAP_DATA_FDT) { + struct intr_map_data_fdt *daf; + + daf = (struct intr_map_data_fdt *)data; + rv = tegra_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq, + NULL); + } else if (data->type == INTR_MAP_DATA_GPIO) { + struct intr_map_data_gpio *dag; + + dag = (struct intr_map_data_gpio *)data; + rv = tegra_gpio_pic_map_gpio(sc, dag->gpio_pin_num, + dag->gpio_pin_flags, dag->gpio_intr_mode, &irq, NULL); + } else + return (ENOTSUP); + + if (rv == 0) + *isrcp = &sc->isrcs[irq].isrc; + return (rv); +} + +static void +tegra_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_gpio_softc *sc; + struct tegra_gpio_irqsrc *tgi; + + sc = device_get_softc(dev); + tgi = (struct tegra_gpio_irqsrc *)isrc; + if (tegra_gpio_isrc_is_level(tgi)) + tegra_gpio_isrc_eoi(sc, tgi); +} + +static void +tegra_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_gpio_softc *sc; + struct tegra_gpio_irqsrc *tgi; + + sc = device_get_softc(dev); + tgi = (struct tegra_gpio_irqsrc *)isrc; + tegra_gpio_isrc_mask(sc, tgi, 1); +} + +static void +tegra_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_gpio_softc *sc; + struct tegra_gpio_irqsrc *tgi; + + sc = device_get_softc(dev); + tgi = (struct tegra_gpio_irqsrc *)isrc; + + tegra_gpio_isrc_mask(sc, tgi, 0); + if (tegra_gpio_isrc_is_level(tgi)) + tegra_gpio_isrc_eoi(sc, tgi); +} + +static int +tegra_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; + int rv; + struct tegra_gpio_softc *sc; + struct tegra_gpio_irqsrc *tgi; + + sc = device_get_softc(dev); + tgi = (struct tegra_gpio_irqsrc *)isrc; + + if (data == NULL) + return (ENOTSUP); + + /* Get and check config for an interrupt. */ + if (data->type == INTR_MAP_DATA_FDT) { + struct intr_map_data_fdt *daf; + + daf = (struct intr_map_data_fdt *)data; + rv = tegra_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq, + &cfgreg); + } else if (data->type == INTR_MAP_DATA_GPIO) { + struct intr_map_data_gpio *dag; + + dag = (struct intr_map_data_gpio *)data; + rv = tegra_gpio_pic_map_gpio(sc, dag->gpio_pin_num, + dag->gpio_pin_flags, dag->gpio_intr_mode, &irq, &cfgreg); + } else + return (ENOTSUP); + if (rv != 0) + return (EINVAL); + + /* + * If this is a setup for another handler, + * only check that its configuration match. + */ + if (isrc->isrc_handlers != 0) + return (tgi->cfgreg == cfgreg ? 0 : EINVAL); + + tgi->cfgreg = cfgreg; + intr_write_modify(sc, GPIO_INT_LVL, tgi, cfgreg, GPIO_INT_LVL_MASK); + tegra_gpio_pic_enable_intr(dev, isrc); + + return (0); +} + +static int +tegra_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct tegra_gpio_softc *sc; + struct tegra_gpio_irqsrc *tgi; + + sc = device_get_softc(dev); + tgi = (struct tegra_gpio_irqsrc *)isrc; + + if (isrc->isrc_handlers == 0) + tegra_gpio_isrc_mask(sc, tgi, 0); + return (0); +} + +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); +} + +/* -------------------------------------------------------------------------- + * + * Bus + * + */ +static int +tegra_gpio_detach(device_t dev) +{ + struct tegra_gpio_softc *sc; + int i; + + sc = device_get_softc(dev); + + KASSERT(mtx_initialized(&sc->mtx), ("gpio mutex not initialized")); + + for (i = 0; i < GPIO_NUM_BANKS; i++) { + if (sc->irq_ih[i] != NULL) + bus_teardown_intr(dev, sc->irq_res[i], sc->irq_ih[i]); + } + + if (sc->isrcs != NULL) + tegra_gpio_pic_detach(sc); + + gpiobus_detach_bus(dev); + + for (i = 0; i < GPIO_NUM_BANKS; i++) { + if (sc->irq_res[i] != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, + sc->irq_res[i]); + } + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + GPIO_LOCK_DESTROY(sc); + + return(0); +} + +static int +tegra_gpio_attach(device_t dev) +{ + struct tegra_gpio_softc *sc; + int i, rid; + + sc = device_get_softc(dev); + sc->dev = dev; + GPIO_LOCK_INIT(sc); + + /* 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); + } + + sc->gpio_npins = NGPIO; + 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 | + GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH | + GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING | + GPIO_INTR_EDGE_BOTH; + 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; + } + + /* Init interrupt related registes. */ + for (i = 0; i < sc->gpio_npins; i += GPIO_PINS_IN_REG) { + bus_write_4(sc->mem_res, GPIO_INT_ENB + GPIO_REGNUM(i), 0); + bus_write_4(sc->mem_res, GPIO_INT_STA + GPIO_REGNUM(i), 0xFF); + bus_write_4(sc->mem_res, GPIO_INT_CLR + GPIO_REGNUM(i), 0xFF); + } + + /* Allocate interrupts. */ + for (i = 0; i < GPIO_NUM_BANKS; i++) { + sc->irq_cookies[i].sc = sc; + sc->irq_cookies[i].bank_num = i; + rid = i; + sc->irq_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &rid, RF_ACTIVE); + if (sc->irq_res[i] == NULL) { + device_printf(dev, "Cannot allocate IRQ resources\n"); + tegra_gpio_detach(dev); + return (ENXIO); + } + if ((bus_setup_intr(dev, sc->irq_res[i], + INTR_TYPE_MISC | INTR_MPSAFE, tegra_gpio_intr, NULL, + &sc->irq_cookies[i], &sc->irq_ih[i]))) { + device_printf(dev, + "WARNING: unable to register interrupt handler\n"); + tegra_gpio_detach(dev); + return (ENXIO); + } + } + + if (tegra_gpio_pic_attach(sc) != 0) { + device_printf(dev, "WARNING: unable to attach PIC\n"); + tegra_gpio_detach(dev); + return (ENXIO); + } + + sc->busdev = gpiobus_attach_bus(dev); + if (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), + + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, tegra_gpio_pic_disable_intr), + DEVMETHOD(pic_enable_intr, tegra_gpio_pic_enable_intr), + DEVMETHOD(pic_map_intr, tegra_gpio_pic_map_intr), + DEVMETHOD(pic_setup_intr, tegra_gpio_pic_setup_intr), + DEVMETHOD(pic_teardown_intr, tegra_gpio_pic_teardown_intr), + DEVMETHOD(pic_post_filter, tegra_gpio_pic_post_filter), + DEVMETHOD(pic_post_ithread, tegra_gpio_pic_post_ithread), + DEVMETHOD(pic_pre_ithread, tegra_gpio_pic_pre_ithread), + + /* 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 devclass_t tegra_gpio_devclass; +static DEFINE_CLASS_0(gpio, tegra_gpio_driver, tegra_gpio_methods, + sizeof(struct tegra_gpio_softc)); +EARLY_DRIVER_MODULE(tegra_gpio, simplebus, tegra_gpio_driver, + tegra_gpio_devclass, NULL, NULL, 70); Property changes on: trunk/sys/arm/nvidia/tegra_gpio.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_i2c.c =================================================================== --- trunk/sys/arm/nvidia/tegra_i2c.c (rev 0) +++ trunk/sys/arm/nvidia/tegra_i2c.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,805 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_i2c.c 308335 2016-11-05 10:56:32Z mmel $"); + +/* + * I2C driver for Tegra SoCs. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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, 0, "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, 0, "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 +}; + +static devclass_t tegra_i2c_devclass; +static DEFINE_CLASS_0(iichb, tegra_i2c_driver, tegra_i2c_methods, + sizeof(struct tegra_i2c_softc)); +EARLY_DRIVER_MODULE(tegra_iic, simplebus, tegra_i2c_driver, tegra_i2c_devclass, + NULL, NULL, 73); Property changes on: trunk/sys/arm/nvidia/tegra_i2c.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_lic.c =================================================================== --- trunk/sys/arm/nvidia/tegra_lic.c (rev 0) +++ trunk/sys/arm/nvidia/tegra_lic.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,290 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_lic.c 308335 2016-11-05 10:56:32Z mmel $"); + +/* + * Local interrupt controller driver for Tegra SoCs. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#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_activate_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_ACTIVATE_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_deactivate_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_DEACTIVATE_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)) == NULL) { + 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_activate_intr, tegra_lic_activate_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_deactivate_intr, tegra_lic_deactivate_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; +static DEFINE_CLASS_0(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); Property changes on: trunk/sys/arm/nvidia/tegra_lic.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_mc.c =================================================================== --- trunk/sys/arm/nvidia/tegra_mc.c (rev 0) +++ trunk/sys/arm/nvidia/tegra_mc.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,312 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_mc.c 317012 2017-04-16 08:18:37Z mmel $"); + +/* + * Memory controller driver for Tegra SoCs. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "clock_if.h" + +#define MC_INTSTATUS 0x000 +#define MC_INTMASK 0x004 +#define MC_INT_DECERR_MTS (1 << 16) +#define MC_INT_SECERR_SEC (1 << 13) +#define MC_INT_DECERR_VPR (1 << 12) +#define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11) +#define MC_INT_INVALID_SMMU_PAGE (1 << 10) +#define MC_INT_ARBITRATION_EMEM (1 << 9) +#define MC_INT_SECURITY_VIOLATION (1 << 8) +#define MC_INT_DECERR_EMEM (1 << 6) +#define MC_INT_INT_MASK (MC_INT_DECERR_MTS | \ + MC_INT_SECERR_SEC | \ + MC_INT_DECERR_VPR | \ + MC_INT_INVALID_APB_ASID_UPDATE | \ + MC_INT_INVALID_SMMU_PAGE | \ + MC_INT_ARBITRATION_EMEM | \ + MC_INT_SECURITY_VIOLATION | \ + MC_INT_DECERR_EMEM) + +#define MC_ERR_STATUS 0x008 +#define MC_ERR_TYPE(x) (((x) >> 28) & 0x7) +#define MC_ERR_TYPE_DECERR_EMEM 2 +#define MC_ERR_TYPE_SECURITY_TRUSTZONE 3 +#define MC_ERR_TYPE_SECURITY_CARVEOUT 4 +#define MC_ERR_TYPE_INVALID_SMMU_PAGE 6 +#define MC_ERR_INVALID_SMMU_PAGE_READABLE (1 << 27) +#define MC_ERR_INVALID_SMMU_PAGE_WRITABLE (1 << 26) +#define MC_ERR_INVALID_SMMU_PAGE_NONSECURE (1 << 25) +#define MC_ERR_ADR_HI(x) (((x) >> 20) & 0x3) +#define MC_ERR_SWAP (1 << 18) +#define MC_ERR_SECURITY (1 << 17) +#define MC_ERR_RW (1 << 16) +#define MC_ERR_ADR1(x) (((x) >> 12) & 0x7) +#define MC_ERR_ID(x) (((x) >> 0) & 07F) + +#define MC_ERR_ADDR 0x00C +#define MC_EMEM_CFG 0x050 +#define MC_EMEM_ADR_CFG 0x054 +#define MC_EMEM_NUMDEV(x) (((x) >> 0 ) & 0x1) + +#define MC_EMEM_ADR_CFG_DEV0 0x058 +#define MC_EMEM_ADR_CFG_DEV1 0x05C +#define EMEM_DEV_DEVSIZE(x) (((x) >> 16) & 0xF) +#define EMEM_DEV_BANKWIDTH(x) (((x) >> 8) & 0x3) +#define EMEM_DEV_COLWIDTH(x) (((x) >> 8) & 0x3) + +#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, "tegra_mc", timeout); +#define LOCK_INIT(_sc) \ + mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_mc", 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-mc", 1}, + {NULL, 0} +}; + +struct tegra_mc_softc { + device_t dev; + struct mtx mtx; + + struct resource *mem_res; + struct resource *irq_res; + void *irq_h; + + clk_t clk; +}; + +static char *smmu_err_tbl[16] = { + "reserved", /* 0 */ + "reserved", /* 1 */ + "DRAM decode", /* 2 */ + "Trustzome Security", /* 3 */ + "Security carveout", /* 4 */ + "reserved", /* 5 */ + "Invalid SMMU page", /* 6 */ + "reserved", /* 7 */ +}; + +static void +tegra_mc_intr(void *arg) +{ + struct tegra_mc_softc *sc; + uint32_t stat, err; + uint64_t addr; + + sc = (struct tegra_mc_softc *)arg; + + stat = RD4(sc, MC_INTSTATUS); + if ((stat & MC_INT_INT_MASK) == 0) { + WR4(sc, MC_INTSTATUS, stat); + return; + } + + device_printf(sc->dev, "Memory Controller Interrupt:\n"); + if (stat & MC_INT_DECERR_MTS) + printf(" - MTS carveout violation\n"); + if (stat & MC_INT_SECERR_SEC) + printf(" - SEC carveout violation\n"); + if (stat & MC_INT_DECERR_VPR) + printf(" - VPR requirements violated\n"); + if (stat & MC_INT_INVALID_APB_ASID_UPDATE) + printf(" - ivalid APB ASID update\n"); + if (stat & MC_INT_INVALID_SMMU_PAGE) + printf(" - SMMU address translation error\n"); + if (stat & MC_INT_ARBITRATION_EMEM) + printf(" - arbitration deadlock-prevention threshold hit\n"); + if (stat & MC_INT_SECURITY_VIOLATION) + printf(" - SMMU address translation security error\n"); + if (stat & MC_INT_DECERR_EMEM) + printf(" - SMMU address decode error\n"); + + if ((stat & (MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION | + MC_INT_DECERR_EMEM)) != 0) { + err = RD4(sc, MC_ERR_STATUS); + addr = RD4(sc, MC_ERR_STATUS); + addr |= (uint64_t)(MC_ERR_ADR_HI(err)) << 32; + printf(" at 0x%012llX [%s %s %s] - %s error.\n", + addr, + stat & MC_ERR_SWAP ? "Swap, " : "", + stat & MC_ERR_SECURITY ? "Sec, " : "", + stat & MC_ERR_RW ? "Write" : "Read", + smmu_err_tbl[MC_ERR_TYPE(err)]); + } + WR4(sc, MC_INTSTATUS, stat); +} + +static void +tegra_mc_init_hw(struct tegra_mc_softc *sc) +{ + + /* Disable and acknowledge all interrupts */ + WR4(sc, MC_INTMASK, 0); + WR4(sc, MC_INTSTATUS, MC_INT_INT_MASK); +} + +static int +tegra_mc_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 Memory Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +tegra_mc_attach(device_t dev) +{ + int rv, rid; + struct tegra_mc_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_name(dev, 0, "mc", &sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot get mc 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. */ + tegra_mc_init_hw(sc); + + /* Setup interrupt */ + rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, tegra_mc_intr, sc, &sc->irq_h); + if (rv) { + device_printf(dev, "Cannot setup interrupt.\n"); + goto fail; + } + + /* Enable Interrupts */ + WR4(sc, MC_INTMASK, MC_INT_INT_MASK); + + 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_mc_detach(device_t dev) +{ + struct tegra_mc_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_mc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_mc_probe), + DEVMETHOD(device_attach, tegra_mc_attach), + DEVMETHOD(device_detach, tegra_mc_detach), + + + DEVMETHOD_END +}; + +static devclass_t tegra_mc_devclass; +static DEFINE_CLASS_0(mc, tegra_mc_driver, tegra_mc_methods, + sizeof(struct tegra_mc_softc)); +DRIVER_MODULE(tegra_mc, simplebus, tegra_mc_driver, tegra_mc_devclass, + NULL, NULL); Property changes on: trunk/sys/arm/nvidia/tegra_mc.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_pcie.c =================================================================== --- trunk/sys/arm/nvidia/tegra_pcie.c (rev 0) +++ trunk/sys/arm/nvidia/tegra_pcie.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,1637 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_pcie.c 332025 2018-04-04 13:23:06Z mmel $"); + +/* + * Nvidia Integrated PCI/PCI-Express controller driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "ofw_bus_if.h" +#include "msi_if.h" +#include "pcib_if.h" +#include "pic_if.h" + + +#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_MSI_VEC(x) (0x06c + 4 * (x)) +#define AFI_MSI_EN_VEC(x) (0x08c + 4 * (x)) +#define AFI_MSI_INTR_IN_REG 32 +#define AFI_MSI_REGS 8 + +#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 + +/* Wait 50 ms (per port) for link. */ +#define TEGRA_PCIE_LINKUP_TIMEOUT 50000 + +#define TEGRA_PCIB_MSI_ENABLE + +#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}, +}; + +#define TEGRA_FLAG_MSI_USED 0x0001 +struct tegra_pcib_irqsrc { + struct intr_irqsrc isrc; + u_int irq; + u_int flags; +}; + +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 */ + phy_t phy; /* port phy */ + + /* 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 +#define TEGRA_PCIB_MAX_MSI AFI_MSI_INTR_IN_REG * AFI_MSI_REGS +struct tegra_pcib_softc { + struct ofw_pci_softc ofw_pci; + device_t dev; + struct mtx mtx; + 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 ofw_pci_range mem_range; + struct ofw_pci_range pref_mem_range; + struct ofw_pci_range io_range; + + 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; + + vm_offset_t msi_page; /* VA of MSI page */ + 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]; + struct tegra_pcib_irqsrc *isrcs; +}; + +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; + u_int irq; + + sc = device_get_softc(bus); + irq = intr_map_clone_irq(rman_get_start(sc->irq_res)); + device_printf(bus, "route pin %d for device %d.%d to %u\n", + pin, pci_get_slot(dev), pci_get_function(dev), + irq); + + return (irq); +} + +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); +} + +/* ----------------------------------------------------------------------- + * + * PCI MSI interface + */ +static int +tegra_pcib_alloc_msi(device_t pci, device_t child, int count, int maxcount, + int *irqs) +{ + phandle_t msi_parent; + + /* XXXX ofw_bus_msimap() don't works for Tegra DT. + ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, + NULL); + */ + msi_parent = OF_xref_from_node(ofw_bus_get_node(pci)); + return (intr_alloc_msi(pci, child, msi_parent, count, maxcount, + irqs)); +} + +static int +tegra_pcib_release_msi(device_t pci, device_t child, int count, int *irqs) +{ + phandle_t msi_parent; + + /* XXXX ofw_bus_msimap() don't works for Tegra DT. + ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, + NULL); + */ + msi_parent = OF_xref_from_node(ofw_bus_get_node(pci)); + return (intr_release_msi(pci, child, msi_parent, count, irqs)); +} + +static int +tegra_pcib_map_msi(device_t pci, device_t child, int irq, uint64_t *addr, + uint32_t *data) +{ + phandle_t msi_parent; + + /* XXXX ofw_bus_msimap() don't works for Tegra DT. + ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, + NULL); + */ + msi_parent = OF_xref_from_node(ofw_bus_get_node(pci)); + return (intr_map_msi(pci, child, msi_parent, irq, addr, data)); +} + +#ifdef TEGRA_PCIB_MSI_ENABLE + +/* -------------------------------------------------------------------------- + * + * Interrupts + * + */ + +static inline void +tegra_pcib_isrc_mask(struct tegra_pcib_softc *sc, + struct tegra_pcib_irqsrc *tgi, uint32_t val) +{ + uint32_t reg; + int offs, bit; + + offs = tgi->irq / AFI_MSI_INTR_IN_REG; + bit = 1 << (tgi->irq % AFI_MSI_INTR_IN_REG); + + if (val != 0) + AFI_WR4(sc, AFI_MSI_VEC(offs), bit); + reg = AFI_RD4(sc, AFI_MSI_EN_VEC(offs)); + if (val != 0) + reg |= bit; + else + reg &= ~bit; + AFI_WR4(sc, AFI_MSI_EN_VEC(offs), reg); +} + +static int +tegra_pcib_msi_intr(void *arg) +{ + u_int irq, i, bit, reg; + struct tegra_pcib_softc *sc; + struct trapframe *tf; + struct tegra_pcib_irqsrc *tgi; + + sc = (struct tegra_pcib_softc *)arg; + tf = curthread->td_intr_frame; + + for (i = 0; i < AFI_MSI_REGS; i++) { + reg = AFI_RD4(sc, AFI_MSI_VEC(i)); + /* Handle one vector. */ + while (reg != 0) { + bit = ffs(reg) - 1; + /* Send EOI */ + AFI_WR4(sc, AFI_MSI_VEC(i), 1 << bit); + irq = i * AFI_MSI_INTR_IN_REG + bit; + tgi = &sc->isrcs[irq]; + if (intr_isrc_dispatch(&tgi->isrc, tf) != 0) { + /* Disable stray. */ + tegra_pcib_isrc_mask(sc, tgi, 0); + device_printf(sc->dev, + "Stray irq %u disabled\n", irq); + } + reg = AFI_RD4(sc, AFI_MSI_VEC(i)); + } + } + return (FILTER_HANDLED); +} + +static int +tegra_pcib_msi_attach(struct tegra_pcib_softc *sc) +{ + int error; + uint32_t irq; + const char *name; + + sc->isrcs = malloc(sizeof(*sc->isrcs) * TEGRA_PCIB_MAX_MSI, M_DEVBUF, + M_WAITOK | M_ZERO); + + name = device_get_nameunit(sc->dev); + for (irq = 0; irq < TEGRA_PCIB_MAX_MSI; irq++) { + sc->isrcs[irq].irq = irq; + error = intr_isrc_register(&sc->isrcs[irq].isrc, + sc->dev, 0, "%s,%u", name, irq); + if (error != 0) + return (error); /* XXX deregister ISRCs */ + } + if (intr_msi_register(sc->dev, + OF_xref_from_node(ofw_bus_get_node(sc->dev))) != 0) + return (ENXIO); + + return (0); +} + +static int +tegra_pcib_msi_detach(struct tegra_pcib_softc *sc) +{ + + /* + * There has not been established any procedure yet + * how to detach PIC from living system correctly. + */ + device_printf(sc->dev, "%s: not implemented yet\n", __func__); + return (EBUSY); +} + + +static void +tegra_pcib_msi_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_pcib_softc *sc; + struct tegra_pcib_irqsrc *tgi; + + sc = device_get_softc(dev); + tgi = (struct tegra_pcib_irqsrc *)isrc; + tegra_pcib_isrc_mask(sc, tgi, 0); +} + +static void +tegra_pcib_msi_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_pcib_softc *sc; + struct tegra_pcib_irqsrc *tgi; + + sc = device_get_softc(dev); + tgi = (struct tegra_pcib_irqsrc *)isrc; + tegra_pcib_isrc_mask(sc, tgi, 1); +} + +/* MSI interrupts are edge trigered -> do nothing */ +static void +tegra_pcib_msi_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ +} + +static void +tegra_pcib_msi_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ +} + +static void +tegra_pcib_msi_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ +} + +static int +tegra_pcib_msi_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct tegra_pcib_softc *sc; + struct tegra_pcib_irqsrc *tgi; + + sc = device_get_softc(dev); + tgi = (struct tegra_pcib_irqsrc *)isrc; + + if (data == NULL || data->type != INTR_MAP_DATA_MSI) + return (ENOTSUP); + + if (isrc->isrc_handlers == 0) + tegra_pcib_msi_enable_intr(dev, isrc); + + return (0); +} + +static int +tegra_pcib_msi_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct tegra_pcib_softc *sc; + struct tegra_pcib_irqsrc *tgi; + + sc = device_get_softc(dev); + tgi = (struct tegra_pcib_irqsrc *)isrc; + + if (isrc->isrc_handlers == 0) + tegra_pcib_isrc_mask(sc, tgi, 0); + return (0); +} + + +static int +tegra_pcib_msi_alloc_msi(device_t dev, device_t child, int count, int maxcount, + device_t *pic, struct intr_irqsrc **srcs) +{ + struct tegra_pcib_softc *sc; + int i, irq, end_irq; + bool found; + + KASSERT(powerof2(count), ("%s: bad count", __func__)); + KASSERT(powerof2(maxcount), ("%s: bad maxcount", __func__)); + + sc = device_get_softc(dev); + mtx_lock(&sc->mtx); + + found = false; + for (irq = 0; (irq + count - 1) < TEGRA_PCIB_MAX_MSI; irq++) { + /* Start on an aligned interrupt */ + if ((irq & (maxcount - 1)) != 0) + continue; + + /* Assume we found a valid range until shown otherwise */ + found = true; + + /* Check this range is valid */ + for (end_irq = irq; end_irq < irq + count; end_irq++) { + /* This is already used */ + if ((sc->isrcs[end_irq].flags & TEGRA_FLAG_MSI_USED) == + TEGRA_FLAG_MSI_USED) { + found = false; + break; + } + } + + if (found) + break; + } + + /* Not enough interrupts were found */ + if (!found || irq == (TEGRA_PCIB_MAX_MSI - 1)) { + mtx_unlock(&sc->mtx); + return (ENXIO); + } + + for (i = 0; i < count; i++) { + /* Mark the interrupt as used */ + sc->isrcs[irq + i].flags |= TEGRA_FLAG_MSI_USED; + + } + mtx_unlock(&sc->mtx); + + for (i = 0; i < count; i++) + srcs[i] = (struct intr_irqsrc *)&sc->isrcs[irq + i]; + *pic = device_get_parent(dev); + return (0); +} + +static int +tegra_pcib_msi_release_msi(device_t dev, device_t child, int count, + struct intr_irqsrc **isrc) +{ + struct tegra_pcib_softc *sc; + struct tegra_pcib_irqsrc *ti; + int i; + + sc = device_get_softc(dev); + mtx_lock(&sc->mtx); + for (i = 0; i < count; i++) { + ti = (struct tegra_pcib_irqsrc *)isrc[i]; + + KASSERT((ti->flags & TEGRA_FLAG_MSI_USED) == TEGRA_FLAG_MSI_USED, + ("%s: Trying to release an unused MSI-X interrupt", + __func__)); + + ti->flags &= ~TEGRA_FLAG_MSI_USED; + } + mtx_unlock(&sc->mtx); + return (0); +} + +static int +tegra_pcib_msi_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc, + uint64_t *addr, uint32_t *data) +{ + struct tegra_pcib_softc *sc = device_get_softc(dev); + struct tegra_pcib_irqsrc *ti = (struct tegra_pcib_irqsrc *)isrc; + + *addr = vtophys(sc->msi_page); + *data = ti->irq; + 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); + + /* Phy. */ + rv = phy_get_by_ofw_name(sc->dev, node, "pcie-0", &port->phy); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'pcie-0' phy for port %d\n", + port->port_idx); + goto fail; + } + + 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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "cml", &sc->clk_cml); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'cml' clock\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 ofw_pci_range *ranges, int nranges) +{ + int i; + + for (i = 2; i < nranges; i++) { + if ((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) == + 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].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) == + OFW_PCI_PHYS_HI_SPACE_MEM32)) { + if (ranges[i].pci_hi & OFW_PCI_PHYS_HI_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]; + } else { + 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 ((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; + DELAY(1); + + } + 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; + + DELAY(1); + } + 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) +{ + 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); + + for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { + if (sc->ports[i] != NULL) { + rv = phy_enable(sc->ports[i]->phy); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable phy for port %d\n", + sc->ports[i]->port_idx); + 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, 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, + sc->pref_mem_range.host, sc->pref_mem_range.size, 1); + + /* BAR 3 - downstream not prefetchable memory 1:1 .*/ + tegra_pcib_set_bar(sc, 3, sc->mem_range.host, + sc->mem_range.host, 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); +} + +#ifdef TEGRA_PCIB_MSI_ENABLE +static int +tegra_pcib_attach_msi(device_t dev) +{ + struct tegra_pcib_softc *sc; + uint32_t reg; + int i, rv; + + sc = device_get_softc(dev); + + sc->msi_page = kmem_alloc_contig(kernel_arena, PAGE_SIZE, M_WAITOK, + 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0, VM_MEMATTR_DEFAULT); + + /* MSI BAR */ + tegra_pcib_set_bar(sc, 9, vtophys(sc->msi_page), vtophys(sc->msi_page), + PAGE_SIZE, 0); + + /* Disble and clear all interrupts. */ + for (i = 0; i < AFI_MSI_REGS; i++) { + AFI_WR4(sc, AFI_MSI_EN_VEC(i), 0); + AFI_WR4(sc, AFI_MSI_VEC(i), 0xFFFFFFFF); + } + rv = bus_setup_intr(dev, sc->msi_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + tegra_pcib_msi_intr, NULL, sc, &sc->msi_intr_cookie); + if (rv != 0) { + device_printf(dev, "cannot setup MSI interrupt handler\n"); + rv = ENXIO; + goto out; + } + + if (tegra_pcib_msi_attach(sc) != 0) { + device_printf(dev, "WARNING: unable to attach PIC\n"); + tegra_pcib_msi_detach(sc); + goto out; + } + + /* Unmask MSI interrupt. */ + reg = AFI_RD4(sc, AFI_INTR_MASK); + reg |= AFI_INTR_MASK_MSI_MASK; + AFI_WR4(sc, AFI_INTR_MASK, reg); + +out: + return (rv); +} +#endif + +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; + int rv; + int rid; + struct tegra_pcib_port *port; + int i; + + sc = device_get_softc(dev); + sc->dev = 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); + } + + /* 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 + */ + 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; + } + + sc->ofw_pci.sc_range_mask = 0x3; + rv = ofw_pci_init(dev); + if (rv != 0) + goto out; + + rv = tegra_pcib_decode_ranges(sc, sc->ofw_pci.sc_range, + sc->ofw_pci.sc_nrange); + if (rv != 0) + 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; + } + + /* + * Enable PCIE device. + */ + rv = tegra_pcib_enable(sc); + 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); + } + +#ifdef TEGRA_PCIB_MSI_ENABLE + rv = tegra_pcib_attach_msi(dev); + if (rv != 0) + goto out; +#endif + device_add_child(dev, "pci", -1); + + return (bus_generic_attach(dev)); + +out: + + 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_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), + DEVMETHOD(pcib_alloc_msi, tegra_pcib_alloc_msi), + DEVMETHOD(pcib_release_msi, tegra_pcib_release_msi), + DEVMETHOD(pcib_map_msi, tegra_pcib_map_msi), + +#ifdef TEGRA_PCIB_MSI_ENABLE + /* MSI/MSI-X */ + DEVMETHOD(msi_alloc_msi, tegra_pcib_msi_alloc_msi), + DEVMETHOD(msi_release_msi, tegra_pcib_msi_release_msi), + DEVMETHOD(msi_map_msi, tegra_pcib_msi_map_msi), + + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, tegra_pcib_msi_disable_intr), + DEVMETHOD(pic_enable_intr, tegra_pcib_msi_enable_intr), + DEVMETHOD(pic_setup_intr, tegra_pcib_msi_setup_intr), + DEVMETHOD(pic_teardown_intr, tegra_pcib_msi_teardown_intr), + DEVMETHOD(pic_post_filter, tegra_pcib_msi_post_filter), + DEVMETHOD(pic_post_ithread, tegra_pcib_msi_post_ithread), + DEVMETHOD(pic_pre_ithread, tegra_pcib_msi_pre_ithread), +#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 devclass_t pcib_devclass; +DEFINE_CLASS_1(pcib, tegra_pcib_driver, tegra_pcib_methods, + sizeof(struct tegra_pcib_softc), ofw_pci_driver); +DRIVER_MODULE(pcib, simplebus, tegra_pcib_driver, pcib_devclass, + NULL, NULL); Property changes on: trunk/sys/arm/nvidia/tegra_pcie.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_pinmux.c =================================================================== --- trunk/sys/arm/nvidia/tegra_pinmux.c (rev 0) +++ trunk/sys/arm/nvidia/tegra_pinmux.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,800 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_pinmux.c 308335 2016-11-05 10:56:32Z mmel $"); + +/* + * Pin multiplexer driver for Tegra SoCs. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +/* 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) + OF_prop_free(pins); + if (cfg.function != NULL) + OF_prop_free(cfg.function); + 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 devclass_t tegra_pinmux_devclass; +static DEFINE_CLASS_0(pinmux, tegra_pinmux_driver, tegra_pinmux_methods, + sizeof(struct pinmux_softc)); +EARLY_DRIVER_MODULE(tegra_pinmux, simplebus, tegra_pinmux_driver, + tegra_pinmux_devclass, NULL, NULL, 71); Property changes on: trunk/sys/arm/nvidia/tegra_pinmux.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_pmc.h =================================================================== --- trunk/sys/arm/nvidia/tegra_pmc.h (rev 0) +++ trunk/sys/arm/nvidia/tegra_pmc.h 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,116 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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. + * + * $FreeBSD: stable/11/sys/arm/nvidia/tegra_pmc.h 296936 2016-03-16 13:01:48Z mmel $ + */ + +#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 Property changes on: trunk/sys/arm/nvidia/tegra_pmc.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_rtc.c =================================================================== --- trunk/sys/arm/nvidia/tegra_rtc.c (rev 0) +++ trunk/sys/arm/nvidia/tegra_rtc.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,305 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_rtc.c 308335 2016-11-05 10:56:32Z mmel $"); + +/* + * RTC driver for Tegra SoCs. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#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, 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 +}; + +static devclass_t tegra_rtc_devclass; +static DEFINE_CLASS_0(rtc, tegra_rtc_driver, tegra_rtc_methods, + sizeof(struct tegra_rtc_softc)); +DRIVER_MODULE(tegra_rtc, simplebus, tegra_rtc_driver, tegra_rtc_devclass, + NULL, NULL); Property changes on: trunk/sys/arm/nvidia/tegra_rtc.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_sdhci.c =================================================================== --- trunk/sys/arm/nvidia/tegra_sdhci.c (rev 0) +++ trunk/sys/arm/nvidia/tegra_sdhci.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,468 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_sdhci.c 343504 2019-01-27 19:04:28Z marius $"); + +/* + * SDHCI driver glue for NVIDIA Tegra family + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "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_power; + struct sdhci_fdt_gpio *gpio; + + 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_sdhci_get_ro(device_t brdev, device_t reqdev) +{ + struct tegra_sdhci_softc *sc = device_get_softc(brdev); + + return (sdhci_fdt_gpio_get_readonly(sc->gpio)); +} + +static bool +tegra_sdhci_get_card_present(device_t dev, struct sdhci_slot *slot) +{ + struct tegra_sdhci_softc *sc = device_get_softc(dev); + + return (sdhci_fdt_gpio_get_present(sc->gpio)); +} + +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, 0, "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, node, "power-gpios", &sc->gpio_power); + + rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk); + if (rv != 0) { + + device_printf(dev, "Cannot get clock\n"); + goto fail; + } + + rv = clk_get_by_ofw_index(dev, 0, 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; + } + + sc->gpio = sdhci_fdt_gpio_setup(sc->dev, &sc->slot); + + bus_generic_probe(dev); + bus_generic_attach(dev); + + sdhci_start_slot(&sc->slot); + + return (0); + +fail: + if (sc->gpio != NULL) + sdhci_fdt_gpio_teardown(sc->gpio); + if (sc->intr_cookie != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie); + 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->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); + sdhci_fdt_gpio_teardown(sc->gpio); + 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_sdhci_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), + DEVMETHOD(sdhci_get_card_present, tegra_sdhci_get_card_present), + + DEVMETHOD_END +}; + +static devclass_t tegra_sdhci_devclass; +static DEFINE_CLASS_0(sdhci, tegra_sdhci_driver, tegra_sdhci_methods, + sizeof(struct tegra_sdhci_softc)); +DRIVER_MODULE(sdhci_tegra, simplebus, tegra_sdhci_driver, tegra_sdhci_devclass, + NULL, NULL); +SDHCI_DEPEND(sdhci_tegra); +MMC_DECLARE_BRIDGE(sdhci); Property changes on: trunk/sys/arm/nvidia/tegra_sdhci.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_soctherm.c =================================================================== --- trunk/sys/arm/nvidia/tegra_soctherm.c (rev 0) +++ trunk/sys/arm/nvidia/tegra_soctherm.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,697 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_soctherm.c 308335 2016-11-05 10:56:32Z mmel $"); + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#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, 0, "soctherm", &sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot get fuse reset\n"); + goto fail; + } + rv = clk_get_by_ofw_name(dev, 0, "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, 0, "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; +static DEFINE_CLASS_0(soctherm, tegra_soctherm_driver, tegra_soctherm_methods, + sizeof(struct soctherm_softc)); +EARLY_DRIVER_MODULE(tegra_soctherm, simplebus, tegra_soctherm_driver, + tegra_soctherm_devclass, NULL, NULL, 79); Property changes on: trunk/sys/arm/nvidia/tegra_soctherm.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_soctherm_if.m =================================================================== --- trunk/sys/arm/nvidia/tegra_soctherm_if.m (rev 0) +++ trunk/sys/arm/nvidia/tegra_soctherm_if.m 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,43 @@ +/* $MidnightBSD$ */ +#- +# Copyright (c) 2016 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. +# +# $FreeBSD: stable/11/sys/arm/nvidia/tegra_soctherm_if.m 296936 2016-03-16 13:01:48Z mmel $ +# + +#include + +INTERFACE tegra_soctherm; + + +/** + * Read temperature + */ +METHOD int get_temperature{ + device_t dev; + device_t consumer; + uintptr_t id; + int *val; +}; Property changes on: trunk/sys/arm/nvidia/tegra_soctherm_if.m ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_uart.c =================================================================== --- trunk/sys/arm/nvidia/tegra_uart.c (rev 0) +++ trunk/sys/arm/nvidia/tegra_uart.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,252 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_uart.c 340145 2018-11-04 23:28:56Z mmacy $"); + + +/* + * UART driver for Tegra SoCs. + */ +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, 0, "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, 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, 0, (int)freq, 0, 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); Property changes on: trunk/sys/arm/nvidia/tegra_uart.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_usbphy.c =================================================================== --- trunk/sys/arm/nvidia/tegra_usbphy.c (rev 0) +++ trunk/sys/arm/nvidia/tegra_usbphy.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,859 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_usbphy.c 332025 2018-04-04 13:23:06Z mmel $"); + + +/* + * USB phy driver for Tegra SoCs. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phynode_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}, +}; + + /* Phy controller class and methods. */ +static int usbphy_phy_enable(struct phynode *phy, bool enable); +static phynode_method_t usbphy_phynode_methods[] = { + PHYNODEMETHOD(phynode_enable, usbphy_phy_enable), + + PHYNODEMETHOD_END +}; +DEFINE_CLASS_1(usbphy_phynode, usbphy_phynode_class, usbphy_phynode_methods, + 0, phynode_class); + +#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(struct phynode *phy, bool enable) +{ + device_t dev; + struct usbphy_softc *sc; + int rv = 0; + + dev = phynode_get_device(phy); + 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); + OF_prop_free(tmpstr); + 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); + OF_prop_free(tmpstr); + 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; + struct phynode *phynode; + struct phynode_init_def phy_init; + + 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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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, 0, "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); + } + } + + /* Create and register phy. */ + bzero(&phy_init, sizeof(phy_init)); + phy_init.id = 1; + phy_init.ofw_node = node; + phynode = phynode_create(dev, &usbphy_phynode_class, &phy_init); + if (phynode == NULL) { + device_printf(sc->dev, "Cannot create phy\n"); + return (ENXIO); + } + if (phynode_register(phynode) == NULL) { + device_printf(sc->dev, "Cannot create phy\n"); + return (ENXIO); + } + + 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), + + DEVMETHOD_END +}; + +static devclass_t tegra_usbphy_devclass; +static DEFINE_CLASS_0(usbphy, tegra_usbphy_driver, tegra_usbphy_methods, + sizeof(struct usbphy_softc)); +EARLY_DRIVER_MODULE(tegra_usbphy, simplebus, tegra_usbphy_driver, + tegra_usbphy_devclass, NULL, NULL, 79); Property changes on: trunk/sys/arm/nvidia/tegra_usbphy.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/nvidia/tegra_xhci.c =================================================================== --- trunk/sys/arm/nvidia/tegra_xhci.c (rev 0) +++ trunk/sys/arm/nvidia/tegra_xhci.c 2020-03-06 17:08:20 UTC (rev 12396) @@ -0,0 +1,1161 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_xhci.c 332025 2018-04-04 13:23:06Z mmel $"); + +/* + * XHCI driver for Tegra SoCs. + */ +#include "opt_bus.h" +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "usbdevs.h" + +/* FPCI address space */ +#define T_XUSB_CFG_0 0x000 +#define T_XUSB_CFG_1 0x004 +#define CFG_1_BUS_MASTER (1 << 2) +#define CFG_1_MEMORY_SPACE (1 << 1) +#define CFG_1_IO_SPACE (1 << 0) + +#define T_XUSB_CFG_2 0x008 +#define T_XUSB_CFG_3 0x00C +#define T_XUSB_CFG_4 0x010 +#define CFG_4_BASE_ADDRESS(x) (((x) & 0x1FFFF) << 15) + +#define T_XUSB_CFG_5 0x014 +#define T_XUSB_CFG_ARU_MAILBOX_CMD 0x0E4 +#define ARU_MAILBOX_CMD_INT_EN (1U << 31) +#define ARU_MAILBOX_CMD_DEST_XHCI (1 << 30) +#define ARU_MAILBOX_CMD_DEST_SMI (1 << 29) +#define ARU_MAILBOX_CMD_DEST_PME (1 << 28) +#define ARU_MAILBOX_CMD_DEST_FALC (1 << 27) + +#define T_XUSB_CFG_ARU_MAILBOX_DATA_IN 0x0E8 +#define ARU_MAILBOX_DATA_IN_DATA(x) (((x) & 0xFFFFFF) << 0) +#define ARU_MAILBOX_DATA_IN_TYPE(x) (((x) & 0x0000FF) << 24) + +#define T_XUSB_CFG_ARU_MAILBOX_DATA_OUT 0x0EC +#define ARU_MAILBOX_DATA_OUT_DATA(x) (((x) >> 0) & 0xFFFFFF) +#define ARU_MAILBOX_DATA_OUT_TYPE(x) (((x) >> 24) & 0x0000FF) + +#define T_XUSB_CFG_ARU_MAILBOX_OWNER 0x0F0 +#define ARU_MAILBOX_OWNER_SW 2 +#define ARU_MAILBOX_OWNER_FW 1 +#define ARU_MAILBOX_OWNER_NONE 0 + +#define XUSB_CFG_ARU_C11_CSBRANGE 0x41C /* ! UNDOCUMENTED ! */ +#define ARU_C11_CSBRANGE_PAGE(x) ((x) >> 9) +#define ARU_C11_CSBRANGE_ADDR(x) (0x800 + ((x) & 0x1FF)) +#define XUSB_CFG_ARU_SMI_INTR 0x428 /* ! UNDOCUMENTED ! */ +#define ARU_SMI_INTR_EN (1 << 3) +#define ARU_SMI_INTR_FW_HANG (1 << 1) +#define XUSB_CFG_ARU_RST 0x42C /* ! UNDOCUMENTED ! */ +#define ARU_RST_RESET (1 << 0) + +#define XUSB_HOST_CONFIGURATION 0x180 +#define CONFIGURATION_CLKEN_OVERRIDE (1U<< 31) +#define CONFIGURATION_PW_NO_DEVSEL_ERR_CYA (1 << 19) +#define CONFIGURATION_INITIATOR_READ_IDLE (1 << 18) +#define CONFIGURATION_INITIATOR_WRITE_IDLE (1 << 17) +#define CONFIGURATION_WDATA_LEAD_CYA (1 << 15) +#define CONFIGURATION_WR_INTRLV_CYA (1 << 14) +#define CONFIGURATION_TARGET_READ_IDLE (1 << 11) +#define CONFIGURATION_TARGET_WRITE_IDLE (1 << 10) +#define CONFIGURATION_MSI_VEC_EMPTY (1 << 9) +#define CONFIGURATION_UFPCI_MSIAW (1 << 7) +#define CONFIGURATION_UFPCI_PWPASSPW (1 << 6) +#define CONFIGURATION_UFPCI_PASSPW (1 << 5) +#define CONFIGURATION_UFPCI_PWPASSNPW (1 << 4) +#define CONFIGURATION_DFPCI_PWPASSNPW (1 << 3) +#define CONFIGURATION_DFPCI_RSPPASSPW (1 << 2) +#define CONFIGURATION_DFPCI_PASSPW (1 << 1) +#define CONFIGURATION_EN_FPCI (1 << 0) + +/* IPFS address space */ +#define XUSB_HOST_FPCI_ERROR_MASKS 0x184 +#define FPCI_ERROR_MASTER_ABORT (1 << 2) +#define FPCI_ERRORI_DATA_ERROR (1 << 1) +#define FPCI_ERROR_TARGET_ABORT (1 << 0) + +#define XUSB_HOST_INTR_MASK 0x188 +#define INTR_IP_INT_MASK (1 << 16) +#define INTR_MSI_MASK (1 << 8) +#define INTR_INT_MASK (1 << 0) + +#define XUSB_HOST_CLKGATE_HYSTERESIS 0x1BC + + /* CSB Falcon CPU */ +#define XUSB_FALCON_CPUCTL 0x100 +#define CPUCTL_STOPPED (1 << 5) +#define CPUCTL_HALTED (1 << 4) +#define CPUCTL_HRESET (1 << 3) +#define CPUCTL_SRESET (1 << 2) +#define CPUCTL_STARTCPU (1 << 1) +#define CPUCTL_IINVAL (1 << 0) + +#define XUSB_FALCON_BOOTVEC 0x104 +#define XUSB_FALCON_DMACTL 0x10C +#define XUSB_FALCON_IMFILLRNG1 0x154 +#define IMFILLRNG1_TAG_HI(x) (((x) & 0xFFF) << 16) +#define IMFILLRNG1_TAG_LO(x) (((x) & 0xFFF) << 0) +#define XUSB_FALCON_IMFILLCTL 0x158 + +/* CSB mempool */ +#define XUSB_CSB_MEMPOOL_APMAP 0x10181C +#define APMAP_BOOTPATH (1U << 31) + +#define XUSB_CSB_MEMPOOL_ILOAD_ATTR 0x101A00 +#define XUSB_CSB_MEMPOOL_ILOAD_BASE_LO 0x101A04 +#define XUSB_CSB_MEMPOOL_ILOAD_BASE_HI 0x101A08 +#define XUSB_CSB_MEMPOOL_L2IMEMOP_SIZE 0x101A10 +#define L2IMEMOP_SIZE_OFFSET(x) (((x) & 0x3FF) << 8) +#define L2IMEMOP_SIZE_SIZE(x) (((x) & 0x0FF) << 24) + +#define XUSB_CSB_MEMPOOL_L2IMEMOP_TRIG 0x101A14 +#define L2IMEMOP_INVALIDATE_ALL (0x40 << 24) +#define L2IMEMOP_LOAD_LOCKED_RESULT (0x11 << 24) + +#define XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT 0x101A18 +#define L2IMEMOP_RESULT_VLD (1U << 31) + +#define XUSB_CSB_IMEM_BLOCK_SIZE 256 + +#define TEGRA_XHCI_SS_HIGH_SPEED 120000000 +#define TEGRA_XHCI_SS_LOW_SPEED 12000000 + +/* MBOX commands. */ +#define MBOX_CMD_MSG_ENABLED 1 +#define MBOX_CMD_INC_FALC_CLOCK 2 +#define MBOX_CMD_DEC_FALC_CLOCK 3 +#define MBOX_CMD_INC_SSPI_CLOCK 4 +#define MBOX_CMD_DEC_SSPI_CLOCK 5 +#define MBOX_CMD_SET_BW 6 +#define MBOX_CMD_SET_SS_PWR_GATING 7 +#define MBOX_CMD_SET_SS_PWR_UNGATING 8 +#define MBOX_CMD_SAVE_DFE_CTLE_CTX 9 +#define MBOX_CMD_AIRPLANE_MODE_ENABLED 10 +#define MBOX_CMD_AIRPLANE_MODE_DISABLED 11 +#define MBOX_CMD_START_HSIC_IDLE 12 +#define MBOX_CMD_STOP_HSIC_IDLE 13 +#define MBOX_CMD_DBC_WAKE_STACK 14 +#define MBOX_CMD_HSIC_PRETEND_CONNECT 15 +#define MBOX_CMD_RESET_SSPI 16 +#define MBOX_CMD_DISABLE_SS_LFPS_DETECTION 17 +#define MBOX_CMD_ENABLE_SS_LFPS_DETECTION 18 + +/* MBOX responses. */ +#define MBOX_CMD_ACK (0x80 + 0) +#define MBOX_CMD_NAK (0x80 + 1) + + +#define IPFS_WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res_ipfs, (_r), (_v)) +#define IPFS_RD4(_sc, _r) bus_read_4((_sc)->mem_res_ipfs, (_r)) +#define FPCI_WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res_fpci, (_r), (_v)) +#define FPCI_RD4(_sc, _r) bus_read_4((_sc)->mem_res_fpci, (_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, "tegra_xhci", timeout); +#define LOCK_INIT(_sc) \ + mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_xhci", 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) + +struct tegra_xusb_fw_hdr { + uint32_t boot_loadaddr_in_imem; + uint32_t boot_codedfi_offset; + uint32_t boot_codetag; + uint32_t boot_codesize; + + uint32_t phys_memaddr; + uint16_t reqphys_memsize; + uint16_t alloc_phys_memsize; + + uint32_t rodata_img_offset; + uint32_t rodata_section_start; + uint32_t rodata_section_end; + uint32_t main_fnaddr; + + uint32_t fwimg_cksum; + uint32_t fwimg_created_time; + + uint32_t imem_resident_start; + uint32_t imem_resident_end; + uint32_t idirect_start; + uint32_t idirect_end; + uint32_t l2_imem_start; + uint32_t l2_imem_end; + uint32_t version_id; + uint8_t init_ddirect; + uint8_t reserved[3]; + uint32_t phys_addr_log_buffer; + uint32_t total_log_entries; + uint32_t dequeue_ptr; + uint32_t dummy[2]; + uint32_t fwimg_len; + uint8_t magic[8]; + uint32_t ss_low_power_entry_timeout; + uint8_t num_hsic_port; + uint8_t ss_portmap; + uint8_t build; + uint8_t padding[137]; /* Pad to 256 bytes */ +}; + +/* Compatible devices. */ +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-xusb", 1}, + {NULL, 0} +}; + +struct tegra_xhci_softc { + struct xhci_softc xhci_softc; + device_t dev; + struct mtx mtx; + struct resource *mem_res_fpci; + struct resource *mem_res_ipfs; + struct resource *irq_res_mbox; + void *irq_hdl_mbox; + + clk_t clk_xusb_host; + clk_t clk_xusb_gate; + clk_t clk_xusb_falcon_src; + clk_t clk_xusb_ss; + clk_t clk_xusb_hs_src; + clk_t clk_xusb_fs_src; + hwreset_t hwreset_xusb_host; + hwreset_t hwreset_xusb_ss; + regulator_t supply_avddio_pex; + regulator_t supply_dvddio_pex; + regulator_t supply_avdd_usb; + regulator_t supply_avdd_pll_utmip; + regulator_t supply_avdd_pll_erefe; + regulator_t supply_avdd_usb_ss_pll; + regulator_t supply_hvdd_usb_ss; + regulator_t supply_hvdd_usb_ss_pll_e; + phy_t phy_usb2_0; + phy_t phy_usb2_1; + phy_t phy_usb2_2; + phy_t phy_usb3_0; + + struct intr_config_hook irq_hook; + bool xhci_inited; + char *fw_name; + vm_offset_t fw_vaddr; + vm_size_t fw_size; +}; + +static uint32_t +CSB_RD4(struct tegra_xhci_softc *sc, uint32_t addr) +{ + + FPCI_WR4(sc, XUSB_CFG_ARU_C11_CSBRANGE, ARU_C11_CSBRANGE_PAGE(addr)); + return (FPCI_RD4(sc, ARU_C11_CSBRANGE_ADDR(addr))); +} + +static void +CSB_WR4(struct tegra_xhci_softc *sc, uint32_t addr, uint32_t val) +{ + + FPCI_WR4(sc, XUSB_CFG_ARU_C11_CSBRANGE, ARU_C11_CSBRANGE_PAGE(addr)); + FPCI_WR4(sc, ARU_C11_CSBRANGE_ADDR(addr), val); +} + +static int +get_fdt_resources(struct tegra_xhci_softc *sc, phandle_t node) +{ + int rv; + + rv = regulator_get_by_ofw_property(sc->dev, 0, "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, 0, "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, 0, "avdd-usb-supply", + &sc->supply_avdd_usb); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'avdd-usb' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, 0, "avdd-pll-utmip-supply", + &sc->supply_avdd_pll_utmip); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'avdd-pll-utmip' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, 0, "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); + } + rv = regulator_get_by_ofw_property(sc->dev, 0, "avdd-usb-ss-pll-supply", + &sc->supply_avdd_usb_ss_pll); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'avdd-usb-ss-pll' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, 0, "hvdd-usb-ss-supply", + &sc->supply_hvdd_usb_ss); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'hvdd-usb-ss' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, 0, + "hvdd-usb-ss-pll-e-supply", &sc->supply_hvdd_usb_ss_pll_e); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'hvdd-usb-ss-pll-e' regulator\n"); + return (ENXIO); + } + + rv = hwreset_get_by_ofw_name(sc->dev, 0, "xusb_host", + &sc->hwreset_xusb_host); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'xusb_host' reset\n"); + return (ENXIO); + } + rv = hwreset_get_by_ofw_name(sc->dev, 0, "xusb_ss", + &sc->hwreset_xusb_ss); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'xusb_ss' reset\n"); + return (ENXIO); + } + + rv = phy_get_by_ofw_name(sc->dev, 0, "usb2-0", &sc->phy_usb2_0); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'usb2-0' phy\n"); + return (ENXIO); + } + rv = phy_get_by_ofw_name(sc->dev, 0, "usb2-1", &sc->phy_usb2_1); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'usb2-1' phy\n"); + return (ENXIO); + } + rv = phy_get_by_ofw_name(sc->dev, 0, "usb2-2", &sc->phy_usb2_2); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'usb2-2' phy\n"); + return (ENXIO); + } + rv = phy_get_by_ofw_name(sc->dev, 0, "usb3-0", &sc->phy_usb3_0); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'usb3-0' phy\n"); + return (ENXIO); + } + + rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_host", + &sc->clk_xusb_host); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'xusb_host' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_falcon_src", + &sc->clk_xusb_falcon_src); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'xusb_falcon_src' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_ss", + &sc->clk_xusb_ss); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'xusb_ss' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_hs_src", + &sc->clk_xusb_hs_src); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'xusb_hs_src' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_fs_src", + &sc->clk_xusb_fs_src); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'xusb_fs_src' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_index_prop(sc->dev, 0, "freebsd,clock-xusb-gate", 0, + &sc->clk_xusb_gate); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'xusb_gate' clock\n"); + return (ENXIO); + } + return (0); +} + +static int +enable_fdt_resources(struct tegra_xhci_softc *sc) +{ + int rv; + + rv = hwreset_assert(sc->hwreset_xusb_host); + if (rv != 0) { + device_printf(sc->dev, "Cannot reset 'xusb_host' reset\n"); + return (rv); + } + rv = hwreset_assert(sc->hwreset_xusb_ss); + if (rv != 0) { + device_printf(sc->dev, "Cannot reset 'xusb_ss' reset\n"); + return (rv); + } + + 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_usb); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'avdd_usb' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_avdd_pll_utmip); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'avdd_pll_utmip-5v' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_avdd_pll_erefe); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'avdd_pll_erefe' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_avdd_usb_ss_pll); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'avdd_usb_ss_pll' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_hvdd_usb_ss); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'hvdd_usb_ss' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_hvdd_usb_ss_pll_e); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'hvdd_usb_ss_pll_e' regulator\n"); + return (rv); + } + + /* Power off XUSB host and XUSB SS domains. */ + rv = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA); + if (rv != 0) { + device_printf(sc->dev, "Cannot powerdown 'xusba' domain\n"); + return (rv); + } + rv = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC); + if (rv != 0) { + device_printf(sc->dev, "Cannot powerdown 'xusbc' domain\n"); + return (rv); + } + + /* Setup XUSB ss_src clock first */ + clk_set_freq(sc->clk_xusb_ss, TEGRA_XHCI_SS_HIGH_SPEED, 0); + if (rv != 0) + return (rv); + + /* The XUSB gate clock must be enabled before XUSBA can be powered. */ + rv = clk_enable(sc->clk_xusb_gate); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'xusb_gate' clock\n"); + return (rv); + } + + /* Power on XUSB host and XUSB SS domains. */ + rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC, + sc->clk_xusb_host, sc->hwreset_xusb_host); + if (rv != 0) { + device_printf(sc->dev, "Cannot powerup 'xusbc' domain\n"); + return (rv); + } + rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBA, + sc->clk_xusb_ss, sc->hwreset_xusb_ss); + if (rv != 0) { + device_printf(sc->dev, "Cannot powerup 'xusba' domain\n"); + return (rv); + } + + /* Enable rest of clocks */ + rv = clk_enable(sc->clk_xusb_falcon_src); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'xusb_falcon_src' clock\n"); + return (rv); + } + rv = clk_enable(sc->clk_xusb_fs_src); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'xusb_fs_src' clock\n"); + return (rv); + } + rv = clk_enable(sc->clk_xusb_hs_src); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'xusb_hs_src' clock\n"); + return (rv); + } + + rv = phy_enable(sc->phy_usb2_0); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable USB2_0 phy\n"); + return (rv); + } + rv = phy_enable(sc->phy_usb2_1); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable USB2_1 phy\n"); + return (rv); + } + rv = phy_enable(sc->phy_usb2_2); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable USB2_2 phy\n"); + return (rv); + } + rv = phy_enable(sc->phy_usb3_0); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable USB3_0 phy\n"); + return (rv); + } + + return (0); +} + +/* Respond by ACK/NAK back to FW */ +static void +mbox_send_ack(struct tegra_xhci_softc *sc, uint32_t cmd, uint32_t data) +{ + uint32_t reg; + + reg = ARU_MAILBOX_DATA_IN_TYPE(cmd) | ARU_MAILBOX_DATA_IN_DATA(data); + FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_DATA_IN, reg); + + reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD); + reg |= ARU_MAILBOX_CMD_DEST_FALC | ARU_MAILBOX_CMD_INT_EN; + FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD, reg); +} + +/* Sent command to FW */ +static int +mbox_send_cmd(struct tegra_xhci_softc *sc, uint32_t cmd, uint32_t data) +{ + uint32_t reg; + int i; + + reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER); + if (reg != ARU_MAILBOX_OWNER_NONE) { + device_printf(sc->dev, + "CPU mailbox is busy: 0x%08X\n", reg); + return (EBUSY); + } + /* XXX Is this right? Retry loop? Wait before send? */ + FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER, ARU_MAILBOX_OWNER_SW); + reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER); + if (reg != ARU_MAILBOX_OWNER_SW) { + device_printf(sc->dev, + "Cannot acquire CPU mailbox: 0x%08X\n", reg); + return (EBUSY); + } + reg = ARU_MAILBOX_DATA_IN_TYPE(cmd) | ARU_MAILBOX_DATA_IN_DATA(data); + FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_DATA_IN, reg); + + reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD); + reg |= ARU_MAILBOX_CMD_DEST_FALC | ARU_MAILBOX_CMD_INT_EN; + FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD, reg); + + for (i = 250; i > 0; i--) { + reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER); + if (reg == ARU_MAILBOX_OWNER_NONE) + break; + DELAY(100); + } + if (i <= 0) { + device_printf(sc->dev, + "Command response timeout: 0x%08X\n", reg); + return (ETIMEDOUT); + } + + return(0); +} + +static void +process_msg(struct tegra_xhci_softc *sc, uint32_t req_cmd, uint32_t req_data, + uint32_t *resp_cmd, uint32_t *resp_data) +{ + uint64_t freq; + int rv; + + /* In most cases, data are echoed back. */ + *resp_data = req_data; + switch (req_cmd) { + case MBOX_CMD_INC_FALC_CLOCK: + case MBOX_CMD_DEC_FALC_CLOCK: + rv = clk_set_freq(sc->clk_xusb_falcon_src, req_data * 1000ULL, + 0); + if (rv == 0) { + rv = clk_get_freq(sc->clk_xusb_falcon_src, &freq); + *resp_data = (uint32_t)(freq / 1000); + } + *resp_cmd = rv == 0 ? MBOX_CMD_ACK: MBOX_CMD_NAK; + break; + + case MBOX_CMD_INC_SSPI_CLOCK: + case MBOX_CMD_DEC_SSPI_CLOCK: + rv = clk_set_freq(sc->clk_xusb_ss, req_data * 1000ULL, + 0); + if (rv == 0) { + rv = clk_get_freq(sc->clk_xusb_ss, &freq); + *resp_data = (uint32_t)(freq / 1000); + } + *resp_cmd = rv == 0 ? MBOX_CMD_ACK: MBOX_CMD_NAK; + break; + + case MBOX_CMD_SET_BW: + /* No respense is expected. */ + *resp_cmd = 0; + break; + + case MBOX_CMD_SET_SS_PWR_GATING: + case MBOX_CMD_SET_SS_PWR_UNGATING: + *resp_cmd = MBOX_CMD_NAK; + break; + + case MBOX_CMD_SAVE_DFE_CTLE_CTX: + /* Not implemented yet. */ + *resp_cmd = MBOX_CMD_ACK; + break; + + + case MBOX_CMD_START_HSIC_IDLE: + case MBOX_CMD_STOP_HSIC_IDLE: + /* Not implemented yet. */ + *resp_cmd = MBOX_CMD_NAK; + break; + + case MBOX_CMD_DISABLE_SS_LFPS_DETECTION: + case MBOX_CMD_ENABLE_SS_LFPS_DETECTION: + /* Not implemented yet. */ + *resp_cmd = MBOX_CMD_NAK; + break; + + case MBOX_CMD_AIRPLANE_MODE_ENABLED: + case MBOX_CMD_AIRPLANE_MODE_DISABLED: + case MBOX_CMD_DBC_WAKE_STACK: + case MBOX_CMD_HSIC_PRETEND_CONNECT: + case MBOX_CMD_RESET_SSPI: + device_printf(sc->dev, + "Received unused/unexpected command: %u\n", req_cmd); + *resp_cmd = 0; + break; + + default: + device_printf(sc->dev, + "Received unknown command: %u\n", req_cmd); + } +} + +static void +intr_mbox(void *arg) +{ + struct tegra_xhci_softc *sc; + uint32_t reg, msg, resp_cmd, resp_data; + + sc = (struct tegra_xhci_softc *)arg; + + /* Clear interrupt first */ + reg = FPCI_RD4(sc, XUSB_CFG_ARU_SMI_INTR); + FPCI_WR4(sc, XUSB_CFG_ARU_SMI_INTR, reg); + if (reg & ARU_SMI_INTR_FW_HANG) { + device_printf(sc->dev, + "XUSB CPU firmware hang!!! CPUCTL: 0x%08X\n", + CSB_RD4(sc, XUSB_FALCON_CPUCTL)); + } + + msg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_DATA_OUT); + resp_cmd = 0; + process_msg(sc, ARU_MAILBOX_DATA_OUT_TYPE(msg), + ARU_MAILBOX_DATA_OUT_DATA(msg), &resp_cmd, &resp_data); + if (resp_cmd != 0) + mbox_send_ack(sc, resp_cmd, resp_data); + else + FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER, + ARU_MAILBOX_OWNER_NONE); + + reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD); + reg &= ~ARU_MAILBOX_CMD_DEST_SMI; + FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD, reg); + +} + +static int +load_fw(struct tegra_xhci_softc *sc) +{ + const struct firmware *fw; + const struct tegra_xusb_fw_hdr *fw_hdr; + vm_paddr_t fw_paddr, fw_base; + vm_offset_t fw_vaddr; + vm_size_t fw_size; + uint32_t code_tags, code_size; + struct clocktime fw_clock; + struct timespec fw_timespec; + int i; + + /* Reset ARU */ + FPCI_WR4(sc, XUSB_CFG_ARU_RST, ARU_RST_RESET); + DELAY(3000); + + /* Check if FALCON already runs */ + if (CSB_RD4(sc, XUSB_CSB_MEMPOOL_ILOAD_BASE_LO) != 0) { + device_printf(sc->dev, + "XUSB CPU is already loaded, CPUCTL: 0x%08X\n", + CSB_RD4(sc, XUSB_FALCON_CPUCTL)); + return (0); + } + + fw = firmware_get(sc->fw_name); + if (fw == NULL) { + device_printf(sc->dev, "Cannot read xusb firmware\n"); + return (ENOENT); + } + + /* Allocate uncached memory and copy firmware into. */ + fw_hdr = (const struct tegra_xusb_fw_hdr *)fw->data; + fw_size = fw_hdr->fwimg_len; + + fw_vaddr = kmem_alloc_contig(kernel_arena, fw_size, + M_WAITOK, 0, -1UL, PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE); + fw_paddr = vtophys(fw_vaddr); + fw_hdr = (const struct tegra_xusb_fw_hdr *)fw_vaddr; + memcpy((void *)fw_vaddr, fw->data, fw_size); + + firmware_put(fw, FIRMWARE_UNLOAD); + sc->fw_vaddr = fw_vaddr; + sc->fw_size = fw_size; + + /* Setup firmware physical address and size. */ + fw_base = fw_paddr + sizeof(*fw_hdr); + CSB_WR4(sc, XUSB_CSB_MEMPOOL_ILOAD_ATTR, fw_size); + CSB_WR4(sc, XUSB_CSB_MEMPOOL_ILOAD_BASE_LO, fw_base & 0xFFFFFFFF); + CSB_WR4(sc, XUSB_CSB_MEMPOOL_ILOAD_BASE_HI, (uint64_t)fw_base >> 32); + CSB_WR4(sc, XUSB_CSB_MEMPOOL_APMAP, APMAP_BOOTPATH); + + /* Invalidate full L2IMEM context. */ + CSB_WR4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_TRIG, + L2IMEMOP_INVALIDATE_ALL); + + /* Program load of L2IMEM by boot code. */ + code_tags = howmany(fw_hdr->boot_codetag, XUSB_CSB_IMEM_BLOCK_SIZE); + code_size = howmany(fw_hdr->boot_codesize, XUSB_CSB_IMEM_BLOCK_SIZE); + CSB_WR4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_SIZE, + L2IMEMOP_SIZE_OFFSET(code_tags) | + L2IMEMOP_SIZE_SIZE(code_size)); + + /* Execute L2IMEM boot code fetch. */ + CSB_WR4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_TRIG, + L2IMEMOP_LOAD_LOCKED_RESULT); + + /* Program FALCON auto-fill range and block count */ + CSB_WR4(sc, XUSB_FALCON_IMFILLCTL, code_size); + CSB_WR4(sc, XUSB_FALCON_IMFILLRNG1, + IMFILLRNG1_TAG_LO(code_tags) | + IMFILLRNG1_TAG_HI(code_tags + code_size)); + + CSB_WR4(sc, XUSB_FALCON_DMACTL, 0); + /* Wait for CPU */ + for (i = 500; i > 0; i--) { + if (CSB_RD4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT) & + L2IMEMOP_RESULT_VLD) + break; + DELAY(100); + } + if (i <= 0) { + device_printf(sc->dev, "Timedout while wating for DMA, " + "state: 0x%08X\n", + CSB_RD4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT)); + return (ETIMEDOUT); + } + + /* Boot FALCON cpu */ + CSB_WR4(sc, XUSB_FALCON_BOOTVEC, fw_hdr->boot_codetag); + CSB_WR4(sc, XUSB_FALCON_CPUCTL, CPUCTL_STARTCPU); + + /* Wait for CPU */ + for (i = 50; i > 0; i--) { + if (CSB_RD4(sc, XUSB_FALCON_CPUCTL) == CPUCTL_STOPPED) + break; + DELAY(100); + } + if (i <= 0) { + device_printf(sc->dev, "Timedout while wating for FALCON cpu, " + "state: 0x%08X\n", CSB_RD4(sc, XUSB_FALCON_CPUCTL)); + return (ETIMEDOUT); + } + + fw_timespec.tv_sec = fw_hdr->fwimg_created_time; + fw_timespec.tv_nsec = 0; + clock_ts_to_ct(&fw_timespec, &fw_clock); + device_printf(sc->dev, + " Falcon firmware version: %02X.%02X.%04X," + " (%d/%d/%d %d:%02d:%02d UTC)\n", + (fw_hdr->version_id >> 24) & 0xFF,(fw_hdr->version_id >> 15) & 0xFF, + fw_hdr->version_id & 0xFFFF, + fw_clock.day, fw_clock.mon, fw_clock.year, + fw_clock.hour, fw_clock.min, fw_clock.sec); + + return (0); +} + +static int +init_hw(struct tegra_xhci_softc *sc) +{ + int rv; + uint32_t reg; + rman_res_t base_addr; + + base_addr = rman_get_start(sc->xhci_softc.sc_io_res); + + /* Enable FPCI access */ + reg = IPFS_RD4(sc, XUSB_HOST_CONFIGURATION); + reg |= CONFIGURATION_EN_FPCI; + IPFS_WR4(sc, XUSB_HOST_CONFIGURATION, reg); + IPFS_RD4(sc, XUSB_HOST_CONFIGURATION); + + + /* Program bar for XHCI base address */ + reg = FPCI_RD4(sc, T_XUSB_CFG_4); + reg &= ~CFG_4_BASE_ADDRESS(~0); + reg |= CFG_4_BASE_ADDRESS((uint32_t)base_addr >> 15); + FPCI_WR4(sc, T_XUSB_CFG_4, reg); + FPCI_WR4(sc, T_XUSB_CFG_5, (uint32_t)((uint64_t)(base_addr) >> 32)); + + /* Enable bus master */ + reg = FPCI_RD4(sc, T_XUSB_CFG_1); + reg |= CFG_1_IO_SPACE; + reg |= CFG_1_MEMORY_SPACE; + reg |= CFG_1_BUS_MASTER; + FPCI_WR4(sc, T_XUSB_CFG_1, reg); + + /* Enable Interrupts */ + reg = IPFS_RD4(sc, XUSB_HOST_INTR_MASK); + reg |= INTR_IP_INT_MASK; + IPFS_WR4(sc, XUSB_HOST_INTR_MASK, reg); + + /* Set hysteresis */ + IPFS_WR4(sc, XUSB_HOST_CLKGATE_HYSTERESIS, 128); + + rv = load_fw(sc); + if (rv != 0) + return rv; + return (0); +} + +static int +tegra_xhci_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 XHCI controller"); + return (BUS_PROBE_DEFAULT); + } + return (ENXIO); +} + +static int +tegra_xhci_detach(device_t dev) +{ + struct tegra_xhci_softc *sc; + struct xhci_softc *xsc; + + sc = device_get_softc(dev); + xsc = &sc->xhci_softc; + + /* during module unload there are lots of children leftover */ + device_delete_children(dev); + if (sc->xhci_inited) { + usb_callout_drain(&xsc->sc_callout); + xhci_halt_controller(xsc); + } + + if (xsc->sc_irq_res && xsc->sc_intr_hdl) { + bus_teardown_intr(dev, xsc->sc_irq_res, xsc->sc_intr_hdl); + xsc->sc_intr_hdl = NULL; + } + if (xsc->sc_irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, + rman_get_rid(xsc->sc_irq_res), xsc->sc_irq_res); + xsc->sc_irq_res = NULL; + } + if (xsc->sc_io_res != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(xsc->sc_io_res), xsc->sc_io_res); + xsc->sc_io_res = NULL; + } + if (sc->xhci_inited) + xhci_uninit(xsc); + if (sc->irq_hdl_mbox != NULL) + bus_teardown_intr(dev, sc->irq_res_mbox, sc->irq_hdl_mbox); + if (sc->fw_vaddr != 0) + kmem_free(kernel_arena, sc->fw_vaddr, sc->fw_size); + LOCK_DESTROY(sc); + return (0); +} + +static int +tegra_xhci_attach(device_t dev) +{ + struct tegra_xhci_softc *sc; + struct xhci_softc *xsc; + int rv, rid; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->fw_name = "tegra124_xusb_fw"; + node = ofw_bus_get_node(dev); + xsc = &sc->xhci_softc; + LOCK_INIT(sc); + + rv = get_fdt_resources(sc, node); + if (rv != 0) { + rv = ENXIO; + goto error; + } + rv = enable_fdt_resources(sc); + if (rv != 0) { + rv = ENXIO; + goto error; + } + + /* Allocate resources. */ + rid = 0; + xsc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (xsc->sc_io_res == NULL) { + device_printf(dev, + "Could not allocate HCD memory resources\n"); + rv = ENXIO; + goto error; + } + rid = 1; + sc->mem_res_fpci = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res_fpci == NULL) { + device_printf(dev, + "Could not allocate FPCI memory resources\n"); + rv = ENXIO; + goto error; + } + rid = 2; + sc->mem_res_ipfs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res_ipfs == NULL) { + device_printf(dev, + "Could not allocate IPFS memory resources\n"); + rv = ENXIO; + goto error; + } + + rid = 0; + xsc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (xsc->sc_irq_res == NULL) { + device_printf(dev, "Could not allocate HCD IRQ resources\n"); + rv = ENXIO; + goto error; + } + rid = 1; + sc->irq_res_mbox = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->irq_res_mbox == NULL) { + device_printf(dev, "Could not allocate MBOX IRQ resources\n"); + rv = ENXIO; + goto error; + } + + rv = init_hw(sc); + if (rv != 0) { + device_printf(dev, "Could not initialize XUSB hardware\n"); + goto error; + } + + /* Wakeup and enable firmaware */ + rv = mbox_send_cmd(sc, MBOX_CMD_MSG_ENABLED, 0); + if (rv != 0) { + device_printf(sc->dev, "Could not enable XUSB firmware\n"); + goto error; + } + + /* Fill data for XHCI driver. */ + xsc->sc_bus.parent = dev; + xsc->sc_bus.devices = xsc->sc_devices; + xsc->sc_bus.devices_max = XHCI_MAX_DEVICES; + + xsc->sc_io_tag = rman_get_bustag(xsc->sc_io_res); + xsc->sc_io_hdl = rman_get_bushandle(xsc->sc_io_res); + xsc->sc_io_size = rman_get_size(xsc->sc_io_res); + strlcpy(xsc->sc_vendor, "Nvidia", sizeof(xsc->sc_vendor)); + + /* Add USB bus device. */ + xsc->sc_bus.bdev = device_add_child(sc->dev, "usbus", -1); + if (xsc->sc_bus.bdev == NULL) { + device_printf(sc->dev, "Could not add USB device\n"); + rv = ENXIO; + goto error; + } + device_set_ivars(xsc->sc_bus.bdev, &xsc->sc_bus); + device_set_desc(xsc->sc_bus.bdev, "Nvidia USB 3.0 controller"); + + rv = xhci_init(xsc, sc->dev, 1); + if (rv != 0) { + device_printf(sc->dev, "USB init failed: %d\n", rv); + goto error; + } + sc->xhci_inited = true; + rv = xhci_start_controller(xsc); + if (rv != 0) { + device_printf(sc->dev, + "Could not start XHCI controller: %d\n", rv); + goto error; + } + + rv = bus_setup_intr(dev, sc->irq_res_mbox, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, intr_mbox, sc, &sc->irq_hdl_mbox); + if (rv != 0) { + device_printf(dev, "Could not setup error IRQ: %d\n",rv); + xsc->sc_intr_hdl = NULL; + goto error; + } + + rv = bus_setup_intr(dev, xsc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (driver_intr_t *)xhci_interrupt, xsc, &xsc->sc_intr_hdl); + if (rv != 0) { + device_printf(dev, "Could not setup error IRQ: %d\n",rv); + xsc->sc_intr_hdl = NULL; + goto error; + } + + /* Probe the bus. */ + rv = device_probe_and_attach(xsc->sc_bus.bdev); + if (rv != 0) { + device_printf(sc->dev, "Could not initialize USB: %d\n", rv); + goto error; + } + + return (0); + +error: +panic("XXXXX"); + tegra_xhci_detach(dev); + return (rv); +} + +static device_method_t xhci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_xhci_probe), + DEVMETHOD(device_attach, tegra_xhci_attach), + DEVMETHOD(device_detach, tegra_xhci_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 devclass_t xhci_devclass; +static DEFINE_CLASS_0(xhci, xhci_driver, xhci_methods, + sizeof(struct tegra_xhci_softc)); +DRIVER_MODULE(tegra_xhci, simplebus, xhci_driver, xhci_devclass, NULL, NULL); +MODULE_DEPEND(tegra_xhci, usb, 1, 1, 1); Property changes on: trunk/sys/arm/nvidia/tegra_xhci.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property From laffer1 at midnightbsd.org Fri Mar 6 12:12:55 2020 From: laffer1 at midnightbsd.org (laffer1 at midnightbsd.org) Date: Fri, 6 Mar 2020 12:12:55 -0500 (EST) Subject: [Midnightbsd-cvs] src [12397] sync with freebsd 11 Message-ID: <202003061712.026HCtcJ014606@stargazer.midnightbsd.org> Revision: 12397 http://svnweb.midnightbsd.org/src/?rev=12397 Author: laffer1 Date: 2020-03-06 12:12:55 -0500 (Fri, 06 Mar 2020) Log Message: ----------- sync with freebsd 11 Modified Paths: -------------- trunk/sys/arm/lpc/files.lpc trunk/sys/arm/lpc/if_lpe.c trunk/sys/arm/lpc/if_lpereg.h trunk/sys/arm/lpc/lpc_dmac.c trunk/sys/arm/lpc/lpc_fb.c trunk/sys/arm/lpc/lpc_gpio.c trunk/sys/arm/lpc/lpc_intc.c trunk/sys/arm/lpc/lpc_machdep.c trunk/sys/arm/lpc/lpc_mmc.c trunk/sys/arm/lpc/lpc_ohci.c trunk/sys/arm/lpc/lpc_pll.c trunk/sys/arm/lpc/lpc_pwr.c trunk/sys/arm/lpc/lpc_rtc.c trunk/sys/arm/lpc/lpc_spi.c trunk/sys/arm/lpc/lpc_timer.c trunk/sys/arm/lpc/lpcreg.h trunk/sys/arm/lpc/lpcvar.h trunk/sys/arm/lpc/ssd1289.c trunk/sys/arm/lpc/std.lpc Property Changed: ---------------- trunk/sys/arm/lpc/files.lpc trunk/sys/arm/lpc/std.lpc Modified: trunk/sys/arm/lpc/files.lpc =================================================================== --- trunk/sys/arm/lpc/files.lpc 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/files.lpc 2020-03-06 17:12:55 UTC (rev 12397) @@ -1,8 +1,4 @@ -# $FreeBSD: stable/10/sys/arm/lpc/files.lpc 278727 2015-02-13 22:32:02Z ian $ -arm/arm/bus_space_base.c standard -arm/arm/bus_space_generic.c standard -arm/arm/cpufunc_asm_arm9.S standard -arm/arm/cpufunc_asm_armv5.S standard +# $MidnightBSD$ arm/lpc/lpc_machdep.c standard arm/lpc/lpc_pwr.c standard arm/lpc/lpc_intc.c standard Property changes on: trunk/sys/arm/lpc/files.lpc ___________________________________________________________________ Deleted: mnbsd:nokeywords ## -1 +0,0 ## -MidnightBSD=%H \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Modified: trunk/sys/arm/lpc/if_lpe.c =================================================================== --- trunk/sys/arm/lpc/if_lpe.c 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/if_lpe.c 2020-03-06 17:12:55 UTC (rev 12397) @@ -26,7 +26,7 @@ * */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/lpc/if_lpe.c 266152 2014-05-15 16:11:06Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/lpc/if_lpe.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -173,7 +173,7 @@ #define lpe_lock(_sc) mtx_lock(&(_sc)->lpe_mtx) #define lpe_unlock(_sc) mtx_unlock(&(_sc)->lpe_mtx) -#define lpe_lock_assert(sc) mtx_assert(&(_sc)->lpe_mtx, MA_OWNED) +#define lpe_lock_assert(_sc) mtx_assert(&(_sc)->lpe_mtx, MA_OWNED) #define lpe_read_4(_sc, _reg) \ bus_space_read_4((_sc)->lpe_bst, (_sc)->lpe_bsh, (_reg)) @@ -755,7 +755,7 @@ /* Check received frame for errors */ if (hws->lhs_info & LPE_HWDESC_RXERRS) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); lpe_discard_rxbuf(sc, cons); lpe_init_rxbuf(sc, cons); goto skip; @@ -765,7 +765,7 @@ m->m_pkthdr.rcvif = ifp; m->m_data += 2; - ifp->if_ipackets++; + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); lpe_unlock(sc); (*ifp->if_input)(ifp, m); @@ -801,12 +801,12 @@ bus_dmamap_sync(sc->lpe_cdata.lpe_tx_buf_tag, txd->lpe_txdesc_dmamap, BUS_DMASYNC_POSTWRITE); - ifp->if_collisions += LPE_HWDESC_COLLISIONS(hws->lhs_info); + if_inc_counter(ifp, IFCOUNTER_COLLISIONS, LPE_HWDESC_COLLISIONS(hws->lhs_info)); if (hws->lhs_info & LPE_HWDESC_TXERRS) - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); else - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); if (txd->lpe_txdesc_first) { bus_dmamap_unload(sc->lpe_cdata.lpe_tx_buf_tag, Modified: trunk/sys/arm/lpc/if_lpereg.h =================================================================== --- trunk/sys/arm/lpc/if_lpereg.h 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/if_lpereg.h 2020-03-06 17:12:55 UTC (rev 12397) @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/lpc/if_lpereg.h 261455 2014-02-04 03:36:42Z eadler $ + * $FreeBSD: stable/11/sys/arm/lpc/if_lpereg.h 331722 2018-03-29 02:50:57Z eadler $ */ #ifndef _ARM_LPC_IF_LPEREG_H Modified: trunk/sys/arm/lpc/lpc_dmac.c =================================================================== --- trunk/sys/arm/lpc/lpc_dmac.c 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/lpc_dmac.c 2020-03-06 17:12:55 UTC (rev 12397) @@ -27,7 +27,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/lpc/lpc_dmac.c 266152 2014-05-15 16:11:06Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/lpc/lpc_dmac.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include Modified: trunk/sys/arm/lpc/lpc_fb.c =================================================================== --- trunk/sys/arm/lpc/lpc_fb.c 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/lpc_fb.c 2020-03-06 17:12:55 UTC (rev 12397) @@ -26,7 +26,7 @@ * */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/lpc/lpc_fb.c 266152 2014-05-15 16:11:06Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/lpc/lpc_fb.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -50,12 +50,9 @@ #include #include -#include -#include #include #include -#include #include #include @@ -283,10 +280,9 @@ static int lpc_fb_fdt_read(phandle_t node, const char *name, uint32_t *ret) { - if (OF_getprop(node, name, ret, sizeof(uint32_t)) <= 0) + if (OF_getencprop(node, name, ret, sizeof(uint32_t)) <= 0) return (ENOENT); - *ret = fdt32_to_cpu(*ret); return (0); } Modified: trunk/sys/arm/lpc/lpc_gpio.c =================================================================== --- trunk/sys/arm/lpc/lpc_gpio.c 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/lpc_gpio.c 2020-03-06 17:12:55 UTC (rev 12397) @@ -51,7 +51,7 @@ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/lpc/lpc_gpio.c 278782 2015-02-14 20:37:33Z loos $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/lpc/lpc_gpio.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -74,12 +74,11 @@ #include #include -#include -#include #include #include #include +#include #include #include @@ -91,6 +90,7 @@ struct lpc_gpio_softc { device_t lg_dev; + device_t lg_busdev; struct resource * lg_res; bus_space_tag_t lg_bst; bus_space_handle_t lg_bsh; @@ -136,6 +136,7 @@ static int lpc_gpio_attach(device_t); static int lpc_gpio_detach(device_t); +static device_t lpc_gpio_get_bus(device_t); static int lpc_gpio_pin_max(device_t, int *); static int lpc_gpio_pin_getcaps(device_t, uint32_t, uint32_t *); static int lpc_gpio_pin_getflags(device_t, uint32_t, uint32_t *); @@ -193,10 +194,13 @@ lpc_gpio_sc = sc; - device_add_child(dev, "gpioc", -1); - device_add_child(dev, "gpiobus", -1); + sc->lg_busdev = gpiobus_attach_bus(dev); + if (sc->lg_busdev == NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->lg_res); + return (ENXIO); + } - return (bus_generic_attach(dev)); + return (0); } static int @@ -205,6 +209,16 @@ return (EBUSY); } +static device_t +lpc_gpio_get_bus(device_t dev) +{ + struct lpc_gpio_softc *sc; + + sc = device_get_softc(dev); + + return (sc->lg_busdev); +} + static int lpc_gpio_pin_max(device_t dev, int *npins) { @@ -505,7 +519,7 @@ } void -platform_gpio_init() +lpc_gpio_init(void) { bus_space_tag_t bst; bus_space_handle_t bsh; @@ -528,6 +542,7 @@ DEVMETHOD(device_detach, lpc_gpio_detach), /* GPIO interface */ + DEVMETHOD(gpio_get_bus, lpc_gpio_get_bus), DEVMETHOD(gpio_pin_max, lpc_gpio_pin_max), DEVMETHOD(gpio_pin_getcaps, lpc_gpio_pin_getcaps), DEVMETHOD(gpio_pin_getflags, lpc_gpio_pin_getflags), Modified: trunk/sys/arm/lpc/lpc_intc.c =================================================================== --- trunk/sys/arm/lpc/lpc_intc.c 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/lpc_intc.c 2020-03-06 17:12:55 UTC (rev 12397) @@ -27,7 +27,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/lpc/lpc_intc.c 266152 2014-05-15 16:11:06Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/lpc/lpc_intc.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -60,10 +60,10 @@ static struct lpc_intc_softc *intc_softc = NULL; -#define intc_read_4(reg) \ - bus_space_read_4(intc_softc->li_bst, intc_softc->li_bsh, reg) -#define intc_write_4(reg, val) \ - bus_space_write_4(intc_softc->li_bst, intc_softc->li_bsh, reg, val) +#define intc_read_4(_sc, _reg) \ + bus_space_read_4((_sc)->li_bst, (_sc)->li_bsh, (_reg)) +#define intc_write_4(_sc, _reg, _val) \ + bus_space_write_4((_sc)->li_bst, (_sc)->li_bsh, (_reg), (_val)) static int lpc_intc_probe(device_t dev) @@ -101,12 +101,12 @@ arm_post_filter = lpc_intc_eoi; /* Clear interrupt status registers and disable all interrupts */ - intc_write_4(LPC_INTC_MIC_ER, 0); - intc_write_4(LPC_INTC_SIC1_ER, 0); - intc_write_4(LPC_INTC_SIC2_ER, 0); - intc_write_4(LPC_INTC_MIC_RSR, ~0); - intc_write_4(LPC_INTC_SIC1_RSR, ~0); - intc_write_4(LPC_INTC_SIC2_RSR, ~0); + intc_write_4(sc, LPC_INTC_MIC_ER, 0); + intc_write_4(sc, LPC_INTC_SIC1_ER, 0); + intc_write_4(sc, LPC_INTC_SIC2_ER, 0); + intc_write_4(sc, LPC_INTC_MIC_RSR, ~0); + intc_write_4(sc, LPC_INTC_SIC1_RSR, ~0); + intc_write_4(sc, LPC_INTC_SIC2_RSR, ~0); return (0); } @@ -129,11 +129,12 @@ int arm_get_next_irq(int last) { + struct lpc_intc_softc *sc = intc_softc; uint32_t value; int i; /* IRQs 0-31 are mapped to LPC_INTC_MIC_SR */ - value = intc_read_4(LPC_INTC_MIC_SR); + value = intc_read_4(sc, LPC_INTC_MIC_SR); for (i = 0; i < 32; i++) { if (value & (1 << i)) return (i); @@ -140,7 +141,7 @@ } /* IRQs 32-63 are mapped to LPC_INTC_SIC1_SR */ - value = intc_read_4(LPC_INTC_SIC1_SR); + value = intc_read_4(sc, LPC_INTC_SIC1_SR); for (i = 0; i < 32; i++) { if (value & (1 << i)) return (i + 32); @@ -147,7 +148,7 @@ } /* IRQs 64-95 are mapped to LPC_INTC_SIC2_SR */ - value = intc_read_4(LPC_INTC_SIC2_SR); + value = intc_read_4(sc, LPC_INTC_SIC2_SR); for (i = 0; i < 32; i++) { if (value & (1 << i)) return (i + 64); @@ -159,6 +160,7 @@ void arm_mask_irq(uintptr_t nb) { + struct lpc_intc_softc *sc = intc_softc; int reg; uint32_t value; @@ -175,14 +177,15 @@ reg = LPC_INTC_MIC_ER; /* Clear bit in ER register */ - value = intc_read_4(reg); + value = intc_read_4(sc, reg); value &= ~(1 << nb); - intc_write_4(reg, value); + intc_write_4(sc, reg, value); } void arm_unmask_irq(uintptr_t nb) { + struct lpc_intc_softc *sc = intc_softc; int reg; uint32_t value; @@ -196,14 +199,15 @@ reg = LPC_INTC_MIC_ER; /* Set bit in ER register */ - value = intc_read_4(reg); + value = intc_read_4(sc, reg); value |= (1 << nb); - intc_write_4(reg, value); + intc_write_4(sc, reg, value); } static void lpc_intc_eoi(void *data) { + struct lpc_intc_softc *sc = intc_softc; int reg; int nb = (int)data; uint32_t value; @@ -218,16 +222,13 @@ reg = LPC_INTC_MIC_RSR; /* Set bit in RSR register */ - value = intc_read_4(reg); + value = intc_read_4(sc, reg); value |= (1 << nb); - intc_write_4(reg, value); + intc_write_4(sc, reg, value); } -struct fdt_fixup_entry fdt_fixup_table[] = { - { NULL, NULL } -}; - +#ifndef INTRNG static int fdt_pic_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, int *pol) @@ -245,3 +246,4 @@ &fdt_pic_decode_ic, NULL }; +#endif Modified: trunk/sys/arm/lpc/lpc_machdep.c =================================================================== --- trunk/sys/arm/lpc/lpc_machdep.c 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/lpc_machdep.c 2020-03-06 17:12:55 UTC (rev 12397) @@ -40,12 +40,13 @@ #include "opt_platform.h" #include -__FBSDID("$FreeBSD: stable/10/sys/arm/lpc/lpc_machdep.c 266084 2014-05-14 19:18:58Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/lpc/lpc_machdep.c 331722 2018-03-29 02:50:57Z eadler $"); #define _ARM32_BUS_DMA_PRIVATE #include #include #include +#include #include #include @@ -52,8 +53,8 @@ #include #include -#include #include +#include #include #include @@ -61,29 +62,29 @@ #include vm_offset_t -initarm_lastaddr(void) +platform_lastaddr(void) { - return (arm_devmap_lastaddr()); + return (devmap_lastaddr()); } void -initarm_early_init(void) +platform_probe_and_attach(void) { } void -initarm_gpio_init(void) +platform_gpio_init(void) { /* * Set initial values of GPIO output ports */ - platform_gpio_init(); + lpc_gpio_init(); } void -initarm_late_init(void) +platform_late_init(void) { } @@ -90,13 +91,13 @@ /* * Add a single static device mapping. * The values used were taken from the ranges property of the SoC node in the - * dts file when this code was converted to arm_devmap_add_entry(). + * dts file when this code was converted to devmap_add_entry(). */ int -initarm_devmap_init(void) +platform_devmap_init(void) { - arm_devmap_add_entry(LPC_DEV_PHYS_BASE, LPC_DEV_SIZE); + devmap_add_entry(LPC_DEV_PHYS_BASE, LPC_DEV_SIZE); return (0); } Modified: trunk/sys/arm/lpc/lpc_mmc.c =================================================================== --- trunk/sys/arm/lpc/lpc_mmc.c 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/lpc_mmc.c 2020-03-06 17:12:55 UTC (rev 12397) @@ -26,7 +26,7 @@ * */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/lpc/lpc_mmc.c 318198 2017-05-11 21:01:02Z marius $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/lpc/lpc_mmc.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -40,8 +40,6 @@ #include #include -#include -#include #include #include Modified: trunk/sys/arm/lpc/lpc_ohci.c =================================================================== --- trunk/sys/arm/lpc/lpc_ohci.c 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/lpc_ohci.c 2020-03-06 17:12:55 UTC (rev 12397) @@ -26,7 +26,7 @@ * */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/lpc/lpc_ohci.c 278278 2015-02-05 20:03:02Z hselasky $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/lpc/lpc_ohci.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include Modified: trunk/sys/arm/lpc/lpc_pll.c =================================================================== --- trunk/sys/arm/lpc/lpc_pll.c 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/lpc_pll.c 2020-03-06 17:12:55 UTC (rev 12397) @@ -26,4 +26,4 @@ * */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/lpc/lpc_pll.c 239278 2012-08-15 05:37:10Z gonzo $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/lpc/lpc_pll.c 331722 2018-03-29 02:50:57Z eadler $"); Modified: trunk/sys/arm/lpc/lpc_pwr.c =================================================================== --- trunk/sys/arm/lpc/lpc_pwr.c 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/lpc_pwr.c 2020-03-06 17:12:55 UTC (rev 12397) @@ -27,7 +27,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/lpc/lpc_pwr.c 266152 2014-05-15 16:11:06Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/lpc/lpc_pwr.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include Modified: trunk/sys/arm/lpc/lpc_rtc.c =================================================================== --- trunk/sys/arm/lpc/lpc_rtc.c 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/lpc_rtc.c 2020-03-06 17:12:55 UTC (rev 12397) @@ -26,7 +26,7 @@ * */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/lpc/lpc_rtc.c 266152 2014-05-15 16:11:06Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/lpc/lpc_rtc.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include Modified: trunk/sys/arm/lpc/lpc_spi.c =================================================================== --- trunk/sys/arm/lpc/lpc_spi.c 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/lpc_spi.c 2020-03-06 17:12:55 UTC (rev 12397) @@ -26,7 +26,7 @@ * */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/lpc/lpc_spi.c 266152 2014-05-15 16:11:06Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/lpc/lpc_spi.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -48,8 +48,6 @@ #include #include -#include -#include #include #include @@ -144,12 +142,16 @@ lpc_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) { struct lpc_spi_softc *sc = device_get_softc(dev); - struct spibus_ivar *devi = SPIBUS_IVAR(child); + uint32_t cs; uint8_t *in_buf, *out_buf; int i; + spibus_get_cs(child, &cs); + + cs &= ~SPIBUS_CS_HIGH; + /* Set CS active */ - lpc_gpio_set_state(child, devi->cs, 0); + lpc_gpio_set_state(child, cs, 0); /* Wait for FIFO to be ready */ while ((lpc_spi_read_4(sc, LPC_SSP_SR) & LPC_SSP_SR_TNF) == 0); @@ -171,7 +173,7 @@ } /* Set CS inactive */ - lpc_gpio_set_state(child, devi->cs, 1); + lpc_gpio_set_state(child, cs, 1); return (0); } Modified: trunk/sys/arm/lpc/lpc_timer.c =================================================================== --- trunk/sys/arm/lpc/lpc_timer.c 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/lpc_timer.c 2020-03-06 17:12:55 UTC (rev 12397) @@ -26,7 +26,7 @@ * */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/lpc/lpc_timer.c 266207 2014-05-16 02:21:51Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/lpc/lpc_timer.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -41,7 +41,6 @@ #include #include -#include #include #include @@ -159,7 +158,7 @@ /* Get PERIPH_CLK encoded in parent bus 'bus-frequency' property */ node = ofw_bus_get_node(dev); - if (OF_getprop(OF_parent(node), "bus-frequency", &freq, + if (OF_getencprop(OF_parent(node), "bus-frequency", &freq, sizeof(pcell_t)) <= 0) { bus_release_resources(dev, lpc_timer_spec, sc->lt_res); bus_teardown_intr(dev, sc->lt_res[2], intrcookie); @@ -167,8 +166,6 @@ return (ENXIO); } - freq = fdt32_to_cpu(freq); - /* Set desired frequency in event timer and timecounter */ sc->lt_et.et_frequency = (uint64_t)freq; lpc_timecounter.tc_frequency = (uint64_t)freq; Modified: trunk/sys/arm/lpc/lpcreg.h =================================================================== --- trunk/sys/arm/lpc/lpcreg.h 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/lpcreg.h 2020-03-06 17:12:55 UTC (rev 12397) @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/lpc/lpcreg.h 266084 2014-05-14 19:18:58Z ian $ + * $FreeBSD: stable/11/sys/arm/lpc/lpcreg.h 331722 2018-03-29 02:50:57Z eadler $ */ #ifndef _ARM_LPC_LPCREG_H Modified: trunk/sys/arm/lpc/lpcvar.h =================================================================== --- trunk/sys/arm/lpc/lpcvar.h 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/lpcvar.h 2020-03-06 17:12:55 UTC (rev 12397) @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/lpc/lpcvar.h 239278 2012-08-15 05:37:10Z gonzo $ + * $FreeBSD: stable/11/sys/arm/lpc/lpcvar.h 331722 2018-03-29 02:50:57Z eadler $ */ #ifndef _ARM_LPC_LPCVAR_H @@ -39,7 +39,7 @@ void lpc_pwr_write(device_t, int, uint32_t); /* GPIO */ -void platform_gpio_init(void); +void lpc_gpio_init(void); int lpc_gpio_set_flags(device_t, int, int); int lpc_gpio_set_state(device_t, int, int); int lpc_gpio_get_state(device_t, int, int *); Modified: trunk/sys/arm/lpc/ssd1289.c =================================================================== --- trunk/sys/arm/lpc/ssd1289.c 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/ssd1289.c 2020-03-06 17:12:55 UTC (rev 12397) @@ -27,7 +27,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/lpc/ssd1289.c 239278 2012-08-15 05:37:10Z gonzo $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/lpc/ssd1289.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include Modified: trunk/sys/arm/lpc/std.lpc =================================================================== --- trunk/sys/arm/lpc/std.lpc 2020-03-06 17:08:20 UTC (rev 12396) +++ trunk/sys/arm/lpc/std.lpc 2020-03-06 17:12:55 UTC (rev 12397) @@ -1,4 +1,4 @@ -# $FreeBSD: stable/10/sys/arm/lpc/std.lpc 266110 2014-05-15 02:41:23Z ian $ +# $MidnightBSD$ # # DM644x # @@ -9,6 +9,5 @@ makeoptions CONF_CFLAGS="-march=armv5te" options PHYSADDR=0x80000000 makeoptions KERNPHYSADDR=0x80100000 -options KERNPHYSADDR=0x80100000 makeoptions KERNVIRTADDR=0xc0100000 options KERNVIRTADDR=0xc0100000 Property changes on: trunk/sys/arm/lpc/std.lpc ___________________________________________________________________ Deleted: mnbsd:nokeywords ## -1 +0,0 ## -MidnightBSD=%H \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property From laffer1 at midnightbsd.org Fri Mar 6 12:15:06 2020 From: laffer1 at midnightbsd.org (laffer1 at midnightbsd.org) Date: Fri, 6 Mar 2020 12:15:06 -0500 (EST) Subject: [Midnightbsd-cvs] src [12398] trunk/sys/arm/allwinner/a10: sync with freebsd 11 stable Message-ID: <202003061715.026HF6NW015115@stargazer.midnightbsd.org> Revision: 12398 http://svnweb.midnightbsd.org/src/?rev=12398 Author: laffer1 Date: 2020-03-06 12:15:06 -0500 (Fri, 06 Mar 2020) Log Message: ----------- sync with freebsd 11 stable Added Paths: ----------- trunk/sys/arm/allwinner/a10/ trunk/sys/arm/allwinner/a10/a10_intc.c trunk/sys/arm/allwinner/a10/a10_padconf.c trunk/sys/arm/allwinner/a10/files.a10 Added: trunk/sys/arm/allwinner/a10/a10_intc.c =================================================================== --- trunk/sys/arm/allwinner/a10/a10_intc.c (rev 0) +++ trunk/sys/arm/allwinner/a10/a10_intc.c 2020-03-06 17:15:06 UTC (rev 12398) @@ -0,0 +1,436 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2012 Ganbold Tsagaankhuu + * Copyright (c) 2016 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10/a10_intc.c 331182 2018-03-19 06:40:11Z eadler $"); + +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef INTRNG +#include +#endif +#include +#include + +#include +#include +#include +#include + +#ifdef INTRNG +#include "pic_if.h" +#endif + +/** + * Interrupt controller registers + * + */ +#define SW_INT_VECTOR_REG 0x00 +#define SW_INT_BASE_ADR_REG 0x04 +#define SW_INT_PROTECTION_REG 0x08 +#define SW_INT_NMI_CTRL_REG 0x0c + +#define SW_INT_IRQ_PENDING_REG0 0x10 +#define SW_INT_IRQ_PENDING_REG1 0x14 +#define SW_INT_IRQ_PENDING_REG2 0x18 + +#define SW_INT_FIQ_PENDING_REG0 0x20 +#define SW_INT_FIQ_PENDING_REG1 0x24 +#define SW_INT_FIQ_PENDING_REG2 0x28 + +#define SW_INT_SELECT_REG0 0x30 +#define SW_INT_SELECT_REG1 0x34 +#define SW_INT_SELECT_REG2 0x38 + +#define SW_INT_ENABLE_REG0 0x40 +#define SW_INT_ENABLE_REG1 0x44 +#define SW_INT_ENABLE_REG2 0x48 + +#define SW_INT_MASK_REG0 0x50 +#define SW_INT_MASK_REG1 0x54 +#define SW_INT_MASK_REG2 0x58 + +#define SW_INT_IRQNO_ENMI 0 + +#define A10_INTR_MAX_NIRQS 81 + +#define SW_INT_IRQ_PENDING_REG(_b) (0x10 + ((_b) * 4)) +#define SW_INT_FIQ_PENDING_REG(_b) (0x20 + ((_b) * 4)) +#define SW_INT_SELECT_REG(_b) (0x30 + ((_b) * 4)) +#define SW_INT_ENABLE_REG(_b) (0x40 + ((_b) * 4)) +#define SW_INT_MASK_REG(_b) (0x50 + ((_b) * 4)) + +#ifdef INTRNG +struct a10_intr_irqsrc { + struct intr_irqsrc isrc; + u_int irq; +}; +#endif + +struct a10_aintc_softc { + device_t sc_dev; + struct resource * aintc_res; + bus_space_tag_t aintc_bst; + bus_space_handle_t aintc_bsh; + struct mtx mtx; +#ifdef INTRNG + struct a10_intr_irqsrc isrcs[A10_INTR_MAX_NIRQS]; +#endif +}; + +#define aintc_read_4(sc, reg) \ + bus_space_read_4(sc->aintc_bst, sc->aintc_bsh, reg) +#define aintc_write_4(sc, reg, val) \ + bus_space_write_4(sc->aintc_bst, sc->aintc_bsh, reg, val) + +static __inline void +a10_intr_eoi(struct a10_aintc_softc *sc, u_int irq) +{ + + if (irq != SW_INT_IRQNO_ENMI) + return; + mtx_lock_spin(&sc->mtx); + aintc_write_4(sc, SW_INT_IRQ_PENDING_REG(0), + (1 << SW_INT_IRQNO_ENMI)); + mtx_unlock_spin(&sc->mtx); +} + +static void +a10_intr_unmask(struct a10_aintc_softc *sc, u_int irq) +{ + uint32_t bit, block, value; + + bit = (irq % 32); + block = (irq / 32); + + mtx_lock_spin(&sc->mtx); + value = aintc_read_4(sc, SW_INT_ENABLE_REG(block)); + value |= (1 << bit); + aintc_write_4(sc, SW_INT_ENABLE_REG(block), value); + + value = aintc_read_4(sc, SW_INT_MASK_REG(block)); + value &= ~(1 << bit); + aintc_write_4(sc, SW_INT_MASK_REG(block), value); + mtx_unlock_spin(&sc->mtx); +} + +static void +a10_intr_mask(struct a10_aintc_softc *sc, u_int irq) +{ + uint32_t bit, block, value; + + bit = (irq % 32); + block = (irq / 32); + + mtx_lock_spin(&sc->mtx); + value = aintc_read_4(sc, SW_INT_ENABLE_REG(block)); + value &= ~(1 << bit); + aintc_write_4(sc, SW_INT_ENABLE_REG(block), value); + + value = aintc_read_4(sc, SW_INT_MASK_REG(block)); + value |= (1 << bit); + aintc_write_4(sc, SW_INT_MASK_REG(block), value); + mtx_unlock_spin(&sc->mtx); +} + +static int +a10_pending_irq(struct a10_aintc_softc *sc) +{ + uint32_t value; + int i, b; + + for (i = 0; i < 3; i++) { + value = aintc_read_4(sc, SW_INT_IRQ_PENDING_REG(i)); + if (value == 0) + continue; + for (b = 0; b < 32; b++) + if (value & (1 << b)) { + return (i * 32 + b); + } + } + + return (-1); +} + +#ifndef INTRNG + +static struct a10_aintc_softc *a10_aintc_sc = NULL; + +int +arm_get_next_irq(int last_irq) +{ + return (a10_pending_irq(a10_aintc_sc)); +} + +void +arm_mask_irq(uintptr_t irq) +{ + a10_intr_mask(a10_aintc_sc, irq); +} + +void +arm_unmask_irq(uintptr_t irq) +{ + a10_intr_unmask(a10_aintc_sc, irq); + a10_intr_eoi(a10_aintc_sc, irq); +} + +#else /* INTRNG */ + +static int +a10_intr(void *arg) +{ + struct a10_aintc_softc *sc = arg; + u_int irq; + + irq = a10_pending_irq(sc); + if (irq == -1 || irq > A10_INTR_MAX_NIRQS) { + device_printf(sc->sc_dev, "Spurious interrupt %d\n", irq); + return (FILTER_HANDLED); + } + + while (irq != -1) { + if (irq > A10_INTR_MAX_NIRQS) { + device_printf(sc->sc_dev, "Spurious interrupt %d\n", + irq); + return (FILTER_HANDLED); + } + if (intr_isrc_dispatch(&sc->isrcs[irq].isrc, + curthread->td_intr_frame) != 0) { + a10_intr_mask(sc, irq); + a10_intr_eoi(sc, irq); + device_printf(sc->sc_dev, + "Stray interrupt %d disabled\n", irq); + } + + arm_irq_memory_barrier(irq); + irq = a10_pending_irq(sc); + } + + return (FILTER_HANDLED); +} + +static int +a10_intr_pic_attach(struct a10_aintc_softc *sc) +{ + struct intr_pic *pic; + int error; + uint32_t irq; + const char *name; + intptr_t xref; + + name = device_get_nameunit(sc->sc_dev); + for (irq = 0; irq < A10_INTR_MAX_NIRQS; irq++) { + sc->isrcs[irq].irq = irq; + + error = intr_isrc_register(&sc->isrcs[irq].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)); + pic = intr_pic_register(sc->sc_dev, xref); + if (pic == NULL) + return (ENXIO); + + return (intr_pic_claim_root(sc->sc_dev, xref, a10_intr, sc, 0)); +} + +static void +a10_intr_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct a10_aintc_softc *sc; + u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq; + + sc = device_get_softc(dev); + arm_irq_memory_barrier(irq); + a10_intr_unmask(sc, irq); +} + +static void +a10_intr_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct a10_aintc_softc *sc; + u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq; + + sc = device_get_softc(dev); + a10_intr_mask(sc, irq); +} + +static int +a10_intr_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct intr_map_data_fdt *daf; + struct a10_aintc_softc *sc; + + if (data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + + daf = (struct intr_map_data_fdt *)data; + if (daf->ncells != 1 || daf->cells[0] >= A10_INTR_MAX_NIRQS) + return (EINVAL); + + sc = device_get_softc(dev); + *isrcp = &sc->isrcs[daf->cells[0]].isrc; + return (0); +} + +static void +a10_intr_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct a10_aintc_softc *sc = device_get_softc(dev); + u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq; + + a10_intr_mask(sc, irq); + a10_intr_eoi(sc, irq); +} + +static void +a10_intr_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + + a10_intr_enable_intr(dev, isrc); +} + +static void +a10_intr_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct a10_aintc_softc *sc = device_get_softc(dev); + u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq; + + a10_intr_eoi(sc, irq); +} + +#endif /* INTRNG */ + +static int +a10_aintc_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-ic")) + return (ENXIO); + device_set_desc(dev, "A10 AINTC Interrupt Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +a10_aintc_attach(device_t dev) +{ + struct a10_aintc_softc *sc = device_get_softc(dev); + int rid = 0; + int i; + sc->sc_dev = dev; + +#ifndef INTRNG + if (a10_aintc_sc) + goto error; + + a10_aintc_sc = sc; +#endif + + sc->aintc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &rid, RF_ACTIVE); + if (!sc->aintc_res) { + device_printf(dev, "could not allocate resource\n"); + goto error; + } + + sc->aintc_bst = rman_get_bustag(sc->aintc_res); + sc->aintc_bsh = rman_get_bushandle(sc->aintc_res); + + mtx_init(&sc->mtx, "A10 AINTC lock", "", MTX_SPIN); + + /* Disable & clear all interrupts */ + for (i = 0; i < 3; i++) { + aintc_write_4(sc, SW_INT_ENABLE_REG(i), 0); + aintc_write_4(sc, SW_INT_MASK_REG(i), 0xffffffff); + } + /* enable protection mode*/ + aintc_write_4(sc, SW_INT_PROTECTION_REG, 0x01); + + /* config the external interrupt source type*/ + aintc_write_4(sc, SW_INT_NMI_CTRL_REG, 0x00); + +#ifdef INTRNG + if (a10_intr_pic_attach(sc) != 0) { + device_printf(dev, "could not attach PIC\n"); + return (ENXIO); + } +#endif + + return (0); + +error: + bus_release_resource(dev, SYS_RES_MEMORY, rid, + sc->aintc_res); + return (ENXIO); +} + +static device_method_t a10_aintc_methods[] = { + DEVMETHOD(device_probe, a10_aintc_probe), + DEVMETHOD(device_attach, a10_aintc_attach), +#ifdef INTRNG + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, a10_intr_disable_intr), + DEVMETHOD(pic_enable_intr, a10_intr_enable_intr), + DEVMETHOD(pic_map_intr, a10_intr_map_intr), + DEVMETHOD(pic_post_filter, a10_intr_post_filter), + DEVMETHOD(pic_post_ithread, a10_intr_post_ithread), + DEVMETHOD(pic_pre_ithread, a10_intr_pre_ithread), +#endif + { 0, 0 } +}; + +static driver_t a10_aintc_driver = { + "aintc", + a10_aintc_methods, + sizeof(struct a10_aintc_softc), +}; + +static devclass_t a10_aintc_devclass; + +EARLY_DRIVER_MODULE(aintc, simplebus, a10_aintc_driver, a10_aintc_devclass, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_FIRST); Property changes on: trunk/sys/arm/allwinner/a10/a10_intc.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/a10/a10_padconf.c =================================================================== --- trunk/sys/arm/allwinner/a10/a10_padconf.c (rev 0) +++ trunk/sys/arm/allwinner/a10/a10_padconf.c 2020-03-06 17:15:06 UTC (rev 12398) @@ -0,0 +1,232 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10/a10_padconf.c 308274 2016-11-04 00:54:21Z manu $"); + +#include +#include +#include +#include + +#include + +#ifdef SOC_ALLWINNER_A10 + +const static struct allwinner_pins a10_pins[] = { + {"PA0", 0, 0, {"gpio_in", "gpio_out", "emac", "spi1", "uart2", NULL, NULL, NULL}}, + {"PA1", 0, 1, {"gpio_in", "gpio_out", "emac", "spi1", "uart2", NULL, NULL, NULL}}, + {"PA2", 0, 2, {"gpio_in", "gpio_out", "emac", "spi1", "uart2", NULL, NULL, NULL}}, + {"PA3", 0, 3, {"gpio_in", "gpio_out", "emac", "spi1", "uart2", NULL, NULL, NULL}}, + {"PA4", 0, 4, {"gpio_in", "gpio_out", "emac", "spi1", NULL, NULL, NULL, NULL}}, + {"PA5", 0, 5, {"gpio_in", "gpio_out", "emac", "spi3", NULL, NULL, NULL, NULL}}, + {"PA6", 0, 6, {"gpio_in", "gpio_out", "emac", "spi3", NULL, NULL, NULL, NULL}}, + {"PA7", 0, 7, {"gpio_in", "gpio_out", "emac", "spi3", NULL, NULL, NULL, NULL}}, + {"PA8", 0, 8, {"gpio_in", "gpio_out", "emac", "spi3", NULL, NULL, NULL, NULL}}, + {"PA9", 0, 9, {"gpio_in", "gpio_out", "emac", "spi3", NULL, NULL, NULL, NULL}}, + {"PA10", 0, 10, {"gpio_in", "gpio_out", "emac", NULL, "uart1", NULL, NULL, NULL}}, + {"PA11", 0, 11, {"gpio_in", "gpio_out", "emac", NULL, "uart1", NULL, NULL, NULL}}, + {"PA12", 0, 12, {"gpio_in", "gpio_out", "emac", "uart6", "uart1", NULL, NULL, NULL}}, + {"PA13", 0, 13, {"gpio_in", "gpio_out", "emac", "uart6", "uart1", NULL, NULL, NULL}}, + {"PA14", 0, 14, {"gpio_in", "gpio_out", "emac", "uart7", "uart1", NULL, NULL, NULL}}, + {"PA15", 0, 15, {"gpio_in", "gpio_out", "emac", "uart7", "uart1", NULL, NULL, NULL}}, + {"PA16", 0, 16, {"gpio_in", "gpio_out", NULL, "can", "uart1", NULL, NULL, NULL}}, + {"PA17", 0, 17, {"gpio_in", "gpio_out", NULL, "can", "uart1", NULL, NULL, NULL}}, + + {"PB0", 1, 0, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}}, + {"PB1", 1, 1, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}}, + {"PB2", 1, 2, {"gpio_in", "gpio_out", "pwm", NULL, NULL, NULL, NULL, NULL}}, + {"PB3", 1, 3, {"gpio_in", "gpio_out", "ir0", NULL, NULL, NULL, NULL, NULL}}, + {"PB4", 1, 4, {"gpio_in", "gpio_out", "ir0", NULL, NULL, NULL, NULL, NULL}}, + {"PB5", 1, 5, {"gpio_in", "gpio_out", "i2s", "ac97", NULL, NULL, NULL, NULL}}, + {"PB6", 1, 6, {"gpio_in", "gpio_out", "i2s", "ac97", NULL, NULL, NULL, NULL}}, + {"PB7", 1, 7, {"gpio_in", "gpio_out", "i2s", "ac97", NULL, NULL, NULL, NULL}}, + {"PB8", 1, 8, {"gpio_in", "gpio_out", "i2s", "ac97", NULL, NULL, NULL, NULL}}, + {"PB9", 1, 9, {"gpio_in", "gpio_out", "i2s", NULL, NULL, NULL, NULL, NULL}}, + {"PB10", 1, 10, {"gpio_in", "gpio_out", "i2s", NULL, NULL, NULL, NULL, NULL}}, + {"PB11", 1, 11, {"gpio_in", "gpio_out", "i2s", NULL, NULL, NULL, NULL, NULL}}, + {"PB12", 1, 12, {"gpio_in", "gpio_out", "i2s", "ac97", NULL, NULL, NULL, NULL}}, + {"PB13", 1, 13, {"gpio_in", "gpio_out", "spi2", NULL, NULL, NULL, NULL, NULL}}, + {"PB14", 1, 14, {"gpio_in", "gpio_out", "spi2", "jtag", NULL, NULL, NULL, NULL}}, + {"PB15", 1, 15, {"gpio_in", "gpio_out", "spi2", "jtag", NULL, NULL, NULL, NULL}}, + {"PB16", 1, 16, {"gpio_in", "gpio_out", "spi2", "jtag", NULL, NULL, NULL, NULL}}, + {"PB17", 1, 17, {"gpio_in", "gpio_out", "spi2", "jtag", NULL, NULL, NULL, NULL}}, + {"PB18", 1, 18, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}}, + {"PB19", 1, 19, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}}, + {"PB20", 1, 20, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}}, + {"PB21", 1, 21, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}}, + {"PB22", 1, 22, {"gpio_in", "gpio_out", "uart0", "ir1", NULL, NULL, NULL, NULL}}, + {"PB23", 1, 23, {"gpio_in", "gpio_out", "uart0", "ir1", NULL, NULL, NULL, NULL}}, + + {"PC0", 2, 0, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}}, + {"PC1", 2, 1, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}}, + {"PC2", 2, 2, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}}, + {"PC3", 2, 3, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}}, + {"PC4", 2, 4, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}}, + {"PC5", 2, 5, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}}, + {"PC6", 2, 6, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC7", 2, 7, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC8", 2, 8, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC9", 2, 9, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC10", 2, 10, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC11", 2, 11, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC12", 2, 12, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}}, + {"PC13", 2, 13, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}}, + {"PC14", 2, 14, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}}, + {"PC15", 2, 15, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}}, + {"PC16", 2, 16, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}}, + {"PC17", 2, 17, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}}, + {"PC18", 2, 18, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}}, + {"PC19", 2, 19, {"gpio_in", "gpio_out", "nand", "spi2", NULL, NULL, NULL, NULL}}, + {"PC20", 2, 20, {"gpio_in", "gpio_out", "nand", "spi2", NULL, NULL, NULL, NULL}}, + {"PC21", 2, 21, {"gpio_in", "gpio_out", "nand", "spi2", NULL, NULL, NULL, NULL}}, + {"PC22", 2, 22, {"gpio_in", "gpio_out", "nand", "spi2", NULL, NULL, NULL, NULL}}, + {"PC23", 2, 23, {"gpio_in", "gpio_out", "spi0", NULL, NULL, NULL, NULL, NULL}}, + {"PC24", 2, 24, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}}, + + {"PD0", 3, 0, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD1", 3, 1, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD2", 3, 2, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD3", 3, 3, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD4", 3, 4, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD5", 3, 5, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD6", 3, 6, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD7", 3, 7, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD8", 3, 8, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD9", 3, 9, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD10", 3, 10, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD11", 3, 11, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD12", 3, 12, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD13", 3, 13, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD14", 3, 14, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD15", 3, 15, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD16", 3, 16, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD17", 3, 17, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD18", 3, 18, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD19", 3, 19, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD20", 3, 20, {"gpio_in", "gpio_out", "lcd0", "csi1", NULL, NULL, NULL, NULL}}, + {"PD21", 3, 21, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}}, + {"PD22", 3, 22, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}}, + {"PD23", 3, 23, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}}, + {"PD24", 3, 24, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}}, + {"PD25", 3, 25, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}}, + {"PD26", 3, 26, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}}, + {"PD27", 3, 27, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}}, + + {"PE0", 4, 0, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE1", 4, 1, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE2", 4, 2, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE3", 4, 3, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE4", 4, 4, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE5", 4, 5, {"gpio_in", "gpio_out", "ts0", "csi0", "sim", NULL, NULL, NULL}}, + {"PE6", 4, 6, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE7", 4, 7, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE8", 4, 8, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE9", 4, 9, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE10", 4, 10, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE11", 4, 11, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + + {"PF0", 5, 0, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + {"PF1", 5, 1, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + {"PF2", 5, 2, {"gpio_in", "gpio_out", "mmc0", NULL, "uart0", NULL, NULL, NULL}}, + {"PF3", 5, 3, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + {"PF4", 5, 4, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + {"PF5", 5, 5, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + + {"PG0", 6, 0, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", NULL, NULL, NULL}}, + {"PG1", 6, 1, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", NULL, NULL, NULL}}, + {"PG2", 6, 2, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", NULL, NULL, NULL}}, + {"PG3", 6, 3, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", NULL, NULL, NULL}}, + {"PG4", 6, 4, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", "csi0", NULL, NULL}}, + {"PG5", 6, 5, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", "csi0", NULL, NULL}}, + {"PG6", 6, 6, {"gpio_in", "gpio_out", "ts1", "csi1", "uart3", "csi0", NULL, NULL}}, + {"PG7", 6, 7, {"gpio_in", "gpio_out", "ts1", "csi1", "uart3", "csi0", NULL, NULL}}, + {"PG8", 6, 8, {"gpio_in", "gpio_out", "ts1", "csi1", "uart3", "csi0", NULL, NULL}}, + {"PG9", 6, 9, {"gpio_in", "gpio_out", "ts1", "csi1", "uart3", "csi0", NULL, NULL}}, + {"PG10", 6, 10, {"gpio_in", "gpio_out", "ts1", "csi1", "uart4", "csi0", NULL, NULL}}, + {"PG11", 6, 11, {"gpio_in", "gpio_out", "ts1", "csi1", "uart4", "csi0", NULL, NULL}}, + + {"PH0", 7, 0, {"gpio_in", "gpio_out", "lcd1", "pata", "uart3", NULL, "eint", "csi1"}}, + {"PH1", 7, 1, {"gpio_in", "gpio_out", "lcd1", "pata", "uart3", NULL, "eint", "csi1"}}, + {"PH2", 7, 2, {"gpio_in", "gpio_out", "lcd1", "pata", "uart3", NULL, "eint", "csi1"}}, + {"PH3", 7, 3, {"gpio_in", "gpio_out", "lcd1", "pata", "uart3", NULL, "eint", "csi1"}}, + {"PH4", 7, 4, {"gpio_in", "gpio_out", "lcd1", "pata", "uart4", NULL, "eint", "csi1"}}, + {"PH5", 7, 5, {"gpio_in", "gpio_out", "lcd1", "pata", "uart4", NULL, "eint", "csi1"}}, + {"PH6", 7, 6, {"gpio_in", "gpio_out", "lcd1", "pata", "uart5", "ms", "eint", "csi1"}}, + {"PH7", 7, 7, {"gpio_in", "gpio_out", "lcd1", "pata", "uart5", "ms", "eint", "csi1"}}, + {"PH8", 7, 8, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "ms", "eint", "csi1"}}, + {"PH9", 7, 9, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "ms", "eint", "csi1"}}, + {"PH10", 7, 10, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "ms", "eint", "csi1"}}, + {"PH11", 7, 11, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "ms", "eint", "csi1"}}, + {"PH12", 7, 12, {"gpio_in", "gpio_out", "lcd1", "pata", "ps2", NULL, "eint", "csi1"}}, + {"PH13", 7, 13, {"gpio_in", "gpio_out", "lcd1", "pata", "ps2", "sim", "eint", "csi1"}}, + {"PH14", 7, 14, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "sim", "eint", "csi1"}}, + {"PH15", 7, 15, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "sim", "eint", "csi1"}}, + {"PH16", 7, 16, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", NULL, "eint", "csi1"}}, + {"PH17", 7, 17, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "sim", "eint", "csi1"}}, + {"PH18", 7, 18, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "sim", "eint", "csi1"}}, + {"PH19", 7, 19, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "sim", "eint", "csi1"}}, + {"PH20", 7, 20, {"gpio_in", "gpio_out", "lcd1", "pata", "can", NULL, "eint", "csi1"}}, + {"PH21", 7, 21, {"gpio_in", "gpio_out", "lcd1", "pata", "can", NULL, "eint", "csi1"}}, + {"PH22", 7, 22, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "mmc1", NULL, "csi1"}}, + {"PH23", 7, 23, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "mmc1", NULL, "csi1"}}, + {"PH24", 7, 24, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "mmc1", NULL, "csi1"}}, + {"PH25", 7, 25, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "mmc1", NULL, "csi1"}}, + {"PH26", 7, 26, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "mmc1", NULL, "csi1"}}, + {"PH27", 7, 27, {"gpio_in", "gpio_out", "lcd1", "pata", "keypad", "mmc1", NULL, "csi1"}}, + + {"PI0", 8, 0, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PI1", 8, 1, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PI2", 8, 2, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PI3", 8, 3, {"gpio_in", "gpio_out", "pwm", NULL, NULL, NULL, NULL, NULL}}, + {"PI4", 8, 4, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}}, + {"PI5", 8, 5, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}}, + {"PI6", 8, 6, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}}, + {"PI7", 8, 7, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}}, + {"PI8", 8, 8, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}}, + {"PI9", 8, 9, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}}, + {"PI10", 8, 10, {"gpio_in", "gpio_out", "spi0", "uart5", NULL, NULL, "eint", NULL}}, + {"PI11", 8, 11, {"gpio_in", "gpio_out", "spi0", "uart5", NULL, NULL, "eint", NULL}}, + {"PI12", 8, 12, {"gpio_in", "gpio_out", "spi0", "uart6", NULL, NULL, "eint", NULL}}, + {"PI13", 8, 13, {"gpio_in", "gpio_out", "spi0", "uart6", NULL, NULL, "eint", NULL}}, + {"PI14", 8, 14, {"gpio_in", "gpio_out", "spi0", "ps2", "timer4", NULL, "eint", NULL}}, + {"PI15", 8, 15, {"gpio_in", "gpio_out", "spi1", "ps2", "timer5", NULL, "eint", NULL}}, + {"PI16", 8, 16, {"gpio_in", "gpio_out", "spi1", "uart2", NULL, NULL, "eint", NULL}}, + {"PI17", 8, 17, {"gpio_in", "gpio_out", "spi1", "uart2", NULL, NULL, "eint", NULL}}, + {"PI18", 8, 18, {"gpio_in", "gpio_out", "spi1", "uart2", NULL, NULL, "eint", NULL}}, + {"PI19", 8, 19, {"gpio_in", "gpio_out", "spi1", "uart2", NULL, NULL, "eint", NULL}}, + {"PI20", 8, 20, {"gpio_in", "gpio_out", "ps2", "uart7", "hdmi", NULL, NULL, NULL}}, + {"PI21", 8, 21, {"gpio_in", "gpio_out", "ps2", "uart7", "hdmi", NULL, NULL, NULL}}, +}; + +const struct allwinner_padconf a10_padconf = { + .npins = sizeof(a10_pins) / sizeof(struct allwinner_pins), + .pins = a10_pins, +}; + +#endif /* SOC_ALLWINNER_A10 */ Property changes on: trunk/sys/arm/allwinner/a10/a10_padconf.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/a10/files.a10 =================================================================== --- trunk/sys/arm/allwinner/a10/files.a10 (rev 0) +++ trunk/sys/arm/allwinner/a10/files.a10 2020-03-06 17:15:06 UTC (rev 12398) @@ -0,0 +1,4 @@ +# $MidnightBSD$ + +arm/allwinner/a10/a10_intc.c standard +arm/allwinner/a10/a10_padconf.c standard Property changes on: trunk/sys/arm/allwinner/a10/files.a10 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property From laffer1 at midnightbsd.org Fri Mar 6 12:15:36 2020 From: laffer1 at midnightbsd.org (laffer1 at midnightbsd.org) Date: Fri, 6 Mar 2020 12:15:36 -0500 (EST) Subject: [Midnightbsd-cvs] src [12399] trunk/sys/arm/allwinner/a13: sync with freebsd 11 stable Message-ID: <202003061715.026HFaEp015406@stargazer.midnightbsd.org> Revision: 12399 http://svnweb.midnightbsd.org/src/?rev=12399 Author: laffer1 Date: 2020-03-06 12:15:35 -0500 (Fri, 06 Mar 2020) Log Message: ----------- sync with freebsd 11 stable Added Paths: ----------- trunk/sys/arm/allwinner/a13/ trunk/sys/arm/allwinner/a13/a13_padconf.c trunk/sys/arm/allwinner/a13/files.a13 Added: trunk/sys/arm/allwinner/a13/a13_padconf.c =================================================================== --- trunk/sys/arm/allwinner/a13/a13_padconf.c (rev 0) +++ trunk/sys/arm/allwinner/a13/a13_padconf.c 2020-03-06 17:15:35 UTC (rev 12399) @@ -0,0 +1,130 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a13/a13_padconf.c 302472 2016-07-08 23:38:25Z manu $"); + +#include +#include +#include +#include + +#include + +#ifdef SOC_ALLWINNER_A13 + +const static struct allwinner_pins a13_pins[] = { + {"PB0", 1, 0, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}}, + {"PB1", 1, 1, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}}, + {"PB2", 1, 2, {"gpio_in", "gpio_out", "pwm", NULL, NULL, "eint", NULL, NULL}}, + {"PB3", 1, 3, {"gpio_in", "gpio_out", "ir0", NULL, NULL, "eint", NULL, NULL}}, + {"PB4", 1, 4, {"gpio_in", "gpio_out", "ir0", NULL, NULL, "eint", NULL, NULL}}, + {"PB10", 1, 10, {"gpio_in", "gpio_out", "spi2", NULL, NULL, "eint", NULL, NULL}}, + {"PB15", 1, 15, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}}, + {"PB16", 1, 16, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}}, + {"PB17", 1, 17, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}}, + {"PB18", 1, 18, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}}, + + {"PC0", 2, 0, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}}, + {"PC1", 2, 1, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}}, + {"PC2", 2, 2, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}}, + {"PC3", 2, 3, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}}, + {"PC4", 2, 4, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}}, + {"PC5", 2, 5, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}}, + {"PC6", 2, 6, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC7", 2, 7, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC8", 2, 8, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC9", 2, 9, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC10", 2, 10, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC11", 2, 11, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC12", 2, 12, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC13", 2, 13, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC14", 2, 14, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC15", 2, 15, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC19", 2, 19, {"gpio_in", "gpio_out", "nand", NULL, "uart3", NULL, NULL, NULL}}, + + {"PD2", 3, 2, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD3", 3, 3, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD4", 3, 4, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD5", 3, 5, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD6", 3, 6, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD7", 3, 7, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD10", 3, 10, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD11", 3, 11, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD12", 3, 12, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD13", 3, 13, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD14", 3, 14, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD15", 3, 15, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD18", 3, 18, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD19", 3, 19, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD20", 3, 20, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD21", 3, 21, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD22", 3, 22, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD23", 3, 23, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD24", 3, 24, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD25", 3, 25, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD26", 3, 26, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD27", 3, 27, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + + {"PE0", 4, 0, {"gpio_in", NULL, NULL, "csi0", "spi2", NULL, "eint", NULL}}, + {"PE1", 4, 1, {"gpio_in", NULL, NULL, "csi0", "spi2", NULL, NULL, NULL}}, + {"PE2", 4, 2, {"gpio_in", NULL, NULL, "csi0", "spi2", NULL, NULL, NULL}}, + {"PE3", 4, 3, {"gpio_in", "gpio_out", NULL, "csi0", "spi2", NULL, NULL, NULL}}, + {"PE4", 4, 4, {"gpio_in", "gpio_out", NULL, "csi0", "mmc2", NULL, NULL, NULL}}, + {"PE5", 4, 5, {"gpio_in", "gpio_out", NULL, "csi0", "mmc2", NULL, NULL, NULL}}, + {"PE6", 4, 6, {"gpio_in", "gpio_out", NULL, "csi0", "mmc2", NULL, NULL, NULL}}, + {"PE7", 4, 7, {"gpio_in", "gpio_out", NULL, "csi0", "mmc2", NULL, NULL, NULL}}, + {"PE8", 4, 8, {"gpio_in", "gpio_out", NULL, "csi0", "mmc2", NULL, NULL, NULL}}, + {"PE9", 4, 9, {"gpio_in", "gpio_out", NULL, "csi0", "mmc2", NULL, NULL, NULL}}, + {"PE10", 4, 10, {"gpio_in", "gpio_out", NULL, "csi0", "uart1", NULL, NULL, NULL}}, + {"PE11", 4, 11, {"gpio_in", "gpio_out", NULL, "csi0", "uart1", NULL, NULL, NULL}}, + + {"PF0", 5, 0, {"gpio_in", "gpio_out", "mmc0", NULL, NULL, NULL, NULL, NULL}}, + {"PF1", 5, 1, {"gpio_in", "gpio_out", "mmc0", NULL, NULL, NULL, NULL, NULL}}, + {"PF2", 5, 2, {"gpio_in", "gpio_out", "mmc0", NULL, NULL, NULL, NULL, NULL}}, + {"PF3", 5, 3, {"gpio_in", "gpio_out", "mmc0", NULL, NULL, NULL, NULL, NULL}}, + {"PF4", 5, 4, {"gpio_in", "gpio_out", "mmc0", NULL, NULL, NULL, NULL, NULL}}, + {"PF5", 5, 5, {"gpio_in", "gpio_out", "mmc0", NULL, NULL, NULL, NULL, NULL}}, + + {"PG0", 6, 0, {"gpio_in", NULL, NULL, NULL, NULL, NULL, "eint", NULL}}, + {"PG1", 6, 1, {"gpio_in", NULL, NULL, NULL, NULL, NULL, "eint", NULL}}, + {"PG2", 6, 2, {"gpio_in", NULL, NULL, NULL, NULL, NULL, "eint", NULL}}, + {"PG3", 6, 3, {"gpio_in", "gpio_out", "mmc1", NULL, "uart1", NULL, "eint", NULL}}, + {"PG4", 6, 4, {"gpio_in", "gpio_out", "mmc1", NULL, "uart1", NULL, "eint", NULL}}, + {"PG9", 6, 9, {"gpio_in", "gpio_out", "spi1", "uart3", NULL, NULL, "eint", NULL}}, + {"PG10", 6, 10, {"gpio_in", "gpio_out", "spi1", "uart3", NULL, NULL, "eint", NULL}}, + {"PG11", 6, 11, {"gpio_in", "gpio_out", "spi1", "uart3", NULL, NULL, "eint", NULL}}, + {"PG12", 6, 12, {"gpio_in", "gpio_out", "spi1", "uart3", NULL, NULL, "eint", NULL}}, +}; + +const struct allwinner_padconf a13_padconf = { + .npins = sizeof(a13_pins) / sizeof(struct allwinner_pins), + .pins = a13_pins, +}; + +#endif /* SOC_ALLWINNER_A13 */ Property changes on: trunk/sys/arm/allwinner/a13/a13_padconf.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/a13/files.a13 =================================================================== --- trunk/sys/arm/allwinner/a13/files.a13 (rev 0) +++ trunk/sys/arm/allwinner/a13/files.a13 2020-03-06 17:15:35 UTC (rev 12399) @@ -0,0 +1,3 @@ +# $MidnightBSD$ + +arm/allwinner/a13/a13_padconf.c standard Property changes on: trunk/sys/arm/allwinner/a13/files.a13 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property From laffer1 at midnightbsd.org Fri Mar 6 12:15:48 2020 From: laffer1 at midnightbsd.org (laffer1 at midnightbsd.org) Date: Fri, 6 Mar 2020 12:15:48 -0500 (EST) Subject: [Midnightbsd-cvs] src [12400] trunk/sys/arm/allwinner/clk: sync with freebsd 11 stable Message-ID: <202003061715.026HFmwk015447@stargazer.midnightbsd.org> Revision: 12400 http://svnweb.midnightbsd.org/src/?rev=12400 Author: laffer1 Date: 2020-03-06 12:15:48 -0500 (Fri, 06 Mar 2020) Log Message: ----------- sync with freebsd 11 stable Added Paths: ----------- trunk/sys/arm/allwinner/clk/ trunk/sys/arm/allwinner/clk/aw_ahbclk.c trunk/sys/arm/allwinner/clk/aw_apbclk.c trunk/sys/arm/allwinner/clk/aw_axiclk.c trunk/sys/arm/allwinner/clk/aw_codecclk.c trunk/sys/arm/allwinner/clk/aw_cpuclk.c trunk/sys/arm/allwinner/clk/aw_cpusclk.c trunk/sys/arm/allwinner/clk/aw_debeclk.c trunk/sys/arm/allwinner/clk/aw_gate.c trunk/sys/arm/allwinner/clk/aw_gmacclk.c trunk/sys/arm/allwinner/clk/aw_hdmiclk.c trunk/sys/arm/allwinner/clk/aw_lcdclk.c trunk/sys/arm/allwinner/clk/aw_mmcclk.c trunk/sys/arm/allwinner/clk/aw_modclk.c trunk/sys/arm/allwinner/clk/aw_oscclk.c trunk/sys/arm/allwinner/clk/aw_pll.c trunk/sys/arm/allwinner/clk/aw_usbclk.c Added: trunk/sys/arm/allwinner/clk/aw_ahbclk.c =================================================================== --- trunk/sys/arm/allwinner/clk/aw_ahbclk.c (rev 0) +++ trunk/sys/arm/allwinner/clk/aw_ahbclk.c 2020-03-06 17:15:48 UTC (rev 12400) @@ -0,0 +1,386 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/clk/aw_ahbclk.c 309771 2016-12-09 21:17:40Z manu $ + */ + +/* + * Allwinner AHB clock + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_ahbclk.c 309771 2016-12-09 21:17:40Z manu $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "clkdev_if.h" + +#define A10_AHB_CLK_DIV_RATIO (0x3 << 4) +#define A10_AHB_CLK_DIV_RATIO_SHIFT 4 + +#define A13_AHB_CLK_SRC_SEL (0x3 << 6) +#define A13_AHB_CLK_SRC_SEL_MAX 3 +#define A13_AHB_CLK_SRC_SEL_SHIFT 6 + +#define A31_AHB1_PRE_DIV (0x3 << 6) +#define A31_AHB1_PRE_DIV_SHIFT 6 +#define A31_AHB1_CLK_SRC_SEL (0x3 << 12) +#define A31_AHB1_CLK_SRC_SEL_PLL6 3 +#define A31_AHB1_CLK_SRC_SEL_MAX 3 +#define A31_AHB1_CLK_SRC_SEL_SHIFT 12 + +#define A83T_AHB1_CLK_SRC_SEL (0x3 << 12) +#define A83T_AHB1_CLK_SRC_SEL_ISPLL(x) ((x) & 0x2) +#define A83T_AHB1_CLK_SRC_SEL_MAX 3 +#define A83T_AHB1_CLK_SRC_SEL_SHIFT 12 +#define A83T_AHB1_PRE_DIV (0x3 << 6) +#define A83T_AHB1_PRE_DIV_SHIFT 6 +#define A83T_AHB1_CLK_DIV_RATIO (0x3 << 4) +#define A83T_AHB1_CLK_DIV_RATIO_SHIFT 4 + +#define H3_AHB2_CLK_CFG (0x3 << 0) +#define H3_AHB2_CLK_CFG_SHIFT 0 +#define H3_AHB2_CLK_CFG_AHB1 0 +#define H3_AHB2_CLK_CFG_PLL_PERIPH_DIV2 1 +#define H3_AHB2_CLK_CFG_MAX 1 + +enum aw_ahbclk_type { + AW_A10_AHB = 1, + AW_A13_AHB, + AW_A31_AHB1, + AW_A83T_AHB1, + AW_H3_AHB2, +}; + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-ahb-clk", AW_A10_AHB }, + { "allwinner,sun5i-a13-ahb-clk", AW_A13_AHB }, + { "allwinner,sun6i-a31-ahb1-clk", AW_A31_AHB1 }, + { "allwinner,sun8i-a83t-ahb1-clk", AW_A83T_AHB1 }, + { "allwinner,sun8i-h3-ahb2-clk", AW_H3_AHB2 }, + { NULL, 0 } +}; + +struct aw_ahbclk_sc { + device_t clkdev; + bus_addr_t reg; + enum aw_ahbclk_type type; +}; + +#define AHBCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define AHBCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) +#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) +#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) + +static int +aw_ahbclk_init(struct clknode *clk, device_t dev) +{ + struct aw_ahbclk_sc *sc; + uint32_t val, index; + + sc = clknode_get_softc(clk); + + switch (sc->type) { + case AW_A10_AHB: + index = 0; + break; + case AW_A13_AHB: + DEVICE_LOCK(sc); + AHBCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + index = (val & A13_AHB_CLK_SRC_SEL) >> + A13_AHB_CLK_SRC_SEL_SHIFT; + break; + case AW_A31_AHB1: + DEVICE_LOCK(sc); + AHBCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + index = (val & A31_AHB1_CLK_SRC_SEL) >> + A31_AHB1_CLK_SRC_SEL_SHIFT; + break; + case AW_A83T_AHB1: + DEVICE_LOCK(sc); + AHBCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + index = (val & A83T_AHB1_CLK_SRC_SEL) >> + A83T_AHB1_CLK_SRC_SEL_SHIFT; + break; + case AW_H3_AHB2: + /* Set source to PLL_PERIPH/2 */ + index = H3_AHB2_CLK_CFG_PLL_PERIPH_DIV2; + DEVICE_LOCK(sc); + AHBCLK_READ(sc, &val); + val &= ~H3_AHB2_CLK_CFG; + val |= (index << H3_AHB2_CLK_CFG_SHIFT); + AHBCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + break; + default: + return (ENXIO); + } + + clknode_init_parent_idx(clk, index); + return (0); +} + +static int +aw_ahbclk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct aw_ahbclk_sc *sc; + uint32_t val, src_sel, div, pre_div; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + AHBCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + switch (sc->type) { + case AW_A31_AHB1: + div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >> + A10_AHB_CLK_DIV_RATIO_SHIFT); + src_sel = (val & A31_AHB1_CLK_SRC_SEL) >> + A31_AHB1_CLK_SRC_SEL_SHIFT; + if (src_sel == A31_AHB1_CLK_SRC_SEL_PLL6) + pre_div = ((val & A31_AHB1_PRE_DIV) >> + A31_AHB1_PRE_DIV_SHIFT) + 1; + else + pre_div = 1; + break; + case AW_A83T_AHB1: + div = 1 << ((val & A83T_AHB1_CLK_DIV_RATIO) >> + A83T_AHB1_CLK_DIV_RATIO_SHIFT); + src_sel = (val & A83T_AHB1_CLK_SRC_SEL) >> + A83T_AHB1_CLK_SRC_SEL_SHIFT; + if (A83T_AHB1_CLK_SRC_SEL_ISPLL(src_sel)) + pre_div = ((val & A83T_AHB1_PRE_DIV) >> + A83T_AHB1_PRE_DIV_SHIFT) + 1; + else + pre_div = 1; + break; + case AW_H3_AHB2: + div = pre_div = 1; + break; + default: + div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >> + A10_AHB_CLK_DIV_RATIO_SHIFT); + pre_div = 1; + break; + } + + *freq = *freq / pre_div / div; + + return (0); +} + +static int +aw_ahbclk_set_mux(struct clknode *clk, int index) +{ + struct aw_ahbclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + switch (sc->type) { + case AW_A10_AHB: + if (index != 0) + return (ERANGE); + break; + case AW_A13_AHB: + if (index < 0 || index > A13_AHB_CLK_SRC_SEL_MAX) + return (ERANGE); + DEVICE_LOCK(sc); + AHBCLK_READ(sc, &val); + val &= ~A13_AHB_CLK_SRC_SEL; + val |= (index << A13_AHB_CLK_SRC_SEL_SHIFT); + AHBCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + break; + case AW_A83T_AHB1: + if (index < 0 || index > A83T_AHB1_CLK_SRC_SEL_MAX) + return (ERANGE); + DEVICE_LOCK(sc); + AHBCLK_READ(sc, &val); + val &= ~A83T_AHB1_CLK_SRC_SEL; + val |= (index << A83T_AHB1_CLK_SRC_SEL_SHIFT); + AHBCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + break; + case AW_H3_AHB2: + if (index < 0 || index > H3_AHB2_CLK_CFG) + return (ERANGE); + DEVICE_LOCK(sc); + AHBCLK_READ(sc, &val); + val &= ~H3_AHB2_CLK_CFG; + val |= (index << H3_AHB2_CLK_CFG_SHIFT); + AHBCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + break; + default: + return (ENXIO); + } + + return (0); +} + +static clknode_method_t aw_ahbclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_ahbclk_init), + CLKNODEMETHOD(clknode_recalc_freq, aw_ahbclk_recalc_freq), + CLKNODEMETHOD(clknode_set_mux, aw_ahbclk_set_mux), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_ahbclk_clknode, aw_ahbclk_clknode_class, + aw_ahbclk_clknode_methods, sizeof(struct aw_ahbclk_sc), clknode_class); + +static int +aw_ahbclk_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, "Allwinner AHB Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_ahbclk_attach(device_t dev) +{ + struct clknode_init_def def; + struct aw_ahbclk_sc *sc; + struct clkdom *clkdom; + struct clknode *clk; + clk_t clk_parent; + bus_addr_t paddr; + bus_size_t psize; + phandle_t node; + int error, ncells, i; + + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { + device_printf(dev, "cannot parse 'reg' property\n"); + return (ENXIO); + } + + error = ofw_bus_parse_xref_list_get_length(node, "clocks", + "#clock-cells", &ncells); + if (error != 0) { + device_printf(dev, "cannot get clock count\n"); + return (error); + } + + clkdom = clkdom_create(dev); + + memset(&def, 0, sizeof(def)); + def.id = 1; + def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, + M_WAITOK); + for (i = 0; i < ncells; i++) { + error = clk_get_by_ofw_index(dev, 0, i, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot get clock %d\n", i); + goto fail; + } + def.parent_names[i] = clk_get_name(clk_parent); + clk_release(clk_parent); + } + def.parent_cnt = ncells; + + error = clk_parse_ofw_clk_name(dev, node, &def.name); + if (error != 0) { + device_printf(dev, "cannot parse clock name\n"); + error = ENXIO; + goto fail; + } + + clk = clknode_create(clkdom, &aw_ahbclk_clknode_class, &def); + if (clk == NULL) { + device_printf(dev, "cannot create clknode\n"); + error = ENXIO; + goto fail; + } + sc = clknode_get_softc(clk); + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + sc->reg = paddr; + sc->clkdev = device_get_parent(dev); + + clknode_register(clkdom, clk); + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + error = clk_set_assigned(dev, node); + if (error != 0 && error != ENOENT) { + device_printf(dev, "cannot set assigned parents: %d\n", error); + goto fail; + } + + if (bootverbose) + clkdom_dump(clkdom); + + return (0); + +fail: + return (error); +} + +static device_method_t aw_ahbclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_ahbclk_probe), + DEVMETHOD(device_attach, aw_ahbclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_ahbclk_driver = { + "aw_ahbclk", + aw_ahbclk_methods, + 0 +}; + +static devclass_t aw_ahbclk_devclass; + +EARLY_DRIVER_MODULE(aw_ahbclk, simplebus, aw_ahbclk_driver, + aw_ahbclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: trunk/sys/arm/allwinner/clk/aw_ahbclk.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/clk/aw_apbclk.c =================================================================== --- trunk/sys/arm/allwinner/clk/aw_apbclk.c (rev 0) +++ trunk/sys/arm/allwinner/clk/aw_apbclk.c 2020-03-06 17:15:48 UTC (rev 12400) @@ -0,0 +1,308 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/clk/aw_apbclk.c 308324 2016-11-05 04:17:32Z mmel $ + */ + +/* + * Allwinner APB clock + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_apbclk.c 308324 2016-11-05 04:17:32Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "clkdev_if.h" + +#define A10_APB0_CLK_RATIO (0x3 << 8) +#define A10_APB0_CLK_RATIO_SHIFT 8 +#define A10_APB1_CLK_SRC_SEL (0x3 << 24) +#define A10_APB1_CLK_SRC_SEL_SHIFT 24 +#define A10_APB1_CLK_SRC_SEL_MAX 0x3 +#define A10_APB1_CLK_RAT_N (0x3 << 16) +#define A10_APB1_CLK_RAT_N_SHIFT 16 +#define A10_APB1_CLK_RAT_M (0x1f << 0) +#define A10_APB1_CLK_RAT_M_SHIFT 0 +#define A23_APB0_CLK_RATIO (0x3 << 0) +#define A23_APB0_CLK_RATIO_SHIFT 0 +#define A83T_APB1_CLK_RATIO (0x3 << 8) +#define A83T_APB1_CLK_RATIO_SHIFT 8 + +enum aw_apbclk_type { + AW_A10_APB0 = 1, + AW_A10_APB1, + AW_A23_APB0, + AW_A83T_APB1, +}; + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-apb0-clk", AW_A10_APB0 }, + { "allwinner,sun4i-a10-apb1-clk", AW_A10_APB1 }, + { "allwinner,sun8i-a23-apb0-clk", AW_A23_APB0 }, + { "allwinner,sun8i-a83t-apb1-clk", AW_A83T_APB1 }, + { NULL, 0 } +}; + +struct aw_apbclk_sc { + device_t clkdev; + bus_addr_t reg; + enum aw_apbclk_type type; +}; + +#define APBCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define APBCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) +#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) +#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) + +static int +aw_apbclk_init(struct clknode *clk, device_t dev) +{ + struct aw_apbclk_sc *sc; + uint32_t val, index; + + sc = clknode_get_softc(clk); + + switch (sc->type) { + case AW_A10_APB0: + case AW_A23_APB0: + case AW_A83T_APB1: + index = 0; + break; + case AW_A10_APB1: + DEVICE_LOCK(sc); + APBCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + index = (val & A10_APB1_CLK_SRC_SEL) >> + A10_APB1_CLK_SRC_SEL_SHIFT; + break; + default: + return (ENXIO); + } + + clknode_init_parent_idx(clk, index); + return (0); +} + +static int +aw_apbclk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct aw_apbclk_sc *sc; + uint32_t val, div, m, n; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + APBCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + switch (sc->type) { + case AW_A10_APB0: + div = 1 << ((val & A10_APB0_CLK_RATIO) >> + A10_APB0_CLK_RATIO_SHIFT); + if (div == 1) + div = 2; + *freq = *freq / div; + break; + case AW_A10_APB1: + n = 1 << ((val & A10_APB1_CLK_RAT_N) >> + A10_APB1_CLK_RAT_N_SHIFT); + m = ((val & A10_APB1_CLK_RAT_N) >> + A10_APB1_CLK_RAT_M_SHIFT) + 1; + *freq = *freq / n / m; + break; + case AW_A23_APB0: + div = 1 << ((val & A23_APB0_CLK_RATIO) >> + A23_APB0_CLK_RATIO_SHIFT); + *freq = *freq / div; + break; + case AW_A83T_APB1: + div = ((val & A83T_APB1_CLK_RATIO) >> + A83T_APB1_CLK_RATIO_SHIFT) + 1; + *freq = *freq / div; + break; + default: + return (ENXIO); + } + + return (0); +} + +static int +aw_apbclk_set_mux(struct clknode *clk, int index) +{ + struct aw_apbclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + if (sc->type != AW_A10_APB1) + return (ENXIO); + + if (index < 0 || index > A10_APB1_CLK_SRC_SEL_MAX) + return (ERANGE); + + DEVICE_LOCK(sc); + APBCLK_READ(sc, &val); + val &= ~A10_APB1_CLK_SRC_SEL; + val |= (index << A10_APB1_CLK_SRC_SEL_SHIFT); + APBCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static clknode_method_t aw_apbclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_apbclk_init), + CLKNODEMETHOD(clknode_recalc_freq, aw_apbclk_recalc_freq), + CLKNODEMETHOD(clknode_set_mux, aw_apbclk_set_mux), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_apbclk_clknode, aw_apbclk_clknode_class, + aw_apbclk_clknode_methods, sizeof(struct aw_apbclk_sc), clknode_class); + +static int +aw_apbclk_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, "Allwinner APB Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_apbclk_attach(device_t dev) +{ + struct clknode_init_def def; + struct aw_apbclk_sc *sc; + struct clkdom *clkdom; + struct clknode *clk; + clk_t clk_parent; + bus_addr_t paddr; + bus_size_t psize; + phandle_t node; + int error, ncells, i; + + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { + device_printf(dev, "cannot parse 'reg' property\n"); + return (ENXIO); + } + + error = ofw_bus_parse_xref_list_get_length(node, "clocks", + "#clock-cells", &ncells); + if (error != 0) { + device_printf(dev, "cannot get clock count\n"); + return (error); + } + + clkdom = clkdom_create(dev); + + memset(&def, 0, sizeof(def)); + error = clk_parse_ofw_clk_name(dev, node, &def.name); + if (error != 0) { + device_printf(dev, "cannot parse clock name\n"); + error = ENXIO; + goto fail; + } + def.id = 1; + def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK); + for (i = 0; i < ncells; i++) { + error = clk_get_by_ofw_index(dev, 0, i, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot get clock %d\n", i); + goto fail; + } + def.parent_names[i] = clk_get_name(clk_parent); + clk_release(clk_parent); + } + def.parent_cnt = ncells; + + clk = clknode_create(clkdom, &aw_apbclk_clknode_class, &def); + if (clk == NULL) { + device_printf(dev, "cannot create clknode\n"); + error = ENXIO; + goto fail; + } + + sc = clknode_get_softc(clk); + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + sc->reg = paddr; + sc->clkdev = device_get_parent(dev); + + clknode_register(clkdom, clk); + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + if (bootverbose) + clkdom_dump(clkdom); + + return (0); + +fail: + return (error); +} + +static device_method_t aw_apbclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_apbclk_probe), + DEVMETHOD(device_attach, aw_apbclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_apbclk_driver = { + "aw_apbclk", + aw_apbclk_methods, + 0 +}; + +static devclass_t aw_apbclk_devclass; + +EARLY_DRIVER_MODULE(aw_apbclk, simplebus, aw_apbclk_driver, + aw_apbclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: trunk/sys/arm/allwinner/clk/aw_apbclk.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/clk/aw_axiclk.c =================================================================== --- trunk/sys/arm/allwinner/clk/aw_axiclk.c (rev 0) +++ trunk/sys/arm/allwinner/clk/aw_axiclk.c 2020-03-06 17:15:48 UTC (rev 12400) @@ -0,0 +1,202 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/clk/aw_axiclk.c 308324 2016-11-05 04:17:32Z mmel $ + */ + +/* + * Allwinner AXI clock + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_axiclk.c 308324 2016-11-05 04:17:32Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "clkdev_if.h" + +#define AXI_CLK_DIV_RATIO (0x3 << 0) + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-axi-clk", 1 }, + { NULL, 0 } +}; + +struct aw_axiclk_sc { + device_t clkdev; + bus_addr_t reg; +}; + +#define AXICLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define AXICLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) +#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) +#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) + +static int +aw_axiclk_init(struct clknode *clk, device_t dev) +{ + clknode_init_parent_idx(clk, 0); + return (0); +} + +static int +aw_axiclk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct aw_axiclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + AXICLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + *freq = *freq / ((val & AXI_CLK_DIV_RATIO) + 1); + + return (0); +} + +static clknode_method_t aw_axiclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_axiclk_init), + CLKNODEMETHOD(clknode_recalc_freq, aw_axiclk_recalc_freq), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_axiclk_clknode, aw_axiclk_clknode_class, + aw_axiclk_clknode_methods, sizeof(struct aw_axiclk_sc), clknode_class); + +static int +aw_axiclk_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, "Allwinner AXI Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_axiclk_attach(device_t dev) +{ + struct clknode_init_def def; + struct aw_axiclk_sc *sc; + struct clkdom *clkdom; + struct clknode *clk; + clk_t clk_parent; + bus_addr_t paddr; + bus_size_t psize; + phandle_t node; + int error; + + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { + device_printf(dev, "cannot parse 'reg' property\n"); + return (ENXIO); + } + + clkdom = clkdom_create(dev); + + error = clk_get_by_ofw_index(dev, 0, 0, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot parse clock parent\n"); + return (ENXIO); + } + + memset(&def, 0, sizeof(def)); + error = clk_parse_ofw_clk_name(dev, node, &def.name); + if (error != 0) { + device_printf(dev, "cannot parse clock name\n"); + error = ENXIO; + goto fail; + } + def.id = 1; + def.parent_names = malloc(sizeof(char *), M_OFWPROP, M_WAITOK); + def.parent_names[0] = clk_get_name(clk_parent); + def.parent_cnt = 1; + + clk = clknode_create(clkdom, &aw_axiclk_clknode_class, &def); + if (clk == NULL) { + device_printf(dev, "cannot create clknode\n"); + error = ENXIO; + goto fail; + } + + sc = clknode_get_softc(clk); + sc->reg = paddr; + sc->clkdev = device_get_parent(dev); + + clknode_register(clkdom, clk); + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + if (bootverbose) + clkdom_dump(clkdom); + + return (0); + +fail: + return (error); +} + +static device_method_t aw_axiclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_axiclk_probe), + DEVMETHOD(device_attach, aw_axiclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_axiclk_driver = { + "aw_axiclk", + aw_axiclk_methods, + 0 +}; + +static devclass_t aw_axiclk_devclass; + +EARLY_DRIVER_MODULE(aw_axiclk, simplebus, aw_axiclk_driver, + aw_axiclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: trunk/sys/arm/allwinner/clk/aw_axiclk.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/clk/aw_codecclk.c =================================================================== --- trunk/sys/arm/allwinner/clk/aw_codecclk.c (rev 0) +++ trunk/sys/arm/allwinner/clk/aw_codecclk.c 2020-03-06 17:15:48 UTC (rev 12400) @@ -0,0 +1,165 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/clk/aw_codecclk.c 308324 2016-11-05 04:17:32Z mmel $ + */ + +/* + * Allwinner CODEC clock + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_codecclk.c 308324 2016-11-05 04:17:32Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "clkdev_if.h" + +#define SCLK_GATING_SHIFT 31 + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-codec-clk", 1 }, + { NULL, 0 } +}; + +static int +aw_codecclk_create(device_t dev, bus_addr_t paddr, struct clkdom *clkdom, + const char *pclkname, const char *clkname, int index) +{ + const char *parent_names[1] = { pclkname }; + struct clk_gate_def def; + + memset(&def, 0, sizeof(def)); + def.clkdef.id = index; + def.clkdef.name = clkname; + def.clkdef.parent_names = parent_names; + def.clkdef.parent_cnt = 1; + def.offset = paddr; + def.shift = SCLK_GATING_SHIFT; + def.mask = 1; + def.on_value = 1; + def.off_value = 0; + + return (clknode_gate_register(clkdom, &def)); +} + +static int +aw_codecclk_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, "Allwinner CODEC Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_codecclk_attach(device_t dev) +{ + struct clkdom *clkdom; + const char **names; + int nout, error; + uint32_t *indices; + clk_t clk_parent; + bus_addr_t paddr; + bus_size_t psize; + phandle_t node; + + node = ofw_bus_get_node(dev); + indices = NULL; + + if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { + device_printf(dev, "cannot parse 'reg' property\n"); + return (ENXIO); + } + + clkdom = clkdom_create(dev); + + nout = clk_parse_ofw_out_names(dev, node, &names, &indices); + if (nout != 1) { + device_printf(dev, "must have exactly one output clock\n"); + error = ENOENT; + goto fail; + } + + error = clk_get_by_ofw_index(dev, 0, 0, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot parse clock parent\n"); + return (ENXIO); + } + + error = aw_codecclk_create(dev, paddr, clkdom, + clk_get_name(clk_parent), names[0], 1); + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + if (bootverbose) + clkdom_dump(clkdom); + + return (0); + +fail: + return (error); +} + +static device_method_t aw_codecclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_codecclk_probe), + DEVMETHOD(device_attach, aw_codecclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_codecclk_driver = { + "aw_codecclk", + aw_codecclk_methods, + 0 +}; + +static devclass_t aw_codecclk_devclass; + +EARLY_DRIVER_MODULE(aw_codecclk, simplebus, aw_codecclk_driver, + aw_codecclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: trunk/sys/arm/allwinner/clk/aw_codecclk.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/clk/aw_cpuclk.c =================================================================== --- trunk/sys/arm/allwinner/clk/aw_cpuclk.c (rev 0) +++ trunk/sys/arm/allwinner/clk/aw_cpuclk.c 2020-03-06 17:15:48 UTC (rev 12400) @@ -0,0 +1,162 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/clk/aw_cpuclk.c 308324 2016-11-05 04:17:32Z mmel $ + */ + +/* + * Allwinner CPU clock + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_cpuclk.c 308324 2016-11-05 04:17:32Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define CPU_CLK_SRC_SEL_WIDTH 2 +#define CPU_CLK_SRC_SEL_SHIFT 16 + +static int +aw_cpuclk_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-cpu-clk")) + return (ENXIO); + + device_set_desc(dev, "Allwinner CPU Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_cpuclk_attach(device_t dev) +{ + struct clk_mux_def def; + struct clkdom *clkdom; + bus_addr_t paddr; + bus_size_t psize; + phandle_t node; + int error, ncells, i; + clk_t clk; + + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { + device_printf(dev, "cannot parse 'reg' property\n"); + return (ENXIO); + } + + error = ofw_bus_parse_xref_list_get_length(node, "clocks", + "#clock-cells", &ncells); + if (error != 0) { + device_printf(dev, "cannot get clock count\n"); + return (error); + } + + clkdom = clkdom_create(dev); + + memset(&def, 0, sizeof(def)); + def.clkdef.id = 1; + def.clkdef.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, + M_WAITOK); + for (i = 0; i < ncells; i++) { + error = clk_get_by_ofw_index(dev, 0, i, &clk); + if (error != 0) { + device_printf(dev, "cannot get clock %d\n", i); + goto fail; + } + def.clkdef.parent_names[i] = clk_get_name(clk); + clk_release(clk); + } + def.clkdef.parent_cnt = ncells; + def.offset = paddr; + def.shift = CPU_CLK_SRC_SEL_SHIFT; + def.width = CPU_CLK_SRC_SEL_WIDTH; + + error = clk_parse_ofw_clk_name(dev, node, &def.clkdef.name); + if (error != 0) { + device_printf(dev, "cannot parse clock name\n"); + error = ENXIO; + goto fail; + } + + error = clknode_mux_register(clkdom, &def); + if (error != 0) { + device_printf(dev, "cannot register mux clock\n"); + error = ENXIO; + goto fail; + } + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + OF_prop_free(__DECONST(char *, def.clkdef.parent_names)); + OF_prop_free(__DECONST(char *, def.clkdef.name)); + + if (bootverbose) + clkdom_dump(clkdom); + + return (0); + +fail: + OF_prop_free(__DECONST(char *, def.clkdef.name)); + return (error); +} + +static device_method_t aw_cpuclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_cpuclk_probe), + DEVMETHOD(device_attach, aw_cpuclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_cpuclk_driver = { + "aw_cpuclk", + aw_cpuclk_methods, + 0 +}; + +static devclass_t aw_cpuclk_devclass; + +EARLY_DRIVER_MODULE(aw_cpuclk, simplebus, aw_cpuclk_driver, + aw_cpuclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: trunk/sys/arm/allwinner/clk/aw_cpuclk.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/clk/aw_cpusclk.c =================================================================== --- trunk/sys/arm/allwinner/clk/aw_cpusclk.c (rev 0) +++ trunk/sys/arm/allwinner/clk/aw_cpusclk.c 2020-03-06 17:15:48 UTC (rev 12400) @@ -0,0 +1,321 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/clk/aw_cpusclk.c 308324 2016-11-05 04:17:32Z mmel $ + */ + +/* + * Allwinner CPUS clock + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_cpusclk.c 308324 2016-11-05 04:17:32Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "clkdev_if.h" + +#define A80_CPUS_CLK_SRC_SEL (0x3 << 16) +#define A80_CPUS_CLK_SRC_SEL_SHIFT 16 +#define A80_CPUS_CLK_SRC_SEL_X32KI 0 +#define A80_CPUS_CLK_SRC_SEL_OSC24M 1 +#define A80_CPUS_CLK_SRC_SEL_PLL_PERIPH 2 +#define A80_CPUS_CLK_SRC_SEL_PLL_AUDIO 3 +#define A80_CPUS_POST_DIV (0x1f << 8) +#define A80_CPUS_POST_DIV_SHIFT 8 +#define A80_CPUS_CLK_RATIO (0x3 << 4) +#define A80_CPUS_CLK_RATIO_SHIFT 4 + +#define A83T_CPUS_CLK_SRC_SEL (0x3 << 16) +#define A83T_CPUS_CLK_SRC_SEL_SHIFT 16 +#define A83T_CPUS_CLK_SRC_SEL_X32KI 0 +#define A83T_CPUS_CLK_SRC_SEL_OSC24M 1 +#define A83T_CPUS_CLK_SRC_SEL_PLL_PERIPH 2 +#define A83T_CPUS_CLK_SRC_SEL_INTERNAL_OSC 3 +#define A83T_CPUS_POST_DIV (0x1f << 8) +#define A83T_CPUS_POST_DIV_SHIFT 8 +#define A83T_CPUS_CLK_RATIO (0x3 << 4) +#define A83T_CPUS_CLK_RATIO_SHIFT 4 + +enum aw_cpusclk_type { + AW_A80_CPUS = 1, + AW_A83T_CPUS, +}; + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun9i-a80-cpus-clk", AW_A80_CPUS }, + { "allwinner,sun8i-a83t-cpus-clk", AW_A83T_CPUS }, + { NULL, 0 } +}; + +struct aw_cpusclk_sc { + device_t clkdev; + bus_addr_t reg; + enum aw_cpusclk_type type; +}; + +#define CPUSCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define CPUSCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) +#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) +#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) + +static int +aw_cpusclk_init(struct clknode *clk, device_t dev) +{ + struct aw_cpusclk_sc *sc; + uint32_t val, mask, shift, index; + + sc = clknode_get_softc(clk); + + switch (sc->type) { + case AW_A80_CPUS: + mask = A80_CPUS_CLK_SRC_SEL; + shift = A80_CPUS_CLK_SRC_SEL_SHIFT; + break; + case AW_A83T_CPUS: + mask = A83T_CPUS_CLK_SRC_SEL; + shift = A83T_CPUS_CLK_SRC_SEL_SHIFT; + break; + default: + return (ENXIO); + } + + DEVICE_LOCK(sc); + CPUSCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + index = (val & mask) >> shift; + + clknode_init_parent_idx(clk, index); + return (0); +} + +static int +aw_cpusclk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct aw_cpusclk_sc *sc; + uint32_t val, src_sel, post_div, clk_ratio; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + CPUSCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + switch (sc->type) { + case AW_A80_CPUS: + src_sel = (val & A80_CPUS_CLK_SRC_SEL) >> + A80_CPUS_CLK_SRC_SEL_SHIFT; + post_div = ((val & A80_CPUS_POST_DIV) >> + A80_CPUS_POST_DIV_SHIFT) + 1; + clk_ratio = ((val & A80_CPUS_CLK_RATIO) >> + A80_CPUS_CLK_RATIO_SHIFT) + 1; + if (src_sel == A80_CPUS_CLK_SRC_SEL_PLL_PERIPH) + *freq = *freq / post_div / clk_ratio; + else + *freq = *freq / clk_ratio; + break; + case AW_A83T_CPUS: + src_sel = (val & A83T_CPUS_CLK_SRC_SEL) >> + A83T_CPUS_CLK_SRC_SEL_SHIFT; + post_div = ((val & A83T_CPUS_POST_DIV) >> + A83T_CPUS_POST_DIV_SHIFT) + 1; + clk_ratio = 1 << ((val & A83T_CPUS_CLK_RATIO) >> + A83T_CPUS_CLK_RATIO_SHIFT); + if (src_sel == A83T_CPUS_CLK_SRC_SEL_PLL_PERIPH) + *freq = *freq / post_div / clk_ratio; + else + *freq = *freq / clk_ratio; + break; + default: + return (EINVAL); + } + + return (0); +} + +static int +aw_cpusclk_set_mux(struct clknode *clk, int index) +{ + struct aw_cpusclk_sc *sc; + uint32_t mask, shift, val; + + sc = clknode_get_softc(clk); + + switch (sc->type) { + case AW_A80_CPUS: + mask = A80_CPUS_CLK_SRC_SEL; + shift = A80_CPUS_CLK_SRC_SEL_SHIFT; + break; + case AW_A83T_CPUS: + mask = A83T_CPUS_CLK_SRC_SEL; + shift = A83T_CPUS_CLK_SRC_SEL_SHIFT; + break; + default: + return (ENXIO); + } + + DEVICE_LOCK(sc); + CPUSCLK_READ(sc, &val); + val &= ~mask; + val |= (index << shift); + CPUSCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static clknode_method_t aw_cpusclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_cpusclk_init), + CLKNODEMETHOD(clknode_recalc_freq, aw_cpusclk_recalc_freq), + CLKNODEMETHOD(clknode_set_mux, aw_cpusclk_set_mux), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_cpusclk_clknode, aw_cpusclk_clknode_class, + aw_cpusclk_clknode_methods, sizeof(struct aw_cpusclk_sc), clknode_class); + +static int +aw_cpusclk_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, "Allwinner CPUS Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_cpusclk_attach(device_t dev) +{ + struct clknode_init_def def; + struct aw_cpusclk_sc *sc; + struct clkdom *clkdom; + struct clknode *clk; + clk_t clk_parent; + bus_addr_t paddr; + bus_size_t psize; + phandle_t node; + int error, ncells, i; + + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { + device_printf(dev, "cannot parse 'reg' property\n"); + return (ENXIO); + } + + error = ofw_bus_parse_xref_list_get_length(node, "clocks", + "#clock-cells", &ncells); + if (error != 0) { + device_printf(dev, "cannot get clock count\n"); + return (error); + } + + clkdom = clkdom_create(dev); + + memset(&def, 0, sizeof(def)); + def.id = 1; + def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, + M_WAITOK); + for (i = 0; i < ncells; i++) { + error = clk_get_by_ofw_index(dev, 0, i, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot get clock %d\n", i); + goto fail; + } + def.parent_names[i] = clk_get_name(clk_parent); + clk_release(clk_parent); + } + def.parent_cnt = ncells; + + error = clk_parse_ofw_clk_name(dev, node, &def.name); + if (error != 0) { + device_printf(dev, "cannot parse clock name\n"); + error = ENXIO; + goto fail; + } + + clk = clknode_create(clkdom, &aw_cpusclk_clknode_class, &def); + if (clk == NULL) { + device_printf(dev, "cannot create clknode\n"); + error = ENXIO; + goto fail; + } + sc = clknode_get_softc(clk); + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + sc->reg = paddr; + sc->clkdev = device_get_parent(dev); + + clknode_register(clkdom, clk); + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + if (bootverbose) + clkdom_dump(clkdom); + + return (0); + +fail: + return (error); +} + +static device_method_t aw_cpusclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_cpusclk_probe), + DEVMETHOD(device_attach, aw_cpusclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_cpusclk_driver = { + "aw_cpusclk", + aw_cpusclk_methods, + 0 +}; + +static devclass_t aw_cpusclk_devclass; + +EARLY_DRIVER_MODULE(aw_cpusclk, simplebus, aw_cpusclk_driver, + aw_cpusclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: trunk/sys/arm/allwinner/clk/aw_cpusclk.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/clk/aw_debeclk.c =================================================================== --- trunk/sys/arm/allwinner/clk/aw_debeclk.c (rev 0) +++ trunk/sys/arm/allwinner/clk/aw_debeclk.c 2020-03-06 17:15:48 UTC (rev 12400) @@ -0,0 +1,352 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/clk/aw_debeclk.c 308324 2016-11-05 04:17:32Z mmel $ + */ + +/* + * Allwinner display backend clocks + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_debeclk.c 308324 2016-11-05 04:17:32Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "clkdev_if.h" +#include "hwreset_if.h" + +#define SCLK_GATING (1 << 31) +#define BE_RST (1 << 30) +#define CLK_SRC_SEL (0x3 << 24) +#define CLK_SRC_SEL_SHIFT 24 +#define CLK_SRC_SEL_MAX 2 +#define CLK_SRC_SEL_PLL3 0 +#define CLK_SRC_SEL_PLL7 1 +#define CLK_SRC_SEL_PLL5 2 +#define CLK_RATIO_M (0xf << 0) +#define CLK_RATIO_M_SHIFT 0 +#define CLK_RATIO_M_MAX 0xf + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-de-be-clk", 1 }, + { NULL, 0 } +}; + +struct aw_debeclk_softc { + device_t clkdev; + bus_addr_t reg; +}; + +#define DEBECLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define DEBECLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) +#define DEBECLK_MODIFY(sc, clr, set) \ + CLKDEV_MODIFY_4((sc)->clkdev, (sc)->reg, (clr), (set)) +#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) +#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) + +static int +aw_debeclk_hwreset_assert(device_t dev, intptr_t id, bool value) +{ + struct aw_debeclk_softc *sc; + int error; + + sc = device_get_softc(dev); + + DEVICE_LOCK(sc); + error = DEBECLK_MODIFY(sc, BE_RST, value ? 0 : BE_RST); + DEVICE_UNLOCK(sc); + + return (error); +} + +static int +aw_debeclk_hwreset_is_asserted(device_t dev, intptr_t id, bool *value) +{ + struct aw_debeclk_softc *sc; + uint32_t val; + int error; + + sc = device_get_softc(dev); + + DEVICE_LOCK(sc); + error = DEBECLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + if (error) + return (error); + + *value = (val & BE_RST) != 0 ? false : true; + + return (0); +} + +static int +aw_debeclk_init(struct clknode *clk, device_t dev) +{ + struct aw_debeclk_softc *sc; + uint32_t val, index; + + sc = clknode_get_softc(clk); + + /* Set BE source to PLL5 (DDR external peripheral clock) */ + index = CLK_SRC_SEL_PLL5; + + DEVICE_LOCK(sc); + DEBECLK_READ(sc, &val); + val &= ~CLK_SRC_SEL; + val |= (index << CLK_SRC_SEL_SHIFT); + DEBECLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + clknode_init_parent_idx(clk, index); + return (0); +} + +static int +aw_debeclk_set_mux(struct clknode *clk, int index) +{ + struct aw_debeclk_softc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + if (index < 0 || index > CLK_SRC_SEL_MAX) + return (ERANGE); + + DEVICE_LOCK(sc); + DEBECLK_READ(sc, &val); + val &= ~CLK_SRC_SEL; + val |= (index << CLK_SRC_SEL_SHIFT); + DEBECLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_debeclk_set_gate(struct clknode *clk, bool enable) +{ + struct aw_debeclk_softc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + DEBECLK_READ(sc, &val); + if (enable) + val |= SCLK_GATING; + else + val &= ~SCLK_GATING; + DEBECLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_debeclk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct aw_debeclk_softc *sc; + uint32_t val, m; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + DEBECLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + m = ((val & CLK_RATIO_M) >> CLK_RATIO_M_SHIFT) + 1; + + *freq = *freq / m; + + return (0); +} + +static int +aw_debeclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct aw_debeclk_softc *sc; + uint32_t val, m; + + sc = clknode_get_softc(clk); + + m = howmany(fin, *fout) - 1; + + DEVICE_LOCK(sc); + DEBECLK_READ(sc, &val); + val &= ~CLK_RATIO_M; + val |= (m << CLK_RATIO_M_SHIFT); + DEBECLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + *fout = fin / (m + 1); + *stop = 1; + + return (0); +} + +static clknode_method_t aw_debeclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_debeclk_init), + CLKNODEMETHOD(clknode_set_gate, aw_debeclk_set_gate), + CLKNODEMETHOD(clknode_set_mux, aw_debeclk_set_mux), + CLKNODEMETHOD(clknode_recalc_freq, aw_debeclk_recalc_freq), + CLKNODEMETHOD(clknode_set_freq, aw_debeclk_set_freq), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_debeclk_clknode, aw_debeclk_clknode_class, + aw_debeclk_clknode_methods, sizeof(struct aw_debeclk_softc), clknode_class); + +static int +aw_debeclk_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, "Allwinner Display Engine Backend Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_debeclk_attach(device_t dev) +{ + struct clknode_init_def def; + struct aw_debeclk_softc *sc, *clk_sc; + struct clkdom *clkdom; + struct clknode *clk; + clk_t clk_parent; + bus_size_t psize; + phandle_t node; + int error, ncells, i; + + sc = device_get_softc(dev); + sc->clkdev = device_get_parent(dev); + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &sc->reg, &psize, NULL) != 0) { + device_printf(dev, "cannot parse 'reg' property\n"); + return (ENXIO); + } + + error = ofw_bus_parse_xref_list_get_length(node, "clocks", + "#clock-cells", &ncells); + if (error != 0) { + device_printf(dev, "cannot get clock count\n"); + return (error); + } + + clkdom = clkdom_create(dev); + + memset(&def, 0, sizeof(def)); + error = clk_parse_ofw_clk_name(dev, node, &def.name); + if (error != 0) { + device_printf(dev, "cannot parse clock name\n"); + error = ENXIO; + goto fail; + } + def.id = 1; + def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK); + for (i = 0; i < ncells; i++) { + error = clk_get_by_ofw_index(dev, 0, i, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot get clock %d\n", i); + goto fail; + } + def.parent_names[i] = clk_get_name(clk_parent); + clk_release(clk_parent); + } + def.parent_cnt = ncells; + + clk = clknode_create(clkdom, &aw_debeclk_clknode_class, &def); + if (clk == NULL) { + device_printf(dev, "cannot create clknode\n"); + error = ENXIO; + goto fail; + } + + clk_sc = clknode_get_softc(clk); + clk_sc->reg = sc->reg; + clk_sc->clkdev = device_get_parent(dev); + + clknode_register(clkdom, clk); + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + if (bootverbose) + clkdom_dump(clkdom); + + hwreset_register_ofw_provider(dev); + + return (0); + +fail: + return (error); +} + +static device_method_t aw_debeclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_debeclk_probe), + DEVMETHOD(device_attach, aw_debeclk_attach), + + /* Reset interface */ + DEVMETHOD(hwreset_assert, aw_debeclk_hwreset_assert), + DEVMETHOD(hwreset_is_asserted, aw_debeclk_hwreset_is_asserted), + + DEVMETHOD_END +}; + +static driver_t aw_debeclk_driver = { + "aw_debeclk", + aw_debeclk_methods, + sizeof(struct aw_debeclk_softc) +}; + +static devclass_t aw_debeclk_devclass; + +EARLY_DRIVER_MODULE(aw_debeclk, simplebus, aw_debeclk_driver, + aw_debeclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: trunk/sys/arm/allwinner/clk/aw_debeclk.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/clk/aw_gate.c =================================================================== --- trunk/sys/arm/allwinner/clk/aw_gate.c (rev 0) +++ trunk/sys/arm/allwinner/clk/aw_gate.c 2020-03-06 17:15:48 UTC (rev 12400) @@ -0,0 +1,232 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/clk/aw_gate.c 309762 2016-12-09 20:35:01Z manu $ + */ + +/* + * Allwinner clock gates + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_gate.c 309762 2016-12-09 20:35:01Z manu $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define GATE_OFFSET(index) ((index / 32) * 4) +#define GATE_SHIFT(index) (index % 32) + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-dram-gates-clk", + (uintptr_t)"Allwinner DRAM Clock Gates" }, + { "allwinner,sun4i-a10-ahb-gates-clk", + (uintptr_t)"Allwinner AHB Clock Gates" }, + { "allwinner,sun4i-a10-apb0-gates-clk", + (uintptr_t)"Allwinner APB0 Clock Gates" }, + { "allwinner,sun4i-a10-apb1-gates-clk", + (uintptr_t)"Allwinner APB1 Clock Gates" }, + + { "allwinner,sun5i-a13-ahb-gates-clk", + (uintptr_t)"Allwinner AHB Clock Gates" }, + { "allwinner,sun5i-a13-apb0-gates-clk", + (uintptr_t)"Allwinner APB0 Clock Gates" }, + { "allwinner,sun5i-a13-apb1-gates-clk", + (uintptr_t)"Allwinner APB1 Clock Gates" }, + + { "allwinner,sun7i-a20-ahb-gates-clk", + (uintptr_t)"Allwinner AHB Clock Gates" }, + { "allwinner,sun7i-a20-apb0-gates-clk", + (uintptr_t)"Allwinner APB0 Clock Gates" }, + { "allwinner,sun7i-a20-apb1-gates-clk", + (uintptr_t)"Allwinner APB1 Clock Gates" }, + + { "allwinner,sun6i-a31-ahb1-gates-clk", + (uintptr_t)"Allwinner AHB1 Clock Gates" }, + { "allwinner,sun6i-a31-apb0-gates-clk", + (uintptr_t)"Allwinner APB0 Clock Gates" }, + { "allwinner,sun6i-a31-apb1-gates-clk", + (uintptr_t)"Allwinner APB1 Clock Gates" }, + { "allwinner,sun6i-a31-apb2-gates-clk", + (uintptr_t)"Allwinner APB2 Clock Gates" }, + + { "allwinner,sun8i-a83t-bus-gates-clk", + (uintptr_t)"Allwinner Bus Clock Gates" }, + { "allwinner,sun8i-a83t-apb0-gates-clk", + (uintptr_t)"Allwinner APB0 Clock Gates" }, + + { "allwinner,sun8i-h3-bus-gates-clk", + (uintptr_t)"Allwinner Bus Clock Gates" }, + { "allwinner,sun8i-h3-apb0-gates-clk", + (uintptr_t)"Allwinner APB0 Clock Gates" }, + + { "allwinner,sun9i-a80-apbs-gates-clk", + (uintptr_t)"Allwinner APBS Clock Gates" }, + + { "allwinner,sunxi-multi-bus-gates-clk", + (uintptr_t)"Allwinner Multi Bus Clock Gates" }, + + { NULL, 0 } +}; + +static int +aw_gate_create(device_t dev, bus_addr_t paddr, struct clkdom *clkdom, + const char *pclkname, const char *clkname, int index) +{ + const char *parent_names[1] = { pclkname }; + struct clk_gate_def def; + + memset(&def, 0, sizeof(def)); + def.clkdef.id = index; + def.clkdef.name = clkname; + def.clkdef.parent_names = parent_names; + def.clkdef.parent_cnt = 1; + def.offset = paddr + GATE_OFFSET(index); + def.shift = GATE_SHIFT(index); + def.mask = 1; + def.on_value = 1; + def.off_value = 0; + + return (clknode_gate_register(clkdom, &def)); +} + +static int +aw_gate_add(device_t dev, struct clkdom *clkdom, phandle_t node, + bus_addr_t paddr) +{ + const char **names; + uint32_t *indices; + clk_t clk_parent; + int index, nout, error; + + indices = NULL; + + nout = clk_parse_ofw_out_names(dev, node, &names, &indices); + if (nout == 0) { + device_printf(dev, "no clock outputs found\n"); + return (ENOENT); + } + if (indices == NULL) { + device_printf(dev, "no clock-indices property\n"); + return (ENXIO); + } + + error = clk_get_by_ofw_index(dev, node, 0, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot parse clock parent\n"); + return (ENXIO); + } + + for (index = 0; index < nout; index++) { + error = aw_gate_create(dev, paddr, clkdom, + clk_get_name(clk_parent), names[index], indices[index]); + if (error) + return (error); + } + + return (0); +} + +static int +aw_gate_probe(device_t dev) +{ + const char *d; + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + d = (const char *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; + if (d == NULL) + return (ENXIO); + + device_set_desc(dev, d); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_gate_attach(device_t dev) +{ + struct clkdom *clkdom; + bus_addr_t paddr; + bus_size_t psize; + phandle_t node, child; + + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { + device_printf(dev, "cannot parse 'reg' property\n"); + return (ENXIO); + } + + clkdom = clkdom_create(dev); + + if (ofw_bus_is_compatible(dev, "allwinner,sunxi-multi-bus-gates-clk")) { + for (child = OF_child(node); child > 0; child = OF_peer(child)) + aw_gate_add(dev, clkdom, child, paddr); + } else + aw_gate_add(dev, clkdom, node, paddr); + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + return (ENXIO); + } + + if (bootverbose) + clkdom_dump(clkdom); + + return (0); +} + +static device_method_t aw_gate_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_gate_probe), + DEVMETHOD(device_attach, aw_gate_attach), + + DEVMETHOD_END +}; + +static driver_t aw_gate_driver = { + "aw_gate", + aw_gate_methods, + 0 +}; + +static devclass_t aw_gate_devclass; + +EARLY_DRIVER_MODULE(aw_gate, simplebus, aw_gate_driver, + aw_gate_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: trunk/sys/arm/allwinner/clk/aw_gate.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/clk/aw_gmacclk.c =================================================================== --- trunk/sys/arm/allwinner/clk/aw_gmacclk.c (rev 0) +++ trunk/sys/arm/allwinner/clk/aw_gmacclk.c 2020-03-06 17:15:48 UTC (rev 12400) @@ -0,0 +1,303 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/clk/aw_gmacclk.c 308324 2016-11-05 04:17:32Z mmel $ + */ + +/* + * Allwinner GMAC clock + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_gmacclk.c 308324 2016-11-05 04:17:32Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "clkdev_if.h" + +#define GMAC_CLK_PIT (0x1 << 2) +#define GMAC_CLK_PIT_SHIFT 2 +#define GMAC_CLK_PIT_MII 0 +#define GMAC_CLK_PIT_RGMII 1 +#define GMAC_CLK_SRC (0x3 << 0) +#define GMAC_CLK_SRC_SHIFT 0 +#define GMAC_CLK_SRC_MII 0 +#define GMAC_CLK_SRC_EXT_RGMII 1 +#define GMAC_CLK_SRC_RGMII 2 + +#define EMAC_TXC_DIV_CFG (1 << 15) +#define EMAC_TXC_DIV_CFG_SHIFT 15 +#define EMAC_TXC_DIV_CFG_125MHZ 0 +#define EMAC_TXC_DIV_CFG_25MHZ 1 +#define EMAC_PHY_SELECT (1 << 16) +#define EMAC_PHY_SELECT_SHIFT 16 +#define EMAC_PHY_SELECT_INT 0 +#define EMAC_PHY_SELECT_EXT 1 +#define EMAC_ETXDC (0x7 << 10) +#define EMAC_ETXDC_SHIFT 10 +#define EMAC_ERXDC (0x1f << 5) +#define EMAC_ERXDC_SHIFT 5 + +#define CLK_IDX_MII 0 +#define CLK_IDX_RGMII 1 +#define CLK_IDX_COUNT 2 + +enum aw_gmacclk_type { + GMACCLK_A20 = 1, + GMACCLK_A83T, +}; + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun7i-a20-gmac-clk", GMACCLK_A20 }, + { "allwinner,sun8i-a83t-emac-clk", GMACCLK_A83T }, + { NULL, 0 } +}; + +struct aw_gmacclk_sc { + device_t clkdev; + bus_addr_t reg; + enum aw_gmacclk_type type; + + int rx_delay; + int tx_delay; +}; + +#define GMACCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define GMACCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) +#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) +#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) + +static int +aw_gmacclk_init(struct clknode *clk, device_t dev) +{ + struct aw_gmacclk_sc *sc; + uint32_t val, index; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + GMACCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + switch ((val & GMAC_CLK_SRC) >> GMAC_CLK_SRC_SHIFT) { + case GMAC_CLK_SRC_MII: + index = CLK_IDX_MII; + break; + case GMAC_CLK_SRC_RGMII: + index = CLK_IDX_RGMII; + break; + default: + return (ENXIO); + } + + clknode_init_parent_idx(clk, index); + return (0); +} + +static int +aw_gmacclk_set_mux(struct clknode *clk, int index) +{ + struct aw_gmacclk_sc *sc; + uint32_t val, clk_src, pit, txc_div; + int error; + + sc = clknode_get_softc(clk); + error = 0; + + switch (index) { + case CLK_IDX_MII: + clk_src = GMAC_CLK_SRC_MII; + pit = GMAC_CLK_PIT_MII; + txc_div = EMAC_TXC_DIV_CFG_25MHZ; + break; + case CLK_IDX_RGMII: + clk_src = GMAC_CLK_SRC_RGMII; + pit = GMAC_CLK_PIT_RGMII; + txc_div = EMAC_TXC_DIV_CFG_125MHZ; + break; + default: + return (ENXIO); + } + + DEVICE_LOCK(sc); + GMACCLK_READ(sc, &val); + val &= ~(GMAC_CLK_SRC | GMAC_CLK_PIT); + val |= (clk_src << GMAC_CLK_SRC_SHIFT); + val |= (pit << GMAC_CLK_PIT_SHIFT); + if (sc->type == GMACCLK_A83T) { + val &= ~EMAC_TXC_DIV_CFG; + val |= (txc_div << EMAC_TXC_DIV_CFG_SHIFT); + val &= ~EMAC_PHY_SELECT; + val |= (EMAC_PHY_SELECT_EXT << EMAC_PHY_SELECT_SHIFT); + if (sc->tx_delay >= 0) { + val &= ~EMAC_ETXDC; + val |= (sc->tx_delay << EMAC_ETXDC_SHIFT); + } + if (sc->rx_delay >= 0) { + val &= ~EMAC_ERXDC; + val |= (sc->rx_delay << EMAC_ERXDC_SHIFT); + } + } + GMACCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static clknode_method_t aw_gmacclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_gmacclk_init), + CLKNODEMETHOD(clknode_set_mux, aw_gmacclk_set_mux), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_gmacclk_clknode, aw_gmacclk_clknode_class, + aw_gmacclk_clknode_methods, sizeof(struct aw_gmacclk_sc), clknode_class); + +static int +aw_gmacclk_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, "Allwinner GMAC Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_gmacclk_attach(device_t dev) +{ + struct clknode_init_def def; + struct aw_gmacclk_sc *sc; + struct clkdom *clkdom; + struct clknode *clk; + clk_t clk_parent; + bus_addr_t paddr; + bus_size_t psize; + phandle_t node; + int error, ncells, i; + + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { + device_printf(dev, "cannot parse 'reg' property\n"); + return (ENXIO); + } + + error = ofw_bus_parse_xref_list_get_length(node, "clocks", + "#clock-cells", &ncells); + if (error != 0 || ncells != CLK_IDX_COUNT) { + device_printf(dev, "couldn't find parent clocks\n"); + return (ENXIO); + } + + clkdom = clkdom_create(dev); + + memset(&def, 0, sizeof(def)); + error = clk_parse_ofw_clk_name(dev, node, &def.name); + if (error != 0) { + device_printf(dev, "cannot parse clock name\n"); + error = ENXIO; + goto fail; + } + def.id = 1; + def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK); + for (i = 0; i < ncells; i++) { + error = clk_get_by_ofw_index(dev, 0, i, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot get clock %d\n", error); + goto fail; + } + def.parent_names[i] = clk_get_name(clk_parent); + clk_release(clk_parent); + } + def.parent_cnt = ncells; + + clk = clknode_create(clkdom, &aw_gmacclk_clknode_class, &def); + if (clk == NULL) { + device_printf(dev, "cannot create clknode\n"); + error = ENXIO; + goto fail; + } + + sc = clknode_get_softc(clk); + sc->reg = paddr; + sc->clkdev = device_get_parent(dev); + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + sc->tx_delay = sc->rx_delay = -1; + OF_getencprop(node, "tx-delay", &sc->tx_delay, sizeof(sc->tx_delay)); + OF_getencprop(node, "rx-delay", &sc->rx_delay, sizeof(sc->rx_delay)); + + clknode_register(clkdom, clk); + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + if (bootverbose) + clkdom_dump(clkdom); + + return (0); + +fail: + return (error); +} + +static device_method_t aw_gmacclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_gmacclk_probe), + DEVMETHOD(device_attach, aw_gmacclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_gmacclk_driver = { + "aw_gmacclk", + aw_gmacclk_methods, + 0 +}; + +static devclass_t aw_gmacclk_devclass; + +EARLY_DRIVER_MODULE(aw_gmacclk, simplebus, aw_gmacclk_driver, + aw_gmacclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: trunk/sys/arm/allwinner/clk/aw_gmacclk.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/clk/aw_hdmiclk.c =================================================================== --- trunk/sys/arm/allwinner/clk/aw_hdmiclk.c (rev 0) +++ trunk/sys/arm/allwinner/clk/aw_hdmiclk.c 2020-03-06 17:15:48 UTC (rev 12400) @@ -0,0 +1,316 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/clk/aw_hdmiclk.c 308324 2016-11-05 04:17:32Z mmel $ + */ + +/* + * Allwinner HDMI clock + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_hdmiclk.c 308324 2016-11-05 04:17:32Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "clkdev_if.h" + +#define SCLK_GATING (1 << 31) +#define CLK_SRC_SEL (0x3 << 24) +#define CLK_SRC_SEL_SHIFT 24 +#define CLK_SRC_SEL_MAX 0x3 +#define CLK_RATIO_N (0x3 << 16) +#define CLK_RATIO_N_SHIFT 16 +#define CLK_RATIO_N_MAX 0x3 +#define CLK_RATIO_M (0x1f << 0) +#define CLK_RATIO_M_SHIFT 0 +#define CLK_RATIO_M_MAX 0x1f + +#define CLK_IDX_PLL3_1X 0 + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-hdmi-clk", 1 }, + { NULL, 0 } +}; + +struct aw_hdmiclk_sc { + device_t clkdev; + bus_addr_t reg; +}; + +#define HDMICLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define HDMICLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) +#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) +#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) + +static int +aw_hdmiclk_init(struct clknode *clk, device_t dev) +{ + struct aw_hdmiclk_sc *sc; + uint32_t val, index; + + sc = clknode_get_softc(clk); + + /* Select PLL3(1X) clock source */ + index = CLK_IDX_PLL3_1X; + + DEVICE_LOCK(sc); + HDMICLK_READ(sc, &val); + val &= ~CLK_SRC_SEL; + val |= (index << CLK_SRC_SEL_SHIFT); + HDMICLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + clknode_init_parent_idx(clk, index); + return (0); +} + +static int +aw_hdmiclk_set_mux(struct clknode *clk, int index) +{ + struct aw_hdmiclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + if (index < 0 || index > CLK_SRC_SEL_MAX) + return (ERANGE); + + DEVICE_LOCK(sc); + HDMICLK_READ(sc, &val); + val &= ~CLK_SRC_SEL; + val |= (index << CLK_SRC_SEL_SHIFT); + HDMICLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_hdmiclk_set_gate(struct clknode *clk, bool enable) +{ + struct aw_hdmiclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + HDMICLK_READ(sc, &val); + if (enable) + val |= SCLK_GATING; + else + val &= ~SCLK_GATING; + HDMICLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_hdmiclk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct aw_hdmiclk_sc *sc; + uint32_t val, m, n; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + HDMICLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + n = 1 << ((val & CLK_RATIO_N) >> CLK_RATIO_N_SHIFT); + m = ((val & CLK_RATIO_M) >> CLK_RATIO_M_SHIFT) + 1; + + *freq = *freq / n / m; + + return (0); +} + +static int +aw_hdmiclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct aw_hdmiclk_sc *sc; + uint32_t val, m, n, best_m, best_n; + uint64_t cur_freq; + int64_t best_diff, cur_diff; + + sc = clknode_get_softc(clk); + best_n = best_m = 0; + best_diff = (int64_t)*fout; + + for (n = 0; n <= CLK_RATIO_N_MAX; n++) + for (m = 0; m <= CLK_RATIO_M_MAX; m++) { + cur_freq = fin / (1 << n) / (m + 1); + cur_diff = (int64_t)*fout - cur_freq; + if (cur_diff >= 0 && cur_diff < best_diff) { + best_diff = cur_diff; + best_m = m; + best_n = n; + } + } + + if (best_diff == (int64_t)*fout) + return (ERANGE); + + DEVICE_LOCK(sc); + HDMICLK_READ(sc, &val); + val &= ~(CLK_RATIO_N | CLK_RATIO_M); + val |= (best_n << CLK_RATIO_N_SHIFT); + val |= (best_m << CLK_RATIO_M_SHIFT); + HDMICLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + *fout = fin / (1 << best_n) / (best_m + 1); + *stop = 1; + + return (0); +} + +static clknode_method_t aw_hdmiclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_hdmiclk_init), + CLKNODEMETHOD(clknode_set_gate, aw_hdmiclk_set_gate), + CLKNODEMETHOD(clknode_set_mux, aw_hdmiclk_set_mux), + CLKNODEMETHOD(clknode_recalc_freq, aw_hdmiclk_recalc_freq), + CLKNODEMETHOD(clknode_set_freq, aw_hdmiclk_set_freq), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_hdmiclk_clknode, aw_hdmiclk_clknode_class, + aw_hdmiclk_clknode_methods, sizeof(struct aw_hdmiclk_sc), clknode_class); + +static int +aw_hdmiclk_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, "Allwinner HDMI Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_hdmiclk_attach(device_t dev) +{ + struct clknode_init_def def; + struct aw_hdmiclk_sc *sc; + struct clkdom *clkdom; + struct clknode *clk; + clk_t clk_parent; + bus_addr_t paddr; + bus_size_t psize; + phandle_t node; + int error; + + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { + device_printf(dev, "cannot parse 'reg' property\n"); + return (ENXIO); + } + + clkdom = clkdom_create(dev); + + error = clk_get_by_ofw_index(dev, 0, 0, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot parse clock parent\n"); + return (ENXIO); + } + + memset(&def, 0, sizeof(def)); + error = clk_parse_ofw_clk_name(dev, node, &def.name); + if (error != 0) { + device_printf(dev, "cannot parse clock name\n"); + error = ENXIO; + goto fail; + } + def.id = 1; + def.parent_names = malloc(sizeof(char *), M_OFWPROP, M_WAITOK); + def.parent_names[0] = clk_get_name(clk_parent); + def.parent_cnt = 1; + + clk = clknode_create(clkdom, &aw_hdmiclk_clknode_class, &def); + if (clk == NULL) { + device_printf(dev, "cannot create clknode\n"); + error = ENXIO; + goto fail; + } + + sc = clknode_get_softc(clk); + sc->reg = paddr; + sc->clkdev = device_get_parent(dev); + + clknode_register(clkdom, clk); + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + if (bootverbose) + clkdom_dump(clkdom); + + return (0); + +fail: + return (error); +} + +static device_method_t aw_hdmiclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_hdmiclk_probe), + DEVMETHOD(device_attach, aw_hdmiclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_hdmiclk_driver = { + "aw_hdmiclk", + aw_hdmiclk_methods, + 0 +}; + +static devclass_t aw_hdmiclk_devclass; + +EARLY_DRIVER_MODULE(aw_hdmiclk, simplebus, aw_hdmiclk_driver, + aw_hdmiclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: trunk/sys/arm/allwinner/clk/aw_hdmiclk.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/clk/aw_lcdclk.c =================================================================== --- trunk/sys/arm/allwinner/clk/aw_lcdclk.c (rev 0) +++ trunk/sys/arm/allwinner/clk/aw_lcdclk.c 2020-03-06 17:15:48 UTC (rev 12400) @@ -0,0 +1,562 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/clk/aw_lcdclk.c 308324 2016-11-05 04:17:32Z mmel $ + */ + +/* + * Allwinner LCD clocks + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_lcdclk.c 308324 2016-11-05 04:17:32Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "clkdev_if.h" +#include "hwreset_if.h" + +/* CH0 */ +#define CH0_SCLK_GATING (1 << 31) +#define CH0_LCD_RST (1 << 30) +#define CH0_CLK_SRC_SEL (0x3 << 24) +#define CH0_CLK_SRC_SEL_SHIFT 24 +#define CH0_CLK_SRC_SEL_PLL3_1X 0 +#define CH0_CLK_SRC_SEL_PLL7_1X 1 +#define CH0_CLK_SRC_SEL_PLL3_2X 2 +#define CH0_CLK_SRC_SEL_PLL6 3 + +/* CH1 */ +#define CH1_SCLK2_GATING (1 << 31) +#define CH1_SCLK2_SEL (0x3 << 24) +#define CH1_SCLK2_SEL_SHIFT 24 +#define CH1_SCLK2_SEL_PLL3_1X 0 +#define CH1_SCLK2_SEL_PLL7_1X 1 +#define CH1_SCLK2_SEL_PLL3_2X 2 +#define CH1_SCLK2_SEL_PLL7_2X 3 +#define CH1_SCLK1_GATING (1 << 15) +#define CH1_SCLK1_SEL (0x1 << 11) +#define CH1_SCLK1_SEL_SHIFT 11 +#define CH1_SCLK1_SEL_SCLK2 0 +#define CH1_SCLK1_SEL_SCLK2_DIV2 1 +#define CH1_CLK_DIV_RATIO_M (0x1f << 0) +#define CH1_CLK_DIV_RATIO_M_SHIFT 0 + +#define TCON_PLLREF 3000000ULL +#define TCON_PLL_M_MIN 1 +#define TCON_PLL_M_MAX 15 +#define TCON_PLL_N_MIN 9 +#define TCON_PLL_N_MAX 127 + +#define CLK_IDX_CH1_SCLK1 0 +#define CLK_IDX_CH1_SCLK2 1 + +#define CLK_IDX_ + +enum aw_lcdclk_type { + AW_LCD_CH0 = 1, + AW_LCD_CH1, +}; + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-lcd-ch0-clk", AW_LCD_CH0 }, + { "allwinner,sun4i-a10-lcd-ch1-clk", AW_LCD_CH1 }, + { NULL, 0 } +}; + +struct aw_lcdclk_softc { + enum aw_lcdclk_type type; + device_t clkdev; + bus_addr_t reg; + int id; +}; + +#define LCDCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define LCDCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) +#define LCDCLK_MODIFY(sc, clr, set) \ + CLKDEV_MODIFY_4((sc)->clkdev, (sc)->reg, (clr), (set)) +#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) +#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) + +static int +aw_lcdclk_hwreset_assert(device_t dev, intptr_t id, bool value) +{ + struct aw_lcdclk_softc *sc; + int error; + + sc = device_get_softc(dev); + + if (sc->type != AW_LCD_CH0) + return (ENXIO); + + DEVICE_LOCK(sc); + error = LCDCLK_MODIFY(sc, CH0_LCD_RST, value ? 0 : CH0_LCD_RST); + DEVICE_UNLOCK(sc); + + return (error); +} + +static int +aw_lcdclk_hwreset_is_asserted(device_t dev, intptr_t id, bool *value) +{ + struct aw_lcdclk_softc *sc; + uint32_t val; + int error; + + sc = device_get_softc(dev); + + if (sc->type != AW_LCD_CH0) + return (ENXIO); + + DEVICE_LOCK(sc); + error = LCDCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + if (error) + return (error); + + *value = (val & CH0_LCD_RST) != 0 ? false : true; + + return (0); +} + +static int +aw_lcdclk_init(struct clknode *clk, device_t dev) +{ + struct aw_lcdclk_softc *sc; + uint32_t val, index; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + LCDCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + switch (sc->type) { + case AW_LCD_CH0: + index = (val & CH0_CLK_SRC_SEL) >> CH0_CLK_SRC_SEL_SHIFT; + break; + case AW_LCD_CH1: + switch (sc->id) { + case CLK_IDX_CH1_SCLK1: + index = 0; + break; + case CLK_IDX_CH1_SCLK2: + index = (val & CH1_SCLK2_SEL_SHIFT) >> + CH1_SCLK2_SEL_SHIFT; + break; + default: + return (ENXIO); + } + break; + default: + return (ENXIO); + } + + clknode_init_parent_idx(clk, index); + return (0); +} + +static int +aw_lcdclk_set_mux(struct clknode *clk, int index) +{ + struct aw_lcdclk_softc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + switch (sc->type) { + case AW_LCD_CH0: + DEVICE_LOCK(sc); + LCDCLK_READ(sc, &val); + val &= ~CH0_CLK_SRC_SEL; + val |= (index << CH0_CLK_SRC_SEL_SHIFT); + LCDCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + break; + case AW_LCD_CH1: + switch (sc->id) { + case CLK_IDX_CH1_SCLK2: + DEVICE_LOCK(sc); + LCDCLK_READ(sc, &val); + val &= ~CH1_SCLK2_SEL; + val |= (index << CH1_SCLK2_SEL_SHIFT); + LCDCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + break; + default: + return (ENXIO); + } + break; + default: + return (ENXIO); + } + + return (0); +} + +static int +aw_lcdclk_set_gate(struct clknode *clk, bool enable) +{ + struct aw_lcdclk_softc *sc; + uint32_t val, mask; + + sc = clknode_get_softc(clk); + + switch (sc->type) { + case AW_LCD_CH0: + mask = CH0_SCLK_GATING; + break; + case AW_LCD_CH1: + mask = (sc->id == CLK_IDX_CH1_SCLK1) ? CH1_SCLK1_GATING : + CH1_SCLK2_GATING; + break; + default: + return (ENXIO); + } + + DEVICE_LOCK(sc); + LCDCLK_READ(sc, &val); + if (enable) + val |= mask; + else + val &= ~mask; + LCDCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_lcdclk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct aw_lcdclk_softc *sc; + uint32_t val, m, src_sel; + + sc = clknode_get_softc(clk); + + if (sc->type != AW_LCD_CH1) + return (0); + + DEVICE_LOCK(sc); + LCDCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + m = ((val & CH1_CLK_DIV_RATIO_M) >> CH1_CLK_DIV_RATIO_M_SHIFT) + 1; + *freq = *freq / m; + + if (sc->id == CLK_IDX_CH1_SCLK1) { + src_sel = (val & CH1_SCLK1_SEL) >> CH1_SCLK1_SEL_SHIFT; + if (src_sel == CH1_SCLK1_SEL_SCLK2_DIV2) + *freq /= 2; + } + + return (0); +} + +static void +calc_tcon_pll(uint64_t fin, uint64_t fout, uint32_t *pm, uint32_t *pn) +{ + int64_t diff, fcur, best; + int m, n; + + best = fout; + for (m = TCON_PLL_M_MIN; m <= TCON_PLL_M_MAX; m++) { + for (n = TCON_PLL_N_MIN; n <= TCON_PLL_N_MAX; n++) { + fcur = (n * fin) / m; + diff = (int64_t)fout - fcur; + if (diff > 0 && diff < best) { + best = diff; + *pm = m; + *pn = n; + } + } + } +} + +static int +aw_lcdclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct aw_lcdclk_softc *sc; + uint32_t val, m, m2, n, n2, src_sel; + uint64_t fsingle, fdouble; + int error; + bool dbl; + + sc = clknode_get_softc(clk); + + switch (sc->type) { + case AW_LCD_CH0: + *stop = 0; + break; + case AW_LCD_CH1: + if (sc->id != CLK_IDX_CH1_SCLK2) + return (ENXIO); + + m = n = m2 = n2 = 0; + dbl = false; + + /* Find the frequency closes to the target dot clock, using + * both 1X and 2X PLL inputs as possible candidates. + */ + calc_tcon_pll(TCON_PLLREF, *fout, &m, &n); + calc_tcon_pll(TCON_PLLREF * 2, *fout, &m2, &n2); + + fsingle = m ? (n * TCON_PLLREF) / m : 0; + fdouble = m2 ? (n2 * TCON_PLLREF * 2) / m2 : 0; + + if (fdouble > fsingle) { + dbl = true; + m = m2; + n = n2; + } + + src_sel = dbl ? CH0_CLK_SRC_SEL_PLL3_2X : + CH0_CLK_SRC_SEL_PLL3_1X; + + /* Switch parent clock if necessary */ + if (src_sel != clknode_get_parent_idx(clk)) { + error = clknode_set_parent_by_idx(clk, src_sel); + if (error != 0) + return (error); + } + + /* Set desired parent frequency */ + fin = n * TCON_PLLREF; + + error = clknode_set_freq(clknode_get_parent(clk), fin, 0, 0); + if (error != 0) + return (error); + + error = clknode_enable(clknode_get_parent(clk)); + if (error != 0) + return (error); + + /* Fetch new input frequency */ + error = clknode_get_freq(clknode_get_parent(clk), &fin); + if (error != 0) + return (error); + + /* Set LCD divisor */ + DEVICE_LOCK(sc); + LCDCLK_READ(sc, &val); + val &= ~CH1_CLK_DIV_RATIO_M; + val |= ((m - 1) << CH1_CLK_DIV_RATIO_M_SHIFT); + LCDCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + *fout = fin / m; + *stop = 1; + + break; + } + + return (0); +} + +static clknode_method_t aw_lcdclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_lcdclk_init), + CLKNODEMETHOD(clknode_set_gate, aw_lcdclk_set_gate), + CLKNODEMETHOD(clknode_set_mux, aw_lcdclk_set_mux), + CLKNODEMETHOD(clknode_recalc_freq, aw_lcdclk_recalc_freq), + CLKNODEMETHOD(clknode_set_freq, aw_lcdclk_set_freq), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_lcdclk_clknode, aw_lcdclk_clknode_class, + aw_lcdclk_clknode_methods, sizeof(struct aw_lcdclk_softc), clknode_class); + +static int +aw_lcdclk_create(device_t dev, struct clkdom *clkdom, + const char **parent_names, int parent_cnt, const char *name, int index) +{ + struct aw_lcdclk_softc *sc, *clk_sc; + struct clknode_init_def def; + struct clknode *clk; + phandle_t node; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + + memset(&def, 0, sizeof(def)); + def.id = index; + def.name = name; + def.parent_names = parent_names; + def.parent_cnt = parent_cnt; + + clk = clknode_create(clkdom, &aw_lcdclk_clknode_class, &def); + if (clk == NULL) { + device_printf(dev, "cannot create clknode\n"); + return (ENXIO); + } + + clk_sc = clknode_get_softc(clk); + clk_sc->type = sc->type; + clk_sc->reg = sc->reg; + clk_sc->clkdev = sc->clkdev; + clk_sc->id = index; + + clknode_register(clkdom, clk); + + return (0); +} + +static int +aw_lcdclk_probe(device_t dev) +{ + enum aw_lcdclk_type type; + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + switch (type) { + case AW_LCD_CH0: + device_set_desc(dev, "Allwinner LCD CH0 Clock"); + break; + case AW_LCD_CH1: + device_set_desc(dev, "Allwinner LCD CH1 Clock"); + break; + default: + return (ENXIO); + } + + return (BUS_PROBE_DEFAULT); +} + +static int +aw_lcdclk_attach(device_t dev) +{ + struct aw_lcdclk_softc *sc; + struct clkdom *clkdom; + clk_t clk_parent; + bus_size_t psize; + phandle_t node; + uint32_t *indices; + const char **parent_names; + const char **names; + int error, ncells, nout, i; + + sc = device_get_softc(dev); + sc->clkdev = device_get_parent(dev); + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &sc->reg, &psize, NULL) != 0) { + device_printf(dev, "cannot parse 'reg' property\n"); + return (ENXIO); + } + + error = ofw_bus_parse_xref_list_get_length(node, "clocks", + "#clock-cells", &ncells); + if (error != 0) { + device_printf(dev, "cannot get clock count\n"); + return (error); + } + + parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK); + for (i = 0; i < ncells; i++) { + error = clk_get_by_ofw_index(dev, 0, i, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot get clock %d\n", i); + goto fail; + } + parent_names[i] = clk_get_name(clk_parent); + clk_release(clk_parent); + } + + nout = clk_parse_ofw_out_names(dev, node, &names, &indices); + if (nout == 0) { + device_printf(dev, "no clock outputs found\n"); + return (error); + } + + clkdom = clkdom_create(dev); + + for (i = 0; i < nout; i++) { + error = aw_lcdclk_create(dev, clkdom, parent_names, ncells, + names[i], nout == 1 ? 1 : i); + if (error) + goto fail; + } + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + if (bootverbose) + clkdom_dump(clkdom); + + if (sc->type == AW_LCD_CH0) + hwreset_register_ofw_provider(dev); + + OF_prop_free(parent_names); + return (0); + +fail: + OF_prop_free(parent_names); + return (error); +} + +static device_method_t aw_lcdclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_lcdclk_probe), + DEVMETHOD(device_attach, aw_lcdclk_attach), + + /* Reset interface */ + DEVMETHOD(hwreset_assert, aw_lcdclk_hwreset_assert), + DEVMETHOD(hwreset_is_asserted, aw_lcdclk_hwreset_is_asserted), + + DEVMETHOD_END +}; + +static driver_t aw_lcdclk_driver = { + "aw_lcdclk", + aw_lcdclk_methods, + sizeof(struct aw_lcdclk_softc) +}; + +static devclass_t aw_lcdclk_devclass; + +EARLY_DRIVER_MODULE(aw_lcdclk, simplebus, aw_lcdclk_driver, + aw_lcdclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: trunk/sys/arm/allwinner/clk/aw_lcdclk.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/clk/aw_mmcclk.c =================================================================== --- trunk/sys/arm/allwinner/clk/aw_mmcclk.c (rev 0) +++ trunk/sys/arm/allwinner/clk/aw_mmcclk.c 2020-03-06 17:15:48 UTC (rev 12400) @@ -0,0 +1,352 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/clk/aw_mmcclk.c 309756 2016-12-09 20:07:01Z manu $ + */ + +/* + * Allwinner MMC clocks + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_mmcclk.c 309756 2016-12-09 20:07:01Z manu $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "clkdev_if.h" + +#define SCLK_GATING (1 << 31) +#define CLK_SRC_SEL (0x3 << 24) +#define CLK_SRC_SEL_SHIFT 24 +#define CLK_SRC_SEL_MAX 0x3 +#define CLK_SRC_SEL_OSC24M 0 +#define CLK_SRC_SEL_PLL6 1 +#define CLK_PHASE_CTR (0x7 << 20) +#define CLK_PHASE_CTR_SHIFT 20 +#define CLK_RATIO_N (0x3 << 16) +#define CLK_RATIO_N_SHIFT 16 +#define CLK_RATIO_N_MAX 0x3 +#define OUTPUT_CLK_PHASE_CTR (0x7 << 8) +#define OUTPUT_CLK_PHASE_CTR_SHIFT 8 +#define CLK_RATIO_M (0xf << 0) +#define CLK_RATIO_M_SHIFT 0 +#define CLK_RATIO_M_MAX 0xf + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-mmc-clk", 1 }, + { NULL, 0 } +}; + +struct aw_mmcclk_sc { + device_t clkdev; + bus_addr_t reg; +}; + +#define MODCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define MODCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) +#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) +#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) + +static int +aw_mmcclk_init(struct clknode *clk, device_t dev) +{ + struct aw_mmcclk_sc *sc; + uint32_t val, index; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + index = (val & CLK_SRC_SEL) >> CLK_SRC_SEL_SHIFT; + + clknode_init_parent_idx(clk, index); + return (0); +} + +static int +aw_mmcclk_set_mux(struct clknode *clk, int index) +{ + struct aw_mmcclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + if (index < 0 || index > CLK_SRC_SEL_MAX) + return (ERANGE); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + val &= ~CLK_SRC_SEL; + val |= (index << CLK_SRC_SEL_SHIFT); + MODCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_mmcclk_set_gate(struct clknode *clk, bool enable) +{ + struct aw_mmcclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + if (enable) + val |= SCLK_GATING; + else + val &= ~SCLK_GATING; + MODCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_mmcclk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct aw_mmcclk_sc *sc; + uint32_t val, m, n; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + n = 1 << ((val & CLK_RATIO_N) >> CLK_RATIO_N_SHIFT); + m = ((val & CLK_RATIO_M) >> CLK_RATIO_M_SHIFT) + 1; + + *freq = *freq / n / m; + + return (0); +} + +static int +aw_mmcclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct aw_mmcclk_sc *sc; + uint32_t val, m, n, phase, ophase; + int parent_idx, error; + + sc = clknode_get_softc(clk); + + /* XXX + * The ophase/phase values should be set by the MMC driver, but + * there is currently no way to do this with the clk API + */ + if (*fout <= 400000) { + parent_idx = CLK_SRC_SEL_OSC24M; + ophase = 0; + phase = 0; + n = 2; + } else if (*fout <= 25000000) { + parent_idx = CLK_SRC_SEL_PLL6; + ophase = 0; + phase = 5; + n = 2; + } else if (*fout <= 52000000) { + parent_idx = CLK_SRC_SEL_PLL6; + ophase = 3; + phase = 5; + n = 0; + } else + return (ERANGE); + + /* Switch parent clock, if necessary */ + if (parent_idx != clknode_get_parent_idx(clk)) { + error = clknode_set_parent_by_idx(clk, parent_idx); + if (error != 0) + return (error); + + /* Fetch new input frequency */ + error = clknode_get_freq(clknode_get_parent(clk), &fin); + if (error != 0) + return (error); + } + + m = ((fin / (1 << n)) / *fout) - 1; + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + val &= ~(CLK_RATIO_N | CLK_RATIO_M | CLK_PHASE_CTR | + OUTPUT_CLK_PHASE_CTR); + val |= (n << CLK_RATIO_N_SHIFT); + val |= (m << CLK_RATIO_M_SHIFT); + val |= (phase << CLK_PHASE_CTR_SHIFT); + val |= (ophase << OUTPUT_CLK_PHASE_CTR_SHIFT); + MODCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + *fout = fin / (1 << n) / (m + 1); + *stop = 1; + + return (0); +} + +static clknode_method_t aw_mmcclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_mmcclk_init), + CLKNODEMETHOD(clknode_set_gate, aw_mmcclk_set_gate), + CLKNODEMETHOD(clknode_set_mux, aw_mmcclk_set_mux), + CLKNODEMETHOD(clknode_recalc_freq, aw_mmcclk_recalc_freq), + CLKNODEMETHOD(clknode_set_freq, aw_mmcclk_set_freq), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_mmcclk_clknode, aw_mmcclk_clknode_class, + aw_mmcclk_clknode_methods, sizeof(struct aw_mmcclk_sc), clknode_class); + +static int +aw_mmcclk_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, "Allwinner MMC Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_mmcclk_attach(device_t dev) +{ + struct clknode_init_def def; + struct aw_mmcclk_sc *sc; + struct clkdom *clkdom; + struct clknode *clk; + const char **names; + uint32_t *indices; + clk_t clk_parent; + bus_addr_t paddr; + bus_size_t psize; + phandle_t node; + int error, nout, ncells, i; + + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { + device_printf(dev, "cannot parse 'reg' property\n"); + return (ENXIO); + } + + error = ofw_bus_parse_xref_list_get_length(node, "clocks", + "#clock-cells", &ncells); + if (error != 0 || ncells == 0) { + device_printf(dev, "couldn't find parent clocks\n"); + return (ENXIO); + } + + clkdom = clkdom_create(dev); + + nout = clk_parse_ofw_out_names(dev, node, &names, &indices); + if (nout == 0) { + device_printf(dev, "no output clocks found\n"); + error = ENXIO; + goto fail; + } + + memset(&def, 0, sizeof(def)); + def.name = names[0]; + def.id = 0; + def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK); + for (i = 0; i < ncells; i++) { + error = clk_get_by_ofw_index(dev, 0, i, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot get clock %d\n", i); + goto fail; + } + def.parent_names[i] = clk_get_name(clk_parent); + clk_release(clk_parent); + } + def.parent_cnt = ncells; + def.flags = CLK_NODE_GLITCH_FREE; + + clk = clknode_create(clkdom, &aw_mmcclk_clknode_class, &def); + if (clk == NULL) { + device_printf(dev, "cannot create clknode\n"); + error = ENXIO; + goto fail; + } + + sc = clknode_get_softc(clk); + sc->reg = paddr; + sc->clkdev = device_get_parent(dev); + + clknode_register(clkdom, clk); + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + if (bootverbose) + clkdom_dump(clkdom); + + return (0); + +fail: + return (error); +} + +static device_method_t aw_mmcclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_mmcclk_probe), + DEVMETHOD(device_attach, aw_mmcclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_mmcclk_driver = { + "aw_mmcclk", + aw_mmcclk_methods, + 0 +}; + +static devclass_t aw_mmcclk_devclass; + +EARLY_DRIVER_MODULE(aw_mmcclk, simplebus, aw_mmcclk_driver, + aw_mmcclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: trunk/sys/arm/allwinner/clk/aw_mmcclk.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/clk/aw_modclk.c =================================================================== --- trunk/sys/arm/allwinner/clk/aw_modclk.c (rev 0) +++ trunk/sys/arm/allwinner/clk/aw_modclk.c 2020-03-06 17:15:48 UTC (rev 12400) @@ -0,0 +1,339 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/clk/aw_modclk.c 309767 2016-12-09 20:52:48Z manu $ + */ + +/* + * Allwinner module clocks + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_modclk.c 309767 2016-12-09 20:52:48Z manu $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "clkdev_if.h" + +#define SCLK_GATING (1 << 31) +#define CLK_SRC_SEL (0x3 << 24) +#define CLK_SRC_SEL_SHIFT 24 +#define CLK_RATIO_N (0x3 << 16) +#define CLK_RATIO_N_SHIFT 16 +#define CLK_RATIO_N_MAX 0x3 +#define CLK_RATIO_M (0x1f << 0) +#define CLK_RATIO_M_SHIFT 0 +#define CLK_RATIO_M_MAX 0x1f + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-mod0-clk", 1 }, + { NULL, 0 } +}; + +struct aw_modclk_sc { + device_t clkdev; + bus_addr_t reg; + u_int parent_cnt; +}; + +#define MODCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define MODCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) +#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) +#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) + +static int +aw_modclk_init(struct clknode *clk, device_t dev) +{ + struct aw_modclk_sc *sc; + uint32_t val, index; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + index = (val & CLK_SRC_SEL) >> CLK_SRC_SEL_SHIFT; + + clknode_init_parent_idx(clk, index); + return (0); +} + +static int +aw_modclk_set_mux(struct clknode *clk, int index) +{ + struct aw_modclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + if (index < 0 || index >= sc->parent_cnt) + return (ERANGE); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + val &= ~CLK_SRC_SEL; + val |= (index << CLK_SRC_SEL_SHIFT); + MODCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_modclk_set_gate(struct clknode *clk, bool enable) +{ + struct aw_modclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + if (enable) + val |= SCLK_GATING; + else + val &= ~SCLK_GATING; + MODCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_modclk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct aw_modclk_sc *sc; + uint32_t val, m, n; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + DEVICE_UNLOCK(sc); + + n = 1 << ((val & CLK_RATIO_N) >> CLK_RATIO_N_SHIFT); + m = ((val & CLK_RATIO_M) >> CLK_RATIO_M_SHIFT) + 1; + + *freq = *freq / n / m; + + return (0); +} + +static int +aw_modclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct aw_modclk_sc *sc; + uint32_t val, m, n, src, best_m, best_n, best_src; + uint64_t cur_freq; + int64_t best_diff, cur_diff; + int error; + + sc = clknode_get_softc(clk); + best_n = best_m = 0; + best_diff = (int64_t)*fout; + best_src = 0; + + for (src = 0; src < sc->parent_cnt; src++) { + error = clknode_set_parent_by_idx(clk, src); + if (error != 0) + continue; + error = clknode_get_freq(clknode_get_parent(clk), &fin); + if (error != 0) + continue; + + for (n = 0; n <= CLK_RATIO_N_MAX; n++) + for (m = 0; m <= CLK_RATIO_M_MAX; m++) { + cur_freq = fin / (1 << n) / (m + 1); + cur_diff = (int64_t)*fout - cur_freq; + if (cur_diff >= 0 && cur_diff < best_diff) { + best_src = src; + best_diff = cur_diff; + best_m = m; + best_n = n; + } + } + } + + if (best_diff == (int64_t)*fout) + return (ERANGE); + + error = clknode_set_parent_by_idx(clk, best_src); + if (error != 0) + return (error); + error = clknode_get_freq(clknode_get_parent(clk), &fin); + if (error != 0) + return (error); + + DEVICE_LOCK(sc); + MODCLK_READ(sc, &val); + val &= ~(CLK_RATIO_N | CLK_RATIO_M); + val |= (best_n << CLK_RATIO_N_SHIFT); + val |= (best_m << CLK_RATIO_M_SHIFT); + MODCLK_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + *fout = fin / (1 << best_n) / (best_m + 1); + *stop = 1; + + return (0); +} + +static clknode_method_t aw_modclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_modclk_init), + CLKNODEMETHOD(clknode_set_gate, aw_modclk_set_gate), + CLKNODEMETHOD(clknode_set_mux, aw_modclk_set_mux), + CLKNODEMETHOD(clknode_recalc_freq, aw_modclk_recalc_freq), + CLKNODEMETHOD(clknode_set_freq, aw_modclk_set_freq), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(aw_modclk_clknode, aw_modclk_clknode_class, + aw_modclk_clknode_methods, sizeof(struct aw_modclk_sc), clknode_class); + +static int +aw_modclk_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, "Allwinner Module Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_modclk_attach(device_t dev) +{ + struct clknode_init_def def; + struct aw_modclk_sc *sc; + struct clkdom *clkdom; + struct clknode *clk; + clk_t clk_parent; + bus_addr_t paddr; + bus_size_t psize; + phandle_t node; + int error, ncells, i; + + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { + device_printf(dev, "cannot parse 'reg' property\n"); + return (ENXIO); + } + + error = ofw_bus_parse_xref_list_get_length(node, "clocks", + "#clock-cells", &ncells); + if (error != 0) { + device_printf(dev, "cannot get clock count\n"); + return (error); + } + + clkdom = clkdom_create(dev); + + memset(&def, 0, sizeof(def)); + error = clk_parse_ofw_clk_name(dev, node, &def.name); + if (error != 0) { + device_printf(dev, "cannot parse clock name\n"); + error = ENXIO; + goto fail; + } + def.id = 1; + def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK); + for (i = 0; i < ncells; i++) { + error = clk_get_by_ofw_index(dev, 0, i, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot get clock %d\n", i); + goto fail; + } + def.parent_names[i] = clk_get_name(clk_parent); + clk_release(clk_parent); + } + def.parent_cnt = ncells; + + clk = clknode_create(clkdom, &aw_modclk_clknode_class, &def); + if (clk == NULL) { + device_printf(dev, "cannot create clknode\n"); + error = ENXIO; + goto fail; + } + + sc = clknode_get_softc(clk); + sc->reg = paddr; + sc->clkdev = device_get_parent(dev); + sc->parent_cnt = def.parent_cnt; + + clknode_register(clkdom, clk); + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + if (bootverbose) + clkdom_dump(clkdom); + + return (0); + +fail: + return (error); +} + +static device_method_t aw_modclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_modclk_probe), + DEVMETHOD(device_attach, aw_modclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_modclk_driver = { + "aw_modclk", + aw_modclk_methods, + 0 +}; + +static devclass_t aw_modclk_devclass; + +EARLY_DRIVER_MODULE(aw_modclk, simplebus, aw_modclk_driver, + aw_modclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: trunk/sys/arm/allwinner/clk/aw_modclk.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/clk/aw_oscclk.c =================================================================== --- trunk/sys/arm/allwinner/clk/aw_oscclk.c (rev 0) +++ trunk/sys/arm/allwinner/clk/aw_oscclk.c 2020-03-06 17:15:48 UTC (rev 12400) @@ -0,0 +1,133 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/clk/aw_oscclk.c 299703 2016-05-13 22:28:02Z gonzo $ + */ + +/* + * Allwinner oscillator clock + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_oscclk.c 299703 2016-05-13 22:28:02Z gonzo $"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +static int +aw_oscclk_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-osc-clk")) + return (ENXIO); + + device_set_desc(dev, "Allwinner Oscillator Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_oscclk_attach(device_t dev) +{ + struct clk_fixed_def def; + struct clkdom *clkdom; + phandle_t node; + uint32_t freq; + int error; + + node = ofw_bus_get_node(dev); + + if (OF_getencprop(node, "clock-frequency", &freq, sizeof(freq)) <= 0) { + device_printf(dev, "missing clock-frequency property\n"); + error = ENXIO; + goto fail; + } + + clkdom = clkdom_create(dev); + + memset(&def, 0, sizeof(def)); + def.clkdef.id = 1; + def.freq = freq; + error = clk_parse_ofw_clk_name(dev, node, &def.clkdef.name); + if (error != 0) { + device_printf(dev, "cannot parse clock name\n"); + error = ENXIO; + goto fail; + } + + error = clknode_fixed_register(clkdom, &def); + if (error != 0) { + device_printf(dev, "cannot register fixed clock\n"); + error = ENXIO; + goto fail; + } + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + if (bootverbose) + clkdom_dump(clkdom); + + OF_prop_free(__DECONST(char *, def.clkdef.name)); + + return (0); + +fail: + OF_prop_free(__DECONST(char *, def.clkdef.name)); + return (error); +} + +static device_method_t aw_oscclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_oscclk_probe), + DEVMETHOD(device_attach, aw_oscclk_attach), + + DEVMETHOD_END +}; + +static driver_t aw_oscclk_driver = { + "aw_oscclk", + aw_oscclk_methods, + 0, +}; + +static devclass_t aw_oscclk_devclass; + +EARLY_DRIVER_MODULE(aw_oscclk, simplebus, aw_oscclk_driver, + aw_oscclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: trunk/sys/arm/allwinner/clk/aw_oscclk.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/clk/aw_pll.c =================================================================== --- trunk/sys/arm/allwinner/clk/aw_pll.c (rev 0) +++ trunk/sys/arm/allwinner/clk/aw_pll.c 2020-03-06 17:15:48 UTC (rev 12400) @@ -0,0 +1,903 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/clk/aw_pll.c 308324 2016-11-05 04:17:32Z mmel $ + */ + +/* + * Allwinner PLL clock + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_pll.c 308324 2016-11-05 04:17:32Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include + +#include "clkdev_if.h" + +#define AW_PLL_ENABLE (1 << 31) + +#define A10_PLL1_OUT_EXT_DIVP (0x3 << 16) +#define A10_PLL1_OUT_EXT_DIVP_SHIFT 16 +#define A10_PLL1_FACTOR_N (0x1f << 8) +#define A10_PLL1_FACTOR_N_SHIFT 8 +#define A10_PLL1_FACTOR_K (0x3 << 4) +#define A10_PLL1_FACTOR_K_SHIFT 4 +#define A10_PLL1_FACTOR_M (0x3 << 0) +#define A10_PLL1_FACTOR_M_SHIFT 0 + +#define A10_PLL2_POST_DIV (0xf << 26) +#define A10_PLL2_POST_DIV_SHIFT 26 +#define A10_PLL2_FACTOR_N (0x7f << 8) +#define A10_PLL2_FACTOR_N_SHIFT 8 +#define A10_PLL2_PRE_DIV (0x1f << 0) +#define A10_PLL2_PRE_DIV_SHIFT 0 + +#define A10_PLL3_MODE_SEL (0x1 << 15) +#define A10_PLL3_MODE_SEL_FRACT (0 << 15) +#define A10_PLL3_MODE_SEL_INT (1 << 15) +#define A10_PLL3_FUNC_SET (0x1 << 14) +#define A10_PLL3_FUNC_SET_270MHZ (0 << 14) +#define A10_PLL3_FUNC_SET_297MHZ (1 << 14) +#define A10_PLL3_FACTOR_M (0x7f << 0) +#define A10_PLL3_FACTOR_M_SHIFT 0 +#define A10_PLL3_REF_FREQ 3000000 + +#define A10_PLL5_OUT_EXT_DIVP (0x3 << 16) +#define A10_PLL5_OUT_EXT_DIVP_SHIFT 16 +#define A10_PLL5_FACTOR_N (0x1f << 8) +#define A10_PLL5_FACTOR_N_SHIFT 8 +#define A10_PLL5_FACTOR_K (0x3 << 4) +#define A10_PLL5_FACTOR_K_SHIFT 4 +#define A10_PLL5_FACTOR_M1 (0x3 << 2) +#define A10_PLL5_FACTOR_M1_SHIFT 2 +#define A10_PLL5_FACTOR_M (0x3 << 0) +#define A10_PLL5_FACTOR_M_SHIFT 0 + +#define A10_PLL6_BYPASS_EN (1 << 30) +#define A10_PLL6_SATA_CLK_EN (1 << 14) +#define A10_PLL6_FACTOR_N (0x1f << 8) +#define A10_PLL6_FACTOR_N_SHIFT 8 +#define A10_PLL6_FACTOR_K (0x3 << 4) +#define A10_PLL6_FACTOR_K_SHIFT 4 +#define A10_PLL6_FACTOR_M (0x3 << 0) +#define A10_PLL6_FACTOR_M_SHIFT 0 + +#define A10_PLL2_POST_DIV (0xf << 26) + +#define A13_PLL2_POST_DIV (0xf << 26) +#define A13_PLL2_POST_DIV_SHIFT 26 +#define A13_PLL2_FACTOR_N (0x7f << 8) +#define A13_PLL2_FACTOR_N_SHIFT 8 +#define A13_PLL2_PRE_DIV (0x1f << 0) +#define A13_PLL2_PRE_DIV_SHIFT 0 + +#define A23_PLL1_FACTOR_N (0x1f << 8) +#define A23_PLL1_FACTOR_N_SHIFT 8 +#define A23_PLL1_FACTOR_K (0x3 << 4) +#define A23_PLL1_FACTOR_K_SHIFT 4 +#define A23_PLL1_FACTOR_M (0x3 << 0) +#define A23_PLL1_FACTOR_M_SHIFT 0 +#define A23_PLL1_FACTOR_P (0x3 << 16) +#define A23_PLL1_FACTOR_P_SHIFT 16 + +#define A31_PLL1_LOCK (1 << 28) +#define A31_PLL1_CPU_SIGMA_DELTA_EN (1 << 24) +#define A31_PLL1_FACTOR_N (0x1f << 8) +#define A31_PLL1_FACTOR_N_SHIFT 8 +#define A31_PLL1_FACTOR_K (0x3 << 4) +#define A31_PLL1_FACTOR_K_SHIFT 4 +#define A31_PLL1_FACTOR_M (0x3 << 0) +#define A31_PLL1_FACTOR_M_SHIFT 0 + +#define A31_PLL6_LOCK (1 << 28) +#define A31_PLL6_BYPASS_EN (1 << 25) +#define A31_PLL6_CLK_OUT_EN (1 << 24) +#define A31_PLL6_24M_OUT_EN (1 << 18) +#define A31_PLL6_24M_POST_DIV (0x3 << 16) +#define A31_PLL6_24M_POST_DIV_SHIFT 16 +#define A31_PLL6_FACTOR_N (0x1f << 8) +#define A31_PLL6_FACTOR_N_SHIFT 8 +#define A31_PLL6_FACTOR_K (0x3 << 4) +#define A31_PLL6_FACTOR_K_SHIFT 4 +#define A31_PLL6_DEFAULT_N 0x18 +#define A31_PLL6_DEFAULT_K 0x1 +#define A31_PLL6_TIMEOUT 10 + +#define A80_PLL4_CLK_OUT_EN (1 << 20) +#define A80_PLL4_PLL_DIV2 (1 << 18) +#define A80_PLL4_PLL_DIV1 (1 << 16) +#define A80_PLL4_FACTOR_N (0xff << 8) +#define A80_PLL4_FACTOR_N_SHIFT 8 + +#define CLKID_A10_PLL3_1X 0 +#define CLKID_A10_PLL3_2X 1 + +#define CLKID_A10_PLL5_DDR 0 +#define CLKID_A10_PLL5_OTHER 1 + +#define CLKID_A10_PLL6_SATA 0 +#define CLKID_A10_PLL6_OTHER 1 +#define CLKID_A10_PLL6 2 +#define CLKID_A10_PLL6_DIV_4 3 + +#define CLKID_A31_PLL6 0 +#define CLKID_A31_PLL6_X2 1 + +enum aw_pll_type { + AWPLL_A10_PLL1 = 1, + AWPLL_A10_PLL2, + AWPLL_A10_PLL3, + AWPLL_A10_PLL5, + AWPLL_A10_PLL6, + AWPLL_A13_PLL2, + AWPLL_A23_PLL1, + AWPLL_A31_PLL1, + AWPLL_A31_PLL6, + AWPLL_A80_PLL4, +}; + +struct aw_pll_sc { + enum aw_pll_type type; + device_t clkdev; + bus_addr_t reg; + int id; +}; + +struct aw_pll_funcs { + int (*recalc)(struct aw_pll_sc *, uint64_t *); + int (*set_freq)(struct aw_pll_sc *, uint64_t, uint64_t *, int); + int (*init)(device_t, bus_addr_t, struct clknode_init_def *); +}; + +#define PLL_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) +#define PLL_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) +#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) +#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) + +static int +a10_pll1_recalc(struct aw_pll_sc *sc, uint64_t *freq) +{ + uint32_t val, m, n, k, p; + + DEVICE_LOCK(sc); + PLL_READ(sc, &val); + DEVICE_UNLOCK(sc); + + p = 1 << ((val & A10_PLL1_OUT_EXT_DIVP) >> A10_PLL1_OUT_EXT_DIVP_SHIFT); + m = ((val & A10_PLL1_FACTOR_M) >> A10_PLL1_FACTOR_M_SHIFT) + 1; + k = ((val & A10_PLL1_FACTOR_K) >> A10_PLL1_FACTOR_K_SHIFT) + 1; + n = (val & A10_PLL1_FACTOR_N) >> A10_PLL1_FACTOR_N_SHIFT; + if (n == 0) + n = 1; + + *freq = (*freq * n * k) / (m * p); + + return (0); +} + +static int +a10_pll2_recalc(struct aw_pll_sc *sc, uint64_t *freq) +{ + uint32_t val, post_div, n, pre_div; + + DEVICE_LOCK(sc); + PLL_READ(sc, &val); + DEVICE_UNLOCK(sc); + + post_div = (val & A10_PLL2_POST_DIV) >> A10_PLL2_POST_DIV_SHIFT; + if (post_div == 0) + post_div = 1; + n = (val & A10_PLL2_FACTOR_N) >> A10_PLL2_FACTOR_N_SHIFT; + if (n == 0) + n = 1; + pre_div = (val & A10_PLL2_PRE_DIV) >> A10_PLL2_PRE_DIV_SHIFT; + if (pre_div == 0) + pre_div = 1; + + switch (sc->id) { + case SUN4I_A10_PLL2_1X: + *freq = (*freq * 2 * n) / pre_div / post_div / 2; + break; + case SUN4I_A10_PLL2_2X: + *freq = (*freq * 2 * n) / pre_div / 4; + break; + case SUN4I_A10_PLL2_4X: + *freq = (*freq * 2 * n) / pre_div / 2; + break; + case SUN4I_A10_PLL2_8X: + *freq = (*freq * 2 * n) / pre_div; + break; + default: + return (EINVAL); + } + + return (0); +} + +static int +a10_pll2_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, + int flags) +{ + uint32_t val, post_div, n, pre_div; + + if (sc->id != SUN4I_A10_PLL2_1X) + return (ENXIO); + + /* + * Audio Codec needs PLL2-1X to be either 24576000 or 22579200. + * + * PLL2-1X output frequency is (48MHz * n) / pre_div / post_div / 2. + * To get as close as possible to the desired rate, we use a + * pre-divider of 21 and a post-divider of 4. With these values, + * a multiplier of 86 or 79 gets us close to the target rates. + */ + if (*fout != 24576000 && *fout != 22579200) + return (EINVAL); + + pre_div = 21; + post_div = 4; + n = (*fout * pre_div * post_div * 2) / (2 * fin); + + DEVICE_LOCK(sc); + PLL_READ(sc, &val); + val &= ~(A10_PLL2_POST_DIV | A10_PLL2_FACTOR_N | A10_PLL2_PRE_DIV); + val |= (post_div << A10_PLL2_POST_DIV_SHIFT); + val |= (n << A10_PLL2_FACTOR_N_SHIFT); + val |= (pre_div << A10_PLL2_PRE_DIV_SHIFT); + PLL_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +a10_pll3_recalc(struct aw_pll_sc *sc, uint64_t *freq) +{ + uint32_t val, m; + + DEVICE_LOCK(sc); + PLL_READ(sc, &val); + DEVICE_UNLOCK(sc); + + if ((val & A10_PLL3_MODE_SEL) == A10_PLL3_MODE_SEL_INT) { + /* In integer mode, output is 3MHz * m */ + m = (val & A10_PLL3_FACTOR_M) >> A10_PLL3_FACTOR_M_SHIFT; + *freq = A10_PLL3_REF_FREQ * m; + } else { + /* In fractional mode, output is either 270MHz or 297MHz */ + if ((val & A10_PLL3_FUNC_SET) == A10_PLL3_FUNC_SET_270MHZ) + *freq = 270000000; + else + *freq = 297000000; + } + + if (sc->id == CLKID_A10_PLL3_2X) + *freq *= 2; + + return (0); +} + +static int +a10_pll3_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, + int flags) +{ + uint32_t val, m, mode, func; + + m = *fout / A10_PLL3_REF_FREQ; + if (sc->id == CLKID_A10_PLL3_2X) + m /= 2; + + mode = A10_PLL3_MODE_SEL_INT; + func = 0; + *fout = m * A10_PLL3_REF_FREQ; + if (sc->id == CLKID_A10_PLL3_2X) + *fout *= 2; + + DEVICE_LOCK(sc); + PLL_READ(sc, &val); + val &= ~(A10_PLL3_MODE_SEL | A10_PLL3_FUNC_SET | A10_PLL3_FACTOR_M); + val |= mode; + val |= func; + val |= (m << A10_PLL3_FACTOR_M_SHIFT); + PLL_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +a10_pll3_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def) +{ + uint32_t val; + + /* Allow changing PLL frequency while enabled */ + def->flags = CLK_NODE_GLITCH_FREE; + + /* Set PLL to 297MHz */ + CLKDEV_DEVICE_LOCK(dev); + CLKDEV_READ_4(dev, reg, &val); + val &= ~(A10_PLL3_MODE_SEL | A10_PLL3_FUNC_SET | A10_PLL3_FACTOR_M); + val |= A10_PLL3_MODE_SEL_FRACT; + val |= A10_PLL3_FUNC_SET_297MHZ; + CLKDEV_WRITE_4(dev, reg, val); + CLKDEV_DEVICE_UNLOCK(dev); + + return (0); +} + +static int +a10_pll5_recalc(struct aw_pll_sc *sc, uint64_t *freq) +{ + uint32_t val, m, n, k, p; + + DEVICE_LOCK(sc); + PLL_READ(sc, &val); + DEVICE_UNLOCK(sc); + + p = 1 << ((val & A10_PLL5_OUT_EXT_DIVP) >> A10_PLL5_OUT_EXT_DIVP_SHIFT); + m = ((val & A10_PLL5_FACTOR_M) >> A10_PLL5_FACTOR_M_SHIFT) + 1; + k = ((val & A10_PLL5_FACTOR_K) >> A10_PLL5_FACTOR_K_SHIFT) + 1; + n = (val & A10_PLL5_FACTOR_N) >> A10_PLL5_FACTOR_N_SHIFT; + if (n == 0) + return (ENXIO); + + switch (sc->id) { + case CLKID_A10_PLL5_DDR: + *freq = (*freq * n * k) / m; + break; + case CLKID_A10_PLL5_OTHER: + *freq = (*freq * n * k) / p; + break; + default: + return (ENXIO); + } + + return (0); +} + +static int +a10_pll6_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def) +{ + uint32_t val, m, n, k; + + /* + * SATA needs PLL6 to be a 100MHz clock. + * + * The SATA output frequency is (24MHz * n * k) / m / 6. + * To get to 100MHz, k & m must be equal and n must be 25. + */ + m = k = 0; + n = 25; + + CLKDEV_DEVICE_LOCK(dev); + CLKDEV_READ_4(dev, reg, &val); + val &= ~(A10_PLL6_FACTOR_N | A10_PLL6_FACTOR_K | A10_PLL6_FACTOR_M); + val &= ~A10_PLL6_BYPASS_EN; + val |= A10_PLL6_SATA_CLK_EN; + val |= (n << A10_PLL6_FACTOR_N_SHIFT); + val |= (k << A10_PLL6_FACTOR_K_SHIFT); + val |= (m << A10_PLL6_FACTOR_M_SHIFT); + CLKDEV_WRITE_4(dev, reg, val); + CLKDEV_DEVICE_UNLOCK(dev); + + return (0); +} + +static int +a10_pll6_recalc(struct aw_pll_sc *sc, uint64_t *freq) +{ + uint32_t val, m, k, n; + + DEVICE_LOCK(sc); + PLL_READ(sc, &val); + DEVICE_UNLOCK(sc); + + m = ((val & A10_PLL6_FACTOR_M) >> A10_PLL6_FACTOR_M_SHIFT) + 1; + k = ((val & A10_PLL6_FACTOR_K) >> A10_PLL6_FACTOR_K_SHIFT) + 1; + n = (val & A10_PLL6_FACTOR_N) >> A10_PLL6_FACTOR_N_SHIFT; + if (n == 0) + return (ENXIO); + + switch (sc->id) { + case CLKID_A10_PLL6_SATA: + *freq = (*freq * n * k) / m / 6; + break; + case CLKID_A10_PLL6_OTHER: + *freq = (*freq * n * k) / 2; + break; + case CLKID_A10_PLL6: + *freq = (*freq * n * k); + break; + case CLKID_A10_PLL6_DIV_4: + *freq = (*freq * n * k) / 4; + break; + default: + return (ENXIO); + } + + return (0); +} + +static int +a10_pll6_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, + int flags) +{ + if (sc->id != CLKID_A10_PLL6_SATA) + return (ENXIO); + + /* PLL6 SATA output has been set to 100MHz in a10_pll6_init */ + if (*fout != 100000000) + return (ERANGE); + + return (0); +} + +static int +a13_pll2_recalc(struct aw_pll_sc *sc, uint64_t *freq) +{ + uint32_t val, post_div, n, pre_div; + + DEVICE_LOCK(sc); + PLL_READ(sc, &val); + DEVICE_UNLOCK(sc); + + post_div = ((val & A13_PLL2_POST_DIV) >> A13_PLL2_POST_DIV_SHIFT) + 1; + if (post_div == 0) + post_div = 1; + n = (val & A13_PLL2_FACTOR_N) >> A13_PLL2_FACTOR_N_SHIFT; + if (n == 0) + n = 1; + pre_div = ((val & A13_PLL2_PRE_DIV) >> A13_PLL2_PRE_DIV_SHIFT) + 1; + if (pre_div == 0) + pre_div = 1; + + switch (sc->id) { + case SUN4I_A10_PLL2_1X: + *freq = (*freq * 2 * n) / pre_div / post_div / 2; + break; + case SUN4I_A10_PLL2_2X: + *freq = (*freq * 2 * n) / pre_div / 4; + break; + case SUN4I_A10_PLL2_4X: + *freq = (*freq * 2 * n) / pre_div / 2; + break; + case SUN4I_A10_PLL2_8X: + *freq = (*freq * 2 * n) / pre_div; + break; + default: + return (EINVAL); + } + + return (0); +} + +static int +a13_pll2_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, + int flags) +{ + uint32_t val, post_div, n, pre_div; + + if (sc->id != SUN4I_A10_PLL2_1X) + return (ENXIO); + + /* + * Audio Codec needs PLL2-1X to be either 24576000 or 22579200. + * + * PLL2-1X output frequency is (48MHz * n) / pre_div / post_div / 2. + * To get as close as possible to the desired rate, we use a + * pre-divider of 21 and a post-divider of 4. With these values, + * a multiplier of 86 or 79 gets us close to the target rates. + */ + if (*fout != 24576000 && *fout != 22579200) + return (EINVAL); + + pre_div = 21; + post_div = 4; + n = (*fout * pre_div * post_div * 2) / (2 * fin); + + DEVICE_LOCK(sc); + PLL_READ(sc, &val); + val &= ~(A13_PLL2_POST_DIV | A13_PLL2_FACTOR_N | A13_PLL2_PRE_DIV); + val |= ((post_div - 1) << A13_PLL2_POST_DIV_SHIFT); + val |= (n << A13_PLL2_FACTOR_N_SHIFT); + val |= ((pre_div - 1) << A13_PLL2_PRE_DIV_SHIFT); + PLL_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +a23_pll1_recalc(struct aw_pll_sc *sc, uint64_t *freq) +{ + uint32_t val, m, n, k, p; + + DEVICE_LOCK(sc); + PLL_READ(sc, &val); + DEVICE_UNLOCK(sc); + + m = ((val & A23_PLL1_FACTOR_M) >> A23_PLL1_FACTOR_M_SHIFT) + 1; + k = ((val & A23_PLL1_FACTOR_K) >> A23_PLL1_FACTOR_K_SHIFT) + 1; + n = ((val & A23_PLL1_FACTOR_N) >> A23_PLL1_FACTOR_N_SHIFT) + 1; + p = ((val & A23_PLL1_FACTOR_P) >> A23_PLL1_FACTOR_P_SHIFT) + 1; + + *freq = (*freq * n * k) / (m * p); + + return (0); +} + +static int +a31_pll1_recalc(struct aw_pll_sc *sc, uint64_t *freq) +{ + uint32_t val, m, n, k; + + DEVICE_LOCK(sc); + PLL_READ(sc, &val); + DEVICE_UNLOCK(sc); + + m = ((val & A31_PLL1_FACTOR_M) >> A31_PLL1_FACTOR_M_SHIFT) + 1; + k = ((val & A31_PLL1_FACTOR_K) >> A31_PLL1_FACTOR_K_SHIFT) + 1; + n = ((val & A31_PLL1_FACTOR_N) >> A31_PLL1_FACTOR_N_SHIFT) + 1; + + *freq = (*freq * n * k) / m; + + return (0); +} + +static int +a31_pll6_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def) +{ + uint32_t val; + int retry; + + if (def->id != CLKID_A31_PLL6) + return (0); + + /* + * The datasheet recommends that PLL6 output should be fixed to + * 600MHz. + */ + CLKDEV_DEVICE_LOCK(dev); + CLKDEV_READ_4(dev, reg, &val); + val &= ~(A31_PLL6_FACTOR_N | A31_PLL6_FACTOR_K | A31_PLL6_BYPASS_EN); + val |= (A31_PLL6_DEFAULT_N << A31_PLL6_FACTOR_N_SHIFT); + val |= (A31_PLL6_DEFAULT_K << A31_PLL6_FACTOR_K_SHIFT); + CLKDEV_WRITE_4(dev, reg, val); + + /* Wait for PLL to become stable */ + for (retry = A31_PLL6_TIMEOUT; retry > 0; retry--) { + CLKDEV_READ_4(dev, reg, &val); + if ((val & A31_PLL6_LOCK) == A31_PLL6_LOCK) + break; + DELAY(1); + } + + CLKDEV_DEVICE_UNLOCK(dev); + + if (retry == 0) + return (ETIMEDOUT); + + return (0); +} + +static int +a31_pll6_recalc(struct aw_pll_sc *sc, uint64_t *freq) +{ + uint32_t val, k, n; + + DEVICE_LOCK(sc); + PLL_READ(sc, &val); + DEVICE_UNLOCK(sc); + + k = ((val & A10_PLL6_FACTOR_K) >> A10_PLL6_FACTOR_K_SHIFT) + 1; + n = ((val & A10_PLL6_FACTOR_N) >> A10_PLL6_FACTOR_N_SHIFT) + 1; + + switch (sc->id) { + case CLKID_A31_PLL6: + *freq = (*freq * n * k) / 2; + break; + case CLKID_A31_PLL6_X2: + *freq = *freq * n * k; + break; + default: + return (ENXIO); + } + + return (0); +} + +static int +a80_pll4_recalc(struct aw_pll_sc *sc, uint64_t *freq) +{ + uint32_t val, n, div1, div2; + + DEVICE_LOCK(sc); + PLL_READ(sc, &val); + DEVICE_UNLOCK(sc); + + n = (val & A80_PLL4_FACTOR_N) >> A80_PLL4_FACTOR_N_SHIFT; + div1 = (val & A80_PLL4_PLL_DIV1) == 0 ? 1 : 2; + div2 = (val & A80_PLL4_PLL_DIV2) == 0 ? 1 : 2; + + *freq = (*freq * n) / div1 / div2; + + return (0); +} + +#define PLL(_type, _recalc, _set_freq, _init) \ + [(_type)] = { \ + .recalc = (_recalc), \ + .set_freq = (_set_freq), \ + .init = (_init) \ + } + +static struct aw_pll_funcs aw_pll_func[] = { + PLL(AWPLL_A10_PLL1, a10_pll1_recalc, NULL, NULL), + PLL(AWPLL_A10_PLL2, a10_pll2_recalc, a10_pll2_set_freq, NULL), + PLL(AWPLL_A10_PLL3, a10_pll3_recalc, a10_pll3_set_freq, a10_pll3_init), + PLL(AWPLL_A10_PLL5, a10_pll5_recalc, NULL, NULL), + PLL(AWPLL_A10_PLL6, a10_pll6_recalc, a10_pll6_set_freq, a10_pll6_init), + PLL(AWPLL_A13_PLL2, a13_pll2_recalc, a13_pll2_set_freq, NULL), + PLL(AWPLL_A23_PLL1, a23_pll1_recalc, NULL, NULL), + PLL(AWPLL_A31_PLL1, a31_pll1_recalc, NULL, NULL), + PLL(AWPLL_A31_PLL6, a31_pll6_recalc, NULL, a31_pll6_init), + PLL(AWPLL_A80_PLL4, a80_pll4_recalc, NULL, NULL), +}; + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-pll1-clk", AWPLL_A10_PLL1 }, + { "allwinner,sun4i-a10-pll2-clk", AWPLL_A10_PLL2 }, + { "allwinner,sun4i-a10-pll3-clk", AWPLL_A10_PLL3 }, + { "allwinner,sun4i-a10-pll5-clk", AWPLL_A10_PLL5 }, + { "allwinner,sun4i-a10-pll6-clk", AWPLL_A10_PLL6 }, + { "allwinner,sun5i-a13-pll2-clk", AWPLL_A13_PLL2 }, + { "allwinner,sun6i-a31-pll1-clk", AWPLL_A31_PLL1 }, + { "allwinner,sun6i-a31-pll6-clk", AWPLL_A31_PLL6 }, + { "allwinner,sun8i-a23-pll1-clk", AWPLL_A23_PLL1 }, + { "allwinner,sun9i-a80-pll4-clk", AWPLL_A80_PLL4 }, + { NULL, 0 } +}; + +static int +aw_pll_init(struct clknode *clk, device_t dev) +{ + clknode_init_parent_idx(clk, 0); + return (0); +} + +static int +aw_pll_set_gate(struct clknode *clk, bool enable) +{ + struct aw_pll_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + PLL_READ(sc, &val); + if (enable) + val |= AW_PLL_ENABLE; + else + val &= ~AW_PLL_ENABLE; + PLL_WRITE(sc, val); + DEVICE_UNLOCK(sc); + + return (0); +} + +static int +aw_pll_recalc(struct clknode *clk, uint64_t *freq) +{ + struct aw_pll_sc *sc; + + sc = clknode_get_softc(clk); + + if (aw_pll_func[sc->type].recalc == NULL) + return (ENXIO); + + return (aw_pll_func[sc->type].recalc(sc, freq)); +} + +static int +aw_pll_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct aw_pll_sc *sc; + + sc = clknode_get_softc(clk); + + *stop = 1; + + if (aw_pll_func[sc->type].set_freq == NULL) + return (ENXIO); + + return (aw_pll_func[sc->type].set_freq(sc, fin, fout, flags)); +} + +static clknode_method_t aw_pll_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, aw_pll_init), + CLKNODEMETHOD(clknode_set_gate, aw_pll_set_gate), + CLKNODEMETHOD(clknode_recalc_freq, aw_pll_recalc), + CLKNODEMETHOD(clknode_set_freq, aw_pll_set_freq), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(aw_pll_clknode, aw_pll_clknode_class, aw_pll_clknode_methods, + sizeof(struct aw_pll_sc), clknode_class); + +static int +aw_pll_create(device_t dev, bus_addr_t paddr, struct clkdom *clkdom, + const char *pclkname, const char *clkname, int index) +{ + enum aw_pll_type type; + struct clknode_init_def clkdef; + struct aw_pll_sc *sc; + struct clknode *clk; + int error; + + type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + memset(&clkdef, 0, sizeof(clkdef)); + clkdef.id = index; + clkdef.name = clkname; + if (pclkname != NULL) { + clkdef.parent_names = malloc(sizeof(char *), M_OFWPROP, + M_WAITOK); + clkdef.parent_names[0] = pclkname; + clkdef.parent_cnt = 1; + } else + clkdef.parent_cnt = 0; + + if (aw_pll_func[type].init != NULL) { + error = aw_pll_func[type].init(device_get_parent(dev), + paddr, &clkdef); + if (error != 0) { + device_printf(dev, "clock %s init failed\n", clkname); + return (error); + } + } + + clk = clknode_create(clkdom, &aw_pll_clknode_class, &clkdef); + if (clk == NULL) { + device_printf(dev, "cannot create clock node\n"); + return (ENXIO); + } + sc = clknode_get_softc(clk); + sc->clkdev = device_get_parent(dev); + sc->reg = paddr; + sc->type = type; + sc->id = clkdef.id; + + clknode_register(clkdom, clk); + + OF_prop_free(__DECONST(char *, clkdef.parent_names)); + + return (0); +} + +static int +aw_pll_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, "Allwinner PLL Clock"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_pll_attach(device_t dev) +{ + struct clkdom *clkdom; + const char **names; + int index, nout, error; + clk_t clk_parent; + uint32_t *indices; + bus_addr_t paddr; + bus_size_t psize; + phandle_t node; + + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { + device_printf(dev, "couldn't parse 'reg' property\n"); + return (ENXIO); + } + + clkdom = clkdom_create(dev); + + nout = clk_parse_ofw_out_names(dev, node, &names, &indices); + if (nout == 0) { + device_printf(dev, "no clock outputs found\n"); + error = ENOENT; + goto fail; + } + + if (clk_get_by_ofw_index(dev, 0, 0, &clk_parent) != 0) + clk_parent = NULL; + + for (index = 0; index < nout; index++) { + error = aw_pll_create(dev, paddr, clkdom, + clk_parent ? clk_get_name(clk_parent) : NULL, + names[index], nout == 1 ? 1 : index); + if (error) + goto fail; + } + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + if (bootverbose) + clkdom_dump(clkdom); + + return (0); + +fail: + return (error); +} + +static device_method_t aw_pll_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_pll_probe), + DEVMETHOD(device_attach, aw_pll_attach), + + DEVMETHOD_END +}; + +static driver_t aw_pll_driver = { + "aw_pll", + aw_pll_methods, + 0, +}; + +static devclass_t aw_pll_devclass; + +EARLY_DRIVER_MODULE(aw_pll, simplebus, aw_pll_driver, + aw_pll_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: trunk/sys/arm/allwinner/clk/aw_pll.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/clk/aw_usbclk.c =================================================================== --- trunk/sys/arm/allwinner/clk/aw_usbclk.c (rev 0) +++ trunk/sys/arm/allwinner/clk/aw_usbclk.c 2020-03-06 17:15:48 UTC (rev 12400) @@ -0,0 +1,268 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/clk/aw_usbclk.c 308324 2016-11-05 04:17:32Z mmel $ + */ + +/* + * Allwinner USB clocks + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_usbclk.c 308324 2016-11-05 04:17:32Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "clkdev_if.h" +#include "hwreset_if.h" + +#define A10_SCLK_GATING_USBPHY (1 << 8) +#define A10_SCLK_GATING_OHCI1 (1 << 7) +#define A10_SCLK_GATING_OHCI0 (1 << 6) + +#define USBPHY2_RST (1 << 2) +#define USBPHY1_RST (1 << 1) +#define USBPHY0_RST (1 << 0) + +enum aw_usbclk_type { + AW_A10_USBCLK = 1, + AW_A13_USBCLK, + AW_A31_USBCLK, + AW_A83T_USBCLK, + AW_H3_USBCLK, +}; + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-usb-clk", AW_A10_USBCLK }, + { "allwinner,sun5i-a13-usb-clk", AW_A13_USBCLK }, + { "allwinner,sun6i-a31-usb-clk", AW_A31_USBCLK }, + { "allwinner,sun8i-a83t-usb-clk", AW_A83T_USBCLK }, + { "allwinner,sun8i-h3-usb-clk", AW_H3_USBCLK }, + { NULL, 0 } +}; + +/* Clock indices for A10, as there is no clock-indices property in the DT */ +static uint32_t aw_usbclk_indices_a10[] = { 6, 7, 8 }; +/* Clock indices for H3, as there is no clock-indices property in the DT */ +static uint32_t aw_usbclk_indices_h3[] = { 8, 9, 10, 11, 16, 17, 18, 19 }; + +struct aw_usbclk_softc { + bus_addr_t reg; +}; + +static int +aw_usbclk_hwreset_assert(device_t dev, intptr_t id, bool value) +{ + struct aw_usbclk_softc *sc; + uint32_t mask; + device_t pdev; + int error; + + sc = device_get_softc(dev); + pdev = device_get_parent(dev); + + mask = USBPHY0_RST << id; + + CLKDEV_DEVICE_LOCK(pdev); + error = CLKDEV_MODIFY_4(pdev, sc->reg, mask, value ? 0 : mask); + CLKDEV_DEVICE_UNLOCK(pdev); + + return (error); +} + +static int +aw_usbclk_hwreset_is_asserted(device_t dev, intptr_t id, bool *value) +{ + struct aw_usbclk_softc *sc; + uint32_t mask, val; + device_t pdev; + int error; + + sc = device_get_softc(dev); + pdev = device_get_parent(dev); + + mask = USBPHY0_RST << id; + + CLKDEV_DEVICE_LOCK(pdev); + error = CLKDEV_READ_4(pdev, sc->reg, &val); + CLKDEV_DEVICE_UNLOCK(pdev); + + if (error) + return (error); + + *value = (val & mask) != 0 ? false : true; + + return (0); +} + +static int +aw_usbclk_create(device_t dev, bus_addr_t paddr, struct clkdom *clkdom, + const char *pclkname, const char *clkname, int index) +{ + const char *parent_names[1] = { pclkname }; + struct clk_gate_def def; + + memset(&def, 0, sizeof(def)); + def.clkdef.id = index; + def.clkdef.name = clkname; + def.clkdef.parent_names = parent_names; + def.clkdef.parent_cnt = 1; + def.offset = paddr; + def.shift = index; + def.mask = 1; + def.on_value = 1; + def.off_value = 0; + + return (clknode_gate_register(clkdom, &def)); +} + +static int +aw_usbclk_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, "Allwinner USB Clocks"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_usbclk_attach(device_t dev) +{ + struct aw_usbclk_softc *sc; + struct clkdom *clkdom; + const char **names; + const char *pname; + int index, nout, error; + enum aw_usbclk_type type; + uint32_t *indices; + clk_t clk_parent, clk_parent_pll; + bus_size_t psize; + phandle_t node; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + indices = NULL; + type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + if (ofw_reg_to_paddr(node, 0, &sc->reg, &psize, NULL) != 0) { + device_printf(dev, "cannot parse 'reg' property\n"); + return (ENXIO); + } + + clkdom = clkdom_create(dev); + + nout = clk_parse_ofw_out_names(dev, node, &names, &indices); + if (nout == 0) { + device_printf(dev, "no clock outputs found\n"); + error = ENOENT; + goto fail; + } + + if (indices == NULL && type == AW_A10_USBCLK) + indices = aw_usbclk_indices_a10; + else if (indices == NULL && type == AW_H3_USBCLK) + indices = aw_usbclk_indices_h3; + + error = clk_get_by_ofw_index(dev, 0, 0, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot parse clock parent\n"); + return (ENXIO); + } + if (type == AW_A83T_USBCLK) { + error = clk_get_by_ofw_index(dev, 0, 1, &clk_parent_pll); + if (error != 0) { + device_printf(dev, "cannot parse pll clock parent\n"); + return (ENXIO); + } + } + + for (index = 0; index < nout; index++) { + if (strcmp(names[index], "usb_hsic_pll") == 0) + pname = clk_get_name(clk_parent_pll); + else + pname = clk_get_name(clk_parent); + error = aw_usbclk_create(dev, sc->reg, clkdom, pname, + names[index], indices != NULL ? indices[index] : index); + if (error) + goto fail; + } + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + error = ENXIO; + goto fail; + } + + if (bootverbose) + clkdom_dump(clkdom); + + hwreset_register_ofw_provider(dev); + + return (0); + +fail: + return (error); +} + +static device_method_t aw_usbclk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_usbclk_probe), + DEVMETHOD(device_attach, aw_usbclk_attach), + + /* Reset interface */ + DEVMETHOD(hwreset_assert, aw_usbclk_hwreset_assert), + DEVMETHOD(hwreset_is_asserted, aw_usbclk_hwreset_is_asserted), + + DEVMETHOD_END +}; + +static driver_t aw_usbclk_driver = { + "aw_usbclk", + aw_usbclk_methods, + sizeof(struct aw_usbclk_softc) +}; + +static devclass_t aw_usbclk_devclass; + +EARLY_DRIVER_MODULE(aw_usbclk, simplebus, aw_usbclk_driver, + aw_usbclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Property changes on: trunk/sys/arm/allwinner/clk/aw_usbclk.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property From laffer1 at midnightbsd.org Sun Mar 8 13:21:07 2020 From: laffer1 at midnightbsd.org (laffer1 at midnightbsd.org) Date: Sun, 8 Mar 2020 13:21:07 -0400 (EDT) Subject: [Midnightbsd-cvs] src [12401] U trunk/sys/arm/allwinner/a20/std.a20: sync with freebsd 11 stable Message-ID: <202003081721.028HL7eu085974@stargazer.midnightbsd.org> Revision: 12401 http://svnweb.midnightbsd.org/src/?rev=12401 Author: laffer1 Date: 2020-03-08 13:21:06 -0400 (Sun, 08 Mar 2020) Log Message: ----------- sync with freebsd 11 stable Modified Paths: -------------- trunk/sys/arm/allwinner/a20/a20_cpu_cfg.c trunk/sys/arm/allwinner/a20/a20_cpu_cfg.h trunk/sys/arm/allwinner/a20/files.a20 trunk/sys/arm/allwinner/a20/std.a20 Added Paths: ----------- trunk/sys/arm/allwinner/a20/a20_padconf.c Property Changed: ---------------- trunk/sys/arm/allwinner/a20/files.a20 trunk/sys/arm/allwinner/a20/std.a20 Modified: trunk/sys/arm/allwinner/a20/a20_cpu_cfg.c =================================================================== --- trunk/sys/arm/allwinner/a20/a20_cpu_cfg.c 2020-03-06 17:15:48 UTC (rev 12400) +++ trunk/sys/arm/allwinner/a20/a20_cpu_cfg.c 2020-03-08 17:21:06 UTC (rev 12401) @@ -28,7 +28,7 @@ /* CPU configuration module for Allwinner A20 */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/allwinner/a20/a20_cpu_cfg.c 266337 2014-05-17 18:53:36Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a20/a20_cpu_cfg.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -50,7 +50,6 @@ #include #include -#include #include "a20_cpu_cfg.h" @@ -119,7 +118,8 @@ static devclass_t a20_cpu_cfg_devclass; -DRIVER_MODULE(a20_cpu_cfg, simplebus, a20_cpu_cfg_driver, a20_cpu_cfg_devclass, 0, 0); +EARLY_DRIVER_MODULE(a20_cpu_cfg, simplebus, a20_cpu_cfg_driver, a20_cpu_cfg_devclass, 0, 0, + BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); uint64_t a20_read_counter64(void) Modified: trunk/sys/arm/allwinner/a20/a20_cpu_cfg.h =================================================================== --- trunk/sys/arm/allwinner/a20/a20_cpu_cfg.h 2020-03-06 17:15:48 UTC (rev 12400) +++ trunk/sys/arm/allwinner/a20/a20_cpu_cfg.h 2020-03-08 17:21:06 UTC (rev 12401) @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/allwinner/a20/a20_cpu_cfg.h 266337 2014-05-17 18:53:36Z ian $ + * $FreeBSD: stable/11/sys/arm/allwinner/a20/a20_cpu_cfg.h 331722 2018-03-29 02:50:57Z eadler $ */ #ifndef _A20_CPU_CFG_H_ Added: trunk/sys/arm/allwinner/a20/a20_padconf.c =================================================================== --- trunk/sys/arm/allwinner/a20/a20_padconf.c (rev 0) +++ trunk/sys/arm/allwinner/a20/a20_padconf.c 2020-03-08 17:21:06 UTC (rev 12401) @@ -0,0 +1,232 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a20/a20_padconf.c 305437 2016-09-05 20:29:04Z manu $"); + +#include +#include +#include +#include + +#include + +#ifdef SOC_ALLWINNER_A20 + +const static struct allwinner_pins a20_pins[] = { + {"PA0", 0, 0, {"gpio_in", "gpio_out", "emac", "spi1", "uart2", "gmac", NULL, NULL}}, + {"PA1", 0, 1, {"gpio_in", "gpio_out", "emac", "spi1", "uart2", "gmac", NULL, NULL}}, + {"PA2", 0, 2, {"gpio_in", "gpio_out", "emac", "spi1", "uart2", "gmac", NULL, NULL}}, + {"PA3", 0, 3, {"gpio_in", "gpio_out", "emac", "spi1", "uart2", "gmac", NULL, NULL}}, + {"PA4", 0, 4, {"gpio_in", "gpio_out", "emac", "spi1", NULL, "gmac", NULL, NULL}}, + {"PA5", 0, 5, {"gpio_in", "gpio_out", "emac", "spi3", NULL, "gmac", NULL, NULL}}, + {"PA6", 0, 6, {"gpio_in", "gpio_out", "emac", "spi3", NULL, "gmac", NULL, NULL}}, + {"PA7", 0, 7, {"gpio_in", "gpio_out", "emac", "spi3", NULL, "gmac", NULL, NULL}}, + {"PA8", 0, 8, {"gpio_in", "gpio_out", "emac", "spi3", NULL, "gmac", NULL, NULL}}, + {"PA9", 0, 9, {"gpio_in", "gpio_out", "emac", "spi3", NULL, "gmac", "i2c1", NULL}}, + {"PA10", 0, 10, {"gpio_in", "gpio_out", "emac", NULL, "uart1", "gmac", NULL, NULL}}, + {"PA11", 0, 11, {"gpio_in", "gpio_out", "emac", NULL, "uart1", "gmac", NULL, NULL}}, + {"PA12", 0, 12, {"gpio_in", "gpio_out", "emac", "uart6", "uart1", "gmac", NULL, NULL}}, + {"PA13", 0, 13, {"gpio_in", "gpio_out", "emac", "uart6", "uart1", "gmac", NULL, NULL}}, + {"PA14", 0, 14, {"gpio_in", "gpio_out", "emac", "uart7", "uart1", "gmac", "i2c1", NULL}}, + {"PA15", 0, 15, {"gpio_in", "gpio_out", "emac", "uart7", "uart1", "gmac", "i2c1", NULL}}, + {"PA16", 0, 16, {"gpio_in", "gpio_out", NULL, "can", "uart1", "gmac", "i2c1", NULL}}, + {"PA17", 0, 17, {"gpio_in", "gpio_out", NULL, "can", "uart1", "gmac", "i2c1", NULL}}, + + {"PB0", 1, 0, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}}, + {"PB1", 1, 1, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}}, + {"PB2", 1, 2, {"gpio_in", "gpio_out", "pwm", NULL, NULL, NULL, NULL, NULL}}, + {"PB3", 1, 3, {"gpio_in", "gpio_out", "ir0", NULL, "spdif", NULL, NULL, NULL}}, + {"PB4", 1, 4, {"gpio_in", "gpio_out", "ir0", NULL, NULL, NULL, NULL, NULL}}, + {"PB5", 1, 5, {"gpio_in", "gpio_out", "i2s0", "ac97", NULL, NULL, NULL, NULL}}, + {"PB6", 1, 6, {"gpio_in", "gpio_out", "i2c0", "ac97", NULL, NULL, NULL, NULL}}, + {"PB7", 1, 7, {"gpio_in", "gpio_out", "i2c0", "ac97", NULL, NULL, NULL, NULL}}, + {"PB8", 1, 8, {"gpio_in", "gpio_out", "i2c0", "ac97", NULL, NULL, NULL, NULL}}, + {"PB9", 1, 9, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}}, + {"PB10", 1, 10, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}}, + {"PB11", 1, 11, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}}, + {"PB12", 1, 12, {"gpio_in", "gpio_out", "i2c0", "ac97", "spdif", NULL, NULL, NULL}}, + {"PB13", 1, 13, {"gpio_in", "gpio_out", "spi2", NULL, "spdif", NULL, NULL, NULL}}, + {"PB14", 1, 14, {"gpio_in", "gpio_out", "spi2", "jtag", NULL, NULL, NULL, NULL}}, + {"PB15", 1, 15, {"gpio_in", "gpio_out", "spi2", "jtag", NULL, NULL, NULL, NULL}}, + {"PB16", 1, 16, {"gpio_in", "gpio_out", "spi2", "jtag", NULL, NULL, NULL, NULL}}, + {"PB17", 1, 17, {"gpio_in", "gpio_out", "spi2", "jtag", NULL, NULL, NULL, NULL}}, + {"PB18", 1, 18, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}}, + {"PB19", 1, 19, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}}, + {"PB20", 1, 20, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}}, + {"PB21", 1, 21, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}}, + {"PB22", 1, 22, {"gpio_in", "gpio_out", "uart0", "ir1", NULL, NULL, NULL, NULL}}, + {"PB23", 1, 23, {"gpio_in", "gpio_out", "uart0", "ir1", NULL, NULL, NULL, NULL}}, + + {"PC0", 2, 0, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}}, + {"PC1", 2, 1, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}}, + {"PC2", 2, 2, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}}, + {"PC3", 2, 3, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC4", 2, 4, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC5", 2, 5, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC6", 2, 6, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC7", 2, 7, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC8", 2, 8, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC9", 2, 9, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC10", 2, 10, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC11", 2, 11, {"gpio_in", "gpio_out", "nand0", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC12", 2, 12, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC13", 2, 13, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC14", 2, 14, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC15", 2, 15, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC16", 2, 16, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC17", 2, 17, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC18", 2, 18, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC19", 2, 19, {"gpio_in", "gpio_out", "nand0", "spi2", NULL, NULL, "eint", NULL}}, + {"PC20", 2, 20, {"gpio_in", "gpio_out", "nand0", "spi2", NULL, NULL, "eint", NULL}}, + {"PC21", 2, 21, {"gpio_in", "gpio_out", "nand0", "spi2", NULL, NULL, "eint", NULL}}, + {"PC22", 2, 22, {"gpio_in", "gpio_out", "nand0", "spi2", NULL, NULL, "eint", NULL}}, + {"PC23", 2, 23, {"gpio_in", "gpio_out", NULL, "spi0", NULL, NULL, NULL, NULL}}, + {"PC24", 2, 24, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + + {"PD0", 3, 0, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD1", 3, 1, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD2", 3, 2, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD3", 3, 3, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD4", 3, 4, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD5", 3, 5, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD6", 3, 6, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD7", 3, 7, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD8", 3, 8, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD9", 3, 9, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD10", 3, 10, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD11", 3, 11, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD12", 3, 12, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD13", 3, 13, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD14", 3, 14, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD15", 3, 15, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD16", 3, 16, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD17", 3, 17, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD18", 3, 18, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD19", 3, 19, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD20", 3, 20, {"gpio_in", "gpio_out", "lcd0", "csi1", NULL, NULL, NULL, NULL}}, + {"PD21", 3, 21, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}}, + {"PD22", 3, 22, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}}, + {"PD23", 3, 23, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}}, + {"PD24", 3, 24, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}}, + {"PD25", 3, 25, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}}, + {"PD26", 3, 26, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}}, + {"PD27", 3, 27, {"gpio_in", "gpio_out", "lcd0", "sim", NULL, NULL, NULL, NULL}}, + + {"PE0", 4, 0, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE1", 4, 1, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE2", 4, 2, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE3", 4, 3, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE4", 4, 4, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE5", 4, 5, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE6", 4, 6, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE7", 4, 7, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE8", 4, 8, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE9", 4, 9, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE10", 4, 10, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + {"PE11", 4, 11, {"gpio_in", "gpio_out", "ts0", "csi0", NULL, NULL, NULL, NULL}}, + + {"PF0", 5, 0, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + {"PF1", 5, 1, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + {"PF2", 5, 2, {"gpio_in", "gpio_out", "mmc0", NULL, "uart0", NULL, NULL, NULL}}, + {"PF3", 5, 3, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + {"PF4", 5, 4, {"gpio_in", "gpio_out", "mmc0", NULL, "uart0", NULL, NULL, NULL}}, + {"PF5", 5, 5, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + + {"PG0", 6, 0, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", NULL, NULL, NULL}}, + {"PG1", 6, 1, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", NULL, NULL, NULL}}, + {"PG2", 6, 2, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", NULL, NULL, NULL}}, + {"PG3", 6, 3, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", NULL, NULL, NULL}}, + {"PG4", 6, 4, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", "csi0", NULL, NULL}}, + {"PG5", 6, 5, {"gpio_in", "gpio_out", "ts1", "csi1", "mmc1", "csi0", NULL, NULL}}, + {"PG6", 6, 6, {"gpio_in", "gpio_out", "ts1", "csi1", "uart3", "csi0", NULL, NULL}}, + {"PG7", 6, 7, {"gpio_in", "gpio_out", "ts1", "csi1", "uart3", "csi0", NULL, NULL}}, + {"PG8", 6, 8, {"gpio_in", "gpio_out", "ts1", "csi1", "uart3", "csi0", NULL, NULL}}, + {"PG9", 6, 9, {"gpio_in", "gpio_out", "ts1", "csi1", "uart3", "csi0", NULL, NULL}}, + {"PG10", 6, 10, {"gpio_in", "gpio_out", "ts1", "csi1", "uart4", "csi0", NULL, NULL}}, + {"PG11", 6, 11, {"gpio_in", "gpio_out", "ts1", "csi1", "uart4", "csi0", NULL, NULL}}, + + {"PH0", 7, 0, {"gpio_in", "gpio_out", "lcd1", NULL, "uart3", NULL, "eint", "csi1"}}, + {"PH1", 7, 1, {"gpio_in", "gpio_out", "lcd1", NULL, "uart3", NULL, "eint", "csi1"}}, + {"PH2", 7, 2, {"gpio_in", "gpio_out", "lcd1", NULL, "uart3", NULL, "eint", "csi1"}}, + {"PH3", 7, 3, {"gpio_in", "gpio_out", "lcd1", NULL, "uart3", NULL, "eint", "csi1"}}, + {"PH4", 7, 4, {"gpio_in", "gpio_out", "lcd1", NULL, "uart4", NULL, "eint", "csi1"}}, + {"PH5", 7, 5, {"gpio_in", "gpio_out", "lcd1", NULL, "uart4", NULL, "eint", "csi1"}}, + {"PH6", 7, 6, {"gpio_in", "gpio_out", "lcd1", NULL, "uart5", "ms", "eint", "csi1"}}, + {"PH7", 7, 7, {"gpio_in", "gpio_out", "lcd1", NULL, "uart5", "ms", "eint", "csi1"}}, + {"PH8", 7, 8, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "ms", "eint", "csi1"}}, + {"PH9", 7, 9, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "ms", "eint", "csi1"}}, + {"PH10", 7, 10, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "ms", "eint", "csi1"}}, + {"PH11", 7, 11, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "ms", "eint", "csi1"}}, + {"PH12", 7, 12, {"gpio_in", "gpio_out", "lcd1", NULL, "ps2", NULL, "eint", "csi1"}}, + {"PH13", 7, 13, {"gpio_in", "gpio_out", "lcd1", NULL, "ps2", "sim", "eint", "csi1"}}, + {"PH14", 7, 14, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "sim", "eint", "csi1"}}, + {"PH15", 7, 15, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "sim", "eint", "csi1"}}, + {"PH16", 7, 16, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "sim", "eint", "csi1"}}, + {"PH17", 7, 17, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "sim", "eint", "csi1"}}, + {"PH18", 7, 18, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "sim", "eint", "csi1"}}, + {"PH19", 7, 19, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "sim", "eint", "csi1"}}, + {"PH20", 7, 20, {"gpio_in", "gpio_out", "lcd1", NULL, "can", NULL, "eint", "csi1"}}, + {"PH21", 7, 21, {"gpio_in", "gpio_out", "lcd1", NULL, "can", NULL, "eint", "csi1"}}, + {"PH22", 7, 22, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "mmc1", NULL, "csi1"}}, + {"PH23", 7, 23, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "mmc1", NULL, "csi1"}}, + {"PH24", 7, 24, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "mmc1", NULL, "csi1"}}, + {"PH25", 7, 25, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "mmc1", NULL, "csi1"}}, + {"PH26", 7, 26, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "mmc1", NULL, "csi1"}}, + {"PH27", 7, 27, {"gpio_in", "gpio_out", "lcd1", NULL, "keypad", "mmc1", NULL, "csi1"}}, + + {"PI0", 8, 0, {"gpio_in", "gpio_out", NULL, "i2c3", NULL, NULL, NULL, NULL}}, + {"PI1", 8, 1, {"gpio_in", "gpio_out", NULL, "i2c3", NULL, NULL, NULL, NULL}}, + {"PI2", 8, 2, {"gpio_in", "gpio_out", NULL, "i2c4", NULL, NULL, NULL, NULL}}, + {"PI3", 8, 3, {"gpio_in", "gpio_out", "pwm", "i2c4", NULL, NULL, NULL, NULL}}, + {"PI4", 8, 4, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}}, + {"PI5", 8, 5, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}}, + {"PI6", 8, 6, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}}, + {"PI7", 8, 7, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}}, + {"PI8", 8, 8, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}}, + {"PI9", 8, 9, {"gpio_in", "gpio_out", "mmc3", NULL, NULL, NULL, NULL, NULL}}, + {"PI10", 8, 10, {"gpio_in", "gpio_out", "spi0", "uart5", NULL, NULL, "eint", NULL}}, + {"PI11", 8, 11, {"gpio_in", "gpio_out", "spi0", "uart5", NULL, NULL, "eint", NULL}}, + {"PI12", 8, 12, {"gpio_in", "gpio_out", "spi0", "uart6", "clk_out_a", NULL, "eint", NULL}}, + {"PI13", 8, 13, {"gpio_in", "gpio_out", "spi0", "uart6", "clk_out_b", NULL, "eint", NULL}}, + {"PI14", 8, 14, {"gpio_in", "gpio_out", "spi0", "ps2", "timer4", NULL, "eint", NULL}}, + {"PI15", 8, 15, {"gpio_in", "gpio_out", "spi1", "ps2", "timer5", NULL, "eint", NULL}}, + {"PI16", 8, 16, {"gpio_in", "gpio_out", "spi1", "uart2", NULL, NULL, "eint", NULL}}, + {"PI17", 8, 17, {"gpio_in", "gpio_out", "spi1", "uart2", NULL, NULL, "eint", NULL}}, + {"PI18", 8, 18, {"gpio_in", "gpio_out", "spi1", "uart2", NULL, NULL, "eint", NULL}}, + {"PI19", 8, 19, {"gpio_in", "gpio_out", "spi1", "uart2", NULL, NULL, "eint", NULL}}, + {"PI20", 8, 20, {"gpio_in", "gpio_out", "ps2", "uart7", "hdmi", NULL, NULL, NULL}}, + {"PI21", 8, 21, {"gpio_in", "gpio_out", "ps2", "uart7", "hdmi", NULL, NULL, NULL}}, +}; + +const struct allwinner_padconf a20_padconf = { + .npins = sizeof(a20_pins) / sizeof(struct allwinner_pins), + .pins = a20_pins, +}; + +#endif /* SOC_ALLWINNER_A20 */ Property changes on: trunk/sys/arm/allwinner/a20/a20_padconf.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Modified: trunk/sys/arm/allwinner/a20/files.a20 =================================================================== --- trunk/sys/arm/allwinner/a20/files.a20 2020-03-06 17:15:48 UTC (rev 12400) +++ trunk/sys/arm/allwinner/a20/files.a20 2020-03-08 17:21:06 UTC (rev 12401) @@ -1,23 +1,3 @@ -# $FreeBSD: stable/10/sys/arm/allwinner/a20/files.a20 278727 2015-02-13 22:32:02Z ian $ -kern/kern_clocksource.c standard +# $MidnightBSD$ -arm/arm/bus_space_asm_generic.S standard -arm/arm/bus_space_generic.c standard -arm/arm/cpufunc_asm_armv5.S standard -arm/arm/cpufunc_asm_arm10.S standard -arm/arm/cpufunc_asm_arm11.S standard -arm/arm/cpufunc_asm_armv7.S standard -arm/arm/gic.c standard - -arm/allwinner/a20/a20_cpu_cfg.c standard -arm/allwinner/a10_clk.c standard -arm/allwinner/a10_sramc.c standard -arm/allwinner/a10_gpio.c optional gpio -arm/allwinner/a10_ehci.c optional ehci -arm/allwinner/if_emac.c optional emac -arm/allwinner/a10_wdog.c standard -arm/allwinner/timer.c standard -arm/arm/bus_space_base.c standard -arm/allwinner/a10_common.c standard -arm/allwinner/a10_machdep.c standard -arm/allwinner/a20/a20_mp.c optional smp +arm/allwinner/a20/a20_padconf.c standard Property changes on: trunk/sys/arm/allwinner/a20/files.a20 ___________________________________________________________________ Deleted: mnbsd:nokeywords ## -1 +0,0 ## -MidnightBSD=%H \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Modified: trunk/sys/arm/allwinner/a20/std.a20 =================================================================== --- trunk/sys/arm/allwinner/a20/std.a20 2020-03-06 17:15:48 UTC (rev 12400) +++ trunk/sys/arm/allwinner/a20/std.a20 2020-03-08 17:21:06 UTC (rev 12401) @@ -1,25 +1,15 @@ # Allwinner A20 common options -#$FreeBSD: stable/10/sys/arm/allwinner/a20/std.a20 278601 2015-02-11 22:47:48Z ian $ +# $MidnightBSD$ cpu CPU_CORTEXA machine arm armv6 -makeoptions CONF_CFLAGS="-march=armv7a -Wa,-march=armv7a" -makeoption ARM_LITTLE_ENDIAN +makeoptions CONF_CFLAGS="-march=armv7a" -# Physical memory starts at 0x40200000. We assume images are loaded at -# 0x40200000, e.g. from u-boot with 'fatload mmc 0 0x40200000 kernel' -# -# -options PHYSADDR=0x40000000 - -makeoptions KERNPHYSADDR=0x40200000 -options KERNPHYSADDR=0x40200000 makeoptions KERNVIRTADDR=0xc0200000 options KERNVIRTADDR=0xc0200000 -options ARM_L2_PIPT - options IPI_IRQ_START=0 options IPI_IRQ_END=15 +files "../allwinner/files.allwinner" files "../allwinner/a20/files.a20" Property changes on: trunk/sys/arm/allwinner/a20/std.a20 ___________________________________________________________________ Deleted: mnbsd:nokeywords ## -1 +0,0 ## -MidnightBSD=%H \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property From laffer1 at midnightbsd.org Sun Mar 8 13:23:22 2020 From: laffer1 at midnightbsd.org (laffer1 at midnightbsd.org) Date: Sun, 8 Mar 2020 13:23:22 -0400 (EDT) Subject: [Midnightbsd-cvs] src [12402] trunk/sys/arm/allwinner: sync with freebsd 11 stable Message-ID: <202003081723.028HNMtC086119@stargazer.midnightbsd.org> Revision: 12402 http://svnweb.midnightbsd.org/src/?rev=12402 Author: laffer1 Date: 2020-03-08 13:23:22 -0400 (Sun, 08 Mar 2020) Log Message: ----------- sync with freebsd 11 stable Modified Paths: -------------- trunk/sys/arm/allwinner/a10_common.c trunk/sys/arm/allwinner/a10_ehci.c trunk/sys/arm/allwinner/a10_gpio.c trunk/sys/arm/allwinner/a10_gpio.h trunk/sys/arm/allwinner/a10_sramc.c trunk/sys/arm/allwinner/a10_sramc.h trunk/sys/arm/allwinner/console.c trunk/sys/arm/allwinner/if_emac.c trunk/sys/arm/allwinner/if_emacreg.h trunk/sys/arm/allwinner/timer.c Added Paths: ----------- trunk/sys/arm/allwinner/a10_ahci.c trunk/sys/arm/allwinner/a10_codec.c trunk/sys/arm/allwinner/a10_dmac.c trunk/sys/arm/allwinner/a10_dmac.h trunk/sys/arm/allwinner/a10_fb.c trunk/sys/arm/allwinner/a10_hdmi.c trunk/sys/arm/allwinner/a10_hdmiaudio.c trunk/sys/arm/allwinner/a10_mmc.c trunk/sys/arm/allwinner/a10_mmc.h trunk/sys/arm/allwinner/a31/ trunk/sys/arm/allwinner/a31/a31_padconf.c trunk/sys/arm/allwinner/a31/a31_r_padconf.c trunk/sys/arm/allwinner/a31/a31s_padconf.c trunk/sys/arm/allwinner/a31/files.a31 trunk/sys/arm/allwinner/a31/std.a31 trunk/sys/arm/allwinner/a83t/ trunk/sys/arm/allwinner/a83t/a83t_padconf.c trunk/sys/arm/allwinner/a83t/a83t_r_padconf.c trunk/sys/arm/allwinner/a83t/files.a83t trunk/sys/arm/allwinner/a83t/std.a83t trunk/sys/arm/allwinner/allwinner_pinctrl.h trunk/sys/arm/allwinner/aw_ccu.c trunk/sys/arm/allwinner/aw_if_dwc.c trunk/sys/arm/allwinner/aw_machdep.c trunk/sys/arm/allwinner/aw_machdep.h trunk/sys/arm/allwinner/aw_mp.c trunk/sys/arm/allwinner/aw_mp.h trunk/sys/arm/allwinner/aw_nmi.c trunk/sys/arm/allwinner/aw_reset.c trunk/sys/arm/allwinner/aw_rsb.c trunk/sys/arm/allwinner/aw_rtc.c trunk/sys/arm/allwinner/aw_sid.c trunk/sys/arm/allwinner/aw_sid.h trunk/sys/arm/allwinner/aw_thermal.c trunk/sys/arm/allwinner/aw_ts.c trunk/sys/arm/allwinner/aw_usbphy.c trunk/sys/arm/allwinner/aw_wdog.c trunk/sys/arm/allwinner/aw_wdog.h trunk/sys/arm/allwinner/axp209.c trunk/sys/arm/allwinner/axp209reg.h trunk/sys/arm/allwinner/axp81x.c trunk/sys/arm/allwinner/files.allwinner trunk/sys/arm/allwinner/files.allwinner_up trunk/sys/arm/allwinner/h3/ trunk/sys/arm/allwinner/h3/files.h3 trunk/sys/arm/allwinner/h3/h3_padconf.c trunk/sys/arm/allwinner/h3/h3_r_padconf.c trunk/sys/arm/allwinner/h3/std.h3 trunk/sys/arm/allwinner/if_awg.c trunk/sys/arm/allwinner/if_awgreg.h trunk/sys/arm/allwinner/std.allwinner trunk/sys/arm/allwinner/std.allwinner_up trunk/sys/arm/allwinner/sunxi_dma_if.m Removed Paths: ------------- trunk/sys/arm/allwinner/files.a10 trunk/sys/arm/allwinner/std.a10 Added: trunk/sys/arm/allwinner/a10_ahci.c =================================================================== --- trunk/sys/arm/allwinner/a10_ahci.c (rev 0) +++ trunk/sys/arm/allwinner/a10_ahci.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,396 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2014-2015 M. Warner Losh + * Copyright (c) 2015 Luiz Otavio O Souza + * 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. + * + * The magic-bit-bang sequence used in this code may be based on a linux + * platform driver in the Allwinner SDK from Allwinner Technology Co., Ltd. + * www.allwinnertech.com, by Daniel Wang + * though none of the original code was copied. + */ + +#include "opt_bus.h" + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10_ahci.c 346524 2019-04-22 04:56:41Z ian $"); + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +/* + * Allwinner a1x/a2x/a8x SATA attachment. This is just the AHCI register + * set with a few extra implementation-specific registers that need to + * be accounted for. There's only one PHY in the system, and it needs + * to be trained to bring the link up. In addition, there's some DMA + * specific things that need to be done as well. These things are also + * just about completely undocumented, except in ugly code in the Linux + * SDK Allwinner releases. + */ + +/* BITx -- Unknown bit that needs to be set/cleared at position x */ +/* UFx -- Uknown multi-bit field frobbed during init */ +#define AHCI_BISTAFR 0x00A0 +#define AHCI_BISTCR 0x00A4 +#define AHCI_BISTFCTR 0x00A8 +#define AHCI_BISTSR 0x00AC +#define AHCI_BISTDECR 0x00B0 +#define AHCI_DIAGNR 0x00B4 +#define AHCI_DIAGNR1 0x00B8 +#define AHCI_OOBR 0x00BC +#define AHCI_PHYCS0R 0x00C0 +/* Bits 0..17 are a mystery */ +#define PHYCS0R_BIT18 (1 << 18) +#define PHYCS0R_POWER_ENABLE (1 << 19) +#define PHYCS0R_UF1_MASK (7 << 20) /* Unknown Field 1 */ +#define PHYCS0R_UF1_INIT (3 << 20) +#define PHYCS0R_BIT23 (1 << 23) +#define PHYCS0R_UF2_MASK (7 << 24) /* Uknown Field 2 */ +#define PHYCS0R_UF2_INIT (5 << 24) +/* Bit 27 mystery */ +#define PHYCS0R_POWER_STATUS_MASK (7 << 28) +#define PHYCS0R_PS_GOOD (2 << 28) +/* Bit 31 mystery */ +#define AHCI_PHYCS1R 0x00C4 +/* Bits 0..5 are a mystery */ +#define PHYCS1R_UF1_MASK (3 << 6) +#define PHYCS1R_UF1_INIT (2 << 6) +#define PHYCS1R_UF2_MASK (0x1f << 8) +#define PHYCS1R_UF2_INIT (6 << 8) +/* Bits 13..14 are a mystery */ +#define PHYCS1R_BIT15 (1 << 15) +#define PHYCS1R_UF3_MASK (3 << 16) +#define PHYCS1R_UF3_INIT (2 << 16) +/* Bit 18 mystery */ +#define PHYCS1R_HIGHZ (1 << 19) +/* Bits 20..27 mystery */ +#define PHYCS1R_BIT28 (1 << 28) +/* Bits 29..31 mystery */ +#define AHCI_PHYCS2R 0x00C8 +/* bits 0..4 mystery */ +#define PHYCS2R_UF1_MASK (0x1f << 5) +#define PHYCS2R_UF1_INIT (0x19 << 5) +/* Bits 10..23 mystery */ +#define PHYCS2R_CALIBRATE (1 << 24) +/* Bits 25..31 mystery */ +#define AHCI_TIMER1MS 0x00E0 +#define AHCI_GPARAM1R 0x00E8 +#define AHCI_GPARAM2R 0x00EC +#define AHCI_PPARAMR 0x00F0 +#define AHCI_TESTR 0x00F4 +#define AHCI_VERSIONR 0x00F8 +#define AHCI_IDR 0x00FC +#define AHCI_RWCR 0x00FC + +#define AHCI_P0DMACR 0x0070 +#define AHCI_P0PHYCR 0x0078 +#define AHCI_P0PHYSR 0x007C + +#define PLL_FREQ 100000000 + +static void inline +ahci_set(struct resource *m, bus_size_t off, uint32_t set) +{ + uint32_t val = ATA_INL(m, off); + + val |= set; + ATA_OUTL(m, off, val); +} + +static void inline +ahci_clr(struct resource *m, bus_size_t off, uint32_t clr) +{ + uint32_t val = ATA_INL(m, off); + + val &= ~clr; + ATA_OUTL(m, off, val); +} + +static void inline +ahci_mask_set(struct resource *m, bus_size_t off, uint32_t mask, uint32_t set) +{ + uint32_t val = ATA_INL(m, off); + + val &= mask; + val |= set; + ATA_OUTL(m, off, val); +} + +/* + * Should this be phy_reset or phy_init + */ +#define PHY_RESET_TIMEOUT 1000 +static void +ahci_a10_phy_reset(device_t dev) +{ + uint32_t to, val; + struct ahci_controller *ctlr = device_get_softc(dev); + + /* + * Here starts the magic -- most of the comments are based + * on guesswork, names of routines and printf error + * messages. The code works, but it will do that even if the + * comments are 100% BS. + */ + + /* + * Lock out other access while we initialize. Or at least that + * seems to be the case based on Linux SDK #defines. Maybe this + * put things into reset? + */ + ATA_OUTL(ctlr->r_mem, AHCI_RWCR, 0); + DELAY(100); + + /* + * Set bit 19 in PHYCS1R. Guessing this disables driving the PHY + * port for a bit while we reset things. + */ + ahci_set(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_HIGHZ); + + /* + * Frob PHYCS0R... + */ + ahci_mask_set(ctlr->r_mem, AHCI_PHYCS0R, + ~PHYCS0R_UF2_MASK, + PHYCS0R_UF2_INIT | PHYCS0R_BIT23 | PHYCS0R_BIT18); + + /* + * Set three fields in PHYCS1R + */ + ahci_mask_set(ctlr->r_mem, AHCI_PHYCS1R, + ~(PHYCS1R_UF1_MASK | PHYCS1R_UF2_MASK | PHYCS1R_UF3_MASK), + PHYCS1R_UF1_INIT | PHYCS1R_UF2_INIT | PHYCS1R_UF3_INIT); + + /* + * Two more mystery bits in PHYCS1R. -- can these be combined above? + */ + ahci_set(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_BIT15 | PHYCS1R_BIT28); + + /* + * Now clear that first mysery bit. Perhaps this starts + * driving the PHY again so we can power it up and start + * talking to the SATA drive, if any below. + */ + ahci_clr(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_HIGHZ); + + /* + * Frob PHYCS0R again... + */ + ahci_mask_set(ctlr->r_mem, AHCI_PHYCS0R, + ~PHYCS0R_UF1_MASK, PHYCS0R_UF1_INIT); + + /* + * Frob PHYCS2R, because 25 means something? + */ + ahci_mask_set(ctlr->r_mem, AHCI_PHYCS2R, ~PHYCS2R_UF1_MASK, + PHYCS2R_UF1_INIT); + + DELAY(100); /* WAG */ + + /* + * Turn on the power to the PHY and wait for it to report back + * good? + */ + ahci_set(ctlr->r_mem, AHCI_PHYCS0R, PHYCS0R_POWER_ENABLE); + for (to = PHY_RESET_TIMEOUT; to > 0; to--) { + val = ATA_INL(ctlr->r_mem, AHCI_PHYCS0R); + if ((val & PHYCS0R_POWER_STATUS_MASK) == PHYCS0R_PS_GOOD) + break; + DELAY(10); + } + if (to == 0 && bootverbose) + device_printf(dev, "PHY Power Failed PHYCS0R = %#x\n", val); + + /* + * Calibrate the clocks between the device and the host. This appears + * to be an automated process that clears the bit when it is done. + */ + ahci_set(ctlr->r_mem, AHCI_PHYCS2R, PHYCS2R_CALIBRATE); + for (to = PHY_RESET_TIMEOUT; to > 0; to--) { + val = ATA_INL(ctlr->r_mem, AHCI_PHYCS2R); + if ((val & PHYCS2R_CALIBRATE) == 0) + break; + DELAY(10); + } + if (to == 0 && bootverbose) + device_printf(dev, "PHY Cal Failed PHYCS2R %#x\n", val); + + /* + * OK, let things settle down a bit. + */ + DELAY(1000); + + /* + * Go back into normal mode now that we've calibrated the PHY. + */ + ATA_OUTL(ctlr->r_mem, AHCI_RWCR, 7); +} + +static void +ahci_a10_ch_start(struct ahci_channel *ch) +{ + uint32_t reg; + + /* + * Magical values from Allwinner SDK, setup the DMA before start + * operations on this channel. + */ + reg = ATA_INL(ch->r_mem, AHCI_P0DMACR); + reg &= ~0xff00; + reg |= 0x4400; + ATA_OUTL(ch->r_mem, AHCI_P0DMACR, reg); +} + +static int +ahci_a10_ctlr_reset(device_t dev) +{ + + ahci_a10_phy_reset(dev); + + return (ahci_ctlr_reset(dev)); +} + +static int +ahci_a10_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-ahci")) + return (ENXIO); + device_set_desc(dev, "Allwinner Integrated AHCI controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +ahci_a10_attach(device_t dev) +{ + int error; + struct ahci_controller *ctlr; + clk_t clk_pll, clk_gate; + + ctlr = device_get_softc(dev); + clk_pll = clk_gate = NULL; + + ctlr->quirks = AHCI_Q_NOPMP; + ctlr->vendorid = 0; + ctlr->deviceid = 0; + ctlr->subvendorid = 0; + ctlr->subdeviceid = 0; + ctlr->r_rid = 0; + if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &ctlr->r_rid, RF_ACTIVE))) + return (ENXIO); + + /* Enable clocks */ + error = clk_get_by_ofw_index(dev, 0, 0, &clk_pll); + if (error != 0) { + device_printf(dev, "Cannot get PLL clock\n"); + goto fail; + } + error = clk_get_by_ofw_index(dev, 0, 1, &clk_gate); + if (error != 0) { + device_printf(dev, "Cannot get gate clock\n"); + goto fail; + } + error = clk_set_freq(clk_pll, PLL_FREQ, CLK_SET_ROUND_DOWN); + if (error != 0) { + device_printf(dev, "Cannot set PLL frequency\n"); + goto fail; + } + error = clk_enable(clk_pll); + if (error != 0) { + device_printf(dev, "Cannot enable PLL\n"); + goto fail; + } + error = clk_enable(clk_gate); + if (error != 0) { + device_printf(dev, "Cannot enable clk gate\n"); + goto fail; + } + + /* Reset controller */ + if ((error = ahci_a10_ctlr_reset(dev)) != 0) + goto fail; + + /* + * No MSI registers on this platform. + */ + ctlr->msi = 0; + ctlr->numirqs = 1; + + /* Channel start callback(). */ + ctlr->ch_start = ahci_a10_ch_start; + + /* + * Note: ahci_attach will release ctlr->r_mem on errors automatically + */ + return (ahci_attach(dev)); + +fail: + if (clk_gate != NULL) + clk_release(clk_gate); + if (clk_pll != NULL) + clk_release(clk_pll); + bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); + return (error); +} + +static int +ahci_a10_detach(device_t dev) +{ + + return (ahci_detach(dev)); +} + +static device_method_t ahci_ata_methods[] = { + DEVMETHOD(device_probe, ahci_a10_probe), + DEVMETHOD(device_attach, ahci_a10_attach), + DEVMETHOD(device_detach, ahci_a10_detach), + 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_END +}; + +static driver_t ahci_ata_driver = { + "ahci", + ahci_ata_methods, + sizeof(struct ahci_controller) +}; + +DRIVER_MODULE(a10_ahci, simplebus, ahci_ata_driver, ahci_devclass, 0, 0); Property changes on: trunk/sys/arm/allwinner/a10_ahci.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/a10_codec.c =================================================================== --- trunk/sys/arm/allwinner/a10_codec.c (rev 0) +++ trunk/sys/arm/allwinner/a10_codec.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,886 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2014-2016 Jared D. McNeill + * 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: stable/11/sys/arm/allwinner/a10_codec.c 308324 2016-11-05 04:17:32Z mmel $ + */ + +/* + * Allwinner A10/A20 Audio Codec + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10_codec.c 308324 2016-11-05 04:17:32Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +#include "sunxi_dma_if.h" +#include "mixer_if.h" +#include "gpio_if.h" + +#define TX_TRIG_LEVEL 0xf +#define RX_TRIG_LEVEL 0x7 +#define DRQ_CLR_CNT 0x3 + +#define AC_DAC_DPC 0x00 +#define DAC_DPC_EN_DA 0x80000000 +#define AC_DAC_FIFOC 0x04 +#define DAC_FIFOC_FS_SHIFT 29 +#define DAC_FIFOC_FS_MASK (7U << DAC_FIFOC_FS_SHIFT) +#define DAC_FS_48KHZ 0 +#define DAC_FS_32KHZ 1 +#define DAC_FS_24KHZ 2 +#define DAC_FS_16KHZ 3 +#define DAC_FS_12KHZ 4 +#define DAC_FS_8KHZ 5 +#define DAC_FS_192KHZ 6 +#define DAC_FS_96KHZ 7 +#define DAC_FIFOC_FIFO_MODE_SHIFT 24 +#define DAC_FIFOC_FIFO_MODE_MASK (3U << DAC_FIFOC_FIFO_MODE_SHIFT) +#define FIFO_MODE_24_31_8 0 +#define FIFO_MODE_16_31_16 0 +#define FIFO_MODE_16_15_0 1 +#define DAC_FIFOC_DRQ_CLR_CNT_SHIFT 21 +#define DAC_FIFOC_DRQ_CLR_CNT_MASK (3U << DAC_FIFOC_DRQ_CLR_CNT_SHIFT) +#define DAC_FIFOC_TX_TRIG_LEVEL_SHIFT 8 +#define DAC_FIFOC_TX_TRIG_LEVEL_MASK (0x7f << DAC_FIFOC_TX_TRIG_LEVEL_SHIFT) +#define DAC_FIFOC_MONO_EN (1U << 6) +#define DAC_FIFOC_TX_BITS (1U << 5) +#define DAC_FIFOC_DRQ_EN (1U << 4) +#define DAC_FIFOC_FIFO_FLUSH (1U << 0) +#define AC_DAC_FIFOS 0x08 +#define AC_DAC_TXDATA 0x0c +#define AC_DAC_ACTL 0x10 +#define DAC_ACTL_DACAREN (1U << 31) +#define DAC_ACTL_DACALEN (1U << 30) +#define DAC_ACTL_MIXEN (1U << 29) +#define DAC_ACTL_DACPAS (1U << 8) +#define DAC_ACTL_PAMUTE (1U << 6) +#define DAC_ACTL_PAVOL_SHIFT 0 +#define DAC_ACTL_PAVOL_MASK (0x3f << DAC_ACTL_PAVOL_SHIFT) +#define AC_ADC_FIFOC 0x1c +#define ADC_FIFOC_FS_SHIFT 29 +#define ADC_FIFOC_FS_MASK (7U << ADC_FIFOC_FS_SHIFT) +#define ADC_FS_48KHZ 0 +#define ADC_FIFOC_EN_AD (1U << 28) +#define ADC_FIFOC_RX_FIFO_MODE (1U << 24) +#define ADC_FIFOC_RX_TRIG_LEVEL_SHIFT 8 +#define ADC_FIFOC_RX_TRIG_LEVEL_MASK (0x1f << ADC_FIFOC_RX_TRIG_LEVEL_SHIFT) +#define ADC_FIFOC_MONO_EN (1U << 7) +#define ADC_FIFOC_RX_BITS (1U << 6) +#define ADC_FIFOC_DRQ_EN (1U << 4) +#define ADC_FIFOC_FIFO_FLUSH (1U << 1) +#define AC_ADC_FIFOS 0x20 +#define AC_ADC_RXDATA 0x24 +#define AC_ADC_ACTL 0x28 +#define ADC_ACTL_ADCREN (1U << 31) +#define ADC_ACTL_ADCLEN (1U << 30) +#define ADC_ACTL_PREG1EN (1U << 29) +#define ADC_ACTL_PREG2EN (1U << 28) +#define ADC_ACTL_VMICEN (1U << 27) +#define ADC_ACTL_ADCG_SHIFT 20 +#define ADC_ACTL_ADCG_MASK (7U << ADC_ACTL_ADCG_SHIFT) +#define ADC_ACTL_ADCIS_SHIFT 17 +#define ADC_ACTL_ADCIS_MASK (7U << ADC_ACTL_ADCIS_SHIFT) +#define ADC_IS_LINEIN 0 +#define ADC_IS_FMIN 1 +#define ADC_IS_MIC1 2 +#define ADC_IS_MIC2 3 +#define ADC_IS_MIC1_L_MIC2_R 4 +#define ADC_IS_MIC1_LR_MIC2_LR 5 +#define ADC_IS_OMIX 6 +#define ADC_IS_LINEIN_L_MIC1_R 7 +#define ADC_ACTL_LNRDF (1U << 16) +#define ADC_ACTL_LNPREG_SHIFT 13 +#define ADC_ACTL_LNPREG_MASK (7U << ADC_ACTL_LNPREG_SHIFT) +#define ADC_ACTL_PA_EN (1U << 4) +#define ADC_ACTL_DDE (1U << 3) +#define AC_DAC_CNT 0x30 +#define AC_ADC_CNT 0x34 + +static uint32_t a10codec_fmt[] = { + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + 0 +}; + +static struct pcmchan_caps a10codec_pcaps = { 8000, 192000, a10codec_fmt, 0 }; +static struct pcmchan_caps a10codec_rcaps = { 8000, 48000, a10codec_fmt, 0 }; + +struct a10codec_info; + +struct a10codec_chinfo { + struct snd_dbuf *buffer; + struct pcm_channel *channel; + struct a10codec_info *parent; + bus_dmamap_t dmamap; + void *dmaaddr; + bus_addr_t physaddr; + bus_size_t fifo; + device_t dmac; + void *dmachan; + + int dir; + int run; + uint32_t pos; + uint32_t format; + uint32_t blocksize; + uint32_t speed; +}; + +struct a10codec_info { + device_t dev; + struct resource *res[2]; + struct mtx *lock; + bus_dma_tag_t dmat; + unsigned dmasize; + void *ih; + + unsigned drqtype_codec; + unsigned drqtype_sdram; + + struct a10codec_chinfo play; + struct a10codec_chinfo rec; +}; + +static struct resource_spec a10codec_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define CODEC_READ(sc, reg) bus_read_4((sc)->res[0], (reg)) +#define CODEC_WRITE(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) + +/* + * Mixer interface + */ + +static int +a10codec_mixer_init(struct snd_mixer *m) +{ + struct a10codec_info *sc = mix_getdevinfo(m); + pcell_t prop[4]; + phandle_t node; + device_t gpio; + uint32_t val; + ssize_t len; + int pin; + + mix_setdevs(m, SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_RECLEV); + mix_setrecdevs(m, SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC); + + /* Unmute input source to PA */ + val = CODEC_READ(sc, AC_DAC_ACTL); + val |= DAC_ACTL_PAMUTE; + CODEC_WRITE(sc, AC_DAC_ACTL, val); + + /* Enable PA */ + val = CODEC_READ(sc, AC_ADC_ACTL); + val |= ADC_ACTL_PA_EN; + CODEC_WRITE(sc, AC_ADC_ACTL, val); + + /* Unmute PA */ + node = ofw_bus_get_node(sc->dev); + len = OF_getencprop(node, "allwinner,pa-gpios", prop, sizeof(prop)); + if (len > 0 && (len / sizeof(prop[0])) == 4) { + gpio = OF_device_from_xref(prop[0]); + if (gpio != NULL) { + pin = prop[1] * 32 + prop[2]; + GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT); + GPIO_PIN_SET(gpio, pin, GPIO_PIN_LOW); + } + } + + return (0); +} + +static const struct a10codec_mixer { + unsigned reg; + unsigned mask; + unsigned shift; +} a10codec_mixers[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_VOLUME] = { AC_DAC_ACTL, DAC_ACTL_PAVOL_MASK, + DAC_ACTL_PAVOL_SHIFT }, + [SOUND_MIXER_LINE] = { AC_ADC_ACTL, ADC_ACTL_LNPREG_MASK, + ADC_ACTL_LNPREG_SHIFT }, + [SOUND_MIXER_RECLEV] = { AC_ADC_ACTL, ADC_ACTL_ADCG_MASK, + ADC_ACTL_ADCG_SHIFT }, +}; + +static int +a10codec_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, + unsigned right) +{ + struct a10codec_info *sc = mix_getdevinfo(m); + uint32_t val; + unsigned nvol, max; + + max = a10codec_mixers[dev].mask >> a10codec_mixers[dev].shift; + nvol = (left * max) / 100; + + val = CODEC_READ(sc, a10codec_mixers[dev].reg); + val &= ~a10codec_mixers[dev].mask; + val |= (nvol << a10codec_mixers[dev].shift); + CODEC_WRITE(sc, a10codec_mixers[dev].reg, val); + + left = right = (left * 100) / max; + return (left | (right << 8)); +} + +static uint32_t +a10codec_mixer_setrecsrc(struct snd_mixer *m, uint32_t src) +{ + struct a10codec_info *sc = mix_getdevinfo(m); + uint32_t val; + + val = CODEC_READ(sc, AC_ADC_ACTL); + + switch (src) { + case SOUND_MASK_LINE: /* line-in */ + val &= ~ADC_ACTL_ADCIS_MASK; + val |= (ADC_IS_LINEIN << ADC_ACTL_ADCIS_SHIFT); + break; + case SOUND_MASK_MIC: /* MIC1 */ + val &= ~ADC_ACTL_ADCIS_MASK; + val |= (ADC_IS_MIC1 << ADC_ACTL_ADCIS_SHIFT); + break; + case SOUND_MASK_LINE1: /* MIC2 */ + val &= ~ADC_ACTL_ADCIS_MASK; + val |= (ADC_IS_MIC2 << ADC_ACTL_ADCIS_SHIFT); + break; + default: + break; + } + + CODEC_WRITE(sc, AC_ADC_ACTL, val); + + switch ((val & ADC_ACTL_ADCIS_MASK) >> ADC_ACTL_ADCIS_SHIFT) { + case ADC_IS_LINEIN: + return (SOUND_MASK_LINE); + case ADC_IS_MIC1: + return (SOUND_MASK_MIC); + case ADC_IS_MIC2: + return (SOUND_MASK_LINE1); + default: + return (0); + } +} + +static kobj_method_t a10codec_mixer_methods[] = { + KOBJMETHOD(mixer_init, a10codec_mixer_init), + KOBJMETHOD(mixer_set, a10codec_mixer_set), + KOBJMETHOD(mixer_setrecsrc, a10codec_mixer_setrecsrc), + KOBJMETHOD_END +}; +MIXER_DECLARE(a10codec_mixer); + + +/* + * Channel interface + */ + +static void +a10codec_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct a10codec_chinfo *ch = arg; + + if (error != 0) + return; + + ch->physaddr = segs[0].ds_addr; +} + +static void +a10codec_transfer(struct a10codec_chinfo *ch) +{ + bus_addr_t src, dst; + int error; + + if (ch->dir == PCMDIR_PLAY) { + src = ch->physaddr + ch->pos; + dst = ch->fifo; + } else { + src = ch->fifo; + dst = ch->physaddr + ch->pos; + } + + error = SUNXI_DMA_TRANSFER(ch->dmac, ch->dmachan, src, dst, + ch->blocksize); + if (error) { + ch->run = 0; + device_printf(ch->parent->dev, "DMA transfer failed: %d\n", + error); + } +} + +static void +a10codec_dmaconfig(struct a10codec_chinfo *ch) +{ + struct a10codec_info *sc = ch->parent; + struct sunxi_dma_config conf; + + memset(&conf, 0, sizeof(conf)); + conf.src_width = conf.dst_width = 16; + conf.src_burst_len = conf.dst_burst_len = 4; + + if (ch->dir == PCMDIR_PLAY) { + conf.dst_noincr = true; + conf.src_drqtype = sc->drqtype_sdram; + conf.dst_drqtype = sc->drqtype_codec; + } else { + conf.src_noincr = true; + conf.src_drqtype = sc->drqtype_codec; + conf.dst_drqtype = sc->drqtype_sdram; + } + + SUNXI_DMA_SET_CONFIG(ch->dmac, ch->dmachan, &conf); +} + +static void +a10codec_dmaintr(void *priv) +{ + struct a10codec_chinfo *ch = priv; + unsigned bufsize; + + bufsize = sndbuf_getsize(ch->buffer); + + ch->pos += ch->blocksize; + if (ch->pos >= bufsize) + ch->pos -= bufsize; + + if (ch->run) { + chn_intr(ch->channel); + a10codec_transfer(ch); + } +} + +static unsigned +a10codec_fs(struct a10codec_chinfo *ch) +{ + switch (ch->speed) { + case 48000: + return (DAC_FS_48KHZ); + case 24000: + return (DAC_FS_24KHZ); + case 12000: + return (DAC_FS_12KHZ); + case 192000: + return (DAC_FS_192KHZ); + case 32000: + return (DAC_FS_32KHZ); + case 16000: + return (DAC_FS_16KHZ); + case 8000: + return (DAC_FS_8KHZ); + case 96000: + return (DAC_FS_96KHZ); + default: + return (DAC_FS_48KHZ); + } +} + +static void +a10codec_start(struct a10codec_chinfo *ch) +{ + struct a10codec_info *sc = ch->parent; + uint32_t val; + + ch->pos = 0; + + if (ch->dir == PCMDIR_PLAY) { + /* Flush DAC FIFO */ + CODEC_WRITE(sc, AC_DAC_FIFOC, DAC_FIFOC_FIFO_FLUSH); + + /* Clear DAC FIFO status */ + CODEC_WRITE(sc, AC_DAC_FIFOS, CODEC_READ(sc, AC_DAC_FIFOS)); + + /* Enable DAC analog left/right channels and output mixer */ + val = CODEC_READ(sc, AC_DAC_ACTL); + val |= DAC_ACTL_DACAREN; + val |= DAC_ACTL_DACALEN; + val |= DAC_ACTL_DACPAS; + CODEC_WRITE(sc, AC_DAC_ACTL, val); + + /* Configure DAC DMA channel */ + a10codec_dmaconfig(ch); + + /* Configure DAC FIFO */ + CODEC_WRITE(sc, AC_DAC_FIFOC, + (AFMT_CHANNEL(ch->format) == 1 ? DAC_FIFOC_MONO_EN : 0) | + (a10codec_fs(ch) << DAC_FIFOC_FS_SHIFT) | + (FIFO_MODE_16_15_0 << DAC_FIFOC_FIFO_MODE_SHIFT) | + (DRQ_CLR_CNT << DAC_FIFOC_DRQ_CLR_CNT_SHIFT) | + (TX_TRIG_LEVEL << DAC_FIFOC_TX_TRIG_LEVEL_SHIFT)); + + /* Enable DAC DRQ */ + val = CODEC_READ(sc, AC_DAC_FIFOC); + val |= DAC_FIFOC_DRQ_EN; + CODEC_WRITE(sc, AC_DAC_FIFOC, val); + } else { + /* Flush ADC FIFO */ + CODEC_WRITE(sc, AC_ADC_FIFOC, ADC_FIFOC_FIFO_FLUSH); + + /* Clear ADC FIFO status */ + CODEC_WRITE(sc, AC_ADC_FIFOS, CODEC_READ(sc, AC_ADC_FIFOS)); + + /* Enable ADC analog left/right channels, MIC1 preamp, + * and VMIC pin voltage + */ + val = CODEC_READ(sc, AC_ADC_ACTL); + val |= ADC_ACTL_ADCREN; + val |= ADC_ACTL_ADCLEN; + val |= ADC_ACTL_PREG1EN; + val |= ADC_ACTL_VMICEN; + CODEC_WRITE(sc, AC_ADC_ACTL, val); + + /* Configure ADC DMA channel */ + a10codec_dmaconfig(ch); + + /* Configure ADC FIFO */ + CODEC_WRITE(sc, AC_ADC_FIFOC, + ADC_FIFOC_EN_AD | + ADC_FIFOC_RX_FIFO_MODE | + (AFMT_CHANNEL(ch->format) == 1 ? ADC_FIFOC_MONO_EN : 0) | + (a10codec_fs(ch) << ADC_FIFOC_FS_SHIFT) | + (RX_TRIG_LEVEL << ADC_FIFOC_RX_TRIG_LEVEL_SHIFT)); + + /* Enable ADC DRQ */ + val = CODEC_READ(sc, AC_ADC_FIFOC); + val |= ADC_FIFOC_DRQ_EN; + CODEC_WRITE(sc, AC_ADC_FIFOC, val); + } + + /* Start DMA transfer */ + a10codec_transfer(ch); +} + +static void +a10codec_stop(struct a10codec_chinfo *ch) +{ + struct a10codec_info *sc = ch->parent; + uint32_t val; + + /* Disable DMA channel */ + SUNXI_DMA_HALT(ch->dmac, ch->dmachan); + + if (ch->dir == PCMDIR_PLAY) { + /* Disable DAC analog left/right channels and output mixer */ + val = CODEC_READ(sc, AC_DAC_ACTL); + val &= ~DAC_ACTL_DACAREN; + val &= ~DAC_ACTL_DACALEN; + val &= ~DAC_ACTL_DACPAS; + CODEC_WRITE(sc, AC_DAC_ACTL, val); + + /* Disable DAC DRQ */ + CODEC_WRITE(sc, AC_DAC_FIFOC, 0); + } else { + /* Disable ADC analog left/right channels, MIC1 preamp, + * and VMIC pin voltage + */ + val = CODEC_READ(sc, AC_ADC_ACTL); + val &= ~ADC_ACTL_ADCREN; + val &= ~ADC_ACTL_ADCLEN; + val &= ~ADC_ACTL_PREG1EN; + val &= ~ADC_ACTL_VMICEN; + CODEC_WRITE(sc, AC_ADC_ACTL, val); + + /* Disable ADC DRQ */ + CODEC_WRITE(sc, AC_ADC_FIFOC, 0); + } +} + +static void * +a10codec_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, + struct pcm_channel *c, int dir) +{ + struct a10codec_info *sc = devinfo; + struct a10codec_chinfo *ch = dir == PCMDIR_PLAY ? &sc->play : &sc->rec; + int error; + + ch->parent = sc; + ch->channel = c; + ch->buffer = b; + ch->dir = dir; + ch->fifo = rman_get_start(sc->res[0]) + + (dir == PCMDIR_REC ? AC_ADC_RXDATA : AC_DAC_TXDATA); + + ch->dmac = devclass_get_device(devclass_find("a10dmac"), 0); + if (ch->dmac == NULL) { + device_printf(sc->dev, "cannot find DMA controller\n"); + return (NULL); + } + ch->dmachan = SUNXI_DMA_ALLOC(ch->dmac, false, a10codec_dmaintr, ch); + if (ch->dmachan == NULL) { + device_printf(sc->dev, "cannot allocate DMA channel\n"); + return (NULL); + } + + error = bus_dmamem_alloc(sc->dmat, &ch->dmaaddr, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &ch->dmamap); + if (error != 0) { + device_printf(sc->dev, "cannot allocate channel buffer\n"); + return (NULL); + } + error = bus_dmamap_load(sc->dmat, ch->dmamap, ch->dmaaddr, + sc->dmasize, a10codec_dmamap_cb, ch, BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc->dev, "cannot load DMA map\n"); + return (NULL); + } + memset(ch->dmaaddr, 0, sc->dmasize); + + if (sndbuf_setup(ch->buffer, ch->dmaaddr, sc->dmasize) != 0) { + device_printf(sc->dev, "cannot setup sndbuf\n"); + return (NULL); + } + + return (ch); +} + +static int +a10codec_chan_free(kobj_t obj, void *data) +{ + struct a10codec_chinfo *ch = data; + struct a10codec_info *sc = ch->parent; + + SUNXI_DMA_FREE(ch->dmac, ch->dmachan); + bus_dmamap_unload(sc->dmat, ch->dmamap); + bus_dmamem_free(sc->dmat, ch->dmaaddr, ch->dmamap); + + return (0); +} + +static int +a10codec_chan_setformat(kobj_t obj, void *data, uint32_t format) +{ + struct a10codec_chinfo *ch = data; + + ch->format = format; + + return (0); +} + +static uint32_t +a10codec_chan_setspeed(kobj_t obj, void *data, uint32_t speed) +{ + struct a10codec_chinfo *ch = data; + + /* + * The codec supports full duplex operation but both DAC and ADC + * use the same source clock (PLL2). Limit the available speeds to + * those supported by a 24576000 Hz input. + */ + switch (speed) { + case 8000: + case 12000: + case 16000: + case 24000: + case 32000: + case 48000: + ch->speed = speed; + break; + case 96000: + case 192000: + /* 96 KHz / 192 KHz mode only supported for playback */ + if (ch->dir == PCMDIR_PLAY) { + ch->speed = speed; + } else { + ch->speed = 48000; + } + break; + case 44100: + ch->speed = 48000; + break; + case 22050: + ch->speed = 24000; + break; + case 11025: + ch->speed = 12000; + break; + default: + ch->speed = 48000; + break; + } + + return (ch->speed); +} + +static uint32_t +a10codec_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) +{ + struct a10codec_chinfo *ch = data; + + ch->blocksize = blocksize & ~3; + + return (ch->blocksize); +} + +static int +a10codec_chan_trigger(kobj_t obj, void *data, int go) +{ + struct a10codec_chinfo *ch = data; + struct a10codec_info *sc = ch->parent; + + if (!PCMTRIG_COMMON(go)) + return (0); + + snd_mtxlock(sc->lock); + switch (go) { + case PCMTRIG_START: + ch->run = 1; + a10codec_start(ch); + break; + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + ch->run = 0; + a10codec_stop(ch); + break; + default: + break; + } + snd_mtxunlock(sc->lock); + + return (0); +} + +static uint32_t +a10codec_chan_getptr(kobj_t obj, void *data) +{ + struct a10codec_chinfo *ch = data; + + return (ch->pos); +} + +static struct pcmchan_caps * +a10codec_chan_getcaps(kobj_t obj, void *data) +{ + struct a10codec_chinfo *ch = data; + + if (ch->dir == PCMDIR_PLAY) { + return (&a10codec_pcaps); + } else { + return (&a10codec_rcaps); + } +} + +static kobj_method_t a10codec_chan_methods[] = { + KOBJMETHOD(channel_init, a10codec_chan_init), + KOBJMETHOD(channel_free, a10codec_chan_free), + KOBJMETHOD(channel_setformat, a10codec_chan_setformat), + KOBJMETHOD(channel_setspeed, a10codec_chan_setspeed), + KOBJMETHOD(channel_setblocksize, a10codec_chan_setblocksize), + KOBJMETHOD(channel_trigger, a10codec_chan_trigger), + KOBJMETHOD(channel_getptr, a10codec_chan_getptr), + KOBJMETHOD(channel_getcaps, a10codec_chan_getcaps), + KOBJMETHOD_END +}; +CHANNEL_DECLARE(a10codec_chan); + + +/* + * Device interface + */ + +static struct ofw_compat_data compat_data[] = { + {"allwinner,sun4i-a10-codec", 1}, + {"allwinner,sun7i-a20-codec", 1}, + {NULL, 0}, +}; + +static int +a10codec_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, "Allwinner Audio Codec"); + return (BUS_PROBE_DEFAULT); +} + +static int +a10codec_attach(device_t dev) +{ + struct a10codec_info *sc; + char status[SND_STATUSLEN]; + clk_t clk_apb, clk_codec; + uint32_t val; + int error; + + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); + sc->dev = dev; + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "a10codec softc"); + + if (bus_alloc_resources(dev, a10codec_spec, sc->res)) { + device_printf(dev, "cannot allocate resources for device\n"); + error = ENXIO; + goto fail; + } + + /* XXX DRQ types should come from FDT, but how? */ + if (ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-codec") || + ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-codec")) { + sc->drqtype_codec = 19; + sc->drqtype_sdram = 22; + } else { + device_printf(dev, "DRQ types not known for this SoC\n"); + error = ENXIO; + goto fail; + } + + sc->dmasize = 131072; + error = bus_dma_tag_create( + bus_get_dma_tag(dev), + 4, sc->dmasize, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + sc->dmasize, 1, /* maxsize, nsegs */ + sc->dmasize, 0, /* maxsegsize, flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->dmat); + if (error != 0) { + device_printf(dev, "cannot create DMA tag\n"); + goto fail; + } + + /* Get clocks */ + error = clk_get_by_ofw_name(dev, 0, "apb", &clk_apb); + if (error != 0) { + device_printf(dev, "cannot find apb clock\n"); + goto fail; + } + error = clk_get_by_ofw_name(dev, 0, "codec", &clk_codec); + if (error != 0) { + device_printf(dev, "cannot find codec clock\n"); + goto fail; + } + + /* Gating APB clock for codec */ + error = clk_enable(clk_apb); + if (error != 0) { + device_printf(dev, "cannot enable apb clock\n"); + goto fail; + } + /* Activate audio codec clock. According to the A10 and A20 user + * manuals, Audio_pll can be either 24.576MHz or 22.5792MHz. Most + * audio sampling rates require an 24.576MHz input clock with the + * exception of 44.1kHz, 22.05kHz, and 11.025kHz. Unfortunately, + * both capture and playback use the same clock source so to + * safely support independent full duplex operation, we use a fixed + * 24.576MHz clock source and don't advertise native support for + * the three sampling rates that require a 22.5792MHz input. + */ + error = clk_set_freq(clk_codec, 24576000, CLK_SET_ROUND_DOWN); + if (error != 0) { + device_printf(dev, "cannot set codec clock frequency\n"); + goto fail; + } + /* Enable audio codec clock */ + error = clk_enable(clk_codec); + if (error != 0) { + device_printf(dev, "cannot enable codec clock\n"); + goto fail; + } + + /* Enable DAC */ + val = CODEC_READ(sc, AC_DAC_DPC); + val |= DAC_DPC_EN_DA; + CODEC_WRITE(sc, AC_DAC_DPC, val); + +#ifdef notdef + error = snd_setup_intr(dev, sc->irq, INTR_MPSAFE, a10codec_intr, sc, + &sc->ih); + if (error != 0) { + device_printf(dev, "could not setup interrupt handler\n"); + goto fail; + } +#endif + + if (mixer_init(dev, &a10codec_mixer_class, sc)) { + device_printf(dev, "mixer_init failed\n"); + goto fail; + } + + pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); + + if (pcm_register(dev, sc, 1, 1)) { + device_printf(dev, "pcm_register failed\n"); + goto fail; + } + + pcm_addchan(dev, PCMDIR_PLAY, &a10codec_chan_class, sc); + pcm_addchan(dev, PCMDIR_REC, &a10codec_chan_class, sc); + + snprintf(status, SND_STATUSLEN, "at %s", ofw_bus_get_name(dev)); + pcm_setstatus(dev, status); + + return (0); + +fail: + bus_release_resources(dev, a10codec_spec, sc->res); + snd_mtxfree(sc->lock); + free(sc, M_DEVBUF); + + return (error); +} + +static device_method_t a10codec_pcm_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, a10codec_probe), + DEVMETHOD(device_attach, a10codec_attach), + + DEVMETHOD_END +}; + +static driver_t a10codec_pcm_driver = { + "pcm", + a10codec_pcm_methods, + PCM_SOFTC_SIZE, +}; + +DRIVER_MODULE(a10codec, simplebus, a10codec_pcm_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(a10codec, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(a10codec, 1); Property changes on: trunk/sys/arm/allwinner/a10_codec.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Modified: trunk/sys/arm/allwinner/a10_common.c =================================================================== --- trunk/sys/arm/allwinner/a10_common.c 2020-03-08 17:21:06 UTC (rev 12401) +++ trunk/sys/arm/allwinner/a10_common.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -26,7 +26,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/allwinner/a10_common.c 266337 2014-05-17 18:53:36Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10_common.c 314506 2017-03-01 19:55:04Z ian $"); #include #include @@ -37,12 +37,9 @@ #include #include -#include #include -struct fdt_fixup_entry fdt_fixup_table[] = { - { NULL, NULL } -}; +#ifndef INTRNG static int fdt_aintc_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, @@ -50,7 +47,7 @@ { int offset; - if (fdt_is_compatible(node, "allwinner,sun4i-ic")) + if (fdt_is_compatible(node, "allwinner,sun4i-a10-ic")) offset = 0; else if (fdt_is_compatible(node, "arm,gic")) offset = 32; @@ -68,3 +65,5 @@ &fdt_aintc_decode_ic, NULL }; + +#endif /* INTRNG */ Added: trunk/sys/arm/allwinner/a10_dmac.c =================================================================== --- trunk/sys/arm/allwinner/a10_dmac.c (rev 0) +++ trunk/sys/arm/allwinner/a10_dmac.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,472 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2014-2016 Jared D. McNeill + * 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. + * + */ + +/* + * Allwinner A10/A20 DMA controller + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10_dmac.c 309759 2016-12-09 20:21:48Z manu $"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include "sunxi_dma_if.h" + +#define NDMA_CHANNELS 8 +#define DDMA_CHANNELS 8 + +enum a10dmac_type { + CH_NDMA, + CH_DDMA +}; + +struct a10dmac_softc; + +struct a10dmac_channel { + struct a10dmac_softc * ch_sc; + uint8_t ch_index; + enum a10dmac_type ch_type; + void (*ch_callback)(void *); + void * ch_callbackarg; + uint32_t ch_regoff; +}; + +struct a10dmac_softc { + struct resource * sc_res[2]; + struct mtx sc_mtx; + void * sc_ih; + + struct a10dmac_channel sc_ndma_channels[NDMA_CHANNELS]; + struct a10dmac_channel sc_ddma_channels[DDMA_CHANNELS]; +}; + +static struct resource_spec a10dmac_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define DMA_READ(sc, reg) bus_read_4((sc)->sc_res[0], (reg)) +#define DMA_WRITE(sc, reg, val) bus_write_4((sc)->sc_res[0], (reg), (val)) +#define DMACH_READ(ch, reg) \ + DMA_READ((ch)->ch_sc, (reg) + (ch)->ch_regoff) +#define DMACH_WRITE(ch, reg, val) \ + DMA_WRITE((ch)->ch_sc, (reg) + (ch)->ch_regoff, (val)) + +static void a10dmac_intr(void *); + +static int +a10dmac_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-dma")) + return (ENXIO); + + device_set_desc(dev, "Allwinner DMA controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +a10dmac_attach(device_t dev) +{ + struct a10dmac_softc *sc; + unsigned int index; + clk_t clk; + int error; + + sc = device_get_softc(dev); + + if (bus_alloc_resources(dev, a10dmac_spec, sc->sc_res)) { + device_printf(dev, "cannot allocate resources for device\n"); + return (ENXIO); + } + + mtx_init(&sc->sc_mtx, "a10 dmac", NULL, MTX_SPIN); + + /* Activate DMA controller clock */ + error = clk_get_by_ofw_index(dev, 0, 0, &clk); + if (error != 0) { + device_printf(dev, "cannot get clock\n"); + return (error); + } + error = clk_enable(clk); + if (error != 0) { + device_printf(dev, "cannot enable clock\n"); + return (error); + } + + /* Disable all interrupts and clear pending status */ + DMA_WRITE(sc, AWIN_DMA_IRQ_EN_REG, 0); + DMA_WRITE(sc, AWIN_DMA_IRQ_PEND_STA_REG, ~0); + + /* Initialize channels */ + for (index = 0; index < NDMA_CHANNELS; index++) { + sc->sc_ndma_channels[index].ch_sc = sc; + sc->sc_ndma_channels[index].ch_index = index; + sc->sc_ndma_channels[index].ch_type = CH_NDMA; + sc->sc_ndma_channels[index].ch_callback = NULL; + sc->sc_ndma_channels[index].ch_callbackarg = NULL; + sc->sc_ndma_channels[index].ch_regoff = AWIN_NDMA_REG(index); + DMACH_WRITE(&sc->sc_ndma_channels[index], AWIN_NDMA_CTL_REG, 0); + } + for (index = 0; index < DDMA_CHANNELS; index++) { + sc->sc_ddma_channels[index].ch_sc = sc; + sc->sc_ddma_channels[index].ch_index = index; + sc->sc_ddma_channels[index].ch_type = CH_DDMA; + sc->sc_ddma_channels[index].ch_callback = NULL; + sc->sc_ddma_channels[index].ch_callbackarg = NULL; + sc->sc_ddma_channels[index].ch_regoff = AWIN_DDMA_REG(index); + DMACH_WRITE(&sc->sc_ddma_channels[index], AWIN_DDMA_CTL_REG, 0); + } + + error = bus_setup_intr(dev, sc->sc_res[1], INTR_MPSAFE | INTR_TYPE_MISC, + NULL, a10dmac_intr, sc, &sc->sc_ih); + if (error != 0) { + device_printf(dev, "could not setup interrupt handler\n"); + bus_release_resources(dev, a10dmac_spec, sc->sc_res); + mtx_destroy(&sc->sc_mtx); + return (ENXIO); + } + + OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev); + return (0); +} + +static void +a10dmac_intr(void *priv) +{ + struct a10dmac_softc *sc = priv; + uint32_t sta, bit, mask; + uint8_t index; + + sta = DMA_READ(sc, AWIN_DMA_IRQ_PEND_STA_REG); + DMA_WRITE(sc, AWIN_DMA_IRQ_PEND_STA_REG, sta); + + while ((bit = ffs(sta & AWIN_DMA_IRQ_END_MASK)) != 0) { + mask = (1U << (bit - 1)); + sta &= ~mask; + /* + * Map status bit to channel number. The status register is + * encoded with two bits of status per channel (lowest bit + * is half transfer pending, highest bit is end transfer + * pending). The 8 normal DMA channel status are in the lower + * 16 bits and the 8 dedicated DMA channel status are in + * the upper 16 bits. The output is a channel number from 0-7. + */ + index = ((bit - 1) / 2) & 7; + if (mask & AWIN_DMA_IRQ_NDMA) { + if (sc->sc_ndma_channels[index].ch_callback == NULL) + continue; + sc->sc_ndma_channels[index].ch_callback( + sc->sc_ndma_channels[index].ch_callbackarg); + } else { + if (sc->sc_ddma_channels[index].ch_callback == NULL) + continue; + sc->sc_ddma_channels[index].ch_callback( + sc->sc_ddma_channels[index].ch_callbackarg); + } + } +} + +static uint32_t +a10dmac_read_ctl(struct a10dmac_channel *ch) +{ + if (ch->ch_type == CH_NDMA) { + return (DMACH_READ(ch, AWIN_NDMA_CTL_REG)); + } else { + return (DMACH_READ(ch, AWIN_DDMA_CTL_REG)); + } +} + +static void +a10dmac_write_ctl(struct a10dmac_channel *ch, uint32_t val) +{ + if (ch->ch_type == CH_NDMA) { + DMACH_WRITE(ch, AWIN_NDMA_CTL_REG, val); + } else { + DMACH_WRITE(ch, AWIN_DDMA_CTL_REG, val); + } +} + +static int +a10dmac_set_config(device_t dev, void *priv, const struct sunxi_dma_config *cfg) +{ + struct a10dmac_channel *ch = priv; + uint32_t val; + unsigned int dst_dw, dst_bl, dst_bs, dst_wc, dst_am; + unsigned int src_dw, src_bl, src_bs, src_wc, src_am; + + switch (cfg->dst_width) { + case 8: + dst_dw = AWIN_DMA_CTL_DATA_WIDTH_8; + break; + case 16: + dst_dw = AWIN_DMA_CTL_DATA_WIDTH_16; + break; + case 32: + dst_dw = AWIN_DMA_CTL_DATA_WIDTH_32; + break; + default: + return (EINVAL); + } + switch (cfg->dst_burst_len) { + case 1: + dst_bl = AWIN_DMA_CTL_BURST_LEN_1; + break; + case 4: + dst_bl = AWIN_DMA_CTL_BURST_LEN_4; + break; + case 8: + dst_bl = AWIN_DMA_CTL_BURST_LEN_8; + break; + default: + return (EINVAL); + } + switch (cfg->src_width) { + case 8: + src_dw = AWIN_DMA_CTL_DATA_WIDTH_8; + break; + case 16: + src_dw = AWIN_DMA_CTL_DATA_WIDTH_16; + break; + case 32: + src_dw = AWIN_DMA_CTL_DATA_WIDTH_32; + break; + default: + return (EINVAL); + } + switch (cfg->src_burst_len) { + case 1: + src_bl = AWIN_DMA_CTL_BURST_LEN_1; + break; + case 4: + src_bl = AWIN_DMA_CTL_BURST_LEN_4; + break; + case 8: + src_bl = AWIN_DMA_CTL_BURST_LEN_8; + break; + default: + return (EINVAL); + } + + val = (dst_dw << AWIN_DMA_CTL_DST_DATA_WIDTH_SHIFT) | + (dst_bl << AWIN_DMA_CTL_DST_BURST_LEN_SHIFT) | + (cfg->dst_drqtype << AWIN_DMA_CTL_DST_DRQ_TYPE_SHIFT) | + (src_dw << AWIN_DMA_CTL_SRC_DATA_WIDTH_SHIFT) | + (src_bl << AWIN_DMA_CTL_SRC_BURST_LEN_SHIFT) | + (cfg->src_drqtype << AWIN_DMA_CTL_SRC_DRQ_TYPE_SHIFT); + + if (ch->ch_type == CH_NDMA) { + if (cfg->dst_noincr) + val |= AWIN_NDMA_CTL_DST_ADDR_NOINCR; + if (cfg->src_noincr) + val |= AWIN_NDMA_CTL_SRC_ADDR_NOINCR; + + DMACH_WRITE(ch, AWIN_NDMA_CTL_REG, val); + } else { + dst_am = cfg->dst_noincr ? AWIN_DDMA_CTL_DMA_ADDR_IO : + AWIN_DDMA_CTL_DMA_ADDR_LINEAR; + src_am = cfg->src_noincr ? AWIN_DDMA_CTL_DMA_ADDR_IO : + AWIN_DDMA_CTL_DMA_ADDR_LINEAR; + + val |= (dst_am << AWIN_DDMA_CTL_DST_ADDR_MODE_SHIFT); + val |= (src_am << AWIN_DDMA_CTL_SRC_ADDR_MODE_SHIFT); + + DMACH_WRITE(ch, AWIN_DDMA_CTL_REG, val); + + dst_bs = cfg->dst_blksize - 1; + dst_wc = cfg->dst_wait_cyc - 1; + src_bs = cfg->src_blksize - 1; + src_wc = cfg->src_wait_cyc - 1; + + DMACH_WRITE(ch, AWIN_DDMA_PARA_REG, + (dst_bs << AWIN_DDMA_PARA_DST_DATA_BLK_SIZ_SHIFT) | + (dst_wc << AWIN_DDMA_PARA_DST_WAIT_CYC_SHIFT) | + (src_bs << AWIN_DDMA_PARA_SRC_DATA_BLK_SIZ_SHIFT) | + (src_wc << AWIN_DDMA_PARA_SRC_WAIT_CYC_SHIFT)); + } + + return (0); +} + +static void * +a10dmac_alloc(device_t dev, bool dedicated, void (*cb)(void *), void *cbarg) +{ + struct a10dmac_softc *sc = device_get_softc(dev); + struct a10dmac_channel *ch_list; + struct a10dmac_channel *ch = NULL; + uint32_t irqen; + uint8_t ch_count, index; + + if (dedicated) { + ch_list = sc->sc_ddma_channels; + ch_count = DDMA_CHANNELS; + } else { + ch_list = sc->sc_ndma_channels; + ch_count = NDMA_CHANNELS; + } + + mtx_lock_spin(&sc->sc_mtx); + for (index = 0; index < ch_count; index++) { + if (ch_list[index].ch_callback == NULL) { + ch = &ch_list[index]; + ch->ch_callback = cb; + ch->ch_callbackarg = cbarg; + + irqen = DMA_READ(sc, AWIN_DMA_IRQ_EN_REG); + if (ch->ch_type == CH_NDMA) + irqen |= AWIN_DMA_IRQ_NDMA_END(index); + else + irqen |= AWIN_DMA_IRQ_DDMA_END(index); + DMA_WRITE(sc, AWIN_DMA_IRQ_EN_REG, irqen); + + break; + } + } + mtx_unlock_spin(&sc->sc_mtx); + + return (ch); +} + +static void +a10dmac_free(device_t dev, void *priv) +{ + struct a10dmac_channel *ch = priv; + struct a10dmac_softc *sc = ch->ch_sc; + uint32_t irqen, sta, cfg; + + mtx_lock_spin(&sc->sc_mtx); + + irqen = DMA_READ(sc, AWIN_DMA_IRQ_EN_REG); + cfg = a10dmac_read_ctl(ch); + if (ch->ch_type == CH_NDMA) { + sta = AWIN_DMA_IRQ_NDMA_END(ch->ch_index); + cfg &= ~AWIN_NDMA_CTL_DMA_LOADING; + } else { + sta = AWIN_DMA_IRQ_DDMA_END(ch->ch_index); + cfg &= ~AWIN_DDMA_CTL_DMA_LOADING; + } + irqen &= ~sta; + a10dmac_write_ctl(ch, cfg); + DMA_WRITE(sc, AWIN_DMA_IRQ_EN_REG, irqen); + DMA_WRITE(sc, AWIN_DMA_IRQ_PEND_STA_REG, sta); + + ch->ch_callback = NULL; + ch->ch_callbackarg = NULL; + + mtx_unlock_spin(&sc->sc_mtx); +} + +static int +a10dmac_transfer(device_t dev, void *priv, bus_addr_t src, bus_addr_t dst, + size_t nbytes) +{ + struct a10dmac_channel *ch = priv; + uint32_t cfg; + + cfg = a10dmac_read_ctl(ch); + if (ch->ch_type == CH_NDMA) { + if (cfg & AWIN_NDMA_CTL_DMA_LOADING) + return (EBUSY); + + DMACH_WRITE(ch, AWIN_NDMA_SRC_ADDR_REG, src); + DMACH_WRITE(ch, AWIN_NDMA_DEST_ADDR_REG, dst); + DMACH_WRITE(ch, AWIN_NDMA_BC_REG, nbytes); + + cfg |= AWIN_NDMA_CTL_DMA_LOADING; + a10dmac_write_ctl(ch, cfg); + } else { + if (cfg & AWIN_DDMA_CTL_DMA_LOADING) + return (EBUSY); + + DMACH_WRITE(ch, AWIN_DDMA_SRC_START_ADDR_REG, src); + DMACH_WRITE(ch, AWIN_DDMA_DEST_START_ADDR_REG, dst); + DMACH_WRITE(ch, AWIN_DDMA_BC_REG, nbytes); + + cfg |= AWIN_DDMA_CTL_DMA_LOADING; + a10dmac_write_ctl(ch, cfg); + } + + return (0); +} + +static void +a10dmac_halt(device_t dev, void *priv) +{ + struct a10dmac_channel *ch = priv; + uint32_t cfg; + + cfg = a10dmac_read_ctl(ch); + if (ch->ch_type == CH_NDMA) { + cfg &= ~AWIN_NDMA_CTL_DMA_LOADING; + } else { + cfg &= ~AWIN_DDMA_CTL_DMA_LOADING; + } + a10dmac_write_ctl(ch, cfg); +} + +static device_method_t a10dmac_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, a10dmac_probe), + DEVMETHOD(device_attach, a10dmac_attach), + + /* sunxi DMA interface */ + DEVMETHOD(sunxi_dma_alloc, a10dmac_alloc), + DEVMETHOD(sunxi_dma_free, a10dmac_free), + DEVMETHOD(sunxi_dma_set_config, a10dmac_set_config), + DEVMETHOD(sunxi_dma_transfer, a10dmac_transfer), + DEVMETHOD(sunxi_dma_halt, a10dmac_halt), + + DEVMETHOD_END +}; + +static driver_t a10dmac_driver = { + "a10dmac", + a10dmac_methods, + sizeof(struct a10dmac_softc) +}; + +static devclass_t a10dmac_devclass; + +DRIVER_MODULE(a10dmac, simplebus, a10dmac_driver, a10dmac_devclass, 0, 0); Property changes on: trunk/sys/arm/allwinner/a10_dmac.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/a10_dmac.h =================================================================== --- trunk/sys/arm/allwinner/a10_dmac.h (rev 0) +++ trunk/sys/arm/allwinner/a10_dmac.h 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,159 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2014-2016 Jared D. McNeill + * 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: stable/11/sys/arm/allwinner/a10_dmac.h 295635 2016-02-15 19:56:35Z andrew $ + */ + +#ifndef _A10_DMAC_H_ +#define _A10_DMAC_H_ + +#define AWIN_DMA_IRQ_EN_REG 0x0000 +#define AWIN_DMA_IRQ_PEND_STA_REG 0x0004 +#define AWIN_NDMA_AUTO_GATE_REG 0x0008 +#define AWIN_NDMA_REG(n) (0x100+0x20*(n)) +#define AWIN_NDMA_CTL_REG 0x0000 +#define AWIN_NDMA_SRC_ADDR_REG 0x0004 +#define AWIN_NDMA_DEST_ADDR_REG 0x0008 +#define AWIN_NDMA_BC_REG 0x000c +#define AWIN_DDMA_REG(n) (0x300+0x20*(n)) +#define AWIN_DDMA_CTL_REG 0x0000 +#define AWIN_DDMA_SRC_START_ADDR_REG 0x0004 +#define AWIN_DDMA_DEST_START_ADDR_REG 0x0008 +#define AWIN_DDMA_BC_REG 0x000c +#define AWIN_DDMA_PARA_REG 0x0018 +#define AWIN_DMA_IRQ_END_MASK 0xaaaaaaaa +#define AWIN_DMA_IRQ_HF_MASK 0x55555555 +#define AWIN_DMA_IRQ_DDMA 0xffff0000 +#define AWIN_DMA_IRQ_DDMA_END(n) (1U << (17+2*(n))) +#define AWIN_DMA_IRQ_DDMA_HF(n) (1U << (16+2*(n))) +#define AWIN_DMA_IRQ_NDMA 0x0000ffff +#define AWIN_DMA_IRQ_NDMA_END(n) (1U << (1+2*(n))) +#define AWIN_DMA_IRQ_NDMA_HF(n) (1U << (0+2*(n))) +#define AWIN_NDMA_AUTO_GATING_DIS (1U << 16) +#define AWIN_DMA_CTL_DST_DATA_WIDTH_SHIFT 25 +#define AWIN_DMA_CTL_DST_DATA_WIDTH_MASK (3U << AWIN_DMA_CTL_DST_DATA_WIDTH_SHIFT) +#define AWIN_DMA_CTL_DATA_WIDTH_8 0 +#define AWIN_DMA_CTL_DATA_WIDTH_16 1 +#define AWIN_DMA_CTL_DATA_WIDTH_32 2 +#define AWIN_DMA_CTL_DST_BURST_LEN_SHIFT 23 +#define AWIN_DMA_CTL_DST_BURST_LEN_MASK (3 << AWIN_DMA_CTL_DST_BURST_LEN_SHIFT) +#define AWIN_DMA_CTL_BURST_LEN_1 0 +#define AWIN_DMA_CTL_BURST_LEN_4 1 +#define AWIN_DMA_CTL_BURST_LEN_8 2 +#define AWIN_DMA_CTL_DST_DRQ_TYPE_SHIFT 16 +#define AWIN_DMA_CTL_DST_DRQ_TYPE_MASK (0x1f << AWIN_DMA_CTL_DST_DRQ_TYPE_SHIFT) +#define AWIN_DMA_CTL_BC_REMAINING (1U << 15) +#define AWIN_DMA_CTL_SRC_DATA_WIDTH_SHIFT 9 +#define AWIN_DMA_CTL_SRC_DATA_WIDTH_MASK (3U << AWIN_DMA_CTL_SRC_DATA_WIDTH_SHIFT) +#define AWIN_DMA_CTL_SRC_BURST_LEN_SHIFT 7 +#define AWIN_DMA_CTL_SRC_BURST_LEN_MASK (3U << AWIN_DMA_CTL_SRC_BURST_LEN_SHIFT) +#define AWIN_DMA_CTL_SRC_DRQ_TYPE_SHIFT 0 +#define AWIN_DMA_CTL_SRC_DRQ_TYPE_MASK (0x1f << AWIN_DMA_CTL_SRC_DRQ_TYPE_SHIFT) +#define AWIN_NDMA_CTL_DMA_LOADING (1U << 31) +#define AWIN_NDMA_CTL_DMA_CONTIN_MODE (1U << 30) +#define AWIN_NDMA_CTL_WAIT_STATE_LOG2_SHIFT 27 +#define AWIN_NDMA_CTL_WAIT_STATE_LOG2_MASK (7U << AWIN_NDMA_CTL_WAIT_STATE_LOG2_SHIFT) +#define AWIN_NDMA_CTL_DST_NON_SECURE (1U << 22) +#define AWIN_NDMA_CTL_DST_ADDR_NOINCR (1U << 21) +#define AWIN_NDMA_CTL_DRQ_IRO 0 +#define AWIN_NDMA_CTL_DRQ_IR1 1 +#define AWIN_NDMA_CTL_DRQ_SPDIF 2 +#define AWIN_NDMA_CTL_DRQ_IISO 3 +#define AWIN_NDMA_CTL_DRQ_IIS1 4 +#define AWIN_NDMA_CTL_DRQ_AC97 5 +#define AWIN_NDMA_CTL_DRQ_IIS2 6 +#define AWIN_NDMA_CTL_DRQ_UARTO 8 +#define AWIN_NDMA_CTL_DRQ_UART1 9 +#define AWIN_NDMA_CTL_DRQ_UART2 10 +#define AWIN_NDMA_CTL_DRQ_UART3 11 +#define AWIN_NDMA_CTL_DRQ_UART4 12 +#define AWIN_NDMA_CTL_DRQ_UART5 13 +#define AWIN_NDMA_CTL_DRQ_UART6 14 +#define AWIN_NDMA_CTL_DRQ_UART7 15 +#define AWIN_NDMA_CTL_DRQ_DDC 16 +#define AWIN_NDMA_CTL_DRQ_USB_EP1 17 +#define AWIN_NDMA_CTL_DRQ_CODEC 19 +#define AWIN_NDMA_CTL_DRQ_SRAM 21 +#define AWIN_NDMA_CTL_DRQ_SDRAM 22 +#define AWIN_NDMA_CTL_DRQ_TP_AD 23 +#define AWIN_NDMA_CTL_DRQ_SPI0 24 +#define AWIN_NDMA_CTL_DRQ_SPI1 25 +#define AWIN_NDMA_CTL_DRQ_SPI2 26 +#define AWIN_NDMA_CTL_DRQ_SPI3 27 +#define AWIN_NDMA_CTL_DRQ_USB_EP2 28 +#define AWIN_NDMA_CTL_DRQ_USB_EP3 29 +#define AWIN_NDMA_CTL_DRQ_USB_EP4 30 +#define AWIN_NDMA_CTL_DRQ_USB_EP5 31 +#define AWIN_NDMA_CTL_SRC_NON_SECURE (1U << 6) +#define AWIN_NDMA_CTL_SRC_ADDR_NOINCR (1U << 5) +#define AWIN_NDMA_BC_COUNT 0x0003ffff +#define AWIN_DDMA_CTL_DMA_LOADING (1U << 31) +#define AWIN_DDMA_CTL_BUSY (1U << 30) +#define AWIN_DDMA_CTL_DMA_CONTIN_MODE (1U << 29) +#define AWIN_DDMA_CTL_DST_NON_SECURE (1U << 28) +#define AWIN_DDMA_CTL_DST_ADDR_MODE_SHIFT 21 +#define AWIN_DDMA_CTL_DST_ADDR_MODE_MASK (3U << AWIN_DDMA_CTL_DST_ADDR_MODE_SHIFT) +#define AWIN_DDMA_CTL_DMA_ADDR_LINEAR 0 +#define AWIN_DDMA_CTL_DMA_ADDR_IO 1 +#define AWIN_DDMA_CTL_DMA_ADDR_HPAGE 2 +#define AWIN_DDMA_CTL_DMA_ADDR_VPAGE 3 +#define AWIN_DDMA_CTL_DST_DRQ_TYPE_SHIFT 16 +#define AWIN_DDMA_CTL_DST_DRQ_TYPE_MASK (0x1f << AWIN_DDMA_CTL_DST_DRQ_TYPE_SHIFT) +#define AWIN_DDMA_CTL_DRQ_SRAM 0 +#define AWIN_DDMA_CTL_DRQ_SDRAM 1 +#define AWIN_DDMA_CTL_DRQ_NFC 3 +#define AWIN_DDMA_CTL_DRQ_USB0 4 +#define AWIN_DDMA_CTL_DRQ_EMAC_TX 6 +#define AWIN_DDMA_CTL_DRQ_EMAC_RX 7 +#define AWIN_DDMA_CTL_DRQ_SPI1_TX 8 +#define AWIN_DDMA_CTL_DRQ_SPI1_RX 9 +#define AWIN_DDMA_CTL_DRQ_SS_TX 10 +#define AWIN_DDMA_CTL_DRQ_SS_RX 11 +#define AWIN_DDMA_CTL_DRQ_TCON0 14 +#define AWIN_DDMA_CTL_DRQ_TCON1 15 +#define AWIN_DDMA_CTL_DRQ_MS_TX 23 +#define AWIN_DDMA_CTL_DRQ_MS_RX 23 +#define AWIN_DDMA_CTL_DRQ_HDMI_AUDIO 24 +#define AWIN_DDMA_CTL_DRQ_SPI0_TX 26 +#define AWIN_DDMA_CTL_DRQ_SPI0_RX 27 +#define AWIN_DDMA_CTL_DRQ_SPI2_TX 28 +#define AWIN_DDMA_CTL_DRQ_SPI2_RX 29 +#define AWIN_DDMA_CTL_DRQ_SPI3_TX 30 +#define AWIN_DDMA_CTL_DRQ_SPI3_RX 31 +#define AWIN_DDMA_CTL_SRC_NON_SECURE (1U << 12) +#define AWIN_DDMA_CTL_SRC_ADDR_MODE_SHIFT 5 +#define AWIN_DDMA_CTL_SRC_ADDR_MODE_MASK (3U << AWIN_DDMA_CTL_SRC_ADDR_MODE_SHIFT) +#define AWIN_DDMA_BC_COUNT 0x00003fff +#define AWIN_DDMA_PARA_DST_DATA_BLK_SIZ_SHIFT 24 +#define AWIN_DDMA_PARA_DST_DATA_BLK_SIZ_MASK (0xff << AWIN_DDMA_PARA_DST_DATA_BLK_SIZ_SHIFT) +#define AWIN_DDMA_PARA_DST_WAIT_CYC_SHIFT 16 +#define AWIN_DDMA_PARA_DST_WAIT_CYC_MASK (0xff << AWIN_DDMA_PARA_DST_WAIT_CYC_SHIFT) +#define AWIN_DDMA_PARA_SRC_DATA_BLK_SIZ_SHIFT 8 +#define AWIN_DDMA_PARA_SRC_DATA_BLK_SIZ_MASK (0xff << AWIN_DDMA_PARA_SRC_DATA_BLK_SIZ_SHIFT) +#define AWIN_DDMA_PARA_SRC_WAIT_CYC_SHIFT 0 +#define AWIN_DDMA_PARA_SRC_WAIT_CYC_MASK (0xff << AWIN_DDMA_PARA_SRC_WAIT_CYC_SHIFT) + +#endif /* !_A10_DMAC_H_ */ Property changes on: trunk/sys/arm/allwinner/a10_dmac.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Modified: trunk/sys/arm/allwinner/a10_ehci.c =================================================================== --- trunk/sys/arm/allwinner/a10_ehci.c 2020-03-08 17:21:06 UTC (rev 12401) +++ trunk/sys/arm/allwinner/a10_ehci.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -30,7 +30,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/allwinner/a10_ehci.c 308402 2016-11-07 09:19:04Z hselasky $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10_ehci.c 346524 2019-04-22 04:56:41Z ian $"); #include "opt_bus.h" @@ -41,13 +41,12 @@ #include #include #include -#include #include -#include +#include #include -#include +#include #include #include @@ -60,10 +59,11 @@ #include #include -#include "gpio_if.h" +#include +#include +#include +#include -#include "a10_clk.h" - #define EHCI_HC_DEVSTR "Allwinner Integrated USB 2.0 controller" #define SW_USB_PMU_IRQ_ENABLE 0x800 @@ -76,9 +76,10 @@ #define SW_AHB_INCRX_ALIGN (1 << 8) #define SW_AHB_INCR4 (1 << 9) #define SW_AHB_INCR8 (1 << 10) -#define GPIO_USB1_PWR 230 -#define GPIO_USB2_PWR 227 +#define USB_CONF(d) \ + (void *)ofw_bus_search_compatible((d), compat_data)->ocd_data + #define A10_READ_4(sc, reg) \ bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg) @@ -88,9 +89,35 @@ static device_attach_t a10_ehci_attach; static device_detach_t a10_ehci_detach; -bs_r_1_proto(reversed); -bs_w_1_proto(reversed); +struct aw_ehci_softc { + ehci_softc_t sc; + clk_t clk; + hwreset_t rst; + phy_t phy; +}; +struct aw_ehci_conf { + bool sdram_init; +}; + +static const struct aw_ehci_conf a10_ehci_conf = { + .sdram_init = true, +}; + +static const struct aw_ehci_conf a31_ehci_conf = { + .sdram_init = false, +}; + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-ehci", (uintptr_t)&a10_ehci_conf }, + { "allwinner,sun5i-a13-ehci", (uintptr_t)&a10_ehci_conf }, + { "allwinner,sun6i-a31-ehci", (uintptr_t)&a31_ehci_conf }, + { "allwinner,sun7i-a20-ehci", (uintptr_t)&a10_ehci_conf }, + { "allwinner,sun8i-a83t-ehci", (uintptr_t)&a31_ehci_conf }, + { "allwinner,sun8i-h3-ehci", (uintptr_t)&a31_ehci_conf }, + { NULL, (uintptr_t)NULL } +}; + static int a10_ehci_probe(device_t self) { @@ -98,7 +125,7 @@ if (!ofw_bus_status_okay(self)) return (ENXIO); - if (!ofw_bus_is_compatible(self, "allwinner,usb-ehci")) + if (ofw_bus_search_compatible(self, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(self, EHCI_HC_DEVSTR); @@ -109,13 +136,16 @@ static int a10_ehci_attach(device_t self) { - ehci_softc_t *sc = device_get_softc(self); + struct aw_ehci_softc *aw_sc = device_get_softc(self); + ehci_softc_t *sc = &aw_sc->sc; + const struct aw_ehci_conf *conf; bus_space_handle_t bsh; - device_t sc_gpio_dev; int err; int rid; uint32_t reg_value = 0; + conf = USB_CONF(self); + /* initialise some bus fields */ sc->sc_bus.parent = self; sc->sc_bus.devices = sc->sc_devices; @@ -165,13 +195,6 @@ sprintf(sc->sc_vendor, "Allwinner"); - /* Get the GPIO device, we need this to give power to USB */ - sc_gpio_dev = devclass_get_device(devclass_find("gpio"), 0); - if (sc_gpio_dev == NULL) { - device_printf(self, "Error: failed to get the GPIO device\n"); - goto error; - } - err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { @@ -182,17 +205,39 @@ sc->sc_flags |= EHCI_SCFLG_DONTRESET; + /* De-assert reset */ + if (hwreset_get_by_ofw_idx(self, 0, 0, &aw_sc->rst) == 0) { + err = hwreset_deassert(aw_sc->rst); + if (err != 0) { + device_printf(self, "Could not de-assert reset\n"); + goto error; + } + } + /* Enable clock for USB */ - a10_clk_usb_activate(); + err = clk_get_by_ofw_index(self, 0, 0, &aw_sc->clk); + if (err != 0) { + device_printf(self, "Could not get clock\n"); + goto error; + } + err = clk_enable(aw_sc->clk); + if (err != 0) { + device_printf(self, "Could not enable clock\n"); + goto error; + } - /* Give power to USB */ - GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB2_PWR, GPIO_PIN_OUTPUT); - GPIO_PIN_SET(sc_gpio_dev, GPIO_USB2_PWR, GPIO_PIN_HIGH); + /* Enable USB PHY */ + err = phy_get_by_ofw_name(self, 0, "usb", &aw_sc->phy); + if (err != 0) { + device_printf(self, "Could not get phy\n"); + goto error; + } + err = phy_enable(aw_sc->phy); + if (err != 0) { + device_printf(self, "Could not enable phy\n"); + goto error; + } - /* Give power to USB */ - GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB1_PWR, GPIO_PIN_OUTPUT); - GPIO_PIN_SET(sc_gpio_dev, GPIO_USB1_PWR, GPIO_PIN_HIGH); - /* Enable passby */ reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); reg_value |= SW_AHB_INCR8; /* AHB INCR8 enable */ @@ -202,9 +247,11 @@ A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); /* Configure port */ - reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); - reg_value |= SW_SDRAM_BP_HPCR_ACCESS; - A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); + if (conf->sdram_init) { + reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); + reg_value |= SW_SDRAM_BP_HPCR_ACCESS; + A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); + } err = ehci_init(sc); if (!err) { @@ -217,6 +264,10 @@ return (0); error: + if (aw_sc->clk != NULL) { + clk_disable(aw_sc->clk); + clk_release(aw_sc->clk); + } a10_ehci_detach(self); return (ENXIO); } @@ -224,10 +275,14 @@ static int a10_ehci_detach(device_t self) { - ehci_softc_t *sc = device_get_softc(self); + struct aw_ehci_softc *aw_sc = device_get_softc(self); + ehci_softc_t *sc = &aw_sc->sc; + const struct aw_ehci_conf *conf; int err; uint32_t reg_value = 0; + conf = USB_CONF(self); + /* during module unload there are lots of children leftover */ device_delete_children(self); @@ -258,9 +313,11 @@ usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); /* Disable configure port */ - reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); - reg_value &= ~SW_SDRAM_BP_HPCR_ACCESS; - A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); + if (conf->sdram_init) { + reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); + reg_value &= ~SW_SDRAM_BP_HPCR_ACCESS; + A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); + } /* Disable passby */ reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); @@ -271,8 +328,17 @@ A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); /* Disable clock for USB */ - a10_clk_usb_deactivate(); + if (aw_sc->clk != NULL) { + clk_disable(aw_sc->clk); + clk_release(aw_sc->clk); + } + /* Assert reset */ + if (aw_sc->rst != NULL) { + hwreset_assert(aw_sc->rst); + hwreset_release(aw_sc->rst); + } + return (0); } @@ -291,10 +357,10 @@ static driver_t ehci_driver = { .name = "ehci", .methods = ehci_methods, - .size = sizeof(ehci_softc_t), + .size = sizeof(struct aw_ehci_softc), }; static devclass_t ehci_devclass; -DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); -MODULE_DEPEND(ehci, usb, 1, 1, 1); +DRIVER_MODULE(a10_ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); +MODULE_DEPEND(a10_ehci, usb, 1, 1, 1); Added: trunk/sys/arm/allwinner/a10_fb.c =================================================================== --- trunk/sys/arm/allwinner/a10_fb.c (rev 0) +++ trunk/sys/arm/allwinner/a10_fb.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,663 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/a10_fb.c 308324 2016-11-05 04:17:32Z mmel $ + */ + +/* + * Allwinner A10/A20 Framebuffer + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10_fb.c 308324 2016-11-05 04:17:32Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +#include "fb_if.h" +#include "hdmi_if.h" + +#define FB_DEFAULT_W 800 +#define FB_DEFAULT_H 600 +#define FB_DEFAULT_REF 60 +#define FB_BPP 32 +#define FB_ALIGN 0x1000 + +#define HDMI_ENABLE_DELAY 20000 +#define DEBE_FREQ 300000000 + +#define DOT_CLOCK_TO_HZ(c) ((c) * 1000) + +/* Display backend */ +#define DEBE_REG_START 0x800 +#define DEBE_REG_END 0x1000 +#define DEBE_REG_WIDTH 4 +#define DEBE_MODCTL 0x800 +#define MODCTL_ITLMOD_EN (1 << 28) +#define MODCTL_OUT_SEL_MASK (0x7 << 20) +#define MODCTL_OUT_SEL(sel) ((sel) << 20) +#define OUT_SEL_LCD 0 +#define MODCTL_LAY0_EN (1 << 8) +#define MODCTL_START_CTL (1 << 1) +#define MODCTL_EN (1 << 0) +#define DEBE_DISSIZE 0x808 +#define DIS_HEIGHT(h) (((h) - 1) << 16) +#define DIS_WIDTH(w) (((w) - 1) << 0) +#define DEBE_LAYSIZE0 0x810 +#define LAY_HEIGHT(h) (((h) - 1) << 16) +#define LAY_WIDTH(w) (((w) - 1) << 0) +#define DEBE_LAYCOOR0 0x820 +#define LAY_XCOOR(x) ((x) << 16) +#define LAY_YCOOR(y) ((y) << 0) +#define DEBE_LAYLINEWIDTH0 0x840 +#define DEBE_LAYFB_L32ADD0 0x850 +#define LAYFB_L32ADD(pa) ((pa) << 3) +#define DEBE_LAYFB_H4ADD 0x860 +#define LAY0FB_H4ADD(pa) ((pa) >> 29) +#define DEBE_REGBUFFCTL 0x870 +#define REGBUFFCTL_LOAD (1 << 0) +#define DEBE_ATTCTL1 0x8a0 +#define ATTCTL1_FBFMT(fmt) ((fmt) << 8) +#define FBFMT_XRGB8888 9 +#define ATTCTL1_FBPS(ps) ((ps) << 0) +#define FBPS_32BPP_ARGB 0 + +/* Timing controller */ +#define TCON_GCTL 0x000 +#define GCTL_TCON_EN (1 << 31) +#define GCTL_IO_MAP_SEL_TCON1 (1 << 0) +#define TCON_GINT1 0x008 +#define GINT1_TCON1_LINENO(n) (((n) + 2) << 0) +#define TCON0_DCLK 0x044 +#define DCLK_EN 0xf0000000 +#define TCON1_CTL 0x090 +#define TCON1_EN (1 << 31) +#define INTERLACE_EN (1 << 20) +#define TCON1_SRC_SEL(src) ((src) << 0) +#define TCON1_SRC_CH1 0 +#define TCON1_SRC_CH2 1 +#define TCON1_SRC_BLUE 2 +#define TCON1_START_DELAY(sd) ((sd) << 4) +#define TCON1_BASIC0 0x094 +#define TCON1_BASIC1 0x098 +#define TCON1_BASIC2 0x09c +#define TCON1_BASIC3 0x0a0 +#define TCON1_BASIC4 0x0a4 +#define TCON1_BASIC5 0x0a8 +#define BASIC_X(x) (((x) - 1) << 16) +#define BASIC_Y(y) (((y) - 1) << 0) +#define BASIC3_HT(ht) (((ht) - 1) << 16) +#define BASIC3_HBP(hbp) (((hbp) - 1) << 0) +#define BASIC4_VT(vt) ((vt) << 16) +#define BASIC4_VBP(vbp) (((vbp) - 1) << 0) +#define BASIC5_HSPW(hspw) (((hspw) - 1) << 16) +#define BASIC5_VSPW(vspw) (((vspw) - 1) << 0) +#define TCON1_IO_POL 0x0f0 +#define IO_POL_IO2_INV (1 << 26) +#define IO_POL_PHSYNC (1 << 25) +#define IO_POL_PVSYNC (1 << 24) +#define TCON1_IO_TRI 0x0f4 +#define IO0_OUTPUT_TRI_EN (1 << 24) +#define IO1_OUTPUT_TRI_EN (1 << 25) +#define IO_TRI_MASK 0xffffffff +#define START_DELAY(vbl) (MIN(32, (vbl)) - 2) +#define VBLANK_LEN(vt, vd, i) ((((vt) << (i)) >> 1) - (vd) - 2) +#define VTOTAL(vt) ((vt) * 2) +#define DIVIDE(x, y) (((x) + ((y) / 2)) / (y)) + +struct a10fb_softc { + device_t dev; + device_t fbdev; + struct resource *res[2]; + + /* Framebuffer */ + struct fb_info info; + size_t fbsize; + bus_addr_t paddr; + vm_offset_t vaddr; + + /* HDMI */ + eventhandler_tag hdmi_evh; +}; + +static struct resource_spec a10fb_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* DEBE */ + { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* TCON */ + { -1, 0 } +}; + +#define DEBE_READ(sc, reg) bus_read_4((sc)->res[0], (reg)) +#define DEBE_WRITE(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) + +#define TCON_READ(sc, reg) bus_read_4((sc)->res[1], (reg)) +#define TCON_WRITE(sc, reg, val) bus_write_4((sc)->res[1], (reg), (val)) + +static int +a10fb_allocfb(struct a10fb_softc *sc) +{ + sc->vaddr = kmem_alloc_contig(kernel_arena, sc->fbsize, + M_NOWAIT | M_ZERO, 0, ~0, FB_ALIGN, 0, VM_MEMATTR_WRITE_COMBINING); + if (sc->vaddr == 0) { + device_printf(sc->dev, "failed to allocate FB memory\n"); + return (ENOMEM); + } + sc->paddr = pmap_kextract(sc->vaddr); + + return (0); +} + +static void +a10fb_freefb(struct a10fb_softc *sc) +{ + kmem_free(kernel_arena, sc->vaddr, sc->fbsize); +} + +static int +a10fb_setup_debe(struct a10fb_softc *sc, const struct videomode *mode) +{ + int width, height, interlace, reg; + clk_t clk_ahb, clk_dram, clk_debe; + hwreset_t rst; + uint32_t val; + int error; + + interlace = !!(mode->flags & VID_INTERLACE); + width = mode->hdisplay; + height = mode->vdisplay << interlace; + + /* Leave reset */ + error = hwreset_get_by_ofw_name(sc->dev, 0, "de_be", &rst); + if (error != 0) { + device_printf(sc->dev, "cannot find reset 'de_be'\n"); + return (error); + } + error = hwreset_deassert(rst); + if (error != 0) { + device_printf(sc->dev, "couldn't de-assert reset 'de_be'\n"); + return (error); + } + /* Gating AHB clock for BE */ + error = clk_get_by_ofw_name(sc->dev, 0, "ahb_de_be", &clk_ahb); + if (error != 0) { + device_printf(sc->dev, "cannot find clk 'ahb_de_be'\n"); + return (error); + } + error = clk_enable(clk_ahb); + if (error != 0) { + device_printf(sc->dev, "cannot enable clk 'ahb_de_be'\n"); + return (error); + } + /* Enable DRAM clock to BE */ + error = clk_get_by_ofw_name(sc->dev, 0, "dram_de_be", &clk_dram); + if (error != 0) { + device_printf(sc->dev, "cannot find clk 'dram_de_be'\n"); + return (error); + } + error = clk_enable(clk_dram); + if (error != 0) { + device_printf(sc->dev, "cannot enable clk 'dram_de_be'\n"); + return (error); + } + /* Set BE clock to 300MHz and enable */ + error = clk_get_by_ofw_name(sc->dev, 0, "de_be", &clk_debe); + if (error != 0) { + device_printf(sc->dev, "cannot find clk 'de_be'\n"); + return (error); + } + error = clk_set_freq(clk_debe, DEBE_FREQ, CLK_SET_ROUND_DOWN); + if (error != 0) { + device_printf(sc->dev, "cannot set 'de_be' frequency\n"); + return (error); + } + error = clk_enable(clk_debe); + if (error != 0) { + device_printf(sc->dev, "cannot enable clk 'de_be'\n"); + return (error); + } + + /* Initialize all registers to 0 */ + for (reg = DEBE_REG_START; reg < DEBE_REG_END; reg += DEBE_REG_WIDTH) + DEBE_WRITE(sc, reg, 0); + + /* Enable display backend */ + DEBE_WRITE(sc, DEBE_MODCTL, MODCTL_EN); + + /* Set display size */ + DEBE_WRITE(sc, DEBE_DISSIZE, DIS_HEIGHT(height) | DIS_WIDTH(width)); + + /* Set layer 0 size, position, and stride */ + DEBE_WRITE(sc, DEBE_LAYSIZE0, LAY_HEIGHT(height) | LAY_WIDTH(width)); + DEBE_WRITE(sc, DEBE_LAYCOOR0, LAY_XCOOR(0) | LAY_YCOOR(0)); + DEBE_WRITE(sc, DEBE_LAYLINEWIDTH0, width * FB_BPP); + + /* Point layer 0 to FB memory */ + DEBE_WRITE(sc, DEBE_LAYFB_L32ADD0, LAYFB_L32ADD(sc->paddr)); + DEBE_WRITE(sc, DEBE_LAYFB_H4ADD, LAY0FB_H4ADD(sc->paddr)); + + /* Set backend format and pixel sequence */ + DEBE_WRITE(sc, DEBE_ATTCTL1, ATTCTL1_FBFMT(FBFMT_XRGB8888) | + ATTCTL1_FBPS(FBPS_32BPP_ARGB)); + + /* Enable layer 0, output to LCD, setup interlace */ + val = DEBE_READ(sc, DEBE_MODCTL); + val |= MODCTL_LAY0_EN; + val &= ~MODCTL_OUT_SEL_MASK; + val |= MODCTL_OUT_SEL(OUT_SEL_LCD); + if (interlace) + val |= MODCTL_ITLMOD_EN; + else + val &= ~MODCTL_ITLMOD_EN; + DEBE_WRITE(sc, DEBE_MODCTL, val); + + /* Commit settings */ + DEBE_WRITE(sc, DEBE_REGBUFFCTL, REGBUFFCTL_LOAD); + + /* Start DEBE */ + val = DEBE_READ(sc, DEBE_MODCTL); + val |= MODCTL_START_CTL; + DEBE_WRITE(sc, DEBE_MODCTL, val); + + return (0); +} + +static int +a10fb_setup_pll(struct a10fb_softc *sc, uint64_t freq) +{ + clk_t clk_sclk1, clk_sclk2; + int error; + + error = clk_get_by_ofw_name(sc->dev, 0, "lcd_ch1_sclk1", &clk_sclk1); + if (error != 0) { + device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk1'\n"); + return (error); + } + error = clk_get_by_ofw_name(sc->dev, 0, "lcd_ch1_sclk2", &clk_sclk2); + if (error != 0) { + device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk2'\n"); + return (error); + } + + error = clk_set_freq(clk_sclk2, freq, 0); + if (error != 0) { + device_printf(sc->dev, "cannot set lcd ch1 frequency\n"); + return (error); + } + error = clk_enable(clk_sclk2); + if (error != 0) { + device_printf(sc->dev, "cannot enable lcd ch1 sclk2\n"); + return (error); + } + error = clk_enable(clk_sclk1); + if (error != 0) { + device_printf(sc->dev, "cannot enable lcd ch1 sclk1\n"); + return (error); + } + + return (0); +} + +static int +a10fb_setup_tcon(struct a10fb_softc *sc, const struct videomode *mode) +{ + u_int interlace, hspw, hbp, vspw, vbp, vbl, width, height, start_delay; + u_int vtotal, framerate, clk; + clk_t clk_ahb; + hwreset_t rst; + uint32_t val; + int error; + + interlace = !!(mode->flags & VID_INTERLACE); + width = mode->hdisplay; + height = mode->vdisplay; + hspw = mode->hsync_end - mode->hsync_start; + hbp = mode->htotal - mode->hsync_start; + vspw = mode->vsync_end - mode->vsync_start; + vbp = mode->vtotal - mode->vsync_start; + vbl = VBLANK_LEN(mode->vtotal, mode->vdisplay, interlace); + start_delay = START_DELAY(vbl); + + /* Leave reset */ + error = hwreset_get_by_ofw_name(sc->dev, 0, "lcd", &rst); + if (error != 0) { + device_printf(sc->dev, "cannot find reset 'lcd'\n"); + return (error); + } + error = hwreset_deassert(rst); + if (error != 0) { + device_printf(sc->dev, "couldn't de-assert reset 'lcd'\n"); + return (error); + } + /* Gating AHB clock for LCD */ + error = clk_get_by_ofw_name(sc->dev, 0, "ahb_lcd", &clk_ahb); + if (error != 0) { + device_printf(sc->dev, "cannot find clk 'ahb_lcd'\n"); + return (error); + } + error = clk_enable(clk_ahb); + if (error != 0) { + device_printf(sc->dev, "cannot enable clk 'ahb_lcd'\n"); + return (error); + } + + /* Disable TCON and TCON1 */ + TCON_WRITE(sc, TCON_GCTL, 0); + TCON_WRITE(sc, TCON1_CTL, 0); + + /* Enable clocks */ + TCON_WRITE(sc, TCON0_DCLK, DCLK_EN); + + /* Disable IO and data output ports */ + TCON_WRITE(sc, TCON1_IO_TRI, IO_TRI_MASK); + + /* Disable TCON and select TCON1 */ + TCON_WRITE(sc, TCON_GCTL, GCTL_IO_MAP_SEL_TCON1); + + /* Source width and height */ + TCON_WRITE(sc, TCON1_BASIC0, BASIC_X(width) | BASIC_Y(height)); + /* Scaler width and height */ + TCON_WRITE(sc, TCON1_BASIC1, BASIC_X(width) | BASIC_Y(height)); + /* Output width and height */ + TCON_WRITE(sc, TCON1_BASIC2, BASIC_X(width) | BASIC_Y(height)); + /* Horizontal total and back porch */ + TCON_WRITE(sc, TCON1_BASIC3, BASIC3_HT(mode->htotal) | BASIC3_HBP(hbp)); + /* Vertical total and back porch */ + vtotal = VTOTAL(mode->vtotal); + if (interlace) { + framerate = DIVIDE(DIVIDE(DOT_CLOCK_TO_HZ(mode->dot_clock), + mode->htotal), mode->vtotal); + clk = mode->htotal * (VTOTAL(mode->vtotal) + 1) * framerate; + if ((clk / 2) == DOT_CLOCK_TO_HZ(mode->dot_clock)) + vtotal += 1; + } + TCON_WRITE(sc, TCON1_BASIC4, BASIC4_VT(vtotal) | BASIC4_VBP(vbp)); + /* Horizontal and vertical sync */ + TCON_WRITE(sc, TCON1_BASIC5, BASIC5_HSPW(hspw) | BASIC5_VSPW(vspw)); + /* Polarity */ + val = IO_POL_IO2_INV; + if (mode->flags & VID_PHSYNC) + val |= IO_POL_PHSYNC; + if (mode->flags & VID_PVSYNC) + val |= IO_POL_PVSYNC; + TCON_WRITE(sc, TCON1_IO_POL, val); + + /* Set scan line for TCON1 line trigger */ + TCON_WRITE(sc, TCON_GINT1, GINT1_TCON1_LINENO(start_delay)); + + /* Enable TCON1 */ + val = TCON1_EN; + if (interlace) + val |= INTERLACE_EN; + val |= TCON1_START_DELAY(start_delay); + val |= TCON1_SRC_SEL(TCON1_SRC_CH1); + TCON_WRITE(sc, TCON1_CTL, val); + + /* Setup PLL */ + return (a10fb_setup_pll(sc, DOT_CLOCK_TO_HZ(mode->dot_clock))); +} + +static void +a10fb_enable_tcon(struct a10fb_softc *sc, int onoff) +{ + uint32_t val; + + /* Enable TCON */ + val = TCON_READ(sc, TCON_GCTL); + if (onoff) + val |= GCTL_TCON_EN; + else + val &= ~GCTL_TCON_EN; + TCON_WRITE(sc, TCON_GCTL, val); + + /* Enable TCON1 IO0/IO1 outputs */ + val = TCON_READ(sc, TCON1_IO_TRI); + if (onoff) + val &= ~(IO0_OUTPUT_TRI_EN | IO1_OUTPUT_TRI_EN); + else + val |= (IO0_OUTPUT_TRI_EN | IO1_OUTPUT_TRI_EN); + TCON_WRITE(sc, TCON1_IO_TRI, val); +} + +static int +a10fb_configure(struct a10fb_softc *sc, const struct videomode *mode) +{ + size_t fbsize; + int error; + + fbsize = round_page(mode->hdisplay * mode->vdisplay * (FB_BPP / NBBY)); + + /* Detach the old FB device */ + if (sc->fbdev != NULL) { + device_delete_child(sc->dev, sc->fbdev); + sc->fbdev = NULL; + } + + /* If the FB size has changed, free the old FB memory */ + if (sc->fbsize > 0 && sc->fbsize != fbsize) { + a10fb_freefb(sc); + sc->vaddr = 0; + } + + /* Allocate the FB if necessary */ + sc->fbsize = fbsize; + if (sc->vaddr == 0) { + error = a10fb_allocfb(sc); + if (error != 0) { + device_printf(sc->dev, "failed to allocate FB memory\n"); + return (ENXIO); + } + } + + /* Setup display backend */ + error = a10fb_setup_debe(sc, mode); + if (error != 0) + return (error); + + /* Setup display timing controller */ + error = a10fb_setup_tcon(sc, mode); + if (error != 0) + return (error); + + /* Attach framebuffer device */ + sc->info.fb_name = device_get_nameunit(sc->dev); + sc->info.fb_vbase = (intptr_t)sc->vaddr; + sc->info.fb_pbase = sc->paddr; + sc->info.fb_size = sc->fbsize; + sc->info.fb_bpp = sc->info.fb_depth = FB_BPP; + sc->info.fb_stride = mode->hdisplay * (FB_BPP / NBBY); + sc->info.fb_width = mode->hdisplay; + sc->info.fb_height = mode->vdisplay; + + sc->fbdev = device_add_child(sc->dev, "fbd", device_get_unit(sc->dev)); + if (sc->fbdev == NULL) { + device_printf(sc->dev, "failed to add fbd child\n"); + return (ENOENT); + } + + error = device_probe_and_attach(sc->fbdev); + if (error != 0) { + device_printf(sc->dev, "failed to attach fbd device\n"); + return (error); + } + + return (0); +} + +static void +a10fb_hdmi_event(void *arg, device_t hdmi_dev) +{ + const struct videomode *mode; + struct videomode hdmi_mode; + struct a10fb_softc *sc; + struct edid_info ei; + uint8_t *edid; + uint32_t edid_len; + int error; + + sc = arg; + edid = NULL; + edid_len = 0; + mode = NULL; + + error = HDMI_GET_EDID(hdmi_dev, &edid, &edid_len); + if (error != 0) { + device_printf(sc->dev, "failed to get EDID: %d\n", error); + } else { + error = edid_parse(edid, &ei); + if (error != 0) { + device_printf(sc->dev, "failed to parse EDID: %d\n", + error); + } else { + if (bootverbose) + edid_print(&ei); + mode = ei.edid_preferred_mode; + } + } + + /* If the preferred mode could not be determined, use the default */ + if (mode == NULL) + mode = pick_mode_by_ref(FB_DEFAULT_W, FB_DEFAULT_H, + FB_DEFAULT_REF); + + if (mode == NULL) { + device_printf(sc->dev, "failed to find usable video mode\n"); + return; + } + + if (bootverbose) + device_printf(sc->dev, "using %dx%d\n", + mode->hdisplay, mode->vdisplay); + + /* Disable HDMI */ + HDMI_ENABLE(hdmi_dev, 0); + + /* Disable timing controller */ + a10fb_enable_tcon(sc, 0); + + /* Configure DEBE and TCON */ + error = a10fb_configure(sc, mode); + if (error != 0) { + device_printf(sc->dev, "failed to configure FB: %d\n", error); + return; + } + + hdmi_mode = *mode; + hdmi_mode.hskew = mode->hsync_end - mode->hsync_start; + hdmi_mode.flags |= VID_HSKEW; + HDMI_SET_VIDEOMODE(hdmi_dev, &hdmi_mode); + + /* Enable timing controller */ + a10fb_enable_tcon(sc, 1); + + DELAY(HDMI_ENABLE_DELAY); + + /* Enable HDMI */ + HDMI_ENABLE(hdmi_dev, 1); +} + +static int +a10fb_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-fb")) + return (ENXIO); + + device_set_desc(dev, "Allwinner Framebuffer"); + return (BUS_PROBE_DEFAULT); +} + +static int +a10fb_attach(device_t dev) +{ + struct a10fb_softc *sc; + + sc = device_get_softc(dev); + + sc->dev = dev; + + if (bus_alloc_resources(dev, a10fb_spec, sc->res)) { + device_printf(dev, "cannot allocate resources for device\n"); + return (ENXIO); + } + + sc->hdmi_evh = EVENTHANDLER_REGISTER(hdmi_event, + a10fb_hdmi_event, sc, 0); + + return (0); +} + +static struct fb_info * +a10fb_fb_getinfo(device_t dev) +{ + struct a10fb_softc *sc; + + sc = device_get_softc(dev); + + return (&sc->info); +} + +static device_method_t a10fb_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, a10fb_probe), + DEVMETHOD(device_attach, a10fb_attach), + + /* FB interface */ + DEVMETHOD(fb_getinfo, a10fb_fb_getinfo), + + DEVMETHOD_END +}; + +static driver_t a10fb_driver = { + "fb", + a10fb_methods, + sizeof(struct a10fb_softc), +}; + +static devclass_t a10fb_devclass; + +DRIVER_MODULE(fb, simplebus, a10fb_driver, a10fb_devclass, 0, 0); Property changes on: trunk/sys/arm/allwinner/a10_fb.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Modified: trunk/sys/arm/allwinner/a10_gpio.c =================================================================== --- trunk/sys/arm/allwinner/a10_gpio.c 2020-03-08 17:21:06 UTC (rev 12401) +++ trunk/sys/arm/allwinner/a10_gpio.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -28,7 +28,7 @@ * */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/allwinner/a10_gpio.c 278786 2015-02-14 21:16:19Z loos $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10_gpio.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -42,40 +42,112 @@ #include #include -#include -#include #include -#include #include #include +#include +#include #include #include +#include +#include +#include +#include + #include "gpio_if.h" -#include "a10_gpio.h" -/* - * A10 have 9 banks of gpio. - * 32 pins per bank: - * PA0 - PA17 | PB0 - PB23 | PC0 - PC24 - * PD0 - PD27 | PE0 - PE31 | PF0 - PF5 - * PG0 - PG9 | PH0 - PH27 | PI0 - PI12 - */ - -#define A10_GPIO_PINS 288 #define A10_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) -#define A10_GPIO_NONE 0 -#define A10_GPIO_PULLUP 1 -#define A10_GPIO_PULLDOWN 2 +#define A10_GPIO_NONE 0 +#define A10_GPIO_PULLUP 1 +#define A10_GPIO_PULLDOWN 2 -#define A10_GPIO_INPUT 0 -#define A10_GPIO_OUTPUT 1 +#define A10_GPIO_INPUT 0 +#define A10_GPIO_OUTPUT 1 +#define AW_GPIO_DRV_MASK 0x3 +#define AW_GPIO_PUD_MASK 0x3 + +#define AW_PINCTRL 1 +#define AW_R_PINCTRL 2 + +/* Defined in a10_padconf.c */ +#ifdef SOC_ALLWINNER_A10 +extern const struct allwinner_padconf a10_padconf; +#endif + +/* Defined in a13_padconf.c */ +#ifdef SOC_ALLWINNER_A13 +extern const struct allwinner_padconf a13_padconf; +#endif + +/* Defined in a20_padconf.c */ +#ifdef SOC_ALLWINNER_A20 +extern const struct allwinner_padconf a20_padconf; +#endif + +/* Defined in a31_padconf.c */ +#ifdef SOC_ALLWINNER_A31 +extern const struct allwinner_padconf a31_padconf; +#endif + +/* Defined in a31s_padconf.c */ +#ifdef SOC_ALLWINNER_A31S +extern const struct allwinner_padconf a31s_padconf; +#endif + +#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S) +extern const struct allwinner_padconf a31_r_padconf; +#endif + +/* Defined in h3_padconf.c */ +#ifdef SOC_ALLWINNER_H3 +extern const struct allwinner_padconf h3_padconf; +extern const struct allwinner_padconf h3_r_padconf; +#endif + +/* Defined in a83t_padconf.c */ +#ifdef SOC_ALLWINNER_A83T +extern const struct allwinner_padconf a83t_padconf; +extern const struct allwinner_padconf a83t_r_padconf; +#endif + +static struct ofw_compat_data compat_data[] = { +#ifdef SOC_ALLWINNER_A10 + {"allwinner,sun4i-a10-pinctrl", (uintptr_t)&a10_padconf}, +#endif +#ifdef SOC_ALLWINNER_A13 + {"allwinner,sun5i-a13-pinctrl", (uintptr_t)&a13_padconf}, +#endif +#ifdef SOC_ALLWINNER_A20 + {"allwinner,sun7i-a20-pinctrl", (uintptr_t)&a20_padconf}, +#endif +#ifdef SOC_ALLWINNER_A31 + {"allwinner,sun6i-a31-pinctrl", (uintptr_t)&a31_padconf}, +#endif +#ifdef SOC_ALLWINNER_A31S + {"allwinner,sun6i-a31s-pinctrl", (uintptr_t)&a31s_padconf}, +#endif +#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S) + {"allwinner,sun6i-a31-r-pinctrl", (uintptr_t)&a31_r_padconf}, +#endif +#ifdef SOC_ALLWINNER_A83T + {"allwinner,sun8i-a83t-pinctrl", (uintptr_t)&a83t_padconf}, + {"allwinner,sun8i-a83t-r-pinctrl", (uintptr_t)&a83t_r_padconf}, +#endif +#ifdef SOC_ALLWINNER_H3 + {"allwinner,sun8i-h3-pinctrl", (uintptr_t)&h3_padconf}, + {"allwinner,sun8i-h3-r-pinctrl", (uintptr_t)&h3_r_padconf}, +#endif + {NULL, 0} +}; + struct a10_gpio_softc { device_t sc_dev; + device_t sc_busdev; struct mtx sc_mtx; struct resource * sc_mem_res; struct resource * sc_irq_res; @@ -82,18 +154,17 @@ bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; void * sc_intrhand; - int sc_gpio_npins; - struct gpio_pin sc_gpio_pins[A10_GPIO_PINS]; + const struct allwinner_padconf * padconf; }; -#define A10_GPIO_LOCK(_sc) mtx_lock(&_sc->sc_mtx) -#define A10_GPIO_UNLOCK(_sc) mtx_unlock(&_sc->sc_mtx) -#define A10_GPIO_LOCK_ASSERT(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED) +#define A10_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) +#define A10_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) +#define A10_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) -#define A10_GPIO_GP_CFG(_bank, _pin) 0x00 + ((_bank) * 0x24) + ((_pin)<<2) +#define A10_GPIO_GP_CFG(_bank, _idx) 0x00 + ((_bank) * 0x24) + ((_idx) << 2) #define A10_GPIO_GP_DAT(_bank) 0x10 + ((_bank) * 0x24) -#define A10_GPIO_GP_DRV(_bank, _pin) 0x14 + ((_bank) * 0x24) + ((_pin)<<2) -#define A10_GPIO_GP_PUL(_bank, _pin) 0x1c + ((_bank) * 0x24) + ((_pin)<<2) +#define A10_GPIO_GP_DRV(_bank, _idx) 0x14 + ((_bank) * 0x24) + ((_idx) << 2) +#define A10_GPIO_GP_PUL(_bank, _idx) 0x1c + ((_bank) * 0x24) + ((_idx) << 2) #define A10_GPIO_GP_INT_CFG0 0x200 #define A10_GPIO_GP_INT_CFG1 0x204 @@ -104,7 +175,8 @@ #define A10_GPIO_GP_INT_STA 0x214 #define A10_GPIO_GP_INT_DEB 0x218 -static struct a10_gpio_softc *a10_gpio_sc; +static int a10_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value); +static int a10_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); #define A10_GPIO_WRITE(_sc, _off, _val) \ bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val) @@ -116,113 +188,178 @@ { uint32_t bank, func, offset; - bank = pin / 32; - pin = pin - 32 * bank; - func = pin >> 3; + /* Must be called with lock held. */ + A10_GPIO_LOCK_ASSERT(sc); + + if (pin > sc->padconf->npins) + return (0); + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; offset = ((pin & 0x07) << 2); - A10_GPIO_LOCK(sc); - func = (A10_GPIO_READ(sc, A10_GPIO_GP_CFG(bank, func)) >> offset) & 7; - A10_GPIO_UNLOCK(sc); + func = A10_GPIO_READ(sc, A10_GPIO_GP_CFG(bank, pin >> 3)); - return (func); + return ((func >> offset) & 0x7); } -static uint32_t -a10_gpio_func_flag(uint32_t nfunc) +static int +a10_gpio_set_function(struct a10_gpio_softc *sc, uint32_t pin, uint32_t f) { + uint32_t bank, data, offset; - switch (nfunc) { - case A10_GPIO_INPUT: - return (GPIO_PIN_INPUT); - case A10_GPIO_OUTPUT: - return (GPIO_PIN_OUTPUT); - } + /* Check if the function exists in the padconf data */ + if (sc->padconf->pins[pin].functions[f] == NULL) + return (EINVAL); + + /* Must be called with lock held. */ + A10_GPIO_LOCK_ASSERT(sc); + + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; + offset = ((pin & 0x07) << 2); + + data = A10_GPIO_READ(sc, A10_GPIO_GP_CFG(bank, pin >> 3)); + data &= ~(7 << offset); + data |= (f << offset); + A10_GPIO_WRITE(sc, A10_GPIO_GP_CFG(bank, pin >> 3), data); + return (0); } -static void -a10_gpio_set_function(struct a10_gpio_softc *sc, uint32_t pin, uint32_t f) +static uint32_t +a10_gpio_get_pud(struct a10_gpio_softc *sc, uint32_t pin) { - uint32_t bank, func, data, offset; + uint32_t bank, offset, val; /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); - bank = pin / 32; - pin = pin - 32 * bank; - func = pin >> 3; - offset = ((pin & 0x07) << 2); + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; + offset = ((pin & 0x0f) << 1); - data = A10_GPIO_READ(sc, A10_GPIO_GP_CFG(bank, func)); - data &= ~(7 << offset); - data |= (f << offset); - A10_GPIO_WRITE(sc, A10_GPIO_GP_CFG(bank, func), data); + val = A10_GPIO_READ(sc, A10_GPIO_GP_PUL(bank, pin >> 4)); + + return ((val >> offset) & AW_GPIO_PUD_MASK); } static void a10_gpio_set_pud(struct a10_gpio_softc *sc, uint32_t pin, uint32_t state) { - uint32_t bank, offset, pull, val; + uint32_t bank, offset, val; /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); - bank = pin / 32; - pin = pin - 32 * bank; - pull = pin >> 4; + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; offset = ((pin & 0x0f) << 1); - val = A10_GPIO_READ(sc, A10_GPIO_GP_PUL(bank, pull)); - val &= ~(0x03 << offset); + val = A10_GPIO_READ(sc, A10_GPIO_GP_PUL(bank, pin >> 4)); + val &= ~(AW_GPIO_PUD_MASK << offset); val |= (state << offset); - A10_GPIO_WRITE(sc, A10_GPIO_GP_PUL(bank, pull), val); + A10_GPIO_WRITE(sc, A10_GPIO_GP_PUL(bank, pin >> 4), val); } +static uint32_t +a10_gpio_get_drv(struct a10_gpio_softc *sc, uint32_t pin) +{ + uint32_t bank, offset, val; + + /* Must be called with lock held. */ + A10_GPIO_LOCK_ASSERT(sc); + + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; + offset = ((pin & 0x0f) << 1); + + val = A10_GPIO_READ(sc, A10_GPIO_GP_DRV(bank, pin >> 4)); + + return ((val >> offset) & AW_GPIO_DRV_MASK); +} + static void -a10_gpio_pin_configure(struct a10_gpio_softc *sc, struct gpio_pin *pin, - unsigned int flags) +a10_gpio_set_drv(struct a10_gpio_softc *sc, uint32_t pin, uint32_t drive) { + uint32_t bank, offset, val; - A10_GPIO_LOCK(sc); + /* Must be called with lock held. */ + A10_GPIO_LOCK_ASSERT(sc); - /* - * Manage input/output. - */ - if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { - pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); - if (flags & GPIO_PIN_OUTPUT) { - pin->gp_flags |= GPIO_PIN_OUTPUT; - a10_gpio_set_function(sc, pin->gp_pin, - A10_GPIO_OUTPUT); + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; + offset = ((pin & 0x0f) << 1); + + val = A10_GPIO_READ(sc, A10_GPIO_GP_DRV(bank, pin >> 4)); + val &= ~(AW_GPIO_DRV_MASK << offset); + val |= (drive << offset); + A10_GPIO_WRITE(sc, A10_GPIO_GP_DRV(bank, pin >> 4), val); +} + +static int +a10_gpio_pin_configure(struct a10_gpio_softc *sc, uint32_t pin, uint32_t flags) +{ + u_int val; + int err = 0; + + /* Must be called with lock held. */ + A10_GPIO_LOCK_ASSERT(sc); + + if (pin > sc->padconf->npins) + return (EINVAL); + + /* Manage input/output. */ + if (flags & GPIO_PIN_INPUT) { + err = a10_gpio_set_function(sc, pin, A10_GPIO_INPUT); + } else if (flags & GPIO_PIN_OUTPUT) { + if (flags & GPIO_PIN_PRESET_LOW) { + a10_gpio_pin_set(sc->sc_dev, pin, 0); + } else if (flags & GPIO_PIN_PRESET_HIGH) { + a10_gpio_pin_set(sc->sc_dev, pin, 1); } else { - pin->gp_flags |= GPIO_PIN_INPUT; - a10_gpio_set_function(sc, pin->gp_pin, - A10_GPIO_INPUT); + /* Read the pin and preset output to current state. */ + err = a10_gpio_set_function(sc, pin, A10_GPIO_INPUT); + if (err == 0) { + a10_gpio_pin_get(sc->sc_dev, pin, &val); + a10_gpio_pin_set(sc->sc_dev, pin, val); + } } + if (err == 0) + err = a10_gpio_set_function(sc, pin, A10_GPIO_OUTPUT); } + if (err) + return (err); + /* Manage Pull-up/pull-down. */ - pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN); - if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) { - if (flags & GPIO_PIN_PULLUP) { - pin->gp_flags |= GPIO_PIN_PULLUP; - a10_gpio_set_pud(sc, pin->gp_pin, A10_GPIO_PULLUP); - } else { - pin->gp_flags |= GPIO_PIN_PULLDOWN; - a10_gpio_set_pud(sc, pin->gp_pin, A10_GPIO_PULLDOWN); - } - } else - a10_gpio_set_pud(sc, pin->gp_pin, A10_GPIO_NONE); + if (flags & GPIO_PIN_PULLUP) + a10_gpio_set_pud(sc, pin, A10_GPIO_PULLUP); + else if (flags & GPIO_PIN_PULLDOWN) + a10_gpio_set_pud(sc, pin, A10_GPIO_PULLDOWN); + else + a10_gpio_set_pud(sc, pin, A10_GPIO_NONE); - A10_GPIO_UNLOCK(sc); + return (0); } +static device_t +a10_gpio_get_bus(device_t dev) +{ + struct a10_gpio_softc *sc; + + sc = device_get_softc(dev); + + return (sc->sc_busdev); +} + static int a10_gpio_pin_max(device_t dev, int *maxpin) { + struct a10_gpio_softc *sc; - *maxpin = A10_GPIO_PINS - 1; + sc = device_get_softc(dev); + + *maxpin = sc->padconf->npins - 1; return (0); } @@ -229,20 +366,13 @@ static int a10_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { - struct a10_gpio_softc *sc = device_get_softc(dev); - int i; + struct a10_gpio_softc *sc; - for (i = 0; i < sc->sc_gpio_npins; i++) { - if (sc->sc_gpio_pins[i].gp_pin == pin) - break; - } - - if (i >= sc->sc_gpio_npins) + sc = device_get_softc(dev); + if (pin >= sc->padconf->npins) return (EINVAL); - A10_GPIO_LOCK(sc); - *caps = sc->sc_gpio_pins[i].gp_caps; - A10_GPIO_UNLOCK(sc); + *caps = A10_GPIO_DEFAULT_CAPS; return (0); } @@ -250,19 +380,40 @@ static int a10_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { - struct a10_gpio_softc *sc = device_get_softc(dev); - int i; + struct a10_gpio_softc *sc; + uint32_t func; + uint32_t pud; - for (i = 0; i < sc->sc_gpio_npins; i++) { - if (sc->sc_gpio_pins[i].gp_pin == pin) - break; + sc = device_get_softc(dev); + if (pin >= sc->padconf->npins) + return (EINVAL); + + A10_GPIO_LOCK(sc); + func = a10_gpio_get_function(sc, pin); + switch (func) { + case A10_GPIO_INPUT: + *flags = GPIO_PIN_INPUT; + break; + case A10_GPIO_OUTPUT: + *flags = GPIO_PIN_OUTPUT; + break; + default: + *flags = 0; + break; } - if (i >= sc->sc_gpio_npins) - return (EINVAL); + pud = a10_gpio_get_pud(sc, pin); + switch (pud) { + case A10_GPIO_PULLDOWN: + *flags |= GPIO_PIN_PULLDOWN; + break; + case A10_GPIO_PULLUP: + *flags |= GPIO_PIN_PULLUP; + break; + default: + break; + } - A10_GPIO_LOCK(sc); - *flags = sc->sc_gpio_pins[i].gp_flags; A10_GPIO_UNLOCK(sc); return (0); @@ -271,20 +422,15 @@ static int a10_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { - struct a10_gpio_softc *sc = device_get_softc(dev); - int i; + struct a10_gpio_softc *sc; - for (i = 0; i < sc->sc_gpio_npins; i++) { - if (sc->sc_gpio_pins[i].gp_pin == pin) - break; - } - - if (i >= sc->sc_gpio_npins) + sc = device_get_softc(dev); + if (pin >= sc->padconf->npins) return (EINVAL); - A10_GPIO_LOCK(sc); - memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME); - A10_GPIO_UNLOCK(sc); + snprintf(name, GPIOMAXNAME - 1, "%s", + sc->padconf->pins[pin].name); + name[GPIOMAXNAME - 1] = '\0'; return (0); } @@ -292,47 +438,39 @@ static int a10_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { - struct a10_gpio_softc *sc = device_get_softc(dev); - int i; + struct a10_gpio_softc *sc; + int err; - for (i = 0; i < sc->sc_gpio_npins; i++) { - if (sc->sc_gpio_pins[i].gp_pin == pin) - break; - } - - if (i >= sc->sc_gpio_npins) + sc = device_get_softc(dev); + if (pin > sc->padconf->npins) return (EINVAL); - a10_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags); + A10_GPIO_LOCK(sc); + err = a10_gpio_pin_configure(sc, pin, flags); + A10_GPIO_UNLOCK(sc); - return (0); + return (err); } static int a10_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { - struct a10_gpio_softc *sc = device_get_softc(dev); - uint32_t bank, offset, data; - int i; + struct a10_gpio_softc *sc; + uint32_t bank, data; - for (i = 0; i < sc->sc_gpio_npins; i++) { - if (sc->sc_gpio_pins[i].gp_pin == pin) - break; - } - - if (i >= sc->sc_gpio_npins) + sc = device_get_softc(dev); + if (pin > sc->padconf->npins) return (EINVAL); - bank = pin / 32; - pin = pin - 32 * bank; - offset = pin & 0x1f; + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; A10_GPIO_LOCK(sc); data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); if (value) - data |= (1 << offset); + data |= (1 << pin); else - data &= ~(1 << offset); + data &= ~(1 << pin); A10_GPIO_WRITE(sc, A10_GPIO_GP_DAT(bank), data); A10_GPIO_UNLOCK(sc); @@ -342,26 +480,20 @@ static int a10_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { - struct a10_gpio_softc *sc = device_get_softc(dev); - uint32_t bank, offset, reg_data; - int i; + struct a10_gpio_softc *sc; + uint32_t bank, reg_data; - for (i = 0; i < sc->sc_gpio_npins; i++) { - if (sc->sc_gpio_pins[i].gp_pin == pin) - break; - } - - if (i >= sc->sc_gpio_npins) + sc = device_get_softc(dev); + if (pin > sc->padconf->npins) return (EINVAL); - bank = pin / 32; - pin = pin - 32 * bank; - offset = pin & 0x1f; + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; A10_GPIO_LOCK(sc); reg_data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); A10_GPIO_UNLOCK(sc); - *val = (reg_data & (1 << offset)) ? 1 : 0; + *val = (reg_data & (1 << pin)) ? 1 : 0; return (0); } @@ -369,28 +501,22 @@ static int a10_gpio_pin_toggle(device_t dev, uint32_t pin) { - struct a10_gpio_softc *sc = device_get_softc(dev); - uint32_t bank, data, offset; - int i; + struct a10_gpio_softc *sc; + uint32_t bank, data; - for (i = 0; i < sc->sc_gpio_npins; i++) { - if (sc->sc_gpio_pins[i].gp_pin == pin) - break; - } - - if (i >= sc->sc_gpio_npins) + sc = device_get_softc(dev); + if (pin > sc->padconf->npins) return (EINVAL); - bank = pin / 32; - pin = pin - 32 * bank; - offset = pin & 0x1f; + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; A10_GPIO_LOCK(sc); data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); - if (data & (1 << offset)) - data &= ~(1 << offset); + if (data & (1 << pin)) + data &= ~(1 << pin); else - data |= (1 << offset); + data |= (1 << pin); A10_GPIO_WRITE(sc, A10_GPIO_GP_DAT(bank), data); A10_GPIO_UNLOCK(sc); @@ -398,6 +524,165 @@ } static int +a10_gpio_pin_access_32(device_t dev, uint32_t first_pin, uint32_t clear_pins, + uint32_t change_pins, uint32_t *orig_pins) +{ + struct a10_gpio_softc *sc; + uint32_t bank, data, pin; + + sc = device_get_softc(dev); + if (first_pin > sc->padconf->npins) + return (EINVAL); + + /* + * We require that first_pin refers to the first pin in a bank, because + * this API is not about convenience, it's for making a set of pins + * change simultaneously (required) with reasonably high performance + * (desired); we need to do a read-modify-write on a single register. + */ + bank = sc->padconf->pins[first_pin].port; + pin = sc->padconf->pins[first_pin].pin; + if (pin != 0) + return (EINVAL); + + A10_GPIO_LOCK(sc); + data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); + if ((clear_pins | change_pins) != 0) + A10_GPIO_WRITE(sc, A10_GPIO_GP_DAT(bank), + (data & ~clear_pins) ^ change_pins); + A10_GPIO_UNLOCK(sc); + + if (orig_pins != NULL) + *orig_pins = data; + + return (0); +} + +static int +a10_gpio_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins, + uint32_t *pin_flags) +{ + struct a10_gpio_softc *sc; + uint32_t bank, pin; + int err; + + sc = device_get_softc(dev); + if (first_pin > sc->padconf->npins) + return (EINVAL); + + bank = sc->padconf->pins[first_pin].port; + if (sc->padconf->pins[first_pin].pin != 0) + return (EINVAL); + + /* + * The configuration for a bank of pins is scattered among several + * registers; we cannot g'tee to simultaneously change the state of all + * the pins in the flags array. So just loop through the array + * configuring each pin for now. If there was a strong need, it might + * be possible to support some limited simultaneous config, such as + * adjacent groups of 8 pins that line up the same as the config regs. + */ + for (err = 0, pin = first_pin; err == 0 && pin < num_pins; ++pin) { + if (pin_flags[pin] & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) + err = a10_gpio_pin_configure(sc, pin, pin_flags[pin]); + } + + return (err); +} + +static int +aw_find_pinnum_by_name(struct a10_gpio_softc *sc, const char *pinname) +{ + int i; + + for (i = 0; i < sc->padconf->npins; i++) + if (!strcmp(pinname, sc->padconf->pins[i].name)) + return i; + + return (-1); +} + +static int +aw_find_pin_func(struct a10_gpio_softc *sc, int pin, const char *func) +{ + int i; + + for (i = 0; i < AW_MAX_FUNC_BY_PIN; i++) + if (sc->padconf->pins[pin].functions[i] && + !strcmp(func, sc->padconf->pins[pin].functions[i])) + return (i); + + return (-1); +} + +static int +aw_fdt_configure_pins(device_t dev, phandle_t cfgxref) +{ + struct a10_gpio_softc *sc; + phandle_t node; + const char **pinlist = NULL; + char *pin_function = NULL; + uint32_t pin_drive, pin_pull; + int pins_nb, pin_num, pin_func, i, ret; + + sc = device_get_softc(dev); + node = OF_node_from_xref(cfgxref); + ret = 0; + + /* Getting all prop for configuring pins */ + pins_nb = ofw_bus_string_list_to_array(node, "allwinner,pins", &pinlist); + if (pins_nb <= 0) + return (ENOENT); + if (OF_getprop_alloc(node, "allwinner,function", + sizeof(*pin_function), + (void **)&pin_function) == -1) { + ret = ENOENT; + goto out; + } + if (OF_getencprop(node, "allwinner,drive", + &pin_drive, sizeof(pin_drive)) == -1) { + ret = ENOENT; + goto out; + } + if (OF_getencprop(node, "allwinner,pull", + &pin_pull, sizeof(pin_pull)) == -1) { + ret = ENOENT; + goto out; + } + + /* Configure each pin to the correct function, drive and pull */ + for (i = 0; i < pins_nb; i++) { + pin_num = aw_find_pinnum_by_name(sc, pinlist[i]); + if (pin_num == -1) { + ret = ENOENT; + goto out; + } + pin_func = aw_find_pin_func(sc, pin_num, pin_function); + if (pin_func == -1) { + ret = ENOENT; + goto out; + } + + A10_GPIO_LOCK(sc); + + if (a10_gpio_get_function(sc, pin_num) != pin_func) + a10_gpio_set_function(sc, pin_num, pin_func); + if (a10_gpio_get_drv(sc, pin_num) != pin_drive) + a10_gpio_set_drv(sc, pin_num, pin_drive); + if (a10_gpio_get_pud(sc, pin_num) != pin_pull && + (pin_pull == A10_GPIO_PULLUP || + pin_pull == A10_GPIO_PULLDOWN)) + a10_gpio_set_pud(sc, pin_num, pin_pull); + A10_GPIO_UNLOCK(sc); + } + + out: + OF_prop_free(pinlist); + OF_prop_free(pin_function); + return (ret); +} + +static int a10_gpio_probe(device_t dev) { @@ -404,10 +689,10 @@ if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-gpio")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); - device_set_desc(dev, "Allwinner GPIO controller"); + device_set_desc(dev, "Allwinner GPIO/Pinmux controller"); return (BUS_PROBE_DEFAULT); } @@ -414,14 +699,16 @@ static int a10_gpio_attach(device_t dev) { - struct a10_gpio_softc *sc = device_get_softc(dev); - uint32_t func; - int i, rid; + int rid, error; phandle_t gpio; + struct a10_gpio_softc *sc; + clk_t clk; + hwreset_t rst; + sc = device_get_softc(dev); sc->sc_dev = dev; - mtx_init(&sc->sc_mtx, "a10 gpio", "gpio", MTX_DEF); + mtx_init(&sc->sc_mtx, "a10 gpio", "gpio", MTX_SPIN); rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, @@ -428,7 +715,7 @@ RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); - return (ENXIO); + goto fail; } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); @@ -438,41 +725,55 @@ 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); + goto fail; } /* Find our node. */ gpio = ofw_bus_get_node(sc->sc_dev); - if (!OF_hasprop(gpio, "gpio-controller")) /* Node is not a GPIO controller. */ goto fail; - /* Initialize the software controlled pins. */ - for (i = 0; i < A10_GPIO_PINS; i++) { - snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, - "pin %d", i); - func = a10_gpio_get_function(sc, i); - sc->sc_gpio_pins[i].gp_pin = i; - sc->sc_gpio_pins[i].gp_caps = A10_GPIO_DEFAULT_CAPS; - sc->sc_gpio_pins[i].gp_flags = a10_gpio_func_flag(func); + /* Use the right pin data for the current SoC */ + sc->padconf = (struct allwinner_padconf *)ofw_bus_search_compatible(dev, + compat_data)->ocd_data; + + if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) { + error = hwreset_deassert(rst); + if (error != 0) { + device_printf(dev, "cannot de-assert reset\n"); + return (error); + } } - sc->sc_gpio_npins = i; - device_add_child(dev, "gpioc", -1); - device_add_child(dev, "gpiobus", -1); + if (clk_get_by_ofw_index(dev, 0, 0, &clk) == 0) { + error = clk_enable(clk); + if (error != 0) { + device_printf(dev, "could not enable clock\n"); + return (error); + } + } - a10_gpio_sc = sc; + sc->sc_busdev = gpiobus_attach_bus(dev); + if (sc->sc_busdev == NULL) + goto fail; - return (bus_generic_attach(dev)); + /* + * Register as a pinctrl device + */ + fdt_pinctrl_register(dev, "allwinner,pins"); + fdt_pinctrl_configure_tree(dev); + return (0); + fail: 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); + mtx_destroy(&sc->sc_mtx); + return (ENXIO); } @@ -483,6 +784,35 @@ return (EBUSY); } +static phandle_t +a10_gpio_get_node(device_t dev, device_t bus) +{ + + /* We only have one child, the GPIO bus, which needs our own node. */ + return (ofw_bus_get_node(dev)); +} + +static int +a10_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, + pcell_t *gpios, uint32_t *pin, uint32_t *flags) +{ + struct a10_gpio_softc *sc; + int i; + + sc = device_get_softc(bus); + + /* The GPIO pins are mapped as: . */ + for (i = 0; i < sc->padconf->npins; i++) + if (sc->padconf->pins[i].port == gpios[0] && + sc->padconf->pins[i].pin == gpios[1]) { + *pin = i; + break; + } + *flags = gpios[gcells - 1]; + + return (0); +} + static device_method_t a10_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, a10_gpio_probe), @@ -490,6 +820,7 @@ DEVMETHOD(device_detach, a10_gpio_detach), /* GPIO protocol */ + DEVMETHOD(gpio_get_bus, a10_gpio_get_bus), DEVMETHOD(gpio_pin_max, a10_gpio_pin_max), DEVMETHOD(gpio_pin_getname, a10_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, a10_gpio_pin_getflags), @@ -498,7 +829,16 @@ DEVMETHOD(gpio_pin_get, a10_gpio_pin_get), DEVMETHOD(gpio_pin_set, a10_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, a10_gpio_pin_toggle), + DEVMETHOD(gpio_pin_access_32, a10_gpio_pin_access_32), + DEVMETHOD(gpio_pin_config_32, a10_gpio_pin_config_32), + DEVMETHOD(gpio_map_gpios, a10_gpio_map_gpios), + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, a10_gpio_get_node), + + /* fdt_pinctrl interface */ + DEVMETHOD(fdt_pinctrl_configure,aw_fdt_configure_pins), + DEVMETHOD_END }; @@ -510,20 +850,5 @@ sizeof(struct a10_gpio_softc), }; -DRIVER_MODULE(a10_gpio, simplebus, a10_gpio_driver, a10_gpio_devclass, 0, 0); - -int -a10_emac_gpio_config(uint32_t pin) -{ - struct a10_gpio_softc *sc = a10_gpio_sc; - - if (sc == NULL) - return (ENXIO); - - /* Configure pin mux settings for MII. */ - A10_GPIO_LOCK(sc); - a10_gpio_set_function(sc, pin, A10_GPIO_PULLDOWN); - A10_GPIO_UNLOCK(sc); - - return (0); -} +EARLY_DRIVER_MODULE(a10_gpio, simplebus, a10_gpio_driver, a10_gpio_devclass, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); Modified: trunk/sys/arm/allwinner/a10_gpio.h =================================================================== --- trunk/sys/arm/allwinner/a10_gpio.h 2020-03-08 17:21:06 UTC (rev 12401) +++ trunk/sys/arm/allwinner/a10_gpio.h 2020-03-08 17:23:22 UTC (rev 12402) @@ -24,12 +24,15 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/allwinner/a10_gpio.h 266337 2014-05-17 18:53:36Z ian $ + * $FreeBSD: stable/11/sys/arm/allwinner/a10_gpio.h 285105 2015-07-03 17:54:41Z loos $ */ #ifndef _A10_GPIO_H_ #define _A10_GPIO_H_ -int a10_emac_gpio_config(uint32_t pin); +#define A10_GPIO_FUNC_MII 2 +#define A10_GPIO_FUNC_RGMII 5 +int a10_gpio_ethernet_activate(uint32_t); + #endif Added: trunk/sys/arm/allwinner/a10_hdmi.c =================================================================== --- trunk/sys/arm/allwinner/a10_hdmi.c (rev 0) +++ trunk/sys/arm/allwinner/a10_hdmi.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,719 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/a10_hdmi.c 308324 2016-11-05 04:17:32Z mmel $ + */ + +/* + * Allwinner A10/A20 HDMI TX + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10_hdmi.c 308324 2016-11-05 04:17:32Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +#include "hdmi_if.h" + +#define HDMI_CTRL 0x004 +#define CTRL_MODULE_EN (1 << 31) +#define HDMI_INT_STATUS 0x008 +#define HDMI_HPD 0x00c +#define HPD_DET (1 << 0) +#define HDMI_VID_CTRL 0x010 +#define VID_CTRL_VIDEO_EN (1 << 31) +#define VID_CTRL_HDMI_MODE (1 << 30) +#define VID_CTRL_INTERLACE (1 << 4) +#define VID_CTRL_REPEATER_2X (1 << 0) +#define HDMI_VID_TIMING0 0x014 +#define VID_ACT_V(v) (((v) - 1) << 16) +#define VID_ACT_H(h) (((h) - 1) << 0) +#define HDMI_VID_TIMING1 0x018 +#define VID_VBP(vbp) (((vbp) - 1) << 16) +#define VID_HBP(hbp) (((hbp) - 1) << 0) +#define HDMI_VID_TIMING2 0x01c +#define VID_VFP(vfp) (((vfp) - 1) << 16) +#define VID_HFP(hfp) (((hfp) - 1) << 0) +#define HDMI_VID_TIMING3 0x020 +#define VID_VSPW(vspw) (((vspw) - 1) << 16) +#define VID_HSPW(hspw) (((hspw) - 1) << 0) +#define HDMI_VID_TIMING4 0x024 +#define TX_CLOCK_NORMAL 0x03e00000 +#define VID_VSYNC_ACTSEL (1 << 1) +#define VID_HSYNC_ACTSEL (1 << 0) +#define HDMI_AUD_CTRL 0x040 +#define AUD_CTRL_EN (1 << 31) +#define AUD_CTRL_RST (1 << 30) +#define HDMI_ADMA_CTRL 0x044 +#define HDMI_ADMA_MODE (1 << 31) +#define HDMI_ADMA_MODE_DDMA (0 << 31) +#define HDMI_ADMA_MODE_NDMA (1 << 31) +#define HDMI_AUD_FMT 0x048 +#define AUD_FMT_CH(n) ((n) - 1) +#define HDMI_PCM_CTRL 0x04c +#define HDMI_AUD_CTS 0x050 +#define HDMI_AUD_N 0x054 +#define HDMI_AUD_CH_STATUS0 0x058 +#define CH_STATUS0_FS_FREQ (0xf << 24) +#define CH_STATUS0_FS_FREQ_48 (2 << 24) +#define HDMI_AUD_CH_STATUS1 0x05c +#define CH_STATUS1_WORD_LEN (0x7 << 1) +#define CH_STATUS1_WORD_LEN_16 (1 << 1) +#define HDMI_AUDIO_RESET_RETRY 1000 +#define HDMI_AUDIO_CHANNELS 2 +#define HDMI_AUDIO_CHANNELMAP 0x76543210 +#define HDMI_AUDIO_N 6144 /* 48 kHz */ +#define HDMI_AUDIO_CTS(r, n) ((((r) * 10) * ((n) / 128)) / 480) +#define HDMI_PADCTRL0 0x200 +#define PADCTRL0_BIASEN (1 << 31) +#define PADCTRL0_LDOCEN (1 << 30) +#define PADCTRL0_LDODEN (1 << 29) +#define PADCTRL0_PWENC (1 << 28) +#define PADCTRL0_PWEND (1 << 27) +#define PADCTRL0_PWENG (1 << 26) +#define PADCTRL0_CKEN (1 << 25) +#define PADCTRL0_SEN (1 << 24) +#define PADCTRL0_TXEN (1 << 23) +#define HDMI_PADCTRL1 0x204 +#define PADCTRL1_AMP_OPT (1 << 23) +#define PADCTRL1_AMPCK_OPT (1 << 22) +#define PADCTRL1_DMP_OPT (1 << 21) +#define PADCTRL1_EMP_OPT (1 << 20) +#define PADCTRL1_EMPCK_OPT (1 << 19) +#define PADCTRL1_PWSCK (1 << 18) +#define PADCTRL1_PWSDT (1 << 17) +#define PADCTRL1_REG_CSMPS (1 << 16) +#define PADCTRL1_REG_DEN (1 << 15) +#define PADCTRL1_REG_DENCK (1 << 14) +#define PADCTRL1_REG_PLRCK (1 << 13) +#define PADCTRL1_REG_EMP (0x7 << 10) +#define PADCTRL1_REG_EMP_EN (0x2 << 10) +#define PADCTRL1_REG_CD (0x3 << 8) +#define PADCTRL1_REG_CKSS (0x3 << 6) +#define PADCTRL1_REG_CKSS_1X (0x1 << 6) +#define PADCTRL1_REG_CKSS_2X (0x0 << 6) +#define PADCTRL1_REG_AMP (0x7 << 3) +#define PADCTRL1_REG_AMP_EN (0x6 << 3) +#define PADCTRL1_REG_PLR (0x7 << 0) +#define HDMI_PLLCTRL0 0x208 +#define PLLCTRL0_PLL_EN (1 << 31) +#define PLLCTRL0_BWS (1 << 30) +#define PLLCTRL0_HV_IS_33 (1 << 29) +#define PLLCTRL0_LDO1_EN (1 << 28) +#define PLLCTRL0_LDO2_EN (1 << 27) +#define PLLCTRL0_SDIV2 (1 << 25) +#define PLLCTRL0_VCO_GAIN (0x1 << 22) +#define PLLCTRL0_S (0x7 << 17) +#define PLLCTRL0_CP_S (0xf << 12) +#define PLLCTRL0_CS (0x7 << 8) +#define PLLCTRL0_PREDIV(x) ((x) << 4) +#define PLLCTRL0_VCO_S (0x8 << 0) +#define HDMI_PLLDBG0 0x20c +#define PLLDBG0_CKIN_SEL (1 << 21) +#define PLLDBG0_CKIN_SEL_PLL3 (0 << 21) +#define PLLDBG0_CKIN_SEL_PLL7 (1 << 21) +#define HDMI_PKTCTRL0 0x2f0 +#define HDMI_PKTCTRL1 0x2f4 +#define PKTCTRL_PACKET(n,t) ((t) << ((n) << 2)) +#define PKT_NULL 0 +#define PKT_GC 1 +#define PKT_AVI 2 +#define PKT_AI 3 +#define PKT_SPD 5 +#define PKT_END 15 +#define DDC_CTRL 0x500 +#define CTRL_DDC_EN (1 << 31) +#define CTRL_DDC_ACMD_START (1 << 30) +#define CTRL_DDC_FIFO_DIR (1 << 8) +#define CTRL_DDC_FIFO_DIR_READ (0 << 8) +#define CTRL_DDC_FIFO_DIR_WRITE (1 << 8) +#define CTRL_DDC_SWRST (1 << 0) +#define DDC_SLAVE_ADDR 0x504 +#define SLAVE_ADDR_SEG_SHIFT 24 +#define SLAVE_ADDR_EDDC_SHIFT 16 +#define SLAVE_ADDR_OFFSET_SHIFT 8 +#define SLAVE_ADDR_SHIFT 0 +#define DDC_INT_STATUS 0x50c +#define INT_STATUS_XFER_DONE (1 << 0) +#define DDC_FIFO_CTRL 0x510 +#define FIFO_CTRL_CLEAR (1 << 31) +#define DDC_BYTE_COUNTER 0x51c +#define DDC_COMMAND 0x520 +#define COMMAND_EOREAD (4 << 0) +#define DDC_CLOCK 0x528 +#define DDC_CLOCK_M (1 << 3) +#define DDC_CLOCK_N (5 << 0) +#define DDC_FIFO 0x518 +#define SWRST_DELAY 1000 +#define DDC_DELAY 1000 +#define DDC_RETRY 1000 +#define DDC_BLKLEN 16 +#define DDC_ADDR 0x50 +#define EDDC_ADDR 0x60 +#define EDID_LENGTH 128 +#define HDMI_ENABLE_DELAY 50000 +#define DDC_READ_RETRY 4 +#define EXT_TAG 0x00 +#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 +#define HDMI_DEFAULT_FREQ 297000000 + +struct a10hdmi_softc { + struct resource *res; + + struct intr_config_hook mode_hook; + + uint8_t edid[EDID_LENGTH]; + + int has_hdmi; + int has_audio; + + clk_t clk_ahb; + clk_t clk_hdmi; + clk_t clk_lcd; +}; + +static struct resource_spec a10hdmi_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define HDMI_READ(sc, reg) bus_read_4((sc)->res, (reg)) +#define HDMI_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) + +static void +a10hdmi_init(struct a10hdmi_softc *sc) +{ + /* Enable the HDMI module */ + HDMI_WRITE(sc, HDMI_CTRL, CTRL_MODULE_EN); + + /* Configure PLL/DRV settings */ + HDMI_WRITE(sc, HDMI_PADCTRL0, PADCTRL0_BIASEN | PADCTRL0_LDOCEN | + PADCTRL0_LDODEN | PADCTRL0_PWENC | PADCTRL0_PWEND | + PADCTRL0_PWENG | PADCTRL0_CKEN | PADCTRL0_TXEN); + HDMI_WRITE(sc, HDMI_PADCTRL1, PADCTRL1_AMP_OPT | PADCTRL1_AMPCK_OPT | + PADCTRL1_EMP_OPT | PADCTRL1_EMPCK_OPT | PADCTRL1_REG_DEN | + PADCTRL1_REG_DENCK | PADCTRL1_REG_EMP_EN | PADCTRL1_REG_AMP_EN); + + /* Select PLL3 as input clock */ + HDMI_WRITE(sc, HDMI_PLLDBG0, PLLDBG0_CKIN_SEL_PLL3); + + DELAY(HDMI_ENABLE_DELAY); +} + +static void +a10hdmi_hpd(void *arg) +{ + struct a10hdmi_softc *sc; + device_t dev; + uint32_t hpd; + + dev = arg; + sc = device_get_softc(dev); + + hpd = HDMI_READ(sc, HDMI_HPD); + if ((hpd & HPD_DET) == HPD_DET) + EVENTHANDLER_INVOKE(hdmi_event, dev, HDMI_EVENT_CONNECTED); + + config_intrhook_disestablish(&sc->mode_hook); +} + +static int +a10hdmi_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-hdmi")) + return (ENXIO); + + device_set_desc(dev, "Allwinner HDMI TX"); + return (BUS_PROBE_DEFAULT); +} + +static int +a10hdmi_attach(device_t dev) +{ + struct a10hdmi_softc *sc; + int error; + + sc = device_get_softc(dev); + + if (bus_alloc_resources(dev, a10hdmi_spec, &sc->res)) { + device_printf(dev, "cannot allocate resources for device\n"); + return (ENXIO); + } + + /* Setup clocks */ + error = clk_get_by_ofw_name(dev, 0, "ahb", &sc->clk_ahb); + if (error != 0) { + device_printf(dev, "cannot find ahb clock\n"); + return (error); + } + error = clk_get_by_ofw_name(dev, 0, "hdmi", &sc->clk_hdmi); + if (error != 0) { + device_printf(dev, "cannot find hdmi clock\n"); + return (error); + } + error = clk_get_by_ofw_name(dev, 0, "lcd", &sc->clk_lcd); + if (error != 0) { + device_printf(dev, "cannot find lcd clock\n"); + } + /* Enable HDMI clock */ + error = clk_enable(sc->clk_hdmi); + if (error != 0) { + device_printf(dev, "cannot enable hdmi clock\n"); + return (error); + } + /* Gating AHB clock for HDMI */ + error = clk_enable(sc->clk_ahb); + if (error != 0) { + device_printf(dev, "cannot enable ahb gate\n"); + return (error); + } + + a10hdmi_init(sc); + + sc->mode_hook.ich_func = a10hdmi_hpd; + sc->mode_hook.ich_arg = dev; + + error = config_intrhook_establish(&sc->mode_hook); + if (error != 0) + return (error); + + return (0); +} + +static int +a10hdmi_ddc_xfer(struct a10hdmi_softc *sc, uint16_t addr, uint8_t seg, + uint8_t off, int len) +{ + uint32_t val; + int retry; + + /* Set FIFO direction to read */ + val = HDMI_READ(sc, DDC_CTRL); + val &= ~CTRL_DDC_FIFO_DIR; + val |= CTRL_DDC_FIFO_DIR_READ; + HDMI_WRITE(sc, DDC_CTRL, val); + + /* Setup DDC slave address */ + val = (addr << SLAVE_ADDR_SHIFT) | (seg << SLAVE_ADDR_SEG_SHIFT) | + (EDDC_ADDR << SLAVE_ADDR_EDDC_SHIFT) | + (off << SLAVE_ADDR_OFFSET_SHIFT); + HDMI_WRITE(sc, DDC_SLAVE_ADDR, val); + + /* Clear FIFO */ + val = HDMI_READ(sc, DDC_FIFO_CTRL); + val |= FIFO_CTRL_CLEAR; + HDMI_WRITE(sc, DDC_FIFO_CTRL, val); + + /* Set transfer length */ + HDMI_WRITE(sc, DDC_BYTE_COUNTER, len); + + /* Set command to "Explicit Offset Address Read" */ + HDMI_WRITE(sc, DDC_COMMAND, COMMAND_EOREAD); + + /* Start transfer */ + val = HDMI_READ(sc, DDC_CTRL); + val |= CTRL_DDC_ACMD_START; + HDMI_WRITE(sc, DDC_CTRL, val); + + /* Wait for command to start */ + retry = DDC_RETRY; + while (--retry > 0) { + val = HDMI_READ(sc, DDC_CTRL); + if ((val & CTRL_DDC_ACMD_START) == 0) + break; + DELAY(DDC_DELAY); + } + if (retry == 0) + return (ETIMEDOUT); + + /* Ensure that the transfer completed */ + val = HDMI_READ(sc, DDC_INT_STATUS); + if ((val & INT_STATUS_XFER_DONE) == 0) + return (EIO); + + return (0); +} + +static int +a10hdmi_ddc_read(struct a10hdmi_softc *sc, int block, uint8_t *edid) +{ + int resid, off, len, error; + uint8_t *pbuf; + + pbuf = edid; + resid = EDID_LENGTH; + off = (block & 1) ? EDID_LENGTH : 0; + + while (resid > 0) { + len = min(resid, DDC_BLKLEN); + error = a10hdmi_ddc_xfer(sc, DDC_ADDR, block >> 1, off, len); + if (error != 0) + return (error); + + bus_read_multi_1(sc->res, DDC_FIFO, pbuf, len); + + pbuf += len; + off += len; + resid -= len; + } + + 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) +{ + struct edid_info ei; + uint8_t edid[EDID_LENGTH]; + int block; + + *phdmi = *paudio = 0; + + if (edid_parse(sc->edid, &ei) != 0) + return; + + /* Scan through extension blocks, looking for a CEA-861 block. */ + for (block = 1; block <= ei.edid_ext_block_count; block++) { + if (a10hdmi_ddc_read(sc, block, edid) != 0) + return; + + if (a10hdmi_detect_hdmi_vsdb(edid) != 0) { + *phdmi = 1; + *paudio = ((edid[CEA_DTD] & DTD_BASIC_AUDIO) != 0); + return; + } + } +} + +static int +a10hdmi_get_edid(device_t dev, uint8_t **edid, uint32_t *edid_len) +{ + struct a10hdmi_softc *sc; + int error, retry; + + sc = device_get_softc(dev); + retry = DDC_READ_RETRY; + + while (--retry > 0) { + /* I2C software reset */ + HDMI_WRITE(sc, DDC_FIFO_CTRL, 0); + HDMI_WRITE(sc, DDC_CTRL, CTRL_DDC_EN | CTRL_DDC_SWRST); + DELAY(SWRST_DELAY); + if (HDMI_READ(sc, DDC_CTRL) & CTRL_DDC_SWRST) { + device_printf(dev, "DDC software reset failed\n"); + return (ENXIO); + } + + /* Configure DDC clock */ + HDMI_WRITE(sc, DDC_CLOCK, DDC_CLOCK_M | DDC_CLOCK_N); + + /* Read EDID block */ + error = a10hdmi_ddc_read(sc, 0, sc->edid); + if (error == 0) { + *edid = sc->edid; + *edid_len = sizeof(sc->edid); + break; + } + } + + if (error == 0) + a10hdmi_detect_hdmi(sc, &sc->has_hdmi, &sc->has_audio); + else + sc->has_hdmi = sc->has_audio = 0; + + return (error); +} + +static void +a10hdmi_set_audiomode(device_t dev, const struct videomode *mode) +{ + struct a10hdmi_softc *sc; + uint32_t val; + int retry; + + sc = device_get_softc(dev); + + /* Disable and reset audio module and wait for reset bit to clear */ + HDMI_WRITE(sc, HDMI_AUD_CTRL, AUD_CTRL_RST); + for (retry = HDMI_AUDIO_RESET_RETRY; retry > 0; retry--) { + val = HDMI_READ(sc, HDMI_AUD_CTRL); + if ((val & AUD_CTRL_RST) == 0) + break; + } + if (retry == 0) { + device_printf(dev, "timeout waiting for audio module\n"); + return; + } + + if (!sc->has_audio) + return; + + /* DMA and FIFO control */ + HDMI_WRITE(sc, HDMI_ADMA_CTRL, HDMI_ADMA_MODE_DDMA); + + /* Audio format control (LPCM, S16LE, stereo) */ + HDMI_WRITE(sc, HDMI_AUD_FMT, AUD_FMT_CH(HDMI_AUDIO_CHANNELS)); + + /* Channel mappings */ + HDMI_WRITE(sc, HDMI_PCM_CTRL, HDMI_AUDIO_CHANNELMAP); + + /* Clocks */ + HDMI_WRITE(sc, HDMI_AUD_CTS, + HDMI_AUDIO_CTS(mode->dot_clock, HDMI_AUDIO_N)); + HDMI_WRITE(sc, HDMI_AUD_N, HDMI_AUDIO_N); + + /* Set sampling frequency to 48 kHz, word length to 16-bit */ + HDMI_WRITE(sc, HDMI_AUD_CH_STATUS0, CH_STATUS0_FS_FREQ_48); + HDMI_WRITE(sc, HDMI_AUD_CH_STATUS1, CH_STATUS1_WORD_LEN_16); + + /* Enable */ + HDMI_WRITE(sc, HDMI_AUD_CTRL, AUD_CTRL_EN); +} + +static int +a10hdmi_get_tcon_config(struct a10hdmi_softc *sc, int *div, int *dbl) +{ + uint64_t lcd_fin, lcd_fout; + clk_t clk_lcd_parent; + const char *pname; + int error; + + error = clk_get_parent(sc->clk_lcd, &clk_lcd_parent); + if (error != 0) + return (error); + + /* Get the LCD CH1 special clock 2 divider */ + error = clk_get_freq(sc->clk_lcd, &lcd_fout); + if (error != 0) + return (error); + error = clk_get_freq(clk_lcd_parent, &lcd_fin); + if (error != 0) + return (error); + *div = lcd_fin / lcd_fout; + + /* Detect LCD CH1 special clock using a 1X or 2X source */ + /* XXX */ + pname = clk_get_name(clk_lcd_parent); + if (strcmp(pname, "pll3-1x") == 0 || strcmp(pname, "pll7-1x") == 0) + *dbl = 0; + else + *dbl = 1; + + return (0); +} + +static int +a10hdmi_set_videomode(device_t dev, const struct videomode *mode) +{ + struct a10hdmi_softc *sc; + int error, clk_div, clk_dbl; + int dblscan, hfp, hspw, hbp, vfp, vspw, vbp; + uint32_t val; + + sc = device_get_softc(dev); + dblscan = !!(mode->flags & VID_DBLSCAN); + hfp = mode->hsync_start - mode->hdisplay; + hspw = mode->hsync_end - mode->hsync_start; + hbp = mode->htotal - mode->hsync_start; + vfp = mode->vsync_start - mode->vdisplay; + vspw = mode->vsync_end - mode->vsync_start; + vbp = mode->vtotal - mode->vsync_start; + + error = a10hdmi_get_tcon_config(sc, &clk_div, &clk_dbl); + if (error != 0) { + device_printf(dev, "couldn't get tcon config: %d\n", error); + return (error); + } + + /* Clear interrupt status */ + HDMI_WRITE(sc, HDMI_INT_STATUS, HDMI_READ(sc, HDMI_INT_STATUS)); + + /* Clock setup */ + val = HDMI_READ(sc, HDMI_PADCTRL1); + val &= ~PADCTRL1_REG_CKSS; + val |= (clk_dbl ? PADCTRL1_REG_CKSS_2X : PADCTRL1_REG_CKSS_1X); + HDMI_WRITE(sc, HDMI_PADCTRL1, val); + HDMI_WRITE(sc, HDMI_PLLCTRL0, PLLCTRL0_PLL_EN | PLLCTRL0_BWS | + PLLCTRL0_HV_IS_33 | PLLCTRL0_LDO1_EN | PLLCTRL0_LDO2_EN | + PLLCTRL0_SDIV2 | PLLCTRL0_VCO_GAIN | PLLCTRL0_S | + PLLCTRL0_CP_S | PLLCTRL0_CS | PLLCTRL0_PREDIV(clk_div) | + 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; + if (mode->flags & VID_INTERLACE) + val |= VID_CTRL_INTERLACE; + if (mode->flags & VID_DBLSCAN) + val |= VID_CTRL_REPEATER_2X; + HDMI_WRITE(sc, HDMI_VID_CTRL, val); + + /* Setup display timings */ + HDMI_WRITE(sc, HDMI_VID_TIMING0, + VID_ACT_V(mode->vdisplay) | VID_ACT_H(mode->hdisplay << dblscan)); + HDMI_WRITE(sc, HDMI_VID_TIMING1, + VID_VBP(vbp) | VID_HBP(hbp << dblscan)); + HDMI_WRITE(sc, HDMI_VID_TIMING2, + VID_VFP(vfp) | VID_HFP(hfp << dblscan)); + HDMI_WRITE(sc, HDMI_VID_TIMING3, + VID_VSPW(vspw) | VID_HSPW(hspw << dblscan)); + val = TX_CLOCK_NORMAL; + if (mode->flags & VID_PVSYNC) + val |= VID_VSYNC_ACTSEL; + if (mode->flags & VID_PHSYNC) + val |= VID_HSYNC_ACTSEL; + HDMI_WRITE(sc, HDMI_VID_TIMING4, val); + + /* This is an ordered list of infoframe packets that the HDMI + * transmitter will send. Transmit packets in the following order: + * 1. General control packet + * 2. AVI infoframe + * 3. Audio infoframe + * There are 2 registers with 4 slots each. The list is terminated + * with the special PKT_END marker. + */ + HDMI_WRITE(sc, HDMI_PKTCTRL0, + PKTCTRL_PACKET(0, PKT_GC) | PKTCTRL_PACKET(1, PKT_AVI) | + PKTCTRL_PACKET(2, PKT_AI) | PKTCTRL_PACKET(3, PKT_END)); + HDMI_WRITE(sc, HDMI_PKTCTRL1, 0); + + /* Setup audio */ + a10hdmi_set_audiomode(dev, mode); + + return (0); +} + +static int +a10hdmi_enable(device_t dev, int onoff) +{ + struct a10hdmi_softc *sc; + uint32_t val; + + sc = device_get_softc(dev); + + /* Enable or disable video output */ + val = HDMI_READ(sc, HDMI_VID_CTRL); + if (onoff) + val |= VID_CTRL_VIDEO_EN; + else + val &= ~VID_CTRL_VIDEO_EN; + HDMI_WRITE(sc, HDMI_VID_CTRL, val); + + return (0); +} + +static device_method_t a10hdmi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, a10hdmi_probe), + DEVMETHOD(device_attach, a10hdmi_attach), + + /* HDMI interface */ + DEVMETHOD(hdmi_get_edid, a10hdmi_get_edid), + DEVMETHOD(hdmi_set_videomode, a10hdmi_set_videomode), + DEVMETHOD(hdmi_enable, a10hdmi_enable), + + DEVMETHOD_END +}; + +static driver_t a10hdmi_driver = { + "a10hdmi", + a10hdmi_methods, + sizeof(struct a10hdmi_softc), +}; + +static devclass_t a10hdmi_devclass; + +DRIVER_MODULE(a10hdmi, simplebus, a10hdmi_driver, a10hdmi_devclass, 0, 0); +MODULE_VERSION(a10hdmi, 1); Property changes on: trunk/sys/arm/allwinner/a10_hdmi.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/a10_hdmiaudio.c =================================================================== --- trunk/sys/arm/allwinner/a10_hdmiaudio.c (rev 0) +++ trunk/sys/arm/allwinner/a10_hdmiaudio.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,439 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/a10_hdmiaudio.c 296064 2016-02-25 20:17:18Z jmcneill $ + */ + +/* + * Allwinner A10/A20 HDMI Audio + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10_hdmiaudio.c 296064 2016-02-25 20:17:18Z jmcneill $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "sunxi_dma_if.h" +#include "mixer_if.h" + +#define DRQTYPE_HDMIAUDIO 24 +#define DRQTYPE_SDRAM 1 + +#define DMA_WIDTH 32 +#define DMA_BURST_LEN 8 +#define DDMA_BLKSIZE 32 +#define DDMA_WAIT_CYC 8 + +#define DMABUF_MIN 4096 +#define DMABUF_DEFAULT 65536 +#define DMABUF_MAX 131072 + +#define HDMI_SAMPLERATE 48000 + +#define TX_FIFO 0x01c16400 + +static uint32_t a10hdmiaudio_fmt[] = { + SND_FORMAT(AFMT_S16_LE, 2, 0), + 0 +}; + +static struct pcmchan_caps a10hdmiaudio_pcaps = { + HDMI_SAMPLERATE, HDMI_SAMPLERATE, a10hdmiaudio_fmt, 0 +}; + +struct a10hdmiaudio_info; + +struct a10hdmiaudio_chinfo { + struct snd_dbuf *buffer; + struct pcm_channel *channel; + struct a10hdmiaudio_info *parent; + bus_dmamap_t dmamap; + void *dmaaddr; + bus_addr_t physaddr; + device_t dmac; + void *dmachan; + + int run; + uint32_t pos; + uint32_t blocksize; +}; + +struct a10hdmiaudio_info { + device_t dev; + struct mtx *lock; + bus_dma_tag_t dmat; + unsigned dmasize; + + struct a10hdmiaudio_chinfo play; +}; + +/* + * Mixer interface + */ + +static int +a10hdmiaudio_mixer_init(struct snd_mixer *m) +{ + mix_setdevs(m, SOUND_MASK_PCM); + + return (0); +} + +static int +a10hdmiaudio_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, + unsigned right) +{ + return (-1); +} + +static kobj_method_t a10hdmiaudio_mixer_methods[] = { + KOBJMETHOD(mixer_init, a10hdmiaudio_mixer_init), + KOBJMETHOD(mixer_set, a10hdmiaudio_mixer_set), + KOBJMETHOD_END +}; +MIXER_DECLARE(a10hdmiaudio_mixer); + + +/* + * Channel interface + */ + +static void +a10hdmiaudio_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct a10hdmiaudio_chinfo *ch = arg; + + if (error != 0) + return; + + ch->physaddr = segs[0].ds_addr; +} + +static void +a10hdmiaudio_transfer(struct a10hdmiaudio_chinfo *ch) +{ + int error; + + error = SUNXI_DMA_TRANSFER(ch->dmac, ch->dmachan, + ch->physaddr + ch->pos, TX_FIFO, ch->blocksize); + if (error) { + ch->run = 0; + device_printf(ch->parent->dev, "DMA transfer failed: %d\n", + error); + } +} + +static void +a10hdmiaudio_dmaconfig(struct a10hdmiaudio_chinfo *ch) +{ + struct sunxi_dma_config conf; + + memset(&conf, 0, sizeof(conf)); + conf.src_width = conf.dst_width = DMA_WIDTH; + conf.src_burst_len = conf.dst_burst_len = DMA_BURST_LEN; + conf.src_blksize = conf.dst_blksize = DDMA_BLKSIZE; + conf.src_wait_cyc = conf.dst_wait_cyc = DDMA_WAIT_CYC; + conf.src_drqtype = DRQTYPE_SDRAM; + conf.dst_drqtype = DRQTYPE_HDMIAUDIO; + conf.dst_noincr = true; + + SUNXI_DMA_SET_CONFIG(ch->dmac, ch->dmachan, &conf); +} + +static void +a10hdmiaudio_dmaintr(void *priv) +{ + struct a10hdmiaudio_chinfo *ch = priv; + unsigned bufsize; + + bufsize = sndbuf_getsize(ch->buffer); + + ch->pos += ch->blocksize; + if (ch->pos >= bufsize) + ch->pos -= bufsize; + + if (ch->run) { + chn_intr(ch->channel); + a10hdmiaudio_transfer(ch); + } +} + +static void +a10hdmiaudio_start(struct a10hdmiaudio_chinfo *ch) +{ + ch->pos = 0; + + /* Configure DMA channel */ + a10hdmiaudio_dmaconfig(ch); + + /* Start DMA transfer */ + a10hdmiaudio_transfer(ch); +} + +static void +a10hdmiaudio_stop(struct a10hdmiaudio_chinfo *ch) +{ + /* Disable DMA channel */ + SUNXI_DMA_HALT(ch->dmac, ch->dmachan); +} + +static void * +a10hdmiaudio_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, + struct pcm_channel *c, int dir) +{ + struct a10hdmiaudio_info *sc = devinfo; + struct a10hdmiaudio_chinfo *ch = &sc->play; + int error; + + ch->parent = sc; + ch->channel = c; + ch->buffer = b; + + ch->dmac = devclass_get_device(devclass_find("a10dmac"), 0); + if (ch->dmac == NULL) { + device_printf(sc->dev, "cannot find DMA controller\n"); + return (NULL); + } + ch->dmachan = SUNXI_DMA_ALLOC(ch->dmac, true, a10hdmiaudio_dmaintr, ch); + if (ch->dmachan == NULL) { + device_printf(sc->dev, "cannot allocate DMA channel\n"); + return (NULL); + } + + error = bus_dmamem_alloc(sc->dmat, &ch->dmaaddr, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &ch->dmamap); + if (error != 0) { + device_printf(sc->dev, "cannot allocate channel buffer\n"); + return (NULL); + } + error = bus_dmamap_load(sc->dmat, ch->dmamap, ch->dmaaddr, + sc->dmasize, a10hdmiaudio_dmamap_cb, ch, BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc->dev, "cannot load DMA map\n"); + return (NULL); + } + memset(ch->dmaaddr, 0, sc->dmasize); + + if (sndbuf_setup(ch->buffer, ch->dmaaddr, sc->dmasize) != 0) { + device_printf(sc->dev, "cannot setup sndbuf\n"); + return (NULL); + } + + return (ch); +} + +static int +a10hdmiaudio_chan_free(kobj_t obj, void *data) +{ + struct a10hdmiaudio_chinfo *ch = data; + struct a10hdmiaudio_info *sc = ch->parent; + + SUNXI_DMA_FREE(ch->dmac, ch->dmachan); + bus_dmamap_unload(sc->dmat, ch->dmamap); + bus_dmamem_free(sc->dmat, ch->dmaaddr, ch->dmamap); + + return (0); +} + +static int +a10hdmiaudio_chan_setformat(kobj_t obj, void *data, uint32_t format) +{ + return (0); +} + +static uint32_t +a10hdmiaudio_chan_setspeed(kobj_t obj, void *data, uint32_t speed) +{ + return (HDMI_SAMPLERATE); +} + +static uint32_t +a10hdmiaudio_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) +{ + struct a10hdmiaudio_chinfo *ch = data; + + ch->blocksize = blocksize & ~3; + + return (ch->blocksize); +} + +static int +a10hdmiaudio_chan_trigger(kobj_t obj, void *data, int go) +{ + struct a10hdmiaudio_chinfo *ch = data; + struct a10hdmiaudio_info *sc = ch->parent; + + if (!PCMTRIG_COMMON(go)) + return (0); + + snd_mtxlock(sc->lock); + switch (go) { + case PCMTRIG_START: + ch->run = 1; + a10hdmiaudio_start(ch); + break; + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + ch->run = 0; + a10hdmiaudio_stop(ch); + break; + default: + break; + } + snd_mtxunlock(sc->lock); + + return (0); +} + +static uint32_t +a10hdmiaudio_chan_getptr(kobj_t obj, void *data) +{ + struct a10hdmiaudio_chinfo *ch = data; + + return (ch->pos); +} + +static struct pcmchan_caps * +a10hdmiaudio_chan_getcaps(kobj_t obj, void *data) +{ + return (&a10hdmiaudio_pcaps); +} + +static kobj_method_t a10hdmiaudio_chan_methods[] = { + KOBJMETHOD(channel_init, a10hdmiaudio_chan_init), + KOBJMETHOD(channel_free, a10hdmiaudio_chan_free), + KOBJMETHOD(channel_setformat, a10hdmiaudio_chan_setformat), + KOBJMETHOD(channel_setspeed, a10hdmiaudio_chan_setspeed), + KOBJMETHOD(channel_setblocksize, a10hdmiaudio_chan_setblocksize), + KOBJMETHOD(channel_trigger, a10hdmiaudio_chan_trigger), + KOBJMETHOD(channel_getptr, a10hdmiaudio_chan_getptr), + KOBJMETHOD(channel_getcaps, a10hdmiaudio_chan_getcaps), + KOBJMETHOD_END +}; +CHANNEL_DECLARE(a10hdmiaudio_chan); + + +/* + * Device interface + */ + +static int +a10hdmiaudio_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-hdmiaudio")) + return (ENXIO); + + device_set_desc(dev, "Allwinner HDMI Audio"); + return (BUS_PROBE_DEFAULT); +} + +static int +a10hdmiaudio_attach(device_t dev) +{ + struct a10hdmiaudio_info *sc; + char status[SND_STATUSLEN]; + int error; + + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); + sc->dev = dev; + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "a10hdmiaudio softc"); + + sc->dmasize = pcm_getbuffersize(dev, DMABUF_MIN, + DMABUF_DEFAULT, DMABUF_MAX); + error = bus_dma_tag_create( + bus_get_dma_tag(dev), + 4, sc->dmasize, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + sc->dmasize, 1, /* maxsize, nsegs */ + sc->dmasize, 0, /* maxsegsize, flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->dmat); + if (error != 0) { + device_printf(dev, "cannot create DMA tag\n"); + goto fail; + } + + if (mixer_init(dev, &a10hdmiaudio_mixer_class, sc)) { + device_printf(dev, "mixer_init failed\n"); + goto fail; + } + + pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); + pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL); + + if (pcm_register(dev, sc, 1, 0)) { + device_printf(dev, "pcm_register failed\n"); + goto fail; + } + + pcm_addchan(dev, PCMDIR_PLAY, &a10hdmiaudio_chan_class, sc); + + snprintf(status, SND_STATUSLEN, "at %s", ofw_bus_get_name(dev)); + pcm_setstatus(dev, status); + + return (0); + +fail: + snd_mtxfree(sc->lock); + free(sc, M_DEVBUF); + + return (error); +} + +static device_method_t a10hdmiaudio_pcm_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, a10hdmiaudio_probe), + DEVMETHOD(device_attach, a10hdmiaudio_attach), + + DEVMETHOD_END +}; + +static driver_t a10hdmiaudio_pcm_driver = { + "pcm", + a10hdmiaudio_pcm_methods, + PCM_SOFTC_SIZE, +}; + +DRIVER_MODULE(a10hdmiaudio, simplebus, a10hdmiaudio_pcm_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(a10hdmiaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(a10hdmiaudio, 1); Property changes on: trunk/sys/arm/allwinner/a10_hdmiaudio.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/a10_mmc.c =================================================================== --- trunk/sys/arm/allwinner/a10_mmc.c (rev 0) +++ trunk/sys/arm/allwinner/a10_mmc.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,921 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2013 Alexander Fedorov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10_mmc.c 318197 2017-05-11 20:55:11Z marius $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#define A10_MMC_MEMRES 0 +#define A10_MMC_IRQRES 1 +#define A10_MMC_RESSZ 2 +#define A10_MMC_DMA_SEGS ((MAXPHYS / PAGE_SIZE) + 1) +#define A10_MMC_DMA_MAX_SIZE 0x2000 +#define A10_MMC_DMA_FTRGLEVEL 0x20070008 +#define A10_MMC_RESET_RETRY 1000 + +#define CARD_ID_FREQUENCY 400000 + +static struct ofw_compat_data compat_data[] = { + {"allwinner,sun4i-a10-mmc", 1}, + {"allwinner,sun5i-a13-mmc", 1}, + {NULL, 0} +}; + +struct a10_mmc_softc { + device_t a10_dev; + clk_t a10_clk_ahb; + clk_t a10_clk_mmc; + hwreset_t a10_rst_ahb; + int a10_bus_busy; + int a10_resid; + int a10_timeout; + struct callout a10_timeoutc; + struct mmc_host a10_host; + struct mmc_request * a10_req; + struct mtx a10_mtx; + struct resource * a10_res[A10_MMC_RESSZ]; + uint32_t a10_intr; + uint32_t a10_intr_wait; + void * a10_intrhand; + + /* Fields required for DMA access. */ + bus_addr_t a10_dma_desc_phys; + bus_dmamap_t a10_dma_map; + bus_dma_tag_t a10_dma_tag; + void * a10_dma_desc; + bus_dmamap_t a10_dma_buf_map; + bus_dma_tag_t a10_dma_buf_tag; + int a10_dma_map_err; +}; + +static struct resource_spec a10_mmc_res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, + { -1, 0, 0 } +}; + +static int a10_mmc_probe(device_t); +static int a10_mmc_attach(device_t); +static int a10_mmc_detach(device_t); +static int a10_mmc_setup_dma(struct a10_mmc_softc *); +static int a10_mmc_reset(struct a10_mmc_softc *); +static void a10_mmc_intr(void *); +static int a10_mmc_update_clock(struct a10_mmc_softc *, uint32_t); + +static int a10_mmc_update_ios(device_t, device_t); +static int a10_mmc_request(device_t, device_t, struct mmc_request *); +static int a10_mmc_get_ro(device_t, device_t); +static int a10_mmc_acquire_host(device_t, device_t); +static int a10_mmc_release_host(device_t, device_t); + +#define A10_MMC_LOCK(_sc) mtx_lock(&(_sc)->a10_mtx) +#define A10_MMC_UNLOCK(_sc) mtx_unlock(&(_sc)->a10_mtx) +#define A10_MMC_READ_4(_sc, _reg) \ + bus_read_4((_sc)->a10_res[A10_MMC_MEMRES], _reg) +#define A10_MMC_WRITE_4(_sc, _reg, _value) \ + bus_write_4((_sc)->a10_res[A10_MMC_MEMRES], _reg, _value) + +static int +a10_mmc_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, "Allwinner Integrated MMC/SD controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +a10_mmc_attach(device_t dev) +{ + device_t child; + struct a10_mmc_softc *sc; + struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *tree; + uint32_t bus_width; + phandle_t node; + int error; + + node = ofw_bus_get_node(dev); + sc = device_get_softc(dev); + sc->a10_dev = dev; + sc->a10_req = NULL; + if (bus_alloc_resources(dev, a10_mmc_res_spec, sc->a10_res) != 0) { + device_printf(dev, "cannot allocate device resources\n"); + return (ENXIO); + } + if (bus_setup_intr(dev, sc->a10_res[A10_MMC_IRQRES], + INTR_TYPE_MISC | INTR_MPSAFE, NULL, a10_mmc_intr, sc, + &sc->a10_intrhand)) { + bus_release_resources(dev, a10_mmc_res_spec, sc->a10_res); + device_printf(dev, "cannot setup interrupt handler\n"); + return (ENXIO); + } + mtx_init(&sc->a10_mtx, device_get_nameunit(sc->a10_dev), "a10_mmc", + MTX_DEF); + callout_init_mtx(&sc->a10_timeoutc, &sc->a10_mtx, 0); + + /* De-assert reset */ + if (hwreset_get_by_ofw_name(dev, 0, "ahb", &sc->a10_rst_ahb) == 0) { + error = hwreset_deassert(sc->a10_rst_ahb); + if (error != 0) { + device_printf(dev, "cannot de-assert reset\n"); + goto fail; + } + } + + /* Activate the module clock. */ + error = clk_get_by_ofw_name(dev, 0, "ahb", &sc->a10_clk_ahb); + if (error != 0) { + device_printf(dev, "cannot get ahb clock\n"); + goto fail; + } + error = clk_enable(sc->a10_clk_ahb); + if (error != 0) { + device_printf(dev, "cannot enable ahb clock\n"); + goto fail; + } + error = clk_get_by_ofw_name(dev, 0, "mmc", &sc->a10_clk_mmc); + if (error != 0) { + device_printf(dev, "cannot get mmc clock\n"); + goto fail; + } + error = clk_set_freq(sc->a10_clk_mmc, CARD_ID_FREQUENCY, + CLK_SET_ROUND_DOWN); + if (error != 0) { + device_printf(dev, "cannot init mmc clock\n"); + goto fail; + } + error = clk_enable(sc->a10_clk_mmc); + if (error != 0) { + device_printf(dev, "cannot enable mmc clock\n"); + goto fail; + } + + sc->a10_timeout = 10; + ctx = device_get_sysctl_ctx(dev); + tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); + SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "req_timeout", CTLFLAG_RW, + &sc->a10_timeout, 0, "Request timeout in seconds"); + + /* Hardware reset */ + A10_MMC_WRITE_4(sc, A10_MMC_HWRST, 1); + DELAY(100); + A10_MMC_WRITE_4(sc, A10_MMC_HWRST, 0); + DELAY(500); + + /* Soft Reset controller. */ + if (a10_mmc_reset(sc) != 0) { + device_printf(dev, "cannot reset the controller\n"); + goto fail; + } + + if (a10_mmc_setup_dma(sc) != 0) { + device_printf(sc->a10_dev, "Couldn't setup DMA!\n"); + goto fail; + } + + if (OF_getencprop(node, "bus-width", &bus_width, sizeof(uint32_t)) <= 0) + bus_width = 4; + + sc->a10_host.f_min = 400000; + sc->a10_host.f_max = 52000000; + sc->a10_host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340; + sc->a10_host.mode = mode_sd; + sc->a10_host.caps = MMC_CAP_HSPEED; + if (bus_width >= 4) + sc->a10_host.caps |= MMC_CAP_4_BIT_DATA; + if (bus_width >= 8) + sc->a10_host.caps |= MMC_CAP_8_BIT_DATA; + + child = device_add_child(dev, "mmc", -1); + if (child == NULL) { + device_printf(dev, "attaching MMC bus failed!\n"); + goto fail; + } + if (device_probe_and_attach(child) != 0) { + device_printf(dev, "attaching MMC child failed!\n"); + device_delete_child(dev, child); + goto fail; + } + + return (0); + +fail: + callout_drain(&sc->a10_timeoutc); + mtx_destroy(&sc->a10_mtx); + bus_teardown_intr(dev, sc->a10_res[A10_MMC_IRQRES], sc->a10_intrhand); + bus_release_resources(dev, a10_mmc_res_spec, sc->a10_res); + + return (ENXIO); +} + +static int +a10_mmc_detach(device_t dev) +{ + + return (EBUSY); +} + +static void +a10_dma_desc_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int err) +{ + struct a10_mmc_softc *sc; + + sc = (struct a10_mmc_softc *)arg; + if (err) { + sc->a10_dma_map_err = err; + return; + } + sc->a10_dma_desc_phys = segs[0].ds_addr; +} + +static int +a10_mmc_setup_dma(struct a10_mmc_softc *sc) +{ + int dma_desc_size, error; + + /* Allocate the DMA descriptor memory. */ + dma_desc_size = sizeof(struct a10_mmc_dma_desc) * A10_MMC_DMA_SEGS; + error = bus_dma_tag_create(bus_get_dma_tag(sc->a10_dev), + A10_MMC_DMA_ALIGN, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + dma_desc_size, 1, dma_desc_size, 0, NULL, NULL, &sc->a10_dma_tag); + if (error) + return (error); + error = bus_dmamem_alloc(sc->a10_dma_tag, &sc->a10_dma_desc, + BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->a10_dma_map); + if (error) + return (error); + + error = bus_dmamap_load(sc->a10_dma_tag, sc->a10_dma_map, + sc->a10_dma_desc, dma_desc_size, a10_dma_desc_cb, sc, 0); + if (error) + return (error); + if (sc->a10_dma_map_err) + return (sc->a10_dma_map_err); + + /* Create the DMA map for data transfers. */ + error = bus_dma_tag_create(bus_get_dma_tag(sc->a10_dev), + A10_MMC_DMA_ALIGN, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + A10_MMC_DMA_MAX_SIZE * A10_MMC_DMA_SEGS, A10_MMC_DMA_SEGS, + A10_MMC_DMA_MAX_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL, + &sc->a10_dma_buf_tag); + if (error) + return (error); + error = bus_dmamap_create(sc->a10_dma_buf_tag, 0, + &sc->a10_dma_buf_map); + if (error) + return (error); + + return (0); +} + +static void +a10_dma_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int err) +{ + int i; + struct a10_mmc_dma_desc *dma_desc; + struct a10_mmc_softc *sc; + + sc = (struct a10_mmc_softc *)arg; + sc->a10_dma_map_err = err; + + if (err) + return; + + dma_desc = sc->a10_dma_desc; + for (i = 0; i < nsegs; i++) { + dma_desc[i].buf_size = segs[i].ds_len; + dma_desc[i].buf_addr = segs[i].ds_addr; + dma_desc[i].config = A10_MMC_DMA_CONFIG_CH | + A10_MMC_DMA_CONFIG_OWN; + if (i == 0) + dma_desc[i].config |= A10_MMC_DMA_CONFIG_FD; + if (i < (nsegs - 1)) { + dma_desc[i].config |= A10_MMC_DMA_CONFIG_DIC; + dma_desc[i].next = sc->a10_dma_desc_phys + + ((i + 1) * sizeof(struct a10_mmc_dma_desc)); + } else { + dma_desc[i].config |= A10_MMC_DMA_CONFIG_LD | + A10_MMC_DMA_CONFIG_ER; + dma_desc[i].next = 0; + } + } +} + +static int +a10_mmc_prepare_dma(struct a10_mmc_softc *sc) +{ + bus_dmasync_op_t sync_op; + int error; + struct mmc_command *cmd; + uint32_t val; + + cmd = sc->a10_req->cmd; + if (cmd->data->len > A10_MMC_DMA_MAX_SIZE * A10_MMC_DMA_SEGS) + return (EFBIG); + error = bus_dmamap_load(sc->a10_dma_buf_tag, sc->a10_dma_buf_map, + cmd->data->data, cmd->data->len, a10_dma_cb, sc, 0); + if (error) + return (error); + if (sc->a10_dma_map_err) + return (sc->a10_dma_map_err); + + if (cmd->data->flags & MMC_DATA_WRITE) + sync_op = BUS_DMASYNC_PREWRITE; + else + sync_op = BUS_DMASYNC_PREREAD; + bus_dmamap_sync(sc->a10_dma_buf_tag, sc->a10_dma_buf_map, sync_op); + bus_dmamap_sync(sc->a10_dma_tag, sc->a10_dma_map, BUS_DMASYNC_PREWRITE); + + /* Enable DMA */ + val = A10_MMC_READ_4(sc, A10_MMC_GCTL); + val &= ~A10_MMC_CTRL_FIFO_AC_MOD; + val |= A10_MMC_CTRL_DMA_ENB; + A10_MMC_WRITE_4(sc, A10_MMC_GCTL, val); + + /* Reset DMA */ + val |= A10_MMC_CTRL_DMA_RST; + A10_MMC_WRITE_4(sc, A10_MMC_GCTL, val); + + A10_MMC_WRITE_4(sc, A10_MMC_DMAC, A10_MMC_DMAC_IDMAC_SOFT_RST); + A10_MMC_WRITE_4(sc, A10_MMC_DMAC, + A10_MMC_DMAC_IDMAC_IDMA_ON | A10_MMC_DMAC_IDMAC_FIX_BURST); + + /* Enable RX or TX DMA interrupt */ + if (cmd->data->flags & MMC_DATA_WRITE) + val |= A10_MMC_IDST_TX_INT; + else + val |= A10_MMC_IDST_RX_INT; + A10_MMC_WRITE_4(sc, A10_MMC_IDIE, val); + + /* Set DMA descritptor list address */ + A10_MMC_WRITE_4(sc, A10_MMC_DLBA, sc->a10_dma_desc_phys); + + /* FIFO trigger level */ + A10_MMC_WRITE_4(sc, A10_MMC_FWLR, A10_MMC_DMA_FTRGLEVEL); + + return (0); +} + +static int +a10_mmc_reset(struct a10_mmc_softc *sc) +{ + int timeout; + + A10_MMC_WRITE_4(sc, A10_MMC_GCTL, A10_MMC_RESET); + timeout = 1000; + while (--timeout > 0) { + if ((A10_MMC_READ_4(sc, A10_MMC_GCTL) & A10_MMC_RESET) == 0) + break; + DELAY(100); + } + if (timeout == 0) + return (ETIMEDOUT); + + /* Set the timeout. */ + A10_MMC_WRITE_4(sc, A10_MMC_TMOR, + A10_MMC_TMOR_DTO_LMT_SHIFT(A10_MMC_TMOR_DTO_LMT_MASK) | + A10_MMC_TMOR_RTO_LMT_SHIFT(A10_MMC_TMOR_RTO_LMT_MASK)); + + /* Clear pending interrupts. */ + A10_MMC_WRITE_4(sc, A10_MMC_RISR, 0xffffffff); + A10_MMC_WRITE_4(sc, A10_MMC_IDST, 0xffffffff); + /* Unmask interrupts. */ + A10_MMC_WRITE_4(sc, A10_MMC_IMKR, + A10_MMC_INT_CMD_DONE | A10_MMC_INT_ERR_BIT | + A10_MMC_INT_DATA_OVER | A10_MMC_INT_AUTO_STOP_DONE); + /* Enable interrupts and AHB access. */ + A10_MMC_WRITE_4(sc, A10_MMC_GCTL, + A10_MMC_READ_4(sc, A10_MMC_GCTL) | A10_MMC_CTRL_INT_ENB); + + return (0); +} + +static void +a10_mmc_req_done(struct a10_mmc_softc *sc) +{ + struct mmc_command *cmd; + struct mmc_request *req; + uint32_t val, mask; + int retry; + + cmd = sc->a10_req->cmd; + if (cmd->error != MMC_ERR_NONE) { + /* Reset the FIFO and DMA engines. */ + mask = A10_MMC_CTRL_FIFO_RST | A10_MMC_CTRL_DMA_RST; + val = A10_MMC_READ_4(sc, A10_MMC_GCTL); + A10_MMC_WRITE_4(sc, A10_MMC_GCTL, val | mask); + + retry = A10_MMC_RESET_RETRY; + while (--retry > 0) { + val = A10_MMC_READ_4(sc, A10_MMC_GCTL); + if ((val & mask) == 0) + break; + DELAY(10); + } + if (retry == 0) + device_printf(sc->a10_dev, + "timeout resetting DMA/FIFO\n"); + a10_mmc_update_clock(sc, 1); + } + + req = sc->a10_req; + callout_stop(&sc->a10_timeoutc); + sc->a10_req = NULL; + sc->a10_intr = 0; + sc->a10_resid = 0; + sc->a10_dma_map_err = 0; + sc->a10_intr_wait = 0; + req->done(req); +} + +static void +a10_mmc_req_ok(struct a10_mmc_softc *sc) +{ + int timeout; + struct mmc_command *cmd; + uint32_t status; + + timeout = 1000; + while (--timeout > 0) { + status = A10_MMC_READ_4(sc, A10_MMC_STAR); + if ((status & A10_MMC_STAR_CARD_BUSY) == 0) + break; + DELAY(1000); + } + cmd = sc->a10_req->cmd; + if (timeout == 0) { + cmd->error = MMC_ERR_FAILED; + a10_mmc_req_done(sc); + return; + } + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) { + cmd->resp[0] = A10_MMC_READ_4(sc, A10_MMC_RESP3); + cmd->resp[1] = A10_MMC_READ_4(sc, A10_MMC_RESP2); + cmd->resp[2] = A10_MMC_READ_4(sc, A10_MMC_RESP1); + cmd->resp[3] = A10_MMC_READ_4(sc, A10_MMC_RESP0); + } else + cmd->resp[0] = A10_MMC_READ_4(sc, A10_MMC_RESP0); + } + /* All data has been transferred ? */ + if (cmd->data != NULL && (sc->a10_resid << 2) < cmd->data->len) + cmd->error = MMC_ERR_FAILED; + a10_mmc_req_done(sc); +} + +static void +a10_mmc_timeout(void *arg) +{ + struct a10_mmc_softc *sc; + + sc = (struct a10_mmc_softc *)arg; + if (sc->a10_req != NULL) { + device_printf(sc->a10_dev, "controller timeout\n"); + sc->a10_req->cmd->error = MMC_ERR_TIMEOUT; + a10_mmc_req_done(sc); + } else + device_printf(sc->a10_dev, + "Spurious timeout - no active request\n"); +} + +static void +a10_mmc_intr(void *arg) +{ + bus_dmasync_op_t sync_op; + struct a10_mmc_softc *sc; + struct mmc_data *data; + uint32_t idst, imask, rint; + + sc = (struct a10_mmc_softc *)arg; + A10_MMC_LOCK(sc); + rint = A10_MMC_READ_4(sc, A10_MMC_RISR); + idst = A10_MMC_READ_4(sc, A10_MMC_IDST); + imask = A10_MMC_READ_4(sc, A10_MMC_IMKR); + if (idst == 0 && imask == 0 && rint == 0) { + A10_MMC_UNLOCK(sc); + return; + } +#ifdef DEBUG + device_printf(sc->a10_dev, "idst: %#x, imask: %#x, rint: %#x\n", + idst, imask, rint); +#endif + if (sc->a10_req == NULL) { + device_printf(sc->a10_dev, + "Spurious interrupt - no active request, rint: 0x%08X\n", + rint); + goto end; + } + if (rint & A10_MMC_INT_ERR_BIT) { + device_printf(sc->a10_dev, "error rint: 0x%08X\n", rint); + if (rint & A10_MMC_INT_RESP_TIMEOUT) + sc->a10_req->cmd->error = MMC_ERR_TIMEOUT; + else + sc->a10_req->cmd->error = MMC_ERR_FAILED; + a10_mmc_req_done(sc); + goto end; + } + if (idst & A10_MMC_IDST_ERROR) { + device_printf(sc->a10_dev, "error idst: 0x%08x\n", idst); + sc->a10_req->cmd->error = MMC_ERR_FAILED; + a10_mmc_req_done(sc); + goto end; + } + + sc->a10_intr |= rint; + data = sc->a10_req->cmd->data; + if (data != NULL && (idst & A10_MMC_IDST_COMPLETE) != 0) { + if (data->flags & MMC_DATA_WRITE) + sync_op = BUS_DMASYNC_POSTWRITE; + else + sync_op = BUS_DMASYNC_POSTREAD; + bus_dmamap_sync(sc->a10_dma_buf_tag, sc->a10_dma_buf_map, + sync_op); + bus_dmamap_sync(sc->a10_dma_tag, sc->a10_dma_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->a10_dma_buf_tag, sc->a10_dma_buf_map); + sc->a10_resid = data->len >> 2; + } + if ((sc->a10_intr & sc->a10_intr_wait) == sc->a10_intr_wait) + a10_mmc_req_ok(sc); + +end: + A10_MMC_WRITE_4(sc, A10_MMC_IDST, idst); + A10_MMC_WRITE_4(sc, A10_MMC_RISR, rint); + A10_MMC_UNLOCK(sc); +} + +static int +a10_mmc_request(device_t bus, device_t child, struct mmc_request *req) +{ + int blksz; + struct a10_mmc_softc *sc; + struct mmc_command *cmd; + uint32_t cmdreg; + int err; + + sc = device_get_softc(bus); + A10_MMC_LOCK(sc); + if (sc->a10_req) { + A10_MMC_UNLOCK(sc); + return (EBUSY); + } + sc->a10_req = req; + cmd = req->cmd; + cmdreg = A10_MMC_CMDR_LOAD; + if (cmd->opcode == MMC_GO_IDLE_STATE) + cmdreg |= A10_MMC_CMDR_SEND_INIT_SEQ; + if (cmd->flags & MMC_RSP_PRESENT) + cmdreg |= A10_MMC_CMDR_RESP_RCV; + if (cmd->flags & MMC_RSP_136) + cmdreg |= A10_MMC_CMDR_LONG_RESP; + if (cmd->flags & MMC_RSP_CRC) + cmdreg |= A10_MMC_CMDR_CHK_RESP_CRC; + + sc->a10_intr = 0; + sc->a10_resid = 0; + sc->a10_intr_wait = A10_MMC_INT_CMD_DONE; + cmd->error = MMC_ERR_NONE; + if (cmd->data != NULL) { + sc->a10_intr_wait |= A10_MMC_INT_DATA_OVER; + cmdreg |= A10_MMC_CMDR_DATA_TRANS | A10_MMC_CMDR_WAIT_PRE_OVER; + if (cmd->data->flags & MMC_DATA_MULTI) { + cmdreg |= A10_MMC_CMDR_STOP_CMD_FLAG; + sc->a10_intr_wait |= A10_MMC_INT_AUTO_STOP_DONE; + } + if (cmd->data->flags & MMC_DATA_WRITE) + cmdreg |= A10_MMC_CMDR_DIR_WRITE; + blksz = min(cmd->data->len, MMC_SECTOR_SIZE); + A10_MMC_WRITE_4(sc, A10_MMC_BKSR, blksz); + A10_MMC_WRITE_4(sc, A10_MMC_BYCR, cmd->data->len); + + err = a10_mmc_prepare_dma(sc); + if (err != 0) + device_printf(sc->a10_dev, "prepare_dma failed: %d\n", err); + } + + A10_MMC_WRITE_4(sc, A10_MMC_CAGR, cmd->arg); + A10_MMC_WRITE_4(sc, A10_MMC_CMDR, cmdreg | cmd->opcode); + callout_reset(&sc->a10_timeoutc, sc->a10_timeout * hz, + a10_mmc_timeout, sc); + A10_MMC_UNLOCK(sc); + + return (0); +} + +static int +a10_mmc_read_ivar(device_t bus, device_t child, int which, + uintptr_t *result) +{ + struct a10_mmc_softc *sc; + + sc = device_get_softc(bus); + switch (which) { + default: + return (EINVAL); + case MMCBR_IVAR_BUS_MODE: + *(int *)result = sc->a10_host.ios.bus_mode; + break; + case MMCBR_IVAR_BUS_WIDTH: + *(int *)result = sc->a10_host.ios.bus_width; + break; + case MMCBR_IVAR_CHIP_SELECT: + *(int *)result = sc->a10_host.ios.chip_select; + break; + case MMCBR_IVAR_CLOCK: + *(int *)result = sc->a10_host.ios.clock; + break; + case MMCBR_IVAR_F_MIN: + *(int *)result = sc->a10_host.f_min; + break; + case MMCBR_IVAR_F_MAX: + *(int *)result = sc->a10_host.f_max; + break; + case MMCBR_IVAR_HOST_OCR: + *(int *)result = sc->a10_host.host_ocr; + break; + case MMCBR_IVAR_MODE: + *(int *)result = sc->a10_host.mode; + break; + case MMCBR_IVAR_OCR: + *(int *)result = sc->a10_host.ocr; + break; + case MMCBR_IVAR_POWER_MODE: + *(int *)result = sc->a10_host.ios.power_mode; + break; + case MMCBR_IVAR_VDD: + *(int *)result = sc->a10_host.ios.vdd; + break; + case MMCBR_IVAR_CAPS: + *(int *)result = sc->a10_host.caps; + break; + case MMCBR_IVAR_MAX_DATA: + *(int *)result = 65535; + break; + } + + return (0); +} + +static int +a10_mmc_write_ivar(device_t bus, device_t child, int which, + uintptr_t value) +{ + struct a10_mmc_softc *sc; + + sc = device_get_softc(bus); + switch (which) { + default: + return (EINVAL); + case MMCBR_IVAR_BUS_MODE: + sc->a10_host.ios.bus_mode = value; + break; + case MMCBR_IVAR_BUS_WIDTH: + sc->a10_host.ios.bus_width = value; + break; + case MMCBR_IVAR_CHIP_SELECT: + sc->a10_host.ios.chip_select = value; + break; + case MMCBR_IVAR_CLOCK: + sc->a10_host.ios.clock = value; + break; + case MMCBR_IVAR_MODE: + sc->a10_host.mode = value; + break; + case MMCBR_IVAR_OCR: + sc->a10_host.ocr = value; + break; + case MMCBR_IVAR_POWER_MODE: + sc->a10_host.ios.power_mode = value; + break; + case MMCBR_IVAR_VDD: + sc->a10_host.ios.vdd = value; + break; + /* These are read-only */ + case MMCBR_IVAR_CAPS: + case MMCBR_IVAR_HOST_OCR: + case MMCBR_IVAR_F_MIN: + case MMCBR_IVAR_F_MAX: + case MMCBR_IVAR_MAX_DATA: + return (EINVAL); + } + + return (0); +} + +static int +a10_mmc_update_clock(struct a10_mmc_softc *sc, uint32_t clkon) +{ + uint32_t cmdreg; + int retry; + uint32_t ckcr; + + ckcr = A10_MMC_READ_4(sc, A10_MMC_CKCR); + ckcr &= ~(A10_MMC_CKCR_CCLK_ENB | A10_MMC_CKCR_CCLK_CTRL); + + if (clkon) + ckcr |= A10_MMC_CKCR_CCLK_ENB; + + A10_MMC_WRITE_4(sc, A10_MMC_CKCR, ckcr); + + cmdreg = A10_MMC_CMDR_LOAD | A10_MMC_CMDR_PRG_CLK | + A10_MMC_CMDR_WAIT_PRE_OVER; + A10_MMC_WRITE_4(sc, A10_MMC_CMDR, cmdreg); + retry = 0xfffff; + while (--retry > 0) { + if ((A10_MMC_READ_4(sc, A10_MMC_CMDR) & A10_MMC_CMDR_LOAD) == 0) { + A10_MMC_WRITE_4(sc, A10_MMC_RISR, 0xffffffff); + return (0); + } + DELAY(10); + } + A10_MMC_WRITE_4(sc, A10_MMC_RISR, 0xffffffff); + device_printf(sc->a10_dev, "timeout updating clock\n"); + + return (ETIMEDOUT); +} + +static int +a10_mmc_update_ios(device_t bus, device_t child) +{ + int error; + struct a10_mmc_softc *sc; + struct mmc_ios *ios; + uint32_t ckcr; + + sc = device_get_softc(bus); + + ios = &sc->a10_host.ios; + + /* Set the bus width. */ + switch (ios->bus_width) { + case bus_width_1: + A10_MMC_WRITE_4(sc, A10_MMC_BWDR, A10_MMC_BWDR1); + break; + case bus_width_4: + A10_MMC_WRITE_4(sc, A10_MMC_BWDR, A10_MMC_BWDR4); + break; + case bus_width_8: + A10_MMC_WRITE_4(sc, A10_MMC_BWDR, A10_MMC_BWDR8); + break; + } + + if (ios->clock) { + + /* Disable clock */ + error = a10_mmc_update_clock(sc, 0); + if (error != 0) + return (error); + + /* Reset the divider. */ + ckcr = A10_MMC_READ_4(sc, A10_MMC_CKCR); + ckcr &= ~A10_MMC_CKCR_CCLK_DIV; + A10_MMC_WRITE_4(sc, A10_MMC_CKCR, ckcr); + + /* Set the MMC clock. */ + error = clk_set_freq(sc->a10_clk_mmc, ios->clock, + CLK_SET_ROUND_DOWN); + if (error != 0) { + device_printf(sc->a10_dev, + "failed to set frequency to %u Hz: %d\n", + ios->clock, error); + return (error); + } + + /* Enable clock. */ + error = a10_mmc_update_clock(sc, 1); + if (error != 0) + return (error); + } + + + return (0); +} + +static int +a10_mmc_get_ro(device_t bus, device_t child) +{ + + return (0); +} + +static int +a10_mmc_acquire_host(device_t bus, device_t child) +{ + struct a10_mmc_softc *sc; + int error; + + sc = device_get_softc(bus); + A10_MMC_LOCK(sc); + while (sc->a10_bus_busy) { + error = msleep(sc, &sc->a10_mtx, PCATCH, "mmchw", 0); + if (error != 0) { + A10_MMC_UNLOCK(sc); + return (error); + } + } + sc->a10_bus_busy++; + A10_MMC_UNLOCK(sc); + + return (0); +} + +static int +a10_mmc_release_host(device_t bus, device_t child) +{ + struct a10_mmc_softc *sc; + + sc = device_get_softc(bus); + A10_MMC_LOCK(sc); + sc->a10_bus_busy--; + wakeup(sc); + A10_MMC_UNLOCK(sc); + + return (0); +} + +static device_method_t a10_mmc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, a10_mmc_probe), + DEVMETHOD(device_attach, a10_mmc_attach), + DEVMETHOD(device_detach, a10_mmc_detach), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, a10_mmc_read_ivar), + DEVMETHOD(bus_write_ivar, a10_mmc_write_ivar), + + /* MMC bridge interface */ + DEVMETHOD(mmcbr_update_ios, a10_mmc_update_ios), + DEVMETHOD(mmcbr_request, a10_mmc_request), + DEVMETHOD(mmcbr_get_ro, a10_mmc_get_ro), + DEVMETHOD(mmcbr_acquire_host, a10_mmc_acquire_host), + DEVMETHOD(mmcbr_release_host, a10_mmc_release_host), + + DEVMETHOD_END +}; + +static devclass_t a10_mmc_devclass; + +static driver_t a10_mmc_driver = { + "a10_mmc", + a10_mmc_methods, + sizeof(struct a10_mmc_softc), +}; + +DRIVER_MODULE(a10_mmc, simplebus, a10_mmc_driver, a10_mmc_devclass, NULL, + NULL); +MMC_DECLARE_BRIDGE(a10_mmc); Property changes on: trunk/sys/arm/allwinner/a10_mmc.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/a10_mmc.h =================================================================== --- trunk/sys/arm/allwinner/a10_mmc.h (rev 0) +++ trunk/sys/arm/allwinner/a10_mmc.h 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,205 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2013 Alexander Fedorov + * 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: stable/11/sys/arm/allwinner/a10_mmc.h 308280 2016-11-04 01:56:29Z manu $ + */ + +#ifndef _A10_MMC_H_ +#define _A10_MMC_H_ + +#define A10_MMC_GCTL 0x00 /* Control Register */ +#define A10_MMC_CKCR 0x04 /* Clock Control Register */ +#define A10_MMC_TMOR 0x08 /* Timeout Register */ +#define A10_MMC_BWDR 0x0C /* Bus Width Register */ +#define A10_MMC_BKSR 0x10 /* Block Size Register */ +#define A10_MMC_BYCR 0x14 /* Byte Count Register */ +#define A10_MMC_CMDR 0x18 /* Command Register */ +#define A10_MMC_CAGR 0x1C /* Argument Register */ +#define A10_MMC_RESP0 0x20 /* Response Register 0 */ +#define A10_MMC_RESP1 0x24 /* Response Register 1 */ +#define A10_MMC_RESP2 0x28 /* Response Register 2 */ +#define A10_MMC_RESP3 0x2C /* Response Register 3 */ +#define A10_MMC_IMKR 0x30 /* Interrupt Mask Register */ +#define A10_MMC_MISR 0x34 /* Masked Interrupt Status Register */ +#define A10_MMC_RISR 0x38 /* Raw Interrupt Status Register */ +#define A10_MMC_STAR 0x3C /* Status Register */ +#define A10_MMC_FWLR 0x40 /* FIFO Threshold Watermark Register */ +#define A10_MMC_FUNS 0x44 /* Function Select Register */ +#define A10_MMC_HWRST 0x78 /* Hardware reset (not documented) */ +#define A10_MMC_DMAC 0x80 /* IDMAC Control Register */ +#define A10_MMC_DLBA 0x84 /* IDMAC Desc List Base Address Reg */ +#define A10_MMC_IDST 0x88 /* IDMAC Status Register */ +#define A10_MMC_IDIE 0x8C /* IDMAC Interrupt Enable Register */ +#define A10_MMC_FIFO 0x100 /* FIFO Access Address (A10/A20) */ +#define A31_MMC_FIFO 0x200 /* FIFO Access Address (A31) */ + +/* A10_MMC_GCTL */ +#define A10_MMC_CTRL_SOFT_RST (1U << 0) +#define A10_MMC_CTRL_FIFO_RST (1U << 1) +#define A10_MMC_CTRL_DMA_RST (1U << 2) +#define A10_MMC_CTRL_INT_ENB (1U << 4) +#define A10_MMC_CTRL_DMA_ENB (1U << 5) +#define A10_MMC_CTRL_CD_DBC_ENB (1U << 8) +#define A10_MMC_CTRL_DDR_MOD_SEL (1U << 10) +#define A10_MMC_CTRL_FIFO_AC_MOD (1U << 31) +#define A10_MMC_RESET \ + (A10_MMC_CTRL_SOFT_RST | A10_MMC_CTRL_FIFO_RST | A10_MMC_CTRL_DMA_RST) + +/* A10_MMC_CKCR */ +#define A10_MMC_CKCR_CCLK_ENB (1U << 16) +#define A10_MMC_CKCR_CCLK_CTRL (1U << 17) +#define A10_MMC_CKCR_CCLK_DIV 0xff + +/* A10_MMC_TMOR */ +#define A10_MMC_TMOR_RTO_LMT_SHIFT(x) x /* Response timeout limit */ +#define A10_MMC_TMOR_RTO_LMT_MASK 0xff +#define A10_MMC_TMOR_DTO_LMT_SHIFT(x) (x << 8) /* Data timeout limit */ +#define A10_MMC_TMOR_DTO_LMT_MASK 0xffffff + +/* A10_MMC_BWDR */ +#define A10_MMC_BWDR1 0 +#define A10_MMC_BWDR4 1 +#define A10_MMC_BWDR8 2 + +/* A10_MMC_CMDR */ +#define A10_MMC_CMDR_RESP_RCV (1U << 6) +#define A10_MMC_CMDR_LONG_RESP (1U << 7) +#define A10_MMC_CMDR_CHK_RESP_CRC (1U << 8) +#define A10_MMC_CMDR_DATA_TRANS (1U << 9) +#define A10_MMC_CMDR_DIR_WRITE (1U << 10) +#define A10_MMC_CMDR_TRANS_MODE_STREAM (1U << 11) +#define A10_MMC_CMDR_STOP_CMD_FLAG (1U << 12) +#define A10_MMC_CMDR_WAIT_PRE_OVER (1U << 13) +#define A10_MMC_CMDR_STOP_ABT_CMD (1U << 14) +#define A10_MMC_CMDR_SEND_INIT_SEQ (1U << 15) +#define A10_MMC_CMDR_PRG_CLK (1U << 21) +#define A10_MMC_CMDR_RD_CEDATA_DEV (1U << 22) +#define A10_MMC_CMDR_CCS_EXP (1U << 23) +#define A10_MMC_CMDR_BOOT_MOD_SHIFT 24 +#define A10_MMC_CMDR_BOOT_MOD_NORMAL 0 +#define A10_MMC_CMDR_BOOT_MOD_MANDATORY 1 +#define A10_MMC_CMDR_BOOT_MOD_ALT 2 +#define A10_MMC_CMDR_EXP_BOOT_ACK (1U << 26) +#define A10_MMC_CMDR_BOOT_ABT (1U << 27) +#define A10_MMC_CMDR_VOL_SW (1U << 28) +#define A10_MMC_CMDR_LOAD (1U << 31) + +/* A10_MMC_IMKR and A10_MMC_RISR */ +#define A10_MMC_INT_RESP_ERR (1U << 1) +#define A10_MMC_INT_CMD_DONE (1U << 2) +#define A10_MMC_INT_DATA_OVER (1U << 3) +#define A10_MMC_INT_TX_DATA_REQ (1U << 4) +#define A10_MMC_INT_RX_DATA_REQ (1U << 5) +#define A10_MMC_INT_RESP_CRC_ERR (1U << 6) +#define A10_MMC_INT_DATA_CRC_ERR (1U << 7) +#define A10_MMC_INT_RESP_TIMEOUT (1U << 8) +#define A10_MMC_INT_BOOT_ACK_RECV (1U << 8) +#define A10_MMC_INT_DATA_TIMEOUT (1U << 9) +#define A10_MMC_INT_BOOT_START (1U << 9) +#define A10_MMC_INT_DATA_STARVE (1U << 10) +#define A10_MMC_INT_VOL_CHG_DONE (1U << 10) +#define A10_MMC_INT_FIFO_RUN_ERR (1U << 11) +#define A10_MMC_INT_CMD_BUSY (1U << 12) +#define A10_MMC_INT_DATA_START_ERR (1U << 13) +#define A10_MMC_INT_AUTO_STOP_DONE (1U << 14) +#define A10_MMC_INT_DATA_END_BIT_ERR (1U << 15) +#define A10_MMC_INT_SDIO (1U << 16) +#define A10_MMC_INT_CARD_INSERT (1U << 30) +#define A10_MMC_INT_CARD_REMOVE (1U << 31) +#define A10_MMC_INT_ERR_BIT \ + (A10_MMC_INT_RESP_ERR | A10_MMC_INT_RESP_CRC_ERR | \ + A10_MMC_INT_DATA_CRC_ERR | A10_MMC_INT_RESP_TIMEOUT | \ + A10_MMC_INT_FIFO_RUN_ERR | A10_MMC_INT_CMD_BUSY | \ + A10_MMC_INT_DATA_START_ERR | A10_MMC_INT_DATA_END_BIT_ERR) + +/* A10_MMC_STAR */ +#define A10_MMC_STAR_FIFO_RX_LEVEL (1U << 0) +#define A10_MMC_STAR_FIFO_TX_LEVEL (1U << 1) +#define A10_MMC_STAR_FIFO_EMPTY (1U << 2) +#define A10_MMC_STAR_FIFO_FULL (1U << 3) +#define A10_MMC_STAR_CARD_PRESENT (1U << 8) +#define A10_MMC_STAR_CARD_BUSY (1U << 9) +#define A10_MMC_STAR_FSM_BUSY (1U << 10) +#define A10_MMC_STAR_DMA_REQ (1U << 31) + +/* A10_MMC_FUNS */ +#define A10_MMC_CE_ATA_ON (0xceaaU << 16) +#define A10_MMC_SEND_IRQ_RESP (1U << 0) +#define A10_MMC_SDIO_RD_WAIT (1U << 1) +#define A10_MMC_ABT_RD_DATA (1U << 2) +#define A10_MMC_SEND_CC_SD (1U << 8) +#define A10_MMC_SEND_AUTOSTOP_CC_SD (1U << 9) +#define A10_MMC_CE_ATA_DEV_INT_ENB (1U << 10) + +/* IDMA CONTROLLER BUS MOD BIT FIELD */ +#define A10_MMC_DMAC_IDMAC_SOFT_RST (1U << 0) +#define A10_MMC_DMAC_IDMAC_FIX_BURST (1U << 1) +#define A10_MMC_DMAC_IDMAC_IDMA_ON (1U << 7) +#define A10_MMC_DMAC_IDMAC_REFETCH_DES (1U << 31) + +/* A10_MMC_IDST */ +#define A10_MMC_IDST_TX_INT (1U << 0) +#define A10_MMC_IDST_RX_INT (1U << 1) +#define A10_MMC_IDST_FATAL_BERR_INT (1U << 2) +#define A10_MMC_IDST_DES_UNAVL_INT (1U << 4) +#define A10_MMC_IDST_ERR_FLAG_SUM (1U << 5) +#define A10_MMC_IDST_NOR_INT_SUM (1U << 8) +#define A10_MMC_IDST_ABN_INT_SUM (1U << 9) +#define A10_MMC_IDST_HOST_ABT_INTX (1U << 10) +#define A10_MMC_IDST_HOST_ABT_INRX (1U << 10) +#define A10_MMC_IDST_IDLE (0U << 13) +#define A10_MMC_IDST_SUSPEND (1U << 13) +#define A10_MMC_IDST_DESC_RD (2U << 13) +#define A10_MMC_IDST_DESC_CHECK (3U << 13) +#define A10_MMC_IDST_RD_REQ_WAIT (4U << 13) +#define A10_MMC_IDST_WR_REQ_WAIT (5U << 13) +#define A10_MMC_IDST_RD (6U << 13) +#define A10_MMC_IDST_WR (7U << 13) +#define A10_MMC_IDST_DESC_CLOSE (8U << 13) +#define A10_MMC_IDST_ERROR \ + (A10_MMC_IDST_FATAL_BERR_INT | A10_MMC_IDST_ERR_FLAG_SUM | \ + A10_MMC_IDST_DES_UNAVL_INT | A10_MMC_IDST_ABN_INT_SUM) +#define A10_MMC_IDST_COMPLETE \ + (A10_MMC_IDST_TX_INT | A10_MMC_IDST_RX_INT) + +/* The DMA descriptor table. */ +struct a10_mmc_dma_desc { + uint32_t config; +#define A10_MMC_DMA_CONFIG_DIC (1U << 1) /* Disable Interrupt Completion */ +#define A10_MMC_DMA_CONFIG_LD (1U << 2) /* Last DES */ +#define A10_MMC_DMA_CONFIG_FD (1U << 3) /* First DES */ +#define A10_MMC_DMA_CONFIG_CH (1U << 4) /* CHAIN MOD */ +#define A10_MMC_DMA_CONFIG_ER (1U << 5) /* End of Ring (undocumented register) */ +#define A10_MMC_DMA_CONFIG_CES (1U << 30) /* Card Error Summary */ +#define A10_MMC_DMA_CONFIG_OWN (1U << 31) /* DES Own Flag */ + uint32_t buf_size; + uint32_t buf_addr; + uint32_t next; +}; + +#define A10_MMC_DMA_ALIGN 4 + +#endif /* _A10_MMC_H_ */ Property changes on: trunk/sys/arm/allwinner/a10_mmc.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Modified: trunk/sys/arm/allwinner/a10_sramc.c =================================================================== --- trunk/sys/arm/allwinner/a10_sramc.c 2020-03-08 17:21:06 UTC (rev 12401) +++ trunk/sys/arm/allwinner/a10_sramc.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -24,11 +24,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/allwinner/a10_sramc.c 266337 2014-05-17 18:53:36Z ian $ + * $FreeBSD: stable/11/sys/arm/allwinner/a10_sramc.c 331722 2018-03-29 02:50:57Z eadler $ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/allwinner/a10_sramc.c 266337 2014-05-17 18:53:36Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10_sramc.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -44,7 +44,6 @@ #include #include #include -#include #include #include @@ -54,6 +53,7 @@ #include "a10_sramc.h" #define SRAM_CTL1_CFG 0x04 +#define CTL1_CFG_SRAMD_MAP_USB0 (1 << 0) struct a10_sramc_softc { struct resource *res; @@ -73,7 +73,7 @@ a10_sramc_probe(device_t dev) { - if (ofw_bus_is_compatible(dev, "allwinner,sun4i-sramc")) { + if (ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-sram-controller")) { device_set_desc(dev, "Allwinner sramc module"); return (BUS_PROBE_DEFAULT); } @@ -115,7 +115,8 @@ static devclass_t a10_sramc_devclass; -DRIVER_MODULE(a10_sramc, simplebus, a10_sramc_driver, a10_sramc_devclass, 0, 0); +EARLY_DRIVER_MODULE(a10_sramc, simplebus, a10_sramc_driver, a10_sramc_devclass, + 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_EARLY); int a10_map_to_emac(void) @@ -133,3 +134,20 @@ return (0); } + +int +a10_map_to_otg(void) +{ + struct a10_sramc_softc *sc = a10_sramc_sc; + uint32_t reg_value; + + if (sc == NULL) + return (ENXIO); + + /* Map SRAM to OTG */ + reg_value = sramc_read_4(sc, SRAM_CTL1_CFG); + reg_value |= CTL1_CFG_SRAMD_MAP_USB0; + sramc_write_4(sc, SRAM_CTL1_CFG, reg_value); + + return (0); +} Modified: trunk/sys/arm/allwinner/a10_sramc.h =================================================================== --- trunk/sys/arm/allwinner/a10_sramc.h 2020-03-08 17:21:06 UTC (rev 12401) +++ trunk/sys/arm/allwinner/a10_sramc.h 2020-03-08 17:23:22 UTC (rev 12402) @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/allwinner/a10_sramc.h 266337 2014-05-17 18:53:36Z ian $ + * $FreeBSD: stable/11/sys/arm/allwinner/a10_sramc.h 331722 2018-03-29 02:50:57Z eadler $ */ #ifndef _A10_SRAMC_H_ @@ -31,5 +31,6 @@ #define _A10_SRAMC_H_ int a10_map_to_emac(void); +int a10_map_to_otg(void); #endif Added: trunk/sys/arm/allwinner/a31/a31_padconf.c =================================================================== --- trunk/sys/arm/allwinner/a31/a31_padconf.c (rev 0) +++ trunk/sys/arm/allwinner/a31/a31_padconf.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,221 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a31/a31_padconf.c 298422 2016-04-21 16:49:04Z jmcneill $"); + +#include +#include +#include +#include + +#include + +#ifdef SOC_ALLWINNER_A31 + +const static struct allwinner_pins a31_pins[] = { + {"PA0", 0, 0, {"gpio_in", "gpio_out", "gmac", "lcd1", "uart1", NULL, NULL, NULL}}, + {"PA1", 0, 1, {"gpio_in", "gpio_out", "gmac", "lcd1", "uart1", NULL, NULL, NULL}}, + {"PA2", 0, 2, {"gpio_in", "gpio_out", "gmac", "lcd1", "uart1", NULL, NULL, NULL}}, + {"PA3", 0, 3, {"gpio_in", "gpio_out", "gmac", "lcd1", "uart1", NULL, NULL, NULL}}, + {"PA4", 0, 4, {"gpio_in", "gpio_out", "gmac", "lcd1", "uart1", NULL, NULL, NULL}}, + {"PA5", 0, 5, {"gpio_in", "gpio_out", "gmac", "lcd1", "uart1", NULL, NULL, NULL}}, + {"PA6", 0, 6, {"gpio_in", "gpio_out", "gmac", "lcd1", "uart1", NULL, NULL, NULL}}, + {"PA7", 0, 7, {"gpio_in", "gpio_out", "gmac", "lcd1", "uart1", NULL, NULL, NULL}}, + {"PA8", 0, 8, {"gpio_in", "gpio_out", "gmac", "lcd1", NULL, NULL, NULL, NULL}}, + {"PA9", 0, 9, {"gpio_in", "gpio_out", "gmac", "lcd1", NULL, NULL, NULL, NULL}}, + {"PA10", 0, 10, {"gpio_in", "gpio_out", "gmac", "lcd1", "mmc3", "mmc2", NULL, NULL}}, + {"PA11", 0, 11, {"gpio_in", "gpio_out", "gmac", "lcd1", "mmc3", "mmc2", NULL, NULL}}, + {"PA12", 0, 12, {"gpio_in", "gpio_out", "gmac", "lcd1", "mmc3", "mmc2", NULL, NULL}}, + {"PA13", 0, 13, {"gpio_in", "gpio_out", "gmac", "lcd1", "mmc3", "mmc2", NULL, NULL}}, + {"PA14", 0, 14, {"gpio_in", "gpio_out", "gmac", "lcd1", "mmc3", "mmc2", NULL, NULL}}, + {"PA15", 0, 15, {"gpio_in", "gpio_out", "gmac", "lcd1", "clk_out_a", NULL, NULL, NULL}}, + {"PA16", 0, 16, {"gpio_in", "gpio_out", "gmac", "lcd1", "dmic", NULL, NULL, NULL}}, + {"PA17", 0, 17, {"gpio_in", "gpio_out", "gmac", "lcd1", "dmic", NULL, NULL, NULL}}, + {"PA18", 0, 18, {"gpio_in", "gpio_out", "gmac", "lcd1", "clk_out_b", NULL, NULL, NULL}}, + {"PA19", 0, 19, {"gpio_in", "gpio_out", "gmac", "lcd1", "pwm3", NULL, NULL, NULL}}, + {"PA20", 0, 20, {"gpio_in", "gpio_out", "gmac", "lcd1", "pwm3", NULL, NULL, NULL}}, + {"PA21", 0, 21, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, NULL, NULL}}, + {"PA22", 0, 22, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, NULL, NULL}}, + {"PA23", 0, 23, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, NULL, NULL}}, + {"PA24", 0, 24, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, NULL, NULL}}, + {"PA25", 0, 25, {"gpio_in", "gpio_out", "gmac", "lcd1", "spi3", NULL, NULL, NULL}}, + {"PA26", 0, 26, {"gpio_in", "gpio_out", "gmac", "lcd1", "clk_out_c", NULL, NULL, NULL}}, + {"PA27", 0, 27, {"gpio_in", "gpio_out", "gmac", "lcd1", NULL, NULL, NULL, NULL}}, + + {"PB0", 1, 0, {"gpio_in", "gpio_out", "i2s0", "uart3", "csi", NULL, NULL, NULL}}, + {"PB1", 1, 1, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, NULL, NULL}}, + {"PB2", 1, 2, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, NULL, NULL}}, + {"PB3", 1, 3, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, NULL, NULL}}, + {"PB4", 1, 4, {"gpio_in", "gpio_out", "i2s0", "uart3", NULL, NULL, NULL, NULL}}, + {"PB5", 1, 5, {"gpio_in", "gpio_out", "i2s0", "uart3", "i2c3", NULL, NULL, NULL}}, + {"PB6", 1, 6, {"gpio_in", "gpio_out", "i2s0", "uart3", "i2c3", NULL, NULL, NULL}}, + {"PB7", 1, 7, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, NULL, NULL}}, + + {"PC0", 2, 0, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}}, + {"PC1", 2, 1, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}}, + {"PC2", 2, 2, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}}, + {"PC3", 2, 3, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC4", 2, 4, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC5", 2, 5, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC6", 2, 6, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC7", 2, 7, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC8", 2, 8, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC9", 2, 9, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC10", 2, 10, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC11", 2, 11, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC12", 2, 12, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC13", 2, 13, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC14", 2, 14, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC15", 2, 15, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC16", 2, 16, {"gpio_in", "gpio_out", "nand0", "nand1", NULL, NULL, NULL, NULL}}, + {"PC17", 2, 17, {"gpio_in", "gpio_out", "nand0", "nand1", NULL, NULL, NULL, NULL}}, + {"PC18", 2, 18, {"gpio_in", "gpio_out", "nand0", "nand1", NULL, NULL, NULL, NULL}}, + {"PC19", 2, 19, {"gpio_in", "gpio_out", "nand0", "nand1", NULL, NULL, NULL, NULL}}, + {"PC20", 2, 20, {"gpio_in", "gpio_out", "nand0", "nand1", NULL, NULL, NULL, NULL}}, + {"PC21", 2, 21, {"gpio_in", "gpio_out", "nand0", "nand1", NULL, NULL, NULL, NULL}}, + {"PC22", 2, 22, {"gpio_in", "gpio_out", "nand0", "nand1", NULL, NULL, NULL, NULL}}, + {"PC23", 2, 23, {"gpio_in", "gpio_out", "nand0", "nand1", NULL, NULL, NULL, NULL}}, + {"PC24", 2, 24, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC25", 2, 24, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC26", 2, 24, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC27", 2, 24, {"gpio_in", "gpio_out", NULL, "spi0",NULL, NULL, NULL, NULL}}, + + {"PD0", 3, 0, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD1", 3, 1, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD2", 3, 2, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD3", 3, 3, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD4", 3, 4, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD5", 3, 5, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD6", 3, 6, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD7", 3, 7, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD8", 3, 8, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD9", 3, 9, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD10", 3, 10, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD11", 3, 11, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD12", 3, 12, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD13", 3, 13, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD14", 3, 14, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD15", 3, 15, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD16", 3, 16, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD17", 3, 17, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD18", 3, 18, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD19", 3, 19, {"gpio_in", "gpio_out", "lcd0", "lvds1", NULL, NULL, NULL, NULL}}, + {"PD20", 3, 20, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD21", 3, 21, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD22", 3, 22, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD23", 3, 23, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD24", 3, 24, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD25", 3, 25, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD26", 3, 26, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD27", 3, 27, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + + {"PE0", 4, 0, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE1", 4, 1, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE2", 4, 2, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE3", 4, 3, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE4", 4, 4, {"gpio_in", "gpio_out", "csi", "uart5", NULL, NULL, NULL, NULL}}, + {"PE5", 4, 5, {"gpio_in", "gpio_out", "csi", "uart5", NULL, NULL, NULL, NULL}}, + {"PE6", 4, 6, {"gpio_in", "gpio_out", "csi", "uart5", NULL, NULL, NULL, NULL}}, + {"PE7", 4, 7, {"gpio_in", "gpio_out", "csi", "uart5", NULL, NULL, NULL, NULL}}, + {"PE8", 4, 8, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE9", 4, 9, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE10", 4, 10, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE11", 4, 11, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE12", 4, 12, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE13", 4, 13, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE14", 4, 14, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE15", 4, 15, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE16", 4, 16, {"gpio_in", "gpio_out", "csi", NULL, NULL, NULL, NULL, NULL}}, + + {"PF0", 5, 0, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + {"PF1", 5, 1, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + {"PF2", 5, 2, {"gpio_in", "gpio_out", "mmc0", NULL, "uart0", NULL, NULL, NULL}}, + {"PF3", 5, 3, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + {"PF4", 5, 4, {"gpio_in", "gpio_out", "mmc0", NULL, "uart0", NULL, NULL, NULL}}, + {"PF5", 5, 5, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + + {"PG0", 6, 0, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, NULL}}, + {"PG1", 6, 1, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, NULL}}, + {"PG2", 6, 2, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, NULL}}, + {"PG3", 6, 3, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, NULL}}, + {"PG4", 6, 4, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, NULL}}, + {"PG5", 6, 5, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, NULL}}, + {"PG6", 6, 6, {"gpio_in", "gpio_out", "uart2", NULL, NULL, NULL, NULL, NULL}}, + {"PG7", 6, 7, {"gpio_in", "gpio_out", "uart2", NULL, NULL, NULL, NULL, NULL}}, + {"PG8", 6, 8, {"gpio_in", "gpio_out", "uart2", NULL, NULL, NULL, NULL, NULL}}, + {"PG9", 6, 9, {"gpio_in", "gpio_out", "uart2", NULL, NULL, NULL, NULL, NULL}}, + {"PG10", 6, 10, {"gpio_in", "gpio_out", "i2c3", "usb", NULL, NULL, NULL, NULL}}, + {"PG11", 6, 11, {"gpio_in", "gpio_out", "i2c3", "usb", NULL, NULL, NULL, NULL}}, + {"PG12", 6, 11, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, NULL, NULL}}, + {"PG13", 6, 11, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, NULL, NULL}}, + {"PG14", 6, 11, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, NULL, NULL}}, + {"PG15", 6, 11, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, NULL, NULL}}, + {"PG16", 6, 11, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, NULL, NULL}}, + {"PG17", 6, 11, {"gpio_in", "gpio_out", "uart4", NULL, NULL, NULL, NULL, NULL}}, + {"PG18", 6, 11, {"gpio_in", "gpio_out", "uart4", NULL, NULL, NULL, NULL, NULL}}, + + {"PH0", 7, 0, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}}, + {"PH1", 7, 1, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}}, + {"PH2", 7, 2, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}}, + {"PH3", 7, 3, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}}, + {"PH4", 7, 4, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}}, + {"PH5", 7, 5, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}}, + {"PH6", 7, 6, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}}, + {"PH7", 7, 7, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}}, + {"PH8", 7, 8, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}}, + {"PH9", 7, 9, {"gpio_in", "gpio_out", "spi2", "jtag", "pwm1", NULL, NULL, NULL}}, + {"PH10", 7, 10, {"gpio_in", "gpio_out", "spi2", "jtag", "pwm1", NULL, NULL, NULL}}, + {"PH11", 7, 11, {"gpio_in", "gpio_out", "spi2", "jtag", "pwm2", NULL, NULL, NULL}}, + {"PH12", 7, 12, {"gpio_in", "gpio_out", "spi2", "jtag", "pwm2", NULL, NULL, NULL}}, + {"PH13", 7, 13, {"gpio_in", "gpio_out", "pwm0", NULL, NULL, NULL, NULL, NULL}}, + {"PH14", 7, 14, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}}, + {"PH15", 7, 15, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}}, + {"PH16", 7, 16, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}}, + {"PH17", 7, 17, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}}, + {"PH18", 7, 18, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}}, + {"PH19", 7, 19, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}}, + {"PH20", 7, 20, {"gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, NULL, NULL}}, + {"PH21", 7, 21, {"gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, NULL, NULL}}, + {"PH22", 7, 22, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PH23", 7, 23, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PH24", 7, 24, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PH25", 7, 25, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PH26", 7, 26, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PH27", 7, 27, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PH28", 7, 28, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PH29", 7, 29, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}}, + {"PH30", 7, 30, {"gpio_in", "gpio_out", "nand1", NULL, NULL, NULL, NULL, NULL}}, +}; + +const struct allwinner_padconf a31_padconf = { + .npins = nitems(a31_pins), + .pins = a31_pins, +}; + +#endif /* SOC_ALLWINNER_A31 */ Property changes on: trunk/sys/arm/allwinner/a31/a31_padconf.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/a31/a31_r_padconf.c =================================================================== --- trunk/sys/arm/allwinner/a31/a31_r_padconf.c (rev 0) +++ trunk/sys/arm/allwinner/a31/a31_r_padconf.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,67 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a31/a31_r_padconf.c 298513 2016-04-23 13:59:18Z jmcneill $"); + +#include +#include +#include +#include + +#include + +#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S) + +const static struct allwinner_pins a31_r_pins[] = { + {"PL0", 0, 0, {"gpio_in", "gpio_out", "s_twi", "s_p2wi", NULL, NULL, NULL, NULL}}, + {"PL1", 0, 1, {"gpio_in", "gpio_out", "s_twi", "s_p2wi", NULL, NULL, NULL, NULL}}, + {"PL2", 0, 2, {"gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, NULL, NULL}}, + {"PL3", 0, 3, {"gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, NULL, NULL}}, + {"PL4", 0, 4, {"gpio_in", "gpio_out", "s_ir", NULL, NULL, NULL, NULL, NULL}}, + {"PL5", 0, 5, {"gpio_in", "gpio_out", "pl_eint0", "s_jtag", NULL, NULL, NULL, NULL}}, + {"PL6", 0, 6, {"gpio_in", "gpio_out", "pl_eint1", "s_jtag", NULL, NULL, NULL, NULL}}, + {"PL7", 0, 7, {"gpio_in", "gpio_out", "pl_eint2", "s_jtag", NULL, NULL, NULL, NULL}}, + {"PL8", 0, 8, {"gpio_in", "gpio_out", "pl_eint3", "s_jtag", NULL, NULL, NULL, NULL}}, + + {"PM0", 1, 0, {"gpio_in", "gpio_out", "pm_eint0", NULL, NULL, NULL, NULL, NULL}}, + {"PM1", 1, 1, {"gpio_in", "gpio_out", "pm_eint1", NULL, NULL, NULL, NULL, NULL}}, + {"PM2", 1, 2, {"gpio_in", "gpio_out", "pm_eint2", "1wire", NULL, NULL, NULL, NULL}}, + {"PM3", 1, 3, {"gpio_in", "gpio_out", "pm_eint3", NULL, NULL, NULL, NULL, NULL}}, + {"PM4", 1, 4, {"gpio_in", "gpio_out", "pm_eint4", NULL, NULL, NULL, NULL, NULL}}, + {"PM5", 1, 5, {"gpio_in", "gpio_out", "pm_eint5", NULL, NULL, NULL, NULL, NULL}}, + {"PM6", 1, 6, {"gpio_in", "gpio_out", "pm_eint6", NULL, NULL, NULL, NULL, NULL}}, + {"PM7", 1, 7, {"gpio_in", "gpio_out", "pm_eint7", "rtc", NULL, NULL, NULL, NULL}}, +}; + +const struct allwinner_padconf a31_r_padconf = { + .npins = nitems(a31_r_pins), + .pins = a31_r_pins, +}; + +#endif Property changes on: trunk/sys/arm/allwinner/a31/a31_r_padconf.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/a31/a31s_padconf.c =================================================================== --- trunk/sys/arm/allwinner/a31/a31s_padconf.c (rev 0) +++ trunk/sys/arm/allwinner/a31/a31s_padconf.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,201 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a31/a31s_padconf.c 298422 2016-04-21 16:49:04Z jmcneill $"); + +#include +#include +#include +#include + +#include + +#ifdef SOC_ALLWINNER_A31S + +const static struct allwinner_pins a31s_pins[] = { + {"PA0", 0, 0, {"gpio_in", "gpio_out", "gmac", NULL, "uart1", NULL, NULL, NULL}}, + {"PA1", 0, 1, {"gpio_in", "gpio_out", "gmac", NULL, "uart1", NULL, NULL, NULL}}, + {"PA2", 0, 2, {"gpio_in", "gpio_out", "gmac", NULL, "uart1", NULL, NULL, NULL}}, + {"PA3", 0, 3, {"gpio_in", "gpio_out", "gmac", NULL, "uart1", NULL, NULL, NULL}}, + {"PA4", 0, 4, {"gpio_in", "gpio_out", "gmac", NULL, "uart1", NULL, NULL, NULL}}, + {"PA5", 0, 5, {"gpio_in", "gpio_out", "gmac", NULL, "uart1", NULL, NULL, NULL}}, + {"PA6", 0, 6, {"gpio_in", "gpio_out", "gmac", NULL, "uart1", NULL, NULL, NULL}}, + {"PA7", 0, 7, {"gpio_in", "gpio_out", "gmac", NULL, "uart1", NULL, NULL, NULL}}, + {"PA8", 0, 8, {"gpio_in", "gpio_out", "gmac", NULL, NULL, NULL, NULL, NULL}}, + {"PA9", 0, 9, {"gpio_in", "gpio_out", "gmac", NULL, NULL, NULL, NULL, NULL}}, + {"PA10", 0, 10, {"gpio_in", "gpio_out", "gmac", NULL, "mmc3", "mmc2", NULL, NULL}}, + {"PA11", 0, 11, {"gpio_in", "gpio_out", "gmac", NULL, "mmc3", "mmc2", NULL, NULL}}, + {"PA12", 0, 12, {"gpio_in", "gpio_out", "gmac", NULL, "mmc3", "mmc2", NULL, NULL}}, + {"PA13", 0, 13, {"gpio_in", "gpio_out", "gmac", NULL, "mmc3", "mmc2", NULL, NULL}}, + {"PA14", 0, 14, {"gpio_in", "gpio_out", "gmac", NULL, "mmc3", "mmc2", NULL, NULL}}, + {"PA15", 0, 15, {"gpio_in", "gpio_out", "gmac", NULL, "dmic", NULL, NULL, NULL}}, + {"PA16", 0, 16, {"gpio_in", "gpio_out", "gmac", NULL, "dmic", NULL, NULL, NULL}}, + {"PA17", 0, 17, {"gpio_in", "gpio_out", "gmac", NULL, "clk_out_b", NULL, NULL, NULL}}, + {"PA18", 0, 17, {"gpio_in", "gpio_out", "gmac", NULL, "pwm3", NULL, NULL, NULL}}, + {"PA19", 0, 17, {"gpio_in", "gpio_out", "gmac", NULL, "pwm3", NULL, NULL, NULL}}, + {"PA20", 0, 17, {"gpio_in", "gpio_out", "gmac", NULL, "spi3", NULL, NULL, NULL}}, + {"PA21", 0, 17, {"gpio_in", "gpio_out", "gmac", NULL, "spi3", NULL, NULL, NULL}}, + {"PA22", 0, 17, {"gpio_in", "gpio_out", "gmac", NULL, "spi3", NULL, NULL, NULL}}, + {"PA23", 0, 17, {"gpio_in", "gpio_out", "gmac", NULL, "spi3", NULL, NULL, NULL}}, + {"PA24", 0, 17, {"gpio_in", "gpio_out", "gmac", NULL, "spi3", NULL, NULL, NULL}}, + {"PA25", 0, 17, {"gpio_in", "gpio_out", "gmac", NULL, "spi3", NULL, NULL, NULL}}, + {"PA26", 0, 17, {"gpio_in", "gpio_out", "gmac", NULL, "clk_out_c", NULL, NULL, NULL}}, + {"PA27", 0, 17, {"gpio_in", "gpio_out", "gmac", NULL, NULL, NULL, NULL, NULL}}, + + {"PB0", 1, 0, {"gpio_in", "gpio_out", "i2s0", "uart3", NULL , NULL, NULL, NULL}}, + {"PB1", 1, 1, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, NULL, NULL}}, + {"PB2", 1, 2, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, NULL, NULL}}, + {"PB3", 1, 3, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, NULL, NULL}}, + {"PB4", 1, 4, {"gpio_in", "gpio_out", "i2s0", "uart3", NULL, NULL, NULL, NULL}}, + {"PB5", 1, 5, {"gpio_in", "gpio_out", "i2s0", "uart3", "i2c3", NULL, NULL, NULL}}, + {"PB6", 1, 6, {"gpio_in", "gpio_out", "i2s0", "uart3", "i2c3", NULL, NULL, NULL}}, + {"PB7", 1, 7, {"gpio_in", "gpio_out", "i2s0", NULL, NULL, NULL, NULL, NULL}}, + + {"PC0", 2, 0, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}}, + {"PC1", 2, 1, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}}, + {"PC2", 2, 2, {"gpio_in", "gpio_out", "nand0", "spi0", NULL, NULL, NULL, NULL}}, + {"PC3", 2, 3, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC4", 2, 4, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC5", 2, 5, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC6", 2, 6, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC7", 2, 7, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC8", 2, 8, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC9", 2, 9, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC10", 2, 10, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC11", 2, 11, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC12", 2, 12, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC13", 2, 13, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC14", 2, 14, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC15", 2, 15, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC24", 2, 24, {"gpio_in", "gpio_out", "nand0", "mmc2", "mmc3", NULL, NULL, NULL}}, + {"PC25", 2, 24, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC26", 2, 24, {"gpio_in", "gpio_out", "nand0", NULL, NULL, NULL, NULL, NULL}}, + {"PC27", 2, 24, {"gpio_in", "gpio_out", NULL, "spi0",NULL, NULL, NULL, NULL}}, + + {"PD0", 3, 0, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD1", 3, 1, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD2", 3, 2, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD3", 3, 3, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD4", 3, 4, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD5", 3, 5, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD6", 3, 6, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD7", 3, 7, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD8", 3, 8, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD9", 3, 9, {"gpio_in", "gpio_out", "lcd0", "lvds0", NULL, NULL, NULL, NULL}}, + {"PD10", 3, 10, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD11", 3, 11, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD12", 3, 12, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD13", 3, 13, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD14", 3, 14, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD15", 3, 15, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD16", 3, 16, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD17", 3, 17, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD18", 3, 18, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD19", 3, 19, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD20", 3, 20, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD21", 3, 21, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD22", 3, 22, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD23", 3, 23, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD24", 3, 24, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD25", 3, 25, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD26", 3, 26, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + {"PD27", 3, 27, {"gpio_in", "gpio_out", "lcd0", NULL, NULL, NULL, NULL, NULL}}, + + {"PE0", 4, 0, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE1", 4, 1, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE2", 4, 2, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE3", 4, 3, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE4", 4, 4, {"gpio_in", "gpio_out", "csi", "uart5", NULL, NULL, NULL, NULL}}, + {"PE5", 4, 5, {"gpio_in", "gpio_out", "csi", "uart5", NULL, NULL, NULL, NULL}}, + {"PE6", 4, 6, {"gpio_in", "gpio_out", "csi", "uart5", NULL, NULL, NULL, NULL}}, + {"PE7", 4, 7, {"gpio_in", "gpio_out", "csi", "uart5", NULL, NULL, NULL, NULL}}, + {"PE8", 4, 8, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE9", 4, 9, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE10", 4, 10, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE11", 4, 11, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE12", 4, 11, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE13", 4, 11, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE14", 4, 11, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE15", 4, 11, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + + {"PF0", 5, 0, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + {"PF1", 5, 1, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + {"PF2", 5, 2, {"gpio_in", "gpio_out", "mmc0", NULL, "uart0", NULL, NULL, NULL}}, + {"PF3", 5, 3, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + {"PF4", 5, 4, {"gpio_in", "gpio_out", "mmc0", NULL, "uart0", NULL, NULL, NULL}}, + {"PF5", 5, 5, {"gpio_in", "gpio_out", "mmc0", NULL, "jtag", NULL, NULL, NULL}}, + + {"PG0", 6, 0, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, NULL}}, + {"PG1", 6, 1, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, NULL}}, + {"PG2", 6, 2, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, NULL}}, + {"PG3", 6, 3, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, NULL}}, + {"PG4", 6, 4, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, NULL}}, + {"PG5", 6, 5, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, NULL}}, + {"PG6", 6, 6, {"gpio_in", "gpio_out", "uart2", NULL, NULL, NULL, NULL, NULL}}, + {"PG7", 6, 7, {"gpio_in", "gpio_out", "uart2", NULL, NULL, NULL, NULL, NULL}}, + {"PG8", 6, 8, {"gpio_in", "gpio_out", "uart2", NULL, NULL, NULL, NULL, NULL}}, + {"PG9", 6, 9, {"gpio_in", "gpio_out", "uart2", NULL, NULL, NULL, NULL, NULL}}, + {"PG10", 6, 10, {"gpio_in", "gpio_out", "i2c3", NULL, NULL, NULL, NULL, NULL}}, + {"PG11", 6, 11, {"gpio_in", "gpio_out", "i2c3", NULL, NULL, NULL, NULL, NULL}}, + {"PG12", 6, 11, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, NULL, NULL}}, + {"PG13", 6, 11, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, NULL, NULL}}, + {"PG14", 6, 11, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, NULL, NULL}}, + {"PG15", 6, 11, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, NULL, NULL}}, + {"PG16", 6, 11, {"gpio_in", "gpio_out", "spi1", "i2s1", NULL, NULL, NULL, NULL}}, + {"PG17", 6, 11, {"gpio_in", "gpio_out", "uart4", NULL, NULL, NULL, NULL, NULL}}, + {"PG18", 6, 11, {"gpio_in", "gpio_out", "uart4", NULL, NULL, NULL, NULL, NULL}}, + + {"PH9", 7, 9, {"gpio_in", "gpio_out", "spi2", "jtag", "pwm1", NULL, NULL, NULL}}, + {"PH10", 7, 10, {"gpio_in", "gpio_out", "spi2", "jtag", "pwm1", NULL, NULL, NULL}}, + {"PH11", 7, 11, {"gpio_in", "gpio_out", "spi2", "jtag", "pwm2", NULL, NULL, NULL}}, + {"PH12", 7, 12, {"gpio_in", "gpio_out", "spi2", "jtag", "pwm2", NULL, NULL, NULL}}, + {"PH13", 7, 13, {"gpio_in", "gpio_out", "pwm0", NULL, NULL, NULL, NULL, NULL}}, + {"PH14", 7, 14, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}}, + {"PH15", 7, 15, {"gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, NULL, NULL}}, + {"PH16", 7, 16, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}}, + {"PH17", 7, 17, {"gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, NULL, NULL}}, + {"PH18", 7, 18, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}}, + {"PH19", 7, 19, {"gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, NULL, NULL}}, + {"PH20", 7, 20, {"gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, NULL, NULL}}, + {"PH21", 7, 21, {"gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, NULL, NULL}}, + {"PH22", 7, 22, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PH23", 7, 23, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PH24", 7, 24, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PH25", 7, 25, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PH26", 7, 26, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PH27", 7, 27, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PH28", 7, 27, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, +}; + +const struct allwinner_padconf a31s_padconf = { + .npins = nitems(a31s_pins), + .pins = a31s_pins, +}; + +#endif /* SOC_ALLWINNER_A31S */ Property changes on: trunk/sys/arm/allwinner/a31/a31s_padconf.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/a31/files.a31 =================================================================== --- trunk/sys/arm/allwinner/a31/files.a31 (rev 0) +++ trunk/sys/arm/allwinner/a31/files.a31 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,5 @@ +# $MidnightBSD$ + +arm/allwinner/a31/a31_padconf.c standard +arm/allwinner/a31/a31_r_padconf.c standard +arm/allwinner/a31/a31s_padconf.c standard Property changes on: trunk/sys/arm/allwinner/a31/files.a31 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: trunk/sys/arm/allwinner/a31/std.a31 =================================================================== --- trunk/sys/arm/allwinner/a31/std.a31 (rev 0) +++ trunk/sys/arm/allwinner/a31/std.a31 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,15 @@ +# Allwinner A31 common options +# $MidnightBSD$ + +cpu CPU_CORTEXA +machine arm armv6 +makeoptions CONF_CFLAGS="-march=armv7a" + +makeoptions KERNVIRTADDR=0xc0200000 +options KERNVIRTADDR=0xc0200000 + +options IPI_IRQ_START=0 +options IPI_IRQ_END=15 + +files "../allwinner/files.allwinner" +files "../allwinner/a31/files.a31" Property changes on: trunk/sys/arm/allwinner/a31/std.a31 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: trunk/sys/arm/allwinner/a83t/a83t_padconf.c =================================================================== --- trunk/sys/arm/allwinner/a83t/a83t_padconf.c (rev 0) +++ trunk/sys/arm/allwinner/a83t/a83t_padconf.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,163 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/a83t/a83t_padconf.c 299113 2016-05-05 09:41:57Z jmcneill $ + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a83t/a83t_padconf.c 299113 2016-05-05 09:41:57Z jmcneill $"); + +#include +#include +#include +#include + +#include + +#ifdef SOC_ALLWINNER_A83T + +static const struct allwinner_pins a83t_pins[] = { + { "PB0", 1, 0, { "gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "eint" } }, + { "PB1", 1, 1, { "gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "eint" } }, + { "PB2", 1, 2, { "gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "eint" } }, + { "PB3", 1, 3, { "gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "eint" } }, + { "PB4", 1, 4, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "eint" } }, + { "PB5", 1, 5, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "eint" } }, + { "PB6", 1, 6, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "eint" } }, + { "PB7", 1, 7, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "eint" } }, + { "PB8", 1, 8, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "eint" } }, + { "PB9", 1, 9, { "gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, "eint" } }, + { "PB10", 1, 10, { "gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, "eint" } }, + + { "PC0", 2, 0, { "gpio_in", "gpio_out", "nand", "spi0" } }, + { "PC1", 2, 1, { "gpio_in", "gpio_out", "nand", "spi0" } }, + { "PC2", 2, 2, { "gpio_in", "gpio_out", "nand", "spi0" } }, + { "PC3", 2, 3, { "gpio_in", "gpio_out", "nand", "spi0" } }, + { "PC4", 2, 4, { "gpio_in", "gpio_out", "nand" } }, + { "PC5", 2, 5, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC6", 2, 6, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC7", 2, 7, { "gpio_in", "gpio_out", "nand" } }, + { "PC8", 2, 8, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC9", 2, 9, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC10", 2, 10, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC11", 2, 11, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC12", 2, 12, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC13", 2, 13, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC14", 2, 14, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC15", 2, 15, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC16", 2, 16, { "gpio_in", "gpio_out", "nand", "mmc2" } }, + { "PC17", 2, 17, { "gpio_in", "gpio_out", "nand" } }, + { "PC18", 2, 18, { "gpio_in", "gpio_out", "nand" } }, + + { "PD2", 3, 2, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD3", 3, 3, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD4", 3, 4, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD5", 3, 5, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD6", 3, 6, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD7", 3, 7, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD10", 3, 10, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD11", 3, 11, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD12", 3, 12, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD13", 3, 13, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD14", 3, 14, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD15", 3, 15, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } }, + { "PD18", 3, 18, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } }, + { "PD19", 3, 19, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } }, + { "PD20", 3, 20, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } }, + { "PD21", 3, 21, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } }, + { "PD22", 3, 22, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } }, + { "PD23", 3, 23, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } }, + { "PD24", 3, 24, { "gpio_in", "gpio_out", "lcd", "lvds" } }, + { "PD25", 3, 25, { "gpio_in", "gpio_out", "lcd", "lvds" } }, + { "PD26", 3, 26, { "gpio_in", "gpio_out", "lcd", "lvds" } }, + { "PD27", 3, 27, { "gpio_in", "gpio_out", "lcd", "lvds" } }, + { "PD28", 3, 28, { "gpio_in", "gpio_out", "pwm" } }, + { "PD29", 3, 29, { "gpio_in", "gpio_out" } }, + + { "PE0", 4, 0, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } }, + { "PE1", 4, 1, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } }, + { "PE2", 4, 2, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } }, + { "PE3", 4, 3, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } }, + { "PE4", 4, 4, { "gpio_in", "gpio_out", "csi" } }, + { "PE5", 4, 5, { "gpio_in", "gpio_out", "csi" } }, + { "PE6", 4, 6, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } }, + { "PE7", 4, 7, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } }, + { "PE8", 4, 8, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } }, + { "PE9", 4, 9, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } }, + { "PE10", 4, 10, { "gpio_in", "gpio_out", "csi", "uart4", "ccir" } }, + { "PE11", 4, 11, { "gpio_in", "gpio_out", "csi", "uart4", "ccir" } }, + { "PE12", 4, 12, { "gpio_in", "gpio_out", "csi", "uart4", "ccir" } }, + { "PE13", 4, 13, { "gpio_in", "gpio_out", "csi", "uart4", "ccir" } }, + { "PE14", 4, 14, { "gpio_in", "gpio_out", "csi", "twi2" } }, + { "PE15", 4, 15, { "gpio_in", "gpio_out", "csi", "twi2" } }, + { "PE16", 4, 16, { "gpio_in", "gpio_out" } }, + { "PE17", 4, 17, { "gpio_in", "gpio_out" } }, + { "PE18", 4, 18, { "gpio_in", "gpio_out", NULL, "owa" } }, + { "PE19", 4, 19, { "gpio_in", "gpio_out" } }, + + { "PF0", 5, 0, { "gpio_in", "gpio_out", "mmc0", "jtag" } }, + { "PF1", 5, 1, { "gpio_in", "gpio_out", "mmc0", "jtag" } }, + { "PF2", 5, 2, { "gpio_in", "gpio_out", "mmc0", "uart0" } }, + { "PF3", 5, 3, { "gpio_in", "gpio_out", "mmc0", "jtag" } }, + { "PF4", 5, 4, { "gpio_in", "gpio_out", "mmc0", "uart0" } }, + { "PF5", 5, 5, { "gpio_in", "gpio_out", "mmc0", "jtag" } }, + { "PF6", 5, 6, { "gpio_in", "gpio_out" } }, + + { "PG0", 6, 0, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } }, + { "PG1", 6, 1, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } }, + { "PG2", 6, 2, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } }, + { "PG3", 6, 3, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } }, + { "PG4", 6, 4, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } }, + { "PG5", 6, 5, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } }, + { "PG6", 6, 6, { "gpio_in", "gpio_out", "uart1", "spi1", NULL, NULL, "eint" } }, + { "PG7", 6, 7, { "gpio_in", "gpio_out", "uart1", "spi1", NULL, NULL, "eint" } }, + { "PG8", 6, 8, { "gpio_in", "gpio_out", "uart1", "spi1", NULL, NULL, "eint" } }, + { "PG9", 6, 9, { "gpio_in", "gpio_out", "uart1", "spi1", NULL, NULL, "eint" } }, + { "PG10", 6, 10, { "gpio_in", "gpio_out", "i2s1", "uart3", NULL, NULL, "eint" } }, + { "PG11", 6, 11, { "gpio_in", "gpio_out", "i2s1", "uart3", NULL, NULL, "eint" } }, + { "PG12", 6, 12, { "gpio_in", "gpio_out", "i2s1", "uart3", NULL, NULL, "eint" } }, + { "PG13", 6, 13, { "gpio_in", "gpio_out", "i2s1", "uart3", NULL, NULL, "eint" } }, + + { "PH0", 7, 0, { "gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, "eint" } }, + { "PH1", 7, 1, { "gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, "eint" } }, + { "PH2", 7, 2, { "gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, "eint" } }, + { "PH3", 7, 3, { "gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, "eint" } }, + { "PH4", 7, 4, { "gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, "eint" } }, + { "PH5", 7, 5, { "gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, "eint" } }, + { "PH6", 7, 6, { "gpio_in", "gpio_out", "hdmiddc", NULL, NULL, NULL, "eint" } }, + { "PH7", 7, 7, { "gpio_in", "gpio_out", "hdmiddc", NULL, NULL, NULL, "eint" } }, + { "PH8", 7, 8, { "gpio_in", "gpio_out", "hdmiddc", NULL, NULL, NULL, "eint" } }, + { "PH9", 7, 9, { "gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "eint" } }, + { "PH10", 7, 10, { "gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "eint" } }, + { "PH11", 7, 11, { "gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "eint" } }, +}; + +const struct allwinner_padconf a83t_padconf = { + .npins = nitems(a83t_pins), + .pins = a83t_pins, +}; + +#endif /* !SOC_ALLWINNER_A83T */ Property changes on: trunk/sys/arm/allwinner/a83t/a83t_padconf.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/a83t/a83t_r_padconf.c =================================================================== --- trunk/sys/arm/allwinner/a83t/a83t_r_padconf.c (rev 0) +++ trunk/sys/arm/allwinner/a83t/a83t_r_padconf.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,63 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/a83t/a83t_r_padconf.c 299113 2016-05-05 09:41:57Z jmcneill $ + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a83t/a83t_r_padconf.c 299113 2016-05-05 09:41:57Z jmcneill $"); + +#include +#include +#include +#include + +#include + +#ifdef SOC_ALLWINNER_A83T + +static const struct allwinner_pins a83t_r_pins[] = { + { "PL0", 0, 0, { "gpio_in", "gpio_out", "s_rsb", "s_i2c", NULL, NULL, "eint" } }, + { "PL1", 0, 1, { "gpio_in", "gpio_out", "s_rsb", "s_i2c", NULL, NULL, "eint" } }, + { "PL2", 0, 2, { "gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, "eint" } }, + { "PL3", 0, 3, { "gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, "eint" } }, + { "PL4", 0, 4, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "eint" } }, + { "PL5", 0, 5, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "eint" } }, + { "PL6", 0, 6, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "eint" } }, + { "PL7", 0, 7, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "eint" } }, + { "PL8", 0, 8, { "gpio_in", "gpio_out", "s_i2c", NULL, NULL, NULL, "eint" } }, + { "PL9", 0, 9, { "gpio_in", "gpio_out", "s_i2c", NULL, NULL, NULL, "eint" } }, + { "PL10", 0, 10, { "gpio_in", "gpio_out", "s_pwm", NULL, NULL, NULL, "eint" } }, + { "PL11", 0, 11, { "gpio_in", "gpio_out", NULL, NULL, NULL, "eint" } }, + { "PL12", 0, 12, { "gpio_in", "gpio_out", "s_cir", NULL, NULL, NULL, "eint" } }, +}; + +const struct allwinner_padconf a83t_r_padconf = { + .npins = nitems(a83t_r_pins), + .pins = a83t_r_pins, +}; + +#endif /* !SOC_ALLWINNER_A83T */ Property changes on: trunk/sys/arm/allwinner/a83t/a83t_r_padconf.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/a83t/files.a83t =================================================================== --- trunk/sys/arm/allwinner/a83t/files.a83t (rev 0) +++ trunk/sys/arm/allwinner/a83t/files.a83t 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,4 @@ +# $MidnightBSD$ + +arm/allwinner/a83t/a83t_padconf.c standard +arm/allwinner/a83t/a83t_r_padconf.c standard Property changes on: trunk/sys/arm/allwinner/a83t/files.a83t ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: trunk/sys/arm/allwinner/a83t/std.a83t =================================================================== --- trunk/sys/arm/allwinner/a83t/std.a83t (rev 0) +++ trunk/sys/arm/allwinner/a83t/std.a83t 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,15 @@ +# Allwinner A83T common options +# $MidnightBSD$ + +cpu CPU_CORTEXA +machine arm armv6 +makeoptions CONF_CFLAGS="-march=armv7a" + +makeoptions KERNVIRTADDR=0xc0200000 +options KERNVIRTADDR=0xc0200000 + +options IPI_IRQ_START=0 +options IPI_IRQ_END=15 + +files "../allwinner/files.allwinner" +files "../allwinner/a83t/files.a83t" Property changes on: trunk/sys/arm/allwinner/a83t/std.a83t ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: trunk/sys/arm/allwinner/allwinner_pinctrl.h =================================================================== --- trunk/sys/arm/allwinner/allwinner_pinctrl.h (rev 0) +++ trunk/sys/arm/allwinner/allwinner_pinctrl.h 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,47 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Emmanuel Vadot + * 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: stable/11/sys/arm/allwinner/allwinner_pinctrl.h 331182 2018-03-19 06:40:11Z eadler $ + */ + +#ifndef _ALLWINNER_PINCTRL_H_ +#define _ALLWINNER_PINCTRL_H_ + +#define AW_MAX_FUNC_BY_PIN 8 + +struct allwinner_pins { + const char *name; + uint8_t port; + uint8_t pin; + const char *functions[8]; +}; + +struct allwinner_padconf { + uint32_t npins; + const struct allwinner_pins * pins; +}; + +#endif /* _ALLWINNER_PINCTRL_H_ */ Property changes on: trunk/sys/arm/allwinner/allwinner_pinctrl.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_ccu.c =================================================================== --- trunk/sys/arm/allwinner/aw_ccu.c (rev 0) +++ trunk/sys/arm/allwinner/aw_ccu.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,302 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/aw_ccu.c 305436 2016-09-05 20:17:18Z manu $ + */ + +/* + * Allwinner oscillator clock + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/aw_ccu.c 305436 2016-09-05 20:17:18Z manu $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include "clkdev_if.h" + +#define CCU_BASE 0x01c20000 +#define CCU_SIZE 0x400 + +#define PRCM_BASE 0x01f01400 +#define PRCM_SIZE 0x200 + +#define SYSCTRL_BASE 0x01c00000 +#define SYSCTRL_SIZE 0x34 + +struct aw_ccu_softc { + struct simplebus_softc sc; + bus_space_tag_t bst; + bus_space_handle_t ccu_bsh; + bus_space_handle_t prcm_bsh; + bus_space_handle_t sysctrl_bsh; + struct mtx mtx; + int flags; +}; + +#define CLOCK_CCU (1 << 0) +#define CLOCK_PRCM (1 << 1) +#define CLOCK_SYSCTRL (1 << 2) + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10", CLOCK_CCU }, + { "allwinner,sun5i-a13", CLOCK_CCU }, + { "allwinner,sun7i-a20", CLOCK_CCU }, + { "allwinner,sun6i-a31", CLOCK_CCU }, + { "allwinner,sun6i-a31s", CLOCK_CCU }, + { "allwinner,sun8i-a83t", CLOCK_CCU|CLOCK_PRCM|CLOCK_SYSCTRL }, + { "allwinner,sun8i-h3", CLOCK_CCU }, + { NULL, 0 } +}; + +static int +aw_ccu_check_addr(struct aw_ccu_softc *sc, bus_addr_t addr, + bus_space_handle_t *pbsh, bus_size_t *poff) +{ + if (addr >= CCU_BASE && addr < (CCU_BASE + CCU_SIZE) && + (sc->flags & CLOCK_CCU) != 0) { + *poff = addr - CCU_BASE; + *pbsh = sc->ccu_bsh; + return (0); + } + if (addr >= PRCM_BASE && addr < (PRCM_BASE + PRCM_SIZE) && + (sc->flags & CLOCK_PRCM) != 0) { + *poff = addr - PRCM_BASE; + *pbsh = sc->prcm_bsh; + return (0); + } + if (addr >= SYSCTRL_BASE && addr < (SYSCTRL_BASE + SYSCTRL_SIZE) && + (sc->flags & CLOCK_SYSCTRL) != 0) { + *poff = addr - SYSCTRL_BASE; + *pbsh = sc->sysctrl_bsh; + return (0); + } + return (EINVAL); +} + +static int +aw_ccu_write_4(device_t dev, bus_addr_t addr, uint32_t val) +{ + struct aw_ccu_softc *sc; + bus_space_handle_t bsh; + bus_size_t reg; + + sc = device_get_softc(dev); + + if (aw_ccu_check_addr(sc, addr, &bsh, ®) != 0) + return (EINVAL); + + mtx_assert(&sc->mtx, MA_OWNED); + bus_space_write_4(sc->bst, bsh, reg, val); + + return (0); +} + +static int +aw_ccu_read_4(device_t dev, bus_addr_t addr, uint32_t *val) +{ + struct aw_ccu_softc *sc; + bus_space_handle_t bsh; + bus_size_t reg; + + sc = device_get_softc(dev); + + if (aw_ccu_check_addr(sc, addr, &bsh, ®) != 0) + return (EINVAL); + + mtx_assert(&sc->mtx, MA_OWNED); + *val = bus_space_read_4(sc->bst, bsh, reg); + + return (0); +} + +static int +aw_ccu_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set) +{ + struct aw_ccu_softc *sc; + bus_space_handle_t bsh; + bus_size_t reg; + uint32_t val; + + sc = device_get_softc(dev); + + if (aw_ccu_check_addr(sc, addr, &bsh, ®) != 0) + return (EINVAL); + + mtx_assert(&sc->mtx, MA_OWNED); + val = bus_space_read_4(sc->bst, bsh, reg); + val &= ~clr; + val |= set; + bus_space_write_4(sc->bst, bsh, reg, val); + + return (0); +} + +static void +aw_ccu_device_lock(device_t dev) +{ + struct aw_ccu_softc *sc; + + sc = device_get_softc(dev); + mtx_lock(&sc->mtx); +} + +static void +aw_ccu_device_unlock(device_t dev) +{ + struct aw_ccu_softc *sc; + + sc = device_get_softc(dev); + mtx_unlock(&sc->mtx); +} + +static const struct ofw_compat_data * +aw_ccu_search_compatible(void) +{ + const struct ofw_compat_data *compat; + phandle_t root; + + root = OF_finddevice("/"); + for (compat = compat_data; compat->ocd_str != NULL; compat++) + if (fdt_is_compatible(root, compat->ocd_str)) + break; + + return (compat); +} + +static int +aw_ccu_probe(device_t dev) +{ + const char *name; + + name = ofw_bus_get_name(dev); + + if (name == NULL || strcmp(name, "clocks") != 0) + return (ENXIO); + + if (aw_ccu_search_compatible()->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "Allwinner Clock Control Unit"); + return (BUS_PROBE_SPECIFIC); +} + +static int +aw_ccu_attach(device_t dev) +{ + struct aw_ccu_softc *sc; + phandle_t node, child; + device_t cdev; + int error; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + + simplebus_init(dev, node); + + sc->flags = aw_ccu_search_compatible()->ocd_data; + + /* + * Map registers. The DT doesn't have a "reg" property + * for the /clocks node and child nodes have conflicting "reg" + * properties. + */ + sc->bst = bus_get_bus_tag(dev); + if (sc->flags & CLOCK_CCU) { + error = bus_space_map(sc->bst, CCU_BASE, CCU_SIZE, 0, + &sc->ccu_bsh); + if (error != 0) { + device_printf(dev, "couldn't map CCU: %d\n", error); + return (error); + } + } + if (sc->flags & CLOCK_PRCM) { + error = bus_space_map(sc->bst, PRCM_BASE, PRCM_SIZE, 0, + &sc->prcm_bsh); + if (error != 0) { + device_printf(dev, "couldn't map PRCM: %d\n", error); + return (error); + } + } + if (sc->flags & CLOCK_SYSCTRL) { + error = bus_space_map(sc->bst, SYSCTRL_BASE, SYSCTRL_SIZE, 0, + &sc->sysctrl_bsh); + if (error != 0) { + device_printf(dev, "couldn't map SYSCTRL: %d\n", error); + return (error); + } + } + + mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + /* Attach child devices */ + 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 aw_ccu_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_ccu_probe), + DEVMETHOD(device_attach, aw_ccu_attach), + + /* clkdev interface */ + DEVMETHOD(clkdev_write_4, aw_ccu_write_4), + DEVMETHOD(clkdev_read_4, aw_ccu_read_4), + DEVMETHOD(clkdev_modify_4, aw_ccu_modify_4), + DEVMETHOD(clkdev_device_lock, aw_ccu_device_lock), + DEVMETHOD(clkdev_device_unlock, aw_ccu_device_unlock), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(aw_ccu, aw_ccu_driver, aw_ccu_methods, + sizeof(struct aw_ccu_softc), simplebus_driver); + +static devclass_t aw_ccu_devclass; + +EARLY_DRIVER_MODULE(aw_ccu, simplebus, aw_ccu_driver, aw_ccu_devclass, + 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); + +MODULE_VERSION(aw_ccu, 1); Property changes on: trunk/sys/arm/allwinner/aw_ccu.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_if_dwc.c =================================================================== --- trunk/sys/arm/allwinner/aw_if_dwc.c (rev 0) +++ trunk/sys/arm/allwinner/aw_if_dwc.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,146 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2015 Luiz Otavio O Souza + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/aw_if_dwc.c 308324 2016-11-05 04:17:32Z mmel $"); + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "if_dwc_if.h" + +static int +a20_if_dwc_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-gmac")) + return (ENXIO); + device_set_desc(dev, "A20 Gigabit Ethernet Controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +a20_if_dwc_init(device_t dev) +{ + const char *tx_parent_name; + char *phy_type; + clk_t clk_tx, clk_tx_parent; + regulator_t reg; + phandle_t node; + int error; + + node = ofw_bus_get_node(dev); + + /* Configure PHY for MII or RGMII mode */ + if (OF_getprop_alloc(node, "phy-mode", 1, (void **)&phy_type)) { + error = clk_get_by_ofw_name(dev, 0, "allwinner_gmac_tx", &clk_tx); + if (error != 0) { + device_printf(dev, "could not get tx clk\n"); + return (error); + } + + if (strcmp(phy_type, "rgmii") == 0) + tx_parent_name = "gmac_int_tx"; + else + tx_parent_name = "mii_phy_tx"; + + error = clk_get_by_name(dev, tx_parent_name, &clk_tx_parent); + if (error != 0) { + device_printf(dev, "could not get clock '%s'\n", + tx_parent_name); + return (error); + } + + error = clk_set_parent_by_clk(clk_tx, clk_tx_parent); + if (error != 0) { + device_printf(dev, "could not set tx clk parent\n"); + return (error); + } + } + + /* Enable PHY regulator if applicable */ + if (regulator_get_by_ofw_property(dev, 0, "phy-supply", ®) == 0) { + error = regulator_enable(reg); + if (error != 0) { + device_printf(dev, "could not enable PHY regulator\n"); + return (error); + } + } + + return (0); +} + +static int +a20_if_dwc_mac_type(device_t dev) +{ + + return (DWC_GMAC_ALT_DESC); +} + +static int +a20_if_dwc_mii_clk(device_t dev) +{ + + return (GMAC_MII_CLK_150_250M_DIV102); +} + +static device_method_t a20_dwc_methods[] = { + DEVMETHOD(device_probe, a20_if_dwc_probe), + + DEVMETHOD(if_dwc_init, a20_if_dwc_init), + DEVMETHOD(if_dwc_mac_type, a20_if_dwc_mac_type), + DEVMETHOD(if_dwc_mii_clk, a20_if_dwc_mii_clk), + + DEVMETHOD_END +}; + +static devclass_t a20_dwc_devclass; + +extern driver_t dwc_driver; + +DEFINE_CLASS_1(dwc, a20_dwc_driver, a20_dwc_methods, sizeof(struct dwc_softc), + dwc_driver); +DRIVER_MODULE(a20_dwc, simplebus, a20_dwc_driver, a20_dwc_devclass, 0, 0); + +MODULE_DEPEND(a20_dwc, dwc, 1, 1, 1); Property changes on: trunk/sys/arm/allwinner/aw_if_dwc.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_machdep.c =================================================================== --- trunk/sys/arm/allwinner/aw_machdep.c (rev 0) +++ trunk/sys/arm/allwinner/aw_machdep.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,272 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2012 Ganbold Tsagaankhuu + * Copyright (c) 2015-2016 Emmanuel Vadot + * All rights reserved. + * + * This code is derived from software written for Brini by Mark Brinicombe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + + * from: FreeBSD: //depot/projects/arm/src/sys/arm/ti/ti_machdep.c + */ + +#include "opt_ddb.h" +#include "opt_platform.h" + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/aw_machdep.c 331893 2018-04-02 23:19:07Z gonzo $"); + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "platform_if.h" + +static u_int soc_type; +static u_int soc_family; + +static int +a10_attach(platform_t plat) +{ + soc_type = ALLWINNERSOC_A10; + soc_family = ALLWINNERSOC_SUN4I; + return (0); +} + +static int +a13_attach(platform_t plat) +{ + soc_type = ALLWINNERSOC_A13; + soc_family = ALLWINNERSOC_SUN5I; + return (0); +} + +static int +a20_attach(platform_t plat) +{ + soc_type = ALLWINNERSOC_A20; + soc_family = ALLWINNERSOC_SUN7I; + + return (0); +} + +static int +a31_attach(platform_t plat) +{ + soc_type = ALLWINNERSOC_A31; + soc_family = ALLWINNERSOC_SUN6I; + + return (0); +} + +static int +a31s_attach(platform_t plat) +{ + soc_type = ALLWINNERSOC_A31S; + soc_family = ALLWINNERSOC_SUN6I; + + return (0); +} + +static int +a83t_attach(platform_t plat) +{ + soc_type = ALLWINNERSOC_A83T; + soc_family = ALLWINNERSOC_SUN8I; + + return (0); +} + +static int +h3_attach(platform_t plat) +{ + soc_type = ALLWINNERSOC_H3; + soc_family = ALLWINNERSOC_SUN8I; + + return (0); +} + +static vm_offset_t +allwinner_lastaddr(platform_t plat) +{ + + return (devmap_lastaddr()); +} + +/* + * Set up static device mappings. + * + * This covers all the on-chip device with 1MB section mappings, which is good + * for performance (uses fewer TLB entries for device access). + * + * XXX It also covers a block of SRAM and some GPU (mali400) stuff that maybe + * shouldn't be device-mapped. The original code mapped a 4MB block, but + * perhaps a 1MB block would be more appropriate. + */ +static int +allwinner_devmap_init(platform_t plat) +{ + + devmap_add_entry(0x01C00000, 0x00400000); /* 4MB */ + + return (0); +} + +static void +allwinner_cpu_reset(platform_t plat) +{ + aw_wdog_watchdog_reset(); + printf("Reset failed!\n"); + while (1); +} + +#if defined(SOC_ALLWINNER_A10) +static platform_method_t a10_methods[] = { + PLATFORMMETHOD(platform_attach, a10_attach), + PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), + PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), + PLATFORMMETHOD(platform_cpu_reset, allwinner_cpu_reset), + + PLATFORMMETHOD_END, +}; +FDT_PLATFORM_DEF(a10, "a10", 0, "allwinner,sun4i-a10", 200); +#endif + +#if defined(SOC_ALLWINNER_A13) +static platform_method_t a13_methods[] = { + PLATFORMMETHOD(platform_attach, a13_attach), + PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), + PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), + PLATFORMMETHOD(platform_cpu_reset, allwinner_cpu_reset), + + PLATFORMMETHOD_END, +}; +FDT_PLATFORM_DEF(a13, "a13", 0, "allwinner,sun5i-a13", 200); +#endif + +#if defined(SOC_ALLWINNER_A20) +static platform_method_t a20_methods[] = { + PLATFORMMETHOD(platform_attach, a20_attach), + PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), + PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), + PLATFORMMETHOD(platform_cpu_reset, allwinner_cpu_reset), + +#ifdef SMP + PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap), + PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid), +#endif + PLATFORMMETHOD_END, +}; +FDT_PLATFORM_DEF(a20, "a20", 0, "allwinner,sun7i-a20", 200); +#endif + +#if defined(SOC_ALLWINNER_A31) +static platform_method_t a31_methods[] = { + PLATFORMMETHOD(platform_attach, a31_attach), + PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), + PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), + PLATFORMMETHOD(platform_cpu_reset, allwinner_cpu_reset), + +#ifdef SMP + PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap), + PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid), +#endif + PLATFORMMETHOD_END, +}; +FDT_PLATFORM_DEF(a31, "a31", 0, "allwinner,sun6i-a31", 200); +#endif + +#if defined(SOC_ALLWINNER_A31S) +static platform_method_t a31s_methods[] = { + PLATFORMMETHOD(platform_attach, a31s_attach), + PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), + PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), + PLATFORMMETHOD(platform_cpu_reset, allwinner_cpu_reset), + +#ifdef SMP + PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap), + PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid), +#endif + PLATFORMMETHOD_END, +}; +FDT_PLATFORM_DEF(a31s, "a31s", 0, "allwinner,sun6i-a31s", 200); +#endif + +#if defined(SOC_ALLWINNER_A83T) +static platform_method_t a83t_methods[] = { + PLATFORMMETHOD(platform_attach, a83t_attach), + PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), + PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), + PLATFORMMETHOD(platform_cpu_reset, allwinner_cpu_reset), + +#ifdef SMP + PLATFORMMETHOD(platform_mp_start_ap, a83t_mp_start_ap), + PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid), +#endif + PLATFORMMETHOD_END, +}; +FDT_PLATFORM_DEF(a83t, "a83t", 0, "allwinner,sun8i-a83t", 200); +#endif + +#if defined(SOC_ALLWINNER_H3) +static platform_method_t h3_methods[] = { + PLATFORMMETHOD(platform_attach, h3_attach), + PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr), + PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init), + PLATFORMMETHOD(platform_cpu_reset, allwinner_cpu_reset), + +#ifdef SMP + PLATFORMMETHOD(platform_mp_start_ap, aw_mp_start_ap), + PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid), +#endif + PLATFORMMETHOD_END, +}; +FDT_PLATFORM_DEF(h3, "h3", 0, "allwinner,sun8i-h3", 200); +#endif + +u_int +allwinner_soc_type(void) +{ + return (soc_type); +} + +u_int +allwinner_soc_family(void) +{ + return (soc_family); +} Property changes on: trunk/sys/arm/allwinner/aw_machdep.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_machdep.h =================================================================== --- trunk/sys/arm/allwinner/aw_machdep.h (rev 0) +++ trunk/sys/arm/allwinner/aw_machdep.h 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,52 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2015 Emmanuel Vadot + * 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: stable/11/sys/arm/allwinner/aw_machdep.h 308274 2016-11-04 00:54:21Z manu $ + * + */ + +#ifndef AW_MACHDEP_H +#define AW_MACHDEP_H + +#define ALLWINNERSOC_A10 0x10000000 +#define ALLWINNERSOC_A13 0x13000000 +#define ALLWINNERSOC_A10S 0x10000001 +#define ALLWINNERSOC_A20 0x20000000 +#define ALLWINNERSOC_H3 0x30000000 +#define ALLWINNERSOC_A31 0x31000000 +#define ALLWINNERSOC_A31S 0x31000001 +#define ALLWINNERSOC_A83T 0x83000000 + +#define ALLWINNERSOC_SUN4I 0x40000000 +#define ALLWINNERSOC_SUN5I 0x50000000 +#define ALLWINNERSOC_SUN6I 0x60000000 +#define ALLWINNERSOC_SUN7I 0x70000000 +#define ALLWINNERSOC_SUN8I 0x80000000 + +u_int allwinner_soc_type(void); +u_int allwinner_soc_family(void); + +#endif /* AW_MACHDEP_H */ Property changes on: trunk/sys/arm/allwinner/aw_machdep.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_mp.c =================================================================== --- trunk/sys/arm/allwinner/aw_mp.c (rev 0) +++ trunk/sys/arm/allwinner/aw_mp.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,289 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2014 Ganbold Tsagaankhuu + * Copyright (c) 2016 Emmanuel Vadot + * 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 +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/aw_mp.c 331182 2018-03-19 06:40:11Z eadler $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Register for all dual-core SoC */ +#define A20_CPUCFG_BASE 0x01c25c00 +/* Register for all quad-core SoC */ +#define CPUCFG_BASE 0x01f01c00 +#define CPUCFG_SIZE 0x400 +#define PRCM_BASE 0x01f01400 +#define PRCM_SIZE 0x800 +/* Register for multi-cluster SoC */ +#define CPUXCFG_BASE 0x01700000 +#define CPUXCFG_SIZE 0x400 + +#define CPU_OFFSET 0x40 +#define CPU_OFFSET_CTL 0x04 +#define CPU_OFFSET_STATUS 0x08 +#define CPU_RST_CTL(cpuid) ((cpuid + 1) * CPU_OFFSET) +#define CPU_CTL(cpuid) (((cpuid + 1) * CPU_OFFSET) + CPU_OFFSET_CTL) +#define CPU_STATUS(cpuid) (((cpuid + 1) * CPU_OFFSET) + CPU_OFFSET_STATUS) + +#define CPU_RESET (1 << 0) +#define CPU_CORE_RESET (1 << 1) + +#define CPUCFG_GENCTL 0x184 +#define CPUCFG_P_REG0 0x1a4 + +#define A20_CPU1_PWR_CLAMP 0x1b0 +#define CPU_PWR_CLAMP_REG 0x140 +#define CPU_PWR_CLAMP(cpu) ((cpu * 4) + CPU_PWR_CLAMP_REG) +#define CPU_PWR_CLAMP_STEPS 8 + +#define A20_CPU1_PWROFF_REG 0x1b4 +#define CPU_PWROFF 0x100 + +#define CPUCFG_DBGCTL0 0x1e0 +#define CPUCFG_DBGCTL1 0x1e4 + +#define CPUS_CL_RST(cl) (0x30 + (cluster) * 0x4) +#define CPUX_CL_CTRL0(cl) (0x0 + (cluster) * 0x10) +#define CPUX_CL_CTRL1(cl) (0x4 + (cluster) * 0x10) +#define CPUX_CL_CPU_STATUS(cl) (0x30 + (cluster) * 0x4) +#define CPUX_CL_RST(cl) (0x80 + (cluster) * 0x4) +#define PRCM_CL_PWROFF(cl) (0x100 + (cluster) * 0x4) +#define PRCM_CL_PWR_CLAMP(cl, cpu) (0x140 + (cluster) * 0x4 + (cpu) * 0x4) + +void +aw_mp_setmaxid(platform_t plat) +{ + int ncpu; + uint32_t reg; + + if (mp_ncpus != 0) + return; + + reg = cp15_l2ctlr_get(); + ncpu = CPUV7_L2CTLR_NPROC(reg); + + mp_ncpus = ncpu; + mp_maxid = ncpu - 1; +} + +void +aw_mp_start_ap(platform_t plat) +{ + bus_space_handle_t cpucfg; + bus_space_handle_t prcm; + int i, j, soc_family; + uint32_t val; + + soc_family = allwinner_soc_family(); + if (soc_family == ALLWINNERSOC_SUN7I) { + if (bus_space_map(fdtbus_bs_tag, A20_CPUCFG_BASE, CPUCFG_SIZE, + 0, &cpucfg) != 0) + panic("Couldn't map the CPUCFG\n"); + } else { + if (bus_space_map(fdtbus_bs_tag, CPUCFG_BASE, CPUCFG_SIZE, + 0, &cpucfg) != 0) + panic("Couldn't map the CPUCFG\n"); + if (bus_space_map(fdtbus_bs_tag, PRCM_BASE, PRCM_SIZE, 0, + &prcm) != 0) + panic("Couldn't map the PRCM\n"); + } + + dcache_wbinv_poc_all(); + + bus_space_write_4(fdtbus_bs_tag, cpucfg, CPUCFG_P_REG0, + pmap_kextract((vm_offset_t)mpentry)); + + /* + * Assert nCOREPORESET low and set L1RSTDISABLE low. + * Ensure DBGPWRDUP is set to LOW to prevent any external + * debug access to the processor. + */ + for (i = 1; i < mp_ncpus; i++) + bus_space_write_4(fdtbus_bs_tag, cpucfg, CPU_RST_CTL(i), 0); + + /* Set L1RSTDISABLE low */ + val = bus_space_read_4(fdtbus_bs_tag, cpucfg, CPUCFG_GENCTL); + for (i = 1; i < mp_ncpus; i++) + val &= ~(1 << i); + bus_space_write_4(fdtbus_bs_tag, cpucfg, CPUCFG_GENCTL, val); + + /* Set DBGPWRDUP low */ + val = bus_space_read_4(fdtbus_bs_tag, cpucfg, CPUCFG_DBGCTL1); + for (i = 1; i < mp_ncpus; i++) + val &= ~(1 << i); + bus_space_write_4(fdtbus_bs_tag, cpucfg, CPUCFG_DBGCTL1, val); + + /* Release power clamp */ + for (i = 1; i < mp_ncpus; i++) + for (j = 0; j <= CPU_PWR_CLAMP_STEPS; j++) { + if (soc_family != ALLWINNERSOC_SUN7I) { + bus_space_write_4(fdtbus_bs_tag, prcm, + CPU_PWR_CLAMP(i), 0xff >> j); + } else { + bus_space_write_4(fdtbus_bs_tag, + cpucfg, A20_CPU1_PWR_CLAMP, 0xff >> j); + } + } + DELAY(10000); + + /* Clear power-off gating */ + if (soc_family != ALLWINNERSOC_SUN7I) { + val = bus_space_read_4(fdtbus_bs_tag, prcm, CPU_PWROFF); + for (i = 0; i < mp_ncpus; i++) + val &= ~(1 << i); + bus_space_write_4(fdtbus_bs_tag, prcm, CPU_PWROFF, val); + } else { + val = bus_space_read_4(fdtbus_bs_tag, + cpucfg, A20_CPU1_PWROFF_REG); + val &= ~(1 << 0); + bus_space_write_4(fdtbus_bs_tag, cpucfg, + A20_CPU1_PWROFF_REG, val); + } + DELAY(1000); + + /* De-assert cpu core reset */ + for (i = 1; i < mp_ncpus; i++) + bus_space_write_4(fdtbus_bs_tag, cpucfg, CPU_RST_CTL(i), + CPU_RESET | CPU_CORE_RESET); + + /* Assert DBGPWRDUP signal */ + val = bus_space_read_4(fdtbus_bs_tag, cpucfg, CPUCFG_DBGCTL1); + for (i = 1; i < mp_ncpus; i++) + val |= (1 << i); + bus_space_write_4(fdtbus_bs_tag, cpucfg, CPUCFG_DBGCTL1, val); + + dsb(); + sev(); + bus_space_unmap(fdtbus_bs_tag, cpucfg, CPUCFG_SIZE); + if (soc_family != ALLWINNERSOC_SUN7I) + bus_space_unmap(fdtbus_bs_tag, prcm, PRCM_SIZE); +} + +static void +aw_mc_mp_start_cpu(bus_space_handle_t cpuscfg, bus_space_handle_t cpuxcfg, + bus_space_handle_t prcm, int cluster, int cpu) +{ + uint32_t val; + int i; + + /* Assert core reset */ + val = bus_space_read_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_RST(cluster)); + val &= ~(1 << cpu); + bus_space_write_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_RST(cluster), val); + + /* Assert power-on reset */ + val = bus_space_read_4(fdtbus_bs_tag, cpuscfg, CPUS_CL_RST(cluster)); + val &= ~(1 << cpu); + bus_space_write_4(fdtbus_bs_tag, cpuscfg, CPUS_CL_RST(cluster), val); + + /* Disable automatic L1 cache invalidate at reset */ + val = bus_space_read_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_CTRL0(cluster)); + val &= ~(1 << cpu); + bus_space_write_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_CTRL0(cluster), val); + + /* Release power clamp */ + for (i = 0; i <= CPU_PWR_CLAMP_STEPS; i++) + bus_space_write_4(fdtbus_bs_tag, prcm, + PRCM_CL_PWR_CLAMP(cluster, cpu), 0xff >> i); + while (bus_space_read_4(fdtbus_bs_tag, prcm, + PRCM_CL_PWR_CLAMP(cluster, cpu)) != 0) + ; + + /* Clear power-off gating */ + val = bus_space_read_4(fdtbus_bs_tag, prcm, PRCM_CL_PWROFF(cluster)); + val &= ~(1 << cpu); + bus_space_write_4(fdtbus_bs_tag, prcm, PRCM_CL_PWROFF(cluster), val); + + /* De-assert power-on reset */ + val = bus_space_read_4(fdtbus_bs_tag, cpuscfg, CPUS_CL_RST(cluster)); + val |= (1 << cpu); + bus_space_write_4(fdtbus_bs_tag, cpuscfg, CPUS_CL_RST(cluster), val); + + /* De-assert core reset */ + val = bus_space_read_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_RST(cluster)); + val |= (1 << cpu); + bus_space_write_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_RST(cluster), val); +} + +static void +aw_mc_mp_start_ap(bus_space_handle_t cpuscfg, bus_space_handle_t cpuxcfg, + bus_space_handle_t prcm) +{ + int cluster, cpu; + + KASSERT(mp_ncpus <= 4, ("multiple clusters not yet supported")); + + dcache_wbinv_poc_all(); + + bus_space_write_4(fdtbus_bs_tag, cpuscfg, CPUCFG_P_REG0, + pmap_kextract((vm_offset_t)mpentry)); + + cluster = 0; + for (cpu = 1; cpu < mp_ncpus; cpu++) + aw_mc_mp_start_cpu(cpuscfg, cpuxcfg, prcm, cluster, cpu); +} + +void +a83t_mp_start_ap(platform_t plat) +{ + bus_space_handle_t cpuscfg, cpuxcfg, prcm; + + if (bus_space_map(fdtbus_bs_tag, CPUCFG_BASE, CPUCFG_SIZE, + 0, &cpuscfg) != 0) + panic("Couldn't map the CPUCFG\n"); + if (bus_space_map(fdtbus_bs_tag, CPUXCFG_BASE, CPUXCFG_SIZE, + 0, &cpuxcfg) != 0) + panic("Couldn't map the CPUXCFG\n"); + if (bus_space_map(fdtbus_bs_tag, PRCM_BASE, PRCM_SIZE, 0, + &prcm) != 0) + panic("Couldn't map the PRCM\n"); + + aw_mc_mp_start_ap(cpuscfg, cpuxcfg, prcm); + dsb(); + sev(); + bus_space_unmap(fdtbus_bs_tag, cpuxcfg, CPUXCFG_SIZE); + bus_space_unmap(fdtbus_bs_tag, cpuscfg, CPUCFG_SIZE); + bus_space_unmap(fdtbus_bs_tag, prcm, PRCM_SIZE); +} Property changes on: trunk/sys/arm/allwinner/aw_mp.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_mp.h =================================================================== --- trunk/sys/arm/allwinner/aw_mp.h (rev 0) +++ trunk/sys/arm/allwinner/aw_mp.h 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,36 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Emmanuel Vadot + * 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: stable/11/sys/arm/allwinner/aw_mp.h 331182 2018-03-19 06:40:11Z eadler $ + */ + +#ifndef _AW_MP_H_ +#define _AW_MP_H_ + +void aw_mp_setmaxid(platform_t plat); +void aw_mp_start_ap(platform_t plat); +void a83t_mp_start_ap(platform_t plat); + +#endif /* _AW_MP_H_ */ Property changes on: trunk/sys/arm/allwinner/aw_mp.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_nmi.c =================================================================== --- trunk/sys/arm/allwinner/aw_nmi.c (rev 0) +++ trunk/sys/arm/allwinner/aw_nmi.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,405 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/aw_nmi.c 309765 2016-12-09 20:48:47Z manu $"); + +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "pic_if.h" + +#define NMI_IRQ_CTRL_REG 0x0 +#define NMI_IRQ_LOW_LEVEL 0x0 +#define NMI_IRQ_LOW_EDGE 0x1 +#define NMI_IRQ_HIGH_LEVEL 0x2 +#define NMI_IRQ_HIGH_EDGE 0x3 +#define NMI_IRQ_PENDING_REG 0x4 +#define NMI_IRQ_ACK (1U << 0) +#define A20_NMI_IRQ_ENABLE_REG 0x8 +#define A31_NMI_IRQ_ENABLE_REG 0x34 +#define NMI_IRQ_ENABLE (1U << 0) + +#define SC_NMI_READ(_sc, _reg) bus_read_4(_sc->res[0], _reg) +#define SC_NMI_WRITE(_sc, _reg, _val) bus_write_4(_sc->res[0], _reg, _val) + +static struct resource_spec aw_nmi_res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0, 0 } +}; + +struct aw_nmi_intr { + struct intr_irqsrc isrc; + u_int irq; + enum intr_polarity pol; + enum intr_trigger tri; +}; + +struct aw_nmi_softc { + device_t dev; + struct resource * res[2]; + void * intrcookie; + struct aw_nmi_intr intr; + uint8_t enable_reg; +}; + +#define A20_NMI 1 +#define A31_NMI 2 + +static struct ofw_compat_data compat_data[] = { + {"allwinner,sun7i-a20-sc-nmi", A20_NMI}, + {"allwinner,sun6i-a31-sc-nmi", A31_NMI}, + + {NULL, 0}, +}; + +static int +aw_nmi_intr(void *arg) +{ + struct aw_nmi_softc *sc; + + sc = arg; + + if (SC_NMI_READ(sc, NMI_IRQ_PENDING_REG) == 0) { + device_printf(sc->dev, "Spurious interrupt\n"); + return (FILTER_HANDLED); + } + + if (intr_isrc_dispatch(&sc->intr.isrc, curthread->td_intr_frame) != 0) { + SC_NMI_WRITE(sc, sc->enable_reg, !NMI_IRQ_ENABLE); + device_printf(sc->dev, "Stray interrupt, NMI disabled\n"); + } + + return (FILTER_HANDLED); +} + +static void +aw_nmi_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct aw_nmi_softc *sc; + + sc = device_get_softc(dev); + + SC_NMI_WRITE(sc, sc->enable_reg, NMI_IRQ_ENABLE); +} + +static void +aw_nmi_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct aw_nmi_softc *sc; + + sc = device_get_softc(dev); + + SC_NMI_WRITE(sc, sc->enable_reg, !NMI_IRQ_ENABLE); +} + +static int +aw_nmi_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp, + enum intr_polarity *polp, enum intr_trigger *trigp) +{ + u_int irq, tripol; + enum intr_polarity pol; + enum intr_trigger trig; + + if (ncells != 2) { + device_printf(dev, "Invalid #interrupt-cells\n"); + return (EINVAL); + } + + irq = cells[0]; + if (irq != 0) { + device_printf(dev, "Controller only support irq 0\n"); + return (EINVAL); + } + + tripol = cells[1]; + + switch (tripol) { + case IRQ_TYPE_EDGE_RISING: + trig = INTR_TRIGGER_EDGE; + pol = INTR_POLARITY_HIGH; + break; + case IRQ_TYPE_EDGE_FALLING: + trig = INTR_TRIGGER_EDGE; + pol = INTR_POLARITY_LOW; + break; + case IRQ_TYPE_LEVEL_HIGH: + trig = INTR_TRIGGER_LEVEL; + pol = INTR_POLARITY_HIGH; + break; + case IRQ_TYPE_LEVEL_LOW: + trig = INTR_TRIGGER_LEVEL; + pol = INTR_POLARITY_LOW; + break; + default: + device_printf(dev, "unsupported trigger/polarity 0x%2x\n", + tripol); + return (ENOTSUP); + } + + *irqp = irq; + if (polp != NULL) + *polp = pol; + if (trigp != NULL) + *trigp = trig; + return (0); +} + +static int +aw_nmi_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct intr_map_data_fdt *daf; + struct aw_nmi_softc *sc; + int error; + u_int irq; + + if (data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + + sc = device_get_softc(dev); + daf = (struct intr_map_data_fdt *)data; + + error = aw_nmi_map_fdt(dev, daf->ncells, daf->cells, &irq, NULL, NULL); + if (error == 0) + *isrcp = &sc->intr.isrc; + + return (error); +} + +static int +aw_nmi_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct intr_map_data_fdt *daf; + struct aw_nmi_softc *sc; + struct aw_nmi_intr *nmi_intr; + int error, icfg; + u_int irq; + enum intr_trigger trig; + enum intr_polarity pol; + + /* Get config for interrupt. */ + if (data == NULL || data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + + sc = device_get_softc(dev); + nmi_intr = (struct aw_nmi_intr *)isrc; + daf = (struct intr_map_data_fdt *)data; + + error = aw_nmi_map_fdt(dev, daf->ncells, daf->cells, &irq, &pol, &trig); + if (error != 0) + return (error); + if (nmi_intr->irq != irq) + return (EINVAL); + + /* Compare config if this is not first setup. */ + if (isrc->isrc_handlers != 0) { + if (pol != nmi_intr->pol || trig != nmi_intr->tri) + return (EINVAL); + else + return (0); + } + + nmi_intr->pol = pol; + nmi_intr->tri = trig; + + if (trig == INTR_TRIGGER_LEVEL) { + if (pol == INTR_POLARITY_LOW) + icfg = NMI_IRQ_LOW_LEVEL; + else + icfg = NMI_IRQ_HIGH_LEVEL; + } else { + if (pol == INTR_POLARITY_HIGH) + icfg = NMI_IRQ_HIGH_EDGE; + else + icfg = NMI_IRQ_LOW_EDGE; + } + + SC_NMI_WRITE(sc, NMI_IRQ_CTRL_REG, icfg); + + return (0); +} + +static int +aw_nmi_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct aw_nmi_softc *sc; + + sc = device_get_softc(dev); + + if (isrc->isrc_handlers == 0) { + sc->intr.pol = INTR_POLARITY_CONFORM; + sc->intr.tri = INTR_TRIGGER_CONFORM; + + SC_NMI_WRITE(sc, sc->enable_reg, !NMI_IRQ_ENABLE); + } + + return (0); +} + +static void +aw_nmi_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct aw_nmi_softc *sc; + + sc = device_get_softc(dev); + aw_nmi_disable_intr(dev, isrc); + SC_NMI_WRITE(sc, NMI_IRQ_PENDING_REG, NMI_IRQ_ACK); +} + +static void +aw_nmi_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + + arm_irq_memory_barrier(0); + aw_nmi_enable_intr(dev, isrc); +} + +static void +aw_nmi_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct aw_nmi_softc *sc; + + sc = device_get_softc(dev); + + arm_irq_memory_barrier(0); + SC_NMI_WRITE(sc, NMI_IRQ_PENDING_REG, NMI_IRQ_ACK); +} + +static int +aw_nmi_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, "Allwinner NMI Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_nmi_attach(device_t dev) +{ + struct aw_nmi_softc *sc; + phandle_t xref; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (bus_alloc_resources(dev, aw_nmi_res_spec, sc->res) != 0) { + device_printf(dev, "can't allocate device resources\n"); + return (ENXIO); + } + if ((bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC, + aw_nmi_intr, NULL, sc, &sc->intrcookie))) { + device_printf(dev, "unable to register interrupt handler\n"); + bus_release_resources(dev, aw_nmi_res_spec, sc->res); + return (ENXIO); + } + + switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { + case A20_NMI: + sc->enable_reg = A20_NMI_IRQ_ENABLE_REG; + break; + case A31_NMI: + sc->enable_reg = A31_NMI_IRQ_ENABLE_REG; + break; + } + + /* Disable and clear interrupts */ + SC_NMI_WRITE(sc, sc->enable_reg, !NMI_IRQ_ENABLE); + SC_NMI_WRITE(sc, NMI_IRQ_PENDING_REG, NMI_IRQ_ACK); + + xref = OF_xref_from_node(ofw_bus_get_node(dev)); + /* Register our isrc */ + sc->intr.irq = 0; + sc->intr.pol = INTR_POLARITY_CONFORM; + sc->intr.tri = INTR_TRIGGER_CONFORM; + if (intr_isrc_register(&sc->intr.isrc, sc->dev, 0, "%s,%u", + device_get_nameunit(sc->dev), sc->intr.irq) != 0) + goto error; + + if (intr_pic_register(dev, (intptr_t)xref) == NULL) { + device_printf(dev, "could not register pic\n"); + goto error; + } + return (0); + +error: + bus_teardown_intr(dev, sc->res[1], sc->intrcookie); + bus_release_resources(dev, aw_nmi_res_spec, sc->res); + return (ENXIO); +} + +static device_method_t aw_nmi_methods[] = { + DEVMETHOD(device_probe, aw_nmi_probe), + DEVMETHOD(device_attach, aw_nmi_attach), + + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, aw_nmi_disable_intr), + DEVMETHOD(pic_enable_intr, aw_nmi_enable_intr), + DEVMETHOD(pic_map_intr, aw_nmi_map_intr), + DEVMETHOD(pic_setup_intr, aw_nmi_setup_intr), + DEVMETHOD(pic_teardown_intr, aw_nmi_teardown_intr), + DEVMETHOD(pic_post_filter, aw_nmi_post_filter), + DEVMETHOD(pic_post_ithread, aw_nmi_post_ithread), + DEVMETHOD(pic_pre_ithread, aw_nmi_pre_ithread), + + {0, 0}, +}; + +static driver_t aw_nmi_driver = { + "aw_nmi", + aw_nmi_methods, + sizeof(struct aw_nmi_softc), +}; + +static devclass_t aw_nmi_devclass; + +EARLY_DRIVER_MODULE(aw_nmi, simplebus, aw_nmi_driver, + aw_nmi_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); Property changes on: trunk/sys/arm/allwinner/aw_nmi.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_reset.c =================================================================== --- trunk/sys/arm/allwinner/aw_reset.c (rev 0) +++ trunk/sys/arm/allwinner/aw_reset.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,165 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/aw_reset.c 297725 2016-04-08 23:07:16Z jmcneill $ + */ + +/* + * Allwinner module software reset registers + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/aw_reset.c 297725 2016-04-08 23:07:16Z jmcneill $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "hwreset_if.h" + +#define RESET_OFFSET(index) ((index / 32) * 4) +#define RESET_SHIFT(index) (index % 32) + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun6i-a31-ahb1-reset", 1 }, + { "allwinner,sun6i-a31-clock-reset", 1 }, + { NULL, 0 } +}; + +struct aw_reset_softc { + struct resource *res; + struct mtx mtx; +}; + +static struct resource_spec aw_reset_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define RESET_READ(sc, reg) bus_read_4((sc)->res, (reg)) +#define RESET_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) + +static int +aw_reset_assert(device_t dev, intptr_t id, bool reset) +{ + struct aw_reset_softc *sc; + uint32_t reg_value; + + sc = device_get_softc(dev); + + mtx_lock(&sc->mtx); + reg_value = RESET_READ(sc, RESET_OFFSET(id)); + if (reset) + reg_value &= ~(1 << RESET_SHIFT(id)); + else + reg_value |= (1 << RESET_SHIFT(id)); + RESET_WRITE(sc, RESET_OFFSET(id), reg_value); + mtx_unlock(&sc->mtx); + + return (0); +} + +static int +aw_reset_is_asserted(device_t dev, intptr_t id, bool *reset) +{ + struct aw_reset_softc *sc; + uint32_t reg_value; + + sc = device_get_softc(dev); + + mtx_lock(&sc->mtx); + reg_value = RESET_READ(sc, RESET_OFFSET(id)); + mtx_unlock(&sc->mtx); + + *reset = (reg_value & (1 << RESET_SHIFT(id))) != 0 ? false : true; + + return (0); +} + +static int +aw_reset_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, "Allwinner Module Resets"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_reset_attach(device_t dev) +{ + struct aw_reset_softc *sc; + + sc = device_get_softc(dev); + + if (bus_alloc_resources(dev, aw_reset_spec, &sc->res) != 0) { + device_printf(dev, "cannot allocate resources for device\n"); + return (ENXIO); + } + + mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + hwreset_register_ofw_provider(dev); + + return (0); +} + +static device_method_t aw_reset_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_reset_probe), + DEVMETHOD(device_attach, aw_reset_attach), + + /* Reset interface */ + DEVMETHOD(hwreset_assert, aw_reset_assert), + DEVMETHOD(hwreset_is_asserted, aw_reset_is_asserted), + + DEVMETHOD_END +}; + +static driver_t aw_reset_driver = { + "aw_reset", + aw_reset_methods, + sizeof(struct aw_reset_softc), +}; + +static devclass_t aw_reset_devclass; + +EARLY_DRIVER_MODULE(aw_reset, simplebus, aw_reset_driver, aw_reset_devclass, + 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(aw_reset, 1); Property changes on: trunk/sys/arm/allwinner/aw_reset.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_rsb.c =================================================================== --- trunk/sys/arm/allwinner/aw_rsb.c (rev 0) +++ trunk/sys/arm/allwinner/aw_rsb.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,480 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/aw_rsb.c 309763 2016-12-09 20:37:34Z manu $ + */ + +/* + * Allwinner RSB (Reduced Serial Bus) + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/aw_rsb.c 309763 2016-12-09 20:37:34Z manu $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include "iicbus_if.h" + +#define RSB_CTRL 0x00 +#define START_TRANS (1 << 7) +#define GLOBAL_INT_ENB (1 << 1) +#define SOFT_RESET (1 << 0) +#define RSB_CCR 0x04 +#define RSB_INTE 0x08 +#define RSB_INTS 0x0c +#define INT_TRANS_ERR_ID(x) (((x) >> 8) & 0xf) +#define INT_LOAD_BSY (1 << 2) +#define INT_TRANS_ERR (1 << 1) +#define INT_TRANS_OVER (1 << 0) +#define INT_MASK (INT_LOAD_BSY|INT_TRANS_ERR|INT_TRANS_OVER) +#define RSB_DADDR0 0x10 +#define RSB_DADDR1 0x14 +#define RSB_DLEN 0x18 +#define DLEN_READ (1 << 4) +#define RSB_DATA0 0x1c +#define RSB_DATA1 0x20 +#define RSB_CMD 0x2c +#define CMD_SRTA 0xe8 +#define CMD_RD8 0x8b +#define CMD_RD16 0x9c +#define CMD_RD32 0xa6 +#define CMD_WR8 0x4e +#define CMD_WR16 0x59 +#define CMD_WR32 0x63 +#define RSB_DAR 0x30 +#define DAR_RTA (0xff << 16) +#define DAR_RTA_SHIFT 16 +#define DAR_DA (0xffff << 0) +#define DAR_DA_SHIFT 0 + +#define RSB_MAXLEN 8 +#define RSB_RESET_RETRY 100 +#define RSB_I2C_TIMEOUT hz + +#define RSB_ADDR_PMIC_PRIMARY 0x3a3 +#define RSB_ADDR_PMIC_SECONDARY 0x745 +#define RSB_ADDR_PERIPH_IC 0xe89 + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun8i-a23-rsb", 1 }, + { NULL, 0 } +}; + +static struct resource_spec rsb_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +/* + * Device address to Run-time address mappings. + * + * Run-time address (RTA) is an 8-bit value used to address the device during + * a read or write transaction. The following are valid RTAs: + * 0x17 0x2d 0x3a 0x4e 0x59 0x63 0x74 0x8b 0x9c 0xa6 0xb1 0xc5 0xd2 0xe8 0xff + * + * Allwinner uses RTA 0x2d for the primary PMIC, 0x3a for the secondary PMIC, + * and 0x4e for the peripheral IC (where applicable). + */ +static const struct { + uint16_t addr; + uint8_t rta; +} rsb_rtamap[] = { + { .addr = RSB_ADDR_PMIC_PRIMARY, .rta = 0x2d }, + { .addr = RSB_ADDR_PMIC_SECONDARY, .rta = 0x3a }, + { .addr = RSB_ADDR_PERIPH_IC, .rta = 0x4e }, + { .addr = 0, .rta = 0 } +}; + +struct rsb_softc { + struct resource *res; + struct mtx mtx; + clk_t clk; + hwreset_t rst; + device_t iicbus; + int busy; + uint32_t status; + uint16_t cur_addr; + + struct iic_msg *msg; +}; + +#define RSB_LOCK(sc) mtx_lock(&(sc)->mtx) +#define RSB_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define RSB_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) +#define RSB_READ(sc, reg) bus_read_4((sc)->res, (reg)) +#define RSB_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) + +static phandle_t +rsb_get_node(device_t bus, device_t dev) +{ + return (ofw_bus_get_node(bus)); +} + +static int +rsb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) +{ + struct rsb_softc *sc; + int retry; + + sc = device_get_softc(dev); + + RSB_LOCK(sc); + + /* Write soft-reset bit and wait for it to self-clear. */ + RSB_WRITE(sc, RSB_CTRL, SOFT_RESET); + for (retry = RSB_RESET_RETRY; retry > 0; retry--) + if ((RSB_READ(sc, RSB_CTRL) & SOFT_RESET) == 0) + break; + + RSB_UNLOCK(sc); + + if (retry == 0) { + device_printf(dev, "soft reset timeout\n"); + return (ETIMEDOUT); + } + + return (IIC_ENOADDR); +} + +static uint32_t +rsb_encode(const uint8_t *buf, u_int len, u_int off) +{ + uint32_t val; + u_int n; + + val = 0; + for (n = off; n < MIN(len, 4 + off); n++) + val |= ((uint32_t)buf[n] << ((n - off) * NBBY)); + + return val; +} + +static void +rsb_decode(const uint32_t val, uint8_t *buf, u_int len, u_int off) +{ + u_int n; + + for (n = off; n < MIN(len, 4 + off); n++) + buf[n] = (val >> ((n - off) * NBBY)) & 0xff; +} + +static int +rsb_start(device_t dev) +{ + struct rsb_softc *sc; + int error, retry; + + sc = device_get_softc(dev); + + RSB_ASSERT_LOCKED(sc); + + /* Start the transfer */ + RSB_WRITE(sc, RSB_CTRL, GLOBAL_INT_ENB | START_TRANS); + + /* Wait for transfer to complete */ + error = ETIMEDOUT; + for (retry = RSB_I2C_TIMEOUT; retry > 0; retry--) { + sc->status |= RSB_READ(sc, RSB_INTS); + if ((sc->status & INT_TRANS_OVER) != 0) { + error = 0; + break; + } + DELAY((1000 * hz) / RSB_I2C_TIMEOUT); + } + if (error == 0 && (sc->status & INT_TRANS_OVER) == 0) { + device_printf(dev, "transfer error, status 0x%08x\n", + sc->status); + error = EIO; + } + + return (error); + +} + +static int +rsb_set_rta(device_t dev, uint16_t addr) +{ + struct rsb_softc *sc; + uint8_t rta; + int i; + + sc = device_get_softc(dev); + + RSB_ASSERT_LOCKED(sc); + + /* Lookup run-time address for given device address */ + for (rta = 0, i = 0; rsb_rtamap[i].rta != 0; i++) + if (rsb_rtamap[i].addr == addr) { + rta = rsb_rtamap[i].rta; + break; + } + if (rta == 0) { + device_printf(dev, "RTA not known for address %#x\n", addr); + return (ENXIO); + } + + /* Set run-time address */ + RSB_WRITE(sc, RSB_INTS, RSB_READ(sc, RSB_INTS)); + RSB_WRITE(sc, RSB_DAR, (addr << DAR_DA_SHIFT) | (rta << DAR_RTA_SHIFT)); + RSB_WRITE(sc, RSB_CMD, CMD_SRTA); + + return (rsb_start(dev)); +} + +static int +rsb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) +{ + struct rsb_softc *sc; + uint32_t daddr[2], data[2], dlen; + uint16_t device_addr; + uint8_t cmd; + int error; + + sc = device_get_softc(dev); + + /* + * RSB is not really an I2C or SMBus controller, so there are some + * restrictions imposed by the driver. + * + * Transfers must contain exactly two messages. The first is always + * a write, containing a single data byte offset. Data will either + * be read from or written to the corresponding data byte in the + * second message. The slave address in both messages must be the + * same. + */ + if (nmsgs != 2 || (msgs[0].flags & IIC_M_RD) == IIC_M_RD || + (msgs[0].slave >> 1) != (msgs[1].slave >> 1) || + msgs[0].len != 1 || msgs[1].len > RSB_MAXLEN) + return (EINVAL); + + /* The controller can read or write 1, 2, or 4 bytes at a time. */ + if ((msgs[1].flags & IIC_M_RD) != 0) { + switch (msgs[1].len) { + case 1: + cmd = CMD_RD8; + break; + case 2: + cmd = CMD_RD16; + break; + case 4: + cmd = CMD_RD32; + break; + default: + return (EINVAL); + } + } else { + switch (msgs[1].len) { + case 1: + cmd = CMD_WR8; + break; + case 2: + cmd = CMD_WR16; + break; + case 4: + cmd = CMD_WR32; + break; + default: + return (EINVAL); + } + } + + RSB_LOCK(sc); + while (sc->busy) + mtx_sleep(sc, &sc->mtx, 0, "i2cbuswait", 0); + sc->busy = 1; + sc->status = 0; + + /* Select current run-time address if necessary */ + device_addr = msgs[0].slave >> 1; + if (sc->cur_addr != device_addr) { + error = rsb_set_rta(dev, device_addr); + if (error != 0) + goto done; + sc->cur_addr = device_addr; + sc->status = 0; + } + + /* Clear interrupt status */ + RSB_WRITE(sc, RSB_INTS, RSB_READ(sc, RSB_INTS)); + + /* Program data access address registers */ + daddr[0] = rsb_encode(msgs[0].buf, msgs[0].len, 0); + RSB_WRITE(sc, RSB_DADDR0, daddr[0]); + + /* Write data */ + if ((msgs[1].flags & IIC_M_RD) == 0) { + data[0] = rsb_encode(msgs[1].buf, msgs[1].len, 0); + RSB_WRITE(sc, RSB_DATA0, data[0]); + } + + /* Set command type */ + RSB_WRITE(sc, RSB_CMD, cmd); + + /* Program data length register and transfer direction */ + dlen = msgs[0].len - 1; + if ((msgs[1].flags & IIC_M_RD) == IIC_M_RD) + dlen |= DLEN_READ; + RSB_WRITE(sc, RSB_DLEN, dlen); + + /* Start transfer */ + error = rsb_start(dev); + if (error != 0) + goto done; + + /* Read data */ + if ((msgs[1].flags & IIC_M_RD) == IIC_M_RD) { + data[0] = RSB_READ(sc, RSB_DATA0); + rsb_decode(data[0], msgs[1].buf, msgs[1].len, 0); + } + +done: + sc->msg = NULL; + sc->busy = 0; + wakeup(sc); + RSB_UNLOCK(sc); + + return (error); +} + +static int +rsb_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, "Allwinner RSB"); + return (BUS_PROBE_DEFAULT); +} + +static int +rsb_attach(device_t dev) +{ + struct rsb_softc *sc; + int error; + + sc = device_get_softc(dev); + mtx_init(&sc->mtx, device_get_nameunit(dev), "rsb", MTX_DEF); + + if (clk_get_by_ofw_index(dev, 0, 0, &sc->clk) == 0) { + error = clk_enable(sc->clk); + if (error != 0) { + device_printf(dev, "cannot enable clock\n"); + goto fail; + } + } + if (hwreset_get_by_ofw_idx(dev, 0, 0, &sc->rst) == 0) { + error = hwreset_deassert(sc->rst); + if (error != 0) { + device_printf(dev, "cannot de-assert reset\n"); + goto fail; + } + } + + if (bus_alloc_resources(dev, rsb_spec, &sc->res) != 0) { + device_printf(dev, "cannot allocate resources for device\n"); + error = ENXIO; + goto fail; + } + + sc->iicbus = device_add_child(dev, "iicbus", -1); + if (sc->iicbus == NULL) { + device_printf(dev, "cannot add iicbus child device\n"); + error = ENXIO; + goto fail; + } + + bus_generic_attach(dev); + + return (0); + +fail: + bus_release_resources(dev, rsb_spec, &sc->res); + if (sc->rst != NULL) + hwreset_release(sc->rst); + if (sc->clk != NULL) + clk_release(sc->clk); + mtx_destroy(&sc->mtx); + return (error); +} + +static device_method_t rsb_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rsb_probe), + DEVMETHOD(device_attach, rsb_attach), + + /* 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, rsb_get_node), + + /* iicbus interface */ + DEVMETHOD(iicbus_callback, iicbus_null_callback), + DEVMETHOD(iicbus_reset, rsb_reset), + DEVMETHOD(iicbus_transfer, rsb_transfer), + + DEVMETHOD_END +}; + +static driver_t rsb_driver = { + "iichb", + rsb_methods, + sizeof(struct rsb_softc), +}; + +static devclass_t rsb_devclass; + +EARLY_DRIVER_MODULE(iicbus, rsb, iicbus_driver, iicbus_devclass, 0, 0, + BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); +EARLY_DRIVER_MODULE(rsb, simplebus, rsb_driver, rsb_devclass, 0, 0, + BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(rsb, 1); Property changes on: trunk/sys/arm/allwinner/aw_rsb.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_rtc.c =================================================================== --- trunk/sys/arm/allwinner/aw_rtc.c (rev 0) +++ trunk/sys/arm/allwinner/aw_rtc.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,299 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Vladimir Belian + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/aw_rtc.c 309754 2016-12-09 19:40:33Z manu $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include "clock_if.h" + +#define LOSC_CTRL_REG 0x00 +#define A10_RTC_DATE_REG 0x04 +#define A10_RTC_TIME_REG 0x08 +#define A31_LOSC_AUTO_SWT_STA 0x04 +#define A31_RTC_DATE_REG 0x10 +#define A31_RTC_TIME_REG 0x14 + +#define TIME_MASK 0x001f3f3f + +#define LOSC_OSC_SRC (1 << 0) +#define LOSC_GSM (1 << 3) +#define LOSC_AUTO_SW_EN (1 << 14) +#define LOSC_MAGIC 0x16aa0000 +#define LOSC_BUSY_MASK 0x00000380 + +#define IS_SUN7I (sc->type == A20_RTC) + +#define YEAR_MIN (IS_SUN7I ? 1970 : 2010) +#define YEAR_MAX (IS_SUN7I ? 2100 : 2073) +#define YEAR_OFFSET (IS_SUN7I ? 1900 : 2010) +#define YEAR_MASK (IS_SUN7I ? 0xff : 0x3f) +#define LEAP_BIT (IS_SUN7I ? 24 : 22) + +#define GET_SEC_VALUE(x) ((x) & 0x0000003f) +#define GET_MIN_VALUE(x) (((x) & 0x00003f00) >> 8) +#define GET_HOUR_VALUE(x) (((x) & 0x001f0000) >> 16) +#define GET_DAY_VALUE(x) ((x) & 0x0000001f) +#define GET_MON_VALUE(x) (((x) & 0x00000f00) >> 8) +#define GET_YEAR_VALUE(x) (((x) >> 16) & YEAR_MASK) + +#define SET_DAY_VALUE(x) GET_DAY_VALUE(x) +#define SET_MON_VALUE(x) (((x) & 0x0000000f) << 8) +#define SET_YEAR_VALUE(x) (((x) & YEAR_MASK) << 16) +#define SET_LEAP_VALUE(x) (((x) & 0x00000001) << LEAP_BIT) +#define SET_SEC_VALUE(x) GET_SEC_VALUE(x) +#define SET_MIN_VALUE(x) (((x) & 0x0000003f) << 8) +#define SET_HOUR_VALUE(x) (((x) & 0x0000001f) << 16) + +#define HALF_OF_SEC_NS 500000000 +#define RTC_RES_US 1000000 +#define RTC_TIMEOUT 70 + +#define RTC_READ(sc, reg) bus_read_4((sc)->res, (reg)) +#define RTC_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) + +#define IS_LEAP_YEAR(y) \ + (((y) % 400) == 0 || (((y) % 100) != 0 && ((y) % 4) == 0)) + +#define A10_RTC 1 +#define A20_RTC 2 +#define A31_RTC 3 + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-rtc", A10_RTC }, + { "allwinner,sun7i-a20-rtc", A20_RTC }, + { "allwinner,sun6i-a31-rtc", A31_RTC }, + { NULL, 0 } +}; + +struct aw_rtc_softc { + struct resource *res; + int type; + bus_size_t rtc_date; + bus_size_t rtc_time; +}; + +static int aw_rtc_probe(device_t dev); +static int aw_rtc_attach(device_t dev); +static int aw_rtc_detach(device_t dev); + +static int aw_rtc_gettime(device_t dev, struct timespec *ts); +static int aw_rtc_settime(device_t dev, struct timespec *ts); + +static device_method_t aw_rtc_methods[] = { + DEVMETHOD(device_probe, aw_rtc_probe), + DEVMETHOD(device_attach, aw_rtc_attach), + DEVMETHOD(device_detach, aw_rtc_detach), + + DEVMETHOD(clock_gettime, aw_rtc_gettime), + DEVMETHOD(clock_settime, aw_rtc_settime), + + DEVMETHOD_END +}; + +static driver_t aw_rtc_driver = { + "rtc", + aw_rtc_methods, + sizeof(struct aw_rtc_softc), +}; + +static devclass_t aw_rtc_devclass; + +EARLY_DRIVER_MODULE(aw_rtc, simplebus, aw_rtc_driver, aw_rtc_devclass, 0, 0, + BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); + + +static int +aw_rtc_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, "Allwinner RTC"); + + return (BUS_PROBE_DEFAULT); +} + +static int +aw_rtc_attach(device_t dev) +{ + struct aw_rtc_softc *sc = device_get_softc(dev); + bus_size_t rtc_losc_sta; + uint32_t val; + int rid = 0; + + sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (!sc->res) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + switch (sc->type) { + case A10_RTC: + case A20_RTC: + sc->rtc_date = A10_RTC_DATE_REG; + sc->rtc_time = A10_RTC_TIME_REG; + rtc_losc_sta = LOSC_CTRL_REG; + break; + case A31_RTC: + sc->rtc_date = A31_RTC_DATE_REG; + sc->rtc_time = A31_RTC_TIME_REG; + rtc_losc_sta = A31_LOSC_AUTO_SWT_STA; + break; + } + val = RTC_READ(sc, LOSC_CTRL_REG); + val |= LOSC_AUTO_SW_EN; + val |= LOSC_MAGIC | LOSC_GSM | LOSC_OSC_SRC; + RTC_WRITE(sc, LOSC_CTRL_REG, val); + + DELAY(100); + + if (bootverbose) { + val = RTC_READ(sc, rtc_losc_sta); + if ((val & LOSC_OSC_SRC) == 0) + device_printf(dev, "Using internal oscillator\n"); + else + device_printf(dev, "Using external oscillator\n"); + } + + clock_register(dev, RTC_RES_US); + + return (0); +} + +static int +aw_rtc_detach(device_t dev) +{ + /* can't support detach, since there's no clock_unregister function */ + return (EBUSY); +} + +static int +aw_rtc_gettime(device_t dev, struct timespec *ts) +{ + struct aw_rtc_softc *sc = device_get_softc(dev); + struct clocktime ct; + uint32_t rdate, rtime; + + rdate = RTC_READ(sc, sc->rtc_date); + rtime = RTC_READ(sc, sc->rtc_time); + + if ((rtime & TIME_MASK) == 0) + rdate = RTC_READ(sc, sc->rtc_date); + + ct.sec = GET_SEC_VALUE(rtime); + ct.min = GET_MIN_VALUE(rtime); + ct.hour = GET_HOUR_VALUE(rtime); + ct.day = GET_DAY_VALUE(rdate); + ct.mon = GET_MON_VALUE(rdate); + ct.year = GET_YEAR_VALUE(rdate) + YEAR_OFFSET; + ct.dow = -1; + /* RTC resolution is 1 sec */ + ct.nsec = 0; + + return (clock_ct_to_ts(&ct, ts)); +} + +static int +aw_rtc_settime(device_t dev, struct timespec *ts) +{ + struct aw_rtc_softc *sc = device_get_softc(dev); + struct clocktime ct; + uint32_t clk, rdate, rtime; + + /* RTC resolution is 1 sec */ + if (ts->tv_nsec >= HALF_OF_SEC_NS) + ts->tv_sec++; + ts->tv_nsec = 0; + + clock_ts_to_ct(ts, &ct); + + if ((ct.year < YEAR_MIN) || (ct.year > YEAR_MAX)) { + device_printf(dev, "could not set time, year out of range\n"); + return (EINVAL); + } + + for (clk = 0; RTC_READ(sc, LOSC_CTRL_REG) & LOSC_BUSY_MASK; clk++) { + if (clk > RTC_TIMEOUT) { + device_printf(dev, "could not set time, RTC busy\n"); + return (EINVAL); + } + DELAY(1); + } + /* reset time register to avoid unexpected date increment */ + RTC_WRITE(sc, sc->rtc_time, 0); + + rdate = SET_DAY_VALUE(ct.day) | SET_MON_VALUE(ct.mon) | + SET_YEAR_VALUE(ct.year - YEAR_OFFSET) | + SET_LEAP_VALUE(IS_LEAP_YEAR(ct.year)); + + rtime = SET_SEC_VALUE(ct.sec) | SET_MIN_VALUE(ct.min) | + SET_HOUR_VALUE(ct.hour); + + for (clk = 0; RTC_READ(sc, LOSC_CTRL_REG) & LOSC_BUSY_MASK; clk++) { + if (clk > RTC_TIMEOUT) { + device_printf(dev, "could not set date, RTC busy\n"); + return (EINVAL); + } + DELAY(1); + } + RTC_WRITE(sc, sc->rtc_date, rdate); + + for (clk = 0; RTC_READ(sc, LOSC_CTRL_REG) & LOSC_BUSY_MASK; clk++) { + if (clk > RTC_TIMEOUT) { + device_printf(dev, "could not set time, RTC busy\n"); + return (EINVAL); + } + DELAY(1); + } + RTC_WRITE(sc, sc->rtc_time, rtime); + + DELAY(RTC_TIMEOUT); + + return (0); +} Property changes on: trunk/sys/arm/allwinner/aw_rtc.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_sid.c =================================================================== --- trunk/sys/arm/allwinner/aw_sid.c (rev 0) +++ trunk/sys/arm/allwinner/aw_sid.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,214 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/aw_sid.c 309757 2016-12-09 20:13:31Z manu $ + */ + +/* + * Allwinner secure ID controller + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/aw_sid.c 309757 2016-12-09 20:13:31Z manu $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define SID_SRAM 0x200 +#define SID_THERMAL_CALIB0 (SID_SRAM + 0x34) +#define SID_THERMAL_CALIB1 (SID_SRAM + 0x38) + +#define A10_ROOT_KEY_OFF 0x0 +#define A83T_ROOT_KEY_OFF SID_SRAM + +#define ROOT_KEY_SIZE 4 + +enum sid_type { + A10_SID = 1, + A20_SID, + A83T_SID, +}; + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-sid", A10_SID}, + { "allwinner,sun7i-a20-sid", A20_SID}, + { "allwinner,sun8i-a83t-sid", A83T_SID}, + { NULL, 0 } +}; + +struct aw_sid_softc { + struct resource *res; + int type; + bus_size_t root_key_off; +}; + +static struct aw_sid_softc *aw_sid_sc; + +static struct resource_spec aw_sid_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +enum sid_keys { + AW_SID_ROOT_KEY, +}; + +#define RD4(sc, reg) bus_read_4((sc)->res, (reg)) +#define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) + +static int aw_sid_sysctl(SYSCTL_HANDLER_ARGS); + +static int +aw_sid_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, "Allwinner Secure ID Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_sid_attach(device_t dev) +{ + struct aw_sid_softc *sc; + + sc = device_get_softc(dev); + + if (bus_alloc_resources(dev, aw_sid_spec, &sc->res) != 0) { + device_printf(dev, "cannot allocate resources for device\n"); + return (ENXIO); + } + + aw_sid_sc = sc; + + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + switch (sc->type) { + case A83T_SID: + sc->root_key_off = A83T_ROOT_KEY_OFF; + break; + default: + sc->root_key_off = A10_ROOT_KEY_OFF; + break; + } + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "rootkey", + CTLTYPE_STRING | CTLFLAG_RD, + dev, AW_SID_ROOT_KEY, aw_sid_sysctl, "A", "Root Key"); + + return (0); +} + +int +aw_sid_read_tscalib(uint32_t *calib0, uint32_t *calib1) +{ + struct aw_sid_softc *sc; + + sc = aw_sid_sc; + if (sc == NULL) + return (ENXIO); + if (sc->type != A83T_SID) + return (ENXIO); + + *calib0 = RD4(sc, SID_THERMAL_CALIB0); + *calib1 = RD4(sc, SID_THERMAL_CALIB1); + + return (0); +} + +int +aw_sid_get_rootkey(u_char *out) +{ + struct aw_sid_softc *sc; + int i; + u_int tmp; + + sc = aw_sid_sc; + if (sc == NULL) + return (ENXIO); + + for (i = 0; i < ROOT_KEY_SIZE ; i++) { + tmp = RD4(aw_sid_sc, aw_sid_sc->root_key_off + (i * 4)); + be32enc(&out[i * 4], tmp); + } + + return (0); +} + +static int +aw_sid_sysctl(SYSCTL_HANDLER_ARGS) +{ + enum sid_keys key = arg2; + u_char rootkey[16]; + char out[33]; + + if (key != AW_SID_ROOT_KEY) + return (ENOENT); + + if (aw_sid_get_rootkey(rootkey) != 0) + return (ENOENT); + snprintf(out, sizeof(out), + "%16D", rootkey, ""); + + return sysctl_handle_string(oidp, out, sizeof(out), req); +} + +static device_method_t aw_sid_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_sid_probe), + DEVMETHOD(device_attach, aw_sid_attach), + + DEVMETHOD_END +}; + +static driver_t aw_sid_driver = { + "aw_sid", + aw_sid_methods, + sizeof(struct aw_sid_softc), +}; + +static devclass_t aw_sid_devclass; + +EARLY_DRIVER_MODULE(aw_sid, simplebus, aw_sid_driver, aw_sid_devclass, 0, 0, + BUS_PASS_RESOURCE + BUS_PASS_ORDER_FIRST); +MODULE_VERSION(aw_sid, 1); Property changes on: trunk/sys/arm/allwinner/aw_sid.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_sid.h =================================================================== --- trunk/sys/arm/allwinner/aw_sid.h (rev 0) +++ trunk/sys/arm/allwinner/aw_sid.h 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,36 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/aw_sid.h 305440 2016-09-05 21:11:27Z manu $ + */ + +#ifndef __AW_SID_H__ +#define __AW_SID_H__ + +int aw_sid_read_tscalib(uint32_t *, uint32_t *); +int aw_sid_get_rootkey(u_char *out); + +#endif /* !__AW_SID_H__ */ Property changes on: trunk/sys/arm/allwinner/aw_sid.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_thermal.c =================================================================== --- trunk/sys/arm/allwinner/aw_thermal.c (rev 0) +++ trunk/sys/arm/allwinner/aw_thermal.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,233 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/aw_thermal.c 299871 2016-05-15 22:36:55Z jmcneill $ + */ + +/* + * Allwinner thermal sensor controller + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/aw_thermal.c 299871 2016-05-15 22:36:55Z jmcneill $"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define THS_CTRL0 0x00 +#define THS_CTRL2 0x40 +#define SENSOR_ACQ1_SHIFT 16 +#define SENSOR2_EN (1 << 2) +#define SENSOR1_EN (1 << 1) +#define SENSOR0_EN (1 << 0) +#define THS_INTC 0x44 +#define THS_INTS 0x48 +#define THS_FILTER 0x70 +#define FILTER_EN (1 << 2) +#define THS_CALIB0 0x74 +#define THS_CALIB1 0x78 +#define THS_DATA0 0x80 +#define THS_DATA1 0x84 +#define THS_DATA2 0x88 +#define DATA_MASK 0xfff + +#define TEMP_BASE 2719 +#define TEMP_MUL 1000 +#define TEMP_DIV 14186 +#define TEMP_TO_K 273 +#define ADC_ACQUIRE_TIME (24 - 1) +#define SENSOR_ENABLE_ALL (SENSOR0_EN|SENSOR1_EN|SENSOR2_EN) + +enum aw_thermal_sensor { + THS_SENSOR_CPU_CLUSTER0, + THS_SENSOR_CPU_CLUSTER1, + THS_SENSOR_GPU, + THS_SENSOR_END = -1 +}; + +struct aw_thermal_sensor_config { + enum aw_thermal_sensor sensor; + const char *name; + const char *desc; +}; + +static const struct aw_thermal_sensor_config a83t_sensor_config[] = { + { .sensor = THS_SENSOR_CPU_CLUSTER0, + .name = "cluster0", .desc = "CPU cluster 0 temperature" }, + { .sensor = THS_SENSOR_CPU_CLUSTER1, + .name = "cluster1", .desc = "CPU cluster 1 temperature" }, + { .sensor = THS_SENSOR_GPU, + .name = "gpu", .desc = "GPU temperature" }, + { .sensor = THS_SENSOR_END } +}; + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun8i-a83t-ts", (uintptr_t)&a83t_sensor_config }, + { NULL, (uintptr_t)NULL } +}; + +#define THS_CONF(d) \ + (void *)ofw_bus_search_compatible((d), compat_data)->ocd_data + +struct aw_thermal_softc { + struct resource *res; + struct aw_thermal_sensor_config *conf; +}; + +static struct resource_spec aw_thermal_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define RD4(sc, reg) bus_read_4((sc)->res, (reg)) +#define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) + +static int +aw_thermal_init(struct aw_thermal_softc *sc) +{ + uint32_t calib0, calib1; + int error; + + /* Read calibration settings from SRAM */ + error = aw_sid_read_tscalib(&calib0, &calib1); + if (error != 0) + return (error); + + /* Write calibration settings to thermal controller */ + WR4(sc, THS_CALIB0, calib0); + WR4(sc, THS_CALIB1, calib1); + + /* Configure ADC acquire time (CLK_IN/(N+1)) and enable sensors */ + WR4(sc, THS_CTRL0, ADC_ACQUIRE_TIME); + WR4(sc, THS_CTRL2, (ADC_ACQUIRE_TIME << SENSOR_ACQ1_SHIFT) | + SENSOR_ENABLE_ALL); + + /* Disable interrupts */ + WR4(sc, THS_INTC, 0); + WR4(sc, THS_INTS, RD4(sc, THS_INTS)); + + /* Enable average filter */ + WR4(sc, THS_FILTER, RD4(sc, THS_FILTER) | FILTER_EN); + + return (0); +} + +static int +aw_thermal_gettemp(uint32_t val) +{ + int raw; + + raw = val & DATA_MASK; + return (((TEMP_BASE - raw) * TEMP_MUL) / TEMP_DIV) + TEMP_TO_K; +} + +static int +aw_thermal_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct aw_thermal_softc *sc; + enum aw_thermal_sensor sensor; + int val; + + sc = arg1; + sensor = arg2; + + val = aw_thermal_gettemp(RD4(sc, THS_DATA0 + (sensor * 4))); + + return sysctl_handle_opaque(oidp, &val, sizeof(val), req); +} + +static int +aw_thermal_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (THS_CONF(dev) == NULL) + return (ENXIO); + + device_set_desc(dev, "Allwinner Thermal Sensor Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_thermal_attach(device_t dev) +{ + struct aw_thermal_softc *sc; + int i; + + sc = device_get_softc(dev); + + sc->conf = THS_CONF(dev); + + if (bus_alloc_resources(dev, aw_thermal_spec, &sc->res) != 0) { + device_printf(dev, "cannot allocate resources for device\n"); + return (ENXIO); + } + + if (aw_thermal_init(sc) != 0) + return (ENXIO); + + for (i = 0; sc->conf[i].sensor != THS_SENSOR_END; i++) + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, sc->conf[i].name, + CTLTYPE_INT | CTLFLAG_RD, + sc, sc->conf[i].sensor, aw_thermal_sysctl, "IK0", + sc->conf[i].desc); + + return (0); +} + +static device_method_t aw_thermal_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aw_thermal_probe), + DEVMETHOD(device_attach, aw_thermal_attach), + + DEVMETHOD_END +}; + +static driver_t aw_thermal_driver = { + "aw_thermal", + aw_thermal_methods, + sizeof(struct aw_thermal_softc), +}; + +static devclass_t aw_thermal_devclass; + +DRIVER_MODULE(aw_thermal, simplebus, aw_thermal_driver, aw_thermal_devclass, + 0, 0); +MODULE_VERSION(aw_thermal, 1); Property changes on: trunk/sys/arm/allwinner/aw_thermal.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_ts.c =================================================================== --- trunk/sys/arm/allwinner/aw_ts.c (rev 0) +++ trunk/sys/arm/allwinner/aw_ts.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,231 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Emmanuel Vadot + * 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. + */ + +/* + * Allwinner Touch Sreen driver + * Touch screen part is not done, only the thermal sensor part is. + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/aw_ts.c 308276 2016-11-04 01:06:14Z manu $"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define READ(_sc, _r) bus_read_4((_sc)->res[0], (_r)) +#define WRITE(_sc, _r, _v) bus_write_4((_sc)->res[0], (_r), (_v)) + +/* Control register 0 */ +#define TP_CTRL0 0x00 +#define TP_CTRL0_TACQ(x) ((x & 0xFF) << 0) +#define TP_CTRL0_FS_DIV(x) ((x & 0xF) << 16) +#define TP_CTRL0_CLK_DIV(x) ((x & 0x3) << 20) +#define TP_CTRL0_CLK_SELECT(x) ((x & 0x1) << 22) + +/* Control register 1 */ +#define TP_CTRL1 0x04 +#define TP_CTRL1_MODE_EN (1 << 4) + +/* Control register 2 */ +#define TP_CTRL2 0x08 + +/* Control register 3 */ +#define TP_CTRL3 0x0C + +/* Int/FIFO control register */ +#define TP_FIFOC 0x10 +#define TP_FIFOC_TEMP_IRQ_ENABLE (1 << 18) + +/* Int/FIFO status register */ +#define TP_FIFOS 0x14 +#define TP_FIFOS_TEMP_IRQ_PENDING (1 << 18) + +/* Temperature Period Register */ +#define TP_TPR 0x18 +#define TP_TPR_TEMP_EN (1 << 16) +#define TP_TPR_TEMP_PERIOD(x) (x << 0) + +/* Common data register */ +#define TP_CDAT 0x1C + +/* Temperature data register */ +#define TEMP_DATA 0x20 + +/* TP Data register*/ +#define TP_DATA 0x24 + +/* TP IO config register */ +#define TP_IO_CONFIG 0x28 + +/* TP IO port data register */ +#define TP_IO_DATA 0x2C + +struct aw_ts_softc { + device_t dev; + struct resource * res[2]; + void * intrhand; + int temp_data; + int temp_offset; + int temp_step; +}; + +static struct resource_spec aw_ts_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, + { -1, 0 } +}; + +#define A10_TS 1 +#define A13_TS 2 + +#define AW_TS_TEMP_SYSCTL 1 + +static struct ofw_compat_data compat_data[] = { + {"allwinner,sun4i-a10-ts", A10_TS}, + {"allwinner,sun5i-a13-ts", A13_TS}, + {NULL, 0} +}; + +static void +aw_ts_intr(void *arg) +{ + struct aw_ts_softc *sc; + int val; + + sc= (struct aw_ts_softc *)arg; + + val = READ(sc, TP_FIFOS); + if (val & TP_FIFOS_TEMP_IRQ_PENDING) { + /* Convert the value to millicelsius then millikelvin */ + sc->temp_data = (READ(sc, TEMP_DATA) * sc->temp_step - sc->temp_offset) + + 273150; + } + + WRITE(sc, TP_FIFOS, val); +} + +static int +aw_ts_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, "Allwinner Touch Screen controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +aw_ts_attach(device_t dev) +{ + struct aw_ts_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (bus_alloc_resources(dev, aw_ts_spec, sc->res) != 0) { + device_printf(dev, "could not allocate memory resource\n"); + return (ENXIO); + } + + if (bus_setup_intr(dev, sc->res[1], + INTR_TYPE_MISC | INTR_MPSAFE, NULL, aw_ts_intr, sc, + &sc->intrhand)) { + bus_release_resources(dev, aw_ts_spec, sc->res); + device_printf(dev, "cannot setup interrupt handler\n"); + return (ENXIO); + } + + /* + * Thoses magic values were taken from linux which take them from + * the allwinner SDK or found them by deduction + */ + switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { + case A10_TS: + sc->temp_offset = 257000; + sc->temp_step = 133; + break; + case A13_TS: + sc->temp_offset = 144700; + sc->temp_step = 100; + break; + } + + /* Enable clock and set divisers */ + WRITE(sc, TP_CTRL0, TP_CTRL0_CLK_SELECT(0) | + TP_CTRL0_CLK_DIV(2) | + TP_CTRL0_FS_DIV(7) | + TP_CTRL0_TACQ(63)); + + /* Enable TS module */ + WRITE(sc, TP_CTRL1, TP_CTRL1_MODE_EN); + + /* Enable Temperature, period is ~2s */ + WRITE(sc, TP_TPR, TP_TPR_TEMP_EN | TP_TPR_TEMP_PERIOD(1953)); + + /* Enable temp irq */ + WRITE(sc, TP_FIFOC, TP_FIFOC_TEMP_IRQ_ENABLE); + + /* Add sysctl */ + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, + &sc->temp_data, 0, sysctl_handle_int, + "IK3", "CPU Temperature"); + + return (0); +} + +static device_method_t aw_ts_methods[] = { + DEVMETHOD(device_probe, aw_ts_probe), + DEVMETHOD(device_attach, aw_ts_attach), + + DEVMETHOD_END +}; + +static driver_t aw_ts_driver = { + "aw_ts", + aw_ts_methods, + sizeof(struct aw_ts_softc), +}; +static devclass_t aw_ts_devclass; + +DRIVER_MODULE(aw_ts, simplebus, aw_ts_driver, aw_ts_devclass, 0, 0); Property changes on: trunk/sys/arm/allwinner/aw_ts.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_usbphy.c =================================================================== --- trunk/sys/arm/allwinner/aw_usbphy.c (rev 0) +++ trunk/sys/arm/allwinner/aw_usbphy.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,272 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/aw_usbphy.c 332025 2018-04-04 13:23:06Z mmel $ + */ + +/* + * Allwinner USB PHY + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/aw_usbphy.c 332025 2018-04-04 13:23:06Z mmel $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "phynode_if.h" + +#define USBPHY_NPHYS 4 + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-usb-phy", 1 }, + { "allwinner,sun5i-a13-usb-phy", 1 }, + { "allwinner,sun6i-a31-usb-phy", 1 }, + { "allwinner,sun7i-a20-usb-phy", 1 }, + { "allwinner,sun8i-a83t-usb-phy", 1 }, + { "allwinner,sun8i-h3-usb-phy", 1 }, + { NULL, 0 } +}; + +struct awusbphy_softc { + regulator_t reg[USBPHY_NPHYS]; + gpio_pin_t id_det_pin; + int id_det_valid; + gpio_pin_t vbus_det_pin; + int vbus_det_valid; +}; + + /* Phy class and methods. */ +static int awusbphy_phy_enable(struct phynode *phy, bool enable); +static phynode_method_t awusbphy_phynode_methods[] = { + PHYNODEMETHOD(phynode_enable, awusbphy_phy_enable), + + PHYNODEMETHOD_END +}; +DEFINE_CLASS_1(awusbphy_phynode, awusbphy_phynode_class, awusbphy_phynode_methods, + 0, phynode_class); + +static int +awusbphy_init(device_t dev) +{ + struct awusbphy_softc *sc; + phandle_t node; + char pname[20]; + int error, off; + regulator_t reg; + hwreset_t rst; + clk_t clk; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + + /* Enable clocks */ + for (off = 0; clk_get_by_ofw_index(dev, 0, off, &clk) == 0; off++) { + error = clk_enable(clk); + if (error != 0) { + device_printf(dev, "couldn't enable clock %s\n", + clk_get_name(clk)); + return (error); + } + } + + /* De-assert resets */ + for (off = 0; hwreset_get_by_ofw_idx(dev, 0, off, &rst) == 0; off++) { + error = hwreset_deassert(rst); + if (error != 0) { + device_printf(dev, "couldn't de-assert reset %d\n", + off); + return (error); + } + } + + /* Get regulators */ + for (off = 0; off < USBPHY_NPHYS; off++) { + snprintf(pname, sizeof(pname), "usb%d_vbus-supply", off); + if (regulator_get_by_ofw_property(dev, 0, pname, ®) == 0) + sc->reg[off] = reg; + } + + /* Get GPIOs */ + error = gpio_pin_get_by_ofw_property(dev, node, "usb0_id_det-gpios", + &sc->id_det_pin); + if (error == 0) + sc->id_det_valid = 1; + error = gpio_pin_get_by_ofw_property(dev, node, "usb0_vbus_det-gpios", + &sc->vbus_det_pin); + if (error == 0) + sc->vbus_det_valid = 1; + + return (0); +} + +static int +awusbphy_vbus_detect(device_t dev, int *val) +{ + struct awusbphy_softc *sc; + bool active; + int error; + + sc = device_get_softc(dev); + + if (sc->vbus_det_valid) { + error = gpio_pin_is_active(sc->vbus_det_pin, &active); + if (error != 0) + return (error); + *val = active; + return (0); + } + + *val = 1; + return (0); +} + +static int +awusbphy_phy_enable(struct phynode *phynode, bool enable) +{ + device_t dev; + intptr_t phy; + struct awusbphy_softc *sc; + regulator_t reg; + int error, vbus_det; + + dev = phynode_get_device(phynode); + phy = phynode_get_id(phynode); + sc = device_get_softc(dev); + + if (phy < 0 || phy >= USBPHY_NPHYS) + return (ERANGE); + + sc = device_get_softc(dev); + + /* Regulators are optional. If not found, return success. */ + reg = sc->reg[phy]; + if (reg == NULL) + return (0); + + if (enable) { + /* If an external vbus is detected, do not enable phy 0 */ + if (phy == 0) { + error = awusbphy_vbus_detect(dev, &vbus_det); + if (error == 0 && vbus_det == 1) + return (0); + } else + error = 0; + if (error == 0) + error = regulator_enable(reg); + } else + error = regulator_disable(reg); + if (error != 0) { + device_printf(dev, + "couldn't %s regulator for phy %jd\n", + enable ? "enable" : "disable", (intmax_t)phy); + return (error); + } + + return (0); +} + +static int +awusbphy_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, "Allwinner USB PHY"); + return (BUS_PROBE_DEFAULT); +} + +static int +awusbphy_attach(device_t dev) +{ + int error; + struct phynode *phynode; + struct phynode_init_def phy_init; + int i; + + error = awusbphy_init(dev); + if (error) { + device_printf(dev, "failed to initialize USB PHY, error %d\n", + error); + return (error); + } + + /* Create and register phys. */ + for (i = 0; i < USBPHY_NPHYS; i++) { + bzero(&phy_init, sizeof(phy_init)); + phy_init.id = i; + phy_init.ofw_node = ofw_bus_get_node(dev); + phynode = phynode_create(dev, &awusbphy_phynode_class, + &phy_init); + if (phynode == NULL) { + device_printf(dev, "failed to create USB PHY\n"); + return (ENXIO); + } + if (phynode_register(phynode) == NULL) { + device_printf(dev, "failed to create USB PHY\n"); + return (ENXIO); + } + } + + return (error); +} + +static device_method_t awusbphy_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, awusbphy_probe), + DEVMETHOD(device_attach, awusbphy_attach), + + DEVMETHOD_END +}; + +static driver_t awusbphy_driver = { + "awusbphy", + awusbphy_methods, + sizeof(struct awusbphy_softc) +}; + +static devclass_t awusbphy_devclass; + +EARLY_DRIVER_MODULE(awusbphy, simplebus, awusbphy_driver, awusbphy_devclass, + 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(awusbphy, 1); Property changes on: trunk/sys/arm/allwinner/aw_usbphy.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_wdog.c =================================================================== --- trunk/sys/arm/allwinner/aw_wdog.c (rev 0) +++ trunk/sys/arm/allwinner/aw_wdog.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,277 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2013 Oleksandr Tymoshenko + * Copyright (c) 2016 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/aw_wdog.c 331182 2018-03-19 06:40:11Z eadler $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#define READ(_sc, _r) bus_read_4((_sc)->res, (_r)) +#define WRITE(_sc, _r, _v) bus_write_4((_sc)->res, (_r), (_v)) + +#define A10_WDOG_CTRL 0x00 +#define A31_WDOG_CTRL 0x10 +#define WDOG_CTRL_RESTART (1 << 0) +#define A31_WDOG_CTRL_KEY (0xa57 << 1) +#define A10_WDOG_MODE 0x04 +#define A31_WDOG_MODE 0x18 +#define A10_WDOG_MODE_INTVL_SHIFT 3 +#define A31_WDOG_MODE_INTVL_SHIFT 4 +#define A10_WDOG_MODE_RST_EN (1 << 1) +#define WDOG_MODE_EN (1 << 0) +#define A31_WDOG_CONFIG 0x14 +#define A31_WDOG_CONFIG_RST_EN_SYSTEM (1 << 0) +#define A31_WDOG_CONFIG_RST_EN_INT (2 << 0) + +struct aw_wdog_interval { + uint64_t milliseconds; + unsigned int value; +}; + +struct aw_wdog_interval wd_intervals[] = { + { 500, 0 }, + { 1000, 1 }, + { 2000, 2 }, + { 3000, 3 }, + { 4000, 4 }, + { 5000, 5 }, + { 6000, 6 }, + { 8000, 7 }, + { 10000, 8 }, + { 12000, 9 }, + { 14000, 10 }, + { 16000, 11 }, + { 0, 0 } /* sentinel */ +}; + +static struct aw_wdog_softc *aw_wdog_sc = NULL; + +struct aw_wdog_softc { + device_t dev; + struct resource * res; + struct mtx mtx; + uint8_t wdog_ctrl; + uint32_t wdog_ctrl_key; + uint8_t wdog_mode; + uint8_t wdog_mode_intvl_shift; + uint8_t wdog_mode_en; + uint8_t wdog_config; + uint8_t wdog_config_value; +}; + +#define A10_WATCHDOG 1 +#define A31_WATCHDOG 2 + +static struct ofw_compat_data compat_data[] = { + {"allwinner,sun4i-a10-wdt", A10_WATCHDOG}, + {"allwinner,sun6i-a31-wdt", A31_WATCHDOG}, + {NULL, 0} +}; + +static void aw_wdog_watchdog_fn(void *, u_int, int *); +static void aw_wdog_shutdown_fn(void *, int); + +static int +aw_wdog_probe(device_t dev) +{ + struct aw_wdog_softc *sc; + + sc = device_get_softc(dev); + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { + case A10_WATCHDOG: + device_set_desc(dev, "Allwinner A10 Watchdog"); + return (BUS_PROBE_DEFAULT); + case A31_WATCHDOG: + device_set_desc(dev, "Allwinner A31 Watchdog"); + return (BUS_PROBE_DEFAULT); + } + return (ENXIO); +} + +static int +aw_wdog_attach(device_t dev) +{ + struct aw_wdog_softc *sc; + int rid; + + if (aw_wdog_sc != NULL) + return (ENXIO); + + sc = device_get_softc(dev); + sc->dev = dev; + + rid = 0; + sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (sc->res == NULL) { + device_printf(dev, "could not allocate memory resource\n"); + return (ENXIO); + } + + aw_wdog_sc = sc; + + switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { + case A10_WATCHDOG: + sc->wdog_ctrl = A10_WDOG_CTRL; + sc->wdog_mode = A10_WDOG_MODE; + sc->wdog_mode_intvl_shift = A10_WDOG_MODE_INTVL_SHIFT; + sc->wdog_mode_en = A10_WDOG_MODE_RST_EN | WDOG_MODE_EN; + break; + case A31_WATCHDOG: + sc->wdog_ctrl = A31_WDOG_CTRL; + sc->wdog_ctrl_key = A31_WDOG_CTRL_KEY; + sc->wdog_mode = A31_WDOG_MODE; + sc->wdog_mode_intvl_shift = A31_WDOG_MODE_INTVL_SHIFT; + sc->wdog_mode_en = WDOG_MODE_EN; + sc->wdog_config = A31_WDOG_CONFIG; + sc->wdog_config_value = A31_WDOG_CONFIG_RST_EN_SYSTEM; + break; + default: + bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res); + return (ENXIO); + } + + mtx_init(&sc->mtx, "AW Watchdog", "aw_wdog", MTX_DEF); + EVENTHANDLER_REGISTER(watchdog_list, aw_wdog_watchdog_fn, sc, 0); + EVENTHANDLER_REGISTER(shutdown_final, aw_wdog_shutdown_fn, sc, + SHUTDOWN_PRI_LAST - 1); + + return (0); +} + +static void +aw_wdog_watchdog_fn(void *private, u_int cmd, int *error) +{ + struct aw_wdog_softc *sc; + uint64_t ms; + int i; + + sc = private; + mtx_lock(&sc->mtx); + + cmd &= WD_INTERVAL; + + if (cmd > 0) { + ms = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000; + i = 0; + while (wd_intervals[i].milliseconds && + (ms > wd_intervals[i].milliseconds)) + i++; + if (wd_intervals[i].milliseconds) { + WRITE(sc, sc->wdog_mode, + (wd_intervals[i].value << sc->wdog_mode_intvl_shift) | + sc->wdog_mode_en); + WRITE(sc, sc->wdog_ctrl, + WDOG_CTRL_RESTART | sc->wdog_ctrl_key); + if (sc->wdog_config) + WRITE(sc, sc->wdog_config, + sc->wdog_config_value); + *error = 0; + } + else { + /* + * Can't arm + * disable watchdog as watchdog(9) requires + */ + device_printf(sc->dev, + "Can't arm, timeout is more than 16 sec\n"); + mtx_unlock(&sc->mtx); + WRITE(sc, sc->wdog_mode, 0); + return; + } + } + else + WRITE(sc, sc->wdog_mode, 0); + + mtx_unlock(&sc->mtx); +} + +static void +aw_wdog_shutdown_fn(void *private, int howto) +{ + if ((howto & (RB_POWEROFF|RB_HALT)) == 0) + aw_wdog_watchdog_reset(); +} + +void +aw_wdog_watchdog_reset(void) +{ + + if (aw_wdog_sc == NULL) { + printf("Reset: watchdog device has not been initialized\n"); + return; + } + + WRITE(aw_wdog_sc, aw_wdog_sc->wdog_mode, + (wd_intervals[0].value << aw_wdog_sc->wdog_mode_intvl_shift) | + aw_wdog_sc->wdog_mode_en); + if (aw_wdog_sc->wdog_config) + WRITE(aw_wdog_sc, aw_wdog_sc->wdog_config, + aw_wdog_sc->wdog_config_value); + WRITE(aw_wdog_sc, aw_wdog_sc->wdog_ctrl, + WDOG_CTRL_RESTART | aw_wdog_sc->wdog_ctrl_key); + while(1) + ; + +} + +static device_method_t aw_wdog_methods[] = { + DEVMETHOD(device_probe, aw_wdog_probe), + DEVMETHOD(device_attach, aw_wdog_attach), + + DEVMETHOD_END +}; + +static driver_t aw_wdog_driver = { + "aw_wdog", + aw_wdog_methods, + sizeof(struct aw_wdog_softc), +}; +static devclass_t aw_wdog_devclass; + +DRIVER_MODULE(aw_wdog, simplebus, aw_wdog_driver, aw_wdog_devclass, 0, 0); Property changes on: trunk/sys/arm/allwinner/aw_wdog.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/aw_wdog.h =================================================================== --- trunk/sys/arm/allwinner/aw_wdog.h (rev 0) +++ trunk/sys/arm/allwinner/aw_wdog.h 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,36 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2013 Oleksandr Tymoshenko + * 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: stable/11/sys/arm/allwinner/aw_wdog.h 296041 2016-02-25 16:50:36Z andrew $ + * + */ +#ifndef __AW_WDOG_H__ +#define __AW_WDOG_H__ + +void aw_wdog_watchdog_reset(void); + +#endif /*__AW_WDOG_H__*/ + Property changes on: trunk/sys/arm/allwinner/aw_wdog.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/axp209.c =================================================================== --- trunk/sys/arm/allwinner/axp209.c (rev 0) +++ trunk/sys/arm/allwinner/axp209.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,1409 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2015-2016 Emmanuel Vadot + * Copyright (c) 2016 Jared McNeill + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/axp209.c 323467 2017-09-11 22:21:15Z ian $"); + +/* +* X-Power AXP209/AXP211 PMU for Allwinner SoCs +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include + +#include "gpio_if.h" +#include "regdev_if.h" + +MALLOC_DEFINE(M_AXP2XX_REG, "Axp2XX regulator", "Axp2XX power regulator"); + +struct axp2xx_regdef { + intptr_t id; + char *name; + uint8_t enable_reg; + uint8_t enable_mask; + uint8_t voltage_reg; + uint8_t voltage_mask; + uint8_t voltage_shift; + int voltage_min; + int voltage_max; + int voltage_step; + int voltage_nstep; +}; + +static struct axp2xx_regdef axp209_regdefs[] = { + { + .id = AXP209_REG_ID_DCDC2, + .name = "dcdc2", + .enable_reg = AXP209_POWERCTL, + .enable_mask = AXP209_POWERCTL_DCDC2, + .voltage_reg = AXP209_REG_DCDC2_VOLTAGE, + .voltage_mask = 0x3f, + .voltage_min = 700, + .voltage_max = 2275, + .voltage_step = 25, + .voltage_nstep = 64, + }, + { + .id = AXP209_REG_ID_DCDC3, + .name = "dcdc3", + .enable_reg = AXP209_POWERCTL, + .enable_mask = AXP209_POWERCTL_DCDC3, + .voltage_reg = AXP209_REG_DCDC3_VOLTAGE, + .voltage_mask = 0x7f, + .voltage_min = 700, + .voltage_max = 3500, + .voltage_step = 25, + .voltage_nstep = 128, + }, + { + .id = AXP209_REG_ID_LDO2, + .name = "ldo2", + .enable_reg = AXP209_POWERCTL, + .enable_mask = AXP209_POWERCTL_LDO2, + .voltage_reg = AXP209_REG_LDO24_VOLTAGE, + .voltage_mask = 0xf0, + .voltage_shift = 4, + .voltage_min = 1800, + .voltage_max = 3300, + .voltage_step = 100, + .voltage_nstep = 16, + }, + { + .id = AXP209_REG_ID_LDO3, + .name = "ldo3", + .enable_reg = AXP209_POWERCTL, + .enable_mask = AXP209_POWERCTL_LDO3, + .voltage_reg = AXP209_REG_LDO3_VOLTAGE, + .voltage_mask = 0x7f, + .voltage_min = 700, + .voltage_max = 2275, + .voltage_step = 25, + .voltage_nstep = 128, + }, +}; + +static struct axp2xx_regdef axp221_regdefs[] = { + { + .id = AXP221_REG_ID_DLDO1, + .name = "dldo1", + .enable_reg = AXP221_POWERCTL_2, + .enable_mask = AXP221_POWERCTL2_DLDO1, + .voltage_reg = AXP221_REG_DLDO1_VOLTAGE, + .voltage_mask = 0x1f, + .voltage_min = 700, + .voltage_max = 3300, + .voltage_step = 100, + .voltage_nstep = 26, + }, + { + .id = AXP221_REG_ID_DLDO2, + .name = "dldo2", + .enable_reg = AXP221_POWERCTL_2, + .enable_mask = AXP221_POWERCTL2_DLDO2, + .voltage_reg = AXP221_REG_DLDO2_VOLTAGE, + .voltage_mask = 0x1f, + .voltage_min = 700, + .voltage_max = 3300, + .voltage_step = 100, + .voltage_nstep = 26, + }, + { + .id = AXP221_REG_ID_DLDO3, + .name = "dldo3", + .enable_reg = AXP221_POWERCTL_2, + .enable_mask = AXP221_POWERCTL2_DLDO3, + .voltage_reg = AXP221_REG_DLDO3_VOLTAGE, + .voltage_mask = 0x1f, + .voltage_min = 700, + .voltage_max = 3300, + .voltage_step = 100, + .voltage_nstep = 26, + }, + { + .id = AXP221_REG_ID_DLDO4, + .name = "dldo4", + .enable_reg = AXP221_POWERCTL_2, + .enable_mask = AXP221_POWERCTL2_DLDO4, + .voltage_reg = AXP221_REG_DLDO4_VOLTAGE, + .voltage_mask = 0x1f, + .voltage_min = 700, + .voltage_max = 3300, + .voltage_step = 100, + .voltage_nstep = 26, + }, + { + .id = AXP221_REG_ID_ELDO1, + .name = "eldo1", + .enable_reg = AXP221_POWERCTL_2, + .enable_mask = AXP221_POWERCTL2_ELDO1, + .voltage_reg = AXP221_REG_ELDO1_VOLTAGE, + .voltage_mask = 0x1f, + .voltage_min = 700, + .voltage_max = 3300, + .voltage_step = 100, + .voltage_nstep = 26, + }, + { + .id = AXP221_REG_ID_ELDO2, + .name = "eldo2", + .enable_reg = AXP221_POWERCTL_2, + .enable_mask = AXP221_POWERCTL2_ELDO2, + .voltage_reg = AXP221_REG_ELDO2_VOLTAGE, + .voltage_mask = 0x1f, + .voltage_min = 700, + .voltage_max = 3300, + .voltage_step = 100, + .voltage_nstep = 26, + }, + { + .id = AXP221_REG_ID_ELDO3, + .name = "eldo3", + .enable_reg = AXP221_POWERCTL_2, + .enable_mask = AXP221_POWERCTL2_ELDO3, + .voltage_reg = AXP221_REG_ELDO3_VOLTAGE, + .voltage_mask = 0x1f, + .voltage_min = 700, + .voltage_max = 3300, + .voltage_step = 100, + .voltage_nstep = 26, + }, + { + .id = AXP221_REG_ID_DC5LDO, + .name = "dc5ldo", + .enable_reg = AXP221_POWERCTL_1, + .enable_mask = AXP221_POWERCTL1_DC5LDO, + .voltage_reg = AXP221_REG_DC5LDO_VOLTAGE, + .voltage_mask = 0x3, + .voltage_min = 700, + .voltage_max = 1400, + .voltage_step = 100, + .voltage_nstep = 7, + }, + { + .id = AXP221_REG_ID_DCDC1, + .name = "dcdc1", + .enable_reg = AXP221_POWERCTL_1, + .enable_mask = AXP221_POWERCTL1_DCDC1, + .voltage_reg = AXP221_REG_DCDC1_VOLTAGE, + .voltage_mask = 0x1f, + .voltage_min = 1600, + .voltage_max = 3400, + .voltage_step = 100, + .voltage_nstep = 18, + }, + { + .id = AXP221_REG_ID_DCDC2, + .name = "dcdc2", + .enable_reg = AXP221_POWERCTL_1, + .enable_mask = AXP221_POWERCTL1_DCDC2, + .voltage_reg = AXP221_REG_DCDC2_VOLTAGE, + .voltage_mask = 0x3f, + .voltage_min = 600, + .voltage_max = 1540, + .voltage_step = 20, + .voltage_nstep = 47, + }, + { + .id = AXP221_REG_ID_DCDC3, + .name = "dcdc3", + .enable_reg = AXP221_POWERCTL_1, + .enable_mask = AXP221_POWERCTL1_DCDC3, + .voltage_reg = AXP221_REG_DCDC3_VOLTAGE, + .voltage_mask = 0x3f, + .voltage_min = 600, + .voltage_max = 1860, + .voltage_step = 20, + .voltage_nstep = 63, + }, + { + .id = AXP221_REG_ID_DCDC4, + .name = "dcdc4", + .enable_reg = AXP221_POWERCTL_1, + .enable_mask = AXP221_POWERCTL1_DCDC4, + .voltage_reg = AXP221_REG_DCDC4_VOLTAGE, + .voltage_mask = 0x3f, + .voltage_min = 600, + .voltage_max = 1540, + .voltage_step = 20, + .voltage_nstep = 47, + }, + { + .id = AXP221_REG_ID_DCDC5, + .name = "dcdc5", + .enable_reg = AXP221_POWERCTL_1, + .enable_mask = AXP221_POWERCTL1_DCDC5, + .voltage_reg = AXP221_REG_DCDC5_VOLTAGE, + .voltage_mask = 0x1f, + .voltage_min = 1000, + .voltage_max = 2550, + .voltage_step = 50, + .voltage_nstep = 31, + }, + { + .id = AXP221_REG_ID_ALDO1, + .name = "aldo1", + .enable_reg = AXP221_POWERCTL_1, + .enable_mask = AXP221_POWERCTL1_ALDO1, + .voltage_reg = AXP221_REG_ALDO1_VOLTAGE, + .voltage_mask = 0x1f, + .voltage_min = 700, + .voltage_max = 3300, + .voltage_step = 100, + .voltage_nstep = 26, + }, + { + .id = AXP221_REG_ID_ALDO2, + .name = "aldo2", + .enable_reg = AXP221_POWERCTL_1, + .enable_mask = AXP221_POWERCTL1_ALDO2, + .voltage_reg = AXP221_REG_ALDO2_VOLTAGE, + .voltage_mask = 0x1f, + .voltage_min = 700, + .voltage_max = 3300, + .voltage_step = 100, + .voltage_nstep = 26, + }, + { + .id = AXP221_REG_ID_ALDO3, + .name = "aldo3", + .enable_reg = AXP221_POWERCTL_3, + .enable_mask = AXP221_POWERCTL3_ALDO3, + .voltage_reg = AXP221_REG_ALDO3_VOLTAGE, + .voltage_mask = 0x1f, + .voltage_min = 700, + .voltage_max = 3300, + .voltage_step = 100, + .voltage_nstep = 26, + }, + { + .id = AXP221_REG_ID_DC1SW, + .name = "dc1sw", + .enable_reg = AXP221_POWERCTL_2, + .enable_mask = AXP221_POWERCTL2_DC1SW, + }, +}; + +struct axp2xx_reg_sc { + struct regnode *regnode; + device_t base_dev; + struct axp2xx_regdef *def; + phandle_t xref; + struct regnode_std_param *param; +}; + +struct axp2xx_pins { + const char *name; + uint8_t ctrl_reg; + uint8_t status_reg; + uint8_t status_mask; + uint8_t status_shift; +}; + +/* GPIO3 is different, don't expose it for now */ +static const struct axp2xx_pins axp209_pins[] = { + { + .name = "GPIO0", + .ctrl_reg = AXP2XX_GPIO0_CTRL, + .status_reg = AXP2XX_GPIO_STATUS, + .status_mask = 0x10, + .status_shift = 4, + }, + { + .name = "GPIO1", + .ctrl_reg = AXP2XX_GPIO1_CTRL, + .status_reg = AXP2XX_GPIO_STATUS, + .status_mask = 0x20, + .status_shift = 5, + }, + { + .name = "GPIO2", + .ctrl_reg = AXP209_GPIO2_CTRL, + .status_reg = AXP2XX_GPIO_STATUS, + .status_mask = 0x40, + .status_shift = 6, + }, +}; + +static const struct axp2xx_pins axp221_pins[] = { + { + .name = "GPIO0", + .ctrl_reg = AXP2XX_GPIO0_CTRL, + .status_reg = AXP2XX_GPIO_STATUS, + .status_mask = 0x1, + .status_shift = 0x0, + }, + { + .name = "GPIO1", + .ctrl_reg = AXP2XX_GPIO0_CTRL, + .status_reg = AXP2XX_GPIO_STATUS, + .status_mask = 0x2, + .status_shift = 0x1, + }, +}; + +struct axp2xx_sensors { + int id; + const char *name; + const char *desc; + const char *format; + uint8_t enable_reg; + uint8_t enable_mask; + uint8_t value_reg; + uint8_t value_size; + uint8_t h_value_mask; + uint8_t h_value_shift; + uint8_t l_value_mask; + uint8_t l_value_shift; + int value_step; + int value_convert; +}; + +static const struct axp2xx_sensors axp209_sensors[] = { + { + .id = AXP209_ACVOLT, + .name = "acvolt", + .desc = "AC Voltage (microvolt)", + .format = "I", + .enable_reg = AXP2XX_ADC_ENABLE1, + .enable_mask = AXP209_ADC1_ACVOLT, + .value_reg = AXP209_ACIN_VOLTAGE, + .value_size = 2, + .h_value_mask = 0xff, + .h_value_shift = 4, + .l_value_mask = 0xf, + .l_value_shift = 0, + .value_step = AXP209_VOLT_STEP, + }, + { + .id = AXP209_ACCURRENT, + .name = "accurrent", + .desc = "AC Current (microAmpere)", + .format = "I", + .enable_reg = AXP2XX_ADC_ENABLE1, + .enable_mask = AXP209_ADC1_ACCURRENT, + .value_reg = AXP209_ACIN_CURRENT, + .value_size = 2, + .h_value_mask = 0xff, + .h_value_shift = 4, + .l_value_mask = 0xf, + .l_value_shift = 0, + .value_step = AXP209_ACCURRENT_STEP, + }, + { + .id = AXP209_VBUSVOLT, + .name = "vbusvolt", + .desc = "VBUS Voltage (microVolt)", + .format = "I", + .enable_reg = AXP2XX_ADC_ENABLE1, + .enable_mask = AXP209_ADC1_VBUSVOLT, + .value_reg = AXP209_VBUS_VOLTAGE, + .value_size = 2, + .h_value_mask = 0xff, + .h_value_shift = 4, + .l_value_mask = 0xf, + .l_value_shift = 0, + .value_step = AXP209_VOLT_STEP, + }, + { + .id = AXP209_VBUSCURRENT, + .name = "vbuscurrent", + .desc = "VBUS Current (microAmpere)", + .format = "I", + .enable_reg = AXP2XX_ADC_ENABLE1, + .enable_mask = AXP209_ADC1_VBUSCURRENT, + .value_reg = AXP209_VBUS_CURRENT, + .value_size = 2, + .h_value_mask = 0xff, + .h_value_shift = 4, + .l_value_mask = 0xf, + .l_value_shift = 0, + .value_step = AXP209_VBUSCURRENT_STEP, + }, + { + .id = AXP2XX_BATVOLT, + .name = "batvolt", + .desc = "Battery Voltage (microVolt)", + .format = "I", + .enable_reg = AXP2XX_ADC_ENABLE1, + .enable_mask = AXP2XX_ADC1_BATVOLT, + .value_reg = AXP2XX_BAT_VOLTAGE, + .value_size = 2, + .h_value_mask = 0xff, + .h_value_shift = 4, + .l_value_mask = 0xf, + .l_value_shift = 0, + .value_step = AXP2XX_BATVOLT_STEP, + }, + { + .id = AXP2XX_BATCHARGECURRENT, + .name = "batchargecurrent", + .desc = "Battery Charging Current (microAmpere)", + .format = "I", + .enable_reg = AXP2XX_ADC_ENABLE1, + .enable_mask = AXP2XX_ADC1_BATCURRENT, + .value_reg = AXP2XX_BAT_CHARGE_CURRENT, + .value_size = 2, + .h_value_mask = 0xff, + .h_value_shift = 5, + .l_value_mask = 0x1f, + .l_value_shift = 0, + .value_step = AXP2XX_BATCURRENT_STEP, + }, + { + .id = AXP2XX_BATDISCHARGECURRENT, + .name = "batdischargecurrent", + .desc = "Battery Discharging Current (microAmpere)", + .format = "I", + .enable_reg = AXP2XX_ADC_ENABLE1, + .enable_mask = AXP2XX_ADC1_BATCURRENT, + .value_reg = AXP2XX_BAT_DISCHARGE_CURRENT, + .value_size = 2, + .h_value_mask = 0xff, + .h_value_shift = 5, + .l_value_mask = 0x1f, + .l_value_shift = 0, + .value_step = AXP2XX_BATCURRENT_STEP, + }, + { + .id = AXP2XX_TEMP, + .name = "temp", + .desc = "Internal Temperature", + .format = "IK", + .enable_reg = AXP209_ADC_ENABLE2, + .enable_mask = AXP209_ADC2_TEMP, + .value_reg = AXP209_TEMPMON, + .value_size = 2, + .h_value_mask = 0xff, + .h_value_shift = 4, + .l_value_mask = 0xf, + .l_value_shift = 0, + .value_step = 1, + .value_convert = -(AXP209_TEMPMON_MIN - AXP209_0C_TO_K), + }, +}; + +static const struct axp2xx_sensors axp221_sensors[] = { + { + .id = AXP2XX_BATVOLT, + .name = "batvolt", + .desc = "Battery Voltage (microVolt)", + .format = "I", + .enable_reg = AXP2XX_ADC_ENABLE1, + .enable_mask = AXP2XX_ADC1_BATVOLT, + .value_reg = AXP2XX_BAT_VOLTAGE, + .value_size = 2, + .h_value_mask = 0xff, + .h_value_shift = 4, + .l_value_mask = 0xf, + .l_value_shift = 0, + .value_step = AXP2XX_BATVOLT_STEP, + }, + { + .id = AXP2XX_BATCHARGECURRENT, + .name = "batchargecurrent", + .desc = "Battery Charging Current (microAmpere)", + .format = "I", + .enable_reg = AXP2XX_ADC_ENABLE1, + .enable_mask = AXP2XX_ADC1_BATCURRENT, + .value_reg = AXP2XX_BAT_CHARGE_CURRENT, + .value_size = 2, + .h_value_mask = 0xff, + .h_value_shift = 5, + .l_value_mask = 0x1f, + .l_value_shift = 0, + .value_step = AXP2XX_BATCURRENT_STEP, + }, + { + .id = AXP2XX_BATDISCHARGECURRENT, + .name = "batdischargecurrent", + .desc = "Battery Discharging Current (microAmpere)", + .format = "I", + .enable_reg = AXP2XX_ADC_ENABLE1, + .enable_mask = AXP2XX_ADC1_BATCURRENT, + .value_reg = AXP2XX_BAT_DISCHARGE_CURRENT, + .value_size = 2, + .h_value_mask = 0xff, + .h_value_shift = 5, + .l_value_mask = 0x1f, + .l_value_shift = 0, + .value_step = AXP2XX_BATCURRENT_STEP, + }, + { + .id = AXP2XX_TEMP, + .name = "temp", + .desc = "Internal Temperature", + .format = "IK", + .enable_reg = AXP2XX_ADC_ENABLE1, + .enable_mask = AXP221_ADC1_TEMP, + .value_reg = AXP221_TEMPMON, + .value_size = 2, + .h_value_mask = 0xff, + .h_value_shift = 4, + .l_value_mask = 0xf, + .l_value_shift = 0, + .value_step = 1, + .value_convert = -(AXP221_TEMPMON_MIN - AXP209_0C_TO_K), + }, +}; + +enum AXP2XX_TYPE { + AXP209 = 1, + AXP221, +}; + +struct axp2xx_softc { + device_t dev; + struct resource * res[1]; + void * intrcookie; + struct intr_config_hook intr_hook; + struct mtx mtx; + uint8_t type; + + /* GPIO */ + device_t gpiodev; + int npins; + const struct axp2xx_pins *pins; + + /* Sensors */ + const struct axp2xx_sensors *sensors; + int nsensors; + + /* Regulators */ + struct axp2xx_reg_sc **regs; + int nregs; + struct axp2xx_regdef *regdefs; +}; + +static struct ofw_compat_data compat_data[] = { + { "x-powers,axp209", AXP209 }, + { "x-powers,axp221", AXP221 }, + { NULL, 0 } +}; + +static struct resource_spec axp_res_spec[] = { + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0, 0 } +}; + +#define AXP_LOCK(sc) mtx_lock(&(sc)->mtx) +#define AXP_UNLOCK(sc) mtx_unlock(&(sc)->mtx) + +static int +axp2xx_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) +{ + + return (iicdev_readfrom(dev, reg, data, size, IIC_INTRWAIT)); +} + +static int +axp2xx_write(device_t dev, uint8_t reg, uint8_t data) +{ + + return (iicdev_writeto(dev, reg, &data, sizeof(data), IIC_INTRWAIT)); +} + +static int +axp2xx_regnode_init(struct regnode *regnode) +{ + return (0); +} + +static int +axp2xx_regnode_enable(struct regnode *regnode, bool enable, int *udelay) +{ + struct axp2xx_reg_sc *sc; + uint8_t val; + + sc = regnode_get_softc(regnode); + + axp2xx_read(sc->base_dev, sc->def->enable_reg, &val, 1); + if (enable) + val |= sc->def->enable_mask; + else + val &= ~sc->def->enable_mask; + axp2xx_write(sc->base_dev, sc->def->enable_reg, val); + + *udelay = 0; + + return (0); +} + +static void +axp2xx_regnode_reg_to_voltage(struct axp2xx_reg_sc *sc, uint8_t val, int *uv) +{ + if (val < sc->def->voltage_nstep) + *uv = sc->def->voltage_min + val * sc->def->voltage_step; + else + *uv = sc->def->voltage_min + + (sc->def->voltage_nstep * sc->def->voltage_step); + *uv *= 1000; +} + +static int +axp2xx_regnode_voltage_to_reg(struct axp2xx_reg_sc *sc, int min_uvolt, + int max_uvolt, uint8_t *val) +{ + uint8_t nval; + int nstep, uvolt; + + nval = 0; + uvolt = sc->def->voltage_min * 1000; + + for (nstep = 0; nstep < sc->def->voltage_nstep && uvolt < min_uvolt; + nstep++) { + ++nval; + uvolt += (sc->def->voltage_step * 1000); + } + if (uvolt > max_uvolt) + return (EINVAL); + + *val = nval; + return (0); +} + +static int +axp2xx_regnode_set_voltage(struct regnode *regnode, int min_uvolt, + int max_uvolt, int *udelay) +{ + struct axp2xx_reg_sc *sc; + uint8_t val; + + sc = regnode_get_softc(regnode); + + if (!sc->def->voltage_step) + return (ENXIO); + + if (axp2xx_regnode_voltage_to_reg(sc, min_uvolt, max_uvolt, &val) != 0) + return (ERANGE); + + axp2xx_write(sc->base_dev, sc->def->voltage_reg, val); + + *udelay = 0; + + return (0); +} + +static int +axp2xx_regnode_get_voltage(struct regnode *regnode, int *uvolt) +{ + struct axp2xx_reg_sc *sc; + uint8_t val; + + sc = regnode_get_softc(regnode); + + if (!sc->def->voltage_step) + return (ENXIO); + + axp2xx_read(sc->base_dev, sc->def->voltage_reg, &val, 1); + axp2xx_regnode_reg_to_voltage(sc, val & sc->def->voltage_mask, uvolt); + + return (0); +} + +static regnode_method_t axp2xx_regnode_methods[] = { + /* Regulator interface */ + REGNODEMETHOD(regnode_init, axp2xx_regnode_init), + REGNODEMETHOD(regnode_enable, axp2xx_regnode_enable), + REGNODEMETHOD(regnode_set_voltage, axp2xx_regnode_set_voltage), + REGNODEMETHOD(regnode_get_voltage, axp2xx_regnode_get_voltage), + REGNODEMETHOD_END +}; +DEFINE_CLASS_1(axp2xx_regnode, axp2xx_regnode_class, axp2xx_regnode_methods, + sizeof(struct axp2xx_reg_sc), regnode_class); + +static int +axp2xx_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct axp2xx_softc *sc; + device_t dev = arg1; + enum axp2xx_sensor sensor = arg2; + uint8_t data[2]; + int val, error, i, found; + + sc = device_get_softc(dev); + + for (found = 0, i = 0; i < sc->nsensors; i++) { + if (sc->sensors[i].id == sensor) { + found = 1; + break; + } + } + + if (found == 0) + return (ENOENT); + + error = axp2xx_read(dev, sc->sensors[i].value_reg, data, 2); + if (error != 0) + return (error); + + val = ((data[0] & sc->sensors[i].h_value_mask) << + sc->sensors[i].h_value_shift); + val |= ((data[1] & sc->sensors[i].l_value_mask) << + sc->sensors[i].l_value_shift); + val *= sc->sensors[i].value_step; + val += sc->sensors[i].value_convert; + + return sysctl_handle_opaque(oidp, &val, sizeof(val), req); +} + +static void +axp2xx_shutdown(void *devp, int howto) +{ + device_t dev; + + if (!(howto & RB_POWEROFF)) + return; + dev = (device_t)devp; + + if (bootverbose) + device_printf(dev, "Shutdown AXP2xx\n"); + + axp2xx_write(dev, AXP2XX_SHUTBAT, AXP2XX_SHUTBAT_SHUTDOWN); +} + +static void +axp2xx_intr(void *arg) +{ + struct axp2xx_softc *sc; + uint8_t reg; + + sc = arg; + + axp2xx_read(sc->dev, AXP2XX_IRQ1_STATUS, ®, 1); + if (reg) { + if (reg & AXP2XX_IRQ1_AC_OVERVOLT) + devctl_notify("PMU", "AC", "overvoltage", NULL); + if (reg & AXP2XX_IRQ1_VBUS_OVERVOLT) + devctl_notify("PMU", "USB", "overvoltage", NULL); + if (reg & AXP2XX_IRQ1_VBUS_LOW) + devctl_notify("PMU", "USB", "undervoltage", NULL); + if (reg & AXP2XX_IRQ1_AC_CONN) + devctl_notify("PMU", "AC", "plugged", NULL); + if (reg & AXP2XX_IRQ1_AC_DISCONN) + devctl_notify("PMU", "AC", "unplugged", NULL); + if (reg & AXP2XX_IRQ1_VBUS_CONN) + devctl_notify("PMU", "USB", "plugged", NULL); + if (reg & AXP2XX_IRQ1_VBUS_DISCONN) + devctl_notify("PMU", "USB", "unplugged", NULL); + axp2xx_write(sc->dev, AXP2XX_IRQ1_STATUS, AXP2XX_IRQ_ACK); + } + + axp2xx_read(sc->dev, AXP2XX_IRQ2_STATUS, ®, 1); + if (reg) { + if (reg & AXP2XX_IRQ2_BATT_CHARGED) + devctl_notify("PMU", "Battery", "charged", NULL); + if (reg & AXP2XX_IRQ2_BATT_CHARGING) + devctl_notify("PMU", "Battery", "charging", NULL); + if (reg & AXP2XX_IRQ2_BATT_CONN) + devctl_notify("PMU", "Battery", "connected", NULL); + if (reg & AXP2XX_IRQ2_BATT_DISCONN) + devctl_notify("PMU", "Battery", "disconnected", NULL); + if (reg & AXP2XX_IRQ2_BATT_TEMP_LOW) + devctl_notify("PMU", "Battery", "low temp", NULL); + if (reg & AXP2XX_IRQ2_BATT_TEMP_OVER) + devctl_notify("PMU", "Battery", "high temp", NULL); + axp2xx_write(sc->dev, AXP2XX_IRQ2_STATUS, AXP2XX_IRQ_ACK); + } + + axp2xx_read(sc->dev, AXP2XX_IRQ3_STATUS, ®, 1); + if (reg) { + if (reg & AXP2XX_IRQ3_PEK_SHORT) + shutdown_nice(RB_POWEROFF); + axp2xx_write(sc->dev, AXP2XX_IRQ3_STATUS, AXP2XX_IRQ_ACK); + } + + axp2xx_read(sc->dev, AXP2XX_IRQ4_STATUS, ®, 1); + if (reg) { + axp2xx_write(sc->dev, AXP2XX_IRQ4_STATUS, AXP2XX_IRQ_ACK); + } + + axp2xx_read(sc->dev, AXP2XX_IRQ5_STATUS, ®, 1); + if (reg) { + axp2xx_write(sc->dev, AXP2XX_IRQ5_STATUS, AXP2XX_IRQ_ACK); + } +} + +static device_t +axp2xx_gpio_get_bus(device_t dev) +{ + struct axp2xx_softc *sc; + + sc = device_get_softc(dev); + + return (sc->gpiodev); +} + +static int +axp2xx_gpio_pin_max(device_t dev, int *maxpin) +{ + struct axp2xx_softc *sc; + + sc = device_get_softc(dev); + + *maxpin = sc->npins - 1; + + return (0); +} + +static int +axp2xx_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct axp2xx_softc *sc; + + sc = device_get_softc(dev); + + if (pin >= sc->npins) + return (EINVAL); + + snprintf(name, GPIOMAXNAME, "%s", axp209_pins[pin].name); + + return (0); +} + +static int +axp2xx_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct axp2xx_softc *sc; + + sc = device_get_softc(dev); + + if (pin >= sc->npins) + return (EINVAL); + + *caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT; + + return (0); +} + +static int +axp2xx_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct axp2xx_softc *sc; + uint8_t data, func; + int error; + + sc = device_get_softc(dev); + + if (pin >= sc->npins) + return (EINVAL); + + AXP_LOCK(sc); + error = axp2xx_read(dev, sc->pins[pin].ctrl_reg, &data, 1); + if (error == 0) { + func = data & AXP2XX_GPIO_FUNC_MASK; + if (func == AXP2XX_GPIO_FUNC_INPUT) + *flags = GPIO_PIN_INPUT; + else if (func == AXP2XX_GPIO_FUNC_DRVLO || + func == AXP2XX_GPIO_FUNC_DRVHI) + *flags = GPIO_PIN_OUTPUT; + else + *flags = 0; + } + AXP_UNLOCK(sc); + + return (error); +} + +static int +axp2xx_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct axp2xx_softc *sc; + uint8_t data; + int error; + + sc = device_get_softc(dev); + + if (pin >= sc->npins) + return (EINVAL); + + AXP_LOCK(sc); + error = axp2xx_read(dev, sc->pins[pin].ctrl_reg, &data, 1); + if (error == 0) { + data &= ~AXP2XX_GPIO_FUNC_MASK; + if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) != 0) { + if ((flags & GPIO_PIN_OUTPUT) == 0) + data |= AXP2XX_GPIO_FUNC_INPUT; + } + error = axp2xx_write(dev, sc->pins[pin].ctrl_reg, data); + } + AXP_UNLOCK(sc); + + return (error); +} + +static int +axp2xx_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) +{ + struct axp2xx_softc *sc; + uint8_t data, func; + int error; + + sc = device_get_softc(dev); + + if (pin >= sc->npins) + return (EINVAL); + + AXP_LOCK(sc); + error = axp2xx_read(dev, sc->pins[pin].ctrl_reg, &data, 1); + if (error == 0) { + func = data & AXP2XX_GPIO_FUNC_MASK; + switch (func) { + case AXP2XX_GPIO_FUNC_DRVLO: + *val = 0; + break; + case AXP2XX_GPIO_FUNC_DRVHI: + *val = 1; + break; + case AXP2XX_GPIO_FUNC_INPUT: + error = axp2xx_read(dev, sc->pins[pin].status_reg, + &data, 1); + if (error == 0) { + *val = (data & sc->pins[pin].status_mask); + *val >>= sc->pins[pin].status_shift; + } + break; + default: + error = EIO; + break; + } + } + AXP_UNLOCK(sc); + + return (error); +} + +static int +axp2xx_gpio_pin_set(device_t dev, uint32_t pin, unsigned int val) +{ + struct axp2xx_softc *sc; + uint8_t data, func; + int error; + + sc = device_get_softc(dev); + + if (pin >= sc->npins) + return (EINVAL); + + AXP_LOCK(sc); + error = axp2xx_read(dev, sc->pins[pin].ctrl_reg, &data, 1); + if (error == 0) { + func = data & AXP2XX_GPIO_FUNC_MASK; + switch (func) { + case AXP2XX_GPIO_FUNC_DRVLO: + case AXP2XX_GPIO_FUNC_DRVHI: + /* GPIO2 can't be set to 1 */ + if (pin == 2 && val == 1) { + error = EINVAL; + break; + } + data &= ~AXP2XX_GPIO_FUNC_MASK; + data |= val; + break; + default: + error = EIO; + break; + } + } + if (error == 0) + error = axp2xx_write(dev, sc->pins[pin].ctrl_reg, data); + AXP_UNLOCK(sc); + + return (error); +} + + +static int +axp2xx_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct axp2xx_softc *sc; + uint8_t data, func; + int error; + + sc = device_get_softc(dev); + + if (pin >= sc->npins) + return (EINVAL); + + AXP_LOCK(sc); + error = axp2xx_read(dev, sc->pins[pin].ctrl_reg, &data, 1); + if (error == 0) { + func = data & AXP2XX_GPIO_FUNC_MASK; + switch (func) { + case AXP2XX_GPIO_FUNC_DRVLO: + /* Pin 2 can't be set to 1*/ + if (pin == 2) { + error = EINVAL; + break; + } + data &= ~AXP2XX_GPIO_FUNC_MASK; + data |= AXP2XX_GPIO_FUNC_DRVHI; + break; + case AXP2XX_GPIO_FUNC_DRVHI: + data &= ~AXP2XX_GPIO_FUNC_MASK; + data |= AXP2XX_GPIO_FUNC_DRVLO; + break; + default: + error = EIO; + break; + } + } + if (error == 0) + error = axp2xx_write(dev, sc->pins[pin].ctrl_reg, data); + AXP_UNLOCK(sc); + + return (error); +} + +static int +axp2xx_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, + int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) +{ + struct axp2xx_softc *sc; + + sc = device_get_softc(bus); + + if (gpios[0] >= sc->npins) + return (EINVAL); + + *pin = gpios[0]; + *flags = gpios[1]; + + return (0); +} + +static phandle_t +axp2xx_get_node(device_t dev, device_t bus) +{ + return (ofw_bus_get_node(dev)); +} + +static struct axp2xx_reg_sc * +axp2xx_reg_attach(device_t dev, phandle_t node, + struct axp2xx_regdef *def) +{ + struct axp2xx_reg_sc *reg_sc; + struct regnode_init_def initdef; + struct regnode *regnode; + + memset(&initdef, 0, sizeof(initdef)); + if (regulator_parse_ofw_stdparam(dev, node, &initdef) != 0) { + device_printf(dev, "cannot create regulator\n"); + return (NULL); + } + if (initdef.std_param.min_uvolt == 0) + initdef.std_param.min_uvolt = def->voltage_min * 1000; + if (initdef.std_param.max_uvolt == 0) + initdef.std_param.max_uvolt = def->voltage_max * 1000; + initdef.id = def->id; + initdef.ofw_node = node; + regnode = regnode_create(dev, &axp2xx_regnode_class, &initdef); + if (regnode == NULL) { + device_printf(dev, "cannot create regulator\n"); + return (NULL); + } + + reg_sc = regnode_get_softc(regnode); + reg_sc->regnode = regnode; + reg_sc->base_dev = dev; + reg_sc->def = def; + reg_sc->xref = OF_xref_from_node(node); + reg_sc->param = regnode_get_stdparam(regnode); + + regnode_register(regnode); + + return (reg_sc); +} + +static int +axp2xx_regdev_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, + intptr_t *num) +{ + struct axp2xx_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 void +axp2xx_start(void *pdev) +{ + device_t dev; + struct axp2xx_softc *sc; + const char *pwr_name[] = {"Battery", "AC", "USB", "AC and USB"}; + int i; + uint8_t reg, data; + uint8_t pwr_src; + + dev = pdev; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (bootverbose) { + /* + * Read the Power State register. + * Shift the AC presence into bit 0. + * Shift the Battery presence into bit 1. + */ + axp2xx_read(dev, AXP2XX_PSR, &data, 1); + pwr_src = ((data & AXP2XX_PSR_ACIN) >> AXP2XX_PSR_ACIN_SHIFT) | + ((data & AXP2XX_PSR_VBUS) >> (AXP2XX_PSR_VBUS_SHIFT - 1)); + + device_printf(dev, "Powered by %s\n", + pwr_name[pwr_src]); + } + + /* Only enable interrupts that we are interested in */ + axp2xx_write(dev, AXP2XX_IRQ1_ENABLE, + AXP2XX_IRQ1_AC_OVERVOLT | + AXP2XX_IRQ1_AC_DISCONN | + AXP2XX_IRQ1_AC_CONN | + AXP2XX_IRQ1_VBUS_OVERVOLT | + AXP2XX_IRQ1_VBUS_DISCONN | + AXP2XX_IRQ1_VBUS_CONN); + axp2xx_write(dev, AXP2XX_IRQ2_ENABLE, + AXP2XX_IRQ2_BATT_CONN | + AXP2XX_IRQ2_BATT_DISCONN | + AXP2XX_IRQ2_BATT_CHARGE_ACCT_ON | + AXP2XX_IRQ2_BATT_CHARGE_ACCT_OFF | + AXP2XX_IRQ2_BATT_CHARGING | + AXP2XX_IRQ2_BATT_CHARGED | + AXP2XX_IRQ2_BATT_TEMP_OVER | + AXP2XX_IRQ2_BATT_TEMP_LOW); + axp2xx_write(dev, AXP2XX_IRQ3_ENABLE, + AXP2XX_IRQ3_PEK_SHORT | AXP2XX_IRQ3_PEK_LONG); + axp2xx_write(dev, AXP2XX_IRQ4_ENABLE, AXP2XX_IRQ4_APS_LOW_2); + axp2xx_write(dev, AXP2XX_IRQ5_ENABLE, 0x0); + + EVENTHANDLER_REGISTER(shutdown_final, axp2xx_shutdown, dev, + SHUTDOWN_PRI_LAST); + + /* Enable ADC sensors */ + for (i = 0; i < sc->nsensors; i++) { + if (axp2xx_read(dev, sc->sensors[i].enable_reg, ®, 1) == -1) { + device_printf(dev, "Cannot enable sensor '%s'\n", + sc->sensors[i].name); + continue; + } + reg |= sc->sensors[i].enable_mask; + if (axp2xx_write(dev, sc->sensors[i].enable_reg, reg) == -1) { + device_printf(dev, "Cannot enable sensor '%s'\n", + sc->sensors[i].name); + continue; + } + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, sc->sensors[i].name, + CTLTYPE_INT | CTLFLAG_RD, + dev, sc->sensors[i].id, axp2xx_sysctl, + sc->sensors[i].format, + sc->sensors[i].desc); + } + + if ((bus_setup_intr(dev, sc->res[0], INTR_TYPE_MISC | INTR_MPSAFE, + NULL, axp2xx_intr, sc, &sc->intrcookie))) + device_printf(dev, "unable to register interrupt handler\n"); + + config_intrhook_disestablish(&sc->intr_hook); +} + +static int +axp2xx_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) + { + case AXP209: + device_set_desc(dev, "X-Powers AXP209 Power Management Unit"); + break; + case AXP221: + device_set_desc(dev, "X-Powers AXP221 Power Management Unit"); + break; + default: + return (ENXIO); + } + + return (BUS_PROBE_DEFAULT); +} + +static int +axp2xx_attach(device_t dev) +{ + struct axp2xx_softc *sc; + struct axp2xx_reg_sc *reg; + struct axp2xx_regdef *regdefs; + phandle_t rnode, child; + int i; + + sc = device_get_softc(dev); + mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + if (bus_alloc_resources(dev, axp_res_spec, sc->res) != 0) { + device_printf(dev, "can't allocate device resources\n"); + return (ENXIO); + } + + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + switch (sc->type) { + case AXP209: + sc->pins = axp209_pins; + sc->npins = nitems(axp209_pins); + sc->gpiodev = gpiobus_attach_bus(dev); + + sc->sensors = axp209_sensors; + sc->nsensors = nitems(axp209_sensors); + + regdefs = axp209_regdefs; + sc->nregs = nitems(axp209_regdefs); + break; + case AXP221: + sc->pins = axp221_pins; + sc->npins = nitems(axp221_pins); + sc->gpiodev = gpiobus_attach_bus(dev); + + sc->sensors = axp221_sensors; + sc->nsensors = nitems(axp221_sensors); + + regdefs = axp221_regdefs; + sc->nregs = nitems(axp221_regdefs); + break; + } + + sc->regs = malloc(sizeof(struct axp2xx_reg_sc *) * sc->nregs, + M_AXP2XX_REG, M_WAITOK | M_ZERO); + + sc->intr_hook.ich_func = axp2xx_start; + sc->intr_hook.ich_arg = dev; + + if (config_intrhook_establish(&sc->intr_hook) != 0) + return (ENOMEM); + + /* Attach known regulators that exist in the DT */ + rnode = ofw_bus_find_child(ofw_bus_get_node(dev), "regulators"); + if (rnode > 0) { + for (i = 0; i < sc->nregs; i++) { + child = ofw_bus_find_child(rnode, + regdefs[i].name); + if (child == 0) + continue; + reg = axp2xx_reg_attach(dev, child, ®defs[i]); + if (reg == NULL) { + device_printf(dev, + "cannot attach regulator %s\n", + regdefs[i].name); + continue; + } + sc->regs[i] = reg; + if (bootverbose) + device_printf(dev, "Regulator %s attached\n", + regdefs[i].name); + } + } + + return (0); +} + +static device_method_t axp2xx_methods[] = { + DEVMETHOD(device_probe, axp2xx_probe), + DEVMETHOD(device_attach, axp2xx_attach), + + /* GPIO interface */ + DEVMETHOD(gpio_get_bus, axp2xx_gpio_get_bus), + DEVMETHOD(gpio_pin_max, axp2xx_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, axp2xx_gpio_pin_getname), + DEVMETHOD(gpio_pin_getcaps, axp2xx_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_getflags, axp2xx_gpio_pin_getflags), + DEVMETHOD(gpio_pin_setflags, axp2xx_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, axp2xx_gpio_pin_get), + DEVMETHOD(gpio_pin_set, axp2xx_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, axp2xx_gpio_pin_toggle), + DEVMETHOD(gpio_map_gpios, axp2xx_gpio_map_gpios), + + /* Regdev interface */ + DEVMETHOD(regdev_map, axp2xx_regdev_map), + + /* OFW bus interface */ + DEVMETHOD(ofw_bus_get_node, axp2xx_get_node), + + DEVMETHOD_END +}; + +static driver_t axp2xx_driver = { + "axp2xx_pmu", + axp2xx_methods, + sizeof(struct axp2xx_softc), +}; + +static devclass_t axp2xx_devclass; +extern devclass_t ofwgpiobus_devclass, gpioc_devclass; +extern driver_t ofw_gpiobus_driver, gpioc_driver; + +EARLY_DRIVER_MODULE(axp2xx, iicbus, axp2xx_driver, axp2xx_devclass, + 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); +EARLY_DRIVER_MODULE(ofw_gpiobus, axp2xx_pmu, ofw_gpiobus_driver, + ofwgpiobus_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); +DRIVER_MODULE(gpioc, axp2xx_pmu, gpioc_driver, gpioc_devclass, + 0, 0); +MODULE_VERSION(axp2xx, 1); +MODULE_DEPEND(axp2xx, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); Property changes on: trunk/sys/arm/allwinner/axp209.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/axp209reg.h =================================================================== --- trunk/sys/arm/allwinner/axp209reg.h (rev 0) +++ trunk/sys/arm/allwinner/axp209reg.h 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,249 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Emmanuel Vadot + * 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: stable/11/sys/arm/allwinner/axp209reg.h 309758 2016-12-09 20:17:07Z manu $ + */ + +#ifndef _AXP209REG_H_ +#define _AXP209REG_H_ + +/* Power State Register */ +#define AXP2XX_PSR 0x00 +#define AXP2XX_PSR_ACIN 0x80 +#define AXP2XX_PSR_ACIN_SHIFT 7 +#define AXP2XX_PSR_VBUS 0x20 +#define AXP2XX_PSR_VBUS_SHIFT 5 + +/* Shutdown and battery control */ +#define AXP2XX_SHUTBAT 0x32 +#define AXP2XX_SHUTBAT_SHUTDOWN 0x80 + +/* Voltage/Current Monitor */ +#define AXP209_ACIN_VOLTAGE 0x56 +#define AXP209_ACIN_CURRENT 0x58 +#define AXP209_VBUS_VOLTAGE 0x5A +#define AXP209_VBUS_CURRENT 0x5C +#define AXP2XX_BAT_VOLTAGE 0x78 +#define AXP2XX_BAT_CHARGE_CURRENT 0x7A +#define AXP2XX_BAT_DISCHARGE_CURRENT 0x7C + +#define AXP209_VOLT_STEP 1700 +#define AXP2XX_BATVOLT_STEP 1100 +#define AXP209_ACCURRENT_STEP 625 +#define AXP209_VBUSCURRENT_STEP 375 +#define AXP2XX_BATCURRENT_STEP 500 + +/* Temperature monitor */ +#define AXP209_TEMPMON 0x5e +#define AXP209_TEMPMON_MIN 1447 /* -144.7C */ +#define AXP221_TEMPMON_MIN 2437 /* -243.7C */ + +#define AXP221_TEMPMON 0x56 + +/* Sensors conversion macros */ +#define AXP209_SENSOR_H(a) ((a) << 4) +#define AXP209_SENSOR_L(a) ((a) & 0xf) +#define AXP209_SENSOR_BAT_H(a) ((a) << 5) +#define AXP209_SENSOR_BAT_L(a) ((a) & 0x1f) + +#define AXP209_0C_TO_K 2732 + +/* ADC Sensors */ +#define AXP2XX_ADC_ENABLE1 0x82 +#define AXP209_ADC_ENABLE2 0x83 + +#define AXP2XX_ADC1_BATVOLT (1 << 7) +#define AXP2XX_ADC1_BATCURRENT (1 << 6) +#define AXP209_ADC1_ACVOLT (1 << 5) +#define AXP221_ADC1_TEMP (1 << 5) +#define AXP209_ADC1_ACCURRENT (1 << 4) +#define AXP209_ADC1_VBUSVOLT (1 << 3) +#define AXP209_ADC1_VBUSCURRENT (1 << 2) +#define AXP221_ADC1_TS_PIN (1 << 0) + +#define AXP209_ADC2_TEMP (1 << 7) + +/* Interrupt related registers */ +#define AXP2XX_IRQ1_ENABLE 0x40 +#define AXP2XX_IRQ1_STATUS 0x48 +#define AXP2XX_IRQ1_AC_OVERVOLT (1 << 7) +#define AXP2XX_IRQ1_AC_CONN (1 << 6) +#define AXP2XX_IRQ1_AC_DISCONN (1 << 5) +#define AXP2XX_IRQ1_VBUS_OVERVOLT (1 << 4) +#define AXP2XX_IRQ1_VBUS_CONN (1 << 3) +#define AXP2XX_IRQ1_VBUS_DISCONN (1 << 2) +#define AXP2XX_IRQ1_VBUS_LOW (1 << 1) + +#define AXP2XX_IRQ2_ENABLE 0x41 +#define AXP2XX_IRQ2_STATUS 0x49 +#define AXP2XX_IRQ2_BATT_CONN (1 << 7) +#define AXP2XX_IRQ2_BATT_DISCONN (1 << 6) +#define AXP2XX_IRQ2_BATT_CHARGE_ACCT_ON (1 << 5) +#define AXP2XX_IRQ2_BATT_CHARGE_ACCT_OFF (1 << 4) +#define AXP2XX_IRQ2_BATT_CHARGING (1 << 3) +#define AXP2XX_IRQ2_BATT_CHARGED (1 << 2) +#define AXP2XX_IRQ2_BATT_TEMP_OVER (1 << 1) +#define AXP2XX_IRQ2_BATT_TEMP_LOW (1 << 0) + +#define AXP2XX_IRQ3_ENABLE 0x42 +#define AXP2XX_IRQ3_STATUS 0x4A +#define AXP2XX_IRQ3_TEMP_OVER (1 << 7) +#define AXP2XX_IRQ3_CHARGE_CURRENT_LOW (1 << 6) +#define AXP2XX_IRQ3_DCDC2_LOW (1 << 4) +#define AXP2XX_IRQ3_DCDC3_LOW (1 << 3) +#define AXP2XX_IRQ3_LDO3_LOW (1 << 2) +#define AXP2XX_IRQ3_PEK_SHORT (1 << 1) +#define AXP2XX_IRQ3_PEK_LONG (1 << 0) + +#define AXP2XX_IRQ4_ENABLE 0x43 +#define AXP2XX_IRQ4_STATUS 0x4B +#define AXP2XX_IRQ4_NOE_START (1 << 7) +#define AXP2XX_IRQ4_NOE_SHUT (1 << 6) +#define AXP2XX_IRQ4_VBUS_VALID (1 << 5) +#define AXP2XX_IRQ4_VBUS_INVALID (1 << 4) +#define AXP2XX_IRQ4_VBUS_SESSION (1 << 3) +#define AXP2XX_IRQ4_VBUS_SESSION_END (1 << 2) +#define AXP2XX_IRQ4_APS_LOW_1 (1 << 1) +#define AXP2XX_IRQ4_APS_LOW_2 (1 << 0) + +#define AXP2XX_IRQ5_ENABLE 0x44 +#define AXP2XX_IRQ5_STATUS 0x4C +#define AXP2XX_IRQ5_TIMER_EXPIRE (1 << 7) +#define AXP2XX_IRQ5_PEK_RISE_EDGE (1 << 6) +#define AXP2XX_IRQ5_PEK_FALL_EDGE (1 << 5) +#define AXP2XX_IRQ5_GPIO3 (1 << 3) +#define AXP2XX_IRQ5_GPIO2 (1 << 2) +#define AXP2XX_IRQ5_GPIO1 (1 << 1) +#define AXP2XX_IRQ5_GPIO0 (1 << 0) + +#define AXP2XX_IRQ_ACK 0xff + +/* GPIOs registers */ +#define AXP2XX_GPIO_FUNC_MASK 0x7 + +#define AXP2XX_GPIO_FUNC_DRVLO 0x0 +#define AXP2XX_GPIO_FUNC_DRVHI 0x1 +#define AXP2XX_GPIO_FUNC_INPUT 0x2 + +#define AXP2XX_GPIO0_CTRL 0x90 +#define AXP2XX_GPIO1_CTRL 0x92 +#define AXP209_GPIO2_CTRL 0x93 +#define AXP2XX_GPIO_STATUS 0x94 + +/* Regulators registers */ +#define AXP209_POWERCTL 0x12 +#define AXP209_POWERCTL_LDO3 (1 << 6) +#define AXP209_POWERCTL_DCDC2 (1 << 4) +#define AXP209_POWERCTL_LDO4 (1 << 3) +#define AXP209_POWERCTL_LDO2 (1 << 2) +#define AXP209_POWERCTL_DCDC3 (1 << 1) + +#define AXP221_POWERCTL_1 0x10 +#define AXP221_POWERCTL1_ALDO2 (1 << 7) +#define AXP221_POWERCTL1_ALDO1 (1 << 6) +#define AXP221_POWERCTL1_DCDC5 (1 << 5) +#define AXP221_POWERCTL1_DCDC4 (1 << 4) +#define AXP221_POWERCTL1_DCDC3 (1 << 3) +#define AXP221_POWERCTL1_DCDC2 (1 << 2) +#define AXP221_POWERCTL1_DCDC1 (1 << 1) +#define AXP221_POWERCTL1_DC5LDO (1 << 0) + +#define AXP221_POWERCTL_2 0x12 +#define AXP221_POWERCTL2_DC1SW (1 << 7) +#define AXP221_POWERCTL2_DLDO4 (1 << 6) +#define AXP221_POWERCTL2_DLDO3 (1 << 5) +#define AXP221_POWERCTL2_DLDO2 (1 << 4) +#define AXP221_POWERCTL2_DLDO1 (1 << 3) +#define AXP221_POWERCTL2_ELDO3 (1 << 2) +#define AXP221_POWERCTL2_ELDO2 (1 << 1) +#define AXP221_POWERCTL2_ELDO1 (1 << 0) + +#define AXP221_POWERCTL_3 0x14 +#define AXP221_POWERCTL3_ALDO3 (1 << 7) + +#define AXP209_REG_DCDC2_VOLTAGE 0x23 +#define AXP209_REG_DCDC3_VOLTAGE 0x27 +#define AXP209_REG_LDO24_VOLTAGE 0x28 +#define AXP209_REG_LDO3_VOLTAGE 0x29 + +#define AXP221_REG_DLDO1_VOLTAGE 0x15 +#define AXP221_REG_DLDO2_VOLTAGE 0x16 +#define AXP221_REG_DLDO3_VOLTAGE 0x17 +#define AXP221_REG_DLDO4_VOLTAGE 0x18 +#define AXP221_REG_ELDO1_VOLTAGE 0x19 +#define AXP221_REG_ELDO2_VOLTAGE 0x1A +#define AXP221_REG_ELDO3_VOLTAGE 0x1B +#define AXP221_REG_DC5LDO_VOLTAGE 0x1C +#define AXP221_REG_DCDC1_VOLTAGE 0x21 +#define AXP221_REG_DCDC2_VOLTAGE 0x22 +#define AXP221_REG_DCDC3_VOLTAGE 0x23 +#define AXP221_REG_DCDC4_VOLTAGE 0x24 +#define AXP221_REG_DCDC5_VOLTAGE 0x25 +#define AXP221_REG_DCDC23_VRC 0x27 +#define AXP221_REG_ALDO1_VOLTAGE 0x28 +#define AXP221_REG_ALDO2_VOLTAGE 0x29 +#define AXP221_REG_ALDO3_VOLTAGE 0x2A + + +enum axp2xx_sensor { + AXP209_ACVOLT, + AXP209_ACCURRENT, + AXP209_VBUSVOLT, + AXP209_VBUSCURRENT, + AXP2XX_TEMP, + AXP2XX_BATVOLT, + AXP2XX_BATCHARGECURRENT, + AXP2XX_BATDISCHARGECURRENT, +}; + +enum axp2xx_regulators { + AXP209_REG_ID_DCDC2, + AXP209_REG_ID_DCDC3, + AXP209_REG_ID_LDO1, + AXP209_REG_ID_LDO2, + AXP209_REG_ID_LDO3, + /* LDO4 is weird, need to find a correct way to handle it */ + /* AXP209_REG_ID_LDO4, */ + AXP209_REG_ID_LDO5, + AXP221_REG_ID_DLDO1, + AXP221_REG_ID_DLDO2, + AXP221_REG_ID_DLDO3, + AXP221_REG_ID_DLDO4, + AXP221_REG_ID_ELDO1, + AXP221_REG_ID_ELDO2, + AXP221_REG_ID_ELDO3, + AXP221_REG_ID_DC5LDO, + AXP221_REG_ID_DCDC1, + AXP221_REG_ID_DCDC2, + AXP221_REG_ID_DCDC3, + AXP221_REG_ID_DCDC4, + AXP221_REG_ID_DCDC5, + AXP221_REG_ID_ALDO1, + AXP221_REG_ID_ALDO2, + AXP221_REG_ID_ALDO3, + AXP221_REG_ID_DC1SW, +}; + +#endif /* _AXP209REG_H_ */ Property changes on: trunk/sys/arm/allwinner/axp209reg.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/axp81x.c =================================================================== --- trunk/sys/arm/allwinner/axp81x.c (rev 0) +++ trunk/sys/arm/allwinner/axp81x.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,520 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/axp81x.c 299862 2016-05-15 16:43:47Z jmcneill $ + */ + +/* + * X-Powers AXP813/818 PMU for Allwinner SoCs + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/axp81x.c 299862 2016-05-15 16:43:47Z jmcneill $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include "iicbus_if.h" +#include "gpio_if.h" + +#define AXP_ICTYPE 0x03 +#define AXP_POWERBAT 0x32 +#define AXP_POWERBAT_SHUTDOWN (1 << 7) +#define AXP_IRQEN1 0x40 +#define AXP_IRQEN2 0x41 +#define AXP_IRQEN3 0x42 +#define AXP_IRQEN4 0x43 +#define AXP_IRQEN5 0x44 +#define AXP_IRQEN5_POKSIRQ (1 << 4) +#define AXP_IRQEN6 0x45 +#define AXP_IRQSTAT5 0x4c +#define AXP_IRQSTAT5_POKSIRQ (1 << 4) +#define AXP_GPIO0_CTRL 0x90 +#define AXP_GPIO1_CTRL 0x92 +#define AXP_GPIO_FUNC (0x7 << 0) +#define AXP_GPIO_FUNC_SHIFT 0 +#define AXP_GPIO_FUNC_DRVLO 0 +#define AXP_GPIO_FUNC_DRVHI 1 +#define AXP_GPIO_FUNC_INPUT 2 +#define AXP_GPIO_SIGBIT 0x94 +#define AXP_GPIO_PD 0x97 + +static const struct { + const char *name; + uint8_t ctrl_reg; +} axp81x_pins[] = { + { "GPIO0", AXP_GPIO0_CTRL }, + { "GPIO1", AXP_GPIO1_CTRL }, +}; + +static struct ofw_compat_data compat_data[] = { + { "x-powers,axp813", 1 }, + { "x-powers,axp818", 1 }, + { NULL, 0 } +}; + +static struct resource_spec axp81x_spec[] = { + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; + +struct axp81x_softc { + struct resource *res; + uint16_t addr; + void *ih; + device_t gpiodev; + struct mtx mtx; + int busy; +}; + +#define AXP_LOCK(sc) mtx_lock(&(sc)->mtx) +#define AXP_UNLOCK(sc) mtx_unlock(&(sc)->mtx) + +static int +axp81x_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) +{ + struct axp81x_softc *sc; + struct iic_msg msg[2]; + + sc = device_get_softc(dev); + + msg[0].slave = sc->addr; + msg[0].flags = IIC_M_WR; + msg[0].len = 1; + msg[0].buf = ® + + msg[1].slave = sc->addr; + msg[1].flags = IIC_M_RD; + msg[1].len = size; + msg[1].buf = data; + + return (iicbus_transfer(dev, msg, 2)); +} + +static int +axp81x_write(device_t dev, uint8_t reg, uint8_t val) +{ + struct axp81x_softc *sc; + struct iic_msg msg[2]; + + sc = device_get_softc(dev); + + msg[0].slave = sc->addr; + msg[0].flags = IIC_M_WR; + msg[0].len = 1; + msg[0].buf = ® + + msg[1].slave = sc->addr; + msg[1].flags = IIC_M_WR; + msg[1].len = 1; + msg[1].buf = &val; + + return (iicbus_transfer(dev, msg, 2)); +} + +static void +axp81x_shutdown(void *devp, int howto) +{ + device_t dev; + + if ((howto & RB_POWEROFF) == 0) + return; + + dev = devp; + + if (bootverbose) + device_printf(dev, "Shutdown AXP81x\n"); + + axp81x_write(dev, AXP_POWERBAT, AXP_POWERBAT_SHUTDOWN); +} + +static void +axp81x_intr(void *arg) +{ + struct axp81x_softc *sc; + device_t dev; + uint8_t val; + int error; + + dev = arg; + sc = device_get_softc(dev); + + error = axp81x_read(dev, AXP_IRQSTAT5, &val, 1); + if (error != 0) + return; + + if (val != 0) { + if ((val & AXP_IRQSTAT5_POKSIRQ) != 0) { + if (bootverbose) + device_printf(dev, "Power button pressed\n"); + shutdown_nice(RB_POWEROFF); + } + /* Acknowledge */ + axp81x_write(dev, AXP_IRQSTAT5, val); + } +} + +static device_t +axp81x_gpio_get_bus(device_t dev) +{ + struct axp81x_softc *sc; + + sc = device_get_softc(dev); + + return (sc->gpiodev); +} + +static int +axp81x_gpio_pin_max(device_t dev, int *maxpin) +{ + *maxpin = nitems(axp81x_pins) - 1; + + return (0); +} + +static int +axp81x_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + if (pin >= nitems(axp81x_pins)) + return (EINVAL); + + snprintf(name, GPIOMAXNAME, "%s", axp81x_pins[pin].name); + + return (0); +} + +static int +axp81x_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + if (pin >= nitems(axp81x_pins)) + return (EINVAL); + + *caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT; + + return (0); +} + +static int +axp81x_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct axp81x_softc *sc; + uint8_t data, func; + int error; + + if (pin >= nitems(axp81x_pins)) + return (EINVAL); + + sc = device_get_softc(dev); + + AXP_LOCK(sc); + error = axp81x_read(dev, axp81x_pins[pin].ctrl_reg, &data, 1); + if (error == 0) { + func = (data & AXP_GPIO_FUNC) >> AXP_GPIO_FUNC_SHIFT; + if (func == AXP_GPIO_FUNC_INPUT) + *flags = GPIO_PIN_INPUT; + else if (func == AXP_GPIO_FUNC_DRVLO || + func == AXP_GPIO_FUNC_DRVHI) + *flags = GPIO_PIN_OUTPUT; + else + *flags = 0; + } + AXP_UNLOCK(sc); + + return (error); +} + +static int +axp81x_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct axp81x_softc *sc; + uint8_t data; + int error; + + if (pin >= nitems(axp81x_pins)) + return (EINVAL); + + sc = device_get_softc(dev); + + AXP_LOCK(sc); + error = axp81x_read(dev, axp81x_pins[pin].ctrl_reg, &data, 1); + if (error == 0) { + data &= ~AXP_GPIO_FUNC; + if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) != 0) { + if ((flags & GPIO_PIN_OUTPUT) == 0) + data |= AXP_GPIO_FUNC_INPUT; + } + error = axp81x_write(dev, axp81x_pins[pin].ctrl_reg, data); + } + AXP_UNLOCK(sc); + + return (error); +} + +static int +axp81x_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) +{ + struct axp81x_softc *sc; + uint8_t data, func; + int error; + + if (pin >= nitems(axp81x_pins)) + return (EINVAL); + + sc = device_get_softc(dev); + + AXP_LOCK(sc); + error = axp81x_read(dev, axp81x_pins[pin].ctrl_reg, &data, 1); + if (error == 0) { + func = (data & AXP_GPIO_FUNC) >> AXP_GPIO_FUNC_SHIFT; + switch (func) { + case AXP_GPIO_FUNC_DRVLO: + *val = 0; + break; + case AXP_GPIO_FUNC_DRVHI: + *val = 1; + break; + case AXP_GPIO_FUNC_INPUT: + error = axp81x_read(dev, AXP_GPIO_SIGBIT, &data, 1); + if (error == 0) + *val = (data & (1 << pin)) ? 1 : 0; + break; + default: + error = EIO; + break; + } + } + AXP_UNLOCK(sc); + + return (error); +} + +static int +axp81x_gpio_pin_set(device_t dev, uint32_t pin, unsigned int val) +{ + struct axp81x_softc *sc; + uint8_t data, func; + int error; + + if (pin >= nitems(axp81x_pins)) + return (EINVAL); + + sc = device_get_softc(dev); + + AXP_LOCK(sc); + error = axp81x_read(dev, axp81x_pins[pin].ctrl_reg, &data, 1); + if (error == 0) { + func = (data & AXP_GPIO_FUNC) >> AXP_GPIO_FUNC_SHIFT; + switch (func) { + case AXP_GPIO_FUNC_DRVLO: + case AXP_GPIO_FUNC_DRVHI: + data &= ~AXP_GPIO_FUNC; + data |= (val << AXP_GPIO_FUNC_SHIFT); + break; + default: + error = EIO; + break; + } + } + if (error == 0) + error = axp81x_write(dev, axp81x_pins[pin].ctrl_reg, data); + AXP_UNLOCK(sc); + + return (error); +} + + +static int +axp81x_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct axp81x_softc *sc; + uint8_t data, func; + int error; + + if (pin >= nitems(axp81x_pins)) + return (EINVAL); + + sc = device_get_softc(dev); + + AXP_LOCK(sc); + error = axp81x_read(dev, axp81x_pins[pin].ctrl_reg, &data, 1); + if (error == 0) { + func = (data & AXP_GPIO_FUNC) >> AXP_GPIO_FUNC_SHIFT; + switch (func) { + case AXP_GPIO_FUNC_DRVLO: + data &= ~AXP_GPIO_FUNC; + data |= (AXP_GPIO_FUNC_DRVHI << AXP_GPIO_FUNC_SHIFT); + break; + case AXP_GPIO_FUNC_DRVHI: + data &= ~AXP_GPIO_FUNC; + data |= (AXP_GPIO_FUNC_DRVLO << AXP_GPIO_FUNC_SHIFT); + break; + default: + error = EIO; + break; + } + } + if (error == 0) + error = axp81x_write(dev, axp81x_pins[pin].ctrl_reg, data); + AXP_UNLOCK(sc); + + return (error); +} + +static int +axp81x_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, + int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) +{ + if (gpios[0] >= nitems(axp81x_pins)) + return (EINVAL); + + *pin = gpios[0]; + *flags = gpios[1]; + + return (0); +} + +static phandle_t +axp81x_get_node(device_t dev, device_t bus) +{ + return (ofw_bus_get_node(dev)); +} + +static int +axp81x_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, "X-Powers AXP81x Power Management Unit"); + + return (BUS_PROBE_DEFAULT); +} + +static int +axp81x_attach(device_t dev) +{ + struct axp81x_softc *sc; + uint8_t chip_id; + int error; + + sc = device_get_softc(dev); + + sc->addr = iicbus_get_addr(dev); + mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + error = bus_alloc_resources(dev, axp81x_spec, &sc->res); + if (error != 0) { + device_printf(dev, "cannot allocate resources for device\n"); + return (error); + } + + if (bootverbose) { + axp81x_read(dev, AXP_ICTYPE, &chip_id, 1); + device_printf(dev, "chip ID 0x%02x\n", chip_id); + } + + /* Enable IRQ on short power key press */ + axp81x_write(dev, AXP_IRQEN1, 0); + axp81x_write(dev, AXP_IRQEN2, 0); + axp81x_write(dev, AXP_IRQEN3, 0); + axp81x_write(dev, AXP_IRQEN4, 0); + axp81x_write(dev, AXP_IRQEN5, AXP_IRQEN5_POKSIRQ); + axp81x_write(dev, AXP_IRQEN6, 0); + + /* Install interrupt handler */ + error = bus_setup_intr(dev, sc->res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, axp81x_intr, dev, &sc->ih); + if (error != 0) { + device_printf(dev, "cannot setup interrupt handler\n"); + return (error); + } + + EVENTHANDLER_REGISTER(shutdown_final, axp81x_shutdown, dev, + SHUTDOWN_PRI_LAST); + + sc->gpiodev = gpiobus_attach_bus(dev); + + return (0); +} + +static device_method_t axp81x_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, axp81x_probe), + DEVMETHOD(device_attach, axp81x_attach), + + /* GPIO interface */ + DEVMETHOD(gpio_get_bus, axp81x_gpio_get_bus), + DEVMETHOD(gpio_pin_max, axp81x_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, axp81x_gpio_pin_getname), + DEVMETHOD(gpio_pin_getcaps, axp81x_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_getflags, axp81x_gpio_pin_getflags), + DEVMETHOD(gpio_pin_setflags, axp81x_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, axp81x_gpio_pin_get), + DEVMETHOD(gpio_pin_set, axp81x_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, axp81x_gpio_pin_toggle), + DEVMETHOD(gpio_map_gpios, axp81x_gpio_map_gpios), + + /* OFW bus interface */ + DEVMETHOD(ofw_bus_get_node, axp81x_get_node), + + DEVMETHOD_END +}; + +static driver_t axp81x_driver = { + "axp81x_pmu", + axp81x_methods, + sizeof(struct axp81x_softc), +}; + +static devclass_t axp81x_devclass; +extern devclass_t ofwgpiobus_devclass, gpioc_devclass; +extern driver_t ofw_gpiobus_driver, gpioc_driver; + +DRIVER_MODULE(axp81x, iicbus, axp81x_driver, axp81x_devclass, 0, 0); +DRIVER_MODULE(ofw_gpiobus, axp81x_pmu, ofw_gpiobus_driver, + ofwgpiobus_devclass, 0, 0); +DRIVER_MODULE(gpioc, axp81x_pmu, gpioc_driver, gpioc_devclass, 0, 0); +MODULE_VERSION(axp81x, 1); +MODULE_DEPEND(axp81x, iicbus, 1, 1, 1); Property changes on: trunk/sys/arm/allwinner/axp81x.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Modified: trunk/sys/arm/allwinner/console.c =================================================================== --- trunk/sys/arm/allwinner/console.c 2020-03-08 17:21:06 UTC (rev 12401) +++ trunk/sys/arm/allwinner/console.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -28,7 +28,7 @@ /* Simple UART console driver for Allwinner A10 */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/allwinner/console.c 266337 2014-05-17 18:53:36Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/console.c 263711 2014-03-25 08:31:47Z ganbold $"); #include #include Deleted: trunk/sys/arm/allwinner/files.a10 =================================================================== --- trunk/sys/arm/allwinner/files.a10 2020-03-08 17:21:06 UTC (rev 12401) +++ trunk/sys/arm/allwinner/files.a10 2020-03-08 17:23:22 UTC (rev 12402) @@ -1,23 +0,0 @@ -# $FreeBSD: stable/10/sys/arm/allwinner/files.a10 278727 2015-02-13 22:32:02Z ian $ -kern/kern_clocksource.c standard - -arm/arm/bus_space_asm_generic.S standard -arm/arm/bus_space_generic.c standard -arm/arm/cpufunc_asm_armv5.S standard -arm/arm/cpufunc_asm_arm10.S standard -arm/arm/cpufunc_asm_arm11.S standard -arm/arm/cpufunc_asm_armv7.S standard - -arm/allwinner/a10_clk.c standard -arm/allwinner/a10_common.c standard -arm/allwinner/a10_gpio.c optional gpio -arm/allwinner/a10_ehci.c optional ehci -arm/allwinner/a10_machdep.c standard -arm/allwinner/a10_sramc.c standard -arm/allwinner/a10_wdog.c standard -arm/allwinner/a20/a20_cpu_cfg.c standard -arm/allwinner/aintc.c standard -arm/allwinner/if_emac.c optional emac -arm/allwinner/timer.c standard -arm/arm/bus_space_base.c standard -#arm/allwinner/console.c standard Added: trunk/sys/arm/allwinner/files.allwinner =================================================================== --- trunk/sys/arm/allwinner/files.allwinner (rev 0) +++ trunk/sys/arm/allwinner/files.allwinner 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,55 @@ +# $MidnightBSD$ +kern/kern_clocksource.c standard + +arm/allwinner/a10_ahci.c optional ahci +arm/allwinner/a10_codec.c optional sound +arm/allwinner/a10_common.c standard +arm/allwinner/a10_dmac.c standard +arm/allwinner/a10_ehci.c optional ehci +arm/allwinner/aw_usbphy.c optional ehci +arm/allwinner/a10_gpio.c optional gpio +arm/allwinner/a10_mmc.c optional mmc +arm/allwinner/a10_sramc.c standard +arm/allwinner/aw_nmi.c optional intrng +arm/allwinner/aw_if_dwc.c optional dwc +arm/allwinner/aw_rsb.c optional rsb +arm/allwinner/aw_rtc.c standard +arm/allwinner/aw_ts.c standard +arm/allwinner/aw_wdog.c standard +arm/allwinner/aw_machdep.c standard +arm/allwinner/aw_mp.c optional smp +arm/allwinner/axp209.c optional axp209 +arm/allwinner/axp81x.c optional axp81x +arm/allwinner/if_awg.c optional awg +arm/allwinner/if_emac.c optional emac +arm/allwinner/sunxi_dma_if.m standard +dev/iicbus/twsi/a10_twsi.c optional twsi +dev/usb/controller/generic_ohci.c optional ohci +dev/usb/controller/generic_usb_if.m optional ohci +arm/allwinner/aw_sid.c standard +arm/allwinner/aw_thermal.c standard +#arm/allwinner/console.c standard + +arm/allwinner/a10_fb.c optional vt +arm/allwinner/a10_hdmi.c optional hdmi +arm/allwinner/a10_hdmiaudio.c optional hdmi sound +arm/arm/hdmi_if.m optional hdmi + +arm/allwinner/aw_reset.c standard +arm/allwinner/aw_ccu.c standard +arm/allwinner/clk/aw_ahbclk.c standard +arm/allwinner/clk/aw_apbclk.c standard +arm/allwinner/clk/aw_axiclk.c standard +arm/allwinner/clk/aw_codecclk.c standard +arm/allwinner/clk/aw_cpuclk.c standard +arm/allwinner/clk/aw_cpusclk.c standard +arm/allwinner/clk/aw_debeclk.c standard +arm/allwinner/clk/aw_gate.c standard +arm/allwinner/clk/aw_gmacclk.c standard +arm/allwinner/clk/aw_hdmiclk.c standard +arm/allwinner/clk/aw_lcdclk.c standard +arm/allwinner/clk/aw_modclk.c standard +arm/allwinner/clk/aw_mmcclk.c standard +arm/allwinner/clk/aw_oscclk.c standard +arm/allwinner/clk/aw_pll.c standard +arm/allwinner/clk/aw_usbclk.c standard Property changes on: trunk/sys/arm/allwinner/files.allwinner ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: trunk/sys/arm/allwinner/files.allwinner_up =================================================================== --- trunk/sys/arm/allwinner/files.allwinner_up (rev 0) +++ trunk/sys/arm/allwinner/files.allwinner_up 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,3 @@ +# $MidnightBSD$ + +arm/allwinner/timer.c standard Property changes on: trunk/sys/arm/allwinner/files.allwinner_up ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: trunk/sys/arm/allwinner/h3/files.h3 =================================================================== --- trunk/sys/arm/allwinner/h3/files.h3 (rev 0) +++ trunk/sys/arm/allwinner/h3/files.h3 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,4 @@ +# $MidnightBSD$ + +arm/allwinner/h3/h3_padconf.c standard +arm/allwinner/h3/h3_r_padconf.c standard Property changes on: trunk/sys/arm/allwinner/h3/files.h3 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: trunk/sys/arm/allwinner/h3/h3_padconf.c =================================================================== --- trunk/sys/arm/allwinner/h3/h3_padconf.c (rev 0) +++ trunk/sys/arm/allwinner/h3/h3_padconf.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,148 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/h3/h3_padconf.c 299688 2016-05-13 18:20:54Z manu $"); + +#include +#include +#include +#include + +#include + +#ifdef SOC_ALLWINNER_H3 + +const static struct allwinner_pins h3_pins[] = { + {"PA0", 0, 0, {"gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "eint", NULL}}, + {"PA1", 0, 1, {"gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "eint", NULL}}, + {"PA2", 0, 2, {"gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "eint", NULL}}, + {"PA3", 0, 3, {"gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "eint", NULL}}, + {"PA4", 0, 4, {"gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, "eint", NULL}}, + {"PA5", 0, 5, {"gpio_in", "gpio_out", "uart0", "pwm0", NULL, NULL, "eint", NULL}}, + {"PA6", 0, 6, {"gpio_in", "gpio_out", "sim", NULL, NULL, NULL, "eint", NULL}}, + {"PA7", 0, 7, {"gpio_in", "gpio_out", "sim", NULL, NULL, NULL, "eint", NULL}}, + {"PA8", 0, 8, {"gpio_in", "gpio_out", "sim", NULL, NULL, NULL, "eint", NULL}}, + {"PA9", 0, 9, {"gpio_in", "gpio_out", "sim", NULL, NULL, NULL, "eint", NULL}}, + {"PA10", 0, 10, {"gpio_in", "gpio_out", "sim", NULL, NULL, NULL, "eint", NULL}}, + {"PA11", 0, 11, {"gpio_in", "gpio_out", "i2c0", "di", NULL, NULL, "eint", NULL}}, + {"PA12", 0, 12, {"gpio_in", "gpio_out", "i2c0", "di", NULL, NULL, "eint", NULL}}, + {"PA13", 0, 13, {"gpio_in", "gpio_out", "spi1", "uart3", NULL, NULL, "eint", NULL}}, + {"PA14", 0, 14, {"gpio_in", "gpio_out", "spi1", "uart3", NULL, NULL, "eint", NULL}}, + {"PA15", 0, 15, {"gpio_in", "gpio_out", "spi1", "uart3", NULL, NULL, "eint", NULL}}, + {"PA16", 0, 16, {"gpio_in", "gpio_out", "spi1", "uart3", NULL, NULL, "eint", NULL}}, + {"PA17", 0, 17, {"gpio_in", "gpio_out", "spdif", NULL, NULL, NULL, "eint", NULL}}, + {"PA18", 0, 18, {"gpio_in", "gpio_out", "i2s0", "i2c1", NULL, NULL, "eint", NULL}}, + {"PA19", 0, 19, {"gpio_in", "gpio_out", "i2s0", "i2c1", NULL, NULL, "eint", NULL}}, + {"PA20", 0, 20, {"gpio_in", "gpio_out", "i2s0", "sim", NULL, NULL, "eint", NULL}}, + {"PA21", 0, 21, {"gpio_in", "gpio_out", "i2s0", "sim", NULL, NULL, "eint", NULL}}, + + {"PC0", 2, 0, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}}, + {"PC1", 2, 1, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}}, + {"PC2", 2, 2, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}}, + {"PC3", 2, 3, {"gpio_in", "gpio_out", "nand", "spi0", NULL, NULL, NULL, NULL}}, + {"PC4", 2, 4, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}}, + {"PC5", 2, 5, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC6", 2, 6, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC7", 2, 7, {"gpio_in", "gpio_out", "nand", NULL, NULL, NULL, NULL, NULL}}, + {"PC8", 2, 8, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC9", 2, 9, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC10", 2, 10, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC11", 2, 11, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC12", 2, 12, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC13", 2, 13, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC14", 2, 14, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC15", 2, 15, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + {"PC16", 2, 16, {"gpio_in", "gpio_out", "nand", "mmc2", NULL, NULL, NULL, NULL}}, + + {"PD0", 3, 0, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD1", 3, 1, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD2", 3, 2, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD3", 3, 3, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD4", 3, 4, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD5", 3, 5, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD6", 3, 6, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD7", 3, 7, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD8", 3, 8, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD9", 3, 9, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD10", 3, 10, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD11", 3, 11, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD12", 3, 12, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD13", 3, 13, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD14", 3, 14, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD15", 3, 15, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD16", 3, 16, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + {"PD17", 3, 17, {"gpio_in", "gpio_out", "emac", NULL, NULL, NULL, NULL, NULL}}, + + {"PE0", 4, 0, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE1", 4, 1, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE2", 4, 2, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE3", 4, 3, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE4", 4, 4, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE5", 4, 5, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE6", 4, 6, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE7", 4, 7, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE8", 4, 8, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE9", 4, 9, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE10", 4, 10, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE11", 4, 11, {"gpio_in", "gpio_out", "csi", "ts", NULL, NULL, NULL, NULL}}, + {"PE12", 4, 12, {"gpio_in", "gpio_out", "csi", "i2c2", NULL, NULL, NULL, NULL}}, + {"PE13", 4, 13, {"gpio_in", "gpio_out", "csi", "i2c2", NULL, NULL, NULL, NULL}}, + {"PE14", 4, 14, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + {"PE15", 4, 15, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + + {"PF0", 5, 0, {"gpio_in", "gpio_out", "mmc0", "jtag", NULL, NULL, NULL, NULL}}, + {"PF1", 5, 1, {"gpio_in", "gpio_out", "mmc0", "jtag", NULL, NULL, NULL, NULL}}, + {"PF2", 5, 2, {"gpio_in", "gpio_out", "mmc0", "uart0", NULL, NULL, NULL, NULL}}, + {"PF3", 5, 3, {"gpio_in", "gpio_out", "mmc0", "jtag", NULL, NULL, NULL, NULL}}, + {"PF4", 5, 4, {"gpio_in", "gpio_out", "mmc0", "uart0", NULL, NULL, NULL, NULL}}, + {"PF5", 5, 5, {"gpio_in", "gpio_out", "mmc0", "jtag", NULL, NULL, NULL, NULL}}, + {"PF6", 5, 6, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, NULL}}, + + {"PG0", 6, 0, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, "eint"}}, + {"PG1", 6, 1, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, "eint"}}, + {"PG2", 6, 2, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, "eint"}}, + {"PG3", 6, 3, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, "eint"}}, + {"PG4", 6, 4, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, "eint"}}, + {"PG5", 6, 5, {"gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, NULL, "eint"}}, + {"PG6", 6, 6, {"gpio_in", "gpio_out", "uart1", NULL, NULL, NULL, NULL, "eint"}}, + {"PG7", 6, 7, {"gpio_in", "gpio_out", "uart1", NULL, NULL, NULL, NULL, "eint"}}, + {"PG8", 6, 8, {"gpio_in", "gpio_out", "uart1", NULL, NULL, NULL, NULL, "eint"}}, + {"PG9", 6, 9, {"gpio_in", "gpio_out", "uart1", NULL, NULL, NULL, NULL, "eint"}}, + {"PG10", 6, 10, {"gpio_in", "gpio_out", "i2s1", NULL, NULL, NULL, NULL, "eint"}}, + {"PG11", 6, 11, {"gpio_in", "gpio_out", "i2s1", NULL, NULL, NULL, NULL, "eint"}}, + {"PG12", 6, 11, {"gpio_in", "gpio_out", "i2s1", NULL, NULL, NULL, NULL, "eint"}}, + {"PG13", 6, 11, {"gpio_in", "gpio_out", "i2s1", NULL, NULL, NULL, NULL, "eint"}}, +}; + +const struct allwinner_padconf h3_padconf = { + .npins = nitems(h3_pins), + .pins = h3_pins, +}; + +#endif /* SOC_ALLWINNER_H3 */ Property changes on: trunk/sys/arm/allwinner/h3/h3_padconf.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/h3/h3_r_padconf.c =================================================================== --- trunk/sys/arm/allwinner/h3/h3_r_padconf.c (rev 0) +++ trunk/sys/arm/allwinner/h3/h3_r_padconf.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,61 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/h3/h3_r_padconf.c 299688 2016-05-13 18:20:54Z manu $"); + +#include +#include +#include +#include + +#include + +#ifdef SOC_ALLWINNER_H3 + +const static struct allwinner_pins h3_r_pins[] = { + {"PL0", 0, 0, {"gpio_in", "gpio_out", "s_twi", NULL, NULL, NULL, NULL, "eint"}}, + {"PL1", 0, 1, {"gpio_in", "gpio_out", "s_twi", NULL, NULL, NULL, NULL, "eint"}}, + {"PL2", 0, 2, {"gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, NULL, "eint"}}, + {"PL3", 0, 3, {"gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, NULL, "eint"}}, + {"PL4", 0, 4, {"gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, NULL, "eint"}}, + {"PL5", 0, 5, {"gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, NULL, "eint"}}, + {"PL6", 0, 6, {"gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, NULL, "eint"}}, + {"PL7", 0, 7, {"gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, NULL, "eint"}}, + {"PL8", 0, 8, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, "eint"}}, + {"PL9", 0, 9, {"gpio_in", "gpio_out", NULL, NULL, NULL, NULL, NULL, "eint"}}, + {"PL10", 0, 10, {"gpio_in", "gpio_out", "s_pwm", NULL, NULL, NULL, NULL, "eint"}}, + {"PL11", 0, 11, {"gpio_in", "gpio_out", "s_cir_rx", NULL, NULL, NULL, NULL, "eint"}}, +}; + +const struct allwinner_padconf h3_r_padconf = { + .npins = nitems(h3_r_pins), + .pins = h3_r_pins, +}; + +#endif /* SOC_ALLWINNER_H3 */ Property changes on: trunk/sys/arm/allwinner/h3/h3_r_padconf.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/h3/std.h3 =================================================================== --- trunk/sys/arm/allwinner/h3/std.h3 (rev 0) +++ trunk/sys/arm/allwinner/h3/std.h3 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,15 @@ +# Allwinner H3 common options +# $MidnightBSD$ + +cpu CPU_CORTEXA +machine arm armv6 +makeoptions CONF_CFLAGS="-march=armv7a" + +makeoptions KERNVIRTADDR=0xc0200000 +options KERNVIRTADDR=0xc0200000 + +options IPI_IRQ_START=0 +options IPI_IRQ_END=15 + +files "../allwinner/files.allwinner" +files "../allwinner/h3/files.h3" Property changes on: trunk/sys/arm/allwinner/h3/std.h3 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: trunk/sys/arm/allwinner/if_awg.c =================================================================== --- trunk/sys/arm/allwinner/if_awg.c (rev 0) +++ trunk/sys/arm/allwinner/if_awg.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,1425 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/if_awg.c 329648 2018-02-20 18:12:07Z gonzo $ + */ + +/* + * Allwinner Gigabit Ethernet MAC (EMAC) controller + */ + +#include +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/if_awg.c 329648 2018-02-20 18:12:07Z gonzo $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "miibus_if.h" + +#define RD4(sc, reg) bus_read_4((sc)->res[0], (reg)) +#define WR4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) + +#define AWG_LOCK(sc) mtx_lock(&(sc)->mtx) +#define AWG_UNLOCK(sc) mtx_unlock(&(sc)->mtx); +#define AWG_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) +#define AWG_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED) + +#define DESC_ALIGN 4 +#define TX_DESC_COUNT 256 +#define TX_DESC_SIZE (sizeof(struct emac_desc) * TX_DESC_COUNT) +#define RX_DESC_COUNT 256 +#define RX_DESC_SIZE (sizeof(struct emac_desc) * RX_DESC_COUNT) + +#define DESC_OFF(n) ((n) * sizeof(struct emac_desc)) +#define TX_NEXT(n) (((n) + 1) & (TX_DESC_COUNT - 1)) +#define TX_SKIP(n, o) (((n) + (o)) & (TX_DESC_COUNT - 1)) +#define RX_NEXT(n) (((n) + 1) & (RX_DESC_COUNT - 1)) + +#define TX_MAX_SEGS 20 + +#define SOFT_RST_RETRY 1000 +#define MII_BUSY_RETRY 1000 +#define MDIO_FREQ 2500000 + +#define BURST_LEN_DEFAULT 8 +#define RX_TX_PRI_DEFAULT 0 +#define PAUSE_TIME_DEFAULT 0x400 +#define TX_INTERVAL_DEFAULT 64 + +/* Burst length of RX and TX DMA transfers */ +static int awg_burst_len = BURST_LEN_DEFAULT; +TUNABLE_INT("hw.awg.burst_len", &awg_burst_len); + +/* RX / TX DMA priority. If 1, RX DMA has priority over TX DMA. */ +static int awg_rx_tx_pri = RX_TX_PRI_DEFAULT; +TUNABLE_INT("hw.awg.rx_tx_pri", &awg_rx_tx_pri); + +/* Pause time field in the transmitted control frame */ +static int awg_pause_time = PAUSE_TIME_DEFAULT; +TUNABLE_INT("hw.awg.pause_time", &awg_pause_time); + +/* Request a TX interrupt every descriptors */ +static int awg_tx_interval = TX_INTERVAL_DEFAULT; +TUNABLE_INT("hw.awg.tx_interval", &awg_tx_interval); + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun8i-a83t-emac", 1 }, + { NULL, 0 } +}; + +struct awg_bufmap { + bus_dmamap_t map; + struct mbuf *mbuf; +}; + +struct awg_txring { + bus_dma_tag_t desc_tag; + bus_dmamap_t desc_map; + struct emac_desc *desc_ring; + bus_addr_t desc_ring_paddr; + bus_dma_tag_t buf_tag; + struct awg_bufmap buf_map[TX_DESC_COUNT]; + u_int cur, next, queued; +}; + +struct awg_rxring { + bus_dma_tag_t desc_tag; + bus_dmamap_t desc_map; + struct emac_desc *desc_ring; + bus_addr_t desc_ring_paddr; + bus_dma_tag_t buf_tag; + struct awg_bufmap buf_map[RX_DESC_COUNT]; + u_int cur; +}; + +struct awg_softc { + struct resource *res[2]; + struct mtx mtx; + if_t ifp; + device_t dev; + device_t miibus; + struct callout stat_ch; + struct task link_task; + void *ih; + u_int mdc_div_ratio_m; + int link; + int if_flags; + + struct awg_txring tx; + struct awg_rxring rx; +}; + +static struct resource_spec awg_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static int +awg_miibus_readreg(device_t dev, int phy, int reg) +{ + struct awg_softc *sc; + int retry, val; + + sc = device_get_softc(dev); + val = 0; + + WR4(sc, EMAC_MII_CMD, + (sc->mdc_div_ratio_m << MDC_DIV_RATIO_M_SHIFT) | + (phy << PHY_ADDR_SHIFT) | + (reg << PHY_REG_ADDR_SHIFT) | + MII_BUSY); + for (retry = MII_BUSY_RETRY; retry > 0; retry--) { + if ((RD4(sc, EMAC_MII_CMD) & MII_BUSY) == 0) { + val = RD4(sc, EMAC_MII_DATA); + break; + } + DELAY(10); + } + + if (retry == 0) + device_printf(dev, "phy read timeout, phy=%d reg=%d\n", + phy, reg); + + return (val); +} + +static int +awg_miibus_writereg(device_t dev, int phy, int reg, int val) +{ + struct awg_softc *sc; + int retry; + + sc = device_get_softc(dev); + + WR4(sc, EMAC_MII_DATA, val); + WR4(sc, EMAC_MII_CMD, + (sc->mdc_div_ratio_m << MDC_DIV_RATIO_M_SHIFT) | + (phy << PHY_ADDR_SHIFT) | + (reg << PHY_REG_ADDR_SHIFT) | + MII_WR | MII_BUSY); + for (retry = MII_BUSY_RETRY; retry > 0; retry--) { + if ((RD4(sc, EMAC_MII_CMD) & MII_BUSY) == 0) + break; + DELAY(10); + } + + if (retry == 0) + device_printf(dev, "phy write timeout, phy=%d reg=%d\n", + phy, reg); + + return (0); +} + +static void +awg_update_link_locked(struct awg_softc *sc) +{ + struct mii_data *mii; + uint32_t val; + + AWG_ASSERT_LOCKED(sc); + + if ((if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING) == 0) + return; + mii = device_get_softc(sc->miibus); + + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID)) { + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_1000_T: + case IFM_1000_SX: + case IFM_100_TX: + case IFM_10_T: + sc->link = 1; + break; + default: + sc->link = 0; + break; + } + } else + sc->link = 0; + + if (sc->link == 0) + return; + + val = RD4(sc, EMAC_BASIC_CTL_0); + val &= ~(BASIC_CTL_SPEED | BASIC_CTL_DUPLEX); + + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T || + IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX) + val |= BASIC_CTL_SPEED_1000 << BASIC_CTL_SPEED_SHIFT; + else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) + val |= BASIC_CTL_SPEED_100 << BASIC_CTL_SPEED_SHIFT; + else + val |= BASIC_CTL_SPEED_10 << BASIC_CTL_SPEED_SHIFT; + + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) + val |= BASIC_CTL_DUPLEX; + + WR4(sc, EMAC_BASIC_CTL_0, val); + + val = RD4(sc, EMAC_RX_CTL_0); + val &= ~RX_FLOW_CTL_EN; + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) + val |= RX_FLOW_CTL_EN; + WR4(sc, EMAC_RX_CTL_0, val); + + val = RD4(sc, EMAC_TX_FLOW_CTL); + val &= ~(PAUSE_TIME|TX_FLOW_CTL_EN); + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) + val |= TX_FLOW_CTL_EN; + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) + val |= awg_pause_time << PAUSE_TIME_SHIFT; + WR4(sc, EMAC_TX_FLOW_CTL, val); +} + +static void +awg_link_task(void *arg, int pending) +{ + struct awg_softc *sc; + + sc = arg; + + AWG_LOCK(sc); + awg_update_link_locked(sc); + AWG_UNLOCK(sc); +} + +static void +awg_miibus_statchg(device_t dev) +{ + struct awg_softc *sc; + + sc = device_get_softc(dev); + + taskqueue_enqueue(taskqueue_swi, &sc->link_task); +} + +static void +awg_media_status(if_t ifp, struct ifmediareq *ifmr) +{ + struct awg_softc *sc; + struct mii_data *mii; + + sc = if_getsoftc(ifp); + mii = device_get_softc(sc->miibus); + + AWG_LOCK(sc); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + AWG_UNLOCK(sc); +} + +static int +awg_media_change(if_t ifp) +{ + struct awg_softc *sc; + struct mii_data *mii; + int error; + + sc = if_getsoftc(ifp); + mii = device_get_softc(sc->miibus); + + AWG_LOCK(sc); + error = mii_mediachg(mii); + AWG_UNLOCK(sc); + + return (error); +} + +static void +awg_setup_txdesc(struct awg_softc *sc, int index, int flags, bus_addr_t paddr, + u_int len) +{ + uint32_t status, size; + + if (paddr == 0 || len == 0) { + status = 0; + size = 0; + --sc->tx.queued; + } else { + status = TX_DESC_CTL; + size = flags | len; + if ((index & (awg_tx_interval - 1)) == 0) + size |= htole32(TX_INT_CTL); + ++sc->tx.queued; + } + + sc->tx.desc_ring[index].addr = htole32((uint32_t)paddr); + sc->tx.desc_ring[index].size = htole32(size); + sc->tx.desc_ring[index].status = htole32(status); +} + +static int +awg_setup_txbuf(struct awg_softc *sc, int index, struct mbuf **mp) +{ + bus_dma_segment_t segs[TX_MAX_SEGS]; + int error, nsegs, cur, i, flags; + u_int csum_flags; + struct mbuf *m; + + m = *mp; + error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag, + sc->tx.buf_map[index].map, m, segs, &nsegs, BUS_DMA_NOWAIT); + if (error == EFBIG) { + m = m_collapse(m, M_NOWAIT, TX_MAX_SEGS); + if (m == NULL) { + device_printf(sc->dev, "awg_setup_txbuf: m_collapse failed\n"); + return (0); + } + *mp = m; + error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag, + sc->tx.buf_map[index].map, m, segs, &nsegs, BUS_DMA_NOWAIT); + } + if (error != 0) { + device_printf(sc->dev, "awg_setup_txbuf: bus_dmamap_load_mbuf_sg failed\n"); + return (0); + } + + bus_dmamap_sync(sc->tx.buf_tag, sc->tx.buf_map[index].map, + BUS_DMASYNC_PREWRITE); + + flags = TX_FIR_DESC; + if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) { + if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0) + csum_flags = TX_CHECKSUM_CTL_FULL; + else + csum_flags = TX_CHECKSUM_CTL_IP; + flags |= (csum_flags << TX_CHECKSUM_CTL_SHIFT); + } + + for (cur = index, i = 0; i < nsegs; i++) { + sc->tx.buf_map[cur].mbuf = (i == 0 ? m : NULL); + if (i == nsegs - 1) + flags |= TX_LAST_DESC; + awg_setup_txdesc(sc, cur, flags, segs[i].ds_addr, + segs[i].ds_len); + flags &= ~TX_FIR_DESC; + cur = TX_NEXT(cur); + } + + return (nsegs); +} + +static void +awg_setup_rxdesc(struct awg_softc *sc, int index, bus_addr_t paddr) +{ + uint32_t status, size; + + status = RX_DESC_CTL; + size = MCLBYTES - 1; + + sc->rx.desc_ring[index].addr = htole32((uint32_t)paddr); + sc->rx.desc_ring[index].size = htole32(size); + sc->rx.desc_ring[index].next = + htole32(sc->rx.desc_ring_paddr + DESC_OFF(RX_NEXT(index))); + sc->rx.desc_ring[index].status = htole32(status); +} + +static int +awg_setup_rxbuf(struct awg_softc *sc, int index, struct mbuf *m) +{ + bus_dma_segment_t seg; + int error, nsegs; + + m_adj(m, ETHER_ALIGN); + + error = bus_dmamap_load_mbuf_sg(sc->rx.buf_tag, + sc->rx.buf_map[index].map, m, &seg, &nsegs, 0); + if (error != 0) + return (error); + + bus_dmamap_sync(sc->rx.buf_tag, sc->rx.buf_map[index].map, + BUS_DMASYNC_PREREAD); + + sc->rx.buf_map[index].mbuf = m; + awg_setup_rxdesc(sc, index, seg.ds_addr); + + return (0); +} + +static struct mbuf * +awg_alloc_mbufcl(struct awg_softc *sc) +{ + struct mbuf *m; + + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m != NULL) + m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; + + return (m); +} + +static void +awg_start_locked(struct awg_softc *sc) +{ + struct mbuf *m; + uint32_t val; + if_t ifp; + int cnt, nsegs; + + AWG_ASSERT_LOCKED(sc); + + if (!sc->link) + return; + + ifp = sc->ifp; + + if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING) + return; + + for (cnt = 0; ; cnt++) { + if (sc->tx.queued >= TX_DESC_COUNT - TX_MAX_SEGS) { + if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); + break; + } + + m = if_dequeue(ifp); + if (m == NULL) + break; + + nsegs = awg_setup_txbuf(sc, sc->tx.cur, &m); + if (nsegs == 0) { + if_sendq_prepend(ifp, m); + break; + } + if_bpfmtap(ifp, m); + sc->tx.cur = TX_SKIP(sc->tx.cur, nsegs); + } + + if (cnt != 0) { + bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + /* Start and run TX DMA */ + val = RD4(sc, EMAC_TX_CTL_1); + WR4(sc, EMAC_TX_CTL_1, val | TX_DMA_START); + } +} + +static void +awg_start(if_t ifp) +{ + struct awg_softc *sc; + + sc = if_getsoftc(ifp); + + AWG_LOCK(sc); + awg_start_locked(sc); + AWG_UNLOCK(sc); +} + +static void +awg_tick(void *softc) +{ + struct awg_softc *sc; + struct mii_data *mii; + if_t ifp; + int link; + + sc = softc; + ifp = sc->ifp; + mii = device_get_softc(sc->miibus); + + AWG_ASSERT_LOCKED(sc); + + if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) + return; + + link = sc->link; + mii_tick(mii); + if (sc->link && !link) + awg_start_locked(sc); + + callout_reset(&sc->stat_ch, hz, awg_tick, sc); +} + +/* Bit Reversal - http://aggregate.org/MAGIC/#Bit%20Reversal */ +static uint32_t +bitrev32(uint32_t x) +{ + x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1)); + x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2)); + x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4)); + x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8)); + + return (x >> 16) | (x << 16); +} + +static void +awg_setup_rxfilter(struct awg_softc *sc) +{ + uint32_t val, crc, hashreg, hashbit, hash[2], machi, maclo; + int mc_count, mcnt, i; + uint8_t *eaddr, *mta; + if_t ifp; + + AWG_ASSERT_LOCKED(sc); + + ifp = sc->ifp; + val = 0; + hash[0] = hash[1] = 0; + + mc_count = if_multiaddr_count(ifp, -1); + + if (if_getflags(ifp) & IFF_PROMISC) + val |= DIS_ADDR_FILTER; + else if (if_getflags(ifp) & IFF_ALLMULTI) { + val |= RX_ALL_MULTICAST; + hash[0] = hash[1] = ~0; + } else if (mc_count > 0) { + val |= HASH_MULTICAST; + + mta = malloc(sizeof(unsigned char) * ETHER_ADDR_LEN * mc_count, + M_DEVBUF, M_NOWAIT); + if (mta == NULL) { + if_printf(ifp, + "failed to allocate temporary multicast list\n"); + return; + } + + if_multiaddr_array(ifp, mta, &mcnt, mc_count); + for (i = 0; i < mcnt; i++) { + crc = ether_crc32_le(mta + (i * ETHER_ADDR_LEN), + ETHER_ADDR_LEN) & 0x7f; + crc = bitrev32(~crc) >> 26; + hashreg = (crc >> 5); + hashbit = (crc & 0x1f); + hash[hashreg] |= (1 << hashbit); + } + + free(mta, M_DEVBUF); + } + + /* Write our unicast address */ + eaddr = IF_LLADDR(ifp); + machi = (eaddr[5] << 8) | eaddr[4]; + maclo = (eaddr[3] << 24) | (eaddr[2] << 16) | (eaddr[1] << 8) | + (eaddr[0] << 0); + WR4(sc, EMAC_ADDR_HIGH(0), machi); + WR4(sc, EMAC_ADDR_LOW(0), maclo); + + /* Multicast hash filters */ + WR4(sc, EMAC_RX_HASH_0, hash[1]); + WR4(sc, EMAC_RX_HASH_1, hash[0]); + + /* RX frame filter config */ + WR4(sc, EMAC_RX_FRM_FLT, val); +} + +static void +awg_init_locked(struct awg_softc *sc) +{ + struct mii_data *mii; + uint32_t val; + if_t ifp; + + mii = device_get_softc(sc->miibus); + ifp = sc->ifp; + + AWG_ASSERT_LOCKED(sc); + + if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) + return; + + awg_setup_rxfilter(sc); + + /* Configure DMA burst length and priorities */ + val = awg_burst_len << BASIC_CTL_BURST_LEN_SHIFT; + if (awg_rx_tx_pri) + val |= BASIC_CTL_RX_TX_PRI; + WR4(sc, EMAC_BASIC_CTL_1, val); + + /* Enable interrupts */ + WR4(sc, EMAC_INT_EN, RX_INT_EN | TX_INT_EN | TX_BUF_UA_INT_EN); + + /* Enable transmit DMA */ + val = RD4(sc, EMAC_TX_CTL_1); + WR4(sc, EMAC_TX_CTL_1, val | TX_DMA_EN | TX_MD); + + /* Enable receive DMA */ + val = RD4(sc, EMAC_RX_CTL_1); + WR4(sc, EMAC_RX_CTL_1, val | RX_DMA_EN | RX_MD); + + /* Enable transmitter */ + val = RD4(sc, EMAC_TX_CTL_0); + WR4(sc, EMAC_TX_CTL_0, val | TX_EN); + + /* Enable receiver */ + val = RD4(sc, EMAC_RX_CTL_0); + WR4(sc, EMAC_RX_CTL_0, val | RX_EN | CHECK_CRC); + + if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); + + mii_mediachg(mii); + callout_reset(&sc->stat_ch, hz, awg_tick, sc); +} + +static void +awg_init(void *softc) +{ + struct awg_softc *sc; + + sc = softc; + + AWG_LOCK(sc); + awg_init_locked(sc); + AWG_UNLOCK(sc); +} + +static void +awg_stop(struct awg_softc *sc) +{ + if_t ifp; + uint32_t val; + + AWG_ASSERT_LOCKED(sc); + + ifp = sc->ifp; + + callout_stop(&sc->stat_ch); + + /* Stop transmit DMA and flush data in the TX FIFO */ + val = RD4(sc, EMAC_TX_CTL_1); + val &= ~TX_DMA_EN; + val |= FLUSH_TX_FIFO; + WR4(sc, EMAC_TX_CTL_1, val); + + /* Disable transmitter */ + val = RD4(sc, EMAC_TX_CTL_0); + WR4(sc, EMAC_TX_CTL_0, val & ~TX_EN); + + /* Disable receiver */ + val = RD4(sc, EMAC_RX_CTL_0); + WR4(sc, EMAC_RX_CTL_0, val & ~RX_EN); + + /* Disable interrupts */ + WR4(sc, EMAC_INT_EN, 0); + + /* Disable transmit DMA */ + val = RD4(sc, EMAC_TX_CTL_1); + WR4(sc, EMAC_TX_CTL_1, val & ~TX_DMA_EN); + + /* Disable receive DMA */ + val = RD4(sc, EMAC_RX_CTL_1); + WR4(sc, EMAC_RX_CTL_1, val & ~RX_DMA_EN); + + sc->link = 0; + + if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING | IFF_DRV_OACTIVE); +} + +static void +awg_rxintr(struct awg_softc *sc) +{ + if_t ifp; + struct mbuf *m, *m0; + int error, index, len; + uint32_t status; + + ifp = sc->ifp; + + bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + for (index = sc->rx.cur; ; index = RX_NEXT(index)) { + status = le32toh(sc->rx.desc_ring[index].status); + if ((status & RX_DESC_CTL) != 0) + break; + + bus_dmamap_sync(sc->rx.buf_tag, sc->rx.buf_map[index].map, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->rx.buf_tag, sc->rx.buf_map[index].map); + + len = (status & RX_FRM_LEN) >> RX_FRM_LEN_SHIFT; + if (len != 0) { + m = sc->rx.buf_map[index].mbuf; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = len; + m->m_len = len; + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); + + if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0 && + (status & RX_FRM_TYPE) != 0) { + m->m_pkthdr.csum_flags = CSUM_IP_CHECKED; + if ((status & RX_HEADER_ERR) == 0) + m->m_pkthdr.csum_flags |= CSUM_IP_VALID; + if ((status & RX_PAYLOAD_ERR) == 0) { + m->m_pkthdr.csum_flags |= + CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xffff; + } + } + + AWG_UNLOCK(sc); + if_input(ifp, m); + AWG_LOCK(sc); + } + + if ((m0 = awg_alloc_mbufcl(sc)) != NULL) { + error = awg_setup_rxbuf(sc, index, m0); + if (error != 0) { + /* XXX hole in RX ring */ + } + } else + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); + } + + if (index != sc->rx.cur) { + bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, + BUS_DMASYNC_PREWRITE); + } + + sc->rx.cur = index; +} + +static void +awg_txintr(struct awg_softc *sc) +{ + struct awg_bufmap *bmap; + struct emac_desc *desc; + uint32_t status; + if_t ifp; + int i; + + AWG_ASSERT_LOCKED(sc); + + bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + ifp = sc->ifp; + for (i = sc->tx.next; sc->tx.queued > 0; i = TX_NEXT(i)) { + desc = &sc->tx.desc_ring[i]; + status = le32toh(desc->status); + if ((status & TX_DESC_CTL) != 0) + break; + bmap = &sc->tx.buf_map[i]; + if (bmap->mbuf != NULL) { + bus_dmamap_sync(sc->tx.buf_tag, bmap->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->tx.buf_tag, bmap->map); + m_freem(bmap->mbuf); + bmap->mbuf = NULL; + } + awg_setup_txdesc(sc, i, 0, 0, 0); + if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + } + + sc->tx.next = i; + + bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, + BUS_DMASYNC_PREWRITE); +} + +static void +awg_intr(void *arg) +{ + struct awg_softc *sc; + uint32_t val; + + sc = arg; + + AWG_LOCK(sc); + val = RD4(sc, EMAC_INT_STA); + WR4(sc, EMAC_INT_STA, val); + + if (val & RX_INT) + awg_rxintr(sc); + + if (val & (TX_INT|TX_BUF_UA_INT)) { + awg_txintr(sc); + if (!if_sendq_empty(sc->ifp)) + awg_start_locked(sc); + } + + AWG_UNLOCK(sc); +} + +static int +awg_ioctl(if_t ifp, u_long cmd, caddr_t data) +{ + struct awg_softc *sc; + struct mii_data *mii; + struct ifreq *ifr; + int flags, mask, error; + + sc = if_getsoftc(ifp); + mii = device_get_softc(sc->miibus); + ifr = (struct ifreq *)data; + error = 0; + + switch (cmd) { + case SIOCSIFFLAGS: + AWG_LOCK(sc); + if (if_getflags(ifp) & IFF_UP) { + if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { + flags = if_getflags(ifp) ^ sc->if_flags; + if ((flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0) + awg_setup_rxfilter(sc); + } else + awg_init_locked(sc); + } else { + if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) + awg_stop(sc); + } + sc->if_flags = if_getflags(ifp); + AWG_UNLOCK(sc); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { + AWG_LOCK(sc); + awg_setup_rxfilter(sc); + AWG_UNLOCK(sc); + } + break; + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); + break; + case SIOCSIFCAP: + mask = ifr->ifr_reqcap ^ if_getcapenable(ifp); + if (mask & IFCAP_VLAN_MTU) + if_togglecapenable(ifp, IFCAP_VLAN_MTU); + if (mask & IFCAP_RXCSUM) + if_togglecapenable(ifp, IFCAP_RXCSUM); + if (mask & IFCAP_TXCSUM) + if_togglecapenable(ifp, IFCAP_TXCSUM); + if ((if_getcapenable(ifp) & (IFCAP_RXCSUM|IFCAP_TXCSUM)) != 0) + if_sethwassistbits(ifp, CSUM_IP, 0); + else + if_sethwassistbits(ifp, 0, CSUM_IP); + break; + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + + return (error); +} + +static int +awg_setup_extres(device_t dev) +{ + struct awg_softc *sc; + hwreset_t rst_ahb; + clk_t clk_ahb, clk_tx, clk_tx_parent; + regulator_t reg; + const char *tx_parent_name; + char *phy_type; + phandle_t node; + uint64_t freq; + int error, div; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + rst_ahb = NULL; + clk_ahb = NULL; + clk_tx = NULL; + clk_tx_parent = NULL; + reg = NULL; + phy_type = NULL; + + /* Get AHB clock and reset resources */ + error = hwreset_get_by_ofw_name(dev, 0, "ahb", &rst_ahb); + if (error != 0) { + device_printf(dev, "cannot get ahb reset\n"); + goto fail; + } + error = clk_get_by_ofw_name(dev, 0, "ahb", &clk_ahb); + if (error != 0) { + device_printf(dev, "cannot get ahb clock\n"); + goto fail; + } + + /* Configure PHY for MII or RGMII mode */ + if (OF_getprop_alloc(node, "phy-mode", 1, (void **)&phy_type)) { + if (bootverbose) + device_printf(dev, "PHY type: %s\n", phy_type); + + if (strcmp(phy_type, "rgmii") == 0) + tx_parent_name = "emac_int_tx"; + else + tx_parent_name = "mii_phy_tx"; + OF_prop_free(phy_type); + + /* Get the TX clock */ + error = clk_get_by_ofw_name(dev, 0, "tx", &clk_tx); + if (error != 0) { + device_printf(dev, "cannot get tx clock\n"); + goto fail; + } + + /* Find the desired parent clock based on phy-mode property */ + error = clk_get_by_name(dev, tx_parent_name, &clk_tx_parent); + if (error != 0) { + device_printf(dev, "cannot get clock '%s'\n", + tx_parent_name); + goto fail; + } + + /* Set TX clock parent */ + error = clk_set_parent_by_clk(clk_tx, clk_tx_parent); + if (error != 0) { + device_printf(dev, "cannot set tx clock parent\n"); + goto fail; + } + + /* Enable TX clock */ + error = clk_enable(clk_tx); + if (error != 0) { + device_printf(dev, "cannot enable tx clock\n"); + goto fail; + } + } + + /* Enable AHB clock */ + error = clk_enable(clk_ahb); + if (error != 0) { + device_printf(dev, "cannot enable ahb clock\n"); + goto fail; + } + + /* De-assert reset */ + error = hwreset_deassert(rst_ahb); + if (error != 0) { + device_printf(dev, "cannot de-assert ahb reset\n"); + goto fail; + } + + /* Enable PHY regulator if applicable */ + if (regulator_get_by_ofw_property(dev, 0, "phy-supply", ®) == 0) { + error = regulator_enable(reg); + if (error != 0) { + device_printf(dev, "cannot enable PHY regulator\n"); + goto fail; + } + } + + /* Determine MDC clock divide ratio based on AHB clock */ + error = clk_get_freq(clk_ahb, &freq); + if (error != 0) { + device_printf(dev, "cannot get AHB clock frequency\n"); + goto fail; + } + div = freq / MDIO_FREQ; + if (div <= 16) + sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_16; + else if (div <= 32) + sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_32; + else if (div <= 64) + sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_64; + else if (div <= 128) + sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_128; + else { + device_printf(dev, "cannot determine MDC clock divide ratio\n"); + error = ENXIO; + goto fail; + } + + if (bootverbose) + device_printf(dev, "AHB frequency %llu Hz, MDC div: 0x%x\n", + freq, sc->mdc_div_ratio_m); + + return (0); + +fail: + OF_prop_free(phy_type); + + if (reg != NULL) + regulator_release(reg); + if (clk_tx_parent != NULL) + clk_release(clk_tx_parent); + if (clk_tx != NULL) + clk_release(clk_tx); + if (clk_ahb != NULL) + clk_release(clk_ahb); + if (rst_ahb != NULL) + hwreset_release(rst_ahb); + return (error); +} + +static void +awg_get_eaddr(device_t dev, uint8_t *eaddr) +{ + struct awg_softc *sc; + uint32_t maclo, machi, rnd; + + sc = device_get_softc(dev); + + machi = RD4(sc, EMAC_ADDR_HIGH(0)) & 0xffff; + maclo = RD4(sc, EMAC_ADDR_LOW(0)); + + if (maclo == 0xffffffff && machi == 0xffff) { + /* MAC address in hardware is invalid, create one */ + rnd = arc4random(); + maclo = 0x00f2 | (rnd & 0xffff0000); + machi = rnd & 0xffff; + } + + eaddr[0] = maclo & 0xff; + eaddr[1] = (maclo >> 8) & 0xff; + eaddr[2] = (maclo >> 16) & 0xff; + eaddr[3] = (maclo >> 24) & 0xff; + eaddr[4] = machi & 0xff; + eaddr[5] = (machi >> 8) & 0xff; +} + +#ifdef AWG_DEBUG +static void +awg_dump_regs(device_t dev) +{ + static const struct { + const char *name; + u_int reg; + } regs[] = { + { "BASIC_CTL_0", EMAC_BASIC_CTL_0 }, + { "BASIC_CTL_1", EMAC_BASIC_CTL_1 }, + { "INT_STA", EMAC_INT_STA }, + { "INT_EN", EMAC_INT_EN }, + { "TX_CTL_0", EMAC_TX_CTL_0 }, + { "TX_CTL_1", EMAC_TX_CTL_1 }, + { "TX_FLOW_CTL", EMAC_TX_FLOW_CTL }, + { "TX_DMA_LIST", EMAC_TX_DMA_LIST }, + { "RX_CTL_0", EMAC_RX_CTL_0 }, + { "RX_CTL_1", EMAC_RX_CTL_1 }, + { "RX_DMA_LIST", EMAC_RX_DMA_LIST }, + { "RX_FRM_FLT", EMAC_RX_FRM_FLT }, + { "RX_HASH_0", EMAC_RX_HASH_0 }, + { "RX_HASH_1", EMAC_RX_HASH_1 }, + { "MII_CMD", EMAC_MII_CMD }, + { "ADDR_HIGH0", EMAC_ADDR_HIGH(0) }, + { "ADDR_LOW0", EMAC_ADDR_LOW(0) }, + { "TX_DMA_STA", EMAC_TX_DMA_STA }, + { "TX_DMA_CUR_DESC", EMAC_TX_DMA_CUR_DESC }, + { "TX_DMA_CUR_BUF", EMAC_TX_DMA_CUR_BUF }, + { "RX_DMA_STA", EMAC_RX_DMA_STA }, + { "RX_DMA_CUR_DESC", EMAC_RX_DMA_CUR_DESC }, + { "RX_DMA_CUR_BUF", EMAC_RX_DMA_CUR_BUF }, + { "RGMII_STA", EMAC_RGMII_STA }, + }; + struct awg_softc *sc; + unsigned int n; + + sc = device_get_softc(dev); + + for (n = 0; n < nitems(regs); n++) + device_printf(dev, " %-20s %08x\n", regs[n].name, + RD4(sc, regs[n].reg)); +} +#endif + +static int +awg_reset(device_t dev) +{ + struct awg_softc *sc; + int retry; + + sc = device_get_softc(dev); + + /* Soft reset all registers and logic */ + WR4(sc, EMAC_BASIC_CTL_1, BASIC_CTL_SOFT_RST); + + /* Wait for soft reset bit to self-clear */ + for (retry = SOFT_RST_RETRY; retry > 0; retry--) { + if ((RD4(sc, EMAC_BASIC_CTL_1) & BASIC_CTL_SOFT_RST) == 0) + break; + DELAY(10); + } + if (retry == 0) { + device_printf(dev, "soft reset timed out\n"); +#ifdef AWG_DEBUG + awg_dump_regs(dev); +#endif + return (ETIMEDOUT); + } + + return (0); +} + +static void +awg_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + if (error != 0) + return; + *(bus_addr_t *)arg = segs[0].ds_addr; +} + +static int +awg_setup_dma(device_t dev) +{ + struct awg_softc *sc; + struct mbuf *m; + int error, i; + + sc = device_get_softc(dev); + + /* Setup TX ring */ + error = bus_dma_tag_create( + bus_get_dma_tag(dev), /* Parent tag */ + DESC_ALIGN, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + TX_DESC_SIZE, 1, /* maxsize, nsegs */ + TX_DESC_SIZE, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->tx.desc_tag); + if (error != 0) { + device_printf(dev, "cannot create TX descriptor ring tag\n"); + return (error); + } + + error = bus_dmamem_alloc(sc->tx.desc_tag, (void **)&sc->tx.desc_ring, + BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->tx.desc_map); + if (error != 0) { + device_printf(dev, "cannot allocate TX descriptor ring\n"); + return (error); + } + + error = bus_dmamap_load(sc->tx.desc_tag, sc->tx.desc_map, + sc->tx.desc_ring, TX_DESC_SIZE, awg_dmamap_cb, + &sc->tx.desc_ring_paddr, 0); + if (error != 0) { + device_printf(dev, "cannot load TX descriptor ring\n"); + return (error); + } + + for (i = 0; i < TX_DESC_COUNT; i++) + sc->tx.desc_ring[i].next = + htole32(sc->tx.desc_ring_paddr + DESC_OFF(TX_NEXT(i))); + + error = bus_dma_tag_create( + bus_get_dma_tag(dev), /* Parent tag */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES, TX_MAX_SEGS, /* maxsize, nsegs */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->tx.buf_tag); + if (error != 0) { + device_printf(dev, "cannot create TX buffer tag\n"); + return (error); + } + + sc->tx.queued = TX_DESC_COUNT; + for (i = 0; i < TX_DESC_COUNT; i++) { + error = bus_dmamap_create(sc->tx.buf_tag, 0, + &sc->tx.buf_map[i].map); + if (error != 0) { + device_printf(dev, "cannot create TX buffer map\n"); + return (error); + } + awg_setup_txdesc(sc, i, 0, 0, 0); + } + + /* Setup RX ring */ + error = bus_dma_tag_create( + bus_get_dma_tag(dev), /* Parent tag */ + DESC_ALIGN, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + RX_DESC_SIZE, 1, /* maxsize, nsegs */ + RX_DESC_SIZE, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->rx.desc_tag); + if (error != 0) { + device_printf(dev, "cannot create RX descriptor ring tag\n"); + return (error); + } + + error = bus_dmamem_alloc(sc->rx.desc_tag, (void **)&sc->rx.desc_ring, + BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->rx.desc_map); + if (error != 0) { + device_printf(dev, "cannot allocate RX descriptor ring\n"); + return (error); + } + + error = bus_dmamap_load(sc->rx.desc_tag, sc->rx.desc_map, + sc->rx.desc_ring, RX_DESC_SIZE, awg_dmamap_cb, + &sc->rx.desc_ring_paddr, 0); + if (error != 0) { + device_printf(dev, "cannot load RX descriptor ring\n"); + return (error); + } + + error = bus_dma_tag_create( + bus_get_dma_tag(dev), /* Parent tag */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES, 1, /* maxsize, nsegs */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->rx.buf_tag); + if (error != 0) { + device_printf(dev, "cannot create RX buffer tag\n"); + return (error); + } + + for (i = 0; i < RX_DESC_COUNT; i++) { + error = bus_dmamap_create(sc->rx.buf_tag, 0, + &sc->rx.buf_map[i].map); + if (error != 0) { + device_printf(dev, "cannot create RX buffer map\n"); + return (error); + } + if ((m = awg_alloc_mbufcl(sc)) == NULL) { + device_printf(dev, "cannot allocate RX mbuf\n"); + return (ENOMEM); + } + error = awg_setup_rxbuf(sc, i, m); + if (error != 0) { + device_printf(dev, "cannot create RX buffer\n"); + return (error); + } + } + bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, + BUS_DMASYNC_PREWRITE); + + /* Write transmit and receive descriptor base address registers */ + WR4(sc, EMAC_TX_DMA_LIST, sc->tx.desc_ring_paddr); + WR4(sc, EMAC_RX_DMA_LIST, sc->rx.desc_ring_paddr); + + return (0); +} + +static int +awg_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, "Allwinner Gigabit Ethernet"); + return (BUS_PROBE_DEFAULT); +} + +static int +awg_attach(device_t dev) +{ + uint8_t eaddr[ETHER_ADDR_LEN]; + struct awg_softc *sc; + phandle_t node; + int error; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + if (bus_alloc_resources(dev, awg_spec, sc->res) != 0) { + device_printf(dev, "cannot allocate resources for device\n"); + return (ENXIO); + } + + mtx_init(&sc->mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); + callout_init_mtx(&sc->stat_ch, &sc->mtx, 0); + TASK_INIT(&sc->link_task, 0, awg_link_task, sc); + + /* Setup clocks and regulators */ + error = awg_setup_extres(dev); + if (error != 0) + return (error); + + /* Read MAC address before resetting the chip */ + awg_get_eaddr(dev, eaddr); + + /* Soft reset EMAC core */ + error = awg_reset(dev); + if (error != 0) + return (error); + + /* Setup DMA descriptors */ + error = awg_setup_dma(dev); + if (error != 0) + return (error); + + /* Install interrupt handler */ + error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE, + NULL, awg_intr, sc, &sc->ih); + if (error != 0) { + device_printf(dev, "cannot setup interrupt handler\n"); + return (error); + } + + /* Setup ethernet interface */ + sc->ifp = if_alloc(IFT_ETHER); + if_setsoftc(sc->ifp, sc); + if_initname(sc->ifp, device_get_name(dev), device_get_unit(dev)); + if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); + if_setstartfn(sc->ifp, awg_start); + if_setioctlfn(sc->ifp, awg_ioctl); + if_setinitfn(sc->ifp, awg_init); + if_setsendqlen(sc->ifp, TX_DESC_COUNT - 1); + if_setsendqready(sc->ifp); + if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP); + if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM); + if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp)); + + /* Attach MII driver */ + error = mii_attach(dev, &sc->miibus, sc->ifp, awg_media_change, + awg_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, + MIIF_DOPAUSE); + if (error != 0) { + device_printf(dev, "cannot attach PHY\n"); + return (error); + } + + /* Attach ethernet interface */ + ether_ifattach(sc->ifp, eaddr); + + return (0); +} + +static device_method_t awg_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, awg_probe), + DEVMETHOD(device_attach, awg_attach), + + /* MII interface */ + DEVMETHOD(miibus_readreg, awg_miibus_readreg), + DEVMETHOD(miibus_writereg, awg_miibus_writereg), + DEVMETHOD(miibus_statchg, awg_miibus_statchg), + + DEVMETHOD_END +}; + +static driver_t awg_driver = { + "awg", + awg_methods, + sizeof(struct awg_softc), +}; + +static devclass_t awg_devclass; + +DRIVER_MODULE(awg, simplebus, awg_driver, awg_devclass, 0, 0); +DRIVER_MODULE(miibus, awg, miibus_driver, miibus_devclass, 0, 0); + +MODULE_DEPEND(awg, ether, 1, 1, 1); +MODULE_DEPEND(awg, miibus, 1, 1, 1); Property changes on: trunk/sys/arm/allwinner/if_awg.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: trunk/sys/arm/allwinner/if_awgreg.h =================================================================== --- trunk/sys/arm/allwinner/if_awgreg.h (rev 0) +++ trunk/sys/arm/allwinner/if_awgreg.h 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,182 @@ +/* $MidnightBSD$ */ +/*- + * Copyright (c) 2016 Jared McNeill + * 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: stable/11/sys/arm/allwinner/if_awgreg.h 299084 2016-05-04 20:06:20Z jmcneill $ + */ + +/* + * Allwinner Gigabit Ethernet + */ + +#ifndef __IF_AWGREG_H__ +#define __IF_AWGREG_H__ + +#define EMAC_BASIC_CTL_0 0x00 +#define BASIC_CTL_SPEED (0x3 << 2) +#define BASIC_CTL_SPEED_SHIFT 2 +#define BASIC_CTL_SPEED_1000 0 +#define BASIC_CTL_SPEED_10 2 +#define BASIC_CTL_SPEED_100 3 +#define BASIC_CTL_LOOPBACK (1 << 1) +#define BASIC_CTL_DUPLEX (1 << 0) +#define EMAC_BASIC_CTL_1 0x04 +#define BASIC_CTL_BURST_LEN (0x3f << 24) +#define BASIC_CTL_BURST_LEN_SHIFT 24 +#define BASIC_CTL_RX_TX_PRI (1 << 1) +#define BASIC_CTL_SOFT_RST (1 << 0) +#define EMAC_INT_STA 0x08 +#define RX_BUF_UA_INT (1 << 10) +#define RX_INT (1 << 8) +#define TX_UNDERFLOW_INT (1 << 4) +#define TX_BUF_UA_INT (1 << 2) +#define TX_DMA_STOPPED_INT (1 << 1) +#define TX_INT (1 << 0) +#define EMAC_INT_EN 0x0c +#define RX_BUF_UA_INT_EN (1 << 10) +#define RX_INT_EN (1 << 8) +#define TX_UNDERFLOW_INT_EN (1 << 4) +#define TX_BUF_UA_INT_EN (1 << 2) +#define TX_DMA_STOPPED_INT_EN (1 << 1) +#define TX_INT_EN (1 << 0) +#define EMAC_TX_CTL_0 0x10 +#define TX_EN (1 << 31) +#define EMAC_TX_CTL_1 0x14 +#define TX_DMA_START (1 << 31) +#define TX_DMA_EN (1 << 30) +#define TX_MD (1 << 1) +#define FLUSH_TX_FIFO (1 << 0) +#define EMAC_TX_FLOW_CTL 0x1c +#define PAUSE_TIME (0xffff << 4) +#define PAUSE_TIME_SHIFT 4 +#define TX_FLOW_CTL_EN (1 << 0) +#define EMAC_TX_DMA_LIST 0x20 +#define EMAC_RX_CTL_0 0x24 +#define RX_EN (1 << 31) +#define JUMBO_FRM_EN (1 << 29) +#define STRIP_FCS (1 << 28) +#define CHECK_CRC (1 << 27) +#define RX_FLOW_CTL_EN (1 << 16) +#define EMAC_RX_CTL_1 0x28 +#define RX_DMA_START (1 << 31) +#define RX_DMA_EN (1 << 30) +#define RX_MD (1 << 1) +#define EMAC_RX_DMA_LIST 0x34 +#define EMAC_RX_FRM_FLT 0x38 +#define DIS_ADDR_FILTER (1 << 31) +#define DIS_BROADCAST (1 << 17) +#define RX_ALL_MULTICAST (1 << 16) +#define CTL_FRM_FILTER (0x3 << 12) +#define CTL_FRM_FILTER_SHIFT 12 +#define HASH_MULTICAST (1 << 9) +#define HASH_UNICAST (1 << 8) +#define SA_FILTER_EN (1 << 6) +#define SA_INV_FILTER (1 << 5) +#define DA_INV_FILTER (1 << 4) +#define FLT_MD (1 << 1) +#define RX_ALL (1 << 0) +#define EMAC_RX_HASH_0 0x40 +#define EMAC_RX_HASH_1 0x44 +#define EMAC_MII_CMD 0x48 +#define MDC_DIV_RATIO_M (0x7 << 20) +#define MDC_DIV_RATIO_M_16 0 +#define MDC_DIV_RATIO_M_32 1 +#define MDC_DIV_RATIO_M_64 2 +#define MDC_DIV_RATIO_M_128 3 +#define MDC_DIV_RATIO_M_SHIFT 20 +#define PHY_ADDR (0x1f << 12) +#define PHY_ADDR_SHIFT 12 +#define PHY_REG_ADDR (0x1f << 4) +#define PHY_REG_ADDR_SHIFT 4 +#define MII_WR (1 << 1) +#define MII_BUSY (1 << 0) +#define EMAC_MII_DATA 0x4c +#define EMAC_ADDR_HIGH(n) (0x50 + (n) * 8) +#define EMAC_ADDR_LOW(n) (0x54 + (n) * 8) +#define EMAC_TX_DMA_STA 0x80 +#define EMAC_TX_DMA_CUR_DESC 0x84 +#define EMAC_TX_DMA_CUR_BUF 0x88 +#define EMAC_RX_DMA_STA 0xc0 +#define EMAC_RX_DMA_CUR_DESC 0xc4 +#define EMAC_RX_DMA_CUR_BUF 0xc8 +#define EMAC_RGMII_STA 0xd0 + +struct emac_desc { + uint32_t status; +/* Transmit */ +#define TX_DESC_CTL (1 << 31) +#define TX_HEADER_ERR (1 << 16) +#define TX_LENGTH_ERR (1 << 14) +#define TX_PAYLOAD_ERR (1 << 12) +#define TX_CRS_ERR (1 << 10) +#define TX_COL_ERR_0 (1 << 9) +#define TX_COL_ERR_1 (1 << 8) +#define TX_COL_CNT (0xf << 3) +#define TX_COL_CNT_SHIFT 3 +#define TX_DEFER_ERR (1 << 2) +#define TX_UNDERFLOW_ERR (1 << 1) +#define TX_DEFER (1 << 0) +/* Receive */ +#define RX_DESC_CTL (1 << 31) +#define RX_DAF_FAIL (1 << 30) +#define RX_FRM_LEN (0x3fff << 16) +#define RX_FRM_LEN_SHIFT 16 +#define RX_NO_ENOUGH_BUF_ERR (1 << 14) +#define RX_SAF_FAIL (1 << 13) +#define RX_OVERFLOW_ERR (1 << 11) +#define RX_FIR_DESC (1 << 9) +#define RX_LAST_DESC (1 << 8) +#define RX_HEADER_ERR (1 << 7) +#define RX_COL_ERR (1 << 6) +#define RX_FRM_TYPE (1 << 5) +#define RX_LENGTH_ERR (1 << 4) +#define RX_PHY_ERR (1 << 3) +#define RX_CRC_ERR (1 << 1) +#define RX_PAYLOAD_ERR (1 << 0) + + uint32_t size; +/* Transmit */ +#define TX_INT_CTL (1 << 31) +#define TX_LAST_DESC (1 << 30) +#define TX_FIR_DESC (1 << 29) +#define TX_CHECKSUM_CTL (0x3 << 27) +#define TX_CHECKSUM_CTL_IP 1 +#define TX_CHECKSUM_CTL_NO_PSE 2 +#define TX_CHECKSUM_CTL_FULL 3 +#define TX_CHECKSUM_CTL_SHIFT 27 +#define TX_CRC_CTL (1 << 26) +#define TX_BUF_SIZE (0xfff << 0) +#define TX_BUF_SIZE_SHIFT 0 +/* Receive */ +#define RX_INT_CTL (1 << 31) +#define RX_BUF_SIZE (0xfff << 0) +#define RX_BUF_SIZE_SHIFT 0 + + uint32_t addr; + + uint32_t next; +} __packed; + +#endif /* !__IF_AWGREG_H__ */ Property changes on: trunk/sys/arm/allwinner/if_awgreg.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Modified: trunk/sys/arm/allwinner/if_emac.c =================================================================== --- trunk/sys/arm/allwinner/if_emac.c 2020-03-08 17:21:06 UTC (rev 12401) +++ trunk/sys/arm/allwinner/if_emac.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -24,13 +24,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/allwinner/if_emac.c 266337 2014-05-17 18:53:36Z ian $ + * $FreeBSD: stable/11/sys/arm/allwinner/if_emac.c 331722 2018-03-29 02:50:57Z eadler $ */ /* A10/A20 EMAC driver */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/allwinner/if_emac.c 266337 2014-05-17 18:53:36Z ian $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/if_emac.c 331722 2018-03-29 02:50:57Z eadler $"); #include #include @@ -78,14 +78,15 @@ #include #include +#include +#include + #include "miibus_if.h" #include "gpio_if.h" -#include "a10_clk.h" #include "a10_sramc.h" -#include "a10_gpio.h" struct emac_softc { struct ifnet *emac_ifp; @@ -96,6 +97,7 @@ struct resource *emac_res; struct resource *emac_irq; void *emac_intrhand; + clk_t emac_clk; int emac_if_flags; struct mtx emac_mtx; struct callout emac_tick_ch; @@ -102,6 +104,7 @@ int emac_watchdog_timer; int emac_rx_process_limit; int emac_link; + uint32_t emac_fifo_mask; }; static int emac_probe(device_t); @@ -111,7 +114,7 @@ static int emac_suspend(device_t); static int emac_resume(device_t); -static void emac_sys_setup(void); +static int emac_sys_setup(struct emac_softc *); static void emac_reset(struct emac_softc *); static void emac_init_locked(struct emac_softc *); @@ -122,7 +125,7 @@ static int emac_ioctl(struct ifnet *, u_long, caddr_t); static void emac_rxeof(struct emac_softc *, int); -static void emac_txeof(struct emac_softc *); +static void emac_txeof(struct emac_softc *, uint32_t); static int emac_miibus_readreg(device_t, int, int); static int emac_miibus_writereg(device_t, int, int, int); @@ -139,21 +142,27 @@ #define EMAC_WRITE_REG(sc, reg, val) \ bus_space_write_4(sc->emac_tag, sc->emac_handle, reg, val) -static void -emac_sys_setup(void) +static int +emac_sys_setup(struct emac_softc *sc) { - int i; + int error; - a10_clk_emac_activate(); + /* Activate EMAC clock. */ + error = clk_get_by_ofw_index(sc->emac_dev, 0, 0, &sc->emac_clk); + if (error != 0) { + device_printf(sc->emac_dev, "cannot get clock\n"); + return (error); + } + error = clk_enable(sc->emac_clk); + if (error != 0) { + device_printf(sc->emac_dev, "cannot enable clock\n"); + return (error); + } - /* - * Configure pin mux settings for MII. - * Pins PA0 from PA17. - */ - for (i = 0; i <= 17; i++) - a10_emac_gpio_config(i); - /* Map sram */ + /* Map sram. */ a10_map_to_emac(); + + return (0); } static void @@ -160,6 +169,7 @@ emac_get_hwaddr(struct emac_softc *sc, uint8_t *hwaddr) { uint32_t val0, val1, rnd; + u_char rootkey[16]; /* * Try to get MAC address from running hardware. @@ -166,6 +176,10 @@ * If there is something non-zero there just use it. * * Otherwise set the address to a convenient locally assigned address, + * using the SID rootkey. + * This is was uboot does so we end up with the same mac as if uboot + * did set it. + * If we can't get the root key, generate a random one, * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally * assigned bit set, and the broadcast/multicast bit clear. */ @@ -179,13 +193,23 @@ hwaddr[4] = (val0 >> 8) & 0xff; hwaddr[5] = (val0 >> 0) & 0xff; } else { - rnd = arc4random() & 0x00ffffff; - hwaddr[0] = 'b'; - hwaddr[1] = 's'; - hwaddr[2] = 'd'; - hwaddr[3] = (rnd >> 16) & 0xff; - hwaddr[4] = (rnd >> 8) & 0xff; - hwaddr[5] = (rnd >> 0) & 0xff; + if (aw_sid_get_rootkey(rootkey) == 0) { + hwaddr[0] = 0x2; + hwaddr[1] = rootkey[3]; + hwaddr[2] = rootkey[12]; + hwaddr[3] = rootkey[13]; + hwaddr[4] = rootkey[14]; + hwaddr[5] = rootkey[15]; + } + else { + rnd = arc4random() & 0x00ffffff; + hwaddr[0] = 'b'; + hwaddr[1] = 's'; + hwaddr[2] = 'd'; + hwaddr[3] = (rnd >> 16) & 0xff; + hwaddr[4] = (rnd >> 8) & 0xff; + hwaddr[5] = (rnd >> 0) & 0xff; + } } if (bootverbose) printf("MAC address: %s\n", ether_sprintf(hwaddr)); @@ -254,14 +278,28 @@ } static void -emac_txeof(struct emac_softc *sc) +emac_drain_rxfifo(struct emac_softc *sc) { + uint32_t data; + + while (EMAC_READ_REG(sc, EMAC_RX_FBC) > 0) + data = EMAC_READ_REG(sc, EMAC_RX_IO_DATA); +} + +static void +emac_txeof(struct emac_softc *sc, uint32_t status) +{ struct ifnet *ifp; EMAC_ASSERT_LOCKED(sc); ifp = sc->emac_ifp; - ifp->if_opackets++; + status &= (EMAC_TX_FIFO0 | EMAC_TX_FIFO1); + sc->emac_fifo_mask &= ~status; + if (status == (EMAC_TX_FIFO0 | EMAC_TX_FIFO1)) + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 2); + else + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; /* Unarm watchdog timer if no TX */ @@ -276,7 +314,7 @@ uint32_t reg_val, rxcount; int16_t len; uint16_t status; - int good_packet, i; + int i; ifp = sc->emac_ifp; for (; count > 0 && @@ -328,25 +366,24 @@ return; } - good_packet = 1; - /* Get packet size and status */ reg_val = EMAC_READ_REG(sc, EMAC_RX_IO_DATA); len = reg_val & 0xffff; status = (reg_val >> 16) & 0xffff; - if (len < 64) { - good_packet = 0; + if (len < 64 || (status & EMAC_PKT_OK) == 0) { if (bootverbose) if_printf(ifp, "bad packet: len = %i status = %i\n", len, status); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + emac_drain_rxfifo(sc); + continue; } #if 0 if (status & (EMAC_CRCERR | EMAC_LENERR)) { good_packet = 0; - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); if (status & EMAC_CRCERR) if_printf(ifp, "crc error\n"); if (status & EMAC_LENERR) @@ -353,63 +390,58 @@ if_printf(ifp, "length error\n"); } #endif - if (good_packet) { - m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); - if (m == NULL) - return; - m->m_len = m->m_pkthdr.len = MCLBYTES; + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) { + emac_drain_rxfifo(sc); + return; + } + m->m_len = m->m_pkthdr.len = MCLBYTES; - len -= ETHER_CRC_LEN; + /* Copy entire frame to mbuf first. */ + bus_space_read_multi_4(sc->emac_tag, sc->emac_handle, + EMAC_RX_IO_DATA, mtod(m, uint32_t *), roundup2(len, 4) / 4); - /* Copy entire frame to mbuf first. */ - bus_space_read_multi_4(sc->emac_tag, sc->emac_handle, - EMAC_RX_IO_DATA, mtod(m, uint32_t *), - roundup2(len, 4) / 4); + m->m_pkthdr.rcvif = ifp; + m->m_len = m->m_pkthdr.len = len - ETHER_CRC_LEN; - m->m_pkthdr.rcvif = ifp; - m->m_len = m->m_pkthdr.len = len; - - /* - * Emac controller needs strict aligment, so to avoid - * copying over an entire frame to align, we allocate - * a new mbuf and copy ethernet header + IP header to - * the new mbuf. The new mbuf is prepended into the - * existing mbuf chain. - */ - if (m->m_len <= (MHLEN - ETHER_HDR_LEN)) { - bcopy(m->m_data, m->m_data + ETHER_HDR_LEN, - m->m_len); - m->m_data += ETHER_HDR_LEN; - } else if (m->m_len <= (MCLBYTES - ETHER_HDR_LEN) && - m->m_len > (MHLEN - ETHER_HDR_LEN)) { - MGETHDR(m0, M_NOWAIT, MT_DATA); - if (m0 != NULL) { - len = ETHER_HDR_LEN + - m->m_pkthdr.l2hlen; - bcopy(m->m_data, m0->m_data, len); - m->m_data += len; - m->m_len -= len; - m0->m_len = len; - M_MOVE_PKTHDR(m0, m); - m0->m_next = m; - m = m0; - } else { - ifp->if_ierrors++; - m_freem(m); - m = NULL; - continue; - } - } else if (m->m_len > EMAC_MAC_MAXF) { - ifp->if_ierrors++; + /* + * Emac controller needs strict aligment, so to avoid + * copying over an entire frame to align, we allocate + * a new mbuf and copy ethernet header + IP header to + * the new mbuf. The new mbuf is prepended into the + * existing mbuf chain. + */ + if (m->m_len <= (MHLEN - ETHER_HDR_LEN)) { + bcopy(m->m_data, m->m_data + ETHER_HDR_LEN, m->m_len); + m->m_data += ETHER_HDR_LEN; + } else if (m->m_len <= (MCLBYTES - ETHER_HDR_LEN) && + m->m_len > (MHLEN - ETHER_HDR_LEN)) { + MGETHDR(m0, M_NOWAIT, MT_DATA); + if (m0 != NULL) { + len = ETHER_HDR_LEN + m->m_pkthdr.l2hlen; + bcopy(m->m_data, m0->m_data, len); + m->m_data += len; + m->m_len -= len; + m0->m_len = len; + M_MOVE_PKTHDR(m0, m); + m0->m_next = m; + m = m0; + } else { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); m_freem(m); m = NULL; continue; } - ifp->if_ipackets++; - EMAC_UNLOCK(sc); - (*ifp->if_input)(ifp, m); - EMAC_LOCK(sc); + } else if (m->m_len > EMAC_MAC_MAXF) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + m_freem(m); + m = NULL; + continue; } + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); + EMAC_UNLOCK(sc); + (*ifp->if_input)(ifp, m); + EMAC_LOCK(sc); } } @@ -432,7 +464,7 @@ } else if_printf(sc->emac_ifp, "watchdog timeout -- resetting\n"); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; emac_init_locked(sc); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) @@ -583,11 +615,13 @@ { struct emac_softc *sc; struct mbuf *m, *m0; - uint32_t reg_val; + uint32_t fifo, reg; sc = ifp->if_softc; if (ifp->if_drv_flags & IFF_DRV_OACTIVE) return; + if (sc->emac_fifo_mask == (EMAC_TX_FIFO0 | EMAC_TX_FIFO1)) + return; if (sc->emac_link == 0) return; IFQ_DRV_DEQUEUE(&ifp->if_snd, m); @@ -595,7 +629,14 @@ return; /* Select channel */ - EMAC_WRITE_REG(sc, EMAC_TX_INS, 0); + if (sc->emac_fifo_mask & EMAC_TX_FIFO0) + fifo = 1; + else + fifo = 0; + sc->emac_fifo_mask |= (1 << fifo); + if (sc->emac_fifo_mask == (EMAC_TX_FIFO0 | EMAC_TX_FIFO1)) + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + EMAC_WRITE_REG(sc, EMAC_TX_INS, fifo); /* * Emac controller wants 4 byte aligned TX buffers. @@ -616,17 +657,17 @@ roundup2(m->m_len, 4) / 4); /* Send the data lengh. */ - EMAC_WRITE_REG(sc, EMAC_TX_PL0, m->m_len); + reg = (fifo == 0) ? EMAC_TX_PL0 : EMAC_TX_PL1; + EMAC_WRITE_REG(sc, reg, m->m_len); /* Start translate from fifo to phy. */ - reg_val = EMAC_READ_REG(sc, EMAC_TX_CTL0); - reg_val |= 1; - EMAC_WRITE_REG(sc, EMAC_TX_CTL0, reg_val); + reg = (fifo == 0) ? EMAC_TX_CTL0 : EMAC_TX_CTL1; + EMAC_WRITE_REG(sc, reg, EMAC_READ_REG(sc, reg) | 1); /* Set timeout */ sc->emac_watchdog_timer = 5; - ifp->if_drv_flags |= IFF_DRV_OACTIVE; + /* Data have been sent to hardware, it is okay to free the mbuf now. */ BPF_MTAP(ifp, m); m_freem(m); } @@ -665,9 +706,6 @@ sc = (struct emac_softc *)arg; EMAC_LOCK(sc); - ifp = sc->emac_ifp; - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) - return; /* Disable all interrupts */ EMAC_WRITE_REG(sc, EMAC_INT_CTL, 0); @@ -681,18 +719,17 @@ emac_rxeof(sc, sc->emac_rx_process_limit); /* Transmit Interrupt check */ - if (reg_val & EMAC_INT_STA_TX){ - emac_txeof(sc); + if (reg_val & EMAC_INT_STA_TX) { + emac_txeof(sc, reg_val); + ifp = sc->emac_ifp; if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) emac_start_locked(ifp); } - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { - /* Re-enable interrupt mask */ - reg_val = EMAC_READ_REG(sc, EMAC_INT_CTL); - reg_val |= EMAC_INT_EN; - EMAC_WRITE_REG(sc, EMAC_INT_CTL, reg_val); - } + /* Re-enable interrupt mask */ + reg_val = EMAC_READ_REG(sc, EMAC_INT_CTL); + reg_val |= EMAC_INT_EN; + EMAC_WRITE_REG(sc, EMAC_INT_CTL, reg_val); EMAC_UNLOCK(sc); } @@ -748,9 +785,12 @@ emac_probe(device_t dev) { - if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-emac")) + if (!ofw_bus_status_okay(dev)) return (ENXIO); + if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-emac")) + return (ENXIO); + device_set_desc(dev, "A10/A20 EMAC ethernet controller"); return (BUS_PROBE_DEFAULT); } @@ -779,6 +819,9 @@ bus_generic_detach(sc->emac_dev); } + if (sc->emac_clk != NULL) + clk_disable(sc->emac_clk); + if (sc->emac_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->emac_res); @@ -892,7 +935,10 @@ } } /* Setup EMAC */ - emac_sys_setup(); + error = emac_sys_setup(sc); + if (error != 0) + goto fail; + emac_reset(sc); ifp = sc->emac_ifp = if_alloc(IFT_ETHER); Modified: trunk/sys/arm/allwinner/if_emacreg.h =================================================================== --- trunk/sys/arm/allwinner/if_emacreg.h 2020-03-08 17:21:06 UTC (rev 12401) +++ trunk/sys/arm/allwinner/if_emacreg.h 2020-03-08 17:23:22 UTC (rev 12402) @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: stable/10/sys/arm/allwinner/if_emacreg.h 266337 2014-05-17 18:53:36Z ian $ + * $FreeBSD: stable/11/sys/arm/allwinner/if_emacreg.h 331722 2018-03-29 02:50:57Z eadler $ */ #ifndef __IF_EMACREG_H__ @@ -52,6 +52,8 @@ #define EMAC_TX_TSVH0 0x30 #define EMAC_TX_TSVL1 0x34 #define EMAC_TX_TSVH1 0x38 +#define EMAC_TX_FIFO0 (1 << 0) +#define EMAC_TX_FIFO1 (1 << 1) #define EMAC_RX_CTL 0x3C #define EMAC_RX_HASH0 0x40 @@ -62,7 +64,7 @@ #define EMAC_INT_CTL 0x54 #define EMAC_INT_STA 0x58 -#define EMAC_INT_STA_TX (0x01 | 0x02) +#define EMAC_INT_STA_TX (EMAC_TX_FIFO0 | EMAC_TX_FIFO1) #define EMAC_INT_STA_RX 0x100 #define EMAC_INT_EN (0xf << 0) | (1 << 8) @@ -224,6 +226,7 @@ /* Receive status */ #define EMAC_CRCERR (1 << 4) #define EMAC_LENERR (3 << 5) +#define EMAC_PKT_OK (1 << 7) #define EMAC_RX_FLUSH_FIFO (1 << 3) #define EMAC_PHY_RESET (1 << 15) Deleted: trunk/sys/arm/allwinner/std.a10 =================================================================== --- trunk/sys/arm/allwinner/std.a10 2020-03-08 17:21:06 UTC (rev 12401) +++ trunk/sys/arm/allwinner/std.a10 2020-03-08 17:23:22 UTC (rev 12402) @@ -1,20 +0,0 @@ -# Allwinner A10 common options -#$FreeBSD: stable/10/sys/arm/allwinner/std.a10 278601 2015-02-11 22:47:48Z ian $ - -cpu CPU_CORTEXA -machine arm armv6 -makeoptions CONF_CFLAGS="-march=armv7a -Wa,-march=armv7a" -makeoption ARM_LITTLE_ENDIAN - -# Physical memory starts at 0x40200000. We assume images are loaded at -# 0x40200000, e.g. from u-boot with 'fatload mmc 0 0x40200000 kernel' -# -# -options PHYSADDR=0x40000000 - -makeoptions KERNPHYSADDR=0x40200000 -options KERNPHYSADDR=0x40200000 -makeoptions KERNVIRTADDR=0xc0200000 -options KERNVIRTADDR=0xc0200000 - -files "../allwinner/files.a10" Added: trunk/sys/arm/allwinner/std.allwinner =================================================================== --- trunk/sys/arm/allwinner/std.allwinner (rev 0) +++ trunk/sys/arm/allwinner/std.allwinner 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,15 @@ +# Allwinner common options +# $MidnightBSD$ + +cpu CPU_CORTEXA +machine arm armv6 +makeoptions CONF_CFLAGS="-march=armv7a" + +options IPI_IRQ_START=0 +options IPI_IRQ_END=15 + +files "../allwinner/files.allwinner" +files "../allwinner/a20/files.a20" +files "../allwinner/a31/files.a31" +files "../allwinner/a83t/files.a83t" +files "../allwinner/h3/files.h3" Property changes on: trunk/sys/arm/allwinner/std.allwinner ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: trunk/sys/arm/allwinner/std.allwinner_up =================================================================== --- trunk/sys/arm/allwinner/std.allwinner_up (rev 0) +++ trunk/sys/arm/allwinner/std.allwinner_up 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,11 @@ +# Allwinner Uniprocessor common options +# $MidnightBSD$ + +cpu CPU_CORTEXA +machine arm armv6 +makeoptions CONF_CFLAGS="-march=armv7a" + +files "../allwinner/files.allwinner_up" +files "../allwinner/files.allwinner" +files "../allwinner/a10/files.a10" +files "../allwinner/a13/files.a13" Property changes on: trunk/sys/arm/allwinner/std.allwinner_up ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Added: trunk/sys/arm/allwinner/sunxi_dma_if.m =================================================================== --- trunk/sys/arm/allwinner/sunxi_dma_if.m (rev 0) +++ trunk/sys/arm/allwinner/sunxi_dma_if.m 2020-03-08 17:23:22 UTC (rev 12402) @@ -0,0 +1,99 @@ +/* $MidnightBSD$ */ +#- +# Copyright (c) 2016 Jared D. McNeill +# 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: stable/11/sys/arm/allwinner/sunxi_dma_if.m 295635 2016-02-15 19:56:35Z andrew $ +# + +#include + +INTERFACE sunxi_dma; + +HEADER { + #include + + struct sunxi_dma_config { + unsigned int dst_width; + unsigned int dst_burst_len; + unsigned int dst_drqtype; + bool dst_noincr; + unsigned int dst_blksize; /* DDMA-only */ + unsigned int dst_wait_cyc; /* DDMA-only */ + unsigned int src_width; + unsigned int src_burst_len; + unsigned int src_drqtype; + bool src_noincr; + unsigned int src_blksize; /* DDMA-only */ + unsigned int src_wait_cyc; /* DDMA-only */ + }; + + typedef void (*sunxi_dma_callback)(void *); +} + +# +# Allocate DMA channel +# +METHOD void * alloc { + device_t dev; + bool dedicated; + sunxi_dma_callback callback; + void *callback_arg; +}; + +# +# Free DMA channel +# +METHOD void free { + device_t dev; + void *dmachan; +}; + +# +# Set DMA channel configuration +# +METHOD int set_config { + device_t dev; + void *dmachan; + const struct sunxi_dma_config *cfg; +}; + +# +# Start DMA channel transfer +# +METHOD int transfer { + device_t dev; + void *dmachan; + bus_addr_t src; + bus_addr_t dst; + size_t nbytes; +}; + +# +# Halt DMA channel transfer +# +METHOD void halt { + device_t dev; + void *dmachan; +}; Property changes on: trunk/sys/arm/allwinner/sunxi_dma_if.m ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +MidnightBSD=%H \ No newline at end of property Modified: trunk/sys/arm/allwinner/timer.c =================================================================== --- trunk/sys/arm/allwinner/timer.c 2020-03-08 17:21:06 UTC (rev 12401) +++ trunk/sys/arm/allwinner/timer.c 2020-03-08 17:23:22 UTC (rev 12402) @@ -26,7 +26,7 @@ */ #include -__FBSDID("$FreeBSD: stable/10/sys/arm/allwinner/timer.c 277113 2015-01-13 07:45:16Z ganbold $"); +__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/timer.c 308274 2016-11-04 00:54:21Z manu $"); #include #include @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -48,11 +49,10 @@ #include #include -#include #include -#include "a20/a20_cpu_cfg.h" +#include /** * Timer registers addr @@ -86,7 +86,6 @@ uint32_t sc_period; uint32_t timer0_freq; struct eventtimer et; - uint8_t sc_timer_type; /* 0 for A10, 1 for A20 */ }; int a10_timer_get_timerfreq(struct a10_timer_softc *); @@ -103,11 +102,12 @@ static uint64_t timer_read_counter64(void); -static int a10_timer_initialized = 0; static int a10_timer_hardclock(void *); static int a10_timer_probe(device_t); static int a10_timer_attach(device_t); +static delay_func a10_timer_delay; + static struct timecounter a10_timer_timecounter = { .tc_name = "a10_timer timer0", .tc_get_timecount = a10_timer_get_timecount, @@ -129,10 +129,6 @@ { uint32_t lo, hi; - /* In case of A20 get appropriate counter info */ - if (a10_timer_sc->sc_timer_type) - return (a20_read_counter64()); - /* Latch counter, wait for it to be ready to read. */ timer_write_4(a10_timer_sc, CNT64_CTRL_REG, CNT64_RL_EN); while (timer_read_4(a10_timer_sc, CNT64_CTRL_REG) & CNT64_RL_EN) @@ -148,16 +144,18 @@ a10_timer_probe(device_t dev) { struct a10_timer_softc *sc; + u_int soc_family; sc = device_get_softc(dev); - if (ofw_bus_is_compatible(dev, "allwinner,sun4i-timer")) - sc->sc_timer_type = 0; - else if (ofw_bus_is_compatible(dev, "allwinner,sun7i-timer")) - sc->sc_timer_type = 1; - else + if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-timer")) return (ENXIO); + soc_family = allwinner_soc_family(); + if (soc_family != ALLWINNERSOC_SUN4I && + soc_family != ALLWINNERSOC_SUN5I) + return (ENXIO); + device_set_desc(dev, "Allwinner A10/A20 timer"); return (BUS_PROBE_DEFAULT); } @@ -214,8 +212,10 @@ sc->et.et_priv = sc; et_register(&sc->et); - if (device_get_unit(dev) == 0) + if (device_get_unit(dev) == 0) { + arm_set_delay(a10_timer_delay, sc); a10_timer_sc = sc; + } a10_timer_timecounter.tc_frequency = sc->timer0_freq; tc_init(&a10_timer_timecounter); @@ -229,8 +229,6 @@ a10_timer_timecounter.tc_frequency); } - a10_timer_initialized = 1; - return (0); } @@ -354,25 +352,18 @@ static devclass_t a10_timer_devclass; -DRIVER_MODULE(a10_timer, simplebus, a10_timer_driver, a10_timer_devclass, 0, 0); +EARLY_DRIVER_MODULE(a10_timer, simplebus, a10_timer_driver, a10_timer_devclass, 0, 0, + BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); -void -DELAY(int usec) +static void +a10_timer_delay(int usec, void *arg) { - uint32_t counter; + struct a10_timer_softc *sc = arg; uint64_t end, now; - if (!a10_timer_initialized) { - for (; usec > 0; usec--) - for (counter = 50; counter > 0; counter--) - cpufunc_nullop(); - return; - } - now = timer_read_counter64(); - end = now + (a10_timer_sc->timer0_freq / 1000000) * (usec + 1); + end = now + (sc->timer0_freq / 1000000) * (usec + 1); while (now < end) now = timer_read_counter64(); } -