Index: head/sys/powerpc/aim/mmu_oea.c =================================================================== --- head/sys/powerpc/aim/mmu_oea.c (revision 326260) +++ head/sys/powerpc/aim/mmu_oea.c (revision 326261) @@ -1,2707 +1,2709 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND BSD-4-Clause + * * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Matt Thomas of Allegro Networks, 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /*- * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: pmap.c,v 1.28 2000/03/26 20:42:36 kleink Exp $ */ /*- * Copyright (C) 2001 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH 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$"); /* * Manages physical address maps. * * Since the information managed by this module is also stored by the * logical address mapping module, this module may throw away valid virtual * to physical mappings at almost any time. However, invalidations of * mappings must be done as requested. * * In order to cope with hardware architectures which make virtual to * physical map invalidates expensive, this module may delay invalidate * reduced protection operations until such time as they are actually * necessary. This module is given full information as to which processors * are currently using which maps, and to when physical maps must be made * correct. */ #include "opt_kstack_pages.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mmu_if.h" #define MOEA_DEBUG #define TODO panic("%s: not implemented", __func__); #define VSID_MAKE(sr, hash) ((sr) | (((hash) & 0xfffff) << 4)) #define VSID_TO_SR(vsid) ((vsid) & 0xf) #define VSID_TO_HASH(vsid) (((vsid) >> 4) & 0xfffff) struct ofw_map { vm_offset_t om_va; vm_size_t om_len; vm_offset_t om_pa; u_int om_mode; }; extern unsigned char _etext[]; extern unsigned char _end[]; /* * Map of physical memory regions. */ static struct mem_region *regions; static struct mem_region *pregions; static u_int phys_avail_count; static int regions_sz, pregions_sz; static struct ofw_map *translations; /* * Lock for the pteg and pvo tables. */ struct mtx moea_table_mutex; struct mtx moea_vsid_mutex; /* tlbie instruction synchronization */ static struct mtx tlbie_mtx; /* * PTEG data. */ static struct pteg *moea_pteg_table; u_int moea_pteg_count; u_int moea_pteg_mask; /* * PVO data. */ struct pvo_head *moea_pvo_table; /* pvo entries by pteg index */ struct pvo_head moea_pvo_kunmanaged = LIST_HEAD_INITIALIZER(moea_pvo_kunmanaged); /* list of unmanaged pages */ static struct rwlock_padalign pvh_global_lock; uma_zone_t moea_upvo_zone; /* zone for pvo entries for unmanaged pages */ uma_zone_t moea_mpvo_zone; /* zone for pvo entries for managed pages */ #define BPVO_POOL_SIZE 32768 static struct pvo_entry *moea_bpvo_pool; static int moea_bpvo_pool_index = 0; #define VSID_NBPW (sizeof(u_int32_t) * 8) static u_int moea_vsid_bitmap[NPMAPS / VSID_NBPW]; static boolean_t moea_initialized = FALSE; /* * Statistics. */ u_int moea_pte_valid = 0; u_int moea_pte_overflow = 0; u_int moea_pte_replacements = 0; u_int moea_pvo_entries = 0; u_int moea_pvo_enter_calls = 0; u_int moea_pvo_remove_calls = 0; u_int moea_pte_spills = 0; SYSCTL_INT(_machdep, OID_AUTO, moea_pte_valid, CTLFLAG_RD, &moea_pte_valid, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea_pte_overflow, CTLFLAG_RD, &moea_pte_overflow, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea_pte_replacements, CTLFLAG_RD, &moea_pte_replacements, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea_pvo_entries, CTLFLAG_RD, &moea_pvo_entries, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea_pvo_enter_calls, CTLFLAG_RD, &moea_pvo_enter_calls, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea_pvo_remove_calls, CTLFLAG_RD, &moea_pvo_remove_calls, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea_pte_spills, CTLFLAG_RD, &moea_pte_spills, 0, ""); /* * Allocate physical memory for use in moea_bootstrap. */ static vm_offset_t moea_bootstrap_alloc(vm_size_t, u_int); /* * PTE calls. */ static int moea_pte_insert(u_int, struct pte *); /* * PVO calls. */ static int moea_pvo_enter(pmap_t, uma_zone_t, struct pvo_head *, vm_offset_t, vm_paddr_t, u_int, int); static void moea_pvo_remove(struct pvo_entry *, int); static struct pvo_entry *moea_pvo_find_va(pmap_t, vm_offset_t, int *); static struct pte *moea_pvo_to_pte(const struct pvo_entry *, int); /* * Utility routines. */ static int moea_enter_locked(pmap_t, vm_offset_t, vm_page_t, vm_prot_t, u_int, int8_t); static void moea_syncicache(vm_paddr_t, vm_size_t); static boolean_t moea_query_bit(vm_page_t, int); static u_int moea_clear_bit(vm_page_t, int); static void moea_kremove(mmu_t, vm_offset_t); int moea_pte_spill(vm_offset_t); /* * Kernel MMU interface */ void moea_clear_modify(mmu_t, vm_page_t); void moea_copy_page(mmu_t, vm_page_t, vm_page_t); void moea_copy_pages(mmu_t mmu, vm_page_t *ma, vm_offset_t a_offset, vm_page_t *mb, vm_offset_t b_offset, int xfersize); int moea_enter(mmu_t, pmap_t, vm_offset_t, vm_page_t, vm_prot_t, u_int, int8_t); void moea_enter_object(mmu_t, pmap_t, vm_offset_t, vm_offset_t, vm_page_t, vm_prot_t); void moea_enter_quick(mmu_t, pmap_t, vm_offset_t, vm_page_t, vm_prot_t); vm_paddr_t moea_extract(mmu_t, pmap_t, vm_offset_t); vm_page_t moea_extract_and_hold(mmu_t, pmap_t, vm_offset_t, vm_prot_t); void moea_init(mmu_t); boolean_t moea_is_modified(mmu_t, vm_page_t); boolean_t moea_is_prefaultable(mmu_t, pmap_t, vm_offset_t); boolean_t moea_is_referenced(mmu_t, vm_page_t); int moea_ts_referenced(mmu_t, vm_page_t); vm_offset_t moea_map(mmu_t, vm_offset_t *, vm_paddr_t, vm_paddr_t, int); boolean_t moea_page_exists_quick(mmu_t, pmap_t, vm_page_t); void moea_page_init(mmu_t, vm_page_t); int moea_page_wired_mappings(mmu_t, vm_page_t); void moea_pinit(mmu_t, pmap_t); void moea_pinit0(mmu_t, pmap_t); void moea_protect(mmu_t, pmap_t, vm_offset_t, vm_offset_t, vm_prot_t); void moea_qenter(mmu_t, vm_offset_t, vm_page_t *, int); void moea_qremove(mmu_t, vm_offset_t, int); void moea_release(mmu_t, pmap_t); void moea_remove(mmu_t, pmap_t, vm_offset_t, vm_offset_t); void moea_remove_all(mmu_t, vm_page_t); void moea_remove_write(mmu_t, vm_page_t); void moea_unwire(mmu_t, pmap_t, vm_offset_t, vm_offset_t); void moea_zero_page(mmu_t, vm_page_t); void moea_zero_page_area(mmu_t, vm_page_t, int, int); void moea_activate(mmu_t, struct thread *); void moea_deactivate(mmu_t, struct thread *); void moea_cpu_bootstrap(mmu_t, int); void moea_bootstrap(mmu_t, vm_offset_t, vm_offset_t); void *moea_mapdev(mmu_t, vm_paddr_t, vm_size_t); void *moea_mapdev_attr(mmu_t, vm_paddr_t, vm_size_t, vm_memattr_t); void moea_unmapdev(mmu_t, vm_offset_t, vm_size_t); vm_paddr_t moea_kextract(mmu_t, vm_offset_t); void moea_kenter_attr(mmu_t, vm_offset_t, vm_paddr_t, vm_memattr_t); void moea_kenter(mmu_t, vm_offset_t, vm_paddr_t); void moea_page_set_memattr(mmu_t mmu, vm_page_t m, vm_memattr_t ma); boolean_t moea_dev_direct_mapped(mmu_t, vm_paddr_t, vm_size_t); static void moea_sync_icache(mmu_t, pmap_t, vm_offset_t, vm_size_t); void moea_dumpsys_map(mmu_t mmu, vm_paddr_t pa, size_t sz, void **va); void moea_scan_init(mmu_t mmu); vm_offset_t moea_quick_enter_page(mmu_t mmu, vm_page_t m); void moea_quick_remove_page(mmu_t mmu, vm_offset_t addr); static mmu_method_t moea_methods[] = { MMUMETHOD(mmu_clear_modify, moea_clear_modify), MMUMETHOD(mmu_copy_page, moea_copy_page), MMUMETHOD(mmu_copy_pages, moea_copy_pages), MMUMETHOD(mmu_enter, moea_enter), MMUMETHOD(mmu_enter_object, moea_enter_object), MMUMETHOD(mmu_enter_quick, moea_enter_quick), MMUMETHOD(mmu_extract, moea_extract), MMUMETHOD(mmu_extract_and_hold, moea_extract_and_hold), MMUMETHOD(mmu_init, moea_init), MMUMETHOD(mmu_is_modified, moea_is_modified), MMUMETHOD(mmu_is_prefaultable, moea_is_prefaultable), MMUMETHOD(mmu_is_referenced, moea_is_referenced), MMUMETHOD(mmu_ts_referenced, moea_ts_referenced), MMUMETHOD(mmu_map, moea_map), MMUMETHOD(mmu_page_exists_quick,moea_page_exists_quick), MMUMETHOD(mmu_page_init, moea_page_init), MMUMETHOD(mmu_page_wired_mappings,moea_page_wired_mappings), MMUMETHOD(mmu_pinit, moea_pinit), MMUMETHOD(mmu_pinit0, moea_pinit0), MMUMETHOD(mmu_protect, moea_protect), MMUMETHOD(mmu_qenter, moea_qenter), MMUMETHOD(mmu_qremove, moea_qremove), MMUMETHOD(mmu_release, moea_release), MMUMETHOD(mmu_remove, moea_remove), MMUMETHOD(mmu_remove_all, moea_remove_all), MMUMETHOD(mmu_remove_write, moea_remove_write), MMUMETHOD(mmu_sync_icache, moea_sync_icache), MMUMETHOD(mmu_unwire, moea_unwire), MMUMETHOD(mmu_zero_page, moea_zero_page), MMUMETHOD(mmu_zero_page_area, moea_zero_page_area), MMUMETHOD(mmu_activate, moea_activate), MMUMETHOD(mmu_deactivate, moea_deactivate), MMUMETHOD(mmu_page_set_memattr, moea_page_set_memattr), MMUMETHOD(mmu_quick_enter_page, moea_quick_enter_page), MMUMETHOD(mmu_quick_remove_page, moea_quick_remove_page), /* Internal interfaces */ MMUMETHOD(mmu_bootstrap, moea_bootstrap), MMUMETHOD(mmu_cpu_bootstrap, moea_cpu_bootstrap), MMUMETHOD(mmu_mapdev_attr, moea_mapdev_attr), MMUMETHOD(mmu_mapdev, moea_mapdev), MMUMETHOD(mmu_unmapdev, moea_unmapdev), MMUMETHOD(mmu_kextract, moea_kextract), MMUMETHOD(mmu_kenter, moea_kenter), MMUMETHOD(mmu_kenter_attr, moea_kenter_attr), MMUMETHOD(mmu_dev_direct_mapped,moea_dev_direct_mapped), MMUMETHOD(mmu_scan_init, moea_scan_init), MMUMETHOD(mmu_dumpsys_map, moea_dumpsys_map), { 0, 0 } }; MMU_DEF(oea_mmu, MMU_TYPE_OEA, moea_methods, 0); static __inline uint32_t moea_calc_wimg(vm_paddr_t pa, vm_memattr_t ma) { uint32_t pte_lo; int i; if (ma != VM_MEMATTR_DEFAULT) { switch (ma) { case VM_MEMATTR_UNCACHEABLE: return (PTE_I | PTE_G); case VM_MEMATTR_CACHEABLE: return (PTE_M); case VM_MEMATTR_WRITE_COMBINING: case VM_MEMATTR_WRITE_BACK: case VM_MEMATTR_PREFETCHABLE: return (PTE_I); case VM_MEMATTR_WRITE_THROUGH: return (PTE_W | PTE_M); } } /* * Assume the page is cache inhibited and access is guarded unless * it's in our available memory array. */ pte_lo = PTE_I | PTE_G; for (i = 0; i < pregions_sz; i++) { if ((pa >= pregions[i].mr_start) && (pa < (pregions[i].mr_start + pregions[i].mr_size))) { pte_lo = PTE_M; break; } } return pte_lo; } static void tlbie(vm_offset_t va) { mtx_lock_spin(&tlbie_mtx); __asm __volatile("ptesync"); __asm __volatile("tlbie %0" :: "r"(va)); __asm __volatile("eieio; tlbsync; ptesync"); mtx_unlock_spin(&tlbie_mtx); } static void tlbia(void) { vm_offset_t va; for (va = 0; va < 0x00040000; va += 0x00001000) { __asm __volatile("tlbie %0" :: "r"(va)); powerpc_sync(); } __asm __volatile("tlbsync"); powerpc_sync(); } static __inline int va_to_sr(u_int *sr, vm_offset_t va) { return (sr[(uintptr_t)va >> ADDR_SR_SHFT]); } static __inline u_int va_to_pteg(u_int sr, vm_offset_t addr) { u_int hash; hash = (sr & SR_VSID_MASK) ^ (((u_int)addr & ADDR_PIDX) >> ADDR_PIDX_SHFT); return (hash & moea_pteg_mask); } static __inline struct pvo_head * vm_page_to_pvoh(vm_page_t m) { return (&m->md.mdpg_pvoh); } static __inline void moea_attr_clear(vm_page_t m, int ptebit) { rw_assert(&pvh_global_lock, RA_WLOCKED); m->md.mdpg_attrs &= ~ptebit; } static __inline int moea_attr_fetch(vm_page_t m) { return (m->md.mdpg_attrs); } static __inline void moea_attr_save(vm_page_t m, int ptebit) { rw_assert(&pvh_global_lock, RA_WLOCKED); m->md.mdpg_attrs |= ptebit; } static __inline int moea_pte_compare(const struct pte *pt, const struct pte *pvo_pt) { if (pt->pte_hi == pvo_pt->pte_hi) return (1); return (0); } static __inline int moea_pte_match(struct pte *pt, u_int sr, vm_offset_t va, int which) { return (pt->pte_hi & ~PTE_VALID) == (((sr & SR_VSID_MASK) << PTE_VSID_SHFT) | ((va >> ADDR_API_SHFT) & PTE_API) | which); } static __inline void moea_pte_create(struct pte *pt, u_int sr, vm_offset_t va, u_int pte_lo) { mtx_assert(&moea_table_mutex, MA_OWNED); /* * Construct a PTE. Default to IMB initially. Valid bit only gets * set when the real pte is set in memory. * * Note: Don't set the valid bit for correct operation of tlb update. */ pt->pte_hi = ((sr & SR_VSID_MASK) << PTE_VSID_SHFT) | (((va & ADDR_PIDX) >> ADDR_API_SHFT) & PTE_API); pt->pte_lo = pte_lo; } static __inline void moea_pte_synch(struct pte *pt, struct pte *pvo_pt) { mtx_assert(&moea_table_mutex, MA_OWNED); pvo_pt->pte_lo |= pt->pte_lo & (PTE_REF | PTE_CHG); } static __inline void moea_pte_clear(struct pte *pt, vm_offset_t va, int ptebit) { mtx_assert(&moea_table_mutex, MA_OWNED); /* * As shown in Section 7.6.3.2.3 */ pt->pte_lo &= ~ptebit; tlbie(va); } static __inline void moea_pte_set(struct pte *pt, struct pte *pvo_pt) { mtx_assert(&moea_table_mutex, MA_OWNED); pvo_pt->pte_hi |= PTE_VALID; /* * Update the PTE as defined in section 7.6.3.1. * Note that the REF/CHG bits are from pvo_pt and thus should have * been saved so this routine can restore them (if desired). */ pt->pte_lo = pvo_pt->pte_lo; powerpc_sync(); pt->pte_hi = pvo_pt->pte_hi; powerpc_sync(); moea_pte_valid++; } static __inline void moea_pte_unset(struct pte *pt, struct pte *pvo_pt, vm_offset_t va) { mtx_assert(&moea_table_mutex, MA_OWNED); pvo_pt->pte_hi &= ~PTE_VALID; /* * Force the reg & chg bits back into the PTEs. */ powerpc_sync(); /* * Invalidate the pte. */ pt->pte_hi &= ~PTE_VALID; tlbie(va); /* * Save the reg & chg bits. */ moea_pte_synch(pt, pvo_pt); moea_pte_valid--; } static __inline void moea_pte_change(struct pte *pt, struct pte *pvo_pt, vm_offset_t va) { /* * Invalidate the PTE */ moea_pte_unset(pt, pvo_pt, va); moea_pte_set(pt, pvo_pt); } /* * Quick sort callout for comparing memory regions. */ static int om_cmp(const void *a, const void *b); static int om_cmp(const void *a, const void *b) { const struct ofw_map *mapa; const struct ofw_map *mapb; mapa = a; mapb = b; if (mapa->om_pa < mapb->om_pa) return (-1); else if (mapa->om_pa > mapb->om_pa) return (1); else return (0); } void moea_cpu_bootstrap(mmu_t mmup, int ap) { u_int sdr; int i; if (ap) { powerpc_sync(); __asm __volatile("mtdbatu 0,%0" :: "r"(battable[0].batu)); __asm __volatile("mtdbatl 0,%0" :: "r"(battable[0].batl)); isync(); __asm __volatile("mtibatu 0,%0" :: "r"(battable[0].batu)); __asm __volatile("mtibatl 0,%0" :: "r"(battable[0].batl)); isync(); } __asm __volatile("mtdbatu 1,%0" :: "r"(battable[8].batu)); __asm __volatile("mtdbatl 1,%0" :: "r"(battable[8].batl)); isync(); __asm __volatile("mtibatu 1,%0" :: "r"(0)); __asm __volatile("mtdbatu 2,%0" :: "r"(0)); __asm __volatile("mtibatu 2,%0" :: "r"(0)); __asm __volatile("mtdbatu 3,%0" :: "r"(0)); __asm __volatile("mtibatu 3,%0" :: "r"(0)); isync(); for (i = 0; i < 16; i++) mtsrin(i << ADDR_SR_SHFT, kernel_pmap->pm_sr[i]); powerpc_sync(); sdr = (u_int)moea_pteg_table | (moea_pteg_mask >> 10); __asm __volatile("mtsdr1 %0" :: "r"(sdr)); isync(); tlbia(); } void moea_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) { ihandle_t mmui; phandle_t chosen, mmu; int sz; int i, j; vm_size_t size, physsz, hwphyssz; vm_offset_t pa, va, off; void *dpcpu; register_t msr; /* * Set up BAT0 to map the lowest 256 MB area */ battable[0x0].batl = BATL(0x00000000, BAT_M, BAT_PP_RW); battable[0x0].batu = BATU(0x00000000, BAT_BL_256M, BAT_Vs); /* * Map PCI memory space. */ battable[0x8].batl = BATL(0x80000000, BAT_I|BAT_G, BAT_PP_RW); battable[0x8].batu = BATU(0x80000000, BAT_BL_256M, BAT_Vs); battable[0x9].batl = BATL(0x90000000, BAT_I|BAT_G, BAT_PP_RW); battable[0x9].batu = BATU(0x90000000, BAT_BL_256M, BAT_Vs); battable[0xa].batl = BATL(0xa0000000, BAT_I|BAT_G, BAT_PP_RW); battable[0xa].batu = BATU(0xa0000000, BAT_BL_256M, BAT_Vs); battable[0xb].batl = BATL(0xb0000000, BAT_I|BAT_G, BAT_PP_RW); battable[0xb].batu = BATU(0xb0000000, BAT_BL_256M, BAT_Vs); /* * Map obio devices. */ battable[0xf].batl = BATL(0xf0000000, BAT_I|BAT_G, BAT_PP_RW); battable[0xf].batu = BATU(0xf0000000, BAT_BL_256M, BAT_Vs); /* * Use an IBAT and a DBAT to map the bottom segment of memory * where we are. Turn off instruction relocation temporarily * to prevent faults while reprogramming the IBAT. */ msr = mfmsr(); mtmsr(msr & ~PSL_IR); __asm (".balign 32; \n" "mtibatu 0,%0; mtibatl 0,%1; isync; \n" "mtdbatu 0,%0; mtdbatl 0,%1; isync" :: "r"(battable[0].batu), "r"(battable[0].batl)); mtmsr(msr); /* map pci space */ __asm __volatile("mtdbatu 1,%0" :: "r"(battable[8].batu)); __asm __volatile("mtdbatl 1,%0" :: "r"(battable[8].batl)); isync(); /* set global direct map flag */ hw_direct_map = 1; mem_regions(&pregions, &pregions_sz, ®ions, ®ions_sz); CTR0(KTR_PMAP, "moea_bootstrap: physical memory"); for (i = 0; i < pregions_sz; i++) { vm_offset_t pa; vm_offset_t end; CTR3(KTR_PMAP, "physregion: %#x - %#x (%#x)", pregions[i].mr_start, pregions[i].mr_start + pregions[i].mr_size, pregions[i].mr_size); /* * Install entries into the BAT table to allow all * of physmem to be convered by on-demand BAT entries. * The loop will sometimes set the same battable element * twice, but that's fine since they won't be used for * a while yet. */ pa = pregions[i].mr_start & 0xf0000000; end = pregions[i].mr_start + pregions[i].mr_size; do { u_int n = pa >> ADDR_SR_SHFT; battable[n].batl = BATL(pa, BAT_M, BAT_PP_RW); battable[n].batu = BATU(pa, BAT_BL_256M, BAT_Vs); pa += SEGMENT_LENGTH; } while (pa < end); } if (sizeof(phys_avail)/sizeof(phys_avail[0]) < regions_sz) panic("moea_bootstrap: phys_avail too small"); phys_avail_count = 0; physsz = 0; hwphyssz = 0; TUNABLE_ULONG_FETCH("hw.physmem", (u_long *) &hwphyssz); for (i = 0, j = 0; i < regions_sz; i++, j += 2) { CTR3(KTR_PMAP, "region: %#x - %#x (%#x)", regions[i].mr_start, regions[i].mr_start + regions[i].mr_size, regions[i].mr_size); if (hwphyssz != 0 && (physsz + regions[i].mr_size) >= hwphyssz) { if (physsz < hwphyssz) { phys_avail[j] = regions[i].mr_start; phys_avail[j + 1] = regions[i].mr_start + hwphyssz - physsz; physsz = hwphyssz; phys_avail_count++; } break; } phys_avail[j] = regions[i].mr_start; phys_avail[j + 1] = regions[i].mr_start + regions[i].mr_size; phys_avail_count++; physsz += regions[i].mr_size; } /* Check for overlap with the kernel and exception vectors */ for (j = 0; j < 2*phys_avail_count; j+=2) { if (phys_avail[j] < EXC_LAST) phys_avail[j] += EXC_LAST; if (kernelstart >= phys_avail[j] && kernelstart < phys_avail[j+1]) { if (kernelend < phys_avail[j+1]) { phys_avail[2*phys_avail_count] = (kernelend & ~PAGE_MASK) + PAGE_SIZE; phys_avail[2*phys_avail_count + 1] = phys_avail[j+1]; phys_avail_count++; } phys_avail[j+1] = kernelstart & ~PAGE_MASK; } if (kernelend >= phys_avail[j] && kernelend < phys_avail[j+1]) { if (kernelstart > phys_avail[j]) { phys_avail[2*phys_avail_count] = phys_avail[j]; phys_avail[2*phys_avail_count + 1] = kernelstart & ~PAGE_MASK; phys_avail_count++; } phys_avail[j] = (kernelend & ~PAGE_MASK) + PAGE_SIZE; } } physmem = btoc(physsz); /* * Allocate PTEG table. */ #ifdef PTEGCOUNT moea_pteg_count = PTEGCOUNT; #else moea_pteg_count = 0x1000; while (moea_pteg_count < physmem) moea_pteg_count <<= 1; moea_pteg_count >>= 1; #endif /* PTEGCOUNT */ size = moea_pteg_count * sizeof(struct pteg); CTR2(KTR_PMAP, "moea_bootstrap: %d PTEGs, %d bytes", moea_pteg_count, size); moea_pteg_table = (struct pteg *)moea_bootstrap_alloc(size, size); CTR1(KTR_PMAP, "moea_bootstrap: PTEG table at %p", moea_pteg_table); bzero((void *)moea_pteg_table, moea_pteg_count * sizeof(struct pteg)); moea_pteg_mask = moea_pteg_count - 1; /* * Allocate pv/overflow lists. */ size = sizeof(struct pvo_head) * moea_pteg_count; moea_pvo_table = (struct pvo_head *)moea_bootstrap_alloc(size, PAGE_SIZE); CTR1(KTR_PMAP, "moea_bootstrap: PVO table at %p", moea_pvo_table); for (i = 0; i < moea_pteg_count; i++) LIST_INIT(&moea_pvo_table[i]); /* * Initialize the lock that synchronizes access to the pteg and pvo * tables. */ mtx_init(&moea_table_mutex, "pmap table", NULL, MTX_DEF | MTX_RECURSE); mtx_init(&moea_vsid_mutex, "VSID table", NULL, MTX_DEF); mtx_init(&tlbie_mtx, "tlbie", NULL, MTX_SPIN); /* * Initialise the unmanaged pvo pool. */ moea_bpvo_pool = (struct pvo_entry *)moea_bootstrap_alloc( BPVO_POOL_SIZE*sizeof(struct pvo_entry), 0); moea_bpvo_pool_index = 0; /* * Make sure kernel vsid is allocated as well as VSID 0. */ moea_vsid_bitmap[(KERNEL_VSIDBITS & (NPMAPS - 1)) / VSID_NBPW] |= 1 << (KERNEL_VSIDBITS % VSID_NBPW); moea_vsid_bitmap[0] |= 1; /* * Initialize the kernel pmap (which is statically allocated). */ PMAP_LOCK_INIT(kernel_pmap); for (i = 0; i < 16; i++) kernel_pmap->pm_sr[i] = EMPTY_SEGMENT + i; CPU_FILL(&kernel_pmap->pm_active); RB_INIT(&kernel_pmap->pmap_pvo); /* * Initialize the global pv list lock. */ rw_init(&pvh_global_lock, "pmap pv global"); /* * Set up the Open Firmware mappings */ chosen = OF_finddevice("/chosen"); if (chosen != -1 && OF_getprop(chosen, "mmu", &mmui, 4) != -1 && (mmu = OF_instance_to_package(mmui)) != -1 && (sz = OF_getproplen(mmu, "translations")) != -1) { translations = NULL; for (i = 0; phys_avail[i] != 0; i += 2) { if (phys_avail[i + 1] >= sz) { translations = (struct ofw_map *)phys_avail[i]; break; } } if (translations == NULL) panic("moea_bootstrap: no space to copy translations"); bzero(translations, sz); if (OF_getprop(mmu, "translations", translations, sz) == -1) panic("moea_bootstrap: can't get ofw translations"); CTR0(KTR_PMAP, "moea_bootstrap: translations"); sz /= sizeof(*translations); qsort(translations, sz, sizeof (*translations), om_cmp); for (i = 0; i < sz; i++) { CTR3(KTR_PMAP, "translation: pa=%#x va=%#x len=%#x", translations[i].om_pa, translations[i].om_va, translations[i].om_len); /* * If the mapping is 1:1, let the RAM and device * on-demand BAT tables take care of the translation. */ if (translations[i].om_va == translations[i].om_pa) continue; /* Enter the pages */ for (off = 0; off < translations[i].om_len; off += PAGE_SIZE) moea_kenter(mmup, translations[i].om_va + off, translations[i].om_pa + off); } } /* * Calculate the last available physical address. */ for (i = 0; phys_avail[i + 2] != 0; i += 2) ; Maxmem = powerpc_btop(phys_avail[i + 1]); moea_cpu_bootstrap(mmup,0); mtmsr(mfmsr() | PSL_DR | PSL_IR); pmap_bootstrapped++; /* * Set the start and end of kva. */ virtual_avail = VM_MIN_KERNEL_ADDRESS; virtual_end = VM_MAX_SAFE_KERNEL_ADDRESS; /* * Allocate a kernel stack with a guard page for thread0 and map it * into the kernel page map. */ pa = moea_bootstrap_alloc(kstack_pages * PAGE_SIZE, PAGE_SIZE); va = virtual_avail + KSTACK_GUARD_PAGES * PAGE_SIZE; virtual_avail = va + kstack_pages * PAGE_SIZE; CTR2(KTR_PMAP, "moea_bootstrap: kstack0 at %#x (%#x)", pa, va); thread0.td_kstack = va; thread0.td_kstack_pages = kstack_pages; for (i = 0; i < kstack_pages; i++) { moea_kenter(mmup, va, pa); pa += PAGE_SIZE; va += PAGE_SIZE; } /* * Allocate virtual address space for the message buffer. */ pa = msgbuf_phys = moea_bootstrap_alloc(msgbufsize, PAGE_SIZE); msgbufp = (struct msgbuf *)virtual_avail; va = virtual_avail; virtual_avail += round_page(msgbufsize); while (va < virtual_avail) { moea_kenter(mmup, va, pa); pa += PAGE_SIZE; va += PAGE_SIZE; } /* * Allocate virtual address space for the dynamic percpu area. */ pa = moea_bootstrap_alloc(DPCPU_SIZE, PAGE_SIZE); dpcpu = (void *)virtual_avail; va = virtual_avail; virtual_avail += DPCPU_SIZE; while (va < virtual_avail) { moea_kenter(mmup, va, pa); pa += PAGE_SIZE; va += PAGE_SIZE; } dpcpu_init(dpcpu, 0); } /* * Activate a user pmap. The pmap must be activated before it's address * space can be accessed in any way. */ void moea_activate(mmu_t mmu, struct thread *td) { pmap_t pm, pmr; /* * Load all the data we need up front to encourage the compiler to * not issue any loads while we have interrupts disabled below. */ pm = &td->td_proc->p_vmspace->vm_pmap; pmr = pm->pmap_phys; CPU_SET(PCPU_GET(cpuid), &pm->pm_active); PCPU_SET(curpmap, pmr); mtsrin(USER_SR << ADDR_SR_SHFT, td->td_pcb->pcb_cpu.aim.usr_vsid); } void moea_deactivate(mmu_t mmu, struct thread *td) { pmap_t pm; pm = &td->td_proc->p_vmspace->vm_pmap; CPU_CLR(PCPU_GET(cpuid), &pm->pm_active); PCPU_SET(curpmap, NULL); } void moea_unwire(mmu_t mmu, pmap_t pm, vm_offset_t sva, vm_offset_t eva) { struct pvo_entry key, *pvo; PMAP_LOCK(pm); key.pvo_vaddr = sva; for (pvo = RB_NFIND(pvo_tree, &pm->pmap_pvo, &key); pvo != NULL && PVO_VADDR(pvo) < eva; pvo = RB_NEXT(pvo_tree, &pm->pmap_pvo, pvo)) { if ((pvo->pvo_vaddr & PVO_WIRED) == 0) panic("moea_unwire: pvo %p is missing PVO_WIRED", pvo); pvo->pvo_vaddr &= ~PVO_WIRED; pm->pm_stats.wired_count--; } PMAP_UNLOCK(pm); } void moea_copy_page(mmu_t mmu, vm_page_t msrc, vm_page_t mdst) { vm_offset_t dst; vm_offset_t src; dst = VM_PAGE_TO_PHYS(mdst); src = VM_PAGE_TO_PHYS(msrc); bcopy((void *)src, (void *)dst, PAGE_SIZE); } void moea_copy_pages(mmu_t mmu, vm_page_t *ma, vm_offset_t a_offset, vm_page_t *mb, vm_offset_t b_offset, int xfersize) { void *a_cp, *b_cp; vm_offset_t a_pg_offset, b_pg_offset; int cnt; while (xfersize > 0) { a_pg_offset = a_offset & PAGE_MASK; cnt = min(xfersize, PAGE_SIZE - a_pg_offset); a_cp = (char *)VM_PAGE_TO_PHYS(ma[a_offset >> PAGE_SHIFT]) + a_pg_offset; b_pg_offset = b_offset & PAGE_MASK; cnt = min(cnt, PAGE_SIZE - b_pg_offset); b_cp = (char *)VM_PAGE_TO_PHYS(mb[b_offset >> PAGE_SHIFT]) + b_pg_offset; bcopy(a_cp, b_cp, cnt); a_offset += cnt; b_offset += cnt; xfersize -= cnt; } } /* * Zero a page of physical memory by temporarily mapping it into the tlb. */ void moea_zero_page(mmu_t mmu, vm_page_t m) { vm_offset_t off, pa = VM_PAGE_TO_PHYS(m); for (off = 0; off < PAGE_SIZE; off += cacheline_size) __asm __volatile("dcbz 0,%0" :: "r"(pa + off)); } void moea_zero_page_area(mmu_t mmu, vm_page_t m, int off, int size) { vm_offset_t pa = VM_PAGE_TO_PHYS(m); void *va = (void *)(pa + off); bzero(va, size); } vm_offset_t moea_quick_enter_page(mmu_t mmu, vm_page_t m) { return (VM_PAGE_TO_PHYS(m)); } void moea_quick_remove_page(mmu_t mmu, vm_offset_t addr) { } /* * Map the given physical page at the specified virtual address in the * target pmap with the protection requested. If specified the page * will be wired down. */ int moea_enter(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, u_int flags, int8_t psind) { int error; for (;;) { rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); error = moea_enter_locked(pmap, va, m, prot, flags, psind); rw_wunlock(&pvh_global_lock); PMAP_UNLOCK(pmap); if (error != ENOMEM) return (KERN_SUCCESS); if ((flags & PMAP_ENTER_NOSLEEP) != 0) return (KERN_RESOURCE_SHORTAGE); VM_OBJECT_ASSERT_UNLOCKED(m->object); VM_WAIT; } } /* * Map the given physical page at the specified virtual address in the * target pmap with the protection requested. If specified the page * will be wired down. * * The global pvh and pmap must be locked. */ static int moea_enter_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, u_int flags, int8_t psind __unused) { struct pvo_head *pvo_head; uma_zone_t zone; u_int pte_lo, pvo_flags; int error; if (pmap_bootstrapped) rw_assert(&pvh_global_lock, RA_WLOCKED); PMAP_LOCK_ASSERT(pmap, MA_OWNED); if ((m->oflags & VPO_UNMANAGED) == 0 && !vm_page_xbusied(m)) VM_OBJECT_ASSERT_LOCKED(m->object); if ((m->oflags & VPO_UNMANAGED) != 0 || !moea_initialized) { pvo_head = &moea_pvo_kunmanaged; zone = moea_upvo_zone; pvo_flags = 0; } else { pvo_head = vm_page_to_pvoh(m); zone = moea_mpvo_zone; pvo_flags = PVO_MANAGED; } pte_lo = moea_calc_wimg(VM_PAGE_TO_PHYS(m), pmap_page_get_memattr(m)); if (prot & VM_PROT_WRITE) { pte_lo |= PTE_BW; if (pmap_bootstrapped && (m->oflags & VPO_UNMANAGED) == 0) vm_page_aflag_set(m, PGA_WRITEABLE); } else pte_lo |= PTE_BR; if ((flags & PMAP_ENTER_WIRED) != 0) pvo_flags |= PVO_WIRED; error = moea_pvo_enter(pmap, zone, pvo_head, va, VM_PAGE_TO_PHYS(m), pte_lo, pvo_flags); /* * Flush the real page from the instruction cache. This has be done * for all user mappings to prevent information leakage via the * instruction cache. moea_pvo_enter() returns ENOENT for the first * mapping for a page. */ if (pmap != kernel_pmap && error == ENOENT && (pte_lo & (PTE_I | PTE_G)) == 0) moea_syncicache(VM_PAGE_TO_PHYS(m), PAGE_SIZE); return (error); } /* * Maps a sequence of resident pages belonging to the same object. * The sequence begins with the given page m_start. This page is * mapped at the given virtual address start. Each subsequent page is * mapped at a virtual address that is offset from start by the same * amount as the page is offset from m_start within the object. The * last page in the sequence is the page with the largest offset from * m_start that can be mapped at a virtual address less than the given * virtual address end. Not every virtual page between start and end * is mapped; only those for which a resident page exists with the * corresponding offset from m_start are mapped. */ void moea_enter_object(mmu_t mmu, pmap_t pm, vm_offset_t start, vm_offset_t end, vm_page_t m_start, vm_prot_t prot) { vm_page_t m; vm_pindex_t diff, psize; VM_OBJECT_ASSERT_LOCKED(m_start->object); psize = atop(end - start); m = m_start; rw_wlock(&pvh_global_lock); PMAP_LOCK(pm); while (m != NULL && (diff = m->pindex - m_start->pindex) < psize) { moea_enter_locked(pm, start + ptoa(diff), m, prot & (VM_PROT_READ | VM_PROT_EXECUTE), 0, 0); m = TAILQ_NEXT(m, listq); } rw_wunlock(&pvh_global_lock); PMAP_UNLOCK(pm); } void moea_enter_quick(mmu_t mmu, pmap_t pm, vm_offset_t va, vm_page_t m, vm_prot_t prot) { rw_wlock(&pvh_global_lock); PMAP_LOCK(pm); moea_enter_locked(pm, va, m, prot & (VM_PROT_READ | VM_PROT_EXECUTE), 0, 0); rw_wunlock(&pvh_global_lock); PMAP_UNLOCK(pm); } vm_paddr_t moea_extract(mmu_t mmu, pmap_t pm, vm_offset_t va) { struct pvo_entry *pvo; vm_paddr_t pa; PMAP_LOCK(pm); pvo = moea_pvo_find_va(pm, va & ~ADDR_POFF, NULL); if (pvo == NULL) pa = 0; else pa = (pvo->pvo_pte.pte.pte_lo & PTE_RPGN) | (va & ADDR_POFF); PMAP_UNLOCK(pm); return (pa); } /* * Atomically extract and hold the physical page with the given * pmap and virtual address pair if that mapping permits the given * protection. */ vm_page_t moea_extract_and_hold(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_prot_t prot) { struct pvo_entry *pvo; vm_page_t m; vm_paddr_t pa; m = NULL; pa = 0; PMAP_LOCK(pmap); retry: pvo = moea_pvo_find_va(pmap, va & ~ADDR_POFF, NULL); if (pvo != NULL && (pvo->pvo_pte.pte.pte_hi & PTE_VALID) && ((pvo->pvo_pte.pte.pte_lo & PTE_PP) == PTE_RW || (prot & VM_PROT_WRITE) == 0)) { if (vm_page_pa_tryrelock(pmap, pvo->pvo_pte.pte.pte_lo & PTE_RPGN, &pa)) goto retry; m = PHYS_TO_VM_PAGE(pvo->pvo_pte.pte.pte_lo & PTE_RPGN); vm_page_hold(m); } PA_UNLOCK_COND(pa); PMAP_UNLOCK(pmap); return (m); } void moea_init(mmu_t mmu) { moea_upvo_zone = uma_zcreate("UPVO entry", sizeof (struct pvo_entry), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM | UMA_ZONE_NOFREE); moea_mpvo_zone = uma_zcreate("MPVO entry", sizeof(struct pvo_entry), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM | UMA_ZONE_NOFREE); moea_initialized = TRUE; } boolean_t moea_is_referenced(mmu_t mmu, vm_page_t m) { boolean_t rv; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea_is_referenced: page %p is not managed", m)); rw_wlock(&pvh_global_lock); rv = moea_query_bit(m, PTE_REF); rw_wunlock(&pvh_global_lock); return (rv); } boolean_t moea_is_modified(mmu_t mmu, vm_page_t m) { boolean_t rv; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea_is_modified: page %p is not managed", m)); /* * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * concurrently set while the object is locked. Thus, if PGA_WRITEABLE * is clear, no PTEs can have PTE_CHG set. */ VM_OBJECT_ASSERT_WLOCKED(m->object); if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return (FALSE); rw_wlock(&pvh_global_lock); rv = moea_query_bit(m, PTE_CHG); rw_wunlock(&pvh_global_lock); return (rv); } boolean_t moea_is_prefaultable(mmu_t mmu, pmap_t pmap, vm_offset_t va) { struct pvo_entry *pvo; boolean_t rv; PMAP_LOCK(pmap); pvo = moea_pvo_find_va(pmap, va & ~ADDR_POFF, NULL); rv = pvo == NULL || (pvo->pvo_pte.pte.pte_hi & PTE_VALID) == 0; PMAP_UNLOCK(pmap); return (rv); } void moea_clear_modify(mmu_t mmu, vm_page_t m) { KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea_clear_modify: page %p is not managed", m)); VM_OBJECT_ASSERT_WLOCKED(m->object); KASSERT(!vm_page_xbusied(m), ("moea_clear_modify: page %p is exclusive busy", m)); /* * If the page is not PGA_WRITEABLE, then no PTEs can have PTE_CHG * set. If the object containing the page is locked and the page is * not exclusive busied, then PGA_WRITEABLE cannot be concurrently set. */ if ((m->aflags & PGA_WRITEABLE) == 0) return; rw_wlock(&pvh_global_lock); moea_clear_bit(m, PTE_CHG); rw_wunlock(&pvh_global_lock); } /* * Clear the write and modified bits in each of the given page's mappings. */ void moea_remove_write(mmu_t mmu, vm_page_t m) { struct pvo_entry *pvo; struct pte *pt; pmap_t pmap; u_int lo; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea_remove_write: page %p is not managed", m)); /* * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * set by another thread while the object is locked. Thus, * if PGA_WRITEABLE is clear, no page table entries need updating. */ VM_OBJECT_ASSERT_WLOCKED(m->object); if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return; rw_wlock(&pvh_global_lock); lo = moea_attr_fetch(m); powerpc_sync(); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { pmap = pvo->pvo_pmap; PMAP_LOCK(pmap); if ((pvo->pvo_pte.pte.pte_lo & PTE_PP) != PTE_BR) { pt = moea_pvo_to_pte(pvo, -1); pvo->pvo_pte.pte.pte_lo &= ~PTE_PP; pvo->pvo_pte.pte.pte_lo |= PTE_BR; if (pt != NULL) { moea_pte_synch(pt, &pvo->pvo_pte.pte); lo |= pvo->pvo_pte.pte.pte_lo; pvo->pvo_pte.pte.pte_lo &= ~PTE_CHG; moea_pte_change(pt, &pvo->pvo_pte.pte, pvo->pvo_vaddr); mtx_unlock(&moea_table_mutex); } } PMAP_UNLOCK(pmap); } if ((lo & PTE_CHG) != 0) { moea_attr_clear(m, PTE_CHG); vm_page_dirty(m); } vm_page_aflag_clear(m, PGA_WRITEABLE); rw_wunlock(&pvh_global_lock); } /* * moea_ts_referenced: * * Return a count of reference bits for a page, clearing those bits. * It is not necessary for every reference bit to be cleared, but it * is necessary that 0 only be returned when there are truly no * reference bits set. * * XXX: The exact number of bits to check and clear is a matter that * should be tested and standardized at some point in the future for * optimal aging of shared pages. */ int moea_ts_referenced(mmu_t mmu, vm_page_t m) { int count; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea_ts_referenced: page %p is not managed", m)); rw_wlock(&pvh_global_lock); count = moea_clear_bit(m, PTE_REF); rw_wunlock(&pvh_global_lock); return (count); } /* * Modify the WIMG settings of all mappings for a page. */ void moea_page_set_memattr(mmu_t mmu, vm_page_t m, vm_memattr_t ma) { struct pvo_entry *pvo; struct pvo_head *pvo_head; struct pte *pt; pmap_t pmap; u_int lo; if ((m->oflags & VPO_UNMANAGED) != 0) { m->md.mdpg_cache_attrs = ma; return; } rw_wlock(&pvh_global_lock); pvo_head = vm_page_to_pvoh(m); lo = moea_calc_wimg(VM_PAGE_TO_PHYS(m), ma); LIST_FOREACH(pvo, pvo_head, pvo_vlink) { pmap = pvo->pvo_pmap; PMAP_LOCK(pmap); pt = moea_pvo_to_pte(pvo, -1); pvo->pvo_pte.pte.pte_lo &= ~PTE_WIMG; pvo->pvo_pte.pte.pte_lo |= lo; if (pt != NULL) { moea_pte_change(pt, &pvo->pvo_pte.pte, pvo->pvo_vaddr); if (pvo->pvo_pmap == kernel_pmap) isync(); } mtx_unlock(&moea_table_mutex); PMAP_UNLOCK(pmap); } m->md.mdpg_cache_attrs = ma; rw_wunlock(&pvh_global_lock); } /* * Map a wired page into kernel virtual address space. */ void moea_kenter(mmu_t mmu, vm_offset_t va, vm_paddr_t pa) { moea_kenter_attr(mmu, va, pa, VM_MEMATTR_DEFAULT); } void moea_kenter_attr(mmu_t mmu, vm_offset_t va, vm_paddr_t pa, vm_memattr_t ma) { u_int pte_lo; int error; #if 0 if (va < VM_MIN_KERNEL_ADDRESS) panic("moea_kenter: attempt to enter non-kernel address %#x", va); #endif pte_lo = moea_calc_wimg(pa, ma); PMAP_LOCK(kernel_pmap); error = moea_pvo_enter(kernel_pmap, moea_upvo_zone, &moea_pvo_kunmanaged, va, pa, pte_lo, PVO_WIRED); if (error != 0 && error != ENOENT) panic("moea_kenter: failed to enter va %#x pa %#x: %d", va, pa, error); PMAP_UNLOCK(kernel_pmap); } /* * Extract the physical page address associated with the given kernel virtual * address. */ vm_paddr_t moea_kextract(mmu_t mmu, vm_offset_t va) { struct pvo_entry *pvo; vm_paddr_t pa; /* * Allow direct mappings on 32-bit OEA */ if (va < VM_MIN_KERNEL_ADDRESS) { return (va); } PMAP_LOCK(kernel_pmap); pvo = moea_pvo_find_va(kernel_pmap, va & ~ADDR_POFF, NULL); KASSERT(pvo != NULL, ("moea_kextract: no addr found")); pa = (pvo->pvo_pte.pte.pte_lo & PTE_RPGN) | (va & ADDR_POFF); PMAP_UNLOCK(kernel_pmap); return (pa); } /* * Remove a wired page from kernel virtual address space. */ void moea_kremove(mmu_t mmu, vm_offset_t va) { moea_remove(mmu, kernel_pmap, va, va + PAGE_SIZE); } /* * Map a range of physical addresses into kernel virtual address space. * * The value passed in *virt is a suggested virtual address for the mapping. * Architectures which can support a direct-mapped physical to virtual region * can return the appropriate address within that region, leaving '*virt' * unchanged. We cannot and therefore do not; *virt is updated with the * first usable address after the mapped region. */ vm_offset_t moea_map(mmu_t mmu, vm_offset_t *virt, vm_paddr_t pa_start, vm_paddr_t pa_end, int prot) { vm_offset_t sva, va; sva = *virt; va = sva; for (; pa_start < pa_end; pa_start += PAGE_SIZE, va += PAGE_SIZE) moea_kenter(mmu, va, pa_start); *virt = va; return (sva); } /* * Returns true if the pmap's pv is one of the first * 16 pvs linked to from this page. This count may * be changed upwards or downwards in the future; it * is only necessary that true be returned for a small * subset of pmaps for proper page aging. */ boolean_t moea_page_exists_quick(mmu_t mmu, pmap_t pmap, vm_page_t m) { int loops; struct pvo_entry *pvo; boolean_t rv; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea_page_exists_quick: page %p is not managed", m)); loops = 0; rv = FALSE; rw_wlock(&pvh_global_lock); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { if (pvo->pvo_pmap == pmap) { rv = TRUE; break; } if (++loops >= 16) break; } rw_wunlock(&pvh_global_lock); return (rv); } void moea_page_init(mmu_t mmu __unused, vm_page_t m) { m->md.mdpg_attrs = 0; m->md.mdpg_cache_attrs = VM_MEMATTR_DEFAULT; LIST_INIT(&m->md.mdpg_pvoh); } /* * Return the number of managed mappings to the given physical page * that are wired. */ int moea_page_wired_mappings(mmu_t mmu, vm_page_t m) { struct pvo_entry *pvo; int count; count = 0; if ((m->oflags & VPO_UNMANAGED) != 0) return (count); rw_wlock(&pvh_global_lock); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) if ((pvo->pvo_vaddr & PVO_WIRED) != 0) count++; rw_wunlock(&pvh_global_lock); return (count); } static u_int moea_vsidcontext; void moea_pinit(mmu_t mmu, pmap_t pmap) { int i, mask; u_int entropy; KASSERT((int)pmap < VM_MIN_KERNEL_ADDRESS, ("moea_pinit: virt pmap")); RB_INIT(&pmap->pmap_pvo); entropy = 0; __asm __volatile("mftb %0" : "=r"(entropy)); if ((pmap->pmap_phys = (pmap_t)moea_kextract(mmu, (vm_offset_t)pmap)) == NULL) { pmap->pmap_phys = pmap; } mtx_lock(&moea_vsid_mutex); /* * Allocate some segment registers for this pmap. */ for (i = 0; i < NPMAPS; i += VSID_NBPW) { u_int hash, n; /* * Create a new value by mutiplying by a prime and adding in * entropy from the timebase register. This is to make the * VSID more random so that the PT hash function collides * less often. (Note that the prime casues gcc to do shifts * instead of a multiply.) */ moea_vsidcontext = (moea_vsidcontext * 0x1105) + entropy; hash = moea_vsidcontext & (NPMAPS - 1); if (hash == 0) /* 0 is special, avoid it */ continue; n = hash >> 5; mask = 1 << (hash & (VSID_NBPW - 1)); hash = (moea_vsidcontext & 0xfffff); if (moea_vsid_bitmap[n] & mask) { /* collision? */ /* anything free in this bucket? */ if (moea_vsid_bitmap[n] == 0xffffffff) { entropy = (moea_vsidcontext >> 20); continue; } i = ffs(~moea_vsid_bitmap[n]) - 1; mask = 1 << i; hash &= rounddown2(0xfffff, VSID_NBPW); hash |= i; } KASSERT(!(moea_vsid_bitmap[n] & mask), ("Allocating in-use VSID group %#x\n", hash)); moea_vsid_bitmap[n] |= mask; for (i = 0; i < 16; i++) pmap->pm_sr[i] = VSID_MAKE(i, hash); mtx_unlock(&moea_vsid_mutex); return; } mtx_unlock(&moea_vsid_mutex); panic("moea_pinit: out of segments"); } /* * Initialize the pmap associated with process 0. */ void moea_pinit0(mmu_t mmu, pmap_t pm) { PMAP_LOCK_INIT(pm); moea_pinit(mmu, pm); bzero(&pm->pm_stats, sizeof(pm->pm_stats)); } /* * Set the physical protection on the specified range of this map as requested. */ void moea_protect(mmu_t mmu, pmap_t pm, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot) { struct pvo_entry *pvo, *tpvo, key; struct pte *pt; KASSERT(pm == &curproc->p_vmspace->vm_pmap || pm == kernel_pmap, ("moea_protect: non current pmap")); if ((prot & VM_PROT_READ) == VM_PROT_NONE) { moea_remove(mmu, pm, sva, eva); return; } rw_wlock(&pvh_global_lock); PMAP_LOCK(pm); key.pvo_vaddr = sva; for (pvo = RB_NFIND(pvo_tree, &pm->pmap_pvo, &key); pvo != NULL && PVO_VADDR(pvo) < eva; pvo = tpvo) { tpvo = RB_NEXT(pvo_tree, &pm->pmap_pvo, pvo); /* * Grab the PTE pointer before we diddle with the cached PTE * copy. */ pt = moea_pvo_to_pte(pvo, -1); /* * Change the protection of the page. */ pvo->pvo_pte.pte.pte_lo &= ~PTE_PP; pvo->pvo_pte.pte.pte_lo |= PTE_BR; /* * If the PVO is in the page table, update that pte as well. */ if (pt != NULL) { moea_pte_change(pt, &pvo->pvo_pte.pte, pvo->pvo_vaddr); mtx_unlock(&moea_table_mutex); } } rw_wunlock(&pvh_global_lock); PMAP_UNLOCK(pm); } /* * Map a list of wired pages into kernel virtual address space. This is * intended for temporary mappings which do not need page modification or * references recorded. Existing mappings in the region are overwritten. */ void moea_qenter(mmu_t mmu, vm_offset_t sva, vm_page_t *m, int count) { vm_offset_t va; va = sva; while (count-- > 0) { moea_kenter(mmu, va, VM_PAGE_TO_PHYS(*m)); va += PAGE_SIZE; m++; } } /* * Remove page mappings from kernel virtual address space. Intended for * temporary mappings entered by moea_qenter. */ void moea_qremove(mmu_t mmu, vm_offset_t sva, int count) { vm_offset_t va; va = sva; while (count-- > 0) { moea_kremove(mmu, va); va += PAGE_SIZE; } } void moea_release(mmu_t mmu, pmap_t pmap) { int idx, mask; /* * Free segment register's VSID */ if (pmap->pm_sr[0] == 0) panic("moea_release"); mtx_lock(&moea_vsid_mutex); idx = VSID_TO_HASH(pmap->pm_sr[0]) & (NPMAPS-1); mask = 1 << (idx % VSID_NBPW); idx /= VSID_NBPW; moea_vsid_bitmap[idx] &= ~mask; mtx_unlock(&moea_vsid_mutex); } /* * Remove the given range of addresses from the specified map. */ void moea_remove(mmu_t mmu, pmap_t pm, vm_offset_t sva, vm_offset_t eva) { struct pvo_entry *pvo, *tpvo, key; rw_wlock(&pvh_global_lock); PMAP_LOCK(pm); key.pvo_vaddr = sva; for (pvo = RB_NFIND(pvo_tree, &pm->pmap_pvo, &key); pvo != NULL && PVO_VADDR(pvo) < eva; pvo = tpvo) { tpvo = RB_NEXT(pvo_tree, &pm->pmap_pvo, pvo); moea_pvo_remove(pvo, -1); } PMAP_UNLOCK(pm); rw_wunlock(&pvh_global_lock); } /* * Remove physical page from all pmaps in which it resides. moea_pvo_remove() * will reflect changes in pte's back to the vm_page. */ void moea_remove_all(mmu_t mmu, vm_page_t m) { struct pvo_head *pvo_head; struct pvo_entry *pvo, *next_pvo; pmap_t pmap; rw_wlock(&pvh_global_lock); pvo_head = vm_page_to_pvoh(m); for (pvo = LIST_FIRST(pvo_head); pvo != NULL; pvo = next_pvo) { next_pvo = LIST_NEXT(pvo, pvo_vlink); pmap = pvo->pvo_pmap; PMAP_LOCK(pmap); moea_pvo_remove(pvo, -1); PMAP_UNLOCK(pmap); } if ((m->aflags & PGA_WRITEABLE) && moea_query_bit(m, PTE_CHG)) { moea_attr_clear(m, PTE_CHG); vm_page_dirty(m); } vm_page_aflag_clear(m, PGA_WRITEABLE); rw_wunlock(&pvh_global_lock); } /* * Allocate a physical page of memory directly from the phys_avail map. * Can only be called from moea_bootstrap before avail start and end are * calculated. */ static vm_offset_t moea_bootstrap_alloc(vm_size_t size, u_int align) { vm_offset_t s, e; int i, j; size = round_page(size); for (i = 0; phys_avail[i + 1] != 0; i += 2) { if (align != 0) s = roundup2(phys_avail[i], align); else s = phys_avail[i]; e = s + size; if (s < phys_avail[i] || e > phys_avail[i + 1]) continue; if (s == phys_avail[i]) { phys_avail[i] += size; } else if (e == phys_avail[i + 1]) { phys_avail[i + 1] -= size; } else { for (j = phys_avail_count * 2; j > i; j -= 2) { phys_avail[j] = phys_avail[j - 2]; phys_avail[j + 1] = phys_avail[j - 1]; } phys_avail[i + 3] = phys_avail[i + 1]; phys_avail[i + 1] = s; phys_avail[i + 2] = e; phys_avail_count++; } return (s); } panic("moea_bootstrap_alloc: could not allocate memory"); } static void moea_syncicache(vm_paddr_t pa, vm_size_t len) { __syncicache((void *)pa, len); } static int moea_pvo_enter(pmap_t pm, uma_zone_t zone, struct pvo_head *pvo_head, vm_offset_t va, vm_paddr_t pa, u_int pte_lo, int flags) { struct pvo_entry *pvo; u_int sr; int first; u_int ptegidx; int i; int bootstrap; moea_pvo_enter_calls++; first = 0; bootstrap = 0; /* * Compute the PTE Group index. */ va &= ~ADDR_POFF; sr = va_to_sr(pm->pm_sr, va); ptegidx = va_to_pteg(sr, va); /* * Remove any existing mapping for this page. Reuse the pvo entry if * there is a mapping. */ mtx_lock(&moea_table_mutex); LIST_FOREACH(pvo, &moea_pvo_table[ptegidx], pvo_olink) { if (pvo->pvo_pmap == pm && PVO_VADDR(pvo) == va) { if ((pvo->pvo_pte.pte.pte_lo & PTE_RPGN) == pa && (pvo->pvo_pte.pte.pte_lo & PTE_PP) == (pte_lo & PTE_PP)) { /* * The PTE is not changing. Instead, this may * be a request to change the mapping's wired * attribute. */ mtx_unlock(&moea_table_mutex); if ((flags & PVO_WIRED) != 0 && (pvo->pvo_vaddr & PVO_WIRED) == 0) { pvo->pvo_vaddr |= PVO_WIRED; pm->pm_stats.wired_count++; } else if ((flags & PVO_WIRED) == 0 && (pvo->pvo_vaddr & PVO_WIRED) != 0) { pvo->pvo_vaddr &= ~PVO_WIRED; pm->pm_stats.wired_count--; } return (0); } moea_pvo_remove(pvo, -1); break; } } /* * If we aren't overwriting a mapping, try to allocate. */ if (moea_initialized) { pvo = uma_zalloc(zone, M_NOWAIT); } else { if (moea_bpvo_pool_index >= BPVO_POOL_SIZE) { panic("moea_enter: bpvo pool exhausted, %d, %d, %d", moea_bpvo_pool_index, BPVO_POOL_SIZE, BPVO_POOL_SIZE * sizeof(struct pvo_entry)); } pvo = &moea_bpvo_pool[moea_bpvo_pool_index]; moea_bpvo_pool_index++; bootstrap = 1; } if (pvo == NULL) { mtx_unlock(&moea_table_mutex); return (ENOMEM); } moea_pvo_entries++; pvo->pvo_vaddr = va; pvo->pvo_pmap = pm; LIST_INSERT_HEAD(&moea_pvo_table[ptegidx], pvo, pvo_olink); pvo->pvo_vaddr &= ~ADDR_POFF; if (flags & PVO_WIRED) pvo->pvo_vaddr |= PVO_WIRED; if (pvo_head != &moea_pvo_kunmanaged) pvo->pvo_vaddr |= PVO_MANAGED; if (bootstrap) pvo->pvo_vaddr |= PVO_BOOTSTRAP; moea_pte_create(&pvo->pvo_pte.pte, sr, va, pa | pte_lo); /* * Add to pmap list */ RB_INSERT(pvo_tree, &pm->pmap_pvo, pvo); /* * Remember if the list was empty and therefore will be the first * item. */ if (LIST_FIRST(pvo_head) == NULL) first = 1; LIST_INSERT_HEAD(pvo_head, pvo, pvo_vlink); if (pvo->pvo_vaddr & PVO_WIRED) pm->pm_stats.wired_count++; pm->pm_stats.resident_count++; i = moea_pte_insert(ptegidx, &pvo->pvo_pte.pte); KASSERT(i < 8, ("Invalid PTE index")); if (i >= 0) { PVO_PTEGIDX_SET(pvo, i); } else { panic("moea_pvo_enter: overflow"); moea_pte_overflow++; } mtx_unlock(&moea_table_mutex); return (first ? ENOENT : 0); } static void moea_pvo_remove(struct pvo_entry *pvo, int pteidx) { struct pte *pt; /* * If there is an active pte entry, we need to deactivate it (and * save the ref & cfg bits). */ pt = moea_pvo_to_pte(pvo, pteidx); if (pt != NULL) { moea_pte_unset(pt, &pvo->pvo_pte.pte, pvo->pvo_vaddr); mtx_unlock(&moea_table_mutex); PVO_PTEGIDX_CLR(pvo); } else { moea_pte_overflow--; } /* * Update our statistics. */ pvo->pvo_pmap->pm_stats.resident_count--; if (pvo->pvo_vaddr & PVO_WIRED) pvo->pvo_pmap->pm_stats.wired_count--; /* * Save the REF/CHG bits into their cache if the page is managed. */ if ((pvo->pvo_vaddr & PVO_MANAGED) == PVO_MANAGED) { struct vm_page *pg; pg = PHYS_TO_VM_PAGE(pvo->pvo_pte.pte.pte_lo & PTE_RPGN); if (pg != NULL) { moea_attr_save(pg, pvo->pvo_pte.pte.pte_lo & (PTE_REF | PTE_CHG)); } } /* * Remove this PVO from the PV and pmap lists. */ LIST_REMOVE(pvo, pvo_vlink); RB_REMOVE(pvo_tree, &pvo->pvo_pmap->pmap_pvo, pvo); /* * Remove this from the overflow list and return it to the pool * if we aren't going to reuse it. */ LIST_REMOVE(pvo, pvo_olink); if (!(pvo->pvo_vaddr & PVO_BOOTSTRAP)) uma_zfree(pvo->pvo_vaddr & PVO_MANAGED ? moea_mpvo_zone : moea_upvo_zone, pvo); moea_pvo_entries--; moea_pvo_remove_calls++; } static __inline int moea_pvo_pte_index(const struct pvo_entry *pvo, int ptegidx) { int pteidx; /* * We can find the actual pte entry without searching by grabbing * the PTEG index from 3 unused bits in pte_lo[11:9] and by * noticing the HID bit. */ pteidx = ptegidx * 8 + PVO_PTEGIDX_GET(pvo); if (pvo->pvo_pte.pte.pte_hi & PTE_HID) pteidx ^= moea_pteg_mask * 8; return (pteidx); } static struct pvo_entry * moea_pvo_find_va(pmap_t pm, vm_offset_t va, int *pteidx_p) { struct pvo_entry *pvo; int ptegidx; u_int sr; va &= ~ADDR_POFF; sr = va_to_sr(pm->pm_sr, va); ptegidx = va_to_pteg(sr, va); mtx_lock(&moea_table_mutex); LIST_FOREACH(pvo, &moea_pvo_table[ptegidx], pvo_olink) { if (pvo->pvo_pmap == pm && PVO_VADDR(pvo) == va) { if (pteidx_p) *pteidx_p = moea_pvo_pte_index(pvo, ptegidx); break; } } mtx_unlock(&moea_table_mutex); return (pvo); } static struct pte * moea_pvo_to_pte(const struct pvo_entry *pvo, int pteidx) { struct pte *pt; /* * If we haven't been supplied the ptegidx, calculate it. */ if (pteidx == -1) { int ptegidx; u_int sr; sr = va_to_sr(pvo->pvo_pmap->pm_sr, pvo->pvo_vaddr); ptegidx = va_to_pteg(sr, pvo->pvo_vaddr); pteidx = moea_pvo_pte_index(pvo, ptegidx); } pt = &moea_pteg_table[pteidx >> 3].pt[pteidx & 7]; mtx_lock(&moea_table_mutex); if ((pvo->pvo_pte.pte.pte_hi & PTE_VALID) && !PVO_PTEGIDX_ISSET(pvo)) { panic("moea_pvo_to_pte: pvo %p has valid pte in pvo but no " "valid pte index", pvo); } if ((pvo->pvo_pte.pte.pte_hi & PTE_VALID) == 0 && PVO_PTEGIDX_ISSET(pvo)) { panic("moea_pvo_to_pte: pvo %p has valid pte index in pvo " "pvo but no valid pte", pvo); } if ((pt->pte_hi ^ (pvo->pvo_pte.pte.pte_hi & ~PTE_VALID)) == PTE_VALID) { if ((pvo->pvo_pte.pte.pte_hi & PTE_VALID) == 0) { panic("moea_pvo_to_pte: pvo %p has valid pte in " "moea_pteg_table %p but invalid in pvo", pvo, pt); } if (((pt->pte_lo ^ pvo->pvo_pte.pte.pte_lo) & ~(PTE_CHG|PTE_REF)) != 0) { panic("moea_pvo_to_pte: pvo %p pte does not match " "pte %p in moea_pteg_table", pvo, pt); } mtx_assert(&moea_table_mutex, MA_OWNED); return (pt); } if (pvo->pvo_pte.pte.pte_hi & PTE_VALID) { panic("moea_pvo_to_pte: pvo %p has invalid pte %p in " "moea_pteg_table but valid in pvo: %8x, %8x", pvo, pt, pvo->pvo_pte.pte.pte_hi, pt->pte_hi); } mtx_unlock(&moea_table_mutex); return (NULL); } /* * XXX: THIS STUFF SHOULD BE IN pte.c? */ int moea_pte_spill(vm_offset_t addr) { struct pvo_entry *source_pvo, *victim_pvo; struct pvo_entry *pvo; int ptegidx, i, j; u_int sr; struct pteg *pteg; struct pte *pt; moea_pte_spills++; sr = mfsrin(addr); ptegidx = va_to_pteg(sr, addr); /* * Have to substitute some entry. Use the primary hash for this. * Use low bits of timebase as random generator. */ pteg = &moea_pteg_table[ptegidx]; mtx_lock(&moea_table_mutex); __asm __volatile("mftb %0" : "=r"(i)); i &= 7; pt = &pteg->pt[i]; source_pvo = NULL; victim_pvo = NULL; LIST_FOREACH(pvo, &moea_pvo_table[ptegidx], pvo_olink) { /* * We need to find a pvo entry for this address. */ if (source_pvo == NULL && moea_pte_match(&pvo->pvo_pte.pte, sr, addr, pvo->pvo_pte.pte.pte_hi & PTE_HID)) { /* * Now found an entry to be spilled into the pteg. * The PTE is now valid, so we know it's active. */ j = moea_pte_insert(ptegidx, &pvo->pvo_pte.pte); if (j >= 0) { PVO_PTEGIDX_SET(pvo, j); moea_pte_overflow--; mtx_unlock(&moea_table_mutex); return (1); } source_pvo = pvo; if (victim_pvo != NULL) break; } /* * We also need the pvo entry of the victim we are replacing * so save the R & C bits of the PTE. */ if ((pt->pte_hi & PTE_HID) == 0 && victim_pvo == NULL && moea_pte_compare(pt, &pvo->pvo_pte.pte)) { victim_pvo = pvo; if (source_pvo != NULL) break; } } if (source_pvo == NULL) { mtx_unlock(&moea_table_mutex); return (0); } if (victim_pvo == NULL) { if ((pt->pte_hi & PTE_HID) == 0) panic("moea_pte_spill: victim p-pte (%p) has no pvo" "entry", pt); /* * If this is a secondary PTE, we need to search it's primary * pvo bucket for the matching PVO. */ LIST_FOREACH(pvo, &moea_pvo_table[ptegidx ^ moea_pteg_mask], pvo_olink) { /* * We also need the pvo entry of the victim we are * replacing so save the R & C bits of the PTE. */ if (moea_pte_compare(pt, &pvo->pvo_pte.pte)) { victim_pvo = pvo; break; } } if (victim_pvo == NULL) panic("moea_pte_spill: victim s-pte (%p) has no pvo" "entry", pt); } /* * We are invalidating the TLB entry for the EA we are replacing even * though it's valid. If we don't, we lose any ref/chg bit changes * contained in the TLB entry. */ source_pvo->pvo_pte.pte.pte_hi &= ~PTE_HID; moea_pte_unset(pt, &victim_pvo->pvo_pte.pte, victim_pvo->pvo_vaddr); moea_pte_set(pt, &source_pvo->pvo_pte.pte); PVO_PTEGIDX_CLR(victim_pvo); PVO_PTEGIDX_SET(source_pvo, i); moea_pte_replacements++; mtx_unlock(&moea_table_mutex); return (1); } static __inline struct pvo_entry * moea_pte_spillable_ident(u_int ptegidx) { struct pte *pt; struct pvo_entry *pvo_walk, *pvo = NULL; LIST_FOREACH(pvo_walk, &moea_pvo_table[ptegidx], pvo_olink) { if (pvo_walk->pvo_vaddr & PVO_WIRED) continue; if (!(pvo_walk->pvo_pte.pte.pte_hi & PTE_VALID)) continue; pt = moea_pvo_to_pte(pvo_walk, -1); if (pt == NULL) continue; pvo = pvo_walk; mtx_unlock(&moea_table_mutex); if (!(pt->pte_lo & PTE_REF)) return (pvo_walk); } return (pvo); } static int moea_pte_insert(u_int ptegidx, struct pte *pvo_pt) { struct pte *pt; struct pvo_entry *victim_pvo; int i; int victim_idx; u_int pteg_bkpidx = ptegidx; mtx_assert(&moea_table_mutex, MA_OWNED); /* * First try primary hash. */ for (pt = moea_pteg_table[ptegidx].pt, i = 0; i < 8; i++, pt++) { if ((pt->pte_hi & PTE_VALID) == 0) { pvo_pt->pte_hi &= ~PTE_HID; moea_pte_set(pt, pvo_pt); return (i); } } /* * Now try secondary hash. */ ptegidx ^= moea_pteg_mask; for (pt = moea_pteg_table[ptegidx].pt, i = 0; i < 8; i++, pt++) { if ((pt->pte_hi & PTE_VALID) == 0) { pvo_pt->pte_hi |= PTE_HID; moea_pte_set(pt, pvo_pt); return (i); } } /* Try again, but this time try to force a PTE out. */ ptegidx = pteg_bkpidx; victim_pvo = moea_pte_spillable_ident(ptegidx); if (victim_pvo == NULL) { ptegidx ^= moea_pteg_mask; victim_pvo = moea_pte_spillable_ident(ptegidx); } if (victim_pvo == NULL) { panic("moea_pte_insert: overflow"); return (-1); } victim_idx = moea_pvo_pte_index(victim_pvo, ptegidx); if (pteg_bkpidx == ptegidx) pvo_pt->pte_hi &= ~PTE_HID; else pvo_pt->pte_hi |= PTE_HID; /* * Synchronize the sacrifice PTE with its PVO, then mark both * invalid. The PVO will be reused when/if the VM system comes * here after a fault. */ pt = &moea_pteg_table[victim_idx >> 3].pt[victim_idx & 7]; if (pt->pte_hi != victim_pvo->pvo_pte.pte.pte_hi) panic("Victim PVO doesn't match PTE! PVO: %8x, PTE: %8x", victim_pvo->pvo_pte.pte.pte_hi, pt->pte_hi); /* * Set the new PTE. */ moea_pte_unset(pt, &victim_pvo->pvo_pte.pte, victim_pvo->pvo_vaddr); PVO_PTEGIDX_CLR(victim_pvo); moea_pte_overflow++; moea_pte_set(pt, pvo_pt); return (victim_idx & 7); } static boolean_t moea_query_bit(vm_page_t m, int ptebit) { struct pvo_entry *pvo; struct pte *pt; rw_assert(&pvh_global_lock, RA_WLOCKED); if (moea_attr_fetch(m) & ptebit) return (TRUE); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { /* * See if we saved the bit off. If so, cache it and return * success. */ if (pvo->pvo_pte.pte.pte_lo & ptebit) { moea_attr_save(m, ptebit); return (TRUE); } } /* * No luck, now go through the hard part of looking at the PTEs * themselves. Sync so that any pending REF/CHG bits are flushed to * the PTEs. */ powerpc_sync(); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { /* * See if this pvo has a valid PTE. if so, fetch the * REF/CHG bits from the valid PTE. If the appropriate * ptebit is set, cache it and return success. */ pt = moea_pvo_to_pte(pvo, -1); if (pt != NULL) { moea_pte_synch(pt, &pvo->pvo_pte.pte); mtx_unlock(&moea_table_mutex); if (pvo->pvo_pte.pte.pte_lo & ptebit) { moea_attr_save(m, ptebit); return (TRUE); } } } return (FALSE); } static u_int moea_clear_bit(vm_page_t m, int ptebit) { u_int count; struct pvo_entry *pvo; struct pte *pt; rw_assert(&pvh_global_lock, RA_WLOCKED); /* * Clear the cached value. */ moea_attr_clear(m, ptebit); /* * Sync so that any pending REF/CHG bits are flushed to the PTEs (so * we can reset the right ones). note that since the pvo entries and * list heads are accessed via BAT0 and are never placed in the page * table, we don't have to worry about further accesses setting the * REF/CHG bits. */ powerpc_sync(); /* * For each pvo entry, clear the pvo's ptebit. If this pvo has a * valid pte clear the ptebit from the valid pte. */ count = 0; LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { pt = moea_pvo_to_pte(pvo, -1); if (pt != NULL) { moea_pte_synch(pt, &pvo->pvo_pte.pte); if (pvo->pvo_pte.pte.pte_lo & ptebit) { count++; moea_pte_clear(pt, PVO_VADDR(pvo), ptebit); } mtx_unlock(&moea_table_mutex); } pvo->pvo_pte.pte.pte_lo &= ~ptebit; } return (count); } /* * Return true if the physical range is encompassed by the battable[idx] */ static int moea_bat_mapped(int idx, vm_paddr_t pa, vm_size_t size) { u_int prot; u_int32_t start; u_int32_t end; u_int32_t bat_ble; /* * Return immediately if not a valid mapping */ if (!(battable[idx].batu & BAT_Vs)) return (EINVAL); /* * The BAT entry must be cache-inhibited, guarded, and r/w * so it can function as an i/o page */ prot = battable[idx].batl & (BAT_I|BAT_G|BAT_PP_RW); if (prot != (BAT_I|BAT_G|BAT_PP_RW)) return (EPERM); /* * The address should be within the BAT range. Assume that the * start address in the BAT has the correct alignment (thus * not requiring masking) */ start = battable[idx].batl & BAT_PBS; bat_ble = (battable[idx].batu & ~(BAT_EBS)) | 0x03; end = start | (bat_ble << 15) | 0x7fff; if ((pa < start) || ((pa + size) > end)) return (ERANGE); return (0); } boolean_t moea_dev_direct_mapped(mmu_t mmu, vm_paddr_t pa, vm_size_t size) { int i; /* * This currently does not work for entries that * overlap 256M BAT segments. */ for(i = 0; i < 16; i++) if (moea_bat_mapped(i, pa, size) == 0) return (0); return (EFAULT); } /* * Map a set of physical memory pages into the kernel virtual * address space. Return a pointer to where it is mapped. This * routine is intended to be used for mapping device memory, * NOT real memory. */ void * moea_mapdev(mmu_t mmu, vm_paddr_t pa, vm_size_t size) { return (moea_mapdev_attr(mmu, pa, size, VM_MEMATTR_DEFAULT)); } void * moea_mapdev_attr(mmu_t mmu, vm_paddr_t pa, vm_size_t size, vm_memattr_t ma) { vm_offset_t va, tmpva, ppa, offset; int i; ppa = trunc_page(pa); offset = pa & PAGE_MASK; size = roundup(offset + size, PAGE_SIZE); /* * If the physical address lies within a valid BAT table entry, * return the 1:1 mapping. This currently doesn't work * for regions that overlap 256M BAT segments. */ for (i = 0; i < 16; i++) { if (moea_bat_mapped(i, pa, size) == 0) return ((void *) pa); } va = kva_alloc(size); if (!va) panic("moea_mapdev: Couldn't alloc kernel virtual memory"); for (tmpva = va; size > 0;) { moea_kenter_attr(mmu, tmpva, ppa, ma); tlbie(tmpva); size -= PAGE_SIZE; tmpva += PAGE_SIZE; ppa += PAGE_SIZE; } return ((void *)(va + offset)); } void moea_unmapdev(mmu_t mmu, vm_offset_t va, vm_size_t size) { vm_offset_t base, offset; /* * If this is outside kernel virtual space, then it's a * battable entry and doesn't require unmapping */ if ((va >= VM_MIN_KERNEL_ADDRESS) && (va <= virtual_end)) { base = trunc_page(va); offset = va & PAGE_MASK; size = roundup(offset + size, PAGE_SIZE); kva_free(base, size); } } static void moea_sync_icache(mmu_t mmu, pmap_t pm, vm_offset_t va, vm_size_t sz) { struct pvo_entry *pvo; vm_offset_t lim; vm_paddr_t pa; vm_size_t len; PMAP_LOCK(pm); while (sz > 0) { lim = round_page(va); len = MIN(lim - va, sz); pvo = moea_pvo_find_va(pm, va & ~ADDR_POFF, NULL); if (pvo != NULL) { pa = (pvo->pvo_pte.pte.pte_lo & PTE_RPGN) | (va & ADDR_POFF); moea_syncicache(pa, len); } va += len; sz -= len; } PMAP_UNLOCK(pm); } void moea_dumpsys_map(mmu_t mmu, vm_paddr_t pa, size_t sz, void **va) { *va = (void *)pa; } extern struct dump_pa dump_map[PHYS_AVAIL_SZ + 1]; void moea_scan_init(mmu_t mmu) { struct pvo_entry *pvo; vm_offset_t va; int i; if (!do_minidump) { /* Initialize phys. segments for dumpsys(). */ memset(&dump_map, 0, sizeof(dump_map)); mem_regions(&pregions, &pregions_sz, ®ions, ®ions_sz); for (i = 0; i < pregions_sz; i++) { dump_map[i].pa_start = pregions[i].mr_start; dump_map[i].pa_size = pregions[i].mr_size; } return; } /* Virtual segments for minidumps: */ memset(&dump_map, 0, sizeof(dump_map)); /* 1st: kernel .data and .bss. */ dump_map[0].pa_start = trunc_page((uintptr_t)_etext); dump_map[0].pa_size = round_page((uintptr_t)_end) - dump_map[0].pa_start; /* 2nd: msgbuf and tables (see pmap_bootstrap()). */ dump_map[1].pa_start = (vm_paddr_t)msgbufp->msg_ptr; dump_map[1].pa_size = round_page(msgbufp->msg_size); /* 3rd: kernel VM. */ va = dump_map[1].pa_start + dump_map[1].pa_size; /* Find start of next chunk (from va). */ while (va < virtual_end) { /* Don't dump the buffer cache. */ if (va >= kmi.buffer_sva && va < kmi.buffer_eva) { va = kmi.buffer_eva; continue; } pvo = moea_pvo_find_va(kernel_pmap, va & ~ADDR_POFF, NULL); if (pvo != NULL && (pvo->pvo_pte.pte.pte_hi & PTE_VALID)) break; va += PAGE_SIZE; } if (va < virtual_end) { dump_map[2].pa_start = va; va += PAGE_SIZE; /* Find last page in chunk. */ while (va < virtual_end) { /* Don't run into the buffer cache. */ if (va == kmi.buffer_sva) break; pvo = moea_pvo_find_va(kernel_pmap, va & ~ADDR_POFF, NULL); if (pvo == NULL || !(pvo->pvo_pte.pte.pte_hi & PTE_VALID)) break; va += PAGE_SIZE; } dump_map[2].pa_size = va - dump_map[2].pa_start; } } Index: head/sys/powerpc/aim/mmu_oea64.c =================================================================== --- head/sys/powerpc/aim/mmu_oea64.c (revision 326260) +++ head/sys/powerpc/aim/mmu_oea64.c (revision 326261) @@ -1,2725 +1,2727 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2008-2015 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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$"); /* * Manages physical address maps. * * Since the information managed by this module is also stored by the * logical address mapping module, this module may throw away valid virtual * to physical mappings at almost any time. However, invalidations of * mappings must be done as requested. * * In order to cope with hardware architectures which make virtual to * physical map invalidates expensive, this module may delay invalidate * reduced protection operations until such time as they are actually * necessary. This module is given full information as to which processors * are currently using which maps, and to when physical maps must be made * correct. */ #include "opt_compat.h" #include "opt_kstack_pages.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mmu_oea64.h" #include "mmu_if.h" #include "moea64_if.h" void moea64_release_vsid(uint64_t vsid); uintptr_t moea64_get_unique_vsid(void); #define DISABLE_TRANS(msr) msr = mfmsr(); mtmsr(msr & ~PSL_DR) #define ENABLE_TRANS(msr) mtmsr(msr) #define VSID_MAKE(sr, hash) ((sr) | (((hash) & 0xfffff) << 4)) #define VSID_TO_HASH(vsid) (((vsid) >> 4) & 0xfffff) #define VSID_HASH_MASK 0x0000007fffffffffULL /* * Locking semantics: * * There are two locks of interest: the page locks and the pmap locks, which * protect their individual PVO lists and are locked in that order. The contents * of all PVO entries are protected by the locks of their respective pmaps. * The pmap of any PVO is guaranteed not to change so long as the PVO is linked * into any list. * */ #define PV_LOCK_COUNT PA_LOCK_COUNT*3 static struct mtx_padalign pv_lock[PV_LOCK_COUNT]; #define PV_LOCKPTR(pa) ((struct mtx *)(&pv_lock[pa_index(pa) % PV_LOCK_COUNT])) #define PV_LOCK(pa) mtx_lock(PV_LOCKPTR(pa)) #define PV_UNLOCK(pa) mtx_unlock(PV_LOCKPTR(pa)) #define PV_LOCKASSERT(pa) mtx_assert(PV_LOCKPTR(pa), MA_OWNED) #define PV_PAGE_LOCK(m) PV_LOCK(VM_PAGE_TO_PHYS(m)) #define PV_PAGE_UNLOCK(m) PV_UNLOCK(VM_PAGE_TO_PHYS(m)) #define PV_PAGE_LOCKASSERT(m) PV_LOCKASSERT(VM_PAGE_TO_PHYS(m)) struct ofw_map { cell_t om_va; cell_t om_len; uint64_t om_pa; cell_t om_mode; }; extern unsigned char _etext[]; extern unsigned char _end[]; /* * Map of physical memory regions. */ static struct mem_region *regions; static struct mem_region *pregions; static u_int phys_avail_count; static int regions_sz, pregions_sz; extern void bs_remap_earlyboot(void); /* * Lock for the SLB tables. */ struct mtx moea64_slb_mutex; /* * PTEG data. */ u_int moea64_pteg_count; u_int moea64_pteg_mask; /* * PVO data. */ uma_zone_t moea64_pvo_zone; /* zone for pvo entries */ static struct pvo_entry *moea64_bpvo_pool; static int moea64_bpvo_pool_index = 0; static int moea64_bpvo_pool_size = 327680; TUNABLE_INT("machdep.moea64_bpvo_pool_size", &moea64_bpvo_pool_size); SYSCTL_INT(_machdep, OID_AUTO, moea64_allocated_bpvo_entries, CTLFLAG_RD, &moea64_bpvo_pool_index, 0, ""); #define VSID_NBPW (sizeof(u_int32_t) * 8) #ifdef __powerpc64__ #define NVSIDS (NPMAPS * 16) #define VSID_HASHMASK 0xffffffffUL #else #define NVSIDS NPMAPS #define VSID_HASHMASK 0xfffffUL #endif static u_int moea64_vsid_bitmap[NVSIDS / VSID_NBPW]; static boolean_t moea64_initialized = FALSE; /* * Statistics. */ u_int moea64_pte_valid = 0; u_int moea64_pte_overflow = 0; u_int moea64_pvo_entries = 0; u_int moea64_pvo_enter_calls = 0; u_int moea64_pvo_remove_calls = 0; SYSCTL_INT(_machdep, OID_AUTO, moea64_pte_valid, CTLFLAG_RD, &moea64_pte_valid, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea64_pte_overflow, CTLFLAG_RD, &moea64_pte_overflow, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea64_pvo_entries, CTLFLAG_RD, &moea64_pvo_entries, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea64_pvo_enter_calls, CTLFLAG_RD, &moea64_pvo_enter_calls, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea64_pvo_remove_calls, CTLFLAG_RD, &moea64_pvo_remove_calls, 0, ""); vm_offset_t moea64_scratchpage_va[2]; struct pvo_entry *moea64_scratchpage_pvo[2]; struct mtx moea64_scratchpage_mtx; uint64_t moea64_large_page_mask = 0; uint64_t moea64_large_page_size = 0; int moea64_large_page_shift = 0; /* * PVO calls. */ static int moea64_pvo_enter(mmu_t mmu, struct pvo_entry *pvo, struct pvo_head *pvo_head); static void moea64_pvo_remove_from_pmap(mmu_t mmu, struct pvo_entry *pvo); static void moea64_pvo_remove_from_page(mmu_t mmu, struct pvo_entry *pvo); static struct pvo_entry *moea64_pvo_find_va(pmap_t, vm_offset_t); /* * Utility routines. */ static boolean_t moea64_query_bit(mmu_t, vm_page_t, uint64_t); static u_int moea64_clear_bit(mmu_t, vm_page_t, uint64_t); static void moea64_kremove(mmu_t, vm_offset_t); static void moea64_syncicache(mmu_t, pmap_t pmap, vm_offset_t va, vm_paddr_t pa, vm_size_t sz); static void moea64_pmap_init_qpages(void); /* * Kernel MMU interface */ void moea64_clear_modify(mmu_t, vm_page_t); void moea64_copy_page(mmu_t, vm_page_t, vm_page_t); void moea64_copy_pages(mmu_t mmu, vm_page_t *ma, vm_offset_t a_offset, vm_page_t *mb, vm_offset_t b_offset, int xfersize); int moea64_enter(mmu_t, pmap_t, vm_offset_t, vm_page_t, vm_prot_t, u_int flags, int8_t psind); void moea64_enter_object(mmu_t, pmap_t, vm_offset_t, vm_offset_t, vm_page_t, vm_prot_t); void moea64_enter_quick(mmu_t, pmap_t, vm_offset_t, vm_page_t, vm_prot_t); vm_paddr_t moea64_extract(mmu_t, pmap_t, vm_offset_t); vm_page_t moea64_extract_and_hold(mmu_t, pmap_t, vm_offset_t, vm_prot_t); void moea64_init(mmu_t); boolean_t moea64_is_modified(mmu_t, vm_page_t); boolean_t moea64_is_prefaultable(mmu_t, pmap_t, vm_offset_t); boolean_t moea64_is_referenced(mmu_t, vm_page_t); int moea64_ts_referenced(mmu_t, vm_page_t); vm_offset_t moea64_map(mmu_t, vm_offset_t *, vm_paddr_t, vm_paddr_t, int); boolean_t moea64_page_exists_quick(mmu_t, pmap_t, vm_page_t); void moea64_page_init(mmu_t, vm_page_t); int moea64_page_wired_mappings(mmu_t, vm_page_t); void moea64_pinit(mmu_t, pmap_t); void moea64_pinit0(mmu_t, pmap_t); void moea64_protect(mmu_t, pmap_t, vm_offset_t, vm_offset_t, vm_prot_t); void moea64_qenter(mmu_t, vm_offset_t, vm_page_t *, int); void moea64_qremove(mmu_t, vm_offset_t, int); void moea64_release(mmu_t, pmap_t); void moea64_remove(mmu_t, pmap_t, vm_offset_t, vm_offset_t); void moea64_remove_pages(mmu_t, pmap_t); void moea64_remove_all(mmu_t, vm_page_t); void moea64_remove_write(mmu_t, vm_page_t); void moea64_unwire(mmu_t, pmap_t, vm_offset_t, vm_offset_t); void moea64_zero_page(mmu_t, vm_page_t); void moea64_zero_page_area(mmu_t, vm_page_t, int, int); void moea64_activate(mmu_t, struct thread *); void moea64_deactivate(mmu_t, struct thread *); void *moea64_mapdev(mmu_t, vm_paddr_t, vm_size_t); void *moea64_mapdev_attr(mmu_t, vm_paddr_t, vm_size_t, vm_memattr_t); void moea64_unmapdev(mmu_t, vm_offset_t, vm_size_t); vm_paddr_t moea64_kextract(mmu_t, vm_offset_t); void moea64_page_set_memattr(mmu_t, vm_page_t m, vm_memattr_t ma); void moea64_kenter_attr(mmu_t, vm_offset_t, vm_paddr_t, vm_memattr_t ma); void moea64_kenter(mmu_t, vm_offset_t, vm_paddr_t); boolean_t moea64_dev_direct_mapped(mmu_t, vm_paddr_t, vm_size_t); static void moea64_sync_icache(mmu_t, pmap_t, vm_offset_t, vm_size_t); void moea64_dumpsys_map(mmu_t mmu, vm_paddr_t pa, size_t sz, void **va); void moea64_scan_init(mmu_t mmu); vm_offset_t moea64_quick_enter_page(mmu_t mmu, vm_page_t m); void moea64_quick_remove_page(mmu_t mmu, vm_offset_t addr); static mmu_method_t moea64_methods[] = { MMUMETHOD(mmu_clear_modify, moea64_clear_modify), MMUMETHOD(mmu_copy_page, moea64_copy_page), MMUMETHOD(mmu_copy_pages, moea64_copy_pages), MMUMETHOD(mmu_enter, moea64_enter), MMUMETHOD(mmu_enter_object, moea64_enter_object), MMUMETHOD(mmu_enter_quick, moea64_enter_quick), MMUMETHOD(mmu_extract, moea64_extract), MMUMETHOD(mmu_extract_and_hold, moea64_extract_and_hold), MMUMETHOD(mmu_init, moea64_init), MMUMETHOD(mmu_is_modified, moea64_is_modified), MMUMETHOD(mmu_is_prefaultable, moea64_is_prefaultable), MMUMETHOD(mmu_is_referenced, moea64_is_referenced), MMUMETHOD(mmu_ts_referenced, moea64_ts_referenced), MMUMETHOD(mmu_map, moea64_map), MMUMETHOD(mmu_page_exists_quick,moea64_page_exists_quick), MMUMETHOD(mmu_page_init, moea64_page_init), MMUMETHOD(mmu_page_wired_mappings,moea64_page_wired_mappings), MMUMETHOD(mmu_pinit, moea64_pinit), MMUMETHOD(mmu_pinit0, moea64_pinit0), MMUMETHOD(mmu_protect, moea64_protect), MMUMETHOD(mmu_qenter, moea64_qenter), MMUMETHOD(mmu_qremove, moea64_qremove), MMUMETHOD(mmu_release, moea64_release), MMUMETHOD(mmu_remove, moea64_remove), MMUMETHOD(mmu_remove_pages, moea64_remove_pages), MMUMETHOD(mmu_remove_all, moea64_remove_all), MMUMETHOD(mmu_remove_write, moea64_remove_write), MMUMETHOD(mmu_sync_icache, moea64_sync_icache), MMUMETHOD(mmu_unwire, moea64_unwire), MMUMETHOD(mmu_zero_page, moea64_zero_page), MMUMETHOD(mmu_zero_page_area, moea64_zero_page_area), MMUMETHOD(mmu_activate, moea64_activate), MMUMETHOD(mmu_deactivate, moea64_deactivate), MMUMETHOD(mmu_page_set_memattr, moea64_page_set_memattr), MMUMETHOD(mmu_quick_enter_page, moea64_quick_enter_page), MMUMETHOD(mmu_quick_remove_page, moea64_quick_remove_page), /* Internal interfaces */ MMUMETHOD(mmu_mapdev, moea64_mapdev), MMUMETHOD(mmu_mapdev_attr, moea64_mapdev_attr), MMUMETHOD(mmu_unmapdev, moea64_unmapdev), MMUMETHOD(mmu_kextract, moea64_kextract), MMUMETHOD(mmu_kenter, moea64_kenter), MMUMETHOD(mmu_kenter_attr, moea64_kenter_attr), MMUMETHOD(mmu_dev_direct_mapped,moea64_dev_direct_mapped), MMUMETHOD(mmu_scan_init, moea64_scan_init), MMUMETHOD(mmu_dumpsys_map, moea64_dumpsys_map), { 0, 0 } }; MMU_DEF(oea64_mmu, "mmu_oea64_base", moea64_methods, 0); static struct pvo_head * vm_page_to_pvoh(vm_page_t m) { mtx_assert(PV_LOCKPTR(VM_PAGE_TO_PHYS(m)), MA_OWNED); return (&m->md.mdpg_pvoh); } static struct pvo_entry * alloc_pvo_entry(int bootstrap) { struct pvo_entry *pvo; if (!moea64_initialized || bootstrap) { if (moea64_bpvo_pool_index >= moea64_bpvo_pool_size) { panic("moea64_enter: bpvo pool exhausted, %d, %d, %zd", moea64_bpvo_pool_index, moea64_bpvo_pool_size, moea64_bpvo_pool_size * sizeof(struct pvo_entry)); } pvo = &moea64_bpvo_pool[ atomic_fetchadd_int(&moea64_bpvo_pool_index, 1)]; bzero(pvo, sizeof(*pvo)); pvo->pvo_vaddr = PVO_BOOTSTRAP; } else { pvo = uma_zalloc(moea64_pvo_zone, M_NOWAIT); bzero(pvo, sizeof(*pvo)); } return (pvo); } static void init_pvo_entry(struct pvo_entry *pvo, pmap_t pmap, vm_offset_t va) { uint64_t vsid; uint64_t hash; int shift; PMAP_LOCK_ASSERT(pmap, MA_OWNED); pvo->pvo_pmap = pmap; va &= ~ADDR_POFF; pvo->pvo_vaddr |= va; vsid = va_to_vsid(pmap, va); pvo->pvo_vpn = (uint64_t)((va & ADDR_PIDX) >> ADDR_PIDX_SHFT) | (vsid << 16); shift = (pvo->pvo_vaddr & PVO_LARGE) ? moea64_large_page_shift : ADDR_PIDX_SHFT; hash = (vsid & VSID_HASH_MASK) ^ (((uint64_t)va & ADDR_PIDX) >> shift); pvo->pvo_pte.slot = (hash & moea64_pteg_mask) << 3; } static void free_pvo_entry(struct pvo_entry *pvo) { if (!(pvo->pvo_vaddr & PVO_BOOTSTRAP)) uma_zfree(moea64_pvo_zone, pvo); } void moea64_pte_from_pvo(const struct pvo_entry *pvo, struct lpte *lpte) { lpte->pte_hi = (pvo->pvo_vpn >> (ADDR_API_SHFT64 - ADDR_PIDX_SHFT)) & LPTE_AVPN_MASK; lpte->pte_hi |= LPTE_VALID; if (pvo->pvo_vaddr & PVO_LARGE) lpte->pte_hi |= LPTE_BIG; if (pvo->pvo_vaddr & PVO_WIRED) lpte->pte_hi |= LPTE_WIRED; if (pvo->pvo_vaddr & PVO_HID) lpte->pte_hi |= LPTE_HID; lpte->pte_lo = pvo->pvo_pte.pa; /* Includes WIMG bits */ if (pvo->pvo_pte.prot & VM_PROT_WRITE) lpte->pte_lo |= LPTE_BW; else lpte->pte_lo |= LPTE_BR; if (!(pvo->pvo_pte.prot & VM_PROT_EXECUTE)) lpte->pte_lo |= LPTE_NOEXEC; } static __inline uint64_t moea64_calc_wimg(vm_paddr_t pa, vm_memattr_t ma) { uint64_t pte_lo; int i; if (ma != VM_MEMATTR_DEFAULT) { switch (ma) { case VM_MEMATTR_UNCACHEABLE: return (LPTE_I | LPTE_G); case VM_MEMATTR_CACHEABLE: return (LPTE_M); case VM_MEMATTR_WRITE_COMBINING: case VM_MEMATTR_WRITE_BACK: case VM_MEMATTR_PREFETCHABLE: return (LPTE_I); case VM_MEMATTR_WRITE_THROUGH: return (LPTE_W | LPTE_M); } } /* * Assume the page is cache inhibited and access is guarded unless * it's in our available memory array. */ pte_lo = LPTE_I | LPTE_G; for (i = 0; i < pregions_sz; i++) { if ((pa >= pregions[i].mr_start) && (pa < (pregions[i].mr_start + pregions[i].mr_size))) { pte_lo &= ~(LPTE_I | LPTE_G); pte_lo |= LPTE_M; break; } } return pte_lo; } /* * Quick sort callout for comparing memory regions. */ static int om_cmp(const void *a, const void *b); static int om_cmp(const void *a, const void *b) { const struct ofw_map *mapa; const struct ofw_map *mapb; mapa = a; mapb = b; if (mapa->om_pa < mapb->om_pa) return (-1); else if (mapa->om_pa > mapb->om_pa) return (1); else return (0); } static void moea64_add_ofw_mappings(mmu_t mmup, phandle_t mmu, size_t sz) { struct ofw_map translations[sz/(4*sizeof(cell_t))]; /*>= 4 cells per */ pcell_t acells, trans_cells[sz/sizeof(cell_t)]; struct pvo_entry *pvo; register_t msr; vm_offset_t off; vm_paddr_t pa_base; int i, j; bzero(translations, sz); OF_getencprop(OF_finddevice("/"), "#address-cells", &acells, sizeof(acells)); if (OF_getencprop(mmu, "translations", trans_cells, sz) == -1) panic("moea64_bootstrap: can't get ofw translations"); CTR0(KTR_PMAP, "moea64_add_ofw_mappings: translations"); sz /= sizeof(cell_t); for (i = 0, j = 0; i < sz; j++) { translations[j].om_va = trans_cells[i++]; translations[j].om_len = trans_cells[i++]; translations[j].om_pa = trans_cells[i++]; if (acells == 2) { translations[j].om_pa <<= 32; translations[j].om_pa |= trans_cells[i++]; } translations[j].om_mode = trans_cells[i++]; } KASSERT(i == sz, ("Translations map has incorrect cell count (%d/%zd)", i, sz)); sz = j; qsort(translations, sz, sizeof (*translations), om_cmp); for (i = 0; i < sz; i++) { pa_base = translations[i].om_pa; #ifndef __powerpc64__ if ((translations[i].om_pa >> 32) != 0) panic("OFW translations above 32-bit boundary!"); #endif if (pa_base % PAGE_SIZE) panic("OFW translation not page-aligned (phys)!"); if (translations[i].om_va % PAGE_SIZE) panic("OFW translation not page-aligned (virt)!"); CTR3(KTR_PMAP, "translation: pa=%#zx va=%#x len=%#x", pa_base, translations[i].om_va, translations[i].om_len); /* Now enter the pages for this mapping */ DISABLE_TRANS(msr); for (off = 0; off < translations[i].om_len; off += PAGE_SIZE) { /* If this address is direct-mapped, skip remapping */ if (hw_direct_map && translations[i].om_va == pa_base && moea64_calc_wimg(pa_base + off, VM_MEMATTR_DEFAULT) == LPTE_M) continue; PMAP_LOCK(kernel_pmap); pvo = moea64_pvo_find_va(kernel_pmap, translations[i].om_va + off); PMAP_UNLOCK(kernel_pmap); if (pvo != NULL) continue; moea64_kenter(mmup, translations[i].om_va + off, pa_base + off); } ENABLE_TRANS(msr); } } #ifdef __powerpc64__ static void moea64_probe_large_page(void) { uint16_t pvr = mfpvr() >> 16; switch (pvr) { case IBM970: case IBM970FX: case IBM970MP: powerpc_sync(); isync(); mtspr(SPR_HID4, mfspr(SPR_HID4) & ~HID4_970_DISABLE_LG_PG); powerpc_sync(); isync(); /* FALLTHROUGH */ default: if (moea64_large_page_size == 0) { moea64_large_page_size = 0x1000000; /* 16 MB */ moea64_large_page_shift = 24; } } moea64_large_page_mask = moea64_large_page_size - 1; } static void moea64_bootstrap_slb_prefault(vm_offset_t va, int large) { struct slb *cache; struct slb entry; uint64_t esid, slbe; uint64_t i; cache = PCPU_GET(slb); esid = va >> ADDR_SR_SHFT; slbe = (esid << SLBE_ESID_SHIFT) | SLBE_VALID; for (i = 0; i < 64; i++) { if (cache[i].slbe == (slbe | i)) return; } entry.slbe = slbe; entry.slbv = KERNEL_VSID(esid) << SLBV_VSID_SHIFT; if (large) entry.slbv |= SLBV_L; slb_insert_kernel(entry.slbe, entry.slbv); } #endif static void moea64_setup_direct_map(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) { struct pvo_entry *pvo; register_t msr; vm_paddr_t pa; vm_offset_t size, off; uint64_t pte_lo; int i; if (moea64_large_page_size == 0) hw_direct_map = 0; DISABLE_TRANS(msr); if (hw_direct_map) { PMAP_LOCK(kernel_pmap); for (i = 0; i < pregions_sz; i++) { for (pa = pregions[i].mr_start; pa < pregions[i].mr_start + pregions[i].mr_size; pa += moea64_large_page_size) { pte_lo = LPTE_M; pvo = alloc_pvo_entry(1 /* bootstrap */); pvo->pvo_vaddr |= PVO_WIRED | PVO_LARGE; init_pvo_entry(pvo, kernel_pmap, pa); /* * Set memory access as guarded if prefetch within * the page could exit the available physmem area. */ if (pa & moea64_large_page_mask) { pa &= moea64_large_page_mask; pte_lo |= LPTE_G; } if (pa + moea64_large_page_size > pregions[i].mr_start + pregions[i].mr_size) pte_lo |= LPTE_G; pvo->pvo_pte.prot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; pvo->pvo_pte.pa = pa | pte_lo; moea64_pvo_enter(mmup, pvo, NULL); } } PMAP_UNLOCK(kernel_pmap); } else { size = moea64_bpvo_pool_size*sizeof(struct pvo_entry); off = (vm_offset_t)(moea64_bpvo_pool); for (pa = off; pa < off + size; pa += PAGE_SIZE) moea64_kenter(mmup, pa, pa); /* * Map certain important things, like ourselves. * * NOTE: We do not map the exception vector space. That code is * used only in real mode, and leaving it unmapped allows us to * catch NULL pointer deferences, instead of making NULL a valid * address. */ for (pa = kernelstart & ~PAGE_MASK; pa < kernelend; pa += PAGE_SIZE) moea64_kenter(mmup, pa, pa); } ENABLE_TRANS(msr); /* * Allow user to override unmapped_buf_allowed for testing. * XXXKIB Only direct map implementation was tested. */ if (!TUNABLE_INT_FETCH("vfs.unmapped_buf_allowed", &unmapped_buf_allowed)) unmapped_buf_allowed = hw_direct_map; } void moea64_early_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) { int i, j; vm_size_t physsz, hwphyssz; #ifndef __powerpc64__ /* We don't have a direct map since there is no BAT */ hw_direct_map = 0; /* Make sure battable is zero, since we have no BAT */ for (i = 0; i < 16; i++) { battable[i].batu = 0; battable[i].batl = 0; } #else moea64_probe_large_page(); /* Use a direct map if we have large page support */ if (moea64_large_page_size > 0) hw_direct_map = 1; else hw_direct_map = 0; #endif /* Get physical memory regions from firmware */ mem_regions(&pregions, &pregions_sz, ®ions, ®ions_sz); CTR0(KTR_PMAP, "moea64_bootstrap: physical memory"); if (sizeof(phys_avail)/sizeof(phys_avail[0]) < regions_sz) panic("moea64_bootstrap: phys_avail too small"); phys_avail_count = 0; physsz = 0; hwphyssz = 0; TUNABLE_ULONG_FETCH("hw.physmem", (u_long *) &hwphyssz); for (i = 0, j = 0; i < regions_sz; i++, j += 2) { CTR3(KTR_PMAP, "region: %#zx - %#zx (%#zx)", regions[i].mr_start, regions[i].mr_start + regions[i].mr_size, regions[i].mr_size); if (hwphyssz != 0 && (physsz + regions[i].mr_size) >= hwphyssz) { if (physsz < hwphyssz) { phys_avail[j] = regions[i].mr_start; phys_avail[j + 1] = regions[i].mr_start + hwphyssz - physsz; physsz = hwphyssz; phys_avail_count++; } break; } phys_avail[j] = regions[i].mr_start; phys_avail[j + 1] = regions[i].mr_start + regions[i].mr_size; phys_avail_count++; physsz += regions[i].mr_size; } /* Check for overlap with the kernel and exception vectors */ for (j = 0; j < 2*phys_avail_count; j+=2) { if (phys_avail[j] < EXC_LAST) phys_avail[j] += EXC_LAST; if (kernelstart >= phys_avail[j] && kernelstart < phys_avail[j+1]) { if (kernelend < phys_avail[j+1]) { phys_avail[2*phys_avail_count] = (kernelend & ~PAGE_MASK) + PAGE_SIZE; phys_avail[2*phys_avail_count + 1] = phys_avail[j+1]; phys_avail_count++; } phys_avail[j+1] = kernelstart & ~PAGE_MASK; } if (kernelend >= phys_avail[j] && kernelend < phys_avail[j+1]) { if (kernelstart > phys_avail[j]) { phys_avail[2*phys_avail_count] = phys_avail[j]; phys_avail[2*phys_avail_count + 1] = kernelstart & ~PAGE_MASK; phys_avail_count++; } phys_avail[j] = (kernelend & ~PAGE_MASK) + PAGE_SIZE; } } physmem = btoc(physsz); #ifdef PTEGCOUNT moea64_pteg_count = PTEGCOUNT; #else moea64_pteg_count = 0x1000; while (moea64_pteg_count < physmem) moea64_pteg_count <<= 1; moea64_pteg_count >>= 1; #endif /* PTEGCOUNT */ } void moea64_mid_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) { int i; /* * Set PTEG mask */ moea64_pteg_mask = moea64_pteg_count - 1; /* * Initialize SLB table lock and page locks */ mtx_init(&moea64_slb_mutex, "SLB table", NULL, MTX_DEF); for (i = 0; i < PV_LOCK_COUNT; i++) mtx_init(&pv_lock[i], "page pv", NULL, MTX_DEF); /* * Initialise the bootstrap pvo pool. */ moea64_bpvo_pool = (struct pvo_entry *)moea64_bootstrap_alloc( moea64_bpvo_pool_size*sizeof(struct pvo_entry), 0); moea64_bpvo_pool_index = 0; /* * Make sure kernel vsid is allocated as well as VSID 0. */ #ifndef __powerpc64__ moea64_vsid_bitmap[(KERNEL_VSIDBITS & (NVSIDS - 1)) / VSID_NBPW] |= 1 << (KERNEL_VSIDBITS % VSID_NBPW); moea64_vsid_bitmap[0] |= 1; #endif /* * Initialize the kernel pmap (which is statically allocated). */ #ifdef __powerpc64__ for (i = 0; i < 64; i++) { pcpup->pc_slb[i].slbv = 0; pcpup->pc_slb[i].slbe = 0; } #else for (i = 0; i < 16; i++) kernel_pmap->pm_sr[i] = EMPTY_SEGMENT + i; #endif kernel_pmap->pmap_phys = kernel_pmap; CPU_FILL(&kernel_pmap->pm_active); RB_INIT(&kernel_pmap->pmap_pvo); PMAP_LOCK_INIT(kernel_pmap); /* * Now map in all the other buffers we allocated earlier */ moea64_setup_direct_map(mmup, kernelstart, kernelend); } void moea64_late_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) { ihandle_t mmui; phandle_t chosen; phandle_t mmu; ssize_t sz; int i; vm_offset_t pa, va; void *dpcpu; /* * Set up the Open Firmware pmap and add its mappings if not in real * mode. */ chosen = OF_finddevice("/chosen"); if (chosen != -1 && OF_getencprop(chosen, "mmu", &mmui, 4) != -1) { mmu = OF_instance_to_package(mmui); if (mmu == -1 || (sz = OF_getproplen(mmu, "translations")) == -1) sz = 0; if (sz > 6144 /* tmpstksz - 2 KB headroom */) panic("moea64_bootstrap: too many ofw translations"); if (sz > 0) moea64_add_ofw_mappings(mmup, mmu, sz); } /* * Calculate the last available physical address. */ Maxmem = 0; for (i = 0; phys_avail[i + 2] != 0; i += 2) Maxmem = max(Maxmem, powerpc_btop(phys_avail[i + 1])); /* * Initialize MMU and remap early physical mappings */ MMU_CPU_BOOTSTRAP(mmup,0); mtmsr(mfmsr() | PSL_DR | PSL_IR); pmap_bootstrapped++; bs_remap_earlyboot(); /* * Set the start and end of kva. */ virtual_avail = VM_MIN_KERNEL_ADDRESS; virtual_end = VM_MAX_SAFE_KERNEL_ADDRESS; /* * Map the entire KVA range into the SLB. We must not fault there. */ #ifdef __powerpc64__ for (va = virtual_avail; va < virtual_end; va += SEGMENT_LENGTH) moea64_bootstrap_slb_prefault(va, 0); #endif /* * Figure out how far we can extend virtual_end into segment 16 * without running into existing mappings. Segment 16 is guaranteed * to contain neither RAM nor devices (at least on Apple hardware), * but will generally contain some OFW mappings we should not * step on. */ #ifndef __powerpc64__ /* KVA is in high memory on PPC64 */ PMAP_LOCK(kernel_pmap); while (virtual_end < VM_MAX_KERNEL_ADDRESS && moea64_pvo_find_va(kernel_pmap, virtual_end+1) == NULL) virtual_end += PAGE_SIZE; PMAP_UNLOCK(kernel_pmap); #endif /* * Allocate a kernel stack with a guard page for thread0 and map it * into the kernel page map. */ pa = moea64_bootstrap_alloc(kstack_pages * PAGE_SIZE, PAGE_SIZE); va = virtual_avail + KSTACK_GUARD_PAGES * PAGE_SIZE; virtual_avail = va + kstack_pages * PAGE_SIZE; CTR2(KTR_PMAP, "moea64_bootstrap: kstack0 at %#x (%#x)", pa, va); thread0.td_kstack = va; thread0.td_kstack_pages = kstack_pages; for (i = 0; i < kstack_pages; i++) { moea64_kenter(mmup, va, pa); pa += PAGE_SIZE; va += PAGE_SIZE; } /* * Allocate virtual address space for the message buffer. */ pa = msgbuf_phys = moea64_bootstrap_alloc(msgbufsize, PAGE_SIZE); msgbufp = (struct msgbuf *)virtual_avail; va = virtual_avail; virtual_avail += round_page(msgbufsize); while (va < virtual_avail) { moea64_kenter(mmup, va, pa); pa += PAGE_SIZE; va += PAGE_SIZE; } /* * Allocate virtual address space for the dynamic percpu area. */ pa = moea64_bootstrap_alloc(DPCPU_SIZE, PAGE_SIZE); dpcpu = (void *)virtual_avail; va = virtual_avail; virtual_avail += DPCPU_SIZE; while (va < virtual_avail) { moea64_kenter(mmup, va, pa); pa += PAGE_SIZE; va += PAGE_SIZE; } dpcpu_init(dpcpu, curcpu); /* * Allocate some things for page zeroing. We put this directly * in the page table and use MOEA64_PTE_REPLACE to avoid any * of the PVO book-keeping or other parts of the VM system * from even knowing that this hack exists. */ if (!hw_direct_map) { mtx_init(&moea64_scratchpage_mtx, "pvo zero page", NULL, MTX_DEF); for (i = 0; i < 2; i++) { moea64_scratchpage_va[i] = (virtual_end+1) - PAGE_SIZE; virtual_end -= PAGE_SIZE; moea64_kenter(mmup, moea64_scratchpage_va[i], 0); PMAP_LOCK(kernel_pmap); moea64_scratchpage_pvo[i] = moea64_pvo_find_va( kernel_pmap, (vm_offset_t)moea64_scratchpage_va[i]); PMAP_UNLOCK(kernel_pmap); } } } static void moea64_pmap_init_qpages(void) { struct pcpu *pc; int i; if (hw_direct_map) return; CPU_FOREACH(i) { pc = pcpu_find(i); pc->pc_qmap_addr = kva_alloc(PAGE_SIZE); if (pc->pc_qmap_addr == 0) panic("pmap_init_qpages: unable to allocate KVA"); PMAP_LOCK(kernel_pmap); pc->pc_qmap_pvo = moea64_pvo_find_va(kernel_pmap, pc->pc_qmap_addr); PMAP_UNLOCK(kernel_pmap); mtx_init(&pc->pc_qmap_lock, "qmap lock", NULL, MTX_DEF); } } SYSINIT(qpages_init, SI_SUB_CPU, SI_ORDER_ANY, moea64_pmap_init_qpages, NULL); /* * Activate a user pmap. This mostly involves setting some non-CPU * state. */ void moea64_activate(mmu_t mmu, struct thread *td) { pmap_t pm; pm = &td->td_proc->p_vmspace->vm_pmap; CPU_SET(PCPU_GET(cpuid), &pm->pm_active); #ifdef __powerpc64__ PCPU_SET(userslb, pm->pm_slb); __asm __volatile("slbmte %0, %1; isync" :: "r"(td->td_pcb->pcb_cpu.aim.usr_vsid), "r"(USER_SLB_SLBE)); #else PCPU_SET(curpmap, pm->pmap_phys); mtsrin(USER_SR << ADDR_SR_SHFT, td->td_pcb->pcb_cpu.aim.usr_vsid); #endif } void moea64_deactivate(mmu_t mmu, struct thread *td) { pmap_t pm; __asm __volatile("isync; slbie %0" :: "r"(USER_ADDR)); pm = &td->td_proc->p_vmspace->vm_pmap; CPU_CLR(PCPU_GET(cpuid), &pm->pm_active); #ifdef __powerpc64__ PCPU_SET(userslb, NULL); #else PCPU_SET(curpmap, NULL); #endif } void moea64_unwire(mmu_t mmu, pmap_t pm, vm_offset_t sva, vm_offset_t eva) { struct pvo_entry key, *pvo; vm_page_t m; int64_t refchg; key.pvo_vaddr = sva; PMAP_LOCK(pm); for (pvo = RB_NFIND(pvo_tree, &pm->pmap_pvo, &key); pvo != NULL && PVO_VADDR(pvo) < eva; pvo = RB_NEXT(pvo_tree, &pm->pmap_pvo, pvo)) { if ((pvo->pvo_vaddr & PVO_WIRED) == 0) panic("moea64_unwire: pvo %p is missing PVO_WIRED", pvo); pvo->pvo_vaddr &= ~PVO_WIRED; refchg = MOEA64_PTE_REPLACE(mmu, pvo, 0 /* No invalidation */); if ((pvo->pvo_vaddr & PVO_MANAGED) && (pvo->pvo_pte.prot & VM_PROT_WRITE)) { if (refchg < 0) refchg = LPTE_CHG; m = PHYS_TO_VM_PAGE(pvo->pvo_pte.pa & LPTE_RPGN); refchg |= atomic_readandclear_32(&m->md.mdpg_attrs); if (refchg & LPTE_CHG) vm_page_dirty(m); if (refchg & LPTE_REF) vm_page_aflag_set(m, PGA_REFERENCED); } pm->pm_stats.wired_count--; } PMAP_UNLOCK(pm); } /* * This goes through and sets the physical address of our * special scratch PTE to the PA we want to zero or copy. Because * of locking issues (this can get called in pvo_enter() by * the UMA allocator), we can't use most other utility functions here */ static __inline void moea64_set_scratchpage_pa(mmu_t mmup, int which, vm_paddr_t pa) { KASSERT(!hw_direct_map, ("Using OEA64 scratchpage with a direct map!")); mtx_assert(&moea64_scratchpage_mtx, MA_OWNED); moea64_scratchpage_pvo[which]->pvo_pte.pa = moea64_calc_wimg(pa, VM_MEMATTR_DEFAULT) | (uint64_t)pa; MOEA64_PTE_REPLACE(mmup, moea64_scratchpage_pvo[which], MOEA64_PTE_INVALIDATE); isync(); } void moea64_copy_page(mmu_t mmu, vm_page_t msrc, vm_page_t mdst) { vm_offset_t dst; vm_offset_t src; dst = VM_PAGE_TO_PHYS(mdst); src = VM_PAGE_TO_PHYS(msrc); if (hw_direct_map) { bcopy((void *)src, (void *)dst, PAGE_SIZE); } else { mtx_lock(&moea64_scratchpage_mtx); moea64_set_scratchpage_pa(mmu, 0, src); moea64_set_scratchpage_pa(mmu, 1, dst); bcopy((void *)moea64_scratchpage_va[0], (void *)moea64_scratchpage_va[1], PAGE_SIZE); mtx_unlock(&moea64_scratchpage_mtx); } } static inline void moea64_copy_pages_dmap(mmu_t mmu, vm_page_t *ma, vm_offset_t a_offset, vm_page_t *mb, vm_offset_t b_offset, int xfersize) { void *a_cp, *b_cp; vm_offset_t a_pg_offset, b_pg_offset; int cnt; while (xfersize > 0) { a_pg_offset = a_offset & PAGE_MASK; cnt = min(xfersize, PAGE_SIZE - a_pg_offset); a_cp = (char *)VM_PAGE_TO_PHYS(ma[a_offset >> PAGE_SHIFT]) + a_pg_offset; b_pg_offset = b_offset & PAGE_MASK; cnt = min(cnt, PAGE_SIZE - b_pg_offset); b_cp = (char *)VM_PAGE_TO_PHYS(mb[b_offset >> PAGE_SHIFT]) + b_pg_offset; bcopy(a_cp, b_cp, cnt); a_offset += cnt; b_offset += cnt; xfersize -= cnt; } } static inline void moea64_copy_pages_nodmap(mmu_t mmu, vm_page_t *ma, vm_offset_t a_offset, vm_page_t *mb, vm_offset_t b_offset, int xfersize) { void *a_cp, *b_cp; vm_offset_t a_pg_offset, b_pg_offset; int cnt; mtx_lock(&moea64_scratchpage_mtx); while (xfersize > 0) { a_pg_offset = a_offset & PAGE_MASK; cnt = min(xfersize, PAGE_SIZE - a_pg_offset); moea64_set_scratchpage_pa(mmu, 0, VM_PAGE_TO_PHYS(ma[a_offset >> PAGE_SHIFT])); a_cp = (char *)moea64_scratchpage_va[0] + a_pg_offset; b_pg_offset = b_offset & PAGE_MASK; cnt = min(cnt, PAGE_SIZE - b_pg_offset); moea64_set_scratchpage_pa(mmu, 1, VM_PAGE_TO_PHYS(mb[b_offset >> PAGE_SHIFT])); b_cp = (char *)moea64_scratchpage_va[1] + b_pg_offset; bcopy(a_cp, b_cp, cnt); a_offset += cnt; b_offset += cnt; xfersize -= cnt; } mtx_unlock(&moea64_scratchpage_mtx); } void moea64_copy_pages(mmu_t mmu, vm_page_t *ma, vm_offset_t a_offset, vm_page_t *mb, vm_offset_t b_offset, int xfersize) { if (hw_direct_map) { moea64_copy_pages_dmap(mmu, ma, a_offset, mb, b_offset, xfersize); } else { moea64_copy_pages_nodmap(mmu, ma, a_offset, mb, b_offset, xfersize); } } void moea64_zero_page_area(mmu_t mmu, vm_page_t m, int off, int size) { vm_paddr_t pa = VM_PAGE_TO_PHYS(m); if (size + off > PAGE_SIZE) panic("moea64_zero_page: size + off > PAGE_SIZE"); if (hw_direct_map) { bzero((caddr_t)pa + off, size); } else { mtx_lock(&moea64_scratchpage_mtx); moea64_set_scratchpage_pa(mmu, 0, pa); bzero((caddr_t)moea64_scratchpage_va[0] + off, size); mtx_unlock(&moea64_scratchpage_mtx); } } /* * Zero a page of physical memory by temporarily mapping it */ void moea64_zero_page(mmu_t mmu, vm_page_t m) { vm_paddr_t pa = VM_PAGE_TO_PHYS(m); vm_offset_t va, off; if (!hw_direct_map) { mtx_lock(&moea64_scratchpage_mtx); moea64_set_scratchpage_pa(mmu, 0, pa); va = moea64_scratchpage_va[0]; } else { va = pa; } for (off = 0; off < PAGE_SIZE; off += cacheline_size) __asm __volatile("dcbz 0,%0" :: "r"(va + off)); if (!hw_direct_map) mtx_unlock(&moea64_scratchpage_mtx); } vm_offset_t moea64_quick_enter_page(mmu_t mmu, vm_page_t m) { struct pvo_entry *pvo; vm_paddr_t pa = VM_PAGE_TO_PHYS(m); if (hw_direct_map) return (pa); /* * MOEA64_PTE_REPLACE does some locking, so we can't just grab * a critical section and access the PCPU data like on i386. * Instead, pin the thread and grab the PCPU lock to prevent * a preempting thread from using the same PCPU data. */ sched_pin(); mtx_assert(PCPU_PTR(qmap_lock), MA_NOTOWNED); pvo = PCPU_GET(qmap_pvo); mtx_lock(PCPU_PTR(qmap_lock)); pvo->pvo_pte.pa = moea64_calc_wimg(pa, pmap_page_get_memattr(m)) | (uint64_t)pa; MOEA64_PTE_REPLACE(mmu, pvo, MOEA64_PTE_INVALIDATE); isync(); return (PCPU_GET(qmap_addr)); } void moea64_quick_remove_page(mmu_t mmu, vm_offset_t addr) { if (hw_direct_map) return; mtx_assert(PCPU_PTR(qmap_lock), MA_OWNED); KASSERT(PCPU_GET(qmap_addr) == addr, ("moea64_quick_remove_page: invalid address")); mtx_unlock(PCPU_PTR(qmap_lock)); sched_unpin(); } /* * Map the given physical page at the specified virtual address in the * target pmap with the protection requested. If specified the page * will be wired down. */ int moea64_enter(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, u_int flags, int8_t psind) { struct pvo_entry *pvo, *oldpvo; struct pvo_head *pvo_head; uint64_t pte_lo; int error; if ((m->oflags & VPO_UNMANAGED) == 0 && !vm_page_xbusied(m)) VM_OBJECT_ASSERT_LOCKED(m->object); pvo = alloc_pvo_entry(0); pvo->pvo_pmap = NULL; /* to be filled in later */ pvo->pvo_pte.prot = prot; pte_lo = moea64_calc_wimg(VM_PAGE_TO_PHYS(m), pmap_page_get_memattr(m)); pvo->pvo_pte.pa = VM_PAGE_TO_PHYS(m) | pte_lo; if ((flags & PMAP_ENTER_WIRED) != 0) pvo->pvo_vaddr |= PVO_WIRED; if ((m->oflags & VPO_UNMANAGED) != 0 || !moea64_initialized) { pvo_head = NULL; } else { pvo_head = &m->md.mdpg_pvoh; pvo->pvo_vaddr |= PVO_MANAGED; } for (;;) { PV_PAGE_LOCK(m); PMAP_LOCK(pmap); if (pvo->pvo_pmap == NULL) init_pvo_entry(pvo, pmap, va); if (prot & VM_PROT_WRITE) if (pmap_bootstrapped && (m->oflags & VPO_UNMANAGED) == 0) vm_page_aflag_set(m, PGA_WRITEABLE); oldpvo = moea64_pvo_find_va(pmap, va); if (oldpvo != NULL) { if (oldpvo->pvo_vaddr == pvo->pvo_vaddr && oldpvo->pvo_pte.pa == pvo->pvo_pte.pa && oldpvo->pvo_pte.prot == prot) { /* Identical mapping already exists */ error = 0; /* If not in page table, reinsert it */ if (MOEA64_PTE_SYNCH(mmu, oldpvo) < 0) { moea64_pte_overflow--; MOEA64_PTE_INSERT(mmu, oldpvo); } /* Then just clean up and go home */ PV_PAGE_UNLOCK(m); PMAP_UNLOCK(pmap); free_pvo_entry(pvo); break; } /* Otherwise, need to kill it first */ KASSERT(oldpvo->pvo_pmap == pmap, ("pmap of old " "mapping does not match new mapping")); moea64_pvo_remove_from_pmap(mmu, oldpvo); } error = moea64_pvo_enter(mmu, pvo, pvo_head); PV_PAGE_UNLOCK(m); PMAP_UNLOCK(pmap); /* Free any dead pages */ if (oldpvo != NULL) { PV_LOCK(oldpvo->pvo_pte.pa & LPTE_RPGN); moea64_pvo_remove_from_page(mmu, oldpvo); PV_UNLOCK(oldpvo->pvo_pte.pa & LPTE_RPGN); free_pvo_entry(oldpvo); } if (error != ENOMEM) break; if ((flags & PMAP_ENTER_NOSLEEP) != 0) return (KERN_RESOURCE_SHORTAGE); VM_OBJECT_ASSERT_UNLOCKED(m->object); VM_WAIT; } /* * Flush the page from the instruction cache if this page is * mapped executable and cacheable. */ if (pmap != kernel_pmap && !(m->aflags & PGA_EXECUTABLE) && (pte_lo & (LPTE_I | LPTE_G | LPTE_NOEXEC)) == 0) { vm_page_aflag_set(m, PGA_EXECUTABLE); moea64_syncicache(mmu, pmap, va, VM_PAGE_TO_PHYS(m), PAGE_SIZE); } return (KERN_SUCCESS); } static void moea64_syncicache(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_paddr_t pa, vm_size_t sz) { /* * This is much trickier than on older systems because * we can't sync the icache on physical addresses directly * without a direct map. Instead we check a couple of cases * where the memory is already mapped in and, failing that, * use the same trick we use for page zeroing to create * a temporary mapping for this physical address. */ if (!pmap_bootstrapped) { /* * If PMAP is not bootstrapped, we are likely to be * in real mode. */ __syncicache((void *)pa, sz); } else if (pmap == kernel_pmap) { __syncicache((void *)va, sz); } else if (hw_direct_map) { __syncicache((void *)pa, sz); } else { /* Use the scratch page to set up a temp mapping */ mtx_lock(&moea64_scratchpage_mtx); moea64_set_scratchpage_pa(mmu, 1, pa & ~ADDR_POFF); __syncicache((void *)(moea64_scratchpage_va[1] + (va & ADDR_POFF)), sz); mtx_unlock(&moea64_scratchpage_mtx); } } /* * Maps a sequence of resident pages belonging to the same object. * The sequence begins with the given page m_start. This page is * mapped at the given virtual address start. Each subsequent page is * mapped at a virtual address that is offset from start by the same * amount as the page is offset from m_start within the object. The * last page in the sequence is the page with the largest offset from * m_start that can be mapped at a virtual address less than the given * virtual address end. Not every virtual page between start and end * is mapped; only those for which a resident page exists with the * corresponding offset from m_start are mapped. */ void moea64_enter_object(mmu_t mmu, pmap_t pm, vm_offset_t start, vm_offset_t end, vm_page_t m_start, vm_prot_t prot) { vm_page_t m; vm_pindex_t diff, psize; VM_OBJECT_ASSERT_LOCKED(m_start->object); psize = atop(end - start); m = m_start; while (m != NULL && (diff = m->pindex - m_start->pindex) < psize) { moea64_enter(mmu, pm, start + ptoa(diff), m, prot & (VM_PROT_READ | VM_PROT_EXECUTE), PMAP_ENTER_NOSLEEP, 0); m = TAILQ_NEXT(m, listq); } } void moea64_enter_quick(mmu_t mmu, pmap_t pm, vm_offset_t va, vm_page_t m, vm_prot_t prot) { moea64_enter(mmu, pm, va, m, prot & (VM_PROT_READ | VM_PROT_EXECUTE), PMAP_ENTER_NOSLEEP, 0); } vm_paddr_t moea64_extract(mmu_t mmu, pmap_t pm, vm_offset_t va) { struct pvo_entry *pvo; vm_paddr_t pa; PMAP_LOCK(pm); pvo = moea64_pvo_find_va(pm, va); if (pvo == NULL) pa = 0; else pa = (pvo->pvo_pte.pa & LPTE_RPGN) | (va - PVO_VADDR(pvo)); PMAP_UNLOCK(pm); return (pa); } /* * Atomically extract and hold the physical page with the given * pmap and virtual address pair if that mapping permits the given * protection. */ vm_page_t moea64_extract_and_hold(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_prot_t prot) { struct pvo_entry *pvo; vm_page_t m; vm_paddr_t pa; m = NULL; pa = 0; PMAP_LOCK(pmap); retry: pvo = moea64_pvo_find_va(pmap, va & ~ADDR_POFF); if (pvo != NULL && (pvo->pvo_pte.prot & prot) == prot) { if (vm_page_pa_tryrelock(pmap, pvo->pvo_pte.pa & LPTE_RPGN, &pa)) goto retry; m = PHYS_TO_VM_PAGE(pvo->pvo_pte.pa & LPTE_RPGN); vm_page_hold(m); } PA_UNLOCK_COND(pa); PMAP_UNLOCK(pmap); return (m); } static mmu_t installed_mmu; static void * moea64_uma_page_alloc(uma_zone_t zone, vm_size_t bytes, uint8_t *flags, int wait) { struct pvo_entry *pvo; vm_offset_t va; vm_page_t m; int needed_lock; /* * This entire routine is a horrible hack to avoid bothering kmem * for new KVA addresses. Because this can get called from inside * kmem allocation routines, calling kmem for a new address here * can lead to multiply locking non-recursive mutexes. */ *flags = UMA_SLAB_PRIV; needed_lock = !PMAP_LOCKED(kernel_pmap); m = vm_page_alloc(NULL, 0, malloc2vm_flags(wait) | VM_ALLOC_WIRED | VM_ALLOC_NOOBJ); if (m == NULL) return (NULL); va = VM_PAGE_TO_PHYS(m); pvo = alloc_pvo_entry(1 /* bootstrap */); pvo->pvo_pte.prot = VM_PROT_READ | VM_PROT_WRITE; pvo->pvo_pte.pa = VM_PAGE_TO_PHYS(m) | LPTE_M; if (needed_lock) PMAP_LOCK(kernel_pmap); init_pvo_entry(pvo, kernel_pmap, va); pvo->pvo_vaddr |= PVO_WIRED; moea64_pvo_enter(installed_mmu, pvo, NULL); if (needed_lock) PMAP_UNLOCK(kernel_pmap); if ((wait & M_ZERO) && (m->flags & PG_ZERO) == 0) bzero((void *)va, PAGE_SIZE); return (void *)va; } extern int elf32_nxstack; void moea64_init(mmu_t mmu) { CTR0(KTR_PMAP, "moea64_init"); moea64_pvo_zone = uma_zcreate("UPVO entry", sizeof (struct pvo_entry), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM | UMA_ZONE_NOFREE); if (!hw_direct_map) { installed_mmu = mmu; uma_zone_set_allocf(moea64_pvo_zone,moea64_uma_page_alloc); } #ifdef COMPAT_FREEBSD32 elf32_nxstack = 1; #endif moea64_initialized = TRUE; } boolean_t moea64_is_referenced(mmu_t mmu, vm_page_t m) { KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea64_is_referenced: page %p is not managed", m)); return (moea64_query_bit(mmu, m, LPTE_REF)); } boolean_t moea64_is_modified(mmu_t mmu, vm_page_t m) { KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea64_is_modified: page %p is not managed", m)); /* * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * concurrently set while the object is locked. Thus, if PGA_WRITEABLE * is clear, no PTEs can have LPTE_CHG set. */ VM_OBJECT_ASSERT_LOCKED(m->object); if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return (FALSE); return (moea64_query_bit(mmu, m, LPTE_CHG)); } boolean_t moea64_is_prefaultable(mmu_t mmu, pmap_t pmap, vm_offset_t va) { struct pvo_entry *pvo; boolean_t rv = TRUE; PMAP_LOCK(pmap); pvo = moea64_pvo_find_va(pmap, va & ~ADDR_POFF); if (pvo != NULL) rv = FALSE; PMAP_UNLOCK(pmap); return (rv); } void moea64_clear_modify(mmu_t mmu, vm_page_t m) { KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea64_clear_modify: page %p is not managed", m)); VM_OBJECT_ASSERT_WLOCKED(m->object); KASSERT(!vm_page_xbusied(m), ("moea64_clear_modify: page %p is exclusive busied", m)); /* * If the page is not PGA_WRITEABLE, then no PTEs can have LPTE_CHG * set. If the object containing the page is locked and the page is * not exclusive busied, then PGA_WRITEABLE cannot be concurrently set. */ if ((m->aflags & PGA_WRITEABLE) == 0) return; moea64_clear_bit(mmu, m, LPTE_CHG); } /* * Clear the write and modified bits in each of the given page's mappings. */ void moea64_remove_write(mmu_t mmu, vm_page_t m) { struct pvo_entry *pvo; int64_t refchg, ret; pmap_t pmap; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea64_remove_write: page %p is not managed", m)); /* * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * set by another thread while the object is locked. Thus, * if PGA_WRITEABLE is clear, no page table entries need updating. */ VM_OBJECT_ASSERT_WLOCKED(m->object); if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return; powerpc_sync(); PV_PAGE_LOCK(m); refchg = 0; LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { pmap = pvo->pvo_pmap; PMAP_LOCK(pmap); if (!(pvo->pvo_vaddr & PVO_DEAD) && (pvo->pvo_pte.prot & VM_PROT_WRITE)) { pvo->pvo_pte.prot &= ~VM_PROT_WRITE; ret = MOEA64_PTE_REPLACE(mmu, pvo, MOEA64_PTE_PROT_UPDATE); if (ret < 0) ret = LPTE_CHG; refchg |= ret; if (pvo->pvo_pmap == kernel_pmap) isync(); } PMAP_UNLOCK(pmap); } if ((refchg | atomic_readandclear_32(&m->md.mdpg_attrs)) & LPTE_CHG) vm_page_dirty(m); vm_page_aflag_clear(m, PGA_WRITEABLE); PV_PAGE_UNLOCK(m); } /* * moea64_ts_referenced: * * Return a count of reference bits for a page, clearing those bits. * It is not necessary for every reference bit to be cleared, but it * is necessary that 0 only be returned when there are truly no * reference bits set. * * XXX: The exact number of bits to check and clear is a matter that * should be tested and standardized at some point in the future for * optimal aging of shared pages. */ int moea64_ts_referenced(mmu_t mmu, vm_page_t m) { KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea64_ts_referenced: page %p is not managed", m)); return (moea64_clear_bit(mmu, m, LPTE_REF)); } /* * Modify the WIMG settings of all mappings for a page. */ void moea64_page_set_memattr(mmu_t mmu, vm_page_t m, vm_memattr_t ma) { struct pvo_entry *pvo; int64_t refchg; pmap_t pmap; uint64_t lo; if ((m->oflags & VPO_UNMANAGED) != 0) { m->md.mdpg_cache_attrs = ma; return; } lo = moea64_calc_wimg(VM_PAGE_TO_PHYS(m), ma); PV_PAGE_LOCK(m); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { pmap = pvo->pvo_pmap; PMAP_LOCK(pmap); if (!(pvo->pvo_vaddr & PVO_DEAD)) { pvo->pvo_pte.pa &= ~LPTE_WIMG; pvo->pvo_pte.pa |= lo; refchg = MOEA64_PTE_REPLACE(mmu, pvo, MOEA64_PTE_INVALIDATE); if (refchg < 0) refchg = (pvo->pvo_pte.prot & VM_PROT_WRITE) ? LPTE_CHG : 0; if ((pvo->pvo_vaddr & PVO_MANAGED) && (pvo->pvo_pte.prot & VM_PROT_WRITE)) { refchg |= atomic_readandclear_32(&m->md.mdpg_attrs); if (refchg & LPTE_CHG) vm_page_dirty(m); if (refchg & LPTE_REF) vm_page_aflag_set(m, PGA_REFERENCED); } if (pvo->pvo_pmap == kernel_pmap) isync(); } PMAP_UNLOCK(pmap); } m->md.mdpg_cache_attrs = ma; PV_PAGE_UNLOCK(m); } /* * Map a wired page into kernel virtual address space. */ void moea64_kenter_attr(mmu_t mmu, vm_offset_t va, vm_paddr_t pa, vm_memattr_t ma) { int error; struct pvo_entry *pvo, *oldpvo; pvo = alloc_pvo_entry(0); pvo->pvo_pte.prot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; pvo->pvo_pte.pa = (pa & ~ADDR_POFF) | moea64_calc_wimg(pa, ma); pvo->pvo_vaddr |= PVO_WIRED; PMAP_LOCK(kernel_pmap); oldpvo = moea64_pvo_find_va(kernel_pmap, va); if (oldpvo != NULL) moea64_pvo_remove_from_pmap(mmu, oldpvo); init_pvo_entry(pvo, kernel_pmap, va); error = moea64_pvo_enter(mmu, pvo, NULL); PMAP_UNLOCK(kernel_pmap); /* Free any dead pages */ if (oldpvo != NULL) { PV_LOCK(oldpvo->pvo_pte.pa & LPTE_RPGN); moea64_pvo_remove_from_page(mmu, oldpvo); PV_UNLOCK(oldpvo->pvo_pte.pa & LPTE_RPGN); free_pvo_entry(oldpvo); } if (error != 0 && error != ENOENT) panic("moea64_kenter: failed to enter va %#zx pa %#zx: %d", va, pa, error); } void moea64_kenter(mmu_t mmu, vm_offset_t va, vm_paddr_t pa) { moea64_kenter_attr(mmu, va, pa, VM_MEMATTR_DEFAULT); } /* * Extract the physical page address associated with the given kernel virtual * address. */ vm_paddr_t moea64_kextract(mmu_t mmu, vm_offset_t va) { struct pvo_entry *pvo; vm_paddr_t pa; /* * Shortcut the direct-mapped case when applicable. We never put * anything but 1:1 mappings below VM_MIN_KERNEL_ADDRESS. */ if (va < VM_MIN_KERNEL_ADDRESS) return (va); PMAP_LOCK(kernel_pmap); pvo = moea64_pvo_find_va(kernel_pmap, va); KASSERT(pvo != NULL, ("moea64_kextract: no addr found for %#" PRIxPTR, va)); pa = (pvo->pvo_pte.pa & LPTE_RPGN) | (va - PVO_VADDR(pvo)); PMAP_UNLOCK(kernel_pmap); return (pa); } /* * Remove a wired page from kernel virtual address space. */ void moea64_kremove(mmu_t mmu, vm_offset_t va) { moea64_remove(mmu, kernel_pmap, va, va + PAGE_SIZE); } /* * Map a range of physical addresses into kernel virtual address space. * * The value passed in *virt is a suggested virtual address for the mapping. * Architectures which can support a direct-mapped physical to virtual region * can return the appropriate address within that region, leaving '*virt' * unchanged. Other architectures should map the pages starting at '*virt' and * update '*virt' with the first usable address after the mapped region. */ vm_offset_t moea64_map(mmu_t mmu, vm_offset_t *virt, vm_paddr_t pa_start, vm_paddr_t pa_end, int prot) { vm_offset_t sva, va; if (hw_direct_map) { /* * Check if every page in the region is covered by the direct * map. The direct map covers all of physical memory. Use * moea64_calc_wimg() as a shortcut to see if the page is in * physical memory as a way to see if the direct map covers it. */ for (va = pa_start; va < pa_end; va += PAGE_SIZE) if (moea64_calc_wimg(va, VM_MEMATTR_DEFAULT) != LPTE_M) break; if (va == pa_end) return (pa_start); } sva = *virt; va = sva; /* XXX respect prot argument */ for (; pa_start < pa_end; pa_start += PAGE_SIZE, va += PAGE_SIZE) moea64_kenter(mmu, va, pa_start); *virt = va; return (sva); } /* * Returns true if the pmap's pv is one of the first * 16 pvs linked to from this page. This count may * be changed upwards or downwards in the future; it * is only necessary that true be returned for a small * subset of pmaps for proper page aging. */ boolean_t moea64_page_exists_quick(mmu_t mmu, pmap_t pmap, vm_page_t m) { int loops; struct pvo_entry *pvo; boolean_t rv; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea64_page_exists_quick: page %p is not managed", m)); loops = 0; rv = FALSE; PV_PAGE_LOCK(m); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { if (!(pvo->pvo_vaddr & PVO_DEAD) && pvo->pvo_pmap == pmap) { rv = TRUE; break; } if (++loops >= 16) break; } PV_PAGE_UNLOCK(m); return (rv); } void moea64_page_init(mmu_t mmu __unused, vm_page_t m) { m->md.mdpg_attrs = 0; m->md.mdpg_cache_attrs = VM_MEMATTR_DEFAULT; LIST_INIT(&m->md.mdpg_pvoh); } /* * Return the number of managed mappings to the given physical page * that are wired. */ int moea64_page_wired_mappings(mmu_t mmu, vm_page_t m) { struct pvo_entry *pvo; int count; count = 0; if ((m->oflags & VPO_UNMANAGED) != 0) return (count); PV_PAGE_LOCK(m); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) if ((pvo->pvo_vaddr & (PVO_DEAD | PVO_WIRED)) == PVO_WIRED) count++; PV_PAGE_UNLOCK(m); return (count); } static uintptr_t moea64_vsidcontext; uintptr_t moea64_get_unique_vsid(void) { u_int entropy; register_t hash; uint32_t mask; int i; entropy = 0; __asm __volatile("mftb %0" : "=r"(entropy)); mtx_lock(&moea64_slb_mutex); for (i = 0; i < NVSIDS; i += VSID_NBPW) { u_int n; /* * Create a new value by mutiplying by a prime and adding in * entropy from the timebase register. This is to make the * VSID more random so that the PT hash function collides * less often. (Note that the prime casues gcc to do shifts * instead of a multiply.) */ moea64_vsidcontext = (moea64_vsidcontext * 0x1105) + entropy; hash = moea64_vsidcontext & (NVSIDS - 1); if (hash == 0) /* 0 is special, avoid it */ continue; n = hash >> 5; mask = 1 << (hash & (VSID_NBPW - 1)); hash = (moea64_vsidcontext & VSID_HASHMASK); if (moea64_vsid_bitmap[n] & mask) { /* collision? */ /* anything free in this bucket? */ if (moea64_vsid_bitmap[n] == 0xffffffff) { entropy = (moea64_vsidcontext >> 20); continue; } i = ffs(~moea64_vsid_bitmap[n]) - 1; mask = 1 << i; hash &= rounddown2(VSID_HASHMASK, VSID_NBPW); hash |= i; } if (hash == VSID_VRMA) /* also special, avoid this too */ continue; KASSERT(!(moea64_vsid_bitmap[n] & mask), ("Allocating in-use VSID %#zx\n", hash)); moea64_vsid_bitmap[n] |= mask; mtx_unlock(&moea64_slb_mutex); return (hash); } mtx_unlock(&moea64_slb_mutex); panic("%s: out of segments",__func__); } #ifdef __powerpc64__ void moea64_pinit(mmu_t mmu, pmap_t pmap) { RB_INIT(&pmap->pmap_pvo); pmap->pm_slb_tree_root = slb_alloc_tree(); pmap->pm_slb = slb_alloc_user_cache(); pmap->pm_slb_len = 0; } #else void moea64_pinit(mmu_t mmu, pmap_t pmap) { int i; uint32_t hash; RB_INIT(&pmap->pmap_pvo); if (pmap_bootstrapped) pmap->pmap_phys = (pmap_t)moea64_kextract(mmu, (vm_offset_t)pmap); else pmap->pmap_phys = pmap; /* * Allocate some segment registers for this pmap. */ hash = moea64_get_unique_vsid(); for (i = 0; i < 16; i++) pmap->pm_sr[i] = VSID_MAKE(i, hash); KASSERT(pmap->pm_sr[0] != 0, ("moea64_pinit: pm_sr[0] = 0")); } #endif /* * Initialize the pmap associated with process 0. */ void moea64_pinit0(mmu_t mmu, pmap_t pm) { PMAP_LOCK_INIT(pm); moea64_pinit(mmu, pm); bzero(&pm->pm_stats, sizeof(pm->pm_stats)); } /* * Set the physical protection on the specified range of this map as requested. */ static void moea64_pvo_protect(mmu_t mmu, pmap_t pm, struct pvo_entry *pvo, vm_prot_t prot) { struct vm_page *pg; vm_prot_t oldprot; int32_t refchg; PMAP_LOCK_ASSERT(pm, MA_OWNED); /* * Change the protection of the page. */ oldprot = pvo->pvo_pte.prot; pvo->pvo_pte.prot = prot; pg = PHYS_TO_VM_PAGE(pvo->pvo_pte.pa & LPTE_RPGN); /* * If the PVO is in the page table, update mapping */ refchg = MOEA64_PTE_REPLACE(mmu, pvo, MOEA64_PTE_PROT_UPDATE); if (refchg < 0) refchg = (oldprot & VM_PROT_WRITE) ? LPTE_CHG : 0; if (pm != kernel_pmap && pg != NULL && !(pg->aflags & PGA_EXECUTABLE) && (pvo->pvo_pte.pa & (LPTE_I | LPTE_G | LPTE_NOEXEC)) == 0) { if ((pg->oflags & VPO_UNMANAGED) == 0) vm_page_aflag_set(pg, PGA_EXECUTABLE); moea64_syncicache(mmu, pm, PVO_VADDR(pvo), pvo->pvo_pte.pa & LPTE_RPGN, PAGE_SIZE); } /* * Update vm about the REF/CHG bits if the page is managed and we have * removed write access. */ if (pg != NULL && (pvo->pvo_vaddr & PVO_MANAGED) && (oldprot & VM_PROT_WRITE)) { refchg |= atomic_readandclear_32(&pg->md.mdpg_attrs); if (refchg & LPTE_CHG) vm_page_dirty(pg); if (refchg & LPTE_REF) vm_page_aflag_set(pg, PGA_REFERENCED); } } void moea64_protect(mmu_t mmu, pmap_t pm, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot) { struct pvo_entry *pvo, *tpvo, key; CTR4(KTR_PMAP, "moea64_protect: pm=%p sva=%#x eva=%#x prot=%#x", pm, sva, eva, prot); KASSERT(pm == &curproc->p_vmspace->vm_pmap || pm == kernel_pmap, ("moea64_protect: non current pmap")); if ((prot & VM_PROT_READ) == VM_PROT_NONE) { moea64_remove(mmu, pm, sva, eva); return; } PMAP_LOCK(pm); key.pvo_vaddr = sva; for (pvo = RB_NFIND(pvo_tree, &pm->pmap_pvo, &key); pvo != NULL && PVO_VADDR(pvo) < eva; pvo = tpvo) { tpvo = RB_NEXT(pvo_tree, &pm->pmap_pvo, pvo); moea64_pvo_protect(mmu, pm, pvo, prot); } PMAP_UNLOCK(pm); } /* * Map a list of wired pages into kernel virtual address space. This is * intended for temporary mappings which do not need page modification or * references recorded. Existing mappings in the region are overwritten. */ void moea64_qenter(mmu_t mmu, vm_offset_t va, vm_page_t *m, int count) { while (count-- > 0) { moea64_kenter(mmu, va, VM_PAGE_TO_PHYS(*m)); va += PAGE_SIZE; m++; } } /* * Remove page mappings from kernel virtual address space. Intended for * temporary mappings entered by moea64_qenter. */ void moea64_qremove(mmu_t mmu, vm_offset_t va, int count) { while (count-- > 0) { moea64_kremove(mmu, va); va += PAGE_SIZE; } } void moea64_release_vsid(uint64_t vsid) { int idx, mask; mtx_lock(&moea64_slb_mutex); idx = vsid & (NVSIDS-1); mask = 1 << (idx % VSID_NBPW); idx /= VSID_NBPW; KASSERT(moea64_vsid_bitmap[idx] & mask, ("Freeing unallocated VSID %#jx", vsid)); moea64_vsid_bitmap[idx] &= ~mask; mtx_unlock(&moea64_slb_mutex); } void moea64_release(mmu_t mmu, pmap_t pmap) { /* * Free segment registers' VSIDs */ #ifdef __powerpc64__ slb_free_tree(pmap); slb_free_user_cache(pmap->pm_slb); #else KASSERT(pmap->pm_sr[0] != 0, ("moea64_release: pm_sr[0] = 0")); moea64_release_vsid(VSID_TO_HASH(pmap->pm_sr[0])); #endif } /* * Remove all pages mapped by the specified pmap */ void moea64_remove_pages(mmu_t mmu, pmap_t pm) { struct pvo_entry *pvo, *tpvo; struct pvo_tree tofree; RB_INIT(&tofree); PMAP_LOCK(pm); RB_FOREACH_SAFE(pvo, pvo_tree, &pm->pmap_pvo, tpvo) { if (pvo->pvo_vaddr & PVO_WIRED) continue; /* * For locking reasons, remove this from the page table and * pmap, but save delinking from the vm_page for a second * pass */ moea64_pvo_remove_from_pmap(mmu, pvo); RB_INSERT(pvo_tree, &tofree, pvo); } PMAP_UNLOCK(pm); RB_FOREACH_SAFE(pvo, pvo_tree, &tofree, tpvo) { PV_LOCK(pvo->pvo_pte.pa & LPTE_RPGN); moea64_pvo_remove_from_page(mmu, pvo); PV_UNLOCK(pvo->pvo_pte.pa & LPTE_RPGN); RB_REMOVE(pvo_tree, &tofree, pvo); free_pvo_entry(pvo); } } /* * Remove the given range of addresses from the specified map. */ void moea64_remove(mmu_t mmu, pmap_t pm, vm_offset_t sva, vm_offset_t eva) { struct pvo_entry *pvo, *tpvo, key; struct pvo_tree tofree; /* * Perform an unsynchronized read. This is, however, safe. */ if (pm->pm_stats.resident_count == 0) return; key.pvo_vaddr = sva; RB_INIT(&tofree); PMAP_LOCK(pm); for (pvo = RB_NFIND(pvo_tree, &pm->pmap_pvo, &key); pvo != NULL && PVO_VADDR(pvo) < eva; pvo = tpvo) { tpvo = RB_NEXT(pvo_tree, &pm->pmap_pvo, pvo); /* * For locking reasons, remove this from the page table and * pmap, but save delinking from the vm_page for a second * pass */ moea64_pvo_remove_from_pmap(mmu, pvo); RB_INSERT(pvo_tree, &tofree, pvo); } PMAP_UNLOCK(pm); RB_FOREACH_SAFE(pvo, pvo_tree, &tofree, tpvo) { PV_LOCK(pvo->pvo_pte.pa & LPTE_RPGN); moea64_pvo_remove_from_page(mmu, pvo); PV_UNLOCK(pvo->pvo_pte.pa & LPTE_RPGN); RB_REMOVE(pvo_tree, &tofree, pvo); free_pvo_entry(pvo); } } /* * Remove physical page from all pmaps in which it resides. moea64_pvo_remove() * will reflect changes in pte's back to the vm_page. */ void moea64_remove_all(mmu_t mmu, vm_page_t m) { struct pvo_entry *pvo, *next_pvo; struct pvo_head freequeue; int wasdead; pmap_t pmap; LIST_INIT(&freequeue); PV_PAGE_LOCK(m); LIST_FOREACH_SAFE(pvo, vm_page_to_pvoh(m), pvo_vlink, next_pvo) { pmap = pvo->pvo_pmap; PMAP_LOCK(pmap); wasdead = (pvo->pvo_vaddr & PVO_DEAD); if (!wasdead) moea64_pvo_remove_from_pmap(mmu, pvo); moea64_pvo_remove_from_page(mmu, pvo); if (!wasdead) LIST_INSERT_HEAD(&freequeue, pvo, pvo_vlink); PMAP_UNLOCK(pmap); } KASSERT(!pmap_page_is_mapped(m), ("Page still has mappings")); KASSERT(!(m->aflags & PGA_WRITEABLE), ("Page still writable")); PV_PAGE_UNLOCK(m); /* Clean up UMA allocations */ LIST_FOREACH_SAFE(pvo, &freequeue, pvo_vlink, next_pvo) free_pvo_entry(pvo); } /* * Allocate a physical page of memory directly from the phys_avail map. * Can only be called from moea64_bootstrap before avail start and end are * calculated. */ vm_offset_t moea64_bootstrap_alloc(vm_size_t size, u_int align) { vm_offset_t s, e; int i, j; size = round_page(size); for (i = 0; phys_avail[i + 1] != 0; i += 2) { if (align != 0) s = roundup2(phys_avail[i], align); else s = phys_avail[i]; e = s + size; if (s < phys_avail[i] || e > phys_avail[i + 1]) continue; if (s + size > platform_real_maxaddr()) continue; if (s == phys_avail[i]) { phys_avail[i] += size; } else if (e == phys_avail[i + 1]) { phys_avail[i + 1] -= size; } else { for (j = phys_avail_count * 2; j > i; j -= 2) { phys_avail[j] = phys_avail[j - 2]; phys_avail[j + 1] = phys_avail[j - 1]; } phys_avail[i + 3] = phys_avail[i + 1]; phys_avail[i + 1] = s; phys_avail[i + 2] = e; phys_avail_count++; } return (s); } panic("moea64_bootstrap_alloc: could not allocate memory"); } static int moea64_pvo_enter(mmu_t mmu, struct pvo_entry *pvo, struct pvo_head *pvo_head) { int first, err; PMAP_LOCK_ASSERT(pvo->pvo_pmap, MA_OWNED); KASSERT(moea64_pvo_find_va(pvo->pvo_pmap, PVO_VADDR(pvo)) == NULL, ("Existing mapping for VA %#jx", (uintmax_t)PVO_VADDR(pvo))); moea64_pvo_enter_calls++; /* * Add to pmap list */ RB_INSERT(pvo_tree, &pvo->pvo_pmap->pmap_pvo, pvo); /* * Remember if the list was empty and therefore will be the first * item. */ if (pvo_head != NULL) { if (LIST_FIRST(pvo_head) == NULL) first = 1; LIST_INSERT_HEAD(pvo_head, pvo, pvo_vlink); } if (pvo->pvo_vaddr & PVO_WIRED) pvo->pvo_pmap->pm_stats.wired_count++; pvo->pvo_pmap->pm_stats.resident_count++; /* * Insert it into the hardware page table */ err = MOEA64_PTE_INSERT(mmu, pvo); if (err != 0) { panic("moea64_pvo_enter: overflow"); } moea64_pvo_entries++; if (pvo->pvo_pmap == kernel_pmap) isync(); #ifdef __powerpc64__ /* * Make sure all our bootstrap mappings are in the SLB as soon * as virtual memory is switched on. */ if (!pmap_bootstrapped) moea64_bootstrap_slb_prefault(PVO_VADDR(pvo), pvo->pvo_vaddr & PVO_LARGE); #endif return (first ? ENOENT : 0); } static void moea64_pvo_remove_from_pmap(mmu_t mmu, struct pvo_entry *pvo) { struct vm_page *pg; int32_t refchg; KASSERT(pvo->pvo_pmap != NULL, ("Trying to remove PVO with no pmap")); PMAP_LOCK_ASSERT(pvo->pvo_pmap, MA_OWNED); KASSERT(!(pvo->pvo_vaddr & PVO_DEAD), ("Trying to remove dead PVO")); /* * If there is an active pte entry, we need to deactivate it */ refchg = MOEA64_PTE_UNSET(mmu, pvo); if (refchg < 0) { /* * If it was evicted from the page table, be pessimistic and * dirty the page. */ if (pvo->pvo_pte.prot & VM_PROT_WRITE) refchg = LPTE_CHG; else refchg = 0; } /* * Update our statistics. */ pvo->pvo_pmap->pm_stats.resident_count--; if (pvo->pvo_vaddr & PVO_WIRED) pvo->pvo_pmap->pm_stats.wired_count--; /* * Remove this PVO from the pmap list. */ RB_REMOVE(pvo_tree, &pvo->pvo_pmap->pmap_pvo, pvo); /* * Mark this for the next sweep */ pvo->pvo_vaddr |= PVO_DEAD; /* Send RC bits to VM */ if ((pvo->pvo_vaddr & PVO_MANAGED) && (pvo->pvo_pte.prot & VM_PROT_WRITE)) { pg = PHYS_TO_VM_PAGE(pvo->pvo_pte.pa & LPTE_RPGN); if (pg != NULL) { refchg |= atomic_readandclear_32(&pg->md.mdpg_attrs); if (refchg & LPTE_CHG) vm_page_dirty(pg); if (refchg & LPTE_REF) vm_page_aflag_set(pg, PGA_REFERENCED); } } } static void moea64_pvo_remove_from_page(mmu_t mmu, struct pvo_entry *pvo) { struct vm_page *pg; KASSERT(pvo->pvo_vaddr & PVO_DEAD, ("Trying to delink live page")); /* Use NULL pmaps as a sentinel for races in page deletion */ if (pvo->pvo_pmap == NULL) return; pvo->pvo_pmap = NULL; /* * Update vm about page writeability/executability if managed */ PV_LOCKASSERT(pvo->pvo_pte.pa & LPTE_RPGN); pg = PHYS_TO_VM_PAGE(pvo->pvo_pte.pa & LPTE_RPGN); if ((pvo->pvo_vaddr & PVO_MANAGED) && pg != NULL) { LIST_REMOVE(pvo, pvo_vlink); if (LIST_EMPTY(vm_page_to_pvoh(pg))) vm_page_aflag_clear(pg, PGA_WRITEABLE | PGA_EXECUTABLE); } moea64_pvo_entries--; moea64_pvo_remove_calls++; } static struct pvo_entry * moea64_pvo_find_va(pmap_t pm, vm_offset_t va) { struct pvo_entry key; PMAP_LOCK_ASSERT(pm, MA_OWNED); key.pvo_vaddr = va & ~ADDR_POFF; return (RB_FIND(pvo_tree, &pm->pmap_pvo, &key)); } static boolean_t moea64_query_bit(mmu_t mmu, vm_page_t m, uint64_t ptebit) { struct pvo_entry *pvo; int64_t ret; boolean_t rv; /* * See if this bit is stored in the page already. */ if (m->md.mdpg_attrs & ptebit) return (TRUE); /* * Examine each PTE. Sync so that any pending REF/CHG bits are * flushed to the PTEs. */ rv = FALSE; powerpc_sync(); PV_PAGE_LOCK(m); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { ret = 0; /* * See if this pvo has a valid PTE. if so, fetch the * REF/CHG bits from the valid PTE. If the appropriate * ptebit is set, return success. */ PMAP_LOCK(pvo->pvo_pmap); if (!(pvo->pvo_vaddr & PVO_DEAD)) ret = MOEA64_PTE_SYNCH(mmu, pvo); PMAP_UNLOCK(pvo->pvo_pmap); if (ret > 0) { atomic_set_32(&m->md.mdpg_attrs, ret & (LPTE_CHG | LPTE_REF)); if (ret & ptebit) { rv = TRUE; break; } } } PV_PAGE_UNLOCK(m); return (rv); } static u_int moea64_clear_bit(mmu_t mmu, vm_page_t m, u_int64_t ptebit) { u_int count; struct pvo_entry *pvo; int64_t ret; /* * Sync so that any pending REF/CHG bits are flushed to the PTEs (so * we can reset the right ones). */ powerpc_sync(); /* * For each pvo entry, clear the pte's ptebit. */ count = 0; PV_PAGE_LOCK(m); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { ret = 0; PMAP_LOCK(pvo->pvo_pmap); if (!(pvo->pvo_vaddr & PVO_DEAD)) ret = MOEA64_PTE_CLEAR(mmu, pvo, ptebit); PMAP_UNLOCK(pvo->pvo_pmap); if (ret > 0 && (ret & ptebit)) count++; } atomic_clear_32(&m->md.mdpg_attrs, ptebit); PV_PAGE_UNLOCK(m); return (count); } boolean_t moea64_dev_direct_mapped(mmu_t mmu, vm_paddr_t pa, vm_size_t size) { struct pvo_entry *pvo, key; vm_offset_t ppa; int error = 0; PMAP_LOCK(kernel_pmap); key.pvo_vaddr = ppa = pa & ~ADDR_POFF; for (pvo = RB_FIND(pvo_tree, &kernel_pmap->pmap_pvo, &key); ppa < pa + size; ppa += PAGE_SIZE, pvo = RB_NEXT(pvo_tree, &kernel_pmap->pmap_pvo, pvo)) { if (pvo == NULL || (pvo->pvo_pte.pa & LPTE_RPGN) != ppa) { error = EFAULT; break; } } PMAP_UNLOCK(kernel_pmap); return (error); } /* * Map a set of physical memory pages into the kernel virtual * address space. Return a pointer to where it is mapped. This * routine is intended to be used for mapping device memory, * NOT real memory. */ void * moea64_mapdev_attr(mmu_t mmu, vm_paddr_t pa, vm_size_t size, vm_memattr_t ma) { vm_offset_t va, tmpva, ppa, offset; ppa = trunc_page(pa); offset = pa & PAGE_MASK; size = roundup2(offset + size, PAGE_SIZE); va = kva_alloc(size); if (!va) panic("moea64_mapdev: Couldn't alloc kernel virtual memory"); for (tmpva = va; size > 0;) { moea64_kenter_attr(mmu, tmpva, ppa, ma); size -= PAGE_SIZE; tmpva += PAGE_SIZE; ppa += PAGE_SIZE; } return ((void *)(va + offset)); } void * moea64_mapdev(mmu_t mmu, vm_paddr_t pa, vm_size_t size) { return moea64_mapdev_attr(mmu, pa, size, VM_MEMATTR_DEFAULT); } void moea64_unmapdev(mmu_t mmu, vm_offset_t va, vm_size_t size) { vm_offset_t base, offset; base = trunc_page(va); offset = va & PAGE_MASK; size = roundup2(offset + size, PAGE_SIZE); kva_free(base, size); } void moea64_sync_icache(mmu_t mmu, pmap_t pm, vm_offset_t va, vm_size_t sz) { struct pvo_entry *pvo; vm_offset_t lim; vm_paddr_t pa; vm_size_t len; PMAP_LOCK(pm); while (sz > 0) { lim = round_page(va); len = MIN(lim - va, sz); pvo = moea64_pvo_find_va(pm, va & ~ADDR_POFF); if (pvo != NULL && !(pvo->pvo_pte.pa & LPTE_I)) { pa = (pvo->pvo_pte.pa & LPTE_RPGN) | (va & ADDR_POFF); moea64_syncicache(mmu, pm, va, pa, len); } va += len; sz -= len; } PMAP_UNLOCK(pm); } void moea64_dumpsys_map(mmu_t mmu, vm_paddr_t pa, size_t sz, void **va) { *va = (void *)pa; } extern struct dump_pa dump_map[PHYS_AVAIL_SZ + 1]; void moea64_scan_init(mmu_t mmu) { struct pvo_entry *pvo; vm_offset_t va; int i; if (!do_minidump) { /* Initialize phys. segments for dumpsys(). */ memset(&dump_map, 0, sizeof(dump_map)); mem_regions(&pregions, &pregions_sz, ®ions, ®ions_sz); for (i = 0; i < pregions_sz; i++) { dump_map[i].pa_start = pregions[i].mr_start; dump_map[i].pa_size = pregions[i].mr_size; } return; } /* Virtual segments for minidumps: */ memset(&dump_map, 0, sizeof(dump_map)); /* 1st: kernel .data and .bss. */ dump_map[0].pa_start = trunc_page((uintptr_t)_etext); dump_map[0].pa_size = round_page((uintptr_t)_end) - dump_map[0].pa_start; /* 2nd: msgbuf and tables (see pmap_bootstrap()). */ dump_map[1].pa_start = (vm_paddr_t)msgbufp->msg_ptr; dump_map[1].pa_size = round_page(msgbufp->msg_size); /* 3rd: kernel VM. */ va = dump_map[1].pa_start + dump_map[1].pa_size; /* Find start of next chunk (from va). */ while (va < virtual_end) { /* Don't dump the buffer cache. */ if (va >= kmi.buffer_sva && va < kmi.buffer_eva) { va = kmi.buffer_eva; continue; } pvo = moea64_pvo_find_va(kernel_pmap, va & ~ADDR_POFF); if (pvo != NULL && !(pvo->pvo_vaddr & PVO_DEAD)) break; va += PAGE_SIZE; } if (va < virtual_end) { dump_map[2].pa_start = va; va += PAGE_SIZE; /* Find last page in chunk. */ while (va < virtual_end) { /* Don't run into the buffer cache. */ if (va == kmi.buffer_sva) break; pvo = moea64_pvo_find_va(kernel_pmap, va & ~ADDR_POFF); if (pvo != NULL && !(pvo->pvo_vaddr & PVO_DEAD)) break; va += PAGE_SIZE; } dump_map[2].pa_size = va - dump_map[2].pa_start; } } Index: head/sys/powerpc/aim/mmu_oea64.h =================================================================== --- head/sys/powerpc/aim/mmu_oea64.h (revision 326260) +++ head/sys/powerpc/aim/mmu_oea64.h (revision 326261) @@ -1,87 +1,89 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2010 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _POWERPC_AIM_MMU_OEA64_H #define _POWERPC_AIM_MMU_OEA64_H #include extern mmu_def_t oea64_mmu; /* * Helper routines */ /* Allocate physical memory for use in moea64_bootstrap. */ vm_offset_t moea64_bootstrap_alloc(vm_size_t, u_int); /* Set an LPTE structure to match the contents of a PVO */ void moea64_pte_from_pvo(const struct pvo_entry *pvo, struct lpte *lpte); /* * Flags */ #define MOEA64_PTE_PROT_UPDATE 1 #define MOEA64_PTE_INVALIDATE 2 /* * Bootstrap subroutines * * An MMU_BOOTSTRAP() implementation looks like this: * moea64_early_bootstrap(); * Allocate Page Table * moea64_mid_bootstrap(); * Add mappings for MMU resources * moea64_late_bootstrap(); */ void moea64_early_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend); void moea64_mid_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend); void moea64_late_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend); /* * Statistics */ extern u_int moea64_pte_valid; extern u_int moea64_pte_overflow; /* * State variables */ extern int moea64_large_page_shift; extern uint64_t moea64_large_page_size; extern u_int moea64_pteg_count; extern u_int moea64_pteg_mask; extern int n_slbs; #endif /* _POWERPC_AIM_MMU_OEA64_H */ Index: head/sys/powerpc/aim/moea64_native.c =================================================================== --- head/sys/powerpc/aim/moea64_native.c (revision 326260) +++ head/sys/powerpc/aim/moea64_native.c (revision 326261) @@ -1,676 +1,678 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND 4-Clause-BSD + * * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Matt Thomas of Allegro Networks, 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /*- * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: pmap.c,v 1.28 2000/03/26 20:42:36 kleink Exp $ */ /*- * Copyright (C) 2001 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH 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$"); /* * Native 64-bit page table operations for running without a hypervisor. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mmu_oea64.h" #include "mmu_if.h" #include "moea64_if.h" #define PTESYNC() __asm __volatile("ptesync"); #define TLBSYNC() __asm __volatile("tlbsync; ptesync"); #define SYNC() __asm __volatile("sync"); #define EIEIO() __asm __volatile("eieio"); #define VSID_HASH_MASK 0x0000007fffffffffULL static __inline void TLBIE(uint64_t vpn) { #ifndef __powerpc64__ register_t vpn_hi, vpn_lo; register_t msr; register_t scratch, intr; #endif static volatile u_int tlbie_lock = 0; vpn <<= ADDR_PIDX_SHFT; vpn &= ~(0xffffULL << 48); /* Hobo spinlock: we need stronger guarantees than mutexes provide */ while (!atomic_cmpset_int(&tlbie_lock, 0, 1)); isync(); /* Flush instruction queue once lock acquired */ #ifdef __powerpc64__ __asm __volatile("tlbie %0" :: "r"(vpn) : "memory"); __asm __volatile("eieio; tlbsync; ptesync" ::: "memory"); #else vpn_hi = (uint32_t)(vpn >> 32); vpn_lo = (uint32_t)vpn; intr = intr_disable(); __asm __volatile("\ mfmsr %0; \ mr %1, %0; \ insrdi %1,%5,1,0; \ mtmsrd %1; isync; \ \ sld %1,%2,%4; \ or %1,%1,%3; \ tlbie %1; \ \ mtmsrd %0; isync; \ eieio; \ tlbsync; \ ptesync;" : "=r"(msr), "=r"(scratch) : "r"(vpn_hi), "r"(vpn_lo), "r"(32), "r"(1) : "memory"); intr_restore(intr); #endif /* No barriers or special ops -- taken care of by ptesync above */ tlbie_lock = 0; } #define DISABLE_TRANS(msr) msr = mfmsr(); mtmsr(msr & ~PSL_DR) #define ENABLE_TRANS(msr) mtmsr(msr) /* * PTEG data. */ static volatile struct lpte *moea64_pteg_table; static struct rwlock moea64_eviction_lock; /* * PTE calls. */ static int moea64_pte_insert_native(mmu_t, struct pvo_entry *); static int64_t moea64_pte_synch_native(mmu_t, struct pvo_entry *); static int64_t moea64_pte_clear_native(mmu_t, struct pvo_entry *, uint64_t); static int64_t moea64_pte_replace_native(mmu_t, struct pvo_entry *, int); static int64_t moea64_pte_unset_native(mmu_t mmu, struct pvo_entry *); /* * Utility routines. */ static void moea64_bootstrap_native(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend); static void moea64_cpu_bootstrap_native(mmu_t, int ap); static void tlbia(void); static mmu_method_t moea64_native_methods[] = { /* Internal interfaces */ MMUMETHOD(mmu_bootstrap, moea64_bootstrap_native), MMUMETHOD(mmu_cpu_bootstrap, moea64_cpu_bootstrap_native), MMUMETHOD(moea64_pte_synch, moea64_pte_synch_native), MMUMETHOD(moea64_pte_clear, moea64_pte_clear_native), MMUMETHOD(moea64_pte_unset, moea64_pte_unset_native), MMUMETHOD(moea64_pte_replace, moea64_pte_replace_native), MMUMETHOD(moea64_pte_insert, moea64_pte_insert_native), { 0, 0 } }; MMU_DEF_INHERIT(oea64_mmu_native, MMU_TYPE_G5, moea64_native_methods, 0, oea64_mmu); static int64_t moea64_pte_synch_native(mmu_t mmu, struct pvo_entry *pvo) { volatile struct lpte *pt = moea64_pteg_table + pvo->pvo_pte.slot; struct lpte properpt; uint64_t ptelo; PMAP_LOCK_ASSERT(pvo->pvo_pmap, MA_OWNED); moea64_pte_from_pvo(pvo, &properpt); rw_rlock(&moea64_eviction_lock); if ((be64toh(pt->pte_hi) & LPTE_AVPN_MASK) != (properpt.pte_hi & LPTE_AVPN_MASK)) { /* Evicted */ rw_runlock(&moea64_eviction_lock); return (-1); } PTESYNC(); ptelo = be64toh(pt->pte_lo); rw_runlock(&moea64_eviction_lock); return (ptelo & (LPTE_REF | LPTE_CHG)); } static int64_t moea64_pte_clear_native(mmu_t mmu, struct pvo_entry *pvo, uint64_t ptebit) { volatile struct lpte *pt = moea64_pteg_table + pvo->pvo_pte.slot; struct lpte properpt; uint64_t ptelo; PMAP_LOCK_ASSERT(pvo->pvo_pmap, MA_OWNED); moea64_pte_from_pvo(pvo, &properpt); rw_rlock(&moea64_eviction_lock); if ((be64toh(pt->pte_hi) & LPTE_AVPN_MASK) != (properpt.pte_hi & LPTE_AVPN_MASK)) { /* Evicted */ rw_runlock(&moea64_eviction_lock); return (-1); } if (ptebit == LPTE_REF) { /* See "Resetting the Reference Bit" in arch manual */ PTESYNC(); /* 2-step here safe: precision is not guaranteed */ ptelo = be64toh(pt->pte_lo); /* One-byte store to avoid touching the C bit */ ((volatile uint8_t *)(&pt->pte_lo))[6] = #if BYTE_ORDER == BIG_ENDIAN ((uint8_t *)(&properpt.pte_lo))[6]; #else ((uint8_t *)(&properpt.pte_lo))[1]; #endif rw_runlock(&moea64_eviction_lock); critical_enter(); TLBIE(pvo->pvo_vpn); critical_exit(); } else { rw_runlock(&moea64_eviction_lock); ptelo = moea64_pte_unset_native(mmu, pvo); moea64_pte_insert_native(mmu, pvo); } return (ptelo & (LPTE_REF | LPTE_CHG)); } static int64_t moea64_pte_unset_native(mmu_t mmu, struct pvo_entry *pvo) { volatile struct lpte *pt = moea64_pteg_table + pvo->pvo_pte.slot; struct lpte properpt; uint64_t ptelo; moea64_pte_from_pvo(pvo, &properpt); rw_rlock(&moea64_eviction_lock); if ((be64toh(pt->pte_hi & LPTE_AVPN_MASK)) != (properpt.pte_hi & LPTE_AVPN_MASK)) { /* Evicted */ moea64_pte_overflow--; rw_runlock(&moea64_eviction_lock); return (-1); } /* * Invalidate the pte, briefly locking it to collect RC bits. No * atomics needed since this is protected against eviction by the lock. */ isync(); critical_enter(); pt->pte_hi = be64toh((pt->pte_hi & ~LPTE_VALID) | LPTE_LOCKED); PTESYNC(); TLBIE(pvo->pvo_vpn); ptelo = be64toh(pt->pte_lo); *((volatile int32_t *)(&pt->pte_hi) + 1) = 0; /* Release lock */ critical_exit(); rw_runlock(&moea64_eviction_lock); /* Keep statistics */ moea64_pte_valid--; return (ptelo & (LPTE_CHG | LPTE_REF)); } static int64_t moea64_pte_replace_native(mmu_t mmu, struct pvo_entry *pvo, int flags) { volatile struct lpte *pt = moea64_pteg_table + pvo->pvo_pte.slot; struct lpte properpt; int64_t ptelo; if (flags == 0) { /* Just some software bits changing. */ moea64_pte_from_pvo(pvo, &properpt); rw_rlock(&moea64_eviction_lock); if ((be64toh(pt->pte_hi) & LPTE_AVPN_MASK) != (properpt.pte_hi & LPTE_AVPN_MASK)) { rw_runlock(&moea64_eviction_lock); return (-1); } pt->pte_hi = htobe64(properpt.pte_hi); ptelo = be64toh(pt->pte_lo); rw_runlock(&moea64_eviction_lock); } else { /* Otherwise, need reinsertion and deletion */ ptelo = moea64_pte_unset_native(mmu, pvo); moea64_pte_insert_native(mmu, pvo); } return (ptelo); } static void moea64_cpu_bootstrap_native(mmu_t mmup, int ap) { int i = 0; #ifdef __powerpc64__ struct slb *slb = PCPU_GET(slb); register_t seg0; #endif /* * Initialize segment registers and MMU */ mtmsr(mfmsr() & ~PSL_DR & ~PSL_IR); /* * Install kernel SLB entries */ #ifdef __powerpc64__ __asm __volatile ("slbia"); __asm __volatile ("slbmfee %0,%1; slbie %0;" : "=r"(seg0) : "r"(0)); for (i = 0; i < n_slbs; i++) { if (!(slb[i].slbe & SLBE_VALID)) continue; __asm __volatile ("slbmte %0, %1" :: "r"(slb[i].slbv), "r"(slb[i].slbe)); } #else for (i = 0; i < 16; i++) mtsrin(i << ADDR_SR_SHFT, kernel_pmap->pm_sr[i]); #endif /* * Install page table */ __asm __volatile ("ptesync; mtsdr1 %0; isync" :: "r"((uintptr_t)moea64_pteg_table | (uintptr_t)(flsl(moea64_pteg_mask >> 11)))); tlbia(); } static void moea64_bootstrap_native(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) { vm_size_t size; vm_offset_t off; vm_paddr_t pa; register_t msr; moea64_early_bootstrap(mmup, kernelstart, kernelend); /* * Allocate PTEG table. */ size = moea64_pteg_count * sizeof(struct lpteg); CTR2(KTR_PMAP, "moea64_bootstrap: %d PTEGs, %d bytes", moea64_pteg_count, size); rw_init(&moea64_eviction_lock, "pte eviction"); /* * We now need to allocate memory. This memory, to be allocated, * has to reside in a page table. The page table we are about to * allocate. We don't have BAT. So drop to data real mode for a minute * as a measure of last resort. We do this a couple times. */ moea64_pteg_table = (struct lpte *)moea64_bootstrap_alloc(size, size); DISABLE_TRANS(msr); bzero(__DEVOLATILE(void *, moea64_pteg_table), moea64_pteg_count * sizeof(struct lpteg)); ENABLE_TRANS(msr); CTR1(KTR_PMAP, "moea64_bootstrap: PTEG table at %p", moea64_pteg_table); moea64_mid_bootstrap(mmup, kernelstart, kernelend); /* * Add a mapping for the page table itself if there is no direct map. */ if (!hw_direct_map) { size = moea64_pteg_count * sizeof(struct lpteg); off = (vm_offset_t)(moea64_pteg_table); DISABLE_TRANS(msr); for (pa = off; pa < off + size; pa += PAGE_SIZE) pmap_kenter(pa, pa); ENABLE_TRANS(msr); } /* Bring up virtual memory */ moea64_late_bootstrap(mmup, kernelstart, kernelend); } static void tlbia(void) { vm_offset_t i; #ifndef __powerpc64__ register_t msr, scratch; #endif i = 0xc00; /* IS = 11 */ switch (mfpvr() >> 16) { case IBM970: case IBM970FX: case IBM970MP: case IBM970GX: case IBMPOWER4: case IBMPOWER4PLUS: case IBMPOWER5: case IBMPOWER5PLUS: i = 0; /* IS not supported */ break; } TLBSYNC(); for (; i < 0x200000; i += 0x00001000) { #ifdef __powerpc64__ __asm __volatile("tlbiel %0" :: "r"(i)); #else __asm __volatile("\ mfmsr %0; \ mr %1, %0; \ insrdi %1,%3,1,0; \ mtmsrd %1; \ isync; \ \ tlbiel %2; \ \ mtmsrd %0; \ isync;" : "=r"(msr), "=r"(scratch) : "r"(i), "r"(1)); #endif } EIEIO(); TLBSYNC(); } static int atomic_pte_lock(volatile struct lpte *pte, uint64_t bitmask, uint64_t *oldhi) { int ret; uint32_t oldhihalf; /* * Note: in principle, if just the locked bit were set here, we * could avoid needing the eviction lock. However, eviction occurs * so rarely that it isn't worth bothering about in practice. */ __asm __volatile ( "1:\tlwarx %1, 0, %3\n\t" /* load old value */ "and. %0,%1,%4\n\t" /* check if any bits set */ "bne 2f\n\t" /* exit if any set */ "stwcx. %5, 0, %3\n\t" /* attempt to store */ "bne- 1b\n\t" /* spin if failed */ "li %0, 1\n\t" /* success - retval = 1 */ "b 3f\n\t" /* we've succeeded */ "2:\n\t" "stwcx. %1, 0, %3\n\t" /* clear reservation (74xx) */ "li %0, 0\n\t" /* failure - retval = 0 */ "3:\n\t" : "=&r" (ret), "=&r"(oldhihalf), "=m" (pte->pte_hi) : "r" ((volatile char *)&pte->pte_hi + 4), "r" ((uint32_t)bitmask), "r" ((uint32_t)LPTE_LOCKED), "m" (pte->pte_hi) : "cr0", "cr1", "cr2", "memory"); *oldhi = (pte->pte_hi & 0xffffffff00000000ULL) | oldhihalf; return (ret); } static uintptr_t moea64_insert_to_pteg_native(struct lpte *pvo_pt, uintptr_t slotbase, uint64_t mask) { volatile struct lpte *pt; uint64_t oldptehi, va; uintptr_t k; int i, j; /* Start at a random slot */ i = mftb() % 8; for (j = 0; j < 8; j++) { k = slotbase + (i + j) % 8; pt = &moea64_pteg_table[k]; /* Invalidate and seize lock only if no bits in mask set */ if (atomic_pte_lock(pt, mask, &oldptehi)) /* Lock obtained */ break; } if (j == 8) return (-1); if (oldptehi & LPTE_VALID) { KASSERT(!(oldptehi & LPTE_WIRED), ("Unmapped wired entry")); /* * Need to invalidate old entry completely: see * "Modifying a Page Table Entry". Need to reconstruct * the virtual address for the outgoing entry to do that. */ if (oldptehi & LPTE_BIG) va = oldptehi >> moea64_large_page_shift; else va = oldptehi >> ADDR_PIDX_SHFT; if (oldptehi & LPTE_HID) va = (((k >> 3) ^ moea64_pteg_mask) ^ va) & VSID_HASH_MASK; else va = ((k >> 3) ^ va) & VSID_HASH_MASK; va |= (oldptehi & LPTE_AVPN_MASK) << (ADDR_API_SHFT64 - ADDR_PIDX_SHFT); PTESYNC(); TLBIE(va); moea64_pte_valid--; moea64_pte_overflow++; } /* * Update the PTE as per "Adding a Page Table Entry". Lock is released * by setting the high doubleworld. */ pt->pte_lo = htobe64(pvo_pt->pte_lo); EIEIO(); pt->pte_hi = htobe64(pvo_pt->pte_hi); PTESYNC(); /* Keep statistics */ moea64_pte_valid++; return (k); } static int moea64_pte_insert_native(mmu_t mmu, struct pvo_entry *pvo) { struct lpte insertpt; uintptr_t slot; /* Initialize PTE */ moea64_pte_from_pvo(pvo, &insertpt); /* Make sure further insertion is locked out during evictions */ rw_rlock(&moea64_eviction_lock); /* * First try primary hash. */ pvo->pvo_pte.slot &= ~7ULL; /* Base slot address */ slot = moea64_insert_to_pteg_native(&insertpt, pvo->pvo_pte.slot, LPTE_VALID | LPTE_WIRED | LPTE_LOCKED); if (slot != -1) { rw_runlock(&moea64_eviction_lock); pvo->pvo_pte.slot = slot; return (0); } /* * Now try secondary hash. */ pvo->pvo_vaddr ^= PVO_HID; insertpt.pte_hi ^= LPTE_HID; pvo->pvo_pte.slot ^= (moea64_pteg_mask << 3); slot = moea64_insert_to_pteg_native(&insertpt, pvo->pvo_pte.slot, LPTE_VALID | LPTE_WIRED | LPTE_LOCKED); if (slot != -1) { rw_runlock(&moea64_eviction_lock); pvo->pvo_pte.slot = slot; return (0); } /* * Out of luck. Find a PTE to sacrifice. */ /* Lock out all insertions for a bit */ if (!rw_try_upgrade(&moea64_eviction_lock)) { rw_runlock(&moea64_eviction_lock); rw_wlock(&moea64_eviction_lock); } slot = moea64_insert_to_pteg_native(&insertpt, pvo->pvo_pte.slot, LPTE_WIRED | LPTE_LOCKED); if (slot != -1) { rw_wunlock(&moea64_eviction_lock); pvo->pvo_pte.slot = slot; return (0); } /* Try other hash table. Now we're getting desperate... */ pvo->pvo_vaddr ^= PVO_HID; insertpt.pte_hi ^= LPTE_HID; pvo->pvo_pte.slot ^= (moea64_pteg_mask << 3); slot = moea64_insert_to_pteg_native(&insertpt, pvo->pvo_pte.slot, LPTE_WIRED | LPTE_LOCKED); if (slot != -1) { rw_wunlock(&moea64_eviction_lock); pvo->pvo_pte.slot = slot; return (0); } /* No freeable slots in either PTEG? We're hosed. */ rw_wunlock(&moea64_eviction_lock); panic("moea64_pte_insert: overflow"); return (-1); } Index: head/sys/powerpc/aim/mp_cpudep.c =================================================================== --- head/sys/powerpc/aim/mp_cpudep.c (revision 326260) +++ head/sys/powerpc/aim/mp_cpudep.c (revision 326261) @@ -1,396 +1,398 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2008 Marcel Moolenaar * 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$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void *ap_pcpu; static register_t bsp_state[8] __aligned(8); static void cpudep_save_config(void *dummy); SYSINIT(cpu_save_config, SI_SUB_CPU, SI_ORDER_ANY, cpudep_save_config, NULL); void cpudep_ap_early_bootstrap(void) { #ifndef __powerpc64__ register_t reg; #endif __asm __volatile("mtsprg 0, %0" :: "r"(ap_pcpu)); powerpc_sync(); switch (mfpvr() >> 16) { case IBM970: case IBM970FX: case IBM970MP: /* Restore HID4 and HID5, which are necessary for the MMU */ #ifdef __powerpc64__ mtspr(SPR_HID4, bsp_state[2]); powerpc_sync(); isync(); mtspr(SPR_HID5, bsp_state[3]); powerpc_sync(); isync(); #else __asm __volatile("ld %0, 16(%2); sync; isync; \ mtspr %1, %0; sync; isync;" : "=r"(reg) : "K"(SPR_HID4), "b"(bsp_state)); __asm __volatile("ld %0, 24(%2); sync; isync; \ mtspr %1, %0; sync; isync;" : "=r"(reg) : "K"(SPR_HID5), "b"(bsp_state)); #endif powerpc_sync(); break; } } uintptr_t cpudep_ap_bootstrap(void) { register_t msr, sp; msr = PSL_KERNSET & ~PSL_EE; mtmsr(msr); pcpup->pc_curthread = pcpup->pc_idlethread; #ifdef __powerpc64__ __asm __volatile("mr 13,%0" :: "r"(pcpup->pc_curthread)); #else __asm __volatile("mr 2,%0" :: "r"(pcpup->pc_curthread)); #endif pcpup->pc_curpcb = pcpup->pc_curthread->td_pcb; sp = pcpup->pc_curpcb->pcb_sp; return (sp); } static register_t mpc74xx_l2_enable(register_t l2cr_config) { register_t ccr, bit; uint16_t vers; vers = mfpvr() >> 16; switch (vers) { case MPC7400: case MPC7410: bit = L2CR_L2IP; break; default: bit = L2CR_L2I; break; } ccr = mfspr(SPR_L2CR); if (ccr & L2CR_L2E) return (ccr); /* Configure L2 cache. */ ccr = l2cr_config & ~L2CR_L2E; mtspr(SPR_L2CR, ccr | L2CR_L2I); do { ccr = mfspr(SPR_L2CR); } while (ccr & bit); powerpc_sync(); mtspr(SPR_L2CR, l2cr_config); powerpc_sync(); return (l2cr_config); } static register_t mpc745x_l3_enable(register_t l3cr_config) { register_t ccr; ccr = mfspr(SPR_L3CR); if (ccr & L3CR_L3E) return (ccr); /* Configure L3 cache. */ ccr = l3cr_config & ~(L3CR_L3E | L3CR_L3I | L3CR_L3PE | L3CR_L3CLKEN); mtspr(SPR_L3CR, ccr); ccr |= 0x4000000; /* Magic, but documented. */ mtspr(SPR_L3CR, ccr); ccr |= L3CR_L3CLKEN; mtspr(SPR_L3CR, ccr); mtspr(SPR_L3CR, ccr | L3CR_L3I); while (mfspr(SPR_L3CR) & L3CR_L3I) ; mtspr(SPR_L3CR, ccr & ~L3CR_L3CLKEN); powerpc_sync(); DELAY(100); mtspr(SPR_L3CR, ccr); powerpc_sync(); DELAY(100); ccr |= L3CR_L3E; mtspr(SPR_L3CR, ccr); powerpc_sync(); return(ccr); } static register_t mpc74xx_l1d_enable(void) { register_t hid; hid = mfspr(SPR_HID0); if (hid & HID0_DCE) return (hid); /* Enable L1 D-cache */ hid |= HID0_DCE; powerpc_sync(); mtspr(SPR_HID0, hid | HID0_DCFI); powerpc_sync(); return (hid); } static register_t mpc74xx_l1i_enable(void) { register_t hid; hid = mfspr(SPR_HID0); if (hid & HID0_ICE) return (hid); /* Enable L1 I-cache */ hid |= HID0_ICE; isync(); mtspr(SPR_HID0, hid | HID0_ICFI); isync(); return (hid); } static void cpudep_save_config(void *dummy) { uint16_t vers; vers = mfpvr() >> 16; switch(vers) { case IBM970: case IBM970FX: case IBM970MP: #ifdef __powerpc64__ bsp_state[0] = mfspr(SPR_HID0); bsp_state[1] = mfspr(SPR_HID1); bsp_state[2] = mfspr(SPR_HID4); bsp_state[3] = mfspr(SPR_HID5); #else __asm __volatile ("mfspr %0,%2; mr %1,%0; srdi %0,%0,32" : "=r" (bsp_state[0]),"=r" (bsp_state[1]) : "K" (SPR_HID0)); __asm __volatile ("mfspr %0,%2; mr %1,%0; srdi %0,%0,32" : "=r" (bsp_state[2]),"=r" (bsp_state[3]) : "K" (SPR_HID1)); __asm __volatile ("mfspr %0,%2; mr %1,%0; srdi %0,%0,32" : "=r" (bsp_state[4]),"=r" (bsp_state[5]) : "K" (SPR_HID4)); __asm __volatile ("mfspr %0,%2; mr %1,%0; srdi %0,%0,32" : "=r" (bsp_state[6]),"=r" (bsp_state[7]) : "K" (SPR_HID5)); #endif powerpc_sync(); break; case IBMCELLBE: #ifdef NOTYET /* Causes problems if in instruction stream on 970 */ if (mfmsr() & PSL_HV) { bsp_state[0] = mfspr(SPR_HID0); bsp_state[1] = mfspr(SPR_HID1); bsp_state[2] = mfspr(SPR_HID4); bsp_state[3] = mfspr(SPR_HID6); bsp_state[4] = mfspr(SPR_CELL_TSCR); } #endif bsp_state[5] = mfspr(SPR_CELL_TSRL); break; case MPC7450: case MPC7455: case MPC7457: /* Only MPC745x CPUs have an L3 cache. */ bsp_state[3] = mfspr(SPR_L3CR); /* Fallthrough */ case MPC7400: case MPC7410: case MPC7447A: case MPC7448: bsp_state[2] = mfspr(SPR_L2CR); bsp_state[1] = mfspr(SPR_HID1); bsp_state[0] = mfspr(SPR_HID0); break; } } void cpudep_ap_setup() { register_t reg; uint16_t vers; vers = mfpvr() >> 16; /* The following is needed for restoring from sleep. */ platform_smp_timebase_sync(0, 1); switch(vers) { case IBM970: case IBM970FX: case IBM970MP: /* Set HIOR to 0 */ __asm __volatile("mtspr 311,%0" :: "r"(0)); powerpc_sync(); /* * The 970 has strange rules about how to update HID registers. * See Table 2-3, 970MP manual * * Note: HID4 and HID5 restored already in * cpudep_ap_early_bootstrap() */ __asm __volatile("mtasr %0; sync" :: "r"(0)); #ifdef __powerpc64__ __asm __volatile(" \ sync; isync; \ mtspr %1, %0; \ mfspr %0, %1; mfspr %0, %1; mfspr %0, %1; \ mfspr %0, %1; mfspr %0, %1; mfspr %0, %1; \ sync; isync" :: "r"(bsp_state[0]), "K"(SPR_HID0)); __asm __volatile("sync; isync; \ mtspr %1, %0; mtspr %1, %0; sync; isync" :: "r"(bsp_state[1]), "K"(SPR_HID1)); #else __asm __volatile(" \ ld %0,0(%2); \ sync; isync; \ mtspr %1, %0; \ mfspr %0, %1; mfspr %0, %1; mfspr %0, %1; \ mfspr %0, %1; mfspr %0, %1; mfspr %0, %1; \ sync; isync" : "=r"(reg) : "K"(SPR_HID0), "b"(bsp_state)); __asm __volatile("ld %0, 8(%2); sync; isync; \ mtspr %1, %0; mtspr %1, %0; sync; isync" : "=r"(reg) : "K"(SPR_HID1), "b"(bsp_state)); #endif powerpc_sync(); break; case IBMCELLBE: #ifdef NOTYET /* Causes problems if in instruction stream on 970 */ if (mfmsr() & PSL_HV) { mtspr(SPR_HID0, bsp_state[0]); mtspr(SPR_HID1, bsp_state[1]); mtspr(SPR_HID4, bsp_state[2]); mtspr(SPR_HID6, bsp_state[3]); mtspr(SPR_CELL_TSCR, bsp_state[4]); } #endif mtspr(SPR_CELL_TSRL, bsp_state[5]); break; case MPC7400: case MPC7410: case MPC7447A: case MPC7448: case MPC7450: case MPC7455: case MPC7457: /* XXX: Program the CPU ID into PIR */ __asm __volatile("mtspr 1023,%0" :: "r"(PCPU_GET(cpuid))); powerpc_sync(); isync(); mtspr(SPR_HID0, bsp_state[0]); isync(); mtspr(SPR_HID1, bsp_state[1]); isync(); /* Now enable the L3 cache. */ switch (vers) { case MPC7450: case MPC7455: case MPC7457: /* Only MPC745x CPUs have an L3 cache. */ reg = mpc745x_l3_enable(bsp_state[3]); default: break; } reg = mpc74xx_l2_enable(bsp_state[2]); reg = mpc74xx_l1d_enable(); reg = mpc74xx_l1i_enable(); break; case IBMPOWER7: case IBMPOWER7PLUS: case IBMPOWER8: case IBMPOWER8E: #ifdef __powerpc64__ if (mfmsr() & PSL_HV) mtspr(SPR_LPCR, mfspr(SPR_LPCR) | LPCR_LPES); #endif break; default: #ifdef __powerpc64__ if (!(mfmsr() & PSL_HV)) /* Rely on HV to have set things up */ break; #endif printf("WARNING: Unknown CPU type. Cache performace may be " "suboptimal.\n"); break; } } Index: head/sys/powerpc/aim/slb.c =================================================================== --- head/sys/powerpc/aim/slb.c (revision 326260) +++ head/sys/powerpc/aim/slb.c (revision 326261) @@ -1,534 +1,536 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2010 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include uintptr_t moea64_get_unique_vsid(void); void moea64_release_vsid(uint64_t vsid); static void slb_zone_init(void *); static uma_zone_t slbt_zone; static uma_zone_t slb_cache_zone; int n_slbs = 64; SYSINIT(slb_zone_init, SI_SUB_KMEM, SI_ORDER_ANY, slb_zone_init, NULL); struct slbtnode { uint16_t ua_alloc; uint8_t ua_level; /* Only 36 bits needed for full 64-bit address space. */ uint64_t ua_base; union { struct slbtnode *ua_child[16]; struct slb slb_entries[16]; } u; }; /* * For a full 64-bit address space, there are 36 bits in play in an * esid, so 8 levels, with the leaf being at level 0. * * |3333|3322|2222|2222|1111|1111|11 | | | esid * |5432|1098|7654|3210|9876|5432|1098|7654|3210| bits * +----+----+----+----+----+----+----+----+----+-------- * | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | level */ #define UAD_ROOT_LEVEL 8 #define UAD_LEAF_LEVEL 0 static inline int esid2idx(uint64_t esid, int level) { int shift; shift = level * 4; return ((esid >> shift) & 0xF); } /* * The ua_base field should have 0 bits after the first 4*(level+1) * bits; i.e. only */ #define uad_baseok(ua) \ (esid2base(ua->ua_base, ua->ua_level) == ua->ua_base) static inline uint64_t esid2base(uint64_t esid, int level) { uint64_t mask; int shift; shift = (level + 1) * 4; mask = ~((1ULL << shift) - 1); return (esid & mask); } /* * Allocate a new leaf node for the specified esid/vmhandle from the * parent node. */ static struct slb * make_new_leaf(uint64_t esid, uint64_t slbv, struct slbtnode *parent) { struct slbtnode *child; struct slb *retval; int idx; idx = esid2idx(esid, parent->ua_level); KASSERT(parent->u.ua_child[idx] == NULL, ("Child already exists!")); /* unlock and M_WAITOK and loop? */ child = uma_zalloc(slbt_zone, M_NOWAIT | M_ZERO); KASSERT(child != NULL, ("unhandled NULL case")); child->ua_level = UAD_LEAF_LEVEL; child->ua_base = esid2base(esid, child->ua_level); idx = esid2idx(esid, child->ua_level); child->u.slb_entries[idx].slbv = slbv; child->u.slb_entries[idx].slbe = (esid << SLBE_ESID_SHIFT) | SLBE_VALID; setbit(&child->ua_alloc, idx); retval = &child->u.slb_entries[idx]; /* * The above stores must be visible before the next one, so * that a lockless searcher always sees a valid path through * the tree. */ powerpc_lwsync(); idx = esid2idx(esid, parent->ua_level); parent->u.ua_child[idx] = child; setbit(&parent->ua_alloc, idx); return (retval); } /* * Allocate a new intermediate node to fit between the parent and * esid. */ static struct slbtnode* make_intermediate(uint64_t esid, struct slbtnode *parent) { struct slbtnode *child, *inter; int idx, level; idx = esid2idx(esid, parent->ua_level); child = parent->u.ua_child[idx]; KASSERT(esid2base(esid, child->ua_level) != child->ua_base, ("No need for an intermediate node?")); /* * Find the level where the existing child and our new esid * meet. It must be lower than parent->ua_level or we would * have chosen a different index in parent. */ level = child->ua_level + 1; while (esid2base(esid, level) != esid2base(child->ua_base, level)) level++; KASSERT(level < parent->ua_level, ("Found splitting level %d for %09jx and %09jx, " "but it's the same as %p's", level, esid, child->ua_base, parent)); /* unlock and M_WAITOK and loop? */ inter = uma_zalloc(slbt_zone, M_NOWAIT | M_ZERO); KASSERT(inter != NULL, ("unhandled NULL case")); /* Set up intermediate node to point to child ... */ inter->ua_level = level; inter->ua_base = esid2base(esid, inter->ua_level); idx = esid2idx(child->ua_base, inter->ua_level); inter->u.ua_child[idx] = child; setbit(&inter->ua_alloc, idx); powerpc_lwsync(); /* Set up parent to point to intermediate node ... */ idx = esid2idx(inter->ua_base, parent->ua_level); parent->u.ua_child[idx] = inter; setbit(&parent->ua_alloc, idx); return (inter); } uint64_t kernel_va_to_slbv(vm_offset_t va) { uint64_t slbv; /* Set kernel VSID to deterministic value */ slbv = (KERNEL_VSID((uintptr_t)va >> ADDR_SR_SHFT)) << SLBV_VSID_SHIFT; /* Figure out if this is a large-page mapping */ if (hw_direct_map && va < VM_MIN_KERNEL_ADDRESS) { /* * XXX: If we have set up a direct map, assumes * all physical memory is mapped with large pages. */ if (mem_valid(va, 0) == 0) slbv |= SLBV_L; } return (slbv); } struct slb * user_va_to_slb_entry(pmap_t pm, vm_offset_t va) { uint64_t esid = va >> ADDR_SR_SHFT; struct slbtnode *ua; int idx; ua = pm->pm_slb_tree_root; for (;;) { KASSERT(uad_baseok(ua), ("uad base %016jx level %d bad!", ua->ua_base, ua->ua_level)); idx = esid2idx(esid, ua->ua_level); /* * This code is specific to ppc64 where a load is * atomic, so no need for atomic_load macro. */ if (ua->ua_level == UAD_LEAF_LEVEL) return ((ua->u.slb_entries[idx].slbe & SLBE_VALID) ? &ua->u.slb_entries[idx] : NULL); /* * The following accesses are implicitly ordered under the POWER * ISA by load dependencies (the store ordering is provided by * the powerpc_lwsync() calls elsewhere) and so are run without * barriers. */ ua = ua->u.ua_child[idx]; if (ua == NULL || esid2base(esid, ua->ua_level) != ua->ua_base) return (NULL); } return (NULL); } uint64_t va_to_vsid(pmap_t pm, vm_offset_t va) { struct slb *entry; /* Shortcut kernel case */ if (pm == kernel_pmap) return (KERNEL_VSID((uintptr_t)va >> ADDR_SR_SHFT)); /* * If there is no vsid for this VA, we need to add a new entry * to the PMAP's segment table. */ entry = user_va_to_slb_entry(pm, va); if (entry == NULL) return (allocate_user_vsid(pm, (uintptr_t)va >> ADDR_SR_SHFT, 0)); return ((entry->slbv & SLBV_VSID_MASK) >> SLBV_VSID_SHIFT); } uint64_t allocate_user_vsid(pmap_t pm, uint64_t esid, int large) { uint64_t vsid, slbv; struct slbtnode *ua, *next, *inter; struct slb *slb; int idx; KASSERT(pm != kernel_pmap, ("Attempting to allocate a kernel VSID")); PMAP_LOCK_ASSERT(pm, MA_OWNED); vsid = moea64_get_unique_vsid(); slbv = vsid << SLBV_VSID_SHIFT; if (large) slbv |= SLBV_L; ua = pm->pm_slb_tree_root; /* Descend to the correct leaf or NULL pointer. */ for (;;) { KASSERT(uad_baseok(ua), ("uad base %09jx level %d bad!", ua->ua_base, ua->ua_level)); idx = esid2idx(esid, ua->ua_level); if (ua->ua_level == UAD_LEAF_LEVEL) { ua->u.slb_entries[idx].slbv = slbv; eieio(); ua->u.slb_entries[idx].slbe = (esid << SLBE_ESID_SHIFT) | SLBE_VALID; setbit(&ua->ua_alloc, idx); slb = &ua->u.slb_entries[idx]; break; } next = ua->u.ua_child[idx]; if (next == NULL) { slb = make_new_leaf(esid, slbv, ua); break; } /* * Check if the next item down has an okay ua_base. * If not, we need to allocate an intermediate node. */ if (esid2base(esid, next->ua_level) != next->ua_base) { inter = make_intermediate(esid, ua); slb = make_new_leaf(esid, slbv, inter); break; } ua = next; } /* * Someone probably wants this soon, and it may be a wired * SLB mapping, so pre-spill this entry. */ eieio(); slb_insert_user(pm, slb); return (vsid); } void free_vsid(pmap_t pm, uint64_t esid, int large) { struct slbtnode *ua; int idx; PMAP_LOCK_ASSERT(pm, MA_OWNED); ua = pm->pm_slb_tree_root; /* Descend to the correct leaf. */ for (;;) { KASSERT(uad_baseok(ua), ("uad base %09jx level %d bad!", ua->ua_base, ua->ua_level)); idx = esid2idx(esid, ua->ua_level); if (ua->ua_level == UAD_LEAF_LEVEL) { ua->u.slb_entries[idx].slbv = 0; eieio(); ua->u.slb_entries[idx].slbe = 0; clrbit(&ua->ua_alloc, idx); return; } ua = ua->u.ua_child[idx]; if (ua == NULL || esid2base(esid, ua->ua_level) != ua->ua_base) { /* Perhaps just return instead of assert? */ KASSERT(0, ("Asked to remove an entry that was never inserted!")); return; } } } static void free_slb_tree_node(struct slbtnode *ua) { int idx; for (idx = 0; idx < 16; idx++) { if (ua->ua_level != UAD_LEAF_LEVEL) { if (ua->u.ua_child[idx] != NULL) free_slb_tree_node(ua->u.ua_child[idx]); } else { if (ua->u.slb_entries[idx].slbv != 0) moea64_release_vsid(ua->u.slb_entries[idx].slbv >> SLBV_VSID_SHIFT); } } uma_zfree(slbt_zone, ua); } void slb_free_tree(pmap_t pm) { free_slb_tree_node(pm->pm_slb_tree_root); } struct slbtnode * slb_alloc_tree(void) { struct slbtnode *root; root = uma_zalloc(slbt_zone, M_NOWAIT | M_ZERO); root->ua_level = UAD_ROOT_LEVEL; return (root); } /* Lock entries mapping kernel text and stacks */ void slb_insert_kernel(uint64_t slbe, uint64_t slbv) { struct slb *slbcache; int i; /* We don't want to be preempted while modifying the kernel map */ critical_enter(); slbcache = PCPU_GET(slb); /* Check for an unused slot, abusing the user slot as a full flag */ if (slbcache[USER_SLB_SLOT].slbe == 0) { for (i = 0; i < n_slbs; i++) { if (i == USER_SLB_SLOT) continue; if (!(slbcache[i].slbe & SLBE_VALID)) goto fillkernslb; } if (i == n_slbs) slbcache[USER_SLB_SLOT].slbe = 1; } i = mftb() % n_slbs; if (i == USER_SLB_SLOT) i = (i+1) % n_slbs; fillkernslb: KASSERT(i != USER_SLB_SLOT, ("Filling user SLB slot with a kernel mapping")); slbcache[i].slbv = slbv; slbcache[i].slbe = slbe | (uint64_t)i; /* If it is for this CPU, put it in the SLB right away */ if (pmap_bootstrapped) { /* slbie not required */ __asm __volatile ("slbmte %0, %1" :: "r"(slbcache[i].slbv), "r"(slbcache[i].slbe)); } critical_exit(); } void slb_insert_user(pmap_t pm, struct slb *slb) { int i; PMAP_LOCK_ASSERT(pm, MA_OWNED); if (pm->pm_slb_len < n_slbs) { i = pm->pm_slb_len; pm->pm_slb_len++; } else { i = mftb() % n_slbs; } /* Note that this replacement is atomic with respect to trap_subr */ pm->pm_slb[i] = slb; } static void * slb_uma_real_alloc(uma_zone_t zone, vm_size_t bytes, u_int8_t *flags, int wait) { static vm_offset_t realmax = 0; void *va; vm_page_t m; if (realmax == 0) realmax = platform_real_maxaddr(); *flags = UMA_SLAB_PRIV; m = vm_page_alloc_contig(NULL, 0, malloc2vm_flags(wait) | VM_ALLOC_NOOBJ | VM_ALLOC_WIRED, 1, 0, realmax, PAGE_SIZE, PAGE_SIZE, VM_MEMATTR_DEFAULT); if (m == NULL) return (NULL); va = (void *) VM_PAGE_TO_PHYS(m); if (!hw_direct_map) pmap_kenter((vm_offset_t)va, VM_PAGE_TO_PHYS(m)); if ((wait & M_ZERO) && (m->flags & PG_ZERO) == 0) bzero(va, PAGE_SIZE); return (va); } static void slb_zone_init(void *dummy) { slbt_zone = uma_zcreate("SLB tree node", sizeof(struct slbtnode), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM); slb_cache_zone = uma_zcreate("SLB cache", (n_slbs + 1)*sizeof(struct slb *), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM); if (platform_real_maxaddr() != VM_MAX_ADDRESS) { uma_zone_set_allocf(slb_cache_zone, slb_uma_real_alloc); uma_zone_set_allocf(slbt_zone, slb_uma_real_alloc); } } struct slb ** slb_alloc_user_cache(void) { return (uma_zalloc(slb_cache_zone, M_ZERO)); } void slb_free_user_cache(struct slb **slb) { uma_zfree(slb_cache_zone, slb); } Index: head/sys/powerpc/booke/machdep_e500.c =================================================================== --- head/sys/powerpc/booke/machdep_e500.c (revision 326260) +++ head/sys/powerpc/booke/machdep_e500.c (revision 326261) @@ -1,125 +1,127 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2011-2012 Semihalf. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include extern void dcache_enable(void); extern void dcache_inval(void); extern void icache_enable(void); extern void icache_inval(void); extern void l2cache_enable(void); extern void l2cache_inval(void); extern void bpred_enable(void); void booke_init_tlb(vm_paddr_t fdt_immr_pa) { } void booke_enable_l1_cache(void) { uint32_t csr; /* Enable D-cache if applicable */ csr = mfspr(SPR_L1CSR0); if ((csr & L1CSR0_DCE) == 0) { dcache_inval(); dcache_enable(); } csr = mfspr(SPR_L1CSR0); if ((boothowto & RB_VERBOSE) != 0 || (csr & L1CSR0_DCE) == 0) printf("L1 D-cache %sabled\n", (csr & L1CSR0_DCE) ? "en" : "dis"); /* Enable L1 I-cache if applicable. */ csr = mfspr(SPR_L1CSR1); if ((csr & L1CSR1_ICE) == 0) { icache_inval(); icache_enable(); } csr = mfspr(SPR_L1CSR1); if ((boothowto & RB_VERBOSE) != 0 || (csr & L1CSR1_ICE) == 0) printf("L1 I-cache %sabled\n", (csr & L1CSR1_ICE) ? "en" : "dis"); } void booke_enable_l2_cache(void) { uint32_t csr; /* Enable L2 cache on E500mc */ if ((((mfpvr() >> 16) & 0xFFFF) == FSL_E500mc) || (((mfpvr() >> 16) & 0xFFFF) == FSL_E5500)) { csr = mfspr(SPR_L2CSR0); if ((csr & L2CSR0_L2E) == 0) { l2cache_inval(); l2cache_enable(); } csr = mfspr(SPR_L2CSR0); if ((boothowto & RB_VERBOSE) != 0 || (csr & L2CSR0_L2E) == 0) printf("L2 cache %sabled\n", (csr & L2CSR0_L2E) ? "en" : "dis"); } } void booke_enable_bpred(void) { uint32_t csr; bpred_enable(); csr = mfspr(SPR_BUCSR); if ((boothowto & RB_VERBOSE) != 0 || (csr & BUCSR_BPEN) == 0) printf("Branch Predictor %sabled\n", (csr & BUCSR_BPEN) ? "en" : "dis"); } void booke_disable_l2_cache(void) { } Index: head/sys/powerpc/booke/machdep_ppc4xx.c =================================================================== --- head/sys/powerpc/booke/machdep_ppc4xx.c (revision 326260) +++ head/sys/powerpc/booke/machdep_ppc4xx.c (revision 326261) @@ -1,214 +1,216 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2011-2012 Semihalf. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #define OCP_ADDR_WORDLO(addr) ((uint32_t)((uint64_t)(addr) & 0xFFFFFFFF)) #define OCP_ADDR_WORDHI(addr) ((uint32_t)((uint64_t)(addr) >> 32)) extern void tlb_write(u_int, uint32_t, uint32_t, uint32_t, tlbtid_t, uint32_t, uint32_t); extern void tlb_read(u_int, uint32_t *, uint32_t *, uint32_t *, uint32_t *, uint32_t *, uint32_t *); unsigned int tlb_static_entries; unsigned int tlb_current_entry = TLB_SIZE; unsigned int tlb_misses = 0; unsigned int tlb_invals = 0; void tlb_map(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); void tlb_map_mem(uint32_t, uint32_t, uint32_t); void tlb_dump(void); void booke_init_tlb(vm_paddr_t fdt_immr_pa) { /* Map register space */ tlb_map(APM86XXX_DEEP_SLEEP_VA, OCP_ADDR_WORDLO(APM86XXX_DEEP_SLEEP_PA), OCP_ADDR_WORDHI(APM86XXX_DEEP_SLEEP_PA), TLB_VALID | TLB_SIZE_16M, TLB_SW | TLB_SR | TLB_I | TLB_G); tlb_map(APM86XXX_CSR_VA, OCP_ADDR_WORDLO(APM86XXX_CSR_PA), OCP_ADDR_WORDHI(APM86XXX_CSR_PA), TLB_VALID | TLB_SIZE_16M, TLB_SW | TLB_SR | TLB_I | TLB_G); tlb_map(APM86XXX_PRIMARY_FABRIC_VA, OCP_ADDR_WORDLO(APM86XXX_PRIMARY_FABRIC_PA), OCP_ADDR_WORDHI(APM86XXX_PRIMARY_FABRIC_PA), TLB_VALID | TLB_SIZE_16M, TLB_SW | TLB_SR | TLB_I | TLB_G); tlb_map(APM86XXX_AHB_VA, OCP_ADDR_WORDLO(APM86XXX_AHB_PA), OCP_ADDR_WORDHI(APM86XXX_AHB_PA), TLB_VALID | TLB_SIZE_16M, TLB_SW | TLB_SR | TLB_I | TLB_G); /* Map MailBox space */ tlb_map(APM86XXX_MBOX_VA, OCP_ADDR_WORDLO(APM86XXX_MBOX_PA), OCP_ADDR_WORDHI(APM86XXX_MBOX_PA), TLB_VALID | TLB_SIZE_4K, TLB_UX | TLB_UW | TLB_UR | TLB_SX | TLB_SW | TLB_SR | TLB_I | TLB_G); tlb_map(APM86XXX_MBOX_VA + 0x1000, OCP_ADDR_WORDLO(APM86XXX_MBOX_PA) + 0x1000, OCP_ADDR_WORDHI(APM86XXX_MBOX_PA), TLB_VALID | TLB_SIZE_4K, TLB_UX | TLB_UW | TLB_UR | TLB_SX | TLB_SW | TLB_SR | TLB_I | TLB_G); tlb_map(APM86XXX_MBOX_VA + 0x2000, OCP_ADDR_WORDLO(APM86XXX_MBOX_PA)+ 0x2000, OCP_ADDR_WORDHI(APM86XXX_MBOX_PA), TLB_VALID | TLB_SIZE_4K, TLB_UX | TLB_UW | TLB_UR | TLB_SX | TLB_SW | TLB_SR | TLB_I | TLB_G); } void booke_enable_l1_cache(void) { } void booke_enable_l2_cache(void) { } void booke_disable_l2_cache(void) { uint32_t ccr1,l2cr0; /* Disable L2 cache op broadcast */ ccr1 = mfspr(SPR_CCR1); ccr1 &= ~CCR1_L2COBE; mtspr(SPR_CCR1, ccr1); /* Set L2 array size to 0 i.e. disable L2 cache */ mtdcr(DCR_L2DCDCRAI, DCR_L2CR0); l2cr0 = mfdcr(DCR_L2DCDCRDI); l2cr0 &= ~L2CR0_AS; mtdcr(DCR_L2DCDCRDI, l2cr0); } void tlb_map(uint32_t epn, uint32_t rpn, uint32_t erpn, uint32_t flags, uint32_t perms) { tlb_write(++tlb_static_entries, epn, rpn, erpn, 0, flags, perms); } static void tlb_dump_entry(u_int entry) { uint32_t epn, rpn, erpn, tid, flags, perms; const char *size; tlb_read(entry, &epn, &rpn, &erpn, &tid, &flags, &perms); switch (flags & TLB_SIZE_MASK) { case TLB_SIZE_1K: size = " 1k"; break; case TLB_SIZE_4K: size = " 4k"; break; case TLB_SIZE_16K: size = " 16k"; break; case TLB_SIZE_256K: size = "256k"; break; case TLB_SIZE_1M: size = " 1M"; break; case TLB_SIZE_16M: size = " 16M"; break; case TLB_SIZE_256M: size = "256M"; break; case TLB_SIZE_1G: size = " 1G"; break; default: size = "????"; break; } printf("TLB[%02u]: 0x%08X => " "0x%01X_%08X %s %c %c %s %s %s %s %s " "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c (%u)\n", entry, epn, erpn, rpn, size, (flags & TLB_TS) ? '1' : '0', (flags & TLB_VALID) ? 'V' : '.', (perms & TLB_WL1) ? "WL1" : "___", (perms & TLB_IL1I) ? "IL1I" : "____", (perms & TLB_IL1D) ? "IL1D" : "____", (perms & TLB_IL2I) ? "IL2I" : "____", (perms & TLB_IL2D) ? "IL2D" : "____", (perms & TLB_U0) ? '1' : '.', (perms & TLB_U1) ? '2' : '.', (perms & TLB_U2) ? '3' : '.', (perms & TLB_U3) ? '4' : '.', (perms & TLB_W) ? 'W' : '.', (perms & TLB_I) ? 'I' : '.', (perms & TLB_M) ? 'M' : '.', (perms & TLB_G) ? 'G' : '.', (perms & TLB_E) ? 'E' : '.', (perms & TLB_UX) ? 'x' : '.', (perms & TLB_UW) ? 'w' : '.', (perms & TLB_UR) ? 'r' : '.', (perms & TLB_SX) ? 'X' : '.', (perms & TLB_SW) ? 'W' : '.', (perms & TLB_SR) ? 'R' : '.', tid); } void tlb_dump(void) { int i; for (i = 0; i < TLB_SIZE; i++) tlb_dump_entry(i); } Index: head/sys/powerpc/booke/mp_cpudep.c =================================================================== --- head/sys/powerpc/booke/mp_cpudep.c (revision 326260) +++ head/sys/powerpc/booke/mp_cpudep.c (revision 326261) @@ -1,95 +1,97 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2008-2009 Semihalf, Rafal Jaworowski * 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$"); #include #include #include #include #include #include #include #include #include #include #include extern void dcache_enable(void); extern void dcache_inval(void); extern void icache_enable(void); extern void icache_inval(void); volatile void *ap_pcpu; uintptr_t cpudep_ap_bootstrap() { uint32_t msr, csr; uintptr_t sp; /* Enable L1 caches */ csr = mfspr(SPR_L1CSR0); if ((csr & L1CSR0_DCE) == 0) { dcache_inval(); dcache_enable(); } csr = mfspr(SPR_L1CSR1); if ((csr & L1CSR1_ICE) == 0) { icache_inval(); icache_enable(); } /* Set MSR */ #ifdef __powerpc64__ msr = PSL_CM | PSL_ME; #else msr = PSL_ME; #endif mtmsr(msr); /* Assign pcpu fields, return ptr to this AP's idle thread kstack */ pcpup->pc_curthread = pcpup->pc_idlethread; #ifdef __powerpc64__ __asm __volatile("mr 13,%0" :: "r"(pcpup->pc_curthread)); #else __asm __volatile("mr 2,%0" :: "r"(pcpup->pc_curthread)); #endif pcpup->pc_curpcb = pcpup->pc_curthread->td_pcb; sp = pcpup->pc_curpcb->pcb_sp; /* XXX shouldn't the pcb_sp be checked/forced for alignment here?? */ return (sp); } void cpudep_ap_setup() { } Index: head/sys/powerpc/booke/platform_bare.c =================================================================== --- head/sys/powerpc/booke/platform_bare.c (revision 326260) +++ head/sys/powerpc/booke/platform_bare.c (revision 326261) @@ -1,160 +1,162 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2008-2012 Semihalf. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "platform_if.h" extern uint32_t *bootinfo; static int bare_probe(platform_t); static void bare_mem_regions(platform_t, struct mem_region *phys, int *physsz, struct mem_region *avail, int *availsz); static u_long bare_timebase_freq(platform_t, struct cpuref *cpuref); static void bare_reset(platform_t); static platform_method_t bare_methods[] = { PLATFORMMETHOD(platform_probe, bare_probe), PLATFORMMETHOD(platform_mem_regions, bare_mem_regions), PLATFORMMETHOD(platform_timebase_freq, bare_timebase_freq), PLATFORMMETHOD(platform_reset, bare_reset), PLATFORMMETHOD_END }; static platform_def_t bare_platform = { "bare", bare_methods, 0 }; PLATFORM_DEF(bare_platform); static int bare_probe(platform_t plat) { if (OF_peer(0) == -1) /* Needs device tree to work */ return (ENXIO); return (BUS_PROBE_GENERIC); } void bare_mem_regions(platform_t plat, struct mem_region *phys, int *physsz, struct mem_region *avail, int *availsz) { ofw_mem_regions(phys, physsz, avail, availsz); } static u_long bare_timebase_freq(platform_t plat, struct cpuref *cpuref) { u_long ticks; phandle_t cpus, child; pcell_t freq; if (bootinfo != NULL) { if (bootinfo[0] == 1) { /* Backward compatibility. See 8-STABLE. */ ticks = bootinfo[3] >> 3; } else { /* Compatibility with Juniper's loader. */ ticks = bootinfo[5] >> 3; } } else ticks = 0; if ((cpus = OF_finddevice("/cpus")) == -1) goto out; if ((child = OF_child(cpus)) == 0) goto out; switch (OF_getproplen(child, "timebase-frequency")) { case 4: { uint32_t tbase; OF_getprop(child, "timebase-frequency", &tbase, sizeof(tbase)); ticks = tbase; return (ticks); } case 8: { uint64_t tbase; OF_getprop(child, "timebase-frequency", &tbase, sizeof(tbase)); ticks = tbase; return (ticks); } default: break; } freq = 0; if (OF_getprop(child, "bus-frequency", (void *)&freq, sizeof(freq)) <= 0) goto out; /* * Time Base and Decrementer are updated every 8 CCB bus clocks. * HID0[SEL_TBCLK] = 0 */ if (freq != 0) ticks = freq / 8; out: if (ticks <= 0) panic("Unable to determine timebase frequency!"); return (ticks); } static void bare_reset(platform_t plat) { printf("Reset failed...\n"); while (1) ; } Index: head/sys/powerpc/booke/pmap.c =================================================================== --- head/sys/powerpc/booke/pmap.c (revision 326260) +++ head/sys/powerpc/booke/pmap.c (revision 326261) @@ -1,4381 +1,4383 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2007-2009 Semihalf, Rafal Jaworowski * Copyright (C) 2006 Semihalf, Marian Balakowicz * 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. * * Some hw specific parts of this pmap were derived or influenced * by NetBSD's ibm4xx pmap module. More generic code is shared with * a few other pmap modules from the FreeBSD tree. */ /* * VM layout notes: * * Kernel and user threads run within one common virtual address space * defined by AS=0. * * 32-bit pmap: * Virtual address space layout: * ----------------------------- * 0x0000_0000 - 0x7fff_ffff : user process * 0x8000_0000 - 0xbfff_ffff : pmap_mapdev()-ed area (PCI/PCIE etc.) * 0xc000_0000 - 0xc0ff_ffff : kernel reserved * 0xc000_0000 - data_end : kernel code+data, env, metadata etc. * 0xc100_0000 - 0xffff_ffff : KVA * 0xc100_0000 - 0xc100_3fff : reserved for page zero/copy * 0xc100_4000 - 0xc200_3fff : reserved for ptbl bufs * 0xc200_4000 - 0xc200_8fff : guard page + kstack0 * 0xc200_9000 - 0xfeef_ffff : actual free KVA space * * 64-bit pmap: * Virtual address space layout: * ----------------------------- * 0x0000_0000_0000_0000 - 0xbfff_ffff_ffff_ffff : user process * 0x0000_0000_0000_0000 - 0x8fff_ffff_ffff_ffff : text, data, heap, maps, libraries * 0x9000_0000_0000_0000 - 0xafff_ffff_ffff_ffff : mmio region * 0xb000_0000_0000_0000 - 0xbfff_ffff_ffff_ffff : stack * 0xc000_0000_0000_0000 - 0xcfff_ffff_ffff_ffff : kernel reserved * 0xc000_0000_0000_0000 - endkernel-1 : kernel code & data * endkernel - msgbufp-1 : flat device tree * msgbufp - ptbl_bufs-1 : message buffer * ptbl_bufs - kernel_pdir-1 : kernel page tables * kernel_pdir - kernel_pp2d-1 : kernel page directory * kernel_pp2d - . : kernel pointers to page directory * pmap_zero_copy_min - crashdumpmap-1 : reserved for page zero/copy * crashdumpmap - ptbl_buf_pool_vabase-1 : reserved for ptbl bufs * ptbl_buf_pool_vabase - virtual_avail-1 : user page directories and page tables * virtual_avail - 0xcfff_ffff_ffff_ffff : actual free KVA space * 0xd000_0000_0000_0000 - 0xdfff_ffff_ffff_ffff : coprocessor region * 0xe000_0000_0000_0000 - 0xefff_ffff_ffff_ffff : mmio region * 0xf000_0000_0000_0000 - 0xffff_ffff_ffff_ffff : direct map * 0xf000_0000_0000_0000 - +Maxmem : physmem map * - 0xffff_ffff_ffff_ffff : device direct map */ #include __FBSDID("$FreeBSD$"); #include "opt_kstack_pages.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mmu_if.h" #define SPARSE_MAPDEV #ifdef DEBUG #define debugf(fmt, args...) printf(fmt, ##args) #else #define debugf(fmt, args...) #endif #ifdef __powerpc64__ #define PRI0ptrX "016lx" #else #define PRI0ptrX "08x" #endif #define TODO panic("%s: not implemented", __func__); extern unsigned char _etext[]; extern unsigned char _end[]; extern uint32_t *bootinfo; vm_paddr_t kernload; vm_offset_t kernstart; vm_size_t kernsize; /* Message buffer and tables. */ static vm_offset_t data_start; static vm_size_t data_end; /* Phys/avail memory regions. */ static struct mem_region *availmem_regions; static int availmem_regions_sz; static struct mem_region *physmem_regions; static int physmem_regions_sz; /* Reserved KVA space and mutex for mmu_booke_zero_page. */ static vm_offset_t zero_page_va; static struct mtx zero_page_mutex; static struct mtx tlbivax_mutex; /* Reserved KVA space and mutex for mmu_booke_copy_page. */ static vm_offset_t copy_page_src_va; static vm_offset_t copy_page_dst_va; static struct mtx copy_page_mutex; /**************************************************************************/ /* PMAP */ /**************************************************************************/ static int mmu_booke_enter_locked(mmu_t, pmap_t, vm_offset_t, vm_page_t, vm_prot_t, u_int flags, int8_t psind); unsigned int kptbl_min; /* Index of the first kernel ptbl. */ unsigned int kernel_ptbls; /* Number of KVA ptbls. */ #ifdef __powerpc64__ unsigned int kernel_pdirs; #endif /* * If user pmap is processed with mmu_booke_remove and the resident count * drops to 0, there are no more pages to remove, so we need not continue. */ #define PMAP_REMOVE_DONE(pmap) \ ((pmap) != kernel_pmap && (pmap)->pm_stats.resident_count == 0) #if defined(COMPAT_FREEBSD32) || !defined(__powerpc64__) extern int elf32_nxstack; #endif /**************************************************************************/ /* TLB and TID handling */ /**************************************************************************/ /* Translation ID busy table */ static volatile pmap_t tidbusy[MAXCPU][TID_MAX + 1]; /* * TLB0 capabilities (entry, way numbers etc.). These can vary between e500 * core revisions and should be read from h/w registers during early config. */ uint32_t tlb0_entries; uint32_t tlb0_ways; uint32_t tlb0_entries_per_way; uint32_t tlb1_entries; #define TLB0_ENTRIES (tlb0_entries) #define TLB0_WAYS (tlb0_ways) #define TLB0_ENTRIES_PER_WAY (tlb0_entries_per_way) #define TLB1_ENTRIES (tlb1_entries) static vm_offset_t tlb1_map_base = VM_MAXUSER_ADDRESS + PAGE_SIZE; static tlbtid_t tid_alloc(struct pmap *); static void tid_flush(tlbtid_t tid); #ifdef __powerpc64__ static void tlb_print_entry(int, uint32_t, uint64_t, uint32_t, uint32_t); #else static void tlb_print_entry(int, uint32_t, uint32_t, uint32_t, uint32_t); #endif static void tlb1_read_entry(tlb_entry_t *, unsigned int); static void tlb1_write_entry(tlb_entry_t *, unsigned int); static int tlb1_iomapped(int, vm_paddr_t, vm_size_t, vm_offset_t *); static vm_size_t tlb1_mapin_region(vm_offset_t, vm_paddr_t, vm_size_t); static vm_size_t tsize2size(unsigned int); static unsigned int size2tsize(vm_size_t); static unsigned int ilog2(unsigned int); static void set_mas4_defaults(void); static inline void tlb0_flush_entry(vm_offset_t); static inline unsigned int tlb0_tableidx(vm_offset_t, unsigned int); /**************************************************************************/ /* Page table management */ /**************************************************************************/ static struct rwlock_padalign pvh_global_lock; /* Data for the pv entry allocation mechanism */ static uma_zone_t pvzone; static int pv_entry_count = 0, pv_entry_max = 0, pv_entry_high_water = 0; #define PV_ENTRY_ZONE_MIN 2048 /* min pv entries in uma zone */ #ifndef PMAP_SHPGPERPROC #define PMAP_SHPGPERPROC 200 #endif static void ptbl_init(void); static struct ptbl_buf *ptbl_buf_alloc(void); static void ptbl_buf_free(struct ptbl_buf *); static void ptbl_free_pmap_ptbl(pmap_t, pte_t *); #ifdef __powerpc64__ static pte_t *ptbl_alloc(mmu_t, pmap_t, pte_t **, unsigned int, boolean_t); static void ptbl_free(mmu_t, pmap_t, pte_t **, unsigned int); static void ptbl_hold(mmu_t, pmap_t, pte_t **, unsigned int); static int ptbl_unhold(mmu_t, pmap_t, vm_offset_t); #else static pte_t *ptbl_alloc(mmu_t, pmap_t, unsigned int, boolean_t); static void ptbl_free(mmu_t, pmap_t, unsigned int); static void ptbl_hold(mmu_t, pmap_t, unsigned int); static int ptbl_unhold(mmu_t, pmap_t, unsigned int); #endif static vm_paddr_t pte_vatopa(mmu_t, pmap_t, vm_offset_t); static int pte_enter(mmu_t, pmap_t, vm_page_t, vm_offset_t, uint32_t, boolean_t); static int pte_remove(mmu_t, pmap_t, vm_offset_t, uint8_t); static pte_t *pte_find(mmu_t, pmap_t, vm_offset_t); static void kernel_pte_alloc(vm_offset_t, vm_offset_t, vm_offset_t); static pv_entry_t pv_alloc(void); static void pv_free(pv_entry_t); static void pv_insert(pmap_t, vm_offset_t, vm_page_t); static void pv_remove(pmap_t, vm_offset_t, vm_page_t); static void booke_pmap_init_qpages(void); /* Number of kva ptbl buffers, each covering one ptbl (PTBL_PAGES). */ #ifdef __powerpc64__ #define PTBL_BUFS (16UL * 16 * 16) #else #define PTBL_BUFS (128 * 16) #endif struct ptbl_buf { TAILQ_ENTRY(ptbl_buf) link; /* list link */ vm_offset_t kva; /* va of mapping */ }; /* ptbl free list and a lock used for access synchronization. */ static TAILQ_HEAD(, ptbl_buf) ptbl_buf_freelist; static struct mtx ptbl_buf_freelist_lock; /* Base address of kva space allocated fot ptbl bufs. */ static vm_offset_t ptbl_buf_pool_vabase; /* Pointer to ptbl_buf structures. */ static struct ptbl_buf *ptbl_bufs; #ifdef SMP extern tlb_entry_t __boot_tlb1[]; void pmap_bootstrap_ap(volatile uint32_t *); #endif /* * Kernel MMU interface */ static void mmu_booke_clear_modify(mmu_t, vm_page_t); static void mmu_booke_copy(mmu_t, pmap_t, pmap_t, vm_offset_t, vm_size_t, vm_offset_t); static void mmu_booke_copy_page(mmu_t, vm_page_t, vm_page_t); static void mmu_booke_copy_pages(mmu_t, vm_page_t *, vm_offset_t, vm_page_t *, vm_offset_t, int); static int mmu_booke_enter(mmu_t, pmap_t, vm_offset_t, vm_page_t, vm_prot_t, u_int flags, int8_t psind); static void mmu_booke_enter_object(mmu_t, pmap_t, vm_offset_t, vm_offset_t, vm_page_t, vm_prot_t); static void mmu_booke_enter_quick(mmu_t, pmap_t, vm_offset_t, vm_page_t, vm_prot_t); static vm_paddr_t mmu_booke_extract(mmu_t, pmap_t, vm_offset_t); static vm_page_t mmu_booke_extract_and_hold(mmu_t, pmap_t, vm_offset_t, vm_prot_t); static void mmu_booke_init(mmu_t); static boolean_t mmu_booke_is_modified(mmu_t, vm_page_t); static boolean_t mmu_booke_is_prefaultable(mmu_t, pmap_t, vm_offset_t); static boolean_t mmu_booke_is_referenced(mmu_t, vm_page_t); static int mmu_booke_ts_referenced(mmu_t, vm_page_t); static vm_offset_t mmu_booke_map(mmu_t, vm_offset_t *, vm_paddr_t, vm_paddr_t, int); static int mmu_booke_mincore(mmu_t, pmap_t, vm_offset_t, vm_paddr_t *); static void mmu_booke_object_init_pt(mmu_t, pmap_t, vm_offset_t, vm_object_t, vm_pindex_t, vm_size_t); static boolean_t mmu_booke_page_exists_quick(mmu_t, pmap_t, vm_page_t); static void mmu_booke_page_init(mmu_t, vm_page_t); static int mmu_booke_page_wired_mappings(mmu_t, vm_page_t); static void mmu_booke_pinit(mmu_t, pmap_t); static void mmu_booke_pinit0(mmu_t, pmap_t); static void mmu_booke_protect(mmu_t, pmap_t, vm_offset_t, vm_offset_t, vm_prot_t); static void mmu_booke_qenter(mmu_t, vm_offset_t, vm_page_t *, int); static void mmu_booke_qremove(mmu_t, vm_offset_t, int); static void mmu_booke_release(mmu_t, pmap_t); static void mmu_booke_remove(mmu_t, pmap_t, vm_offset_t, vm_offset_t); static void mmu_booke_remove_all(mmu_t, vm_page_t); static void mmu_booke_remove_write(mmu_t, vm_page_t); static void mmu_booke_unwire(mmu_t, pmap_t, vm_offset_t, vm_offset_t); static void mmu_booke_zero_page(mmu_t, vm_page_t); static void mmu_booke_zero_page_area(mmu_t, vm_page_t, int, int); static void mmu_booke_activate(mmu_t, struct thread *); static void mmu_booke_deactivate(mmu_t, struct thread *); static void mmu_booke_bootstrap(mmu_t, vm_offset_t, vm_offset_t); static void *mmu_booke_mapdev(mmu_t, vm_paddr_t, vm_size_t); static void *mmu_booke_mapdev_attr(mmu_t, vm_paddr_t, vm_size_t, vm_memattr_t); static void mmu_booke_unmapdev(mmu_t, vm_offset_t, vm_size_t); static vm_paddr_t mmu_booke_kextract(mmu_t, vm_offset_t); static void mmu_booke_kenter(mmu_t, vm_offset_t, vm_paddr_t); static void mmu_booke_kenter_attr(mmu_t, vm_offset_t, vm_paddr_t, vm_memattr_t); static void mmu_booke_kremove(mmu_t, vm_offset_t); static boolean_t mmu_booke_dev_direct_mapped(mmu_t, vm_paddr_t, vm_size_t); static void mmu_booke_sync_icache(mmu_t, pmap_t, vm_offset_t, vm_size_t); static void mmu_booke_dumpsys_map(mmu_t, vm_paddr_t pa, size_t, void **); static void mmu_booke_dumpsys_unmap(mmu_t, vm_paddr_t pa, size_t, void *); static void mmu_booke_scan_init(mmu_t); static vm_offset_t mmu_booke_quick_enter_page(mmu_t mmu, vm_page_t m); static void mmu_booke_quick_remove_page(mmu_t mmu, vm_offset_t addr); static int mmu_booke_change_attr(mmu_t mmu, vm_offset_t addr, vm_size_t sz, vm_memattr_t mode); static mmu_method_t mmu_booke_methods[] = { /* pmap dispatcher interface */ MMUMETHOD(mmu_clear_modify, mmu_booke_clear_modify), MMUMETHOD(mmu_copy, mmu_booke_copy), MMUMETHOD(mmu_copy_page, mmu_booke_copy_page), MMUMETHOD(mmu_copy_pages, mmu_booke_copy_pages), MMUMETHOD(mmu_enter, mmu_booke_enter), MMUMETHOD(mmu_enter_object, mmu_booke_enter_object), MMUMETHOD(mmu_enter_quick, mmu_booke_enter_quick), MMUMETHOD(mmu_extract, mmu_booke_extract), MMUMETHOD(mmu_extract_and_hold, mmu_booke_extract_and_hold), MMUMETHOD(mmu_init, mmu_booke_init), MMUMETHOD(mmu_is_modified, mmu_booke_is_modified), MMUMETHOD(mmu_is_prefaultable, mmu_booke_is_prefaultable), MMUMETHOD(mmu_is_referenced, mmu_booke_is_referenced), MMUMETHOD(mmu_ts_referenced, mmu_booke_ts_referenced), MMUMETHOD(mmu_map, mmu_booke_map), MMUMETHOD(mmu_mincore, mmu_booke_mincore), MMUMETHOD(mmu_object_init_pt, mmu_booke_object_init_pt), MMUMETHOD(mmu_page_exists_quick,mmu_booke_page_exists_quick), MMUMETHOD(mmu_page_init, mmu_booke_page_init), MMUMETHOD(mmu_page_wired_mappings, mmu_booke_page_wired_mappings), MMUMETHOD(mmu_pinit, mmu_booke_pinit), MMUMETHOD(mmu_pinit0, mmu_booke_pinit0), MMUMETHOD(mmu_protect, mmu_booke_protect), MMUMETHOD(mmu_qenter, mmu_booke_qenter), MMUMETHOD(mmu_qremove, mmu_booke_qremove), MMUMETHOD(mmu_release, mmu_booke_release), MMUMETHOD(mmu_remove, mmu_booke_remove), MMUMETHOD(mmu_remove_all, mmu_booke_remove_all), MMUMETHOD(mmu_remove_write, mmu_booke_remove_write), MMUMETHOD(mmu_sync_icache, mmu_booke_sync_icache), MMUMETHOD(mmu_unwire, mmu_booke_unwire), MMUMETHOD(mmu_zero_page, mmu_booke_zero_page), MMUMETHOD(mmu_zero_page_area, mmu_booke_zero_page_area), MMUMETHOD(mmu_activate, mmu_booke_activate), MMUMETHOD(mmu_deactivate, mmu_booke_deactivate), MMUMETHOD(mmu_quick_enter_page, mmu_booke_quick_enter_page), MMUMETHOD(mmu_quick_remove_page, mmu_booke_quick_remove_page), /* Internal interfaces */ MMUMETHOD(mmu_bootstrap, mmu_booke_bootstrap), MMUMETHOD(mmu_dev_direct_mapped,mmu_booke_dev_direct_mapped), MMUMETHOD(mmu_mapdev, mmu_booke_mapdev), MMUMETHOD(mmu_mapdev_attr, mmu_booke_mapdev_attr), MMUMETHOD(mmu_kenter, mmu_booke_kenter), MMUMETHOD(mmu_kenter_attr, mmu_booke_kenter_attr), MMUMETHOD(mmu_kextract, mmu_booke_kextract), MMUMETHOD(mmu_kremove, mmu_booke_kremove), MMUMETHOD(mmu_unmapdev, mmu_booke_unmapdev), MMUMETHOD(mmu_change_attr, mmu_booke_change_attr), /* dumpsys() support */ MMUMETHOD(mmu_dumpsys_map, mmu_booke_dumpsys_map), MMUMETHOD(mmu_dumpsys_unmap, mmu_booke_dumpsys_unmap), MMUMETHOD(mmu_scan_init, mmu_booke_scan_init), { 0, 0 } }; MMU_DEF(booke_mmu, MMU_TYPE_BOOKE, mmu_booke_methods, 0); static __inline uint32_t tlb_calc_wimg(vm_paddr_t pa, vm_memattr_t ma) { uint32_t attrib; int i; if (ma != VM_MEMATTR_DEFAULT) { switch (ma) { case VM_MEMATTR_UNCACHEABLE: return (MAS2_I | MAS2_G); case VM_MEMATTR_WRITE_COMBINING: case VM_MEMATTR_WRITE_BACK: case VM_MEMATTR_PREFETCHABLE: return (MAS2_I); case VM_MEMATTR_WRITE_THROUGH: return (MAS2_W | MAS2_M); case VM_MEMATTR_CACHEABLE: return (MAS2_M); } } /* * Assume the page is cache inhibited and access is guarded unless * it's in our available memory array. */ attrib = _TLB_ENTRY_IO; for (i = 0; i < physmem_regions_sz; i++) { if ((pa >= physmem_regions[i].mr_start) && (pa < (physmem_regions[i].mr_start + physmem_regions[i].mr_size))) { attrib = _TLB_ENTRY_MEM; break; } } return (attrib); } static inline void tlb_miss_lock(void) { #ifdef SMP struct pcpu *pc; if (!smp_started) return; STAILQ_FOREACH(pc, &cpuhead, pc_allcpu) { if (pc != pcpup) { CTR3(KTR_PMAP, "%s: tlb miss LOCK of CPU=%d, " "tlb_lock=%p", __func__, pc->pc_cpuid, pc->pc_booke_tlb_lock); KASSERT((pc->pc_cpuid != PCPU_GET(cpuid)), ("tlb_miss_lock: tried to lock self")); tlb_lock(pc->pc_booke_tlb_lock); CTR1(KTR_PMAP, "%s: locked", __func__); } } #endif } static inline void tlb_miss_unlock(void) { #ifdef SMP struct pcpu *pc; if (!smp_started) return; STAILQ_FOREACH(pc, &cpuhead, pc_allcpu) { if (pc != pcpup) { CTR2(KTR_PMAP, "%s: tlb miss UNLOCK of CPU=%d", __func__, pc->pc_cpuid); tlb_unlock(pc->pc_booke_tlb_lock); CTR1(KTR_PMAP, "%s: unlocked", __func__); } } #endif } /* Return number of entries in TLB0. */ static __inline void tlb0_get_tlbconf(void) { uint32_t tlb0_cfg; tlb0_cfg = mfspr(SPR_TLB0CFG); tlb0_entries = tlb0_cfg & TLBCFG_NENTRY_MASK; tlb0_ways = (tlb0_cfg & TLBCFG_ASSOC_MASK) >> TLBCFG_ASSOC_SHIFT; tlb0_entries_per_way = tlb0_entries / tlb0_ways; } /* Return number of entries in TLB1. */ static __inline void tlb1_get_tlbconf(void) { uint32_t tlb1_cfg; tlb1_cfg = mfspr(SPR_TLB1CFG); tlb1_entries = tlb1_cfg & TLBCFG_NENTRY_MASK; } /**************************************************************************/ /* Page table related */ /**************************************************************************/ #ifdef __powerpc64__ /* Initialize pool of kva ptbl buffers. */ static void ptbl_init(void) { int i; mtx_init(&ptbl_buf_freelist_lock, "ptbl bufs lock", NULL, MTX_DEF); TAILQ_INIT(&ptbl_buf_freelist); for (i = 0; i < PTBL_BUFS; i++) { ptbl_bufs[i].kva = ptbl_buf_pool_vabase + i * MAX(PTBL_PAGES,PDIR_PAGES) * PAGE_SIZE; TAILQ_INSERT_TAIL(&ptbl_buf_freelist, &ptbl_bufs[i], link); } } /* Get an sf_buf from the freelist. */ static struct ptbl_buf * ptbl_buf_alloc(void) { struct ptbl_buf *buf; mtx_lock(&ptbl_buf_freelist_lock); buf = TAILQ_FIRST(&ptbl_buf_freelist); if (buf != NULL) TAILQ_REMOVE(&ptbl_buf_freelist, buf, link); mtx_unlock(&ptbl_buf_freelist_lock); return (buf); } /* Return ptbl buff to free pool. */ static void ptbl_buf_free(struct ptbl_buf *buf) { mtx_lock(&ptbl_buf_freelist_lock); TAILQ_INSERT_TAIL(&ptbl_buf_freelist, buf, link); mtx_unlock(&ptbl_buf_freelist_lock); } /* * Search the list of allocated ptbl bufs and find on list of allocated ptbls */ static void ptbl_free_pmap_ptbl(pmap_t pmap, pte_t * ptbl) { struct ptbl_buf *pbuf; TAILQ_FOREACH(pbuf, &pmap->pm_ptbl_list, link) { if (pbuf->kva == (vm_offset_t) ptbl) { /* Remove from pmap ptbl buf list. */ TAILQ_REMOVE(&pmap->pm_ptbl_list, pbuf, link); /* Free corresponding ptbl buf. */ ptbl_buf_free(pbuf); break; } } } /* Get a pointer to a PTE in a page table. */ static __inline pte_t * pte_find(mmu_t mmu, pmap_t pmap, vm_offset_t va) { pte_t **pdir; pte_t *ptbl; KASSERT((pmap != NULL), ("pte_find: invalid pmap")); pdir = pmap->pm_pp2d[PP2D_IDX(va)]; if (!pdir) return NULL; ptbl = pdir[PDIR_IDX(va)]; return ((ptbl != NULL) ? &ptbl[PTBL_IDX(va)] : NULL); } /* * Search the list of allocated pdir bufs and find on list of allocated pdirs */ static void ptbl_free_pmap_pdir(mmu_t mmu, pmap_t pmap, pte_t ** pdir) { struct ptbl_buf *pbuf; TAILQ_FOREACH(pbuf, &pmap->pm_pdir_list, link) { if (pbuf->kva == (vm_offset_t) pdir) { /* Remove from pmap ptbl buf list. */ TAILQ_REMOVE(&pmap->pm_pdir_list, pbuf, link); /* Free corresponding pdir buf. */ ptbl_buf_free(pbuf); break; } } } /* Free pdir pages and invalidate pdir entry. */ static void pdir_free(mmu_t mmu, pmap_t pmap, unsigned int pp2d_idx) { pte_t **pdir; vm_paddr_t pa; vm_offset_t va; vm_page_t m; int i; pdir = pmap->pm_pp2d[pp2d_idx]; KASSERT((pdir != NULL), ("pdir_free: null pdir")); pmap->pm_pp2d[pp2d_idx] = NULL; for (i = 0; i < PDIR_PAGES; i++) { va = ((vm_offset_t) pdir + (i * PAGE_SIZE)); pa = pte_vatopa(mmu, kernel_pmap, va); m = PHYS_TO_VM_PAGE(pa); vm_page_free_zero(m); atomic_subtract_int(&vm_cnt.v_wire_count, 1); pmap_kremove(va); } ptbl_free_pmap_pdir(mmu, pmap, pdir); } /* * Decrement pdir pages hold count and attempt to free pdir pages. Called * when removing directory entry from pdir. * * Return 1 if pdir pages were freed. */ static int pdir_unhold(mmu_t mmu, pmap_t pmap, u_int pp2d_idx) { pte_t **pdir; vm_paddr_t pa; vm_page_t m; int i; KASSERT((pmap != kernel_pmap), ("pdir_unhold: unholding kernel pdir!")); pdir = pmap->pm_pp2d[pp2d_idx]; KASSERT(((vm_offset_t) pdir >= VM_MIN_KERNEL_ADDRESS), ("pdir_unhold: non kva pdir")); /* decrement hold count */ for (i = 0; i < PDIR_PAGES; i++) { pa = pte_vatopa(mmu, kernel_pmap, (vm_offset_t) pdir + (i * PAGE_SIZE)); m = PHYS_TO_VM_PAGE(pa); m->wire_count--; } /* * Free pdir pages if there are no dir entries in this pdir. * wire_count has the same value for all ptbl pages, so check the * last page. */ if (m->wire_count == 0) { pdir_free(mmu, pmap, pp2d_idx); return (1); } return (0); } /* * Increment hold count for pdir pages. This routine is used when new ptlb * entry is being inserted into pdir. */ static void pdir_hold(mmu_t mmu, pmap_t pmap, pte_t ** pdir) { vm_paddr_t pa; vm_page_t m; int i; KASSERT((pmap != kernel_pmap), ("pdir_hold: holding kernel pdir!")); KASSERT((pdir != NULL), ("pdir_hold: null pdir")); for (i = 0; i < PDIR_PAGES; i++) { pa = pte_vatopa(mmu, kernel_pmap, (vm_offset_t) pdir + (i * PAGE_SIZE)); m = PHYS_TO_VM_PAGE(pa); m->wire_count++; } } /* Allocate page table. */ static pte_t * ptbl_alloc(mmu_t mmu, pmap_t pmap, pte_t ** pdir, unsigned int pdir_idx, boolean_t nosleep) { vm_page_t mtbl [PTBL_PAGES]; vm_page_t m; struct ptbl_buf *pbuf; unsigned int pidx; pte_t *ptbl; int i, j; int req; KASSERT((pdir[pdir_idx] == NULL), ("%s: valid ptbl entry exists!", __func__)); pbuf = ptbl_buf_alloc(); if (pbuf == NULL) panic("%s: couldn't alloc kernel virtual memory", __func__); ptbl = (pte_t *) pbuf->kva; for (i = 0; i < PTBL_PAGES; i++) { pidx = (PTBL_PAGES * pdir_idx) + i; req = VM_ALLOC_NOOBJ | VM_ALLOC_WIRED; while ((m = vm_page_alloc(NULL, pidx, req)) == NULL) { PMAP_UNLOCK(pmap); rw_wunlock(&pvh_global_lock); if (nosleep) { ptbl_free_pmap_ptbl(pmap, ptbl); for (j = 0; j < i; j++) vm_page_free(mtbl[j]); atomic_subtract_int(&vm_cnt.v_wire_count, i); return (NULL); } VM_WAIT; rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); } mtbl[i] = m; } /* Mapin allocated pages into kernel_pmap. */ mmu_booke_qenter(mmu, (vm_offset_t) ptbl, mtbl, PTBL_PAGES); /* Zero whole ptbl. */ bzero((caddr_t) ptbl, PTBL_PAGES * PAGE_SIZE); /* Add pbuf to the pmap ptbl bufs list. */ TAILQ_INSERT_TAIL(&pmap->pm_ptbl_list, pbuf, link); return (ptbl); } /* Free ptbl pages and invalidate pdir entry. */ static void ptbl_free(mmu_t mmu, pmap_t pmap, pte_t ** pdir, unsigned int pdir_idx) { pte_t *ptbl; vm_paddr_t pa; vm_offset_t va; vm_page_t m; int i; ptbl = pdir[pdir_idx]; KASSERT((ptbl != NULL), ("ptbl_free: null ptbl")); pdir[pdir_idx] = NULL; for (i = 0; i < PTBL_PAGES; i++) { va = ((vm_offset_t) ptbl + (i * PAGE_SIZE)); pa = pte_vatopa(mmu, kernel_pmap, va); m = PHYS_TO_VM_PAGE(pa); vm_page_free_zero(m); atomic_subtract_int(&vm_cnt.v_wire_count, 1); pmap_kremove(va); } ptbl_free_pmap_ptbl(pmap, ptbl); } /* * Decrement ptbl pages hold count and attempt to free ptbl pages. Called * when removing pte entry from ptbl. * * Return 1 if ptbl pages were freed. */ static int ptbl_unhold(mmu_t mmu, pmap_t pmap, vm_offset_t va) { pte_t *ptbl; vm_paddr_t pa; vm_page_t m; u_int pp2d_idx; pte_t **pdir; u_int pdir_idx; int i; pp2d_idx = PP2D_IDX(va); pdir_idx = PDIR_IDX(va); KASSERT((pmap != kernel_pmap), ("ptbl_unhold: unholding kernel ptbl!")); pdir = pmap->pm_pp2d[pp2d_idx]; ptbl = pdir[pdir_idx]; KASSERT(((vm_offset_t) ptbl >= VM_MIN_KERNEL_ADDRESS), ("ptbl_unhold: non kva ptbl")); /* decrement hold count */ for (i = 0; i < PTBL_PAGES; i++) { pa = pte_vatopa(mmu, kernel_pmap, (vm_offset_t) ptbl + (i * PAGE_SIZE)); m = PHYS_TO_VM_PAGE(pa); m->wire_count--; } /* * Free ptbl pages if there are no pte entries in this ptbl. * wire_count has the same value for all ptbl pages, so check the * last page. */ if (m->wire_count == 0) { /* A pair of indirect entries might point to this ptbl page */ #if 0 tlb_flush_entry(pmap, va & ~((2UL * PAGE_SIZE_1M) - 1), TLB_SIZE_1M, MAS6_SIND); tlb_flush_entry(pmap, (va & ~((2UL * PAGE_SIZE_1M) - 1)) | PAGE_SIZE_1M, TLB_SIZE_1M, MAS6_SIND); #endif ptbl_free(mmu, pmap, pdir, pdir_idx); pdir_unhold(mmu, pmap, pp2d_idx); return (1); } return (0); } /* * Increment hold count for ptbl pages. This routine is used when new pte * entry is being inserted into ptbl. */ static void ptbl_hold(mmu_t mmu, pmap_t pmap, pte_t ** pdir, unsigned int pdir_idx) { vm_paddr_t pa; pte_t *ptbl; vm_page_t m; int i; KASSERT((pmap != kernel_pmap), ("ptbl_hold: holding kernel ptbl!")); ptbl = pdir[pdir_idx]; KASSERT((ptbl != NULL), ("ptbl_hold: null ptbl")); for (i = 0; i < PTBL_PAGES; i++) { pa = pte_vatopa(mmu, kernel_pmap, (vm_offset_t) ptbl + (i * PAGE_SIZE)); m = PHYS_TO_VM_PAGE(pa); m->wire_count++; } } #else /* Initialize pool of kva ptbl buffers. */ static void ptbl_init(void) { int i; CTR3(KTR_PMAP, "%s: s (ptbl_bufs = 0x%08x size 0x%08x)", __func__, (uint32_t)ptbl_bufs, sizeof(struct ptbl_buf) * PTBL_BUFS); CTR3(KTR_PMAP, "%s: s (ptbl_buf_pool_vabase = 0x%08x size = 0x%08x)", __func__, ptbl_buf_pool_vabase, PTBL_BUFS * PTBL_PAGES * PAGE_SIZE); mtx_init(&ptbl_buf_freelist_lock, "ptbl bufs lock", NULL, MTX_DEF); TAILQ_INIT(&ptbl_buf_freelist); for (i = 0; i < PTBL_BUFS; i++) { ptbl_bufs[i].kva = ptbl_buf_pool_vabase + i * PTBL_PAGES * PAGE_SIZE; TAILQ_INSERT_TAIL(&ptbl_buf_freelist, &ptbl_bufs[i], link); } } /* Get a ptbl_buf from the freelist. */ static struct ptbl_buf * ptbl_buf_alloc(void) { struct ptbl_buf *buf; mtx_lock(&ptbl_buf_freelist_lock); buf = TAILQ_FIRST(&ptbl_buf_freelist); if (buf != NULL) TAILQ_REMOVE(&ptbl_buf_freelist, buf, link); mtx_unlock(&ptbl_buf_freelist_lock); CTR2(KTR_PMAP, "%s: buf = %p", __func__, buf); return (buf); } /* Return ptbl buff to free pool. */ static void ptbl_buf_free(struct ptbl_buf *buf) { CTR2(KTR_PMAP, "%s: buf = %p", __func__, buf); mtx_lock(&ptbl_buf_freelist_lock); TAILQ_INSERT_TAIL(&ptbl_buf_freelist, buf, link); mtx_unlock(&ptbl_buf_freelist_lock); } /* * Search the list of allocated ptbl bufs and find on list of allocated ptbls */ static void ptbl_free_pmap_ptbl(pmap_t pmap, pte_t *ptbl) { struct ptbl_buf *pbuf; CTR2(KTR_PMAP, "%s: ptbl = %p", __func__, ptbl); PMAP_LOCK_ASSERT(pmap, MA_OWNED); TAILQ_FOREACH(pbuf, &pmap->pm_ptbl_list, link) if (pbuf->kva == (vm_offset_t)ptbl) { /* Remove from pmap ptbl buf list. */ TAILQ_REMOVE(&pmap->pm_ptbl_list, pbuf, link); /* Free corresponding ptbl buf. */ ptbl_buf_free(pbuf); break; } } /* Allocate page table. */ static pte_t * ptbl_alloc(mmu_t mmu, pmap_t pmap, unsigned int pdir_idx, boolean_t nosleep) { vm_page_t mtbl[PTBL_PAGES]; vm_page_t m; struct ptbl_buf *pbuf; unsigned int pidx; pte_t *ptbl; int i, j; CTR4(KTR_PMAP, "%s: pmap = %p su = %d pdir_idx = %d", __func__, pmap, (pmap == kernel_pmap), pdir_idx); KASSERT((pdir_idx <= (VM_MAXUSER_ADDRESS / PDIR_SIZE)), ("ptbl_alloc: invalid pdir_idx")); KASSERT((pmap->pm_pdir[pdir_idx] == NULL), ("pte_alloc: valid ptbl entry exists!")); pbuf = ptbl_buf_alloc(); if (pbuf == NULL) panic("pte_alloc: couldn't alloc kernel virtual memory"); ptbl = (pte_t *)pbuf->kva; CTR2(KTR_PMAP, "%s: ptbl kva = %p", __func__, ptbl); for (i = 0; i < PTBL_PAGES; i++) { pidx = (PTBL_PAGES * pdir_idx) + i; while ((m = vm_page_alloc(NULL, pidx, VM_ALLOC_NOOBJ | VM_ALLOC_WIRED)) == NULL) { PMAP_UNLOCK(pmap); rw_wunlock(&pvh_global_lock); if (nosleep) { ptbl_free_pmap_ptbl(pmap, ptbl); for (j = 0; j < i; j++) vm_page_free(mtbl[j]); atomic_subtract_int(&vm_cnt.v_wire_count, i); return (NULL); } VM_WAIT; rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); } mtbl[i] = m; } /* Map allocated pages into kernel_pmap. */ mmu_booke_qenter(mmu, (vm_offset_t)ptbl, mtbl, PTBL_PAGES); /* Zero whole ptbl. */ bzero((caddr_t)ptbl, PTBL_PAGES * PAGE_SIZE); /* Add pbuf to the pmap ptbl bufs list. */ TAILQ_INSERT_TAIL(&pmap->pm_ptbl_list, pbuf, link); return (ptbl); } /* Free ptbl pages and invalidate pdir entry. */ static void ptbl_free(mmu_t mmu, pmap_t pmap, unsigned int pdir_idx) { pte_t *ptbl; vm_paddr_t pa; vm_offset_t va; vm_page_t m; int i; CTR4(KTR_PMAP, "%s: pmap = %p su = %d pdir_idx = %d", __func__, pmap, (pmap == kernel_pmap), pdir_idx); KASSERT((pdir_idx <= (VM_MAXUSER_ADDRESS / PDIR_SIZE)), ("ptbl_free: invalid pdir_idx")); ptbl = pmap->pm_pdir[pdir_idx]; CTR2(KTR_PMAP, "%s: ptbl = %p", __func__, ptbl); KASSERT((ptbl != NULL), ("ptbl_free: null ptbl")); /* * Invalidate the pdir entry as soon as possible, so that other CPUs * don't attempt to look up the page tables we are releasing. */ mtx_lock_spin(&tlbivax_mutex); tlb_miss_lock(); pmap->pm_pdir[pdir_idx] = NULL; tlb_miss_unlock(); mtx_unlock_spin(&tlbivax_mutex); for (i = 0; i < PTBL_PAGES; i++) { va = ((vm_offset_t)ptbl + (i * PAGE_SIZE)); pa = pte_vatopa(mmu, kernel_pmap, va); m = PHYS_TO_VM_PAGE(pa); vm_page_free_zero(m); atomic_subtract_int(&vm_cnt.v_wire_count, 1); mmu_booke_kremove(mmu, va); } ptbl_free_pmap_ptbl(pmap, ptbl); } /* * Decrement ptbl pages hold count and attempt to free ptbl pages. * Called when removing pte entry from ptbl. * * Return 1 if ptbl pages were freed. */ static int ptbl_unhold(mmu_t mmu, pmap_t pmap, unsigned int pdir_idx) { pte_t *ptbl; vm_paddr_t pa; vm_page_t m; int i; CTR4(KTR_PMAP, "%s: pmap = %p su = %d pdir_idx = %d", __func__, pmap, (pmap == kernel_pmap), pdir_idx); KASSERT((pdir_idx <= (VM_MAXUSER_ADDRESS / PDIR_SIZE)), ("ptbl_unhold: invalid pdir_idx")); KASSERT((pmap != kernel_pmap), ("ptbl_unhold: unholding kernel ptbl!")); ptbl = pmap->pm_pdir[pdir_idx]; //debugf("ptbl_unhold: ptbl = 0x%08x\n", (u_int32_t)ptbl); KASSERT(((vm_offset_t)ptbl >= VM_MIN_KERNEL_ADDRESS), ("ptbl_unhold: non kva ptbl")); /* decrement hold count */ for (i = 0; i < PTBL_PAGES; i++) { pa = pte_vatopa(mmu, kernel_pmap, (vm_offset_t)ptbl + (i * PAGE_SIZE)); m = PHYS_TO_VM_PAGE(pa); m->wire_count--; } /* * Free ptbl pages if there are no pte etries in this ptbl. * wire_count has the same value for all ptbl pages, so check the last * page. */ if (m->wire_count == 0) { ptbl_free(mmu, pmap, pdir_idx); //debugf("ptbl_unhold: e (freed ptbl)\n"); return (1); } return (0); } /* * Increment hold count for ptbl pages. This routine is used when a new pte * entry is being inserted into the ptbl. */ static void ptbl_hold(mmu_t mmu, pmap_t pmap, unsigned int pdir_idx) { vm_paddr_t pa; pte_t *ptbl; vm_page_t m; int i; CTR3(KTR_PMAP, "%s: pmap = %p pdir_idx = %d", __func__, pmap, pdir_idx); KASSERT((pdir_idx <= (VM_MAXUSER_ADDRESS / PDIR_SIZE)), ("ptbl_hold: invalid pdir_idx")); KASSERT((pmap != kernel_pmap), ("ptbl_hold: holding kernel ptbl!")); ptbl = pmap->pm_pdir[pdir_idx]; KASSERT((ptbl != NULL), ("ptbl_hold: null ptbl")); for (i = 0; i < PTBL_PAGES; i++) { pa = pte_vatopa(mmu, kernel_pmap, (vm_offset_t)ptbl + (i * PAGE_SIZE)); m = PHYS_TO_VM_PAGE(pa); m->wire_count++; } } #endif /* Allocate pv_entry structure. */ pv_entry_t pv_alloc(void) { pv_entry_t pv; pv_entry_count++; if (pv_entry_count > pv_entry_high_water) pagedaemon_wakeup(); pv = uma_zalloc(pvzone, M_NOWAIT); return (pv); } /* Free pv_entry structure. */ static __inline void pv_free(pv_entry_t pve) { pv_entry_count--; uma_zfree(pvzone, pve); } /* Allocate and initialize pv_entry structure. */ static void pv_insert(pmap_t pmap, vm_offset_t va, vm_page_t m) { pv_entry_t pve; //int su = (pmap == kernel_pmap); //debugf("pv_insert: s (su = %d pmap = 0x%08x va = 0x%08x m = 0x%08x)\n", su, // (u_int32_t)pmap, va, (u_int32_t)m); pve = pv_alloc(); if (pve == NULL) panic("pv_insert: no pv entries!"); pve->pv_pmap = pmap; pve->pv_va = va; /* add to pv_list */ PMAP_LOCK_ASSERT(pmap, MA_OWNED); rw_assert(&pvh_global_lock, RA_WLOCKED); TAILQ_INSERT_TAIL(&m->md.pv_list, pve, pv_link); //debugf("pv_insert: e\n"); } /* Destroy pv entry. */ static void pv_remove(pmap_t pmap, vm_offset_t va, vm_page_t m) { pv_entry_t pve; //int su = (pmap == kernel_pmap); //debugf("pv_remove: s (su = %d pmap = 0x%08x va = 0x%08x)\n", su, (u_int32_t)pmap, va); PMAP_LOCK_ASSERT(pmap, MA_OWNED); rw_assert(&pvh_global_lock, RA_WLOCKED); /* find pv entry */ TAILQ_FOREACH(pve, &m->md.pv_list, pv_link) { if ((pmap == pve->pv_pmap) && (va == pve->pv_va)) { /* remove from pv_list */ TAILQ_REMOVE(&m->md.pv_list, pve, pv_link); if (TAILQ_EMPTY(&m->md.pv_list)) vm_page_aflag_clear(m, PGA_WRITEABLE); /* free pv entry struct */ pv_free(pve); break; } } //debugf("pv_remove: e\n"); } #ifdef __powerpc64__ /* * Clean pte entry, try to free page table page if requested. * * Return 1 if ptbl pages were freed, otherwise return 0. */ static int pte_remove(mmu_t mmu, pmap_t pmap, vm_offset_t va, u_int8_t flags) { vm_page_t m; pte_t *pte; pte = pte_find(mmu, pmap, va); KASSERT(pte != NULL, ("%s: NULL pte", __func__)); if (!PTE_ISVALID(pte)) return (0); /* Get vm_page_t for mapped pte. */ m = PHYS_TO_VM_PAGE(PTE_PA(pte)); if (PTE_ISWIRED(pte)) pmap->pm_stats.wired_count--; /* Handle managed entry. */ if (PTE_ISMANAGED(pte)) { /* Handle modified pages. */ if (PTE_ISMODIFIED(pte)) vm_page_dirty(m); /* Referenced pages. */ if (PTE_ISREFERENCED(pte)) vm_page_aflag_set(m, PGA_REFERENCED); /* Remove pv_entry from pv_list. */ pv_remove(pmap, va, m); } mtx_lock_spin(&tlbivax_mutex); tlb_miss_lock(); tlb0_flush_entry(va); *pte = 0; tlb_miss_unlock(); mtx_unlock_spin(&tlbivax_mutex); pmap->pm_stats.resident_count--; if (flags & PTBL_UNHOLD) { return (ptbl_unhold(mmu, pmap, va)); } return (0); } /* * allocate a page of pointers to page directories, do not preallocate the * page tables */ static pte_t ** pdir_alloc(mmu_t mmu, pmap_t pmap, unsigned int pp2d_idx, bool nosleep) { vm_page_t mtbl [PDIR_PAGES]; vm_page_t m; struct ptbl_buf *pbuf; pte_t **pdir; unsigned int pidx; int i; int req; pbuf = ptbl_buf_alloc(); if (pbuf == NULL) panic("%s: couldn't alloc kernel virtual memory", __func__); /* Allocate pdir pages, this will sleep! */ for (i = 0; i < PDIR_PAGES; i++) { pidx = (PDIR_PAGES * pp2d_idx) + i; req = VM_ALLOC_NOOBJ | VM_ALLOC_WIRED; while ((m = vm_page_alloc(NULL, pidx, req)) == NULL) { PMAP_UNLOCK(pmap); VM_WAIT; PMAP_LOCK(pmap); } mtbl[i] = m; } /* Mapin allocated pages into kernel_pmap. */ pdir = (pte_t **) pbuf->kva; pmap_qenter((vm_offset_t) pdir, mtbl, PDIR_PAGES); /* Zero whole pdir. */ bzero((caddr_t) pdir, PDIR_PAGES * PAGE_SIZE); /* Add pdir to the pmap pdir bufs list. */ TAILQ_INSERT_TAIL(&pmap->pm_pdir_list, pbuf, link); return pdir; } /* * Insert PTE for a given page and virtual address. */ static int pte_enter(mmu_t mmu, pmap_t pmap, vm_page_t m, vm_offset_t va, uint32_t flags, boolean_t nosleep) { unsigned int pp2d_idx = PP2D_IDX(va); unsigned int pdir_idx = PDIR_IDX(va); unsigned int ptbl_idx = PTBL_IDX(va); pte_t *ptbl, *pte; pte_t **pdir; /* Get the page directory pointer. */ pdir = pmap->pm_pp2d[pp2d_idx]; if (pdir == NULL) pdir = pdir_alloc(mmu, pmap, pp2d_idx, nosleep); /* Get the page table pointer. */ ptbl = pdir[pdir_idx]; if (ptbl == NULL) { /* Allocate page table pages. */ ptbl = ptbl_alloc(mmu, pmap, pdir, pdir_idx, nosleep); if (ptbl == NULL) { KASSERT(nosleep, ("nosleep and NULL ptbl")); return (ENOMEM); } } else { /* * Check if there is valid mapping for requested va, if there * is, remove it. */ pte = &pdir[pdir_idx][ptbl_idx]; if (PTE_ISVALID(pte)) { pte_remove(mmu, pmap, va, PTBL_HOLD); } else { /* * pte is not used, increment hold count for ptbl * pages. */ if (pmap != kernel_pmap) ptbl_hold(mmu, pmap, pdir, pdir_idx); } } if (pdir[pdir_idx] == NULL) { if (pmap != kernel_pmap && pmap->pm_pp2d[pp2d_idx] != NULL) pdir_hold(mmu, pmap, pdir); pdir[pdir_idx] = ptbl; } if (pmap->pm_pp2d[pp2d_idx] == NULL) pmap->pm_pp2d[pp2d_idx] = pdir; /* * Insert pv_entry into pv_list for mapped page if part of managed * memory. */ if ((m->oflags & VPO_UNMANAGED) == 0) { flags |= PTE_MANAGED; /* Create and insert pv entry. */ pv_insert(pmap, va, m); } mtx_lock_spin(&tlbivax_mutex); tlb_miss_lock(); tlb0_flush_entry(va); pmap->pm_stats.resident_count++; pte = &pdir[pdir_idx][ptbl_idx]; *pte = PTE_RPN_FROM_PA(VM_PAGE_TO_PHYS(m)); *pte |= (PTE_VALID | flags); tlb_miss_unlock(); mtx_unlock_spin(&tlbivax_mutex); return (0); } /* Return the pa for the given pmap/va. */ static vm_paddr_t pte_vatopa(mmu_t mmu, pmap_t pmap, vm_offset_t va) { vm_paddr_t pa = 0; pte_t *pte; pte = pte_find(mmu, pmap, va); if ((pte != NULL) && PTE_ISVALID(pte)) pa = (PTE_PA(pte) | (va & PTE_PA_MASK)); return (pa); } /* allocate pte entries to manage (addr & mask) to (addr & mask) + size */ static void kernel_pte_alloc(vm_offset_t data_end, vm_offset_t addr, vm_offset_t pdir) { int i, j; vm_offset_t va; pte_t *pte; va = addr; /* Initialize kernel pdir */ for (i = 0; i < kernel_pdirs; i++) { kernel_pmap->pm_pp2d[i + PP2D_IDX(va)] = (pte_t **)(pdir + (i * PAGE_SIZE * PDIR_PAGES)); for (j = PDIR_IDX(va + (i * PAGE_SIZE * PDIR_NENTRIES * PTBL_NENTRIES)); j < PDIR_NENTRIES; j++) { kernel_pmap->pm_pp2d[i + PP2D_IDX(va)][j] = (pte_t *)(pdir + (kernel_pdirs * PAGE_SIZE * PDIR_PAGES) + (((i * PDIR_NENTRIES) + j) * PAGE_SIZE * PTBL_PAGES)); } } /* * Fill in PTEs covering kernel code and data. They are not required * for address translation, as this area is covered by static TLB1 * entries, but for pte_vatopa() to work correctly with kernel area * addresses. */ for (va = addr; va < data_end; va += PAGE_SIZE) { pte = &(kernel_pmap->pm_pp2d[PP2D_IDX(va)][PDIR_IDX(va)][PTBL_IDX(va)]); *pte = PTE_RPN_FROM_PA(kernload + (va - kernstart)); *pte |= PTE_M | PTE_SR | PTE_SW | PTE_SX | PTE_WIRED | PTE_VALID | PTE_PS_4KB; } } #else /* * Clean pte entry, try to free page table page if requested. * * Return 1 if ptbl pages were freed, otherwise return 0. */ static int pte_remove(mmu_t mmu, pmap_t pmap, vm_offset_t va, uint8_t flags) { unsigned int pdir_idx = PDIR_IDX(va); unsigned int ptbl_idx = PTBL_IDX(va); vm_page_t m; pte_t *ptbl; pte_t *pte; //int su = (pmap == kernel_pmap); //debugf("pte_remove: s (su = %d pmap = 0x%08x va = 0x%08x flags = %d)\n", // su, (u_int32_t)pmap, va, flags); ptbl = pmap->pm_pdir[pdir_idx]; KASSERT(ptbl, ("pte_remove: null ptbl")); pte = &ptbl[ptbl_idx]; if (pte == NULL || !PTE_ISVALID(pte)) return (0); if (PTE_ISWIRED(pte)) pmap->pm_stats.wired_count--; /* Get vm_page_t for mapped pte. */ m = PHYS_TO_VM_PAGE(PTE_PA(pte)); /* Handle managed entry. */ if (PTE_ISMANAGED(pte)) { if (PTE_ISMODIFIED(pte)) vm_page_dirty(m); if (PTE_ISREFERENCED(pte)) vm_page_aflag_set(m, PGA_REFERENCED); pv_remove(pmap, va, m); } else if (m->md.pv_tracked) { /* * Always pv_insert()/pv_remove() on MPC85XX, in case DPAA is * used. This is needed by the NCSW support code for fast * VA<->PA translation. */ pv_remove(pmap, va, m); if (TAILQ_EMPTY(&m->md.pv_list)) m->md.pv_tracked = false; } mtx_lock_spin(&tlbivax_mutex); tlb_miss_lock(); tlb0_flush_entry(va); *pte = 0; tlb_miss_unlock(); mtx_unlock_spin(&tlbivax_mutex); pmap->pm_stats.resident_count--; if (flags & PTBL_UNHOLD) { //debugf("pte_remove: e (unhold)\n"); return (ptbl_unhold(mmu, pmap, pdir_idx)); } //debugf("pte_remove: e\n"); return (0); } /* * Insert PTE for a given page and virtual address. */ static int pte_enter(mmu_t mmu, pmap_t pmap, vm_page_t m, vm_offset_t va, uint32_t flags, boolean_t nosleep) { unsigned int pdir_idx = PDIR_IDX(va); unsigned int ptbl_idx = PTBL_IDX(va); pte_t *ptbl, *pte; CTR4(KTR_PMAP, "%s: su = %d pmap = %p va = %p", __func__, pmap == kernel_pmap, pmap, va); /* Get the page table pointer. */ ptbl = pmap->pm_pdir[pdir_idx]; if (ptbl == NULL) { /* Allocate page table pages. */ ptbl = ptbl_alloc(mmu, pmap, pdir_idx, nosleep); if (ptbl == NULL) { KASSERT(nosleep, ("nosleep and NULL ptbl")); return (ENOMEM); } } else { /* * Check if there is valid mapping for requested * va, if there is, remove it. */ pte = &pmap->pm_pdir[pdir_idx][ptbl_idx]; if (PTE_ISVALID(pte)) { pte_remove(mmu, pmap, va, PTBL_HOLD); } else { /* * pte is not used, increment hold count * for ptbl pages. */ if (pmap != kernel_pmap) ptbl_hold(mmu, pmap, pdir_idx); } } /* * Insert pv_entry into pv_list for mapped page if part of managed * memory. */ if ((m->oflags & VPO_UNMANAGED) == 0) { flags |= PTE_MANAGED; /* Create and insert pv entry. */ pv_insert(pmap, va, m); } pmap->pm_stats.resident_count++; mtx_lock_spin(&tlbivax_mutex); tlb_miss_lock(); tlb0_flush_entry(va); if (pmap->pm_pdir[pdir_idx] == NULL) { /* * If we just allocated a new page table, hook it in * the pdir. */ pmap->pm_pdir[pdir_idx] = ptbl; } pte = &(pmap->pm_pdir[pdir_idx][ptbl_idx]); *pte = PTE_RPN_FROM_PA(VM_PAGE_TO_PHYS(m)); *pte |= (PTE_VALID | flags | PTE_PS_4KB); /* 4KB pages only */ tlb_miss_unlock(); mtx_unlock_spin(&tlbivax_mutex); return (0); } /* Return the pa for the given pmap/va. */ static vm_paddr_t pte_vatopa(mmu_t mmu, pmap_t pmap, vm_offset_t va) { vm_paddr_t pa = 0; pte_t *pte; pte = pte_find(mmu, pmap, va); if ((pte != NULL) && PTE_ISVALID(pte)) pa = (PTE_PA(pte) | (va & PTE_PA_MASK)); return (pa); } /* Get a pointer to a PTE in a page table. */ static pte_t * pte_find(mmu_t mmu, pmap_t pmap, vm_offset_t va) { unsigned int pdir_idx = PDIR_IDX(va); unsigned int ptbl_idx = PTBL_IDX(va); KASSERT((pmap != NULL), ("pte_find: invalid pmap")); if (pmap->pm_pdir[pdir_idx]) return (&(pmap->pm_pdir[pdir_idx][ptbl_idx])); return (NULL); } /* Set up kernel page tables. */ static void kernel_pte_alloc(vm_offset_t data_end, vm_offset_t addr, vm_offset_t pdir) { int i; vm_offset_t va; pte_t *pte; /* Initialize kernel pdir */ for (i = 0; i < kernel_ptbls; i++) kernel_pmap->pm_pdir[kptbl_min + i] = (pte_t *)(pdir + (i * PAGE_SIZE * PTBL_PAGES)); /* * Fill in PTEs covering kernel code and data. They are not required * for address translation, as this area is covered by static TLB1 * entries, but for pte_vatopa() to work correctly with kernel area * addresses. */ for (va = addr; va < data_end; va += PAGE_SIZE) { pte = &(kernel_pmap->pm_pdir[PDIR_IDX(va)][PTBL_IDX(va)]); *pte = PTE_RPN_FROM_PA(kernload + (va - kernstart)); *pte |= PTE_M | PTE_SR | PTE_SW | PTE_SX | PTE_WIRED | PTE_VALID | PTE_PS_4KB; } } #endif /**************************************************************************/ /* PMAP related */ /**************************************************************************/ /* * This is called during booke_init, before the system is really initialized. */ static void mmu_booke_bootstrap(mmu_t mmu, vm_offset_t start, vm_offset_t kernelend) { vm_paddr_t phys_kernelend; struct mem_region *mp, *mp1; int cnt, i, j; vm_paddr_t s, e, sz; vm_paddr_t physsz, hwphyssz; u_int phys_avail_count; vm_size_t kstack0_sz; vm_offset_t kernel_pdir, kstack0; vm_paddr_t kstack0_phys; void *dpcpu; debugf("mmu_booke_bootstrap: entered\n"); /* Set interesting system properties */ hw_direct_map = 0; #if defined(COMPAT_FREEBSD32) || !defined(__powerpc64__) elf32_nxstack = 1; #endif /* Initialize invalidation mutex */ mtx_init(&tlbivax_mutex, "tlbivax", NULL, MTX_SPIN); /* Read TLB0 size and associativity. */ tlb0_get_tlbconf(); /* * Align kernel start and end address (kernel image). * Note that kernel end does not necessarily relate to kernsize. * kernsize is the size of the kernel that is actually mapped. */ kernstart = trunc_page(start); data_start = round_page(kernelend); data_end = data_start; /* * Addresses of preloaded modules (like file systems) use * physical addresses. Make sure we relocate those into * virtual addresses. */ preload_addr_relocate = kernstart - kernload; /* Allocate the dynamic per-cpu area. */ dpcpu = (void *)data_end; data_end += DPCPU_SIZE; /* Allocate space for the message buffer. */ msgbufp = (struct msgbuf *)data_end; data_end += msgbufsize; debugf(" msgbufp at 0x%"PRI0ptrX" end = 0x%"PRI0ptrX"\n", (uintptr_t)msgbufp, data_end); data_end = round_page(data_end); /* Allocate space for ptbl_bufs. */ ptbl_bufs = (struct ptbl_buf *)data_end; data_end += sizeof(struct ptbl_buf) * PTBL_BUFS; debugf(" ptbl_bufs at 0x%"PRI0ptrX" end = 0x%"PRI0ptrX"\n", (uintptr_t)ptbl_bufs, data_end); data_end = round_page(data_end); /* Allocate PTE tables for kernel KVA. */ kernel_pdir = data_end; kernel_ptbls = howmany(VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS, PDIR_SIZE); #ifdef __powerpc64__ kernel_pdirs = howmany(kernel_ptbls, PDIR_NENTRIES); data_end += kernel_pdirs * PDIR_PAGES * PAGE_SIZE; #endif data_end += kernel_ptbls * PTBL_PAGES * PAGE_SIZE; debugf(" kernel ptbls: %d\n", kernel_ptbls); debugf(" kernel pdir at 0x%"PRI0ptrX" end = 0x%"PRI0ptrX"\n", kernel_pdir, data_end); debugf(" data_end: 0x%"PRI0ptrX"\n", data_end); if (data_end - kernstart > kernsize) { kernsize += tlb1_mapin_region(kernstart + kernsize, kernload + kernsize, (data_end - kernstart) - kernsize); } data_end = kernstart + kernsize; debugf(" updated data_end: 0x%"PRI0ptrX"\n", data_end); /* * Clear the structures - note we can only do it safely after the * possible additional TLB1 translations are in place (above) so that * all range up to the currently calculated 'data_end' is covered. */ dpcpu_init(dpcpu, 0); memset((void *)ptbl_bufs, 0, sizeof(struct ptbl_buf) * PTBL_SIZE); #ifdef __powerpc64__ memset((void *)kernel_pdir, 0, kernel_pdirs * PDIR_PAGES * PAGE_SIZE + kernel_ptbls * PTBL_PAGES * PAGE_SIZE); #else memset((void *)kernel_pdir, 0, kernel_ptbls * PTBL_PAGES * PAGE_SIZE); #endif /*******************************************************/ /* Set the start and end of kva. */ /*******************************************************/ virtual_avail = round_page(data_end); virtual_end = VM_MAX_KERNEL_ADDRESS; /* Allocate KVA space for page zero/copy operations. */ zero_page_va = virtual_avail; virtual_avail += PAGE_SIZE; copy_page_src_va = virtual_avail; virtual_avail += PAGE_SIZE; copy_page_dst_va = virtual_avail; virtual_avail += PAGE_SIZE; debugf("zero_page_va = 0x%08x\n", zero_page_va); debugf("copy_page_src_va = 0x%08x\n", copy_page_src_va); debugf("copy_page_dst_va = 0x%08x\n", copy_page_dst_va); /* Initialize page zero/copy mutexes. */ mtx_init(&zero_page_mutex, "mmu_booke_zero_page", NULL, MTX_DEF); mtx_init(©_page_mutex, "mmu_booke_copy_page", NULL, MTX_DEF); /* Allocate KVA space for ptbl bufs. */ ptbl_buf_pool_vabase = virtual_avail; virtual_avail += PTBL_BUFS * PTBL_PAGES * PAGE_SIZE; debugf("ptbl_buf_pool_vabase = 0x%08x end = 0x%08x\n", ptbl_buf_pool_vabase, virtual_avail); /* Calculate corresponding physical addresses for the kernel region. */ phys_kernelend = kernload + kernsize; debugf("kernel image and allocated data:\n"); debugf(" kernload = 0x%09llx\n", (uint64_t)kernload); debugf(" kernstart = 0x%08x\n", kernstart); debugf(" kernsize = 0x%08x\n", kernsize); if (sizeof(phys_avail) / sizeof(phys_avail[0]) < availmem_regions_sz) panic("mmu_booke_bootstrap: phys_avail too small"); /* * Remove kernel physical address range from avail regions list. Page * align all regions. Non-page aligned memory isn't very interesting * to us. Also, sort the entries for ascending addresses. */ /* Retrieve phys/avail mem regions */ mem_regions(&physmem_regions, &physmem_regions_sz, &availmem_regions, &availmem_regions_sz); sz = 0; cnt = availmem_regions_sz; debugf("processing avail regions:\n"); for (mp = availmem_regions; mp->mr_size; mp++) { s = mp->mr_start; e = mp->mr_start + mp->mr_size; debugf(" %09jx-%09jx -> ", (uintmax_t)s, (uintmax_t)e); /* Check whether this region holds all of the kernel. */ if (s < kernload && e > phys_kernelend) { availmem_regions[cnt].mr_start = phys_kernelend; availmem_regions[cnt++].mr_size = e - phys_kernelend; e = kernload; } /* Look whether this regions starts within the kernel. */ if (s >= kernload && s < phys_kernelend) { if (e <= phys_kernelend) goto empty; s = phys_kernelend; } /* Now look whether this region ends within the kernel. */ if (e > kernload && e <= phys_kernelend) { if (s >= kernload) goto empty; e = kernload; } /* Now page align the start and size of the region. */ s = round_page(s); e = trunc_page(e); if (e < s) e = s; sz = e - s; debugf("%09jx-%09jx = %jx\n", (uintmax_t)s, (uintmax_t)e, (uintmax_t)sz); /* Check whether some memory is left here. */ if (sz == 0) { empty: memmove(mp, mp + 1, (cnt - (mp - availmem_regions)) * sizeof(*mp)); cnt--; mp--; continue; } /* Do an insertion sort. */ for (mp1 = availmem_regions; mp1 < mp; mp1++) if (s < mp1->mr_start) break; if (mp1 < mp) { memmove(mp1 + 1, mp1, (char *)mp - (char *)mp1); mp1->mr_start = s; mp1->mr_size = sz; } else { mp->mr_start = s; mp->mr_size = sz; } } availmem_regions_sz = cnt; /*******************************************************/ /* Steal physical memory for kernel stack from the end */ /* of the first avail region */ /*******************************************************/ kstack0_sz = kstack_pages * PAGE_SIZE; kstack0_phys = availmem_regions[0].mr_start + availmem_regions[0].mr_size; kstack0_phys -= kstack0_sz; availmem_regions[0].mr_size -= kstack0_sz; /*******************************************************/ /* Fill in phys_avail table, based on availmem_regions */ /*******************************************************/ phys_avail_count = 0; physsz = 0; hwphyssz = 0; TUNABLE_ULONG_FETCH("hw.physmem", (u_long *) &hwphyssz); debugf("fill in phys_avail:\n"); for (i = 0, j = 0; i < availmem_regions_sz; i++, j += 2) { debugf(" region: 0x%jx - 0x%jx (0x%jx)\n", (uintmax_t)availmem_regions[i].mr_start, (uintmax_t)availmem_regions[i].mr_start + availmem_regions[i].mr_size, (uintmax_t)availmem_regions[i].mr_size); if (hwphyssz != 0 && (physsz + availmem_regions[i].mr_size) >= hwphyssz) { debugf(" hw.physmem adjust\n"); if (physsz < hwphyssz) { phys_avail[j] = availmem_regions[i].mr_start; phys_avail[j + 1] = availmem_regions[i].mr_start + hwphyssz - physsz; physsz = hwphyssz; phys_avail_count++; } break; } phys_avail[j] = availmem_regions[i].mr_start; phys_avail[j + 1] = availmem_regions[i].mr_start + availmem_regions[i].mr_size; phys_avail_count++; physsz += availmem_regions[i].mr_size; } physmem = btoc(physsz); /* Calculate the last available physical address. */ for (i = 0; phys_avail[i + 2] != 0; i += 2) ; Maxmem = powerpc_btop(phys_avail[i + 1]); debugf("Maxmem = 0x%08lx\n", Maxmem); debugf("phys_avail_count = %d\n", phys_avail_count); debugf("physsz = 0x%09jx physmem = %jd (0x%09jx)\n", (uintmax_t)physsz, (uintmax_t)physmem, (uintmax_t)physmem); /*******************************************************/ /* Initialize (statically allocated) kernel pmap. */ /*******************************************************/ PMAP_LOCK_INIT(kernel_pmap); #ifndef __powerpc64__ kptbl_min = VM_MIN_KERNEL_ADDRESS / PDIR_SIZE; #endif debugf("kernel_pmap = 0x%"PRI0ptrX"\n", (uintptr_t)kernel_pmap); kernel_pte_alloc(virtual_avail, kernstart, kernel_pdir); for (i = 0; i < MAXCPU; i++) { kernel_pmap->pm_tid[i] = TID_KERNEL; /* Initialize each CPU's tidbusy entry 0 with kernel_pmap */ tidbusy[i][TID_KERNEL] = kernel_pmap; } /* Mark kernel_pmap active on all CPUs */ CPU_FILL(&kernel_pmap->pm_active); /* * Initialize the global pv list lock. */ rw_init(&pvh_global_lock, "pmap pv global"); /*******************************************************/ /* Final setup */ /*******************************************************/ /* Enter kstack0 into kernel map, provide guard page */ kstack0 = virtual_avail + KSTACK_GUARD_PAGES * PAGE_SIZE; thread0.td_kstack = kstack0; thread0.td_kstack_pages = kstack_pages; debugf("kstack_sz = 0x%08x\n", kstack0_sz); debugf("kstack0_phys at 0x%09llx - 0x%09llx\n", kstack0_phys, kstack0_phys + kstack0_sz); debugf("kstack0 at 0x%"PRI0ptrX" - 0x%"PRI0ptrX"\n", kstack0, kstack0 + kstack0_sz); virtual_avail += KSTACK_GUARD_PAGES * PAGE_SIZE + kstack0_sz; for (i = 0; i < kstack_pages; i++) { mmu_booke_kenter(mmu, kstack0, kstack0_phys); kstack0 += PAGE_SIZE; kstack0_phys += PAGE_SIZE; } pmap_bootstrapped = 1; debugf("virtual_avail = %"PRI0ptrX"\n", virtual_avail); debugf("virtual_end = %"PRI0ptrX"\n", virtual_end); debugf("mmu_booke_bootstrap: exit\n"); } #ifdef SMP void tlb1_ap_prep(void) { tlb_entry_t *e, tmp; unsigned int i; /* Prepare TLB1 image for AP processors */ e = __boot_tlb1; for (i = 0; i < TLB1_ENTRIES; i++) { tlb1_read_entry(&tmp, i); if ((tmp.mas1 & MAS1_VALID) && (tmp.mas2 & _TLB_ENTRY_SHARED)) memcpy(e++, &tmp, sizeof(tmp)); } } void pmap_bootstrap_ap(volatile uint32_t *trcp __unused) { int i; /* * Finish TLB1 configuration: the BSP already set up its TLB1 and we * have the snapshot of its contents in the s/w __boot_tlb1[] table * created by tlb1_ap_prep(), so use these values directly to * (re)program AP's TLB1 hardware. * * Start at index 1 because index 0 has the kernel map. */ for (i = 1; i < TLB1_ENTRIES; i++) { if (__boot_tlb1[i].mas1 & MAS1_VALID) tlb1_write_entry(&__boot_tlb1[i], i); } set_mas4_defaults(); } #endif static void booke_pmap_init_qpages(void) { struct pcpu *pc; int i; CPU_FOREACH(i) { pc = pcpu_find(i); pc->pc_qmap_addr = kva_alloc(PAGE_SIZE); if (pc->pc_qmap_addr == 0) panic("pmap_init_qpages: unable to allocate KVA"); } } SYSINIT(qpages_init, SI_SUB_CPU, SI_ORDER_ANY, booke_pmap_init_qpages, NULL); /* * Get the physical page address for the given pmap/virtual address. */ static vm_paddr_t mmu_booke_extract(mmu_t mmu, pmap_t pmap, vm_offset_t va) { vm_paddr_t pa; PMAP_LOCK(pmap); pa = pte_vatopa(mmu, pmap, va); PMAP_UNLOCK(pmap); return (pa); } /* * Extract the physical page address associated with the given * kernel virtual address. */ static vm_paddr_t mmu_booke_kextract(mmu_t mmu, vm_offset_t va) { tlb_entry_t e; vm_paddr_t p; int i; p = pte_vatopa(mmu, kernel_pmap, va); if (p == 0) { /* Check TLB1 mappings */ for (i = 0; i < TLB1_ENTRIES; i++) { tlb1_read_entry(&e, i); if (!(e.mas1 & MAS1_VALID)) continue; if (va >= e.virt && va < e.virt + e.size) return (e.phys + (va - e.virt)); } } return (p); } /* * Initialize the pmap module. * Called by vm_init, to initialize any structures that the pmap * system needs to map virtual memory. */ static void mmu_booke_init(mmu_t mmu) { int shpgperproc = PMAP_SHPGPERPROC; /* * Initialize the address space (zone) for the pv entries. Set a * high water mark so that the system can recover from excessive * numbers of pv entries. */ pvzone = uma_zcreate("PV ENTRY", sizeof(struct pv_entry), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM | UMA_ZONE_NOFREE); TUNABLE_INT_FETCH("vm.pmap.shpgperproc", &shpgperproc); pv_entry_max = shpgperproc * maxproc + vm_cnt.v_page_count; TUNABLE_INT_FETCH("vm.pmap.pv_entries", &pv_entry_max); pv_entry_high_water = 9 * (pv_entry_max / 10); uma_zone_reserve_kva(pvzone, pv_entry_max); /* Pre-fill pvzone with initial number of pv entries. */ uma_prealloc(pvzone, PV_ENTRY_ZONE_MIN); /* Initialize ptbl allocation. */ ptbl_init(); } /* * Map a list of wired pages into kernel virtual address space. This is * intended for temporary mappings which do not need page modification or * references recorded. Existing mappings in the region are overwritten. */ static void mmu_booke_qenter(mmu_t mmu, vm_offset_t sva, vm_page_t *m, int count) { vm_offset_t va; va = sva; while (count-- > 0) { mmu_booke_kenter(mmu, va, VM_PAGE_TO_PHYS(*m)); va += PAGE_SIZE; m++; } } /* * Remove page mappings from kernel virtual address space. Intended for * temporary mappings entered by mmu_booke_qenter. */ static void mmu_booke_qremove(mmu_t mmu, vm_offset_t sva, int count) { vm_offset_t va; va = sva; while (count-- > 0) { mmu_booke_kremove(mmu, va); va += PAGE_SIZE; } } /* * Map a wired page into kernel virtual address space. */ static void mmu_booke_kenter(mmu_t mmu, vm_offset_t va, vm_paddr_t pa) { mmu_booke_kenter_attr(mmu, va, pa, VM_MEMATTR_DEFAULT); } static void mmu_booke_kenter_attr(mmu_t mmu, vm_offset_t va, vm_paddr_t pa, vm_memattr_t ma) { uint32_t flags; pte_t *pte; KASSERT(((va >= VM_MIN_KERNEL_ADDRESS) && (va <= VM_MAX_KERNEL_ADDRESS)), ("mmu_booke_kenter: invalid va")); flags = PTE_SR | PTE_SW | PTE_SX | PTE_WIRED | PTE_VALID; flags |= tlb_calc_wimg(pa, ma) << PTE_MAS2_SHIFT; flags |= PTE_PS_4KB; pte = pte_find(mmu, kernel_pmap, va); KASSERT((pte != NULL), ("mmu_booke_kenter: invalid va. NULL PTE")); mtx_lock_spin(&tlbivax_mutex); tlb_miss_lock(); if (PTE_ISVALID(pte)) { CTR1(KTR_PMAP, "%s: replacing entry!", __func__); /* Flush entry from TLB0 */ tlb0_flush_entry(va); } *pte = PTE_RPN_FROM_PA(pa) | flags; //debugf("mmu_booke_kenter: pdir_idx = %d ptbl_idx = %d va=0x%08x " // "pa=0x%08x rpn=0x%08x flags=0x%08x\n", // pdir_idx, ptbl_idx, va, pa, pte->rpn, pte->flags); /* Flush the real memory from the instruction cache. */ if ((flags & (PTE_I | PTE_G)) == 0) __syncicache((void *)va, PAGE_SIZE); tlb_miss_unlock(); mtx_unlock_spin(&tlbivax_mutex); } /* * Remove a page from kernel page table. */ static void mmu_booke_kremove(mmu_t mmu, vm_offset_t va) { pte_t *pte; CTR2(KTR_PMAP,"%s: s (va = 0x%08x)\n", __func__, va); KASSERT(((va >= VM_MIN_KERNEL_ADDRESS) && (va <= VM_MAX_KERNEL_ADDRESS)), ("mmu_booke_kremove: invalid va")); pte = pte_find(mmu, kernel_pmap, va); if (!PTE_ISVALID(pte)) { CTR1(KTR_PMAP, "%s: invalid pte", __func__); return; } mtx_lock_spin(&tlbivax_mutex); tlb_miss_lock(); /* Invalidate entry in TLB0, update PTE. */ tlb0_flush_entry(va); *pte = 0; tlb_miss_unlock(); mtx_unlock_spin(&tlbivax_mutex); } /* * Initialize pmap associated with process 0. */ static void mmu_booke_pinit0(mmu_t mmu, pmap_t pmap) { PMAP_LOCK_INIT(pmap); mmu_booke_pinit(mmu, pmap); PCPU_SET(curpmap, pmap); } /* * Initialize a preallocated and zeroed pmap structure, * such as one in a vmspace structure. */ static void mmu_booke_pinit(mmu_t mmu, pmap_t pmap) { int i; CTR4(KTR_PMAP, "%s: pmap = %p, proc %d '%s'", __func__, pmap, curthread->td_proc->p_pid, curthread->td_proc->p_comm); KASSERT((pmap != kernel_pmap), ("pmap_pinit: initializing kernel_pmap")); for (i = 0; i < MAXCPU; i++) pmap->pm_tid[i] = TID_NONE; CPU_ZERO(&kernel_pmap->pm_active); bzero(&pmap->pm_stats, sizeof(pmap->pm_stats)); #ifdef __powerpc64__ bzero(&pmap->pm_pp2d, sizeof(pte_t **) * PP2D_NENTRIES); TAILQ_INIT(&pmap->pm_pdir_list); #else bzero(&pmap->pm_pdir, sizeof(pte_t *) * PDIR_NENTRIES); #endif TAILQ_INIT(&pmap->pm_ptbl_list); } /* * Release any resources held by the given physical map. * Called when a pmap initialized by mmu_booke_pinit is being released. * Should only be called if the map contains no valid mappings. */ static void mmu_booke_release(mmu_t mmu, pmap_t pmap) { KASSERT(pmap->pm_stats.resident_count == 0, ("pmap_release: pmap resident count %ld != 0", pmap->pm_stats.resident_count)); } /* * Insert the given physical page at the specified virtual address in the * target physical map with the protection requested. If specified the page * will be wired down. */ static int mmu_booke_enter(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, u_int flags, int8_t psind) { int error; rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); error = mmu_booke_enter_locked(mmu, pmap, va, m, prot, flags, psind); PMAP_UNLOCK(pmap); rw_wunlock(&pvh_global_lock); return (error); } static int mmu_booke_enter_locked(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, u_int pmap_flags, int8_t psind __unused) { pte_t *pte; vm_paddr_t pa; uint32_t flags; int error, su, sync; pa = VM_PAGE_TO_PHYS(m); su = (pmap == kernel_pmap); sync = 0; //debugf("mmu_booke_enter_locked: s (pmap=0x%08x su=%d tid=%d m=0x%08x va=0x%08x " // "pa=0x%08x prot=0x%08x flags=%#x)\n", // (u_int32_t)pmap, su, pmap->pm_tid, // (u_int32_t)m, va, pa, prot, flags); if (su) { KASSERT(((va >= virtual_avail) && (va <= VM_MAX_KERNEL_ADDRESS)), ("mmu_booke_enter_locked: kernel pmap, non kernel va")); } else { KASSERT((va <= VM_MAXUSER_ADDRESS), ("mmu_booke_enter_locked: user pmap, non user va")); } if ((m->oflags & VPO_UNMANAGED) == 0 && !vm_page_xbusied(m)) VM_OBJECT_ASSERT_LOCKED(m->object); PMAP_LOCK_ASSERT(pmap, MA_OWNED); /* * If there is an existing mapping, and the physical address has not * changed, must be protection or wiring change. */ if (((pte = pte_find(mmu, pmap, va)) != NULL) && (PTE_ISVALID(pte)) && (PTE_PA(pte) == pa)) { /* * Before actually updating pte->flags we calculate and * prepare its new value in a helper var. */ flags = *pte; flags &= ~(PTE_UW | PTE_UX | PTE_SW | PTE_SX | PTE_MODIFIED); /* Wiring change, just update stats. */ if ((pmap_flags & PMAP_ENTER_WIRED) != 0) { if (!PTE_ISWIRED(pte)) { flags |= PTE_WIRED; pmap->pm_stats.wired_count++; } } else { if (PTE_ISWIRED(pte)) { flags &= ~PTE_WIRED; pmap->pm_stats.wired_count--; } } if (prot & VM_PROT_WRITE) { /* Add write permissions. */ flags |= PTE_SW; if (!su) flags |= PTE_UW; if ((flags & PTE_MANAGED) != 0) vm_page_aflag_set(m, PGA_WRITEABLE); } else { /* Handle modified pages, sense modify status. */ /* * The PTE_MODIFIED flag could be set by underlying * TLB misses since we last read it (above), possibly * other CPUs could update it so we check in the PTE * directly rather than rely on that saved local flags * copy. */ if (PTE_ISMODIFIED(pte)) vm_page_dirty(m); } if (prot & VM_PROT_EXECUTE) { flags |= PTE_SX; if (!su) flags |= PTE_UX; /* * Check existing flags for execute permissions: if we * are turning execute permissions on, icache should * be flushed. */ if ((*pte & (PTE_UX | PTE_SX)) == 0) sync++; } flags &= ~PTE_REFERENCED; /* * The new flags value is all calculated -- only now actually * update the PTE. */ mtx_lock_spin(&tlbivax_mutex); tlb_miss_lock(); tlb0_flush_entry(va); *pte &= ~PTE_FLAGS_MASK; *pte |= flags; tlb_miss_unlock(); mtx_unlock_spin(&tlbivax_mutex); } else { /* * If there is an existing mapping, but it's for a different * physical address, pte_enter() will delete the old mapping. */ //if ((pte != NULL) && PTE_ISVALID(pte)) // debugf("mmu_booke_enter_locked: replace\n"); //else // debugf("mmu_booke_enter_locked: new\n"); /* Now set up the flags and install the new mapping. */ flags = (PTE_SR | PTE_VALID); flags |= PTE_M; if (!su) flags |= PTE_UR; if (prot & VM_PROT_WRITE) { flags |= PTE_SW; if (!su) flags |= PTE_UW; if ((m->oflags & VPO_UNMANAGED) == 0) vm_page_aflag_set(m, PGA_WRITEABLE); } if (prot & VM_PROT_EXECUTE) { flags |= PTE_SX; if (!su) flags |= PTE_UX; } /* If its wired update stats. */ if ((pmap_flags & PMAP_ENTER_WIRED) != 0) flags |= PTE_WIRED; error = pte_enter(mmu, pmap, m, va, flags, (pmap_flags & PMAP_ENTER_NOSLEEP) != 0); if (error != 0) return (KERN_RESOURCE_SHORTAGE); if ((flags & PMAP_ENTER_WIRED) != 0) pmap->pm_stats.wired_count++; /* Flush the real memory from the instruction cache. */ if (prot & VM_PROT_EXECUTE) sync++; } if (sync && (su || pmap == PCPU_GET(curpmap))) { __syncicache((void *)va, PAGE_SIZE); sync = 0; } return (KERN_SUCCESS); } /* * Maps a sequence of resident pages belonging to the same object. * The sequence begins with the given page m_start. This page is * mapped at the given virtual address start. Each subsequent page is * mapped at a virtual address that is offset from start by the same * amount as the page is offset from m_start within the object. The * last page in the sequence is the page with the largest offset from * m_start that can be mapped at a virtual address less than the given * virtual address end. Not every virtual page between start and end * is mapped; only those for which a resident page exists with the * corresponding offset from m_start are mapped. */ static void mmu_booke_enter_object(mmu_t mmu, pmap_t pmap, vm_offset_t start, vm_offset_t end, vm_page_t m_start, vm_prot_t prot) { vm_page_t m; vm_pindex_t diff, psize; VM_OBJECT_ASSERT_LOCKED(m_start->object); psize = atop(end - start); m = m_start; rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); while (m != NULL && (diff = m->pindex - m_start->pindex) < psize) { mmu_booke_enter_locked(mmu, pmap, start + ptoa(diff), m, prot & (VM_PROT_READ | VM_PROT_EXECUTE), PMAP_ENTER_NOSLEEP, 0); m = TAILQ_NEXT(m, listq); } rw_wunlock(&pvh_global_lock); PMAP_UNLOCK(pmap); } static void mmu_booke_enter_quick(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot) { rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); mmu_booke_enter_locked(mmu, pmap, va, m, prot & (VM_PROT_READ | VM_PROT_EXECUTE), PMAP_ENTER_NOSLEEP, 0); rw_wunlock(&pvh_global_lock); PMAP_UNLOCK(pmap); } /* * Remove the given range of addresses from the specified map. * * It is assumed that the start and end are properly rounded to the page size. */ static void mmu_booke_remove(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_offset_t endva) { pte_t *pte; uint8_t hold_flag; int su = (pmap == kernel_pmap); //debugf("mmu_booke_remove: s (su = %d pmap=0x%08x tid=%d va=0x%08x endva=0x%08x)\n", // su, (u_int32_t)pmap, pmap->pm_tid, va, endva); if (su) { KASSERT(((va >= virtual_avail) && (va <= VM_MAX_KERNEL_ADDRESS)), ("mmu_booke_remove: kernel pmap, non kernel va")); } else { KASSERT((va <= VM_MAXUSER_ADDRESS), ("mmu_booke_remove: user pmap, non user va")); } if (PMAP_REMOVE_DONE(pmap)) { //debugf("mmu_booke_remove: e (empty)\n"); return; } hold_flag = PTBL_HOLD_FLAG(pmap); //debugf("mmu_booke_remove: hold_flag = %d\n", hold_flag); rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); for (; va < endva; va += PAGE_SIZE) { pte = pte_find(mmu, pmap, va); if ((pte != NULL) && PTE_ISVALID(pte)) pte_remove(mmu, pmap, va, hold_flag); } PMAP_UNLOCK(pmap); rw_wunlock(&pvh_global_lock); //debugf("mmu_booke_remove: e\n"); } /* * Remove physical page from all pmaps in which it resides. */ static void mmu_booke_remove_all(mmu_t mmu, vm_page_t m) { pv_entry_t pv, pvn; uint8_t hold_flag; rw_wlock(&pvh_global_lock); for (pv = TAILQ_FIRST(&m->md.pv_list); pv != NULL; pv = pvn) { pvn = TAILQ_NEXT(pv, pv_link); PMAP_LOCK(pv->pv_pmap); hold_flag = PTBL_HOLD_FLAG(pv->pv_pmap); pte_remove(mmu, pv->pv_pmap, pv->pv_va, hold_flag); PMAP_UNLOCK(pv->pv_pmap); } vm_page_aflag_clear(m, PGA_WRITEABLE); rw_wunlock(&pvh_global_lock); } /* * Map a range of physical addresses into kernel virtual address space. */ static vm_offset_t mmu_booke_map(mmu_t mmu, vm_offset_t *virt, vm_paddr_t pa_start, vm_paddr_t pa_end, int prot) { vm_offset_t sva = *virt; vm_offset_t va = sva; //debugf("mmu_booke_map: s (sva = 0x%08x pa_start = 0x%08x pa_end = 0x%08x)\n", // sva, pa_start, pa_end); while (pa_start < pa_end) { mmu_booke_kenter(mmu, va, pa_start); va += PAGE_SIZE; pa_start += PAGE_SIZE; } *virt = va; //debugf("mmu_booke_map: e (va = 0x%08x)\n", va); return (sva); } /* * The pmap must be activated before it's address space can be accessed in any * way. */ static void mmu_booke_activate(mmu_t mmu, struct thread *td) { pmap_t pmap; u_int cpuid; pmap = &td->td_proc->p_vmspace->vm_pmap; CTR5(KTR_PMAP, "%s: s (td = %p, proc = '%s', id = %d, pmap = 0x%08x)", __func__, td, td->td_proc->p_comm, td->td_proc->p_pid, pmap); KASSERT((pmap != kernel_pmap), ("mmu_booke_activate: kernel_pmap!")); sched_pin(); cpuid = PCPU_GET(cpuid); CPU_SET_ATOMIC(cpuid, &pmap->pm_active); PCPU_SET(curpmap, pmap); if (pmap->pm_tid[cpuid] == TID_NONE) tid_alloc(pmap); /* Load PID0 register with pmap tid value. */ mtspr(SPR_PID0, pmap->pm_tid[cpuid]); __asm __volatile("isync"); mtspr(SPR_DBCR0, td->td_pcb->pcb_cpu.booke.dbcr0); sched_unpin(); CTR3(KTR_PMAP, "%s: e (tid = %d for '%s')", __func__, pmap->pm_tid[PCPU_GET(cpuid)], td->td_proc->p_comm); } /* * Deactivate the specified process's address space. */ static void mmu_booke_deactivate(mmu_t mmu, struct thread *td) { pmap_t pmap; pmap = &td->td_proc->p_vmspace->vm_pmap; CTR5(KTR_PMAP, "%s: td=%p, proc = '%s', id = %d, pmap = 0x%08x", __func__, td, td->td_proc->p_comm, td->td_proc->p_pid, pmap); td->td_pcb->pcb_cpu.booke.dbcr0 = mfspr(SPR_DBCR0); CPU_CLR_ATOMIC(PCPU_GET(cpuid), &pmap->pm_active); PCPU_SET(curpmap, NULL); } /* * Copy the range specified by src_addr/len * from the source map to the range dst_addr/len * in the destination map. * * This routine is only advisory and need not do anything. */ static void mmu_booke_copy(mmu_t mmu, pmap_t dst_pmap, pmap_t src_pmap, vm_offset_t dst_addr, vm_size_t len, vm_offset_t src_addr) { } /* * Set the physical protection on the specified range of this map as requested. */ static void mmu_booke_protect(mmu_t mmu, pmap_t pmap, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot) { vm_offset_t va; vm_page_t m; pte_t *pte; if ((prot & VM_PROT_READ) == VM_PROT_NONE) { mmu_booke_remove(mmu, pmap, sva, eva); return; } if (prot & VM_PROT_WRITE) return; PMAP_LOCK(pmap); for (va = sva; va < eva; va += PAGE_SIZE) { if ((pte = pte_find(mmu, pmap, va)) != NULL) { if (PTE_ISVALID(pte)) { m = PHYS_TO_VM_PAGE(PTE_PA(pte)); mtx_lock_spin(&tlbivax_mutex); tlb_miss_lock(); /* Handle modified pages. */ if (PTE_ISMODIFIED(pte) && PTE_ISMANAGED(pte)) vm_page_dirty(m); tlb0_flush_entry(va); *pte &= ~(PTE_UW | PTE_SW | PTE_MODIFIED); tlb_miss_unlock(); mtx_unlock_spin(&tlbivax_mutex); } } } PMAP_UNLOCK(pmap); } /* * Clear the write and modified bits in each of the given page's mappings. */ static void mmu_booke_remove_write(mmu_t mmu, vm_page_t m) { pv_entry_t pv; pte_t *pte; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("mmu_booke_remove_write: page %p is not managed", m)); /* * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * set by another thread while the object is locked. Thus, * if PGA_WRITEABLE is clear, no page table entries need updating. */ VM_OBJECT_ASSERT_WLOCKED(m->object); if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return; rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_link) { PMAP_LOCK(pv->pv_pmap); if ((pte = pte_find(mmu, pv->pv_pmap, pv->pv_va)) != NULL) { if (PTE_ISVALID(pte)) { m = PHYS_TO_VM_PAGE(PTE_PA(pte)); mtx_lock_spin(&tlbivax_mutex); tlb_miss_lock(); /* Handle modified pages. */ if (PTE_ISMODIFIED(pte)) vm_page_dirty(m); /* Flush mapping from TLB0. */ *pte &= ~(PTE_UW | PTE_SW | PTE_MODIFIED); tlb_miss_unlock(); mtx_unlock_spin(&tlbivax_mutex); } } PMAP_UNLOCK(pv->pv_pmap); } vm_page_aflag_clear(m, PGA_WRITEABLE); rw_wunlock(&pvh_global_lock); } static void mmu_booke_sync_icache(mmu_t mmu, pmap_t pm, vm_offset_t va, vm_size_t sz) { pte_t *pte; pmap_t pmap; vm_page_t m; vm_offset_t addr; vm_paddr_t pa = 0; int active, valid; va = trunc_page(va); sz = round_page(sz); rw_wlock(&pvh_global_lock); pmap = PCPU_GET(curpmap); active = (pm == kernel_pmap || pm == pmap) ? 1 : 0; while (sz > 0) { PMAP_LOCK(pm); pte = pte_find(mmu, pm, va); valid = (pte != NULL && PTE_ISVALID(pte)) ? 1 : 0; if (valid) pa = PTE_PA(pte); PMAP_UNLOCK(pm); if (valid) { if (!active) { /* Create a mapping in the active pmap. */ addr = 0; m = PHYS_TO_VM_PAGE(pa); PMAP_LOCK(pmap); pte_enter(mmu, pmap, m, addr, PTE_SR | PTE_VALID | PTE_UR, FALSE); __syncicache((void *)addr, PAGE_SIZE); pte_remove(mmu, pmap, addr, PTBL_UNHOLD); PMAP_UNLOCK(pmap); } else __syncicache((void *)va, PAGE_SIZE); } va += PAGE_SIZE; sz -= PAGE_SIZE; } rw_wunlock(&pvh_global_lock); } /* * Atomically extract and hold the physical page with the given * pmap and virtual address pair if that mapping permits the given * protection. */ static vm_page_t mmu_booke_extract_and_hold(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_prot_t prot) { pte_t *pte; vm_page_t m; uint32_t pte_wbit; vm_paddr_t pa; m = NULL; pa = 0; PMAP_LOCK(pmap); retry: pte = pte_find(mmu, pmap, va); if ((pte != NULL) && PTE_ISVALID(pte)) { if (pmap == kernel_pmap) pte_wbit = PTE_SW; else pte_wbit = PTE_UW; if ((*pte & pte_wbit) || ((prot & VM_PROT_WRITE) == 0)) { if (vm_page_pa_tryrelock(pmap, PTE_PA(pte), &pa)) goto retry; m = PHYS_TO_VM_PAGE(PTE_PA(pte)); vm_page_hold(m); } } PA_UNLOCK_COND(pa); PMAP_UNLOCK(pmap); return (m); } /* * Initialize a vm_page's machine-dependent fields. */ static void mmu_booke_page_init(mmu_t mmu, vm_page_t m) { m->md.pv_tracked = 0; TAILQ_INIT(&m->md.pv_list); } /* * mmu_booke_zero_page_area zeros the specified hardware page by * mapping it into virtual memory and using bzero to clear * its contents. * * off and size must reside within a single page. */ static void mmu_booke_zero_page_area(mmu_t mmu, vm_page_t m, int off, int size) { vm_offset_t va; /* XXX KASSERT off and size are within a single page? */ mtx_lock(&zero_page_mutex); va = zero_page_va; mmu_booke_kenter(mmu, va, VM_PAGE_TO_PHYS(m)); bzero((caddr_t)va + off, size); mmu_booke_kremove(mmu, va); mtx_unlock(&zero_page_mutex); } /* * mmu_booke_zero_page zeros the specified hardware page. */ static void mmu_booke_zero_page(mmu_t mmu, vm_page_t m) { vm_offset_t off, va; mtx_lock(&zero_page_mutex); va = zero_page_va; mmu_booke_kenter(mmu, va, VM_PAGE_TO_PHYS(m)); for (off = 0; off < PAGE_SIZE; off += cacheline_size) __asm __volatile("dcbz 0,%0" :: "r"(va + off)); mmu_booke_kremove(mmu, va); mtx_unlock(&zero_page_mutex); } /* * mmu_booke_copy_page copies the specified (machine independent) page by * mapping the page into virtual memory and using memcopy to copy the page, * one machine dependent page at a time. */ static void mmu_booke_copy_page(mmu_t mmu, vm_page_t sm, vm_page_t dm) { vm_offset_t sva, dva; sva = copy_page_src_va; dva = copy_page_dst_va; mtx_lock(©_page_mutex); mmu_booke_kenter(mmu, sva, VM_PAGE_TO_PHYS(sm)); mmu_booke_kenter(mmu, dva, VM_PAGE_TO_PHYS(dm)); memcpy((caddr_t)dva, (caddr_t)sva, PAGE_SIZE); mmu_booke_kremove(mmu, dva); mmu_booke_kremove(mmu, sva); mtx_unlock(©_page_mutex); } static inline void mmu_booke_copy_pages(mmu_t mmu, vm_page_t *ma, vm_offset_t a_offset, vm_page_t *mb, vm_offset_t b_offset, int xfersize) { void *a_cp, *b_cp; vm_offset_t a_pg_offset, b_pg_offset; int cnt; mtx_lock(©_page_mutex); while (xfersize > 0) { a_pg_offset = a_offset & PAGE_MASK; cnt = min(xfersize, PAGE_SIZE - a_pg_offset); mmu_booke_kenter(mmu, copy_page_src_va, VM_PAGE_TO_PHYS(ma[a_offset >> PAGE_SHIFT])); a_cp = (char *)copy_page_src_va + a_pg_offset; b_pg_offset = b_offset & PAGE_MASK; cnt = min(cnt, PAGE_SIZE - b_pg_offset); mmu_booke_kenter(mmu, copy_page_dst_va, VM_PAGE_TO_PHYS(mb[b_offset >> PAGE_SHIFT])); b_cp = (char *)copy_page_dst_va + b_pg_offset; bcopy(a_cp, b_cp, cnt); mmu_booke_kremove(mmu, copy_page_dst_va); mmu_booke_kremove(mmu, copy_page_src_va); a_offset += cnt; b_offset += cnt; xfersize -= cnt; } mtx_unlock(©_page_mutex); } static vm_offset_t mmu_booke_quick_enter_page(mmu_t mmu, vm_page_t m) { vm_paddr_t paddr; vm_offset_t qaddr; uint32_t flags; pte_t *pte; paddr = VM_PAGE_TO_PHYS(m); flags = PTE_SR | PTE_SW | PTE_SX | PTE_WIRED | PTE_VALID; flags |= tlb_calc_wimg(paddr, pmap_page_get_memattr(m)) << PTE_MAS2_SHIFT; flags |= PTE_PS_4KB; critical_enter(); qaddr = PCPU_GET(qmap_addr); pte = pte_find(mmu, kernel_pmap, qaddr); KASSERT(*pte == 0, ("mmu_booke_quick_enter_page: PTE busy")); /* * XXX: tlbivax is broadcast to other cores, but qaddr should * not be present in other TLBs. Is there a better instruction * sequence to use? Or just forget it & use mmu_booke_kenter()... */ __asm __volatile("tlbivax 0, %0" :: "r"(qaddr & MAS2_EPN_MASK)); __asm __volatile("isync; msync"); *pte = PTE_RPN_FROM_PA(paddr) | flags; /* Flush the real memory from the instruction cache. */ if ((flags & (PTE_I | PTE_G)) == 0) __syncicache((void *)qaddr, PAGE_SIZE); return (qaddr); } static void mmu_booke_quick_remove_page(mmu_t mmu, vm_offset_t addr) { pte_t *pte; pte = pte_find(mmu, kernel_pmap, addr); KASSERT(PCPU_GET(qmap_addr) == addr, ("mmu_booke_quick_remove_page: invalid address")); KASSERT(*pte != 0, ("mmu_booke_quick_remove_page: PTE not in use")); *pte = 0; critical_exit(); } /* * Return whether or not the specified physical page was modified * in any of physical maps. */ static boolean_t mmu_booke_is_modified(mmu_t mmu, vm_page_t m) { pte_t *pte; pv_entry_t pv; boolean_t rv; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("mmu_booke_is_modified: page %p is not managed", m)); rv = FALSE; /* * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * concurrently set while the object is locked. Thus, if PGA_WRITEABLE * is clear, no PTEs can be modified. */ VM_OBJECT_ASSERT_WLOCKED(m->object); if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return (rv); rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_link) { PMAP_LOCK(pv->pv_pmap); if ((pte = pte_find(mmu, pv->pv_pmap, pv->pv_va)) != NULL && PTE_ISVALID(pte)) { if (PTE_ISMODIFIED(pte)) rv = TRUE; } PMAP_UNLOCK(pv->pv_pmap); if (rv) break; } rw_wunlock(&pvh_global_lock); return (rv); } /* * Return whether or not the specified virtual address is eligible * for prefault. */ static boolean_t mmu_booke_is_prefaultable(mmu_t mmu, pmap_t pmap, vm_offset_t addr) { return (FALSE); } /* * Return whether or not the specified physical page was referenced * in any physical maps. */ static boolean_t mmu_booke_is_referenced(mmu_t mmu, vm_page_t m) { pte_t *pte; pv_entry_t pv; boolean_t rv; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("mmu_booke_is_referenced: page %p is not managed", m)); rv = FALSE; rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_link) { PMAP_LOCK(pv->pv_pmap); if ((pte = pte_find(mmu, pv->pv_pmap, pv->pv_va)) != NULL && PTE_ISVALID(pte)) { if (PTE_ISREFERENCED(pte)) rv = TRUE; } PMAP_UNLOCK(pv->pv_pmap); if (rv) break; } rw_wunlock(&pvh_global_lock); return (rv); } /* * Clear the modify bits on the specified physical page. */ static void mmu_booke_clear_modify(mmu_t mmu, vm_page_t m) { pte_t *pte; pv_entry_t pv; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("mmu_booke_clear_modify: page %p is not managed", m)); VM_OBJECT_ASSERT_WLOCKED(m->object); KASSERT(!vm_page_xbusied(m), ("mmu_booke_clear_modify: page %p is exclusive busied", m)); /* * If the page is not PG_AWRITEABLE, then no PTEs can be modified. * If the object containing the page is locked and the page is not * exclusive busied, then PG_AWRITEABLE cannot be concurrently set. */ if ((m->aflags & PGA_WRITEABLE) == 0) return; rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_link) { PMAP_LOCK(pv->pv_pmap); if ((pte = pte_find(mmu, pv->pv_pmap, pv->pv_va)) != NULL && PTE_ISVALID(pte)) { mtx_lock_spin(&tlbivax_mutex); tlb_miss_lock(); if (*pte & (PTE_SW | PTE_UW | PTE_MODIFIED)) { tlb0_flush_entry(pv->pv_va); *pte &= ~(PTE_SW | PTE_UW | PTE_MODIFIED | PTE_REFERENCED); } tlb_miss_unlock(); mtx_unlock_spin(&tlbivax_mutex); } PMAP_UNLOCK(pv->pv_pmap); } rw_wunlock(&pvh_global_lock); } /* * Return a count of reference bits for a page, clearing those bits. * It is not necessary for every reference bit to be cleared, but it * is necessary that 0 only be returned when there are truly no * reference bits set. * * As an optimization, update the page's dirty field if a modified bit is * found while counting reference bits. This opportunistic update can be * performed at low cost and can eliminate the need for some future calls * to pmap_is_modified(). However, since this function stops after * finding PMAP_TS_REFERENCED_MAX reference bits, it may not detect some * dirty pages. Those dirty pages will only be detected by a future call * to pmap_is_modified(). */ static int mmu_booke_ts_referenced(mmu_t mmu, vm_page_t m) { pte_t *pte; pv_entry_t pv; int count; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("mmu_booke_ts_referenced: page %p is not managed", m)); count = 0; rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_link) { PMAP_LOCK(pv->pv_pmap); if ((pte = pte_find(mmu, pv->pv_pmap, pv->pv_va)) != NULL && PTE_ISVALID(pte)) { if (PTE_ISMODIFIED(pte)) vm_page_dirty(m); if (PTE_ISREFERENCED(pte)) { mtx_lock_spin(&tlbivax_mutex); tlb_miss_lock(); tlb0_flush_entry(pv->pv_va); *pte &= ~PTE_REFERENCED; tlb_miss_unlock(); mtx_unlock_spin(&tlbivax_mutex); if (++count >= PMAP_TS_REFERENCED_MAX) { PMAP_UNLOCK(pv->pv_pmap); break; } } } PMAP_UNLOCK(pv->pv_pmap); } rw_wunlock(&pvh_global_lock); return (count); } /* * Clear the wired attribute from the mappings for the specified range of * addresses in the given pmap. Every valid mapping within that range must * have the wired attribute set. In contrast, invalid mappings cannot have * the wired attribute set, so they are ignored. * * The wired attribute of the page table entry is not a hardware feature, so * there is no need to invalidate any TLB entries. */ static void mmu_booke_unwire(mmu_t mmu, pmap_t pmap, vm_offset_t sva, vm_offset_t eva) { vm_offset_t va; pte_t *pte; PMAP_LOCK(pmap); for (va = sva; va < eva; va += PAGE_SIZE) { if ((pte = pte_find(mmu, pmap, va)) != NULL && PTE_ISVALID(pte)) { if (!PTE_ISWIRED(pte)) panic("mmu_booke_unwire: pte %p isn't wired", pte); *pte &= ~PTE_WIRED; pmap->pm_stats.wired_count--; } } PMAP_UNLOCK(pmap); } /* * Return true if the pmap's pv is one of the first 16 pvs linked to from this * page. This count may be changed upwards or downwards in the future; it is * only necessary that true be returned for a small subset of pmaps for proper * page aging. */ static boolean_t mmu_booke_page_exists_quick(mmu_t mmu, pmap_t pmap, vm_page_t m) { pv_entry_t pv; int loops; boolean_t rv; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("mmu_booke_page_exists_quick: page %p is not managed", m)); loops = 0; rv = FALSE; rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_link) { if (pv->pv_pmap == pmap) { rv = TRUE; break; } if (++loops >= 16) break; } rw_wunlock(&pvh_global_lock); return (rv); } /* * Return the number of managed mappings to the given physical page that are * wired. */ static int mmu_booke_page_wired_mappings(mmu_t mmu, vm_page_t m) { pv_entry_t pv; pte_t *pte; int count = 0; if ((m->oflags & VPO_UNMANAGED) != 0) return (count); rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_link) { PMAP_LOCK(pv->pv_pmap); if ((pte = pte_find(mmu, pv->pv_pmap, pv->pv_va)) != NULL) if (PTE_ISVALID(pte) && PTE_ISWIRED(pte)) count++; PMAP_UNLOCK(pv->pv_pmap); } rw_wunlock(&pvh_global_lock); return (count); } static int mmu_booke_dev_direct_mapped(mmu_t mmu, vm_paddr_t pa, vm_size_t size) { int i; vm_offset_t va; /* * This currently does not work for entries that * overlap TLB1 entries. */ for (i = 0; i < TLB1_ENTRIES; i ++) { if (tlb1_iomapped(i, pa, size, &va) == 0) return (0); } return (EFAULT); } void mmu_booke_dumpsys_map(mmu_t mmu, vm_paddr_t pa, size_t sz, void **va) { vm_paddr_t ppa; vm_offset_t ofs; vm_size_t gran; /* Minidumps are based on virtual memory addresses. */ if (do_minidump) { *va = (void *)(vm_offset_t)pa; return; } /* Raw physical memory dumps don't have a virtual address. */ /* We always map a 256MB page at 256M. */ gran = 256 * 1024 * 1024; ppa = rounddown2(pa, gran); ofs = pa - ppa; *va = (void *)gran; tlb1_set_entry((vm_offset_t)va, ppa, gran, _TLB_ENTRY_IO); if (sz > (gran - ofs)) tlb1_set_entry((vm_offset_t)(va + gran), ppa + gran, gran, _TLB_ENTRY_IO); } void mmu_booke_dumpsys_unmap(mmu_t mmu, vm_paddr_t pa, size_t sz, void *va) { vm_paddr_t ppa; vm_offset_t ofs; vm_size_t gran; tlb_entry_t e; int i; /* Minidumps are based on virtual memory addresses. */ /* Nothing to do... */ if (do_minidump) return; for (i = 0; i < TLB1_ENTRIES; i++) { tlb1_read_entry(&e, i); if (!(e.mas1 & MAS1_VALID)) break; } /* Raw physical memory dumps don't have a virtual address. */ i--; e.mas1 = 0; e.mas2 = 0; e.mas3 = 0; tlb1_write_entry(&e, i); gran = 256 * 1024 * 1024; ppa = rounddown2(pa, gran); ofs = pa - ppa; if (sz > (gran - ofs)) { i--; e.mas1 = 0; e.mas2 = 0; e.mas3 = 0; tlb1_write_entry(&e, i); } } extern struct dump_pa dump_map[PHYS_AVAIL_SZ + 1]; void mmu_booke_scan_init(mmu_t mmu) { vm_offset_t va; pte_t *pte; int i; if (!do_minidump) { /* Initialize phys. segments for dumpsys(). */ memset(&dump_map, 0, sizeof(dump_map)); mem_regions(&physmem_regions, &physmem_regions_sz, &availmem_regions, &availmem_regions_sz); for (i = 0; i < physmem_regions_sz; i++) { dump_map[i].pa_start = physmem_regions[i].mr_start; dump_map[i].pa_size = physmem_regions[i].mr_size; } return; } /* Virtual segments for minidumps: */ memset(&dump_map, 0, sizeof(dump_map)); /* 1st: kernel .data and .bss. */ dump_map[0].pa_start = trunc_page((uintptr_t)_etext); dump_map[0].pa_size = round_page((uintptr_t)_end) - dump_map[0].pa_start; /* 2nd: msgbuf and tables (see pmap_bootstrap()). */ dump_map[1].pa_start = data_start; dump_map[1].pa_size = data_end - data_start; /* 3rd: kernel VM. */ va = dump_map[1].pa_start + dump_map[1].pa_size; /* Find start of next chunk (from va). */ while (va < virtual_end) { /* Don't dump the buffer cache. */ if (va >= kmi.buffer_sva && va < kmi.buffer_eva) { va = kmi.buffer_eva; continue; } pte = pte_find(mmu, kernel_pmap, va); if (pte != NULL && PTE_ISVALID(pte)) break; va += PAGE_SIZE; } if (va < virtual_end) { dump_map[2].pa_start = va; va += PAGE_SIZE; /* Find last page in chunk. */ while (va < virtual_end) { /* Don't run into the buffer cache. */ if (va == kmi.buffer_sva) break; pte = pte_find(mmu, kernel_pmap, va); if (pte == NULL || !PTE_ISVALID(pte)) break; va += PAGE_SIZE; } dump_map[2].pa_size = va - dump_map[2].pa_start; } } /* * Map a set of physical memory pages into the kernel virtual address space. * Return a pointer to where it is mapped. This routine is intended to be used * for mapping device memory, NOT real memory. */ static void * mmu_booke_mapdev(mmu_t mmu, vm_paddr_t pa, vm_size_t size) { return (mmu_booke_mapdev_attr(mmu, pa, size, VM_MEMATTR_DEFAULT)); } static void * mmu_booke_mapdev_attr(mmu_t mmu, vm_paddr_t pa, vm_size_t size, vm_memattr_t ma) { tlb_entry_t e; void *res; uintptr_t va, tmpva; vm_size_t sz; int i; /* * Check if this is premapped in TLB1. Note: this should probably also * check whether a sequence of TLB1 entries exist that match the * requirement, but now only checks the easy case. */ for (i = 0; i < TLB1_ENTRIES; i++) { tlb1_read_entry(&e, i); if (!(e.mas1 & MAS1_VALID)) continue; if (pa >= e.phys && (pa + size) <= (e.phys + e.size) && (ma == VM_MEMATTR_DEFAULT || tlb_calc_wimg(pa, ma) == (e.mas2 & (MAS2_WIMGE_MASK & ~_TLB_ENTRY_SHARED)))) return (void *)(e.virt + (vm_offset_t)(pa - e.phys)); } size = roundup(size, PAGE_SIZE); /* * The device mapping area is between VM_MAXUSER_ADDRESS and * VM_MIN_KERNEL_ADDRESS. This gives 1GB of device addressing. */ #ifdef SPARSE_MAPDEV /* * With a sparse mapdev, align to the largest starting region. This * could feasibly be optimized for a 'best-fit' alignment, but that * calculation could be very costly. * Align to the smaller of: * - first set bit in overlap of (pa & size mask) * - largest size envelope * * It's possible the device mapping may start at a PA that's not larger * than the size mask, so we need to offset in to maximize the TLB entry * range and minimize the number of used TLB entries. */ do { tmpva = tlb1_map_base; sz = ffsl(((1 << flsl(size-1)) - 1) & pa); sz = sz ? min(roundup(sz + 3, 4), flsl(size) - 1) : flsl(size) - 1; va = roundup(tlb1_map_base, 1 << sz) | (((1 << sz) - 1) & pa); #ifdef __powerpc64__ } while (!atomic_cmpset_long(&tlb1_map_base, tmpva, va + size)); #else } while (!atomic_cmpset_int(&tlb1_map_base, tmpva, va + size)); #endif #else #ifdef __powerpc64__ va = atomic_fetchadd_long(&tlb1_map_base, size); #else va = atomic_fetchadd_int(&tlb1_map_base, size); #endif #endif res = (void *)va; do { sz = 1 << (ilog2(size) & ~1); /* Align size to PA */ if (pa % sz != 0) { do { sz >>= 2; } while (pa % sz != 0); } /* Now align from there to VA */ if (va % sz != 0) { do { sz >>= 2; } while (va % sz != 0); } if (bootverbose) printf("Wiring VA=%lx to PA=%jx (size=%lx)\n", va, (uintmax_t)pa, sz); if (tlb1_set_entry(va, pa, sz, _TLB_ENTRY_SHARED | tlb_calc_wimg(pa, ma)) < 0) return (NULL); size -= sz; pa += sz; va += sz; } while (size > 0); return (res); } /* * 'Unmap' a range mapped by mmu_booke_mapdev(). */ static void mmu_booke_unmapdev(mmu_t mmu, vm_offset_t va, vm_size_t size) { #ifdef SUPPORTS_SHRINKING_TLB1 vm_offset_t base, offset; /* * Unmap only if this is inside kernel virtual space. */ if ((va >= VM_MIN_KERNEL_ADDRESS) && (va <= VM_MAX_KERNEL_ADDRESS)) { base = trunc_page(va); offset = va & PAGE_MASK; size = roundup(offset + size, PAGE_SIZE); kva_free(base, size); } #endif } /* * mmu_booke_object_init_pt preloads the ptes for a given object into the * specified pmap. This eliminates the blast of soft faults on process startup * and immediately after an mmap. */ static void mmu_booke_object_init_pt(mmu_t mmu, pmap_t pmap, vm_offset_t addr, vm_object_t object, vm_pindex_t pindex, vm_size_t size) { VM_OBJECT_ASSERT_WLOCKED(object); KASSERT(object->type == OBJT_DEVICE || object->type == OBJT_SG, ("mmu_booke_object_init_pt: non-device object")); } /* * Perform the pmap work for mincore. */ static int mmu_booke_mincore(mmu_t mmu, pmap_t pmap, vm_offset_t addr, vm_paddr_t *locked_pa) { /* XXX: this should be implemented at some point */ return (0); } static int mmu_booke_change_attr(mmu_t mmu, vm_offset_t addr, vm_size_t sz, vm_memattr_t mode) { vm_offset_t va; pte_t *pte; int i, j; tlb_entry_t e; /* Check TLB1 mappings */ for (i = 0; i < TLB1_ENTRIES; i++) { tlb1_read_entry(&e, i); if (!(e.mas1 & MAS1_VALID)) continue; if (addr >= e.virt && addr < e.virt + e.size) break; } if (i < TLB1_ENTRIES) { /* Only allow full mappings to be modified for now. */ /* Validate the range. */ for (j = i, va = addr; va < addr + sz; va += e.size, j++) { tlb1_read_entry(&e, j); if (va != e.virt || (sz - (va - addr) < e.size)) return (EINVAL); } for (va = addr; va < addr + sz; va += e.size, i++) { tlb1_read_entry(&e, i); e.mas2 &= ~MAS2_WIMGE_MASK; e.mas2 |= tlb_calc_wimg(e.phys, mode); /* * Write it out to the TLB. Should really re-sync with other * cores. */ tlb1_write_entry(&e, i); } return (0); } /* Not in TLB1, try through pmap */ /* First validate the range. */ for (va = addr; va < addr + sz; va += PAGE_SIZE) { pte = pte_find(mmu, kernel_pmap, va); if (pte == NULL || !PTE_ISVALID(pte)) return (EINVAL); } mtx_lock_spin(&tlbivax_mutex); tlb_miss_lock(); for (va = addr; va < addr + sz; va += PAGE_SIZE) { pte = pte_find(mmu, kernel_pmap, va); *pte &= ~(PTE_MAS2_MASK << PTE_MAS2_SHIFT); *pte |= tlb_calc_wimg(PTE_PA(pte), mode) << PTE_MAS2_SHIFT; tlb0_flush_entry(va); } tlb_miss_unlock(); mtx_unlock_spin(&tlbivax_mutex); return (0); } /**************************************************************************/ /* TID handling */ /**************************************************************************/ /* * Allocate a TID. If necessary, steal one from someone else. * The new TID is flushed from the TLB before returning. */ static tlbtid_t tid_alloc(pmap_t pmap) { tlbtid_t tid; int thiscpu; KASSERT((pmap != kernel_pmap), ("tid_alloc: kernel pmap")); CTR2(KTR_PMAP, "%s: s (pmap = %p)", __func__, pmap); thiscpu = PCPU_GET(cpuid); tid = PCPU_GET(tid_next); if (tid > TID_MAX) tid = TID_MIN; PCPU_SET(tid_next, tid + 1); /* If we are stealing TID then clear the relevant pmap's field */ if (tidbusy[thiscpu][tid] != NULL) { CTR2(KTR_PMAP, "%s: warning: stealing tid %d", __func__, tid); tidbusy[thiscpu][tid]->pm_tid[thiscpu] = TID_NONE; /* Flush all entries from TLB0 matching this TID. */ tid_flush(tid); } tidbusy[thiscpu][tid] = pmap; pmap->pm_tid[thiscpu] = tid; __asm __volatile("msync; isync"); CTR3(KTR_PMAP, "%s: e (%02d next = %02d)", __func__, tid, PCPU_GET(tid_next)); return (tid); } /**************************************************************************/ /* TLB0 handling */ /**************************************************************************/ static void #ifdef __powerpc64__ tlb_print_entry(int i, uint32_t mas1, uint64_t mas2, uint32_t mas3, #else tlb_print_entry(int i, uint32_t mas1, uint32_t mas2, uint32_t mas3, #endif uint32_t mas7) { int as; char desc[3]; tlbtid_t tid; vm_size_t size; unsigned int tsize; desc[2] = '\0'; if (mas1 & MAS1_VALID) desc[0] = 'V'; else desc[0] = ' '; if (mas1 & MAS1_IPROT) desc[1] = 'P'; else desc[1] = ' '; as = (mas1 & MAS1_TS_MASK) ? 1 : 0; tid = MAS1_GETTID(mas1); tsize = (mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; size = 0; if (tsize) size = tsize2size(tsize); debugf("%3d: (%s) [AS=%d] " "sz = 0x%08x tsz = %d tid = %d mas1 = 0x%08x " "mas2(va) = 0x%"PRI0ptrX" mas3(pa) = 0x%08x mas7 = 0x%08x\n", i, desc, as, size, tsize, tid, mas1, mas2, mas3, mas7); } /* Convert TLB0 va and way number to tlb0[] table index. */ static inline unsigned int tlb0_tableidx(vm_offset_t va, unsigned int way) { unsigned int idx; idx = (way * TLB0_ENTRIES_PER_WAY); idx += (va & MAS2_TLB0_ENTRY_IDX_MASK) >> MAS2_TLB0_ENTRY_IDX_SHIFT; return (idx); } /* * Invalidate TLB0 entry. */ static inline void tlb0_flush_entry(vm_offset_t va) { CTR2(KTR_PMAP, "%s: s va=0x%08x", __func__, va); mtx_assert(&tlbivax_mutex, MA_OWNED); __asm __volatile("tlbivax 0, %0" :: "r"(va & MAS2_EPN_MASK)); __asm __volatile("isync; msync"); __asm __volatile("tlbsync; msync"); CTR1(KTR_PMAP, "%s: e", __func__); } /* Print out contents of the MAS registers for each TLB0 entry */ void tlb0_print_tlbentries(void) { uint32_t mas0, mas1, mas3, mas7; #ifdef __powerpc64__ uint64_t mas2; #else uint32_t mas2; #endif int entryidx, way, idx; debugf("TLB0 entries:\n"); for (way = 0; way < TLB0_WAYS; way ++) for (entryidx = 0; entryidx < TLB0_ENTRIES_PER_WAY; entryidx++) { mas0 = MAS0_TLBSEL(0) | MAS0_ESEL(way); mtspr(SPR_MAS0, mas0); __asm __volatile("isync"); mas2 = entryidx << MAS2_TLB0_ENTRY_IDX_SHIFT; mtspr(SPR_MAS2, mas2); __asm __volatile("isync; tlbre"); mas1 = mfspr(SPR_MAS1); mas2 = mfspr(SPR_MAS2); mas3 = mfspr(SPR_MAS3); mas7 = mfspr(SPR_MAS7); idx = tlb0_tableidx(mas2, way); tlb_print_entry(idx, mas1, mas2, mas3, mas7); } } /**************************************************************************/ /* TLB1 handling */ /**************************************************************************/ /* * TLB1 mapping notes: * * TLB1[0] Kernel text and data. * TLB1[1-15] Additional kernel text and data mappings (if required), PCI * windows, other devices mappings. */ /* * Read an entry from given TLB1 slot. */ void tlb1_read_entry(tlb_entry_t *entry, unsigned int slot) { register_t msr; uint32_t mas0; KASSERT((entry != NULL), ("%s(): Entry is NULL!", __func__)); msr = mfmsr(); __asm __volatile("wrteei 0"); mas0 = MAS0_TLBSEL(1) | MAS0_ESEL(slot); mtspr(SPR_MAS0, mas0); __asm __volatile("isync; tlbre"); entry->mas1 = mfspr(SPR_MAS1); entry->mas2 = mfspr(SPR_MAS2); entry->mas3 = mfspr(SPR_MAS3); switch ((mfpvr() >> 16) & 0xFFFF) { case FSL_E500v2: case FSL_E500mc: case FSL_E5500: case FSL_E6500: entry->mas7 = mfspr(SPR_MAS7); break; default: entry->mas7 = 0; break; } mtmsr(msr); entry->virt = entry->mas2 & MAS2_EPN_MASK; entry->phys = ((vm_paddr_t)(entry->mas7 & MAS7_RPN) << 32) | (entry->mas3 & MAS3_RPN); entry->size = tsize2size((entry->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT); } struct tlbwrite_args { tlb_entry_t *e; unsigned int idx; }; static void tlb1_write_entry_int(void *arg) { struct tlbwrite_args *args = arg; uint32_t mas0; /* Select entry */ mas0 = MAS0_TLBSEL(1) | MAS0_ESEL(args->idx); mtspr(SPR_MAS0, mas0); __asm __volatile("isync"); mtspr(SPR_MAS1, args->e->mas1); __asm __volatile("isync"); mtspr(SPR_MAS2, args->e->mas2); __asm __volatile("isync"); mtspr(SPR_MAS3, args->e->mas3); __asm __volatile("isync"); switch ((mfpvr() >> 16) & 0xFFFF) { case FSL_E500mc: case FSL_E5500: case FSL_E6500: mtspr(SPR_MAS8, 0); __asm __volatile("isync"); /* FALLTHROUGH */ case FSL_E500v2: mtspr(SPR_MAS7, args->e->mas7); __asm __volatile("isync"); break; default: break; } __asm __volatile("tlbwe; isync; msync"); } static void tlb1_write_entry_sync(void *arg) { /* Empty synchronization point for smp_rendezvous(). */ } /* * Write given entry to TLB1 hardware. */ static void tlb1_write_entry(tlb_entry_t *e, unsigned int idx) { struct tlbwrite_args args; args.e = e; args.idx = idx; #ifdef SMP if ((e->mas2 & _TLB_ENTRY_SHARED) && smp_started) { mb(); smp_rendezvous(tlb1_write_entry_sync, tlb1_write_entry_int, tlb1_write_entry_sync, &args); } else #endif { register_t msr; msr = mfmsr(); __asm __volatile("wrteei 0"); tlb1_write_entry_int(&args); mtmsr(msr); } } /* * Return the largest uint value log such that 2^log <= num. */ static unsigned int ilog2(unsigned int num) { int lz; __asm ("cntlzw %0, %1" : "=r" (lz) : "r" (num)); return (31 - lz); } /* * Convert TLB TSIZE value to mapped region size. */ static vm_size_t tsize2size(unsigned int tsize) { /* * size = 4^tsize KB * size = 4^tsize * 2^10 = 2^(2 * tsize - 10) */ return ((1 << (2 * tsize)) * 1024); } /* * Convert region size (must be power of 4) to TLB TSIZE value. */ static unsigned int size2tsize(vm_size_t size) { return (ilog2(size) / 2 - 5); } /* * Register permanent kernel mapping in TLB1. * * Entries are created starting from index 0 (current free entry is * kept in tlb1_idx) and are not supposed to be invalidated. */ int tlb1_set_entry(vm_offset_t va, vm_paddr_t pa, vm_size_t size, uint32_t flags) { tlb_entry_t e; uint32_t ts, tid; int tsize, index; for (index = 0; index < TLB1_ENTRIES; index++) { tlb1_read_entry(&e, index); if ((e.mas1 & MAS1_VALID) == 0) break; /* Check if we're just updating the flags, and update them. */ if (e.phys == pa && e.virt == va && e.size == size) { e.mas2 = (va & MAS2_EPN_MASK) | flags; tlb1_write_entry(&e, index); return (0); } } if (index >= TLB1_ENTRIES) { printf("tlb1_set_entry: TLB1 full!\n"); return (-1); } /* Convert size to TSIZE */ tsize = size2tsize(size); tid = (TID_KERNEL << MAS1_TID_SHIFT) & MAS1_TID_MASK; /* XXX TS is hard coded to 0 for now as we only use single address space */ ts = (0 << MAS1_TS_SHIFT) & MAS1_TS_MASK; e.phys = pa; e.virt = va; e.size = size; e.mas1 = MAS1_VALID | MAS1_IPROT | ts | tid; e.mas1 |= ((tsize << MAS1_TSIZE_SHIFT) & MAS1_TSIZE_MASK); e.mas2 = (va & MAS2_EPN_MASK) | flags; /* Set supervisor RWX permission bits */ e.mas3 = (pa & MAS3_RPN) | MAS3_SR | MAS3_SW | MAS3_SX; e.mas7 = (pa >> 32) & MAS7_RPN; tlb1_write_entry(&e, index); /* * XXX in general TLB1 updates should be propagated between CPUs, * since current design assumes to have the same TLB1 set-up on all * cores. */ return (0); } /* * Map in contiguous RAM region into the TLB1 using maximum of * KERNEL_REGION_MAX_TLB_ENTRIES entries. * * If necessary round up last entry size and return total size * used by all allocated entries. */ vm_size_t tlb1_mapin_region(vm_offset_t va, vm_paddr_t pa, vm_size_t size) { vm_size_t pgs[KERNEL_REGION_MAX_TLB_ENTRIES]; vm_size_t mapped, pgsz, base, mask; int idx, nents; /* Round up to the next 1M */ size = roundup2(size, 1 << 20); mapped = 0; idx = 0; base = va; pgsz = 64*1024*1024; while (mapped < size) { while (mapped < size && idx < KERNEL_REGION_MAX_TLB_ENTRIES) { while (pgsz > (size - mapped)) pgsz >>= 2; pgs[idx++] = pgsz; mapped += pgsz; } /* We under-map. Correct for this. */ if (mapped < size) { while (pgs[idx - 1] == pgsz) { idx--; mapped -= pgsz; } /* XXX We may increase beyond out starting point. */ pgsz <<= 2; pgs[idx++] = pgsz; mapped += pgsz; } } nents = idx; mask = pgs[0] - 1; /* Align address to the boundary */ if (va & mask) { va = (va + mask) & ~mask; pa = (pa + mask) & ~mask; } for (idx = 0; idx < nents; idx++) { pgsz = pgs[idx]; debugf("%u: %llx -> %x, size=%x\n", idx, pa, va, pgsz); tlb1_set_entry(va, pa, pgsz, _TLB_ENTRY_SHARED | _TLB_ENTRY_MEM); pa += pgsz; va += pgsz; } mapped = (va - base); printf("mapped size 0x%"PRI0ptrX" (wasted space 0x%"PRIxPTR")\n", mapped, mapped - size); return (mapped); } /* * TLB1 initialization routine, to be called after the very first * assembler level setup done in locore.S. */ void tlb1_init() { uint32_t mas0, mas1, mas2, mas3, mas7; uint32_t tsz; tlb1_get_tlbconf(); mas0 = MAS0_TLBSEL(1) | MAS0_ESEL(0); mtspr(SPR_MAS0, mas0); __asm __volatile("isync; tlbre"); mas1 = mfspr(SPR_MAS1); mas2 = mfspr(SPR_MAS2); mas3 = mfspr(SPR_MAS3); mas7 = mfspr(SPR_MAS7); kernload = ((vm_paddr_t)(mas7 & MAS7_RPN) << 32) | (mas3 & MAS3_RPN); tsz = (mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; kernsize += (tsz > 0) ? tsize2size(tsz) : 0; /* Setup TLB miss defaults */ set_mas4_defaults(); } /* * pmap_early_io_unmap() should be used in short conjunction with * pmap_early_io_map(), as in the following snippet: * * x = pmap_early_io_map(...); * * pmap_early_io_unmap(x, size); * * And avoiding more allocations between. */ void pmap_early_io_unmap(vm_offset_t va, vm_size_t size) { int i; tlb_entry_t e; vm_size_t isize; size = roundup(size, PAGE_SIZE); isize = size; for (i = 0; i < TLB1_ENTRIES && size > 0; i++) { tlb1_read_entry(&e, i); if (!(e.mas1 & MAS1_VALID)) continue; if (va <= e.virt && (va + isize) >= (e.virt + e.size)) { size -= e.size; e.mas1 &= ~MAS1_VALID; tlb1_write_entry(&e, i); } } if (tlb1_map_base == va + isize) tlb1_map_base -= isize; } vm_offset_t pmap_early_io_map(vm_paddr_t pa, vm_size_t size) { vm_paddr_t pa_base; vm_offset_t va, sz; int i; tlb_entry_t e; KASSERT(!pmap_bootstrapped, ("Do not use after PMAP is up!")); for (i = 0; i < TLB1_ENTRIES; i++) { tlb1_read_entry(&e, i); if (!(e.mas1 & MAS1_VALID)) continue; if (pa >= e.phys && (pa + size) <= (e.phys + e.size)) return (e.virt + (pa - e.phys)); } pa_base = rounddown(pa, PAGE_SIZE); size = roundup(size + (pa - pa_base), PAGE_SIZE); tlb1_map_base = roundup2(tlb1_map_base, 1 << (ilog2(size) & ~1)); va = tlb1_map_base + (pa - pa_base); do { sz = 1 << (ilog2(size) & ~1); tlb1_set_entry(tlb1_map_base, pa_base, sz, _TLB_ENTRY_SHARED | _TLB_ENTRY_IO); size -= sz; pa_base += sz; tlb1_map_base += sz; } while (size > 0); return (va); } void pmap_track_page(pmap_t pmap, vm_offset_t va) { vm_paddr_t pa; vm_page_t page; struct pv_entry *pve; va = trunc_page(va); pa = pmap_kextract(va); rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); page = PHYS_TO_VM_PAGE(pa); TAILQ_FOREACH(pve, &page->md.pv_list, pv_link) { if ((pmap == pve->pv_pmap) && (va == pve->pv_va)) { goto out; } } page->md.pv_tracked = true; pv_insert(pmap, va, page); out: PMAP_UNLOCK(pmap); rw_wunlock(&pvh_global_lock); } /* * Setup MAS4 defaults. * These values are loaded to MAS0-2 on a TLB miss. */ static void set_mas4_defaults(void) { uint32_t mas4; /* Defaults: TLB0, PID0, TSIZED=4K */ mas4 = MAS4_TLBSELD0; mas4 |= (TLB_SIZE_4K << MAS4_TSIZED_SHIFT) & MAS4_TSIZED_MASK; #ifdef SMP mas4 |= MAS4_MD; #endif mtspr(SPR_MAS4, mas4); __asm __volatile("isync"); } /* * Print out contents of the MAS registers for each TLB1 entry */ void tlb1_print_tlbentries(void) { uint32_t mas0, mas1, mas3, mas7; #ifdef __powerpc64__ uint64_t mas2; #else uint32_t mas2; #endif int i; debugf("TLB1 entries:\n"); for (i = 0; i < TLB1_ENTRIES; i++) { mas0 = MAS0_TLBSEL(1) | MAS0_ESEL(i); mtspr(SPR_MAS0, mas0); __asm __volatile("isync; tlbre"); mas1 = mfspr(SPR_MAS1); mas2 = mfspr(SPR_MAS2); mas3 = mfspr(SPR_MAS3); mas7 = mfspr(SPR_MAS7); tlb_print_entry(i, mas1, mas2, mas3, mas7); } } /* * Return 0 if the physical IO range is encompassed by one of the * the TLB1 entries, otherwise return related error code. */ static int tlb1_iomapped(int i, vm_paddr_t pa, vm_size_t size, vm_offset_t *va) { uint32_t prot; vm_paddr_t pa_start; vm_paddr_t pa_end; unsigned int entry_tsize; vm_size_t entry_size; tlb_entry_t e; *va = (vm_offset_t)NULL; tlb1_read_entry(&e, i); /* Skip invalid entries */ if (!(e.mas1 & MAS1_VALID)) return (EINVAL); /* * The entry must be cache-inhibited, guarded, and r/w * so it can function as an i/o page */ prot = e.mas2 & (MAS2_I | MAS2_G); if (prot != (MAS2_I | MAS2_G)) return (EPERM); prot = e.mas3 & (MAS3_SR | MAS3_SW); if (prot != (MAS3_SR | MAS3_SW)) return (EPERM); /* The address should be within the entry range. */ entry_tsize = (e.mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; KASSERT((entry_tsize), ("tlb1_iomapped: invalid entry tsize")); entry_size = tsize2size(entry_tsize); pa_start = (((vm_paddr_t)e.mas7 & MAS7_RPN) << 32) | (e.mas3 & MAS3_RPN); pa_end = pa_start + entry_size; if ((pa < pa_start) || ((pa + size) > pa_end)) return (ERANGE); /* Return virtual address of this mapping. */ *va = (e.mas2 & MAS2_EPN_MASK) + (pa - pa_start); return (0); } /* * Invalidate all TLB0 entries which match the given TID. Note this is * dedicated for cases when invalidations should NOT be propagated to other * CPUs. */ static void tid_flush(tlbtid_t tid) { register_t msr; uint32_t mas0, mas1, mas2; int entry, way; /* Don't evict kernel translations */ if (tid == TID_KERNEL) return; msr = mfmsr(); __asm __volatile("wrteei 0"); for (way = 0; way < TLB0_WAYS; way++) for (entry = 0; entry < TLB0_ENTRIES_PER_WAY; entry++) { mas0 = MAS0_TLBSEL(0) | MAS0_ESEL(way); mtspr(SPR_MAS0, mas0); __asm __volatile("isync"); mas2 = entry << MAS2_TLB0_ENTRY_IDX_SHIFT; mtspr(SPR_MAS2, mas2); __asm __volatile("isync; tlbre"); mas1 = mfspr(SPR_MAS1); if (!(mas1 & MAS1_VALID)) continue; if (((mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT) != tid) continue; mas1 &= ~MAS1_VALID; mtspr(SPR_MAS1, mas1); __asm __volatile("isync; tlbwe; isync; msync"); } mtmsr(msr); } Index: head/sys/powerpc/cpufreq/dfs.c =================================================================== --- head/sys/powerpc/cpufreq/dfs.c (revision 326260) +++ head/sys/powerpc/cpufreq/dfs.c (revision 326261) @@ -1,228 +1,230 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2009 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include "cpufreq_if.h" struct dfs_softc { device_t dev; int dfs4; }; static void dfs_identify(driver_t *driver, device_t parent); static int dfs_probe(device_t dev); static int dfs_attach(device_t dev); static int dfs_settings(device_t dev, struct cf_setting *sets, int *count); static int dfs_set(device_t dev, const struct cf_setting *set); static int dfs_get(device_t dev, struct cf_setting *set); static int dfs_type(device_t dev, int *type); static device_method_t dfs_methods[] = { /* Device interface */ DEVMETHOD(device_identify, dfs_identify), DEVMETHOD(device_probe, dfs_probe), DEVMETHOD(device_attach, dfs_attach), /* cpufreq interface */ DEVMETHOD(cpufreq_drv_set, dfs_set), DEVMETHOD(cpufreq_drv_get, dfs_get), DEVMETHOD(cpufreq_drv_type, dfs_type), DEVMETHOD(cpufreq_drv_settings, dfs_settings), {0, 0} }; static driver_t dfs_driver = { "dfs", dfs_methods, sizeof(struct dfs_softc) }; static devclass_t dfs_devclass; DRIVER_MODULE(dfs, cpu, dfs_driver, dfs_devclass, 0, 0); /* * Bits of the HID1 register to enable DFS. See page 2-24 of "MPC7450 * RISC Microprocessor Family Reference Manual", rev. 5. */ #define HID1_DFS2 (1UL << 22) #define HID1_DFS4 (1UL << 23) static void dfs_identify(driver_t *driver, device_t parent) { uint16_t vers; vers = mfpvr() >> 16; /* Check for an MPC 7447A or 7448 CPU */ switch (vers) { case MPC7447A: case MPC7448: break; default: return; } /* Make sure we're not being doubly invoked. */ if (device_find_child(parent, "dfs", -1) != NULL) return; /* * We attach a child for every CPU since settings need to * be performed on every CPU in the SMP case. */ if (BUS_ADD_CHILD(parent, 10, "dfs", -1) == NULL) device_printf(parent, "add dfs child failed\n"); } static int dfs_probe(device_t dev) { if (resource_disabled("dfs", 0)) return (ENXIO); device_set_desc(dev, "Dynamic Frequency Switching"); return (0); } static int dfs_attach(device_t dev) { struct dfs_softc *sc; uint16_t vers; sc = device_get_softc(dev); sc->dev = dev; sc->dfs4 = 0; vers = mfpvr() >> 16; /* The 7448 supports divide-by-four as well */ if (vers == MPC7448) sc->dfs4 = 1; cpufreq_register(dev); return (0); } static int dfs_settings(device_t dev, struct cf_setting *sets, int *count) { struct dfs_softc *sc; int states; sc = device_get_softc(dev); states = sc->dfs4 ? 3 : 2; if (sets == NULL || count == NULL) return (EINVAL); if (*count < states) return (E2BIG); /* Return a list of valid settings for this driver. */ memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * states); sets[0].freq = 10000; sets[0].dev = dev; sets[1].freq = 5000; sets[1].dev = dev; if (sc->dfs4) sets[2].freq = 2500; sets[2].dev = dev; *count = states; return (0); } static int dfs_set(device_t dev, const struct cf_setting *set) { register_t hid1; if (set == NULL) return (EINVAL); hid1 = mfspr(SPR_HID1); hid1 &= ~(HID1_DFS2 | HID1_DFS4); if (set->freq == 5000) hid1 |= HID1_DFS2; else if (set->freq == 2500) hid1 |= HID1_DFS4; /* * Now set the HID1 register with new values. Calling sequence * taken from page 2-26 of the MPC7450 family CPU manual. */ powerpc_sync(); mtspr(SPR_HID1, hid1); powerpc_sync(); isync(); return (0); } static int dfs_get(device_t dev, struct cf_setting *set) { struct dfs_softc *sc; register_t hid1; if (set == NULL) return (EINVAL); sc = device_get_softc(dev); memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set)); hid1 = mfspr(SPR_HID1); set->freq = 10000; if (hid1 & HID1_DFS2) set->freq = 5000; else if (sc->dfs4 && (hid1 & HID1_DFS4)) set->freq = 2500; set->dev = dev; return (0); } static int dfs_type(device_t dev, int *type) { if (type == NULL) return (EINVAL); *type = CPUFREQ_TYPE_RELATIVE; return (0); } Index: head/sys/powerpc/cpufreq/pcr.c =================================================================== --- head/sys/powerpc/cpufreq/pcr.c (revision 326260) +++ head/sys/powerpc/cpufreq/pcr.c (revision 326261) @@ -1,341 +1,343 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2009 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "cpufreq_if.h" struct pcr_softc { device_t dev; uint32_t pcr_vals[3]; int nmodes; }; static void pcr_identify(driver_t *driver, device_t parent); static int pcr_probe(device_t dev); static int pcr_attach(device_t dev); static int pcr_settings(device_t dev, struct cf_setting *sets, int *count); static int pcr_set(device_t dev, const struct cf_setting *set); static int pcr_get(device_t dev, struct cf_setting *set); static int pcr_type(device_t dev, int *type); static device_method_t pcr_methods[] = { /* Device interface */ DEVMETHOD(device_identify, pcr_identify), DEVMETHOD(device_probe, pcr_probe), DEVMETHOD(device_attach, pcr_attach), /* cpufreq interface */ DEVMETHOD(cpufreq_drv_set, pcr_set), DEVMETHOD(cpufreq_drv_get, pcr_get), DEVMETHOD(cpufreq_drv_type, pcr_type), DEVMETHOD(cpufreq_drv_settings, pcr_settings), {0, 0} }; static driver_t pcr_driver = { "pcr", pcr_methods, sizeof(struct pcr_softc) }; static devclass_t pcr_devclass; DRIVER_MODULE(pcr, cpu, pcr_driver, pcr_devclass, 0, 0); /* * States */ #define PCR_TO_FREQ(a) ((a >> 17) & 3) #define PCR_FULL 0 #define PCR_HALF 1 #define PCR_QUARTER 2 /* Only on 970MP */ #define PSR_RECEIVED (1ULL << 61) #define PSR_COMPLETED (1ULL << 61) /* * SCOM addresses */ #define SCOM_PCR 0x0aa00100 /* Power Control Register */ #define SCOM_PCR_BIT 0x80000000 /* Data bit for PCR */ #define SCOM_PSR 0x40800100 /* Power Status Register */ /* * SCOM Glue */ #define SCOMC_READ 0x00008000 #define SCOMC_WRITE 0x00000000 static void write_scom(register_t address, uint64_t value) { register_t msr; #ifndef __powerpc64__ register_t hi, lo, scratch; #endif msr = mfmsr(); mtmsr(msr & ~PSL_EE); isync(); #ifdef __powerpc64__ mtspr(SPR_SCOMD, value); #else hi = (value >> 32) & 0xffffffff; lo = value & 0xffffffff; mtspr64(SPR_SCOMD, hi, lo, scratch); #endif isync(); mtspr(SPR_SCOMC, address | SCOMC_WRITE); isync(); mtmsr(msr); isync(); } static uint64_t read_scom(register_t address) { register_t msr; uint64_t ret; msr = mfmsr(); mtmsr(msr & ~PSL_EE); isync(); mtspr(SPR_SCOMC, address | SCOMC_READ); isync(); __asm __volatile ("mfspr %0,%1;" " mr %0+1, %0; srdi %0,%0,32" : "=r" (ret) : "K" (SPR_SCOMD)); (void)mfspr(SPR_SCOMC); /* Complete transcation */ mtmsr(msr); isync(); return (ret); } static void pcr_identify(driver_t *driver, device_t parent) { uint16_t vers; vers = mfpvr() >> 16; /* Check for an IBM 970-class CPU */ switch (vers) { case IBM970FX: case IBM970GX: case IBM970MP: break; default: return; } /* Make sure we're not being doubly invoked. */ if (device_find_child(parent, "pcr", -1) != NULL) return; /* * We attach a child for every CPU since settings need to * be performed on every CPU in the SMP case. */ if (BUS_ADD_CHILD(parent, 10, "pcr", -1) == NULL) device_printf(parent, "add pcr child failed\n"); } static int pcr_probe(device_t dev) { if (resource_disabled("pcr", 0)) return (ENXIO); device_set_desc(dev, "PPC 970 Power Control Register"); return (0); } static int pcr_attach(device_t dev) { struct pcr_softc *sc; phandle_t cpu; uint32_t modes[3]; int i; sc = device_get_softc(dev); sc->dev = dev; cpu = ofw_bus_get_node(device_get_parent(dev)); if (cpu <= 0) { device_printf(dev,"No CPU device tree node!\n"); return (ENXIO); } if (OF_getproplen(cpu, "power-mode-data") <= 0) { /* Use the first CPU's node */ cpu = OF_child(OF_parent(cpu)); } /* * Collect the PCR values for each mode from the device tree. * These include bus timing information, and so cannot be * directly computed. */ sc->nmodes = OF_getproplen(cpu, "power-mode-data"); if (sc->nmodes <= 0 || sc->nmodes > sizeof(sc->pcr_vals)) { device_printf(dev,"No power mode data in device tree!\n"); return (ENXIO); } OF_getprop(cpu, "power-mode-data", modes, sc->nmodes); sc->nmodes /= sizeof(modes[0]); /* Sort the modes */ for (i = 0; i < sc->nmodes; i++) sc->pcr_vals[PCR_TO_FREQ(modes[i])] = modes[i]; cpufreq_register(dev); return (0); } static int pcr_settings(device_t dev, struct cf_setting *sets, int *count) { struct pcr_softc *sc; sc = device_get_softc(dev); if (sets == NULL || count == NULL) return (EINVAL); if (*count < sc->nmodes) return (E2BIG); /* Return a list of valid settings for this driver. */ memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * sc->nmodes); sets[0].freq = 10000; sets[0].dev = dev; sets[1].freq = 5000; sets[1].dev = dev; if (sc->nmodes > 2) sets[2].freq = 2500; sets[2].dev = dev; *count = sc->nmodes; return (0); } static int pcr_set(device_t dev, const struct cf_setting *set) { struct pcr_softc *sc; register_t pcr, msr; uint64_t psr; if (set == NULL) return (EINVAL); sc = device_get_softc(dev); /* Construct the new PCR */ pcr = SCOM_PCR_BIT; if (set->freq == 10000) pcr |= sc->pcr_vals[0]; else if (set->freq == 5000) pcr |= sc->pcr_vals[1]; else if (set->freq == 2500) pcr |= sc->pcr_vals[2]; msr = mfmsr(); mtmsr(msr & ~PSL_EE); isync(); /* 970MP requires PCR and PCRH to be cleared first */ write_scom(SCOM_PCR,0); /* Clear PCRH */ write_scom(SCOM_PCR,SCOM_PCR_BIT); /* Clear PCR */ /* Set PCR */ write_scom(SCOM_PCR, pcr); /* Wait for completion */ do { DELAY(100); psr = read_scom(SCOM_PSR); } while ((psr & PSR_RECEIVED) && !(psr & PSR_COMPLETED)); mtmsr(msr); isync(); return (0); } static int pcr_get(device_t dev, struct cf_setting *set) { uint64_t psr; if (set == NULL) return (EINVAL); memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set)); psr = read_scom(SCOM_PSR); /* We want bits 6 and 7 */ psr = (psr >> 56) & 3; set->freq = 10000; if (psr == PCR_HALF) set->freq = 5000; else if (psr == PCR_QUARTER) set->freq = 2500; set->dev = dev; return (0); } static int pcr_type(device_t dev, int *type) { if (type == NULL) return (EINVAL); *type = CPUFREQ_TYPE_RELATIVE; return (0); } Index: head/sys/powerpc/cpufreq/pmufreq.c =================================================================== --- head/sys/powerpc/cpufreq/pmufreq.c (revision 326260) +++ head/sys/powerpc/cpufreq/pmufreq.c (revision 326261) @@ -1,221 +1,223 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2011 Justin Hibbits * Copyright (c) 2009 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "cpufreq_if.h" #include "powerpc/powermac/pmuvar.h" struct pmufreq_softc { device_t dev; uint32_t minfreq; uint32_t maxfreq; uint32_t curfreq; }; static void pmufreq_identify(driver_t *driver, device_t parent); static int pmufreq_probe(device_t dev); static int pmufreq_attach(device_t dev); static int pmufreq_settings(device_t dev, struct cf_setting *sets, int *count); static int pmufreq_set(device_t dev, const struct cf_setting *set); static int pmufreq_get(device_t dev, struct cf_setting *set); static int pmufreq_type(device_t dev, int *type); static device_method_t pmufreq_methods[] = { /* Device interface */ DEVMETHOD(device_identify, pmufreq_identify), DEVMETHOD(device_probe, pmufreq_probe), DEVMETHOD(device_attach, pmufreq_attach), /* cpufreq interface */ DEVMETHOD(cpufreq_drv_set, pmufreq_set), DEVMETHOD(cpufreq_drv_get, pmufreq_get), DEVMETHOD(cpufreq_drv_type, pmufreq_type), DEVMETHOD(cpufreq_drv_settings, pmufreq_settings), {0, 0} }; static driver_t pmufreq_driver = { "pmufreq", pmufreq_methods, sizeof(struct pmufreq_softc) }; static devclass_t pmufreq_devclass; DRIVER_MODULE(pmufreq, cpu, pmufreq_driver, pmufreq_devclass, 0, 0); static void pmufreq_identify(driver_t *driver, device_t parent) { phandle_t node; uint32_t min_freq; node = ofw_bus_get_node(parent); if (OF_getprop(node, "min-clock-frequency", &min_freq, sizeof(min_freq)) == -1) return; /* Make sure we're not being doubly invoked. */ if (device_find_child(parent, "pmufreq", -1) != NULL) return; /* * We attach a child for every CPU since settings need to * be performed on every CPU in the SMP case. */ if (BUS_ADD_CHILD(parent, 10, "pmufreq", -1) == NULL) device_printf(parent, "add pmufreq child failed\n"); } static int pmufreq_probe(device_t dev) { struct pmufreq_softc *sc; phandle_t node; uint32_t min_freq; if (resource_disabled("pmufreq", 0)) return (ENXIO); sc = device_get_softc(dev); node = ofw_bus_get_node(device_get_parent(dev)); /* * A scalable MPC7455 has min-clock-frequency/max-clock-frequency as OFW * properties of the 'cpu' node. */ if (OF_getprop(node, "min-clock-frequency", &min_freq, sizeof(min_freq)) == -1) return (ENXIO); device_set_desc(dev, "PMU-based frequency scaling"); return (0); } static int pmufreq_attach(device_t dev) { struct pmufreq_softc *sc; phandle_t node; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(device_get_parent(dev)); OF_getprop(node, "min-clock-frequency", &sc->minfreq, sizeof(sc->minfreq)); OF_getprop(node, "max-clock-frequency", &sc->maxfreq, sizeof(sc->maxfreq)); OF_getprop(node, "rounded-clock-frequency", &sc->curfreq, sizeof(sc->curfreq)); sc->minfreq /= 1000000; sc->maxfreq /= 1000000; sc->curfreq /= 1000000; cpufreq_register(dev); return (0); } static int pmufreq_settings(device_t dev, struct cf_setting *sets, int *count) { struct pmufreq_softc *sc; sc = device_get_softc(dev); if (sets == NULL || count == NULL) return (EINVAL); if (*count < 2) return (E2BIG); /* Return a list of valid settings for this driver. */ memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * 2); sets[0].freq = sc->maxfreq; sets[0].dev = dev; sets[1].freq = sc->minfreq; sets[1].dev = dev; /* Set high latency for CPU frequency changes, it's a tedious process. */ sets[0].lat = INT_MAX; sets[1].lat = INT_MAX; *count = 2; return (0); } static int pmufreq_set(device_t dev, const struct cf_setting *set) { struct pmufreq_softc *sc; int error, speed_sel; if (set == NULL) return (EINVAL); sc = device_get_softc(dev); if (set->freq == sc->maxfreq) speed_sel = 0; else speed_sel = 1; error = pmu_set_speed(speed_sel); if (error == 0) sc->curfreq = set->freq; return (error); } static int pmufreq_get(device_t dev, struct cf_setting *set) { struct pmufreq_softc *sc; if (set == NULL) return (EINVAL); sc = device_get_softc(dev); set->freq = sc->curfreq; set->dev = dev; return (0); } static int pmufreq_type(device_t dev, int *type) { if (type == NULL) return (EINVAL); *type = CPUFREQ_TYPE_ABSOLUTE; return (0); } Index: head/sys/powerpc/fpu/fpu_extern.h =================================================================== --- head/sys/powerpc/fpu/fpu_extern.h (revision 326260) +++ head/sys/powerpc/fpu/fpu_extern.h (revision 326261) @@ -1,55 +1,57 @@ /* $NetBSD: fpu_extern.h,v 1.3 2005/12/11 12:18:42 christos Exp $ */ /* $FreeBSD$ */ /*- + * SPDX-License-Identifier: BSD-2-Clause-NetBSD + * * Copyright (c) 1995 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Christos Zoulas. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ struct proc; struct fpu; struct trapframe; union instr; struct fpemu; struct fpn; /* fpu.c */ int fpu_emulate(struct trapframe *, struct fpu *); int fpu_execute(struct trapframe *, struct fpemu *, union instr *); /* fpu_explode.c */ int fpu_itof(struct fpn *, u_int); int fpu_xtof(struct fpn *, u_int64_t); int fpu_stof(struct fpn *, u_int); int fpu_dtof(struct fpn *, u_int, u_int); /* fpu_implode.c */ u_int fpu_ftoi(struct fpemu *, struct fpn *); u_int fpu_ftox(struct fpemu *, struct fpn *, u_int *); u_int fpu_ftos(struct fpemu *, struct fpn *); u_int fpu_ftod(struct fpemu *, struct fpn *, u_int *); Index: head/sys/powerpc/include/_bus.h =================================================================== --- head/sys/powerpc/include/_bus.h (revision 326260) +++ head/sys/powerpc/include/_bus.h (revision 326261) @@ -1,48 +1,50 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2005 M. Warner Losh. * 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, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef POWERPC_INCLUDE__BUS_H #define POWERPC_INCLUDE__BUS_H #include /* * Bus address and size types */ typedef vm_paddr_t bus_addr_t; typedef vm_size_t bus_size_t; /* * Access methods for bus resources and address space. */ typedef struct bus_space *bus_space_tag_t; typedef vm_offset_t bus_space_handle_t; #endif /* POWERPC_INCLUDE__BUS_H */ Index: head/sys/powerpc/include/_inttypes.h =================================================================== --- head/sys/powerpc/include/_inttypes.h (revision 326260) +++ head/sys/powerpc/include/_inttypes.h (revision 326261) @@ -1,221 +1,223 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Klaus Klein. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * From: $NetBSD: int_fmtio.h,v 1.2 2001/04/26 16:25:21 kleink Exp $ * $FreeBSD$ */ #ifndef _MACHINE_INTTYPES_H_ #define _MACHINE_INTTYPES_H_ /* * Macros for format specifiers. */ #ifdef __LP64__ #define __PRI64 "l" #define __PRIptr "l" #else #define __PRI64 "ll" #define __PRIptr #endif /* fprintf(3) macros for signed integers. */ #define PRId8 "d" /* int8_t */ #define PRId16 "d" /* int16_t */ #define PRId32 "d" /* int32_t */ #define PRId64 __PRI64"d" /* int64_t */ #define PRIdLEAST8 "d" /* int_least8_t */ #define PRIdLEAST16 "d" /* int_least16_t */ #define PRIdLEAST32 "d" /* int_least32_t */ #define PRIdLEAST64 __PRI64"d" /* int_least64_t */ #define PRIdFAST8 "d" /* int_fast8_t */ #define PRIdFAST16 "d" /* int_fast16_t */ #define PRIdFAST32 "d" /* int_fast32_t */ #define PRIdFAST64 __PRI64"d" /* int_fast64_t */ #define PRIdMAX "jd" /* intmax_t */ #define PRIdPTR __PRIptr"d" /* intptr_t */ #define PRIi8 "i" /* int8_t */ #define PRIi16 "i" /* int16_t */ #define PRIi32 "i" /* int32_t */ #define PRIi64 __PRI64"i" /* int64_t */ #define PRIiLEAST8 "i" /* int_least8_t */ #define PRIiLEAST16 "i" /* int_least16_t */ #define PRIiLEAST32 "i" /* int_least32_t */ #define PRIiLEAST64 __PRI64"i" /* int_least64_t */ #define PRIiFAST8 "i" /* int_fast8_t */ #define PRIiFAST16 "i" /* int_fast16_t */ #define PRIiFAST32 "i" /* int_fast32_t */ #define PRIiFAST64 __PRI64"i" /* int_fast64_t */ #define PRIiMAX "ji" /* intmax_t */ #define PRIiPTR __PRIptr"i" /* intptr_t */ /* fprintf(3) macros for unsigned integers. */ #define PRIo8 "o" /* uint8_t */ #define PRIo16 "o" /* uint16_t */ #define PRIo32 "o" /* uint32_t */ #define PRIo64 __PRI64"o" /* uint64_t */ #define PRIoLEAST8 "o" /* uint_least8_t */ #define PRIoLEAST16 "o" /* uint_least16_t */ #define PRIoLEAST32 "o" /* uint_least32_t */ #define PRIoLEAST64 __PRI64"o" /* uint_least64_t */ #define PRIoFAST8 "o" /* uint_fast8_t */ #define PRIoFAST16 "o" /* uint_fast16_t */ #define PRIoFAST32 "o" /* uint_fast32_t */ #define PRIoFAST64 __PRI64"o" /* uint_fast64_t */ #define PRIoMAX "jo" /* uintmax_t */ #define PRIoPTR __PRIptr"o" /* uintptr_t */ #define PRIu8 "u" /* uint8_t */ #define PRIu16 "u" /* uint16_t */ #define PRIu32 "u" /* uint32_t */ #define PRIu64 __PRI64"u" /* uint64_t */ #define PRIuLEAST8 "u" /* uint_least8_t */ #define PRIuLEAST16 "u" /* uint_least16_t */ #define PRIuLEAST32 "u" /* uint_least32_t */ #define PRIuLEAST64 __PRI64"u" /* uint_least64_t */ #define PRIuFAST8 "u" /* uint_fast8_t */ #define PRIuFAST16 "u" /* uint_fast16_t */ #define PRIuFAST32 "u" /* uint_fast32_t */ #define PRIuFAST64 __PRI64"u" /* uint_fast64_t */ #define PRIuMAX "ju" /* uintmax_t */ #define PRIuPTR __PRIptr"u" /* uintptr_t */ #define PRIx8 "x" /* uint8_t */ #define PRIx16 "x" /* uint16_t */ #define PRIx32 "x" /* uint32_t */ #define PRIx64 __PRI64"x" /* uint64_t */ #define PRIxLEAST8 "x" /* uint_least8_t */ #define PRIxLEAST16 "x" /* uint_least16_t */ #define PRIxLEAST32 "x" /* uint_least32_t */ #define PRIxLEAST64 __PRI64"x" /* uint_least64_t */ #define PRIxFAST8 "x" /* uint_fast8_t */ #define PRIxFAST16 "x" /* uint_fast16_t */ #define PRIxFAST32 "x" /* uint_fast32_t */ #define PRIxFAST64 __PRI64"x" /* uint_fast64_t */ #define PRIxMAX "jx" /* uintmax_t */ #define PRIxPTR __PRIptr"x" /* uintptr_t */ #define PRIX8 "X" /* uint8_t */ #define PRIX16 "X" /* uint16_t */ #define PRIX32 "X" /* uint32_t */ #define PRIX64 __PRI64"X" /* uint64_t */ #define PRIXLEAST8 "X" /* uint_least8_t */ #define PRIXLEAST16 "X" /* uint_least16_t */ #define PRIXLEAST32 "X" /* uint_least32_t */ #define PRIXLEAST64 __PRI64"X" /* uint_least64_t */ #define PRIXFAST8 "X" /* uint_fast8_t */ #define PRIXFAST16 "X" /* uint_fast16_t */ #define PRIXFAST32 "X" /* uint_fast32_t */ #define PRIXFAST64 __PRI64"X" /* uint_fast64_t */ #define PRIXMAX "jX" /* uintmax_t */ #define PRIXPTR __PRIptr"X" /* uintptr_t */ /* fscanf(3) macros for signed integers. */ #define SCNd8 "hhd" /* int8_t */ #define SCNd16 "hd" /* int16_t */ #define SCNd32 "d" /* int32_t */ #define SCNd64 __PRI64"d" /* int64_t */ #define SCNdLEAST8 "hhd" /* int_least8_t */ #define SCNdLEAST16 "hd" /* int_least16_t */ #define SCNdLEAST32 "d" /* int_least32_t */ #define SCNdLEAST64 __PRI64"d" /* int_least64_t */ #define SCNdFAST8 "d" /* int_fast8_t */ #define SCNdFAST16 "d" /* int_fast16_t */ #define SCNdFAST32 "d" /* int_fast32_t */ #define SCNdFAST64 __PRI64"d" /* int_fast64_t */ #define SCNdMAX "jd" /* intmax_t */ #define SCNdPTR __PRIptr"d" /* intptr_t */ #define SCNi8 "hhi" /* int8_t */ #define SCNi16 "hi" /* int16_t */ #define SCNi32 "i" /* int32_t */ #define SCNi64 __PRI64"i" /* int64_t */ #define SCNiLEAST8 "hhi" /* int_least8_t */ #define SCNiLEAST16 "hi" /* int_least16_t */ #define SCNiLEAST32 "i" /* int_least32_t */ #define SCNiLEAST64 __PRI64"i" /* int_least64_t */ #define SCNiFAST8 "i" /* int_fast8_t */ #define SCNiFAST16 "i" /* int_fast16_t */ #define SCNiFAST32 "i" /* int_fast32_t */ #define SCNiFAST64 __PRI64"i" /* int_fast64_t */ #define SCNiMAX "ji" /* intmax_t */ #define SCNiPTR __PRIptr"i" /* intptr_t */ /* fscanf(3) macros for unsigned integers. */ #define SCNo8 "hho" /* uint8_t */ #define SCNo16 "ho" /* uint16_t */ #define SCNo32 "o" /* uint32_t */ #define SCNo64 __PRI64"o" /* uint64_t */ #define SCNoLEAST8 "hho" /* uint_least8_t */ #define SCNoLEAST16 "ho" /* uint_least16_t */ #define SCNoLEAST32 "o" /* uint_least32_t */ #define SCNoLEAST64 __PRI64"o" /* uint_least64_t */ #define SCNoFAST8 "o" /* uint_fast8_t */ #define SCNoFAST16 "o" /* uint_fast16_t */ #define SCNoFAST32 "o" /* uint_fast32_t */ #define SCNoFAST64 __PRI64"o" /* uint_fast64_t */ #define SCNoMAX "jo" /* uintmax_t */ #define SCNoPTR __PRIptr"o" /* uintptr_t */ #define SCNu8 "hhu" /* uint8_t */ #define SCNu16 "hu" /* uint16_t */ #define SCNu32 "u" /* uint32_t */ #define SCNu64 __PRI64"u" /* uint64_t */ #define SCNuLEAST8 "hhu" /* uint_least8_t */ #define SCNuLEAST16 "hu" /* uint_least16_t */ #define SCNuLEAST32 "u" /* uint_least32_t */ #define SCNuLEAST64 __PRI64"u" /* uint_least64_t */ #define SCNuFAST8 "u" /* uint_fast8_t */ #define SCNuFAST16 "u" /* uint_fast16_t */ #define SCNuFAST32 "u" /* uint_fast32_t */ #define SCNuFAST64 __PRI64"u" /* uint_fast64_t */ #define SCNuMAX "ju" /* uintmax_t */ #define SCNuPTR __PRIptr"u" /* uintptr_t */ #define SCNx8 "hhx" /* uint8_t */ #define SCNx16 "hx" /* uint16_t */ #define SCNx32 "x" /* uint32_t */ #define SCNx64 __PRI64"x" /* uint64_t */ #define SCNxLEAST8 "hhx" /* uint_least8_t */ #define SCNxLEAST16 "hx" /* uint_least16_t */ #define SCNxLEAST32 "x" /* uint_least32_t */ #define SCNxLEAST64 __PRI64"x" /* uint_least64_t */ #define SCNxFAST8 "x" /* uint_fast8_t */ #define SCNxFAST16 "x" /* uint_fast16_t */ #define SCNxFAST32 "x" /* uint_fast32_t */ #define SCNxFAST64 __PRI64"x" /* uint_fast64_t */ #define SCNxMAX "jx" /* uintmax_t */ #define SCNxPTR __PRIptr"x" /* uintptr_t */ #endif /* !_MACHINE_INTTYPES_H_ */ Index: head/sys/powerpc/include/altivec.h =================================================================== --- head/sys/powerpc/include/altivec.h (revision 326260) +++ head/sys/powerpc/include/altivec.h (revision 326261) @@ -1,40 +1,42 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2008 Nathan Whitehorn * All rights reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_ALTIVEC_H_ #define _MACHINE_ALTIVEC_H_ #define ALTIVEC_VSCR_NJ 0x00010000 /* Enable non-Java mode */ #define ALTIVEC_VSCR_SAT 0x00000001 /* Saturation status bit */ void enable_vec(struct thread *); void save_vec(struct thread *); void save_vec_nodrop(struct thread *); #endif /* _MACHINE_ALTIVEC_H_ */ Index: head/sys/powerpc/include/atomic.h =================================================================== --- head/sys/powerpc/include/atomic.h (revision 326260) +++ head/sys/powerpc/include/atomic.h (revision 326261) @@ -1,884 +1,886 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2008 Marcel Moolenaar * Copyright (c) 2001 Benno Rice * Copyright (c) 2001 David E. O'Brien * Copyright (c) 1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_ATOMIC_H_ #define _MACHINE_ATOMIC_H_ #ifndef _SYS_CDEFS_H_ #error this file needs sys/cdefs.h as a prerequisite #endif /* * The __ATOMIC_REL/ACQ() macros provide memory barriers only in conjunction * with the atomic lXarx/stXcx. sequences below. They are not exposed outside * of this file. See also Appendix B.2 of Book II of the architecture manual. * * Note that not all Book-E processors accept the light-weight sync variant. * In particular, early models of E500 cores are known to wedge. Bank on all * 64-bit capable CPUs to accept lwsync properly and pressimize 32-bit CPUs * to use the heavier-weight sync. */ #ifdef __powerpc64__ #define mb() __asm __volatile("sync" : : : "memory") #define rmb() __asm __volatile("lwsync" : : : "memory") #define wmb() __asm __volatile("lwsync" : : : "memory") #define __ATOMIC_REL() __asm __volatile("lwsync" : : : "memory") #define __ATOMIC_ACQ() __asm __volatile("isync" : : : "memory") #else #define mb() __asm __volatile("sync" : : : "memory") #define rmb() __asm __volatile("sync" : : : "memory") #define wmb() __asm __volatile("sync" : : : "memory") #define __ATOMIC_REL() __asm __volatile("sync" : : : "memory") #define __ATOMIC_ACQ() __asm __volatile("isync" : : : "memory") #endif static __inline void powerpc_lwsync(void) { #ifdef __powerpc64__ __asm __volatile("lwsync" : : : "memory"); #else __asm __volatile("sync" : : : "memory"); #endif } /* * atomic_add(p, v) * { *p += v; } */ #define __atomic_add_int(p, v, t) \ __asm __volatile( \ "1: lwarx %0, 0, %2\n" \ " add %0, %3, %0\n" \ " stwcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_add_int */ #ifdef __powerpc64__ #define __atomic_add_long(p, v, t) \ __asm __volatile( \ "1: ldarx %0, 0, %2\n" \ " add %0, %3, %0\n" \ " stdcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_add_long */ #else #define __atomic_add_long(p, v, t) \ __asm __volatile( \ "1: lwarx %0, 0, %2\n" \ " add %0, %3, %0\n" \ " stwcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_add_long */ #endif #define _ATOMIC_ADD(type) \ static __inline void \ atomic_add_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __atomic_add_##type(p, v, t); \ } \ \ static __inline void \ atomic_add_acq_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __atomic_add_##type(p, v, t); \ __ATOMIC_ACQ(); \ } \ \ static __inline void \ atomic_add_rel_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __ATOMIC_REL(); \ __atomic_add_##type(p, v, t); \ } \ /* _ATOMIC_ADD */ _ATOMIC_ADD(int) _ATOMIC_ADD(long) #define atomic_add_32 atomic_add_int #define atomic_add_acq_32 atomic_add_acq_int #define atomic_add_rel_32 atomic_add_rel_int #ifdef __powerpc64__ #define atomic_add_64 atomic_add_long #define atomic_add_acq_64 atomic_add_acq_long #define atomic_add_rel_64 atomic_add_rel_long #define atomic_add_ptr atomic_add_long #define atomic_add_acq_ptr atomic_add_acq_long #define atomic_add_rel_ptr atomic_add_rel_long #else #define atomic_add_ptr atomic_add_int #define atomic_add_acq_ptr atomic_add_acq_int #define atomic_add_rel_ptr atomic_add_rel_int #endif #undef _ATOMIC_ADD #undef __atomic_add_long #undef __atomic_add_int /* * atomic_clear(p, v) * { *p &= ~v; } */ #define __atomic_clear_int(p, v, t) \ __asm __volatile( \ "1: lwarx %0, 0, %2\n" \ " andc %0, %0, %3\n" \ " stwcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_clear_int */ #ifdef __powerpc64__ #define __atomic_clear_long(p, v, t) \ __asm __volatile( \ "1: ldarx %0, 0, %2\n" \ " andc %0, %0, %3\n" \ " stdcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_clear_long */ #else #define __atomic_clear_long(p, v, t) \ __asm __volatile( \ "1: lwarx %0, 0, %2\n" \ " andc %0, %0, %3\n" \ " stwcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_clear_long */ #endif #define _ATOMIC_CLEAR(type) \ static __inline void \ atomic_clear_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __atomic_clear_##type(p, v, t); \ } \ \ static __inline void \ atomic_clear_acq_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __atomic_clear_##type(p, v, t); \ __ATOMIC_ACQ(); \ } \ \ static __inline void \ atomic_clear_rel_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __ATOMIC_REL(); \ __atomic_clear_##type(p, v, t); \ } \ /* _ATOMIC_CLEAR */ _ATOMIC_CLEAR(int) _ATOMIC_CLEAR(long) #define atomic_clear_32 atomic_clear_int #define atomic_clear_acq_32 atomic_clear_acq_int #define atomic_clear_rel_32 atomic_clear_rel_int #ifdef __powerpc64__ #define atomic_clear_64 atomic_clear_long #define atomic_clear_acq_64 atomic_clear_acq_long #define atomic_clear_rel_64 atomic_clear_rel_long #define atomic_clear_ptr atomic_clear_long #define atomic_clear_acq_ptr atomic_clear_acq_long #define atomic_clear_rel_ptr atomic_clear_rel_long #else #define atomic_clear_ptr atomic_clear_int #define atomic_clear_acq_ptr atomic_clear_acq_int #define atomic_clear_rel_ptr atomic_clear_rel_int #endif #undef _ATOMIC_CLEAR #undef __atomic_clear_long #undef __atomic_clear_int /* * atomic_cmpset(p, o, n) */ /* TODO -- see below */ /* * atomic_load_acq(p) */ /* TODO -- see below */ /* * atomic_readandclear(p) */ /* TODO -- see below */ /* * atomic_set(p, v) * { *p |= v; } */ #define __atomic_set_int(p, v, t) \ __asm __volatile( \ "1: lwarx %0, 0, %2\n" \ " or %0, %3, %0\n" \ " stwcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_set_int */ #ifdef __powerpc64__ #define __atomic_set_long(p, v, t) \ __asm __volatile( \ "1: ldarx %0, 0, %2\n" \ " or %0, %3, %0\n" \ " stdcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_set_long */ #else #define __atomic_set_long(p, v, t) \ __asm __volatile( \ "1: lwarx %0, 0, %2\n" \ " or %0, %3, %0\n" \ " stwcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_set_long */ #endif #define _ATOMIC_SET(type) \ static __inline void \ atomic_set_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __atomic_set_##type(p, v, t); \ } \ \ static __inline void \ atomic_set_acq_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __atomic_set_##type(p, v, t); \ __ATOMIC_ACQ(); \ } \ \ static __inline void \ atomic_set_rel_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __ATOMIC_REL(); \ __atomic_set_##type(p, v, t); \ } \ /* _ATOMIC_SET */ _ATOMIC_SET(int) _ATOMIC_SET(long) #define atomic_set_32 atomic_set_int #define atomic_set_acq_32 atomic_set_acq_int #define atomic_set_rel_32 atomic_set_rel_int #ifdef __powerpc64__ #define atomic_set_64 atomic_set_long #define atomic_set_acq_64 atomic_set_acq_long #define atomic_set_rel_64 atomic_set_rel_long #define atomic_set_ptr atomic_set_long #define atomic_set_acq_ptr atomic_set_acq_long #define atomic_set_rel_ptr atomic_set_rel_long #else #define atomic_set_ptr atomic_set_int #define atomic_set_acq_ptr atomic_set_acq_int #define atomic_set_rel_ptr atomic_set_rel_int #endif #undef _ATOMIC_SET #undef __atomic_set_long #undef __atomic_set_int /* * atomic_subtract(p, v) * { *p -= v; } */ #define __atomic_subtract_int(p, v, t) \ __asm __volatile( \ "1: lwarx %0, 0, %2\n" \ " subf %0, %3, %0\n" \ " stwcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_subtract_int */ #ifdef __powerpc64__ #define __atomic_subtract_long(p, v, t) \ __asm __volatile( \ "1: ldarx %0, 0, %2\n" \ " subf %0, %3, %0\n" \ " stdcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_subtract_long */ #else #define __atomic_subtract_long(p, v, t) \ __asm __volatile( \ "1: lwarx %0, 0, %2\n" \ " subf %0, %3, %0\n" \ " stwcx. %0, 0, %2\n" \ " bne- 1b\n" \ : "=&r" (t), "=m" (*p) \ : "r" (p), "r" (v), "m" (*p) \ : "cr0", "memory") \ /* __atomic_subtract_long */ #endif #define _ATOMIC_SUBTRACT(type) \ static __inline void \ atomic_subtract_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __atomic_subtract_##type(p, v, t); \ } \ \ static __inline void \ atomic_subtract_acq_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __atomic_subtract_##type(p, v, t); \ __ATOMIC_ACQ(); \ } \ \ static __inline void \ atomic_subtract_rel_##type(volatile u_##type *p, u_##type v) { \ u_##type t; \ __ATOMIC_REL(); \ __atomic_subtract_##type(p, v, t); \ } \ /* _ATOMIC_SUBTRACT */ _ATOMIC_SUBTRACT(int) _ATOMIC_SUBTRACT(long) #define atomic_subtract_32 atomic_subtract_int #define atomic_subtract_acq_32 atomic_subtract_acq_int #define atomic_subtract_rel_32 atomic_subtract_rel_int #ifdef __powerpc64__ #define atomic_subtract_64 atomic_subtract_long #define atomic_subtract_acq_64 atomic_subract_acq_long #define atomic_subtract_rel_64 atomic_subtract_rel_long #define atomic_subtract_ptr atomic_subtract_long #define atomic_subtract_acq_ptr atomic_subtract_acq_long #define atomic_subtract_rel_ptr atomic_subtract_rel_long #else #define atomic_subtract_ptr atomic_subtract_int #define atomic_subtract_acq_ptr atomic_subtract_acq_int #define atomic_subtract_rel_ptr atomic_subtract_rel_int #endif #undef _ATOMIC_SUBTRACT #undef __atomic_subtract_long #undef __atomic_subtract_int /* * atomic_store_rel(p, v) */ /* TODO -- see below */ /* * Old/original implementations that still need revisiting. */ static __inline u_int atomic_readandclear_int(volatile u_int *addr) { u_int result,temp; __asm __volatile ( "\tsync\n" /* drain writes */ "1:\tlwarx %0, 0, %3\n\t" /* load old value */ "li %1, 0\n\t" /* load new value */ "stwcx. %1, 0, %3\n\t" /* attempt to store */ "bne- 1b\n\t" /* spin if failed */ : "=&r"(result), "=&r"(temp), "=m" (*addr) : "r" (addr), "m" (*addr) : "cr0", "memory"); return (result); } #ifdef __powerpc64__ static __inline u_long atomic_readandclear_long(volatile u_long *addr) { u_long result,temp; __asm __volatile ( "\tsync\n" /* drain writes */ "1:\tldarx %0, 0, %3\n\t" /* load old value */ "li %1, 0\n\t" /* load new value */ "stdcx. %1, 0, %3\n\t" /* attempt to store */ "bne- 1b\n\t" /* spin if failed */ : "=&r"(result), "=&r"(temp), "=m" (*addr) : "r" (addr), "m" (*addr) : "cr0", "memory"); return (result); } #endif #define atomic_readandclear_32 atomic_readandclear_int #ifdef __powerpc64__ #define atomic_readandclear_64 atomic_readandclear_long #define atomic_readandclear_ptr atomic_readandclear_long #else static __inline u_long atomic_readandclear_long(volatile u_long *addr) { return ((u_long)atomic_readandclear_int((volatile u_int *)addr)); } #define atomic_readandclear_ptr atomic_readandclear_int #endif /* * We assume that a = b will do atomic loads and stores. */ #define ATOMIC_STORE_LOAD(TYPE) \ static __inline u_##TYPE \ atomic_load_acq_##TYPE(volatile u_##TYPE *p) \ { \ u_##TYPE v; \ \ v = *p; \ mb(); \ return (v); \ } \ \ static __inline void \ atomic_store_rel_##TYPE(volatile u_##TYPE *p, u_##TYPE v) \ { \ \ powerpc_lwsync(); \ *p = v; \ } ATOMIC_STORE_LOAD(int) #define atomic_load_acq_32 atomic_load_acq_int #define atomic_store_rel_32 atomic_store_rel_int #ifdef __powerpc64__ ATOMIC_STORE_LOAD(long) #define atomic_load_acq_64 atomic_load_acq_long #define atomic_store_rel_64 atomic_store_rel_long #define atomic_load_acq_ptr atomic_load_acq_long #define atomic_store_rel_ptr atomic_store_rel_long #else static __inline u_long atomic_load_acq_long(volatile u_long *addr) { return ((u_long)atomic_load_acq_int((volatile u_int *)addr)); } static __inline void atomic_store_rel_long(volatile u_long *addr, u_long val) { atomic_store_rel_int((volatile u_int *)addr, (u_int)val); } #define atomic_load_acq_ptr atomic_load_acq_int #define atomic_store_rel_ptr atomic_store_rel_int #endif #undef ATOMIC_STORE_LOAD /* * Atomically compare the value stored at *p with cmpval and if the * two values are equal, update the value of *p with newval. Returns * zero if the compare failed, nonzero otherwise. */ static __inline int atomic_cmpset_int(volatile u_int* p, u_int cmpval, u_int newval) { int ret; __asm __volatile ( "1:\tlwarx %0, 0, %2\n\t" /* load old value */ "cmplw %3, %0\n\t" /* compare */ "bne 2f\n\t" /* exit if not equal */ "stwcx. %4, 0, %2\n\t" /* attempt to store */ "bne- 1b\n\t" /* spin if failed */ "li %0, 1\n\t" /* success - retval = 1 */ "b 3f\n\t" /* we've succeeded */ "2:\n\t" "stwcx. %0, 0, %2\n\t" /* clear reservation (74xx) */ "li %0, 0\n\t" /* failure - retval = 0 */ "3:\n\t" : "=&r" (ret), "=m" (*p) : "r" (p), "r" (cmpval), "r" (newval), "m" (*p) : "cr0", "memory"); return (ret); } static __inline int atomic_cmpset_long(volatile u_long* p, u_long cmpval, u_long newval) { int ret; __asm __volatile ( #ifdef __powerpc64__ "1:\tldarx %0, 0, %2\n\t" /* load old value */ "cmpld %3, %0\n\t" /* compare */ "bne 2f\n\t" /* exit if not equal */ "stdcx. %4, 0, %2\n\t" /* attempt to store */ #else "1:\tlwarx %0, 0, %2\n\t" /* load old value */ "cmplw %3, %0\n\t" /* compare */ "bne 2f\n\t" /* exit if not equal */ "stwcx. %4, 0, %2\n\t" /* attempt to store */ #endif "bne- 1b\n\t" /* spin if failed */ "li %0, 1\n\t" /* success - retval = 1 */ "b 3f\n\t" /* we've succeeded */ "2:\n\t" #ifdef __powerpc64__ "stdcx. %0, 0, %2\n\t" /* clear reservation (74xx) */ #else "stwcx. %0, 0, %2\n\t" /* clear reservation (74xx) */ #endif "li %0, 0\n\t" /* failure - retval = 0 */ "3:\n\t" : "=&r" (ret), "=m" (*p) : "r" (p), "r" (cmpval), "r" (newval), "m" (*p) : "cr0", "memory"); return (ret); } static __inline int atomic_cmpset_acq_int(volatile u_int *p, u_int cmpval, u_int newval) { int retval; retval = atomic_cmpset_int(p, cmpval, newval); __ATOMIC_ACQ(); return (retval); } static __inline int atomic_cmpset_rel_int(volatile u_int *p, u_int cmpval, u_int newval) { __ATOMIC_REL(); return (atomic_cmpset_int(p, cmpval, newval)); } static __inline int atomic_cmpset_acq_long(volatile u_long *p, u_long cmpval, u_long newval) { u_long retval; retval = atomic_cmpset_long(p, cmpval, newval); __ATOMIC_ACQ(); return (retval); } static __inline int atomic_cmpset_rel_long(volatile u_long *p, u_long cmpval, u_long newval) { __ATOMIC_REL(); return (atomic_cmpset_long(p, cmpval, newval)); } #define atomic_cmpset_32 atomic_cmpset_int #define atomic_cmpset_acq_32 atomic_cmpset_acq_int #define atomic_cmpset_rel_32 atomic_cmpset_rel_int #ifdef __powerpc64__ #define atomic_cmpset_64 atomic_cmpset_long #define atomic_cmpset_acq_64 atomic_cmpset_acq_long #define atomic_cmpset_rel_64 atomic_cmpset_rel_long #define atomic_cmpset_ptr atomic_cmpset_long #define atomic_cmpset_acq_ptr atomic_cmpset_acq_long #define atomic_cmpset_rel_ptr atomic_cmpset_rel_long #else #define atomic_cmpset_ptr atomic_cmpset_int #define atomic_cmpset_acq_ptr atomic_cmpset_acq_int #define atomic_cmpset_rel_ptr atomic_cmpset_rel_int #endif /* * Atomically compare the value stored at *p with *cmpval and if the * two values are equal, update the value of *p with newval. Returns * zero if the compare failed and sets *cmpval to the read value from *p, * nonzero otherwise. */ static __inline int atomic_fcmpset_int(volatile u_int *p, u_int *cmpval, u_int newval) { int ret; __asm __volatile ( "lwarx %0, 0, %3\n\t" /* load old value */ "cmplw %4, %0\n\t" /* compare */ "bne 1f\n\t" /* exit if not equal */ "stwcx. %5, 0, %3\n\t" /* attempt to store */ "bne- 1f\n\t" /* exit if failed */ "li %0, 1\n\t" /* success - retval = 1 */ "b 2f\n\t" /* we've succeeded */ "1:\n\t" "stwcx. %0, 0, %3\n\t" /* clear reservation (74xx) */ "stwx %0, 0, %7\n\t" "li %0, 0\n\t" /* failure - retval = 0 */ "2:\n\t" : "=&r" (ret), "=m" (*p), "=m" (*cmpval) : "r" (p), "r" (*cmpval), "r" (newval), "m" (*p), "r"(cmpval) : "cr0", "memory"); return (ret); } static __inline int atomic_fcmpset_long(volatile u_long *p, u_long *cmpval, u_long newval) { int ret; __asm __volatile ( #ifdef __powerpc64__ "ldarx %0, 0, %3\n\t" /* load old value */ "cmpld %4, %0\n\t" /* compare */ "bne 1f\n\t" /* exit if not equal */ "stdcx. %5, 0, %3\n\t" /* attempt to store */ #else "lwarx %0, 0, %3\n\t" /* load old value */ "cmplw %4, %0\n\t" /* compare */ "bne 1f\n\t" /* exit if not equal */ "stwcx. %5, 0, %3\n\t" /* attempt to store */ #endif "bne- 1f\n\t" /* exit if failed */ "li %0, 1\n\t" /* success - retval = 1 */ "b 2f\n\t" /* we've succeeded */ "1:\n\t" #ifdef __powerpc64__ "stdcx. %0, 0, %3\n\t" /* clear reservation (74xx) */ "stdx %0, 0, %7\n\t" #else "stwcx. %0, 0, %3\n\t" /* clear reservation (74xx) */ "stwx %0, 0, %7\n\t" #endif "li %0, 0\n\t" /* failure - retval = 0 */ "2:\n\t" : "=&r" (ret), "=m" (*p), "=m" (*cmpval) : "r" (p), "r" (*cmpval), "r" (newval), "m" (*p), "r"(cmpval) : "cr0", "memory"); return (ret); } static __inline int atomic_fcmpset_acq_int(volatile u_int *p, u_int *cmpval, u_int newval) { int retval; retval = atomic_fcmpset_int(p, cmpval, newval); __ATOMIC_ACQ(); return (retval); } static __inline int atomic_fcmpset_rel_int(volatile u_int *p, u_int *cmpval, u_int newval) { __ATOMIC_REL(); return (atomic_fcmpset_int(p, cmpval, newval)); } static __inline int atomic_fcmpset_acq_long(volatile u_long *p, u_long *cmpval, u_long newval) { u_long retval; retval = atomic_fcmpset_long(p, cmpval, newval); __ATOMIC_ACQ(); return (retval); } static __inline int atomic_fcmpset_rel_long(volatile u_long *p, u_long *cmpval, u_long newval) { __ATOMIC_REL(); return (atomic_fcmpset_long(p, cmpval, newval)); } #define atomic_fcmpset_32 atomic_fcmpset_int #define atomic_fcmpset_acq_32 atomic_fcmpset_acq_int #define atomic_fcmpset_rel_32 atomic_fcmpset_rel_int #ifdef __powerpc64__ #define atomic_fcmpset_64 atomic_fcmpset_long #define atomic_fcmpset_acq_64 atomic_fcmpset_acq_long #define atomic_fcmpset_rel_64 atomic_fcmpset_rel_long #define atomic_fcmpset_ptr atomic_fcmpset_long #define atomic_fcmpset_acq_ptr atomic_fcmpset_acq_long #define atomic_fcmpset_rel_ptr atomic_fcmpset_rel_long #else #define atomic_fcmpset_ptr atomic_fcmpset_int #define atomic_fcmpset_acq_ptr atomic_fcmpset_acq_int #define atomic_fcmpset_rel_ptr atomic_fcmpset_rel_int #endif static __inline u_int atomic_fetchadd_int(volatile u_int *p, u_int v) { u_int value; do { value = *p; } while (!atomic_cmpset_int(p, value, value + v)); return (value); } static __inline u_long atomic_fetchadd_long(volatile u_long *p, u_long v) { u_long value; do { value = *p; } while (!atomic_cmpset_long(p, value, value + v)); return (value); } static __inline u_int atomic_swap_32(volatile u_int *p, u_int v) { u_int prev; __asm __volatile( "1: lwarx %0,0,%2\n" " stwcx. %3,0,%2\n" " bne- 1b\n" : "=&r" (prev), "+m" (*(volatile u_int *)p) : "r" (p), "r" (v) : "cr0", "memory"); return (prev); } #ifdef __powerpc64__ static __inline u_long atomic_swap_64(volatile u_long *p, u_long v) { u_long prev; __asm __volatile( "1: ldarx %0,0,%2\n" " stdcx. %3,0,%2\n" " bne- 1b\n" : "=&r" (prev), "+m" (*(volatile u_long *)p) : "r" (p), "r" (v) : "cr0", "memory"); return (prev); } #endif #define atomic_fetchadd_32 atomic_fetchadd_int #define atomic_swap_int atomic_swap_32 #ifdef __powerpc64__ #define atomic_fetchadd_64 atomic_fetchadd_long #define atomic_swap_long atomic_swap_64 #define atomic_swap_ptr atomic_swap_64 #endif #undef __ATOMIC_REL #undef __ATOMIC_ACQ static __inline void atomic_thread_fence_acq(void) { powerpc_lwsync(); } static __inline void atomic_thread_fence_rel(void) { powerpc_lwsync(); } static __inline void atomic_thread_fence_acq_rel(void) { powerpc_lwsync(); } static __inline void atomic_thread_fence_seq_cst(void) { __asm __volatile("sync" : : : "memory"); } #endif /* ! _MACHINE_ATOMIC_H_ */ Index: head/sys/powerpc/include/bat.h =================================================================== --- head/sys/powerpc/include/bat.h (revision 326260) +++ head/sys/powerpc/include/bat.h (revision 326261) @@ -1,167 +1,169 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-NetBSD AND BSD-4-Clause + * * Copyright (c) 1999 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /*- * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: bat.h,v 1.2 1999/12/18 01:36:06 thorpej Exp $ * $FreeBSD$ */ #ifndef _MACHINE_BAT_H_ #define _MACHINE_BAT_H_ #ifndef LOCORE struct bat { u_int32_t batu; u_int32_t batl; }; #endif /* Lower BAT bits (all but PowerPC 601): */ #define BAT_PBS 0xfffe0000 /* physical block start */ #define BAT_W 0x00000040 /* 1 = write-through, 0 = write-back */ #define BAT_I 0x00000020 /* cache inhibit */ #define BAT_M 0x00000010 /* memory coherency enable */ #define BAT_G 0x00000008 /* guarded region */ #define BAT_PP_NONE 0x00000000 /* no access permission */ #define BAT_PP_RO_S 0x00000001 /* read-only (soft) */ #define BAT_PP_RW 0x00000002 /* read/write */ #define BAT_PP_RO 0x00000003 /* read-only */ /* Upper BAT bits (all but PowerPC 601): */ #define BAT_EBS 0xfffe0000 /* effective block start */ #define BAT_BL 0x00001ffc /* block length */ #define BAT_Vs 0x00000002 /* valid in supervisor mode */ #define BAT_Vu 0x00000001 /* valid in user mode */ #define BAT_V (BAT_Vs|BAT_Vu) /* Block Length encoding (all but PowerPC 601): */ #define BAT_BL_128K 0x00000000 #define BAT_BL_256K 0x00000004 #define BAT_BL_512K 0x0000000c #define BAT_BL_1M 0x0000001c #define BAT_BL_2M 0x0000003c #define BAT_BL_4M 0x0000007c #define BAT_BL_8M 0x000000fc #define BAT_BL_16M 0x000001fc #define BAT_BL_32M 0x000003fc #define BAT_BL_64M 0x000007fc #define BAT_BL_128M 0x00000ffc #define BAT_BL_256M 0x00001ffc #define BATU(va, len, v) \ (((va) & BAT_EBS) | ((len) & BAT_BL) | ((v) & BAT_V)) #define BATL(pa, wimg, pp) \ (((pa) & BAT_PBS) | (wimg) | (pp)) /* Lower BAT bits (PowerPC 601): */ #define BAT601_PBN 0xfffe0000 /* physical block number */ #define BAT601_V 0x00000040 /* valid */ #define BAT601_BSM 0x0000003f /* block size mask */ /* Upper BAT bits (PowerPC 601): */ #define BAT601_BLPI 0xfffe0000 /* block logical page index */ #define BAT601_W 0x00000040 /* 1 = write-through, 0 = write-back */ #define BAT601_I 0x00000020 /* cache inhibit */ #define BAT601_M 0x00000010 /* memory coherency enable */ #define BAT601_Ks 0x00000008 /* key-supervisor */ #define BAT601_Ku 0x00000004 /* key-user */ /* * Permission bits on the PowerPC 601 are modified by the appropriate * Key bit: * * Key PP Access * 0 NONE read/write * 0 RO_S read/write * 0 RW read/write * 0 RO read-only * * 1 NONE none * 1 RO_S read-only * 1 RW read/write * 1 RO read-only */ #define BAT601_PP_NONE 0x00000000 /* no access permission */ #define BAT601_PP_RO_S 0x00000001 /* read-only (soft) */ #define BAT601_PP_RW 0x00000002 /* read/write */ #define BAT601_PP_RO 0x00000003 /* read-only */ /* Block Size Mask encoding (PowerPC 601): */ #define BAT601_BSM_128K 0x00000000 #define BAT601_BSM_256K 0x00000001 #define BAT601_BSM_512K 0x00000003 #define BAT601_BSM_1M 0x00000007 #define BAT601_BSM_2M 0x0000000f #define BAT601_BSM_4M 0x0000001f #define BAT601_BSM_8M 0x0000003f #define BATU601(va, wim, key, pp) \ (((va) & BAT601_BLPI) | (wim) | (key) | (pp)) #define BATL601(pa, size, v) \ (((pa) & BAT601_PBN) | (v) | (size)) #if defined(_KERNEL) && !defined(LOCORE) extern struct bat battable[16]; #endif #endif /* _MACHINE_BAT_H_ */ Index: head/sys/powerpc/include/bus.h =================================================================== --- head/sys/powerpc/include/bus.h (revision 326260) +++ head/sys/powerpc/include/bus.h (revision 326261) @@ -1,465 +1,467 @@ /* $NetBSD: bus.h,v 1.11 2003/07/28 17:35:54 thorpej Exp $ */ /*- + * SPDX-License-Identifier: BSD-2-Clause-NetBSD AND BSD-4-Clause + * * Copyright (c) 1996, 1997, 1998, 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /*- * Copyright (c) 1996 Charles M. Hannum. All rights reserved. * Copyright (c) 1996 Christopher G. Demetriou. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Christopher G. Demetriou * for the NetBSD Project. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_BUS_H_ #define _MACHINE_BUS_H_ #include #define BUS_SPACE_ALIGNED_POINTER(p, t) ALIGNED_POINTER(p, t) #define BUS_SPACE_MAXADDR_24BIT 0xFFFFFFUL #define BUS_SPACE_MAXADDR_32BIT 0xFFFFFFFFUL #define BUS_SPACE_MAXSIZE_24BIT 0xFFFFFFUL #define BUS_SPACE_MAXSIZE_32BIT 0xFFFFFFFFUL #ifdef __powerpc64__ #define BUS_SPACE_MAXADDR 0xFFFFFFFFFFFFFFFFUL #define BUS_SPACE_MAXSIZE 0xFFFFFFFFFFFFFFFFUL #else #ifdef BOOKE #define BUS_SPACE_MAXADDR 0xFFFFFFFFFULL #define BUS_SPACE_MAXSIZE 0xFFFFFFFFUL #else #define BUS_SPACE_MAXADDR 0xFFFFFFFFUL #define BUS_SPACE_MAXSIZE 0xFFFFFFFFUL #endif #endif #define BUS_SPACE_MAP_CACHEABLE 0x01 #define BUS_SPACE_MAP_LINEAR 0x02 #define BUS_SPACE_MAP_PREFETCHABLE 0x04 #define BUS_SPACE_UNRESTRICTED (~0) #define BUS_SPACE_BARRIER_READ 0x01 #define BUS_SPACE_BARRIER_WRITE 0x02 struct bus_space_access; struct bus_space { /* mapping/unmapping */ int (*bs_map)(bus_addr_t, bus_size_t, int, bus_space_handle_t *); void (*bs_unmap)(bus_size_t); int (*bs_subregion)(bus_space_handle_t, bus_size_t, bus_size_t, bus_space_handle_t *); /* allocation/deallocation */ int (*bs_alloc)(bus_addr_t, bus_addr_t, bus_size_t, bus_size_t, bus_size_t, int, bus_addr_t *, bus_space_handle_t *); void (*bs_free)(bus_space_handle_t, bus_size_t); void (*bs_barrier)(bus_space_handle_t, bus_size_t, bus_size_t, int); /* Read single. */ uint8_t (*bs_r_1)(bus_space_handle_t, bus_size_t); uint16_t (*bs_r_2)(bus_space_handle_t, bus_size_t); uint32_t (*bs_r_4)(bus_space_handle_t, bus_size_t); uint64_t (*bs_r_8)(bus_space_handle_t, bus_size_t); uint16_t (*bs_r_s_2)(bus_space_handle_t, bus_size_t); uint32_t (*bs_r_s_4)(bus_space_handle_t, bus_size_t); uint64_t (*bs_r_s_8)(bus_space_handle_t, bus_size_t); /* read multiple */ void (*bs_rm_1)(bus_space_handle_t, bus_size_t, uint8_t *, bus_size_t); void (*bs_rm_2)(bus_space_handle_t, bus_size_t, uint16_t *, bus_size_t); void (*bs_rm_4)(bus_space_handle_t, bus_size_t, uint32_t *, bus_size_t); void (*bs_rm_8)(bus_space_handle_t, bus_size_t, uint64_t *, bus_size_t); void (*bs_rm_s_2)(bus_space_handle_t, bus_size_t, uint16_t *, bus_size_t); void (*bs_rm_s_4)(bus_space_handle_t, bus_size_t, uint32_t *, bus_size_t); void (*bs_rm_s_8)(bus_space_handle_t, bus_size_t, uint64_t *, bus_size_t); /* read region */ void (*bs_rr_1)(bus_space_handle_t, bus_size_t, uint8_t *, bus_size_t); void (*bs_rr_2)(bus_space_handle_t, bus_size_t, uint16_t *, bus_size_t); void (*bs_rr_4)(bus_space_handle_t, bus_size_t, uint32_t *, bus_size_t); void (*bs_rr_8)(bus_space_handle_t, bus_size_t, uint64_t *, bus_size_t); void (*bs_rr_s_2)(bus_space_handle_t, bus_size_t, uint16_t *, bus_size_t); void (*bs_rr_s_4)(bus_space_handle_t, bus_size_t, uint32_t *, bus_size_t); void (*bs_rr_s_8)(bus_space_handle_t, bus_size_t, uint64_t *, bus_size_t); /* write */ void (*bs_w_1)(bus_space_handle_t, bus_size_t, uint8_t); void (*bs_w_2)(bus_space_handle_t, bus_size_t, uint16_t); void (*bs_w_4)(bus_space_handle_t, bus_size_t, uint32_t); void (*bs_w_8)(bus_space_handle_t, bus_size_t, uint64_t); void (*bs_w_s_2)(bus_space_handle_t, bus_size_t, uint16_t); void (*bs_w_s_4)(bus_space_handle_t, bus_size_t, uint32_t); void (*bs_w_s_8)(bus_space_handle_t, bus_size_t, uint64_t); /* write multiple */ void (*bs_wm_1)(bus_space_handle_t, bus_size_t, const uint8_t *, bus_size_t); void (*bs_wm_2)(bus_space_handle_t, bus_size_t, const uint16_t *, bus_size_t); void (*bs_wm_4)(bus_space_handle_t, bus_size_t, const uint32_t *, bus_size_t); void (*bs_wm_8)(bus_space_handle_t, bus_size_t, const uint64_t *, bus_size_t); void (*bs_wm_s_2)(bus_space_handle_t, bus_size_t, const uint16_t *, bus_size_t); void (*bs_wm_s_4)(bus_space_handle_t, bus_size_t, const uint32_t *, bus_size_t); void (*bs_wm_s_8)(bus_space_handle_t, bus_size_t, const uint64_t *, bus_size_t); /* write region */ void (*bs_wr_1)(bus_space_handle_t, bus_size_t, const uint8_t *, bus_size_t); void (*bs_wr_2)(bus_space_handle_t, bus_size_t, const uint16_t *, bus_size_t); void (*bs_wr_4)(bus_space_handle_t, bus_size_t, const uint32_t *, bus_size_t); void (*bs_wr_8)(bus_space_handle_t, bus_size_t, const uint64_t *, bus_size_t); void (*bs_wr_s_2)(bus_space_handle_t, bus_size_t, const uint16_t *, bus_size_t); void (*bs_wr_s_4)(bus_space_handle_t, bus_size_t, const uint32_t *, bus_size_t); void (*bs_wr_s_8)(bus_space_handle_t, bus_size_t, const uint64_t *, bus_size_t); /* set multiple */ void (*bs_sm_1)(bus_space_handle_t, bus_size_t, uint8_t, bus_size_t); void (*bs_sm_2)(bus_space_handle_t, bus_size_t, uint16_t, bus_size_t); void (*bs_sm_4)(bus_space_handle_t, bus_size_t, uint32_t, bus_size_t); void (*bs_sm_8)(bus_space_handle_t, bus_size_t, uint64_t, bus_size_t); void (*bs_sm_s_2)(bus_space_handle_t, bus_size_t, uint16_t, bus_size_t); void (*bs_sm_s_4)(bus_space_handle_t, bus_size_t, uint32_t, bus_size_t); void (*bs_sm_s_8)(bus_space_handle_t, bus_size_t, uint64_t, bus_size_t); /* set region */ void (*bs_sr_1)(bus_space_handle_t, bus_size_t, uint8_t, bus_size_t); void (*bs_sr_2)(bus_space_handle_t, bus_size_t, uint16_t, bus_size_t); void (*bs_sr_4)(bus_space_handle_t, bus_size_t, uint32_t, bus_size_t); void (*bs_sr_8)(bus_space_handle_t, bus_size_t, uint64_t, bus_size_t); void (*bs_sr_s_2)(bus_space_handle_t, bus_size_t, uint16_t, bus_size_t); void (*bs_sr_s_4)(bus_space_handle_t, bus_size_t, uint32_t, bus_size_t); void (*bs_sr_s_8)(bus_space_handle_t, bus_size_t, uint64_t, bus_size_t); /* copy region */ void (*bs_cr_1)(bus_space_handle_t, bus_size_t, bus_space_handle_t, bus_size_t, bus_size_t); void (*bs_cr_2)(bus_space_handle_t, bus_size_t, bus_space_handle_t, bus_size_t, bus_size_t); void (*bs_cr_4)(bus_space_handle_t, bus_size_t, bus_space_handle_t, bus_size_t, bus_size_t); void (*bs_cr_8)(bus_space_handle_t, bus_size_t, bus_space_handle_t, bus_size_t, bus_size_t); void (*bs_cr_s_2)(bus_space_handle_t, bus_size_t, bus_space_handle_t, bus_size_t, bus_size_t); void (*bs_cr_s_4)(bus_space_handle_t, bus_size_t, bus_space_handle_t, bus_size_t, bus_size_t); void (*bs_cr_s_8)(bus_space_handle_t, bus_size_t, bus_space_handle_t, bus_size_t, bus_size_t); }; extern struct bus_space bs_be_tag; extern struct bus_space bs_le_tag; #define __bs_c(a,b) __CONCAT(a,b) #define __bs_opname(op,size) __bs_c(__bs_c(__bs_c(bs_,op),_),size) #define __bs_rs(sz, t, h, o) \ (*(t)->__bs_opname(r,sz))(h, o) #define __bs_ws(sz, t, h, o, v) \ (*(t)->__bs_opname(w,sz))(h, o, v) #define __bs_nonsingle(type, sz, t, h, o, a, c) \ (*(t)->__bs_opname(type,sz))(h, o, a, c) #define __bs_set(type, sz, t, h, o, v, c) \ (*(t)->__bs_opname(type,sz))(h, o, v, c) #define __bs_copy(sz, t, h1, o1, h2, o2, cnt) \ (*(t)->__bs_opname(c,sz))(h1, o1, h2, o2, cnt) /* * Mapping and unmapping operations. */ #define bus_space_map(t, a, s, c, hp) (*(t)->bs_map)(a, s, c, hp) #define bus_space_unmap(t, h, s) (*(t)->bs_unmap)(h, s) #define bus_space_subregion(t, h, o, s, hp) (*(t)->bs_subregion)(h, o, s, hp) /* * Allocation and deallocation operations. */ #define bus_space_alloc(t, rs, re, s, a, b, c, ap, hp) \ (*(t)->bs_alloc)(rs, re, s, a, b, c, ap, hp) #define bus_space_free(t, h, s) \ (*(t)->bs_free)(h, s) /* * Bus barrier operations. */ #define bus_space_barrier(t, h, o, l, f) (*(t)->bs_barrier)(h, o, l, f) /* * Bus read (single) operations. */ #define bus_space_read_1(t, h, o) __bs_rs(1,t,h,o) #define bus_space_read_2(t, h, o) __bs_rs(2,t,h,o) #define bus_space_read_4(t, h, o) __bs_rs(4,t,h,o) #define bus_space_read_8(t, h, o) __bs_rs(8,t,h,o) #define bus_space_read_stream_1 bus_space_read_1 #define bus_space_read_stream_2(t, h, o) __bs_rs(s_2,t,h,o) #define bus_space_read_stream_4(t, h, o) __bs_rs(s_4,t,h,o) #define bus_space_read_stream_8(t, h, o) __bs_rs(s_8,t,h,o) /* * Bus read multiple operations. */ #define bus_space_read_multi_1(t, h, o, a, c) \ __bs_nonsingle(rm,1,(t),(h),(o),(a),(c)) #define bus_space_read_multi_2(t, h, o, a, c) \ __bs_nonsingle(rm,2,(t),(h),(o),(a),(c)) #define bus_space_read_multi_4(t, h, o, a, c) \ __bs_nonsingle(rm,4,(t),(h),(o),(a),(c)) #define bus_space_read_multi_8(t, h, o, a, c) \ __bs_nonsingle(rm,8,(t),(h),(o),(a),(c)) #define bus_space_read_multi_stream_1 bus_space_read_multi_1 #define bus_space_read_multi_stream_2(t, h, o, a, c) \ __bs_nonsingle(rm,s_2,(t),(h),(o),(a),(c)) #define bus_space_read_multi_stream_4(t, h, o, a, c) \ __bs_nonsingle(rm,s_4,(t),(h),(o),(a),(c)) #define bus_space_read_multi_stream_8(t, h, o, a, c) \ __bs_nonsingle(rm,s_8,(t),(h),(o),(a),(c)) /* * Bus read region operations. */ #define bus_space_read_region_1(t, h, o, a, c) \ __bs_nonsingle(rr,1,(t),(h),(o),(a),(c)) #define bus_space_read_region_2(t, h, o, a, c) \ __bs_nonsingle(rr,2,(t),(h),(o),(a),(c)) #define bus_space_read_region_4(t, h, o, a, c) \ __bs_nonsingle(rr,4,(t),(h),(o),(a),(c)) #define bus_space_read_region_8(t, h, o, a, c) \ __bs_nonsingle(rr,8,(t),(h),(o),(a),(c)) #define bus_space_read_region_stream_1 bus_space_read_region_1 #define bus_space_read_region_stream_2(t, h, o, a, c) \ __bs_nonsingle(rr,s_2,(t),(h),(o),(a),(c)) #define bus_space_read_region_stream_4(t, h, o, a, c) \ __bs_nonsingle(rr,s_4,(t),(h),(o),(a),(c)) #define bus_space_read_region_stream_8(t, h, o, a, c) \ __bs_nonsingle(rr,s_8,(t),(h),(o),(a),(c)) /* * Bus write (single) operations. */ #define bus_space_write_1(t, h, o, v) __bs_ws(1,(t),(h),(o),(v)) #define bus_space_write_2(t, h, o, v) __bs_ws(2,(t),(h),(o),(v)) #define bus_space_write_4(t, h, o, v) __bs_ws(4,(t),(h),(o),(v)) #define bus_space_write_8(t, h, o, v) __bs_ws(8,(t),(h),(o),(v)) #define bus_space_write_stream_1 bus_space_write_1 #define bus_space_write_stream_2(t, h, o, v) __bs_ws(s_2,(t),(h),(o),(v)) #define bus_space_write_stream_4(t, h, o, v) __bs_ws(s_4,(t),(h),(o),(v)) #define bus_space_write_stream_8(t, h, o, v) __bs_ws(s_8,(t),(h),(o),(v)) /* * Bus write multiple operations. */ #define bus_space_write_multi_1(t, h, o, a, c) \ __bs_nonsingle(wm,1,(t),(h),(o),(a),(c)) #define bus_space_write_multi_2(t, h, o, a, c) \ __bs_nonsingle(wm,2,(t),(h),(o),(a),(c)) #define bus_space_write_multi_4(t, h, o, a, c) \ __bs_nonsingle(wm,4,(t),(h),(o),(a),(c)) #define bus_space_write_multi_8(t, h, o, a, c) \ __bs_nonsingle(wm,8,(t),(h),(o),(a),(c)) #define bus_space_write_multi_stream_1 bus_space_write_multi_1 #define bus_space_write_multi_stream_2(t, h, o, a, c) \ __bs_nonsingle(wm,s_2,(t),(h),(o),(a),(c)) #define bus_space_write_multi_stream_4(t, h, o, a, c) \ __bs_nonsingle(wm,s_4,(t),(h),(o),(a),(c)) #define bus_space_write_multi_stream_8(t, h, o, a, c) \ __bs_nonsingle(wm,s_8,(t),(h),(o),(a),(c)) /* * Bus write region operations. */ #define bus_space_write_region_1(t, h, o, a, c) \ __bs_nonsingle(wr,1,(t),(h),(o),(a),(c)) #define bus_space_write_region_2(t, h, o, a, c) \ __bs_nonsingle(wr,2,(t),(h),(o),(a),(c)) #define bus_space_write_region_4(t, h, o, a, c) \ __bs_nonsingle(wr,4,(t),(h),(o),(a),(c)) #define bus_space_write_region_8(t, h, o, a, c) \ __bs_nonsingle(wr,8,(t),(h),(o),(a),(c)) #define bus_space_write_region_stream_1 bus_space_write_region_1 #define bus_space_write_region_stream_2(t, h, o, a, c) \ __bs_nonsingle(wr,s_2,(t),(h),(o),(a),(c)) #define bus_space_write_region_stream_4(t, h, o, a, c) \ __bs_nonsingle(wr,s_4,(t),(h),(o),(a),(c)) #define bus_space_write_region_stream_8(t, h, o, a, c) \ __bs_nonsingle(wr,s_8,(t),(h),(o),(a),(c)) /* * Set multiple operations. */ #define bus_space_set_multi_1(t, h, o, v, c) \ __bs_set(sm,1,(t),(h),(o),(v),(c)) #define bus_space_set_multi_2(t, h, o, v, c) \ __bs_set(sm,2,(t),(h),(o),(v),(c)) #define bus_space_set_multi_4(t, h, o, v, c) \ __bs_set(sm,4,(t),(h),(o),(v),(c)) #define bus_space_set_multi_8(t, h, o, v, c) \ __bs_set(sm,8,(t),(h),(o),(v),(c)) #define bus_space_set_multi_stream_1 bus_space_set_multi_1 #define bus_space_set_multi_stream_2(t, h, o, v, c) \ __bs_set(sm,s_2,(t),(h),(o),(v),(c)) #define bus_space_set_multi_stream_4(t, h, o, v, c) \ __bs_set(sm,s_4,(t),(h),(o),(v),(c)) #define bus_space_set_multi_stream_8(t, h, o, v, c) \ __bs_set(sm,s_8,(t),(h),(o),(v),(c)) /* * Set region operations. */ #define bus_space_set_region_1(t, h, o, v, c) \ __bs_set(sr,1,(t),(h),(o),(v),(c)) #define bus_space_set_region_2(t, h, o, v, c) \ __bs_set(sr,2,(t),(h),(o),(v),(c)) #define bus_space_set_region_4(t, h, o, v, c) \ __bs_set(sr,4,(t),(h),(o),(v),(c)) #define bus_space_set_region_8(t, h, o, v, c) \ __bs_set(sr,8,(t),(h),(o),(v),(c)) #define bus_space_set_region_stream_1 bus_space_set_region_1 #define bus_space_set_region_stream_2(t, h, o, v, c) \ __bs_set(sr,s_2,(t),(h),(o),(v),(c)) #define bus_space_set_region_stream_4(t, h, o, v, c) \ __bs_set(sr,s_4,(t),(h),(o),(v),(c)) #define bus_space_set_region_stream_8(t, h, o, v, c) \ __bs_set(sr,s_8,(t),(h),(o),(v),(c)) #if 0 /* * Copy operations. */ #define bus_space_copy_region_1(t, h1, o1, h2, o2, c) \ __bs_copy(1, t, h1, o1, h2, o2, c) #define bus_space_copy_region_2(t, h1, o1, h2, o2, c) \ __bs_copy(2, t, h1, o1, h2, o2, c) #define bus_space_copy_region_4(t, h1, o1, h2, o2, c) \ __bs_copy(4, t, h1, o1, h2, o2, c) #define bus_space_copy_region_8(t, h1, o1, h2, o2, c) \ __bs_copy(8, t, h1, o1, h2, o2, c) #define bus_space_copy_region_stream_1 bus_space_copy_region_1 #define bus_space_copy_region_stream_2(t, h1, o1, h2, o2, c) \ __bs_copy(s_2, t, h1, o1, h2, o2, c) #define bus_space_copy_region_stream_4(t, h1, o1, h2, o2, c) \ __bs_copy(s_4, t, h1, o1, h2, o2, c) #define bus_space_copy_region_stream_8(t, h1, o1, h2, o2, c) \ __bs_copy(s_8, t, h1, o1, h2, o2, c) #endif #include #endif /* _MACHINE_BUS_H_ */ Index: head/sys/powerpc/include/bus_dma.h =================================================================== --- head/sys/powerpc/include/bus_dma.h (revision 326260) +++ head/sys/powerpc/include/bus_dma.h (revision 326261) @@ -1,38 +1,40 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2005 Scott Long * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* $FreeBSD$ */ #ifndef _POWERPC_BUS_DMA_H_ #define _POWERPC_BUS_DMA_H_ #include #include struct device; int bus_dma_tag_set_iommu(bus_dma_tag_t, struct device *iommu, void *cookie); #endif /* _POWERPC_BUS_DMA_H_ */ Index: head/sys/powerpc/include/counter.h =================================================================== --- head/sys/powerpc/include/counter.h (revision 326260) +++ head/sys/powerpc/include/counter.h (revision 326261) @@ -1,164 +1,166 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2012, 2013 Konstantin Belousov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef __MACHINE_COUNTER_H__ #define __MACHINE_COUNTER_H__ #include #ifdef INVARIANTS #include #endif extern struct pcpu __pcpu[]; #define EARLY_COUNTER &__pcpu[0].pc_early_dummy_counter #ifdef __powerpc64__ #define counter_enter() do {} while (0) #define counter_exit() do {} while (0) #ifdef IN_SUBR_COUNTER_C static inline uint64_t counter_u64_read_one(uint64_t *p, int cpu) { return (*(uint64_t *)((char *)p + sizeof(struct pcpu) * cpu)); } static inline uint64_t counter_u64_fetch_inline(uint64_t *p) { uint64_t r; int i; r = 0; CPU_FOREACH(i) r += counter_u64_read_one((uint64_t *)p, i); return (r); } static void counter_u64_zero_one_cpu(void *arg) { *((uint64_t *)((char *)arg + sizeof(struct pcpu) * PCPU_GET(cpuid))) = 0; } static inline void counter_u64_zero_inline(counter_u64_t c) { smp_rendezvous(smp_no_rendezvous_barrier, counter_u64_zero_one_cpu, smp_no_rendezvous_barrier, c); } #endif #define counter_u64_add_protected(c, i) counter_u64_add(c, i) static inline void counter_u64_add(counter_u64_t c, int64_t inc) { uint64_t ccpu, old; __asm __volatile("\n" "1:\n\t" "mfsprg %0, 0\n\t" "ldarx %1, %0, %2\n\t" "add %1, %1, %3\n\t" "stdcx. %1, %0, %2\n\t" "bne- 1b" : "=&b" (ccpu), "=&r" (old) : "r" ((char *)c - (char *)&__pcpu[0]), "r" (inc) : "cr0", "memory"); } #else /* !64bit */ #define counter_enter() critical_enter() #define counter_exit() critical_exit() #ifdef IN_SUBR_COUNTER_C /* XXXKIB non-atomic 64bit read */ static inline uint64_t counter_u64_read_one(uint64_t *p, int cpu) { return (*(uint64_t *)((char *)p + sizeof(struct pcpu) * cpu)); } static inline uint64_t counter_u64_fetch_inline(uint64_t *p) { uint64_t r; int i; r = 0; for (i = 0; i < mp_ncpus; i++) r += counter_u64_read_one((uint64_t *)p, i); return (r); } /* XXXKIB non-atomic 64bit store, might interrupt increment */ static void counter_u64_zero_one_cpu(void *arg) { *((uint64_t *)((char *)arg + sizeof(struct pcpu) * PCPU_GET(cpuid))) = 0; } static inline void counter_u64_zero_inline(counter_u64_t c) { smp_rendezvous(smp_no_rendezvous_barrier, counter_u64_zero_one_cpu, smp_no_rendezvous_barrier, c); } #endif #define counter_u64_add_protected(c, inc) do { \ CRITICAL_ASSERT(curthread); \ *(uint64_t *)zpcpu_get(c) += (inc); \ } while (0) static inline void counter_u64_add(counter_u64_t c, int64_t inc) { counter_enter(); counter_u64_add_protected(c, inc); counter_exit(); } #endif /* 64bit */ #endif /* ! __MACHINE_COUNTER_H__ */ Index: head/sys/powerpc/include/cpufunc.h =================================================================== --- head/sys/powerpc/include/cpufunc.h (revision 326260) +++ head/sys/powerpc/include/cpufunc.h (revision 326261) @@ -1,215 +1,217 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_CPUFUNC_H_ #define _MACHINE_CPUFUNC_H_ #ifdef _KERNEL #include #include #include struct thread; #ifdef KDB void breakpoint(void); #else static __inline void breakpoint(void) { return; } #endif /* CPU register mangling inlines */ static __inline void mtmsr(register_t value) { __asm __volatile ("mtmsr %0; isync" :: "r"(value)); } #ifdef __powerpc64__ static __inline void mtmsrd(register_t value) { __asm __volatile ("mtmsrd %0; isync" :: "r"(value)); } #endif static __inline register_t mfmsr(void) { register_t value; __asm __volatile ("mfmsr %0" : "=r"(value)); return (value); } #ifndef __powerpc64__ static __inline void mtsrin(vm_offset_t va, register_t value) { __asm __volatile ("mtsrin %0,%1; isync" :: "r"(value), "r"(va)); } static __inline register_t mfsrin(vm_offset_t va) { register_t value; __asm __volatile ("mfsrin %0,%1" : "=r"(value) : "r"(va)); return (value); } #endif static __inline register_t mfctrl(void) { register_t value; __asm __volatile ("mfspr %0,136" : "=r"(value)); return (value); } static __inline void mtdec(register_t value) { __asm __volatile ("mtdec %0" :: "r"(value)); } static __inline register_t mfdec(void) { register_t value; __asm __volatile ("mfdec %0" : "=r"(value)); return (value); } static __inline register_t mfpvr(void) { register_t value; __asm __volatile ("mfpvr %0" : "=r"(value)); return (value); } static __inline u_quad_t mftb(void) { u_quad_t tb; #ifdef __powerpc64__ __asm __volatile ("mftb %0" : "=r"(tb)); #else uint32_t *tbup = (uint32_t *)&tb; uint32_t *tblp = tbup + 1; do { *tbup = mfspr(TBR_TBU); *tblp = mfspr(TBR_TBL); } while (*tbup != mfspr(TBR_TBU)); #endif return (tb); } static __inline void mttb(u_quad_t time) { mtspr(TBR_TBWL, 0); mtspr(TBR_TBWU, (uint32_t)(time >> 32)); mtspr(TBR_TBWL, (uint32_t)(time & 0xffffffff)); } static __inline void eieio(void) { __asm __volatile ("eieio" : : : "memory"); } static __inline void isync(void) { __asm __volatile ("isync" : : : "memory"); } static __inline void powerpc_sync(void) { __asm __volatile ("sync" : : : "memory"); } static __inline register_t intr_disable(void) { register_t msr; msr = mfmsr(); mtmsr(msr & ~PSL_EE); return (msr); } static __inline void intr_restore(register_t msr) { mtmsr(msr); } static __inline struct pcpu * get_pcpu(void) { struct pcpu *ret; __asm __volatile("mfsprg %0, 0" : "=r"(ret)); return (ret); } #endif /* _KERNEL */ #endif /* !_MACHINE_CPUFUNC_H_ */ Index: head/sys/powerpc/include/dbdma.h =================================================================== --- head/sys/powerpc/include/dbdma.h (revision 326260) +++ head/sys/powerpc/include/dbdma.h (revision 326261) @@ -1,153 +1,155 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2008 Nathan Whitehorn * All rights reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_DBDMA_H_ #define _MACHINE_DBDMA_H_ #include #include /* * Apple's DBDMA (Descriptor-based DMA) interface is a common DMA engine * used by a variety of custom Apple ASICs. It is described in the CHRP * specification and in the book Macintosh Technology in the Common * Hardware Reference Platform, copyright 1995 Apple Computer. */ /* DBDMA Command Values */ enum { DBDMA_OUTPUT_MORE = 0, DBDMA_OUTPUT_LAST = 1, DBDMA_INPUT_MORE = 2, DBDMA_INPUT_LAST = 3, DBDMA_STORE_QUAD = 4, DBDMA_LOAD_QUAD = 5, DBDMA_NOP = 6, DBDMA_STOP = 7 }; /* These codes are for the interrupt, branch, and wait flags */ enum { DBDMA_NEVER = 0, DBDMA_COND_TRUE = 1, DBDMA_COND_FALSE = 2, DBDMA_ALWAYS = 3 }; /* Channel status bits */ #define DBDMA_STATUS_RUN (0x01 << 15) #define DBDMA_STATUS_PAUSE (0x01 << 14) #define DBDMA_STATUS_FLUSH (0x01 << 13) #define DBDMA_STATUS_WAKE (0x01 << 12) #define DBDMA_STATUS_DEAD (0x01 << 11) #define DBDMA_STATUS_ACTIVE (0x01 << 10) /* Set by hardware if a branch was taken */ #define DBDMA_STATUS_BRANCH 8 struct dbdma_command; typedef struct dbdma_command dbdma_command_t; struct dbdma_channel; typedef struct dbdma_channel dbdma_channel_t; int dbdma_allocate_channel(struct resource *dbdma_regs, u_int offset, bus_dma_tag_t parent_dma, int slots, dbdma_channel_t **chan); int dbdma_resize_channel(dbdma_channel_t *chan, int newslots); int dbdma_free_channel(dbdma_channel_t *chan); void dbdma_run(dbdma_channel_t *chan); void dbdma_stop(dbdma_channel_t *chan); void dbdma_reset(dbdma_channel_t *chan); void dbdma_set_current_cmd(dbdma_channel_t *chan, int slot); void dbdma_pause(dbdma_channel_t *chan); void dbdma_wake(dbdma_channel_t *chan); /* * DBDMA uses a 16 bit channel control register to describe the current * state of DMA on the channel. The high-order bits (8-15) contain information * on the run state and are listed in the DBDMA_STATUS_* constants above. These * are manipulated with the dbdma_run/stop/reset() routines above. * * The low order bits (0-7) are device dependent status bits. These can be set * and read by both hardware and software. The mask is the set of bits to * modify; if mask is 0x03 and value is 0, the lowest order 2 bits will be * zeroed. */ uint16_t dbdma_get_chan_status(dbdma_channel_t *chan); uint8_t dbdma_get_device_status(dbdma_channel_t *chan); void dbdma_set_device_status(dbdma_channel_t *chan, uint8_t mask, uint8_t value); /* * Each DBDMA command word has the current channel status register and the * number of residual bytes (requested - actually transferred) written to it * at time of command completion. */ uint16_t dbdma_get_cmd_status(dbdma_channel_t *chan, int slot); uint16_t dbdma_get_residuals(dbdma_channel_t *chan, int slot); void dbdma_clear_cmd_status(dbdma_channel_t *chan, int slot); /* * The interrupt/branch/wait selector let you specify a set of values * of the device dependent status bits that will cause intterupt/branch/wait * conditions to be taken if the flags for these are set to one of the * DBDMA_COND_* values. * * The condition is considered true if (status & mask) == value. */ void dbdma_set_interrupt_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t value); void dbdma_set_branch_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t value); void dbdma_set_wait_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t value); void dbdma_insert_command(dbdma_channel_t *chan, int slot, int command, int stream, bus_addr_t data, size_t count, uint8_t interrupt, uint8_t branch, uint8_t wait, uint32_t branch_slot); void dbdma_insert_stop(dbdma_channel_t *chan, int slot); void dbdma_insert_nop(dbdma_channel_t *chan, int slot); void dbdma_insert_branch(dbdma_channel_t *chan, int slot, int to_slot); void dbdma_sync_commands(dbdma_channel_t *chan, bus_dmasync_op_t op); void dbdma_save_state(dbdma_channel_t *chan); void dbdma_restore_state(dbdma_channel_t *chan); #endif /* _MACHINE_DBDMA_H_ */ Index: head/sys/powerpc/include/elf.h =================================================================== --- head/sys/powerpc/include/elf.h (revision 326260) +++ head/sys/powerpc/include/elf.h (revision 326261) @@ -1,140 +1,142 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2001 David E. O'Brien * Copyright (c) 1996-1997 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_ELF_H_ #define _MACHINE_ELF_H_ 1 /* * EABI ELF definitions for the PowerPC architecture. * See "PowerPC Embedded Application Binary Interface, 32-Bit Impliementation" * [ppc-eabi-1995-01.pdf] for details. */ #ifndef __ELF_WORD_SIZE #ifdef __powerpc64__ #define __ELF_WORD_SIZE 64 /* Used by */ #else #define __ELF_WORD_SIZE 32 /* Used by */ #endif #endif #include /* Definitions common to all 32 bit architectures. */ #include /* Definitions common to all 64 bit architectures. */ #include #if __ELF_WORD_SIZE == 64 #define ELF_ARCH EM_PPC64 #define ELF_MACHINE_OK(x) ((x) == EM_PPC64) #else #define ELF_ARCH EM_PPC #define ELF_ARCH32 EM_PPC #define ELF_MACHINE_OK(x) ((x) == EM_PPC) #endif /* * Auxiliary vector entries for passing information to the interpreter. * * The PowerPC supplement to the SVR4 ABI specification names this "auxv_t", * but POSIX lays claim to all symbols ending with "_t". */ typedef struct { /* Auxiliary vector entry on initial stack */ int a_type; /* Entry type. */ union { long a_val; /* Integer value. */ void *a_ptr; /* Address. */ void (*a_fcn)(void); /* Function pointer (not used). */ } a_un; } Elf32_Auxinfo; typedef struct { /* Auxiliary vector entry on initial stack */ long a_type; /* Entry type. */ union { long a_val; /* Integer value. */ void *a_ptr; /* Address. */ void (*a_fcn)(void); /* Function pointer (not used). */ } a_un; } Elf64_Auxinfo; __ElfType(Auxinfo); /* Values for a_type. */ #define AT_NULL 0 /* Terminates the vector. */ #define AT_IGNORE 1 /* Ignored entry. */ #define AT_EXECFD 2 /* File descriptor of program to load. */ #define AT_PHDR 3 /* Program header of program already loaded. */ #define AT_PHENT 4 /* Size of each program header entry. */ #define AT_PHNUM 5 /* Number of program header entries. */ #define AT_PAGESZ 6 /* Page size in bytes. */ #define AT_BASE 7 /* Interpreter's base address. */ #define AT_FLAGS 8 /* Flags (unused for PowerPC). */ #define AT_ENTRY 9 /* Where interpreter should transfer control. */ #define AT_DCACHEBSIZE 10 /* Data cache block size for the processor. */ #define AT_ICACHEBSIZE 11 /* Instruction cache block size for the uP. */ #define AT_UCACHEBSIZE 12 /* Cache block size, or `0' if cache not unified. */ #define AT_EXECPATH 13 /* Path to the executable. */ #define AT_CANARY 14 /* Canary for SSP */ #define AT_CANARYLEN 15 /* Length of the canary. */ #define AT_OSRELDATE 16 /* OSRELDATE. */ #define AT_NCPUS 17 /* Number of CPUs. */ #define AT_PAGESIZES 18 /* Pagesizes. */ #define AT_PAGESIZESLEN 19 /* Number of pagesizes. */ #define AT_STACKPROT 21 /* Initial stack protection. */ #define AT_TIMEKEEP 22 /* Pointer to timehands. */ #define AT_EHDRFLAGS 24 /* e_flags field from elf hdr */ #define AT_HWCAP 25 /* CPU feature flags. */ #define AT_HWCAP2 26 /* CPU feature flags 2. */ #define AT_COUNT 27 /* Count of defined aux entry types. */ /* * Relocation types. */ #define R_PPC_COUNT 37 /* Count of defined relocation types. */ /* Count of defined relocation types. */ #define R_PPC_EMB_COUNT (R_PPC_EMB_RELSDA - R_PPC_EMB_NADDR32 + 1) /* Define "machine" characteristics */ #if __ELF_WORD_SIZE == 64 #define ELF_TARG_CLASS ELFCLASS64 #define ELF_TARG_DATA ELFDATA2MSB #define ELF_TARG_MACH EM_PPC64 #define ELF_TARG_VER 1 #else #define ELF_TARG_CLASS ELFCLASS32 #define ELF_TARG_DATA ELFDATA2MSB #define ELF_TARG_MACH EM_PPC #define ELF_TARG_VER 1 #endif #define ET_DYN_LOAD_ADDR 0x01010000 #endif /* !_MACHINE_ELF_H_ */ Index: head/sys/powerpc/include/gdb_machdep.h =================================================================== --- head/sys/powerpc/include/gdb_machdep.h (revision 326260) +++ head/sys/powerpc/include/gdb_machdep.h (revision 326261) @@ -1,81 +1,83 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2006 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_GDB_MACHDEP_H_ #define _MACHINE_GDB_MACHDEP_H_ #ifdef BOOKE #define PPC_GDB_NREGS0 1 #define PPC_GDB_NREGS4 (70 + 1) #define PPC_GDB_NREGS8 (1 + 32) #define PPC_GDB_NREGS16 0 #else #define PPC_GDB_NREGS0 0 #define PPC_GDB_NREGS4 (32 + 7 + 2) #define PPC_GDB_NREGS8 32 #define PPC_GDB_NREGS16 32 #endif #define GDB_NREGS (PPC_GDB_NREGS0 + PPC_GDB_NREGS4 + \ PPC_GDB_NREGS8 + PPC_GDB_NREGS16) #define GDB_REG_PC 64 #define GDB_BUFSZ (PPC_GDB_NREGS4 * 8 + \ PPC_GDB_NREGS8 * 16 + \ PPC_GDB_NREGS16 * 32) static __inline size_t gdb_cpu_regsz(int regnum) { #ifdef BOOKE if (regnum == 70) return (0); if (regnum == 71 || regnum >= 73) return (8); #else if (regnum >= 32 && regnum <= 63) return (8); if (regnum >= 71 && regnum <= 102) return (16); #endif return (4); } static __inline int gdb_cpu_query(void) { return (0); } void *gdb_cpu_getreg(int, size_t *); void gdb_cpu_setreg(int, void *); int gdb_cpu_signal(int, int); #endif /* !_MACHINE_GDB_MACHDEP_H_ */ Index: head/sys/powerpc/include/intr_machdep.h =================================================================== --- head/sys/powerpc/include/intr_machdep.h (revision 326260) +++ head/sys/powerpc/include/intr_machdep.h (revision 326261) @@ -1,64 +1,66 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2002 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_INTR_MACHDEP_H_ #define _MACHINE_INTR_MACHDEP_H_ #define INTR_VECTORS 256 #define MAX_PICS 32 #define MAP_IRQ(node, pin) powerpc_get_irq(node, pin) /* * Default base address for MSI messages on PowerPC */ #define MSI_INTEL_ADDR_BASE 0xfee00000 extern device_t root_pic; struct trapframe; driver_filter_t powerpc_ipi_handler; void intrcnt_add(const char *name, u_long **countp); u_int powerpc_register_pic(device_t, uint32_t, u_int, u_int, u_int); u_int powerpc_get_irq(uint32_t, u_int); void powerpc_dispatch_intr(u_int, struct trapframe *); int powerpc_enable_intr(void); int powerpc_setup_intr(const char *, u_int, driver_filter_t, driver_intr_t, void *, enum intr_type, void **); int powerpc_teardown_intr(void *); int powerpc_bind_intr(u_int irq, u_char cpu); int powerpc_config_intr(int, enum intr_trigger, enum intr_polarity); int powerpc_fw_config_intr(int irq, int sense_code); void powerpc_intr_mask(u_int irq); void powerpc_intr_unmask(u_int irq); #endif /* _MACHINE_INTR_MACHDEP_H_ */ Index: head/sys/powerpc/include/kdb.h =================================================================== --- head/sys/powerpc/include/kdb.h (revision 326260) +++ head/sys/powerpc/include/kdb.h (revision 326261) @@ -1,53 +1,55 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2004 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_KDB_H_ #define _MACHINE_KDB_H_ #include #include #include #include #include void kdb_cpu_clear_singlestep(void); void kdb_cpu_set_singlestep(void); static __inline void kdb_cpu_sync_icache(unsigned char *addr, size_t size) { __syncicache(addr, size); } static __inline void kdb_cpu_trap(int vector, int _) { } #endif /* _MACHINE_KDB_H_ */ Index: head/sys/powerpc/include/machdep.h =================================================================== --- head/sys/powerpc/include/machdep.h (revision 326260) +++ head/sys/powerpc/include/machdep.h (revision 326261) @@ -1,38 +1,40 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2011-2012 Semihalf * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _POWERPC_MACHDEP_H_ #define _POWERPC_MACHDEP_H_ void booke_disable_l2_cache(void); void booke_enable_l1_cache(void); void booke_enable_l2_cache(void); void booke_enable_bpred(void); void booke_init_tlb(vm_paddr_t); #endif /* _POWERPC_MACHDEP_H_ */ Index: head/sys/powerpc/include/md_var.h =================================================================== --- head/sys/powerpc/include/md_var.h (revision 326260) +++ head/sys/powerpc/include/md_var.h (revision 326261) @@ -1,69 +1,71 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_MD_VAR_H_ #define _MACHINE_MD_VAR_H_ /* * Miscellaneous machine-dependent declarations. */ extern char sigcode32[]; extern int szsigcode32; #ifdef __powerpc64__ extern char sigcode64[], sigcode64_elfv2[]; extern int szsigcode64, szsigcode64_elfv2; #endif extern long Maxmem; extern int busdma_swi_pending; extern vm_offset_t kstack0; extern vm_offset_t kstack0_phys; extern int powerpc_pow_enabled; extern int cacheline_size; extern int hw_direct_map; void __syncicache(void *, int); void busdma_swi(void); int is_physical_memory(vm_offset_t addr); int mem_valid(vm_offset_t addr, int len); void decr_init(void); void decr_ap_init(void); void decr_tc_init(void); void cpu_setup(u_int); struct trapframe; void powerpc_interrupt(struct trapframe *); #endif /* !_MACHINE_MD_VAR_H_ */ Index: head/sys/powerpc/include/memdev.h =================================================================== --- head/sys/powerpc/include/memdev.h (revision 326260) +++ head/sys/powerpc/include/memdev.h (revision 326261) @@ -1,40 +1,42 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2004 Mark R V Murray * 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 * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_MEMDEV_H_ #define _MACHINE_MEMDEV_H_ #define CDEV_MINOR_MEM 0 #define CDEV_MINOR_KMEM 1 d_open_t memopen; d_read_t memrw; d_ioctl_t memioctl; d_mmap_t memmmap; #endif /* _MACHINE_MEMDEV_H_ */ Index: head/sys/powerpc/include/metadata.h =================================================================== --- head/sys/powerpc/include/metadata.h (revision 326260) +++ head/sys/powerpc/include/metadata.h (revision 326261) @@ -1,38 +1,40 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2001 Jake Burkholder. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_METADATA_H_ #define _MACHINE_METADATA_H_ #define MODINFOMD_ENVP 0x1001 #define MODINFOMD_HOWTO 0x1002 #define MODINFOMD_KERNEND 0x1003 #define MODINFOMD_BOOTINFO 0x1004 #define MODINFOMD_DTBP 0x1005 #endif /* !_MACHINE_METADATA_H_ */ Index: head/sys/powerpc/include/mmuvar.h =================================================================== --- head/sys/powerpc/include/mmuvar.h (revision 326260) +++ head/sys/powerpc/include/mmuvar.h (revision 326261) @@ -1,119 +1,121 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2005 Peter Grehan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_MMUVAR_H_ #define _MACHINE_MMUVAR_H_ /* * A PowerPC MMU implementation is declared with a kernel object and * an associated method table. The MMU_DEF macro is used to declare * the class, and also links it to the global MMU class list. * * e.g. * * static mmu_method_t ppc8xx_methods[] = { * MMUMETHOD(mmu_change_wiring, ppc8xx_mmu_change_wiring), * MMUMETHOD(mmu_clear_modify, ppc8xx_mmu_clear_modify), * MMUMETHOD(mmu_clear_reference, ppc8xx_mmu_clear_reference), * ... * MMUMETHOD(mmu_dev_direct_mapped, ppc8xx_mmu_dev_direct_mapped), * { 0, 0 } * }; * * MMU_DEF(ppc8xx, MMU_TYPE_8xx, ppc8xx_methods, sizeof(ppc8xx_mmu_softc)); * * A single level of inheritance is supported in a similar fashion to * kobj inheritance e.g. * * MMU_DEF_1(ppc860c, MMU_TYPE_860c, ppc860c_methods, 0, ppc8xx); */ #include struct mmu_kobj { /* * An MMU instance is a kernel object */ KOBJ_FIELDS; /* * Utility elements that an instance may use */ struct mtx mmu_mtx; /* available for instance use */ void *mmu_iptr; /* instance data pointer */ /* * Opaque data that can be overlaid with an instance-private * structure. MMU code can test that this is large enough at * compile time with a sizeof() test againt it's softc. There * is also a run-time test when the MMU kernel object is * registered. */ #define MMU_OPAQUESZ 64 u_int mmu_opaque[MMU_OPAQUESZ]; }; typedef struct mmu_kobj *mmu_t; typedef struct kobj_class mmu_def_t; #define mmu_method_t kobj_method_t #define MMUMETHOD KOBJMETHOD #define MMU_DEF(name, ident, methods, size) \ \ mmu_def_t name = { \ ident, methods, size, NULL \ }; \ DATA_SET(mmu_set, name) #define MMU_DEF_INHERIT(name, ident, methods, size, base1) \ \ static kobj_class_t name ## _baseclasses[] = \ { &base1, NULL }; \ mmu_def_t name = { \ ident, methods, size, name ## _baseclasses \ }; \ DATA_SET(mmu_set, name) #if 0 mmu_def_t name = { \ ident, methods, size, name ## _baseclasses \ }; DATA_SET(mmu_set, name) #endif /* * Known MMU names */ #define MMU_TYPE_BOOKE "mmu_booke" /* Book-E MMU specification */ #define MMU_TYPE_OEA "mmu_oea" /* 32-bit OEA */ #define MMU_TYPE_G5 "mmu_g5" /* 64-bit bridge (ibm 970) */ #define MMU_TYPE_8xx "mmu_8xx" /* 8xx quicc TLB */ #endif /* _MACHINE_MMUVAR_H_ */ Index: head/sys/powerpc/include/ofw_machdep.h =================================================================== --- head/sys/powerpc/include/ofw_machdep.h (revision 326260) +++ head/sys/powerpc/include/ofw_machdep.h (revision 326261) @@ -1,51 +1,53 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2001 by Thomas Moestl . * 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 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_OFW_MACHDEP_H_ #define _MACHINE_OFW_MACHDEP_H_ #include #include #include #include #include #include typedef uint32_t cell_t; void OF_getetheraddr(device_t dev, u_char *addr); void OF_initial_setup(void *fdt_ptr, void *junk, int (*openfirm)(void *)); boolean_t OF_bootstrap(void); void OF_reboot(void); void ofw_mem_regions(struct mem_region *, int *, struct mem_region *, int *); void ofw_quiesce(void); /* Must be called before VM is up! */ void ofw_save_trap_vec(char *); #endif /* _MACHINE_OFW_MACHDEP_H_ */ Index: head/sys/powerpc/include/openpicreg.h =================================================================== --- head/sys/powerpc/include/openpicreg.h (revision 326260) +++ head/sys/powerpc/include/openpicreg.h (revision 326261) @@ -1,141 +1,143 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright (c) 2000 Tsubai Masanari. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * from NetBSD: openpicreg.h,v 1.3 2001/08/30 03:08:52 briggs Exp * $FreeBSD$ */ /* * Size of OpenPIC register space */ #define OPENPIC_SIZE 0x40000 /* * Per Processor Registers [private access] (0x00000 - 0x00fff) */ /* IPI dispatch command reg */ #define OPENPIC_IPI_DISPATCH(ipi) (0x40 + (ipi) * 0x10) /* current task priority reg */ #define OPENPIC_TPR 0x80 #define OPENPIC_TPR_MASK 0x0000000f #define OPENPIC_WHOAMI 0x90 /* interrupt acknowledge reg */ #define OPENPIC_IACK 0xa0 /* end of interrupt reg */ #define OPENPIC_EOI 0xb0 /* * Global registers (0x01000-0x0ffff) */ /* feature reporting reg 0 */ #define OPENPIC_FEATURE 0x1000 #define OPENPIC_FEATURE_VERSION_MASK 0x000000ff #define OPENPIC_FEATURE_LAST_CPU_MASK 0x00001f00 #define OPENPIC_FEATURE_LAST_CPU_SHIFT 8 #define OPENPIC_FEATURE_LAST_IRQ_MASK 0x07ff0000 #define OPENPIC_FEATURE_LAST_IRQ_SHIFT 16 /* global config reg 0 */ #define OPENPIC_CONFIG 0x1020 #define OPENPIC_CONFIG_RESET 0x80000000 #define OPENPIC_CONFIG_8259_PASSTHRU_DISABLE 0x20000000 /* interrupt configuration mode (direct or serial) */ #define OPENPIC_ICR 0x1030 #define OPENPIC_ICR_SERIAL_MODE (1 << 27) #define OPENPIC_ICR_SERIAL_RATIO_MASK (0x7 << 28) #define OPENPIC_ICR_SERIAL_RATIO_SHIFT 28 /* vendor ID */ #define OPENPIC_VENDOR_ID 0x1080 /* processor initialization reg */ #define OPENPIC_PROC_INIT 0x1090 /* IPI vector/priority reg */ #define OPENPIC_IPI_VECTOR(ipi) (0x10a0 + (ipi) * 0x10) /* spurious intr. vector */ #define OPENPIC_SPURIOUS_VECTOR 0x10e0 /* Timer registers */ #define OPENPIC_TIMERS 4 #define OPENPIC_TFREQ 0x10f0 #define OPENPIC_TCNT(t) (0x1100 + (t) * 0x40) #define OPENPIC_TBASE(t) (0x1110 + (t) * 0x40) #define OPENPIC_TVEC(t) (0x1120 + (t) * 0x40) #define OPENPIC_TDST(t) (0x1130 + (t) * 0x40) /* * Interrupt Source Configuration Registers (0x10000 - 0x1ffff) */ /* interrupt vector/priority reg */ #define OPENPIC_SRC_VECTOR_COUNT 64 #ifndef OPENPIC_SRC_VECTOR #define OPENPIC_SRC_VECTOR(irq) (0x10000 + (irq) * 0x20) #endif #define OPENPIC_SENSE_LEVEL 0x00400000 #define OPENPIC_SENSE_EDGE 0x00000000 #define OPENPIC_POLARITY_POSITIVE 0x00800000 #define OPENPIC_POLARITY_NEGATIVE 0x00000000 #define OPENPIC_IMASK 0x80000000 #define OPENPIC_ACTIVITY 0x40000000 #define OPENPIC_PRIORITY_MASK 0x000f0000 #define OPENPIC_PRIORITY_SHIFT 16 #define OPENPIC_VECTOR_MASK 0x000000ff /* interrupt destination cpu */ #ifndef OPENPIC_IDEST #define OPENPIC_IDEST(irq) (0x10010 + (irq) * 0x20) #endif /* * Per Processor Registers [global access] (0x20000 - 0x3ffff) */ #define OPENPIC_PCPU_BASE(cpu) (0x20000 + (cpu) * 0x1000) #define OPENPIC_PCPU_IPI_DISPATCH(cpu, ipi) \ (OPENPIC_PCPU_BASE(cpu) + OPENPIC_IPI_DISPATCH(ipi)) #define OPENPIC_PCPU_TPR(cpu) \ (OPENPIC_PCPU_BASE(cpu) + OPENPIC_TPR) #define OPENPIC_PCPU_WHOAMI(cpu) \ (OPENPIC_PCPU_BASE(cpu) + OPENPIC_WHOAMI) #define OPENPIC_PCPU_IACK(cpu) \ (OPENPIC_PCPU_BASE(cpu) + OPENPIC_IACK) #define OPENPIC_PCPU_EOI(cpu) \ (OPENPIC_PCPU_BASE(cpu) + OPENPIC_EOI) Index: head/sys/powerpc/include/openpicvar.h =================================================================== --- head/sys/powerpc/include/openpicvar.h (revision 326260) +++ head/sys/powerpc/include/openpicvar.h (revision 326261) @@ -1,88 +1,90 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2002 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _POWERPC_OPENPICVAR_H_ #define _POWERPC_OPENPICVAR_H_ #define OPENPIC_DEVSTR "OpenPIC Interrupt Controller" #define OPENPIC_IRQMAX 256 /* h/w allows more */ /* Names match the macros in openpicreg.h. */ struct openpic_timer { uint32_t tcnt; uint32_t tbase; uint32_t tvec; uint32_t tdst; }; struct openpic_softc { device_t sc_dev; struct resource *sc_memr; struct resource *sc_intr; bus_space_tag_t sc_bt; bus_space_handle_t sc_bh; char *sc_version; int sc_rid; int sc_irq; void *sc_icookie; u_int sc_ncpu; u_int sc_nirq; int sc_psim; /* Saved states. */ uint32_t sc_saved_config; uint32_t sc_saved_ipis[4]; uint32_t sc_saved_prios[4]; struct openpic_timer sc_saved_timers[OPENPIC_TIMERS]; uint32_t sc_saved_vectors[OPENPIC_SRC_VECTOR_COUNT]; }; extern devclass_t openpic_devclass; /* * Bus-independent attach i/f */ int openpic_common_attach(device_t, uint32_t); /* * PIC interface. */ void openpic_bind(device_t dev, u_int irq, cpuset_t cpumask); void openpic_config(device_t, u_int, enum intr_trigger, enum intr_polarity); void openpic_dispatch(device_t, struct trapframe *); void openpic_enable(device_t, u_int, u_int); void openpic_eoi(device_t, u_int); void openpic_ipi(device_t, u_int); void openpic_mask(device_t, u_int); void openpic_unmask(device_t, u_int); int openpic_suspend(device_t dev); int openpic_resume(device_t dev); #endif /* _POWERPC_OPENPICVAR_H_ */ Index: head/sys/powerpc/include/pcpu.h =================================================================== --- head/sys/powerpc/include/pcpu.h (revision 326260) +++ head/sys/powerpc/include/pcpu.h (revision 326261) @@ -1,180 +1,182 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 1999 Luoqi Chen * Copyright (c) Peter Wemm * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_PCPU_H_ #define _MACHINE_PCPU_H_ #include #include #include struct pmap; struct pvo_entry; #define CPUSAVE_LEN 9 #define PCPU_MD_COMMON_FIELDS \ int pc_inside_intr; \ struct pmap *pc_curpmap; /* current pmap */ \ struct thread *pc_fputhread; /* current fpu user */ \ struct thread *pc_vecthread; /* current vec user */ \ uintptr_t pc_hwref; \ uint32_t pc_pir; \ int pc_bsp; \ volatile int pc_awake; \ uint32_t pc_ipimask; \ register_t pc_tempsave[CPUSAVE_LEN]; \ register_t pc_disisave[CPUSAVE_LEN]; \ register_t pc_dbsave[CPUSAVE_LEN]; \ void *pc_restore; #define PCPU_MD_AIM32_FIELDS \ vm_offset_t pc_qmap_addr; \ struct pvo_entry *pc_qmap_pvo; \ struct mtx pc_qmap_lock; \ char __pad[128] #define PCPU_MD_AIM64_FIELDS \ struct slb pc_slb[64]; \ struct slb **pc_userslb; \ register_t pc_slbsave[18]; \ uint8_t pc_slbstack[1024]; \ vm_offset_t pc_qmap_addr; \ struct pvo_entry *pc_qmap_pvo; \ struct mtx pc_qmap_lock; \ char __pad[1345] #ifdef __powerpc64__ #define PCPU_MD_AIM_FIELDS PCPU_MD_AIM64_FIELDS #else #define PCPU_MD_AIM_FIELDS PCPU_MD_AIM32_FIELDS #endif #define BOOKE_CRITSAVE_LEN (CPUSAVE_LEN + 2) #define BOOKE_TLB_MAXNEST 3 #define BOOKE_TLB_SAVELEN 16 #define BOOKE_TLBSAVE_LEN (BOOKE_TLB_SAVELEN * BOOKE_TLB_MAXNEST) #ifdef __powerpc64__ #define BOOKE_PCPU_PAD 901 #else #define BOOKE_PCPU_PAD 429 #endif #define PCPU_MD_BOOKE_FIELDS \ register_t pc_booke_critsave[BOOKE_CRITSAVE_LEN]; \ register_t pc_booke_mchksave[CPUSAVE_LEN]; \ register_t pc_booke_tlbsave[BOOKE_TLBSAVE_LEN]; \ register_t pc_booke_tlb_level; \ vm_offset_t pc_qmap_addr; \ uintptr_t *pc_booke_tlb_lock; \ int pc_tid_next; \ char __pad[BOOKE_PCPU_PAD] /* Definitions for register offsets within the exception tmp save areas */ #define CPUSAVE_R27 0 /* where r27 gets saved */ #define CPUSAVE_R28 1 /* where r28 gets saved */ #define CPUSAVE_R29 2 /* where r29 gets saved */ #define CPUSAVE_R30 3 /* where r30 gets saved */ #define CPUSAVE_R31 4 /* where r31 gets saved */ #define CPUSAVE_AIM_DAR 5 /* where SPR_DAR gets saved */ #define CPUSAVE_AIM_DSISR 6 /* where SPR_DSISR gets saved */ #define CPUSAVE_BOOKE_DEAR 5 /* where SPR_DEAR gets saved */ #define CPUSAVE_BOOKE_ESR 6 /* where SPR_ESR gets saved */ #define CPUSAVE_SRR0 7 /* where SRR0 gets saved */ #define CPUSAVE_SRR1 8 /* where SRR1 gets saved */ #define BOOKE_CRITSAVE_SRR0 9 /* where real SRR0 gets saved (critical) */ #define BOOKE_CRITSAVE_SRR1 10 /* where real SRR0 gets saved (critical) */ /* Book-E TLBSAVE is more elaborate */ #define TLBSAVE_BOOKE_LR 0 #define TLBSAVE_BOOKE_CR 1 #define TLBSAVE_BOOKE_SRR0 2 #define TLBSAVE_BOOKE_SRR1 3 #define TLBSAVE_BOOKE_R20 4 #define TLBSAVE_BOOKE_R21 5 #define TLBSAVE_BOOKE_R22 6 #define TLBSAVE_BOOKE_R23 7 #define TLBSAVE_BOOKE_R24 8 #define TLBSAVE_BOOKE_R25 9 #define TLBSAVE_BOOKE_R26 10 #define TLBSAVE_BOOKE_R27 11 #define TLBSAVE_BOOKE_R28 12 #define TLBSAVE_BOOKE_R29 13 #define TLBSAVE_BOOKE_R30 14 #define TLBSAVE_BOOKE_R31 15 #ifdef AIM #define PCPU_MD_FIELDS \ PCPU_MD_COMMON_FIELDS \ PCPU_MD_AIM_FIELDS #endif #if defined(BOOKE) #define PCPU_MD_FIELDS \ PCPU_MD_COMMON_FIELDS \ PCPU_MD_BOOKE_FIELDS #endif /* * Catch-all for ports (e.g. lsof, used by gtop) */ #ifndef PCPU_MD_FIELDS #define PCPU_MD_FIELDS \ int pc_md_placeholder[32] #endif #ifdef _KERNEL #define pcpup (get_pcpu()) static __inline __pure2 struct thread * __curthread(void) { struct thread *td; #ifdef __powerpc64__ __asm __volatile("mr %0,13" : "=r"(td)); #else __asm __volatile("mr %0,2" : "=r"(td)); #endif return (td); } #define curthread (__curthread()) #define PCPU_GET(member) (pcpup->pc_ ## member) /* * XXX The implementation of this operation should be made atomic * with respect to preemption. */ #define PCPU_ADD(member, value) (pcpup->pc_ ## member += (value)) #define PCPU_INC(member) PCPU_ADD(member, 1) #define PCPU_PTR(member) (&pcpup->pc_ ## member) #define PCPU_SET(member,value) (pcpup->pc_ ## member = (value)) #endif /* _KERNEL */ #endif /* !_MACHINE_PCPU_H_ */ Index: head/sys/powerpc/include/platformvar.h =================================================================== --- head/sys/powerpc/include/platformvar.h (revision 326260) +++ head/sys/powerpc/include/platformvar.h (revision 326261) @@ -1,89 +1,91 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2005 Peter Grehan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_PLATFORMVAR_H_ #define _MACHINE_PLATFORMVAR_H_ /* * A PowerPC platform implementation is declared with a kernel object and * an associated method table, similar to a device driver. * * e.g. * * static platform_method_t chrp_methods[] = { * PLATFORMMETHOD(platform_probe, chrp_probe), * PLATFORMMETHOD(platform_mem_regions, ofw_mem_regions), * ... * PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu), * { 0, 0 } * }; * * static platform_def_t chrp_platform = { * "chrp", * chrp_methods, * sizeof(chrp_platform_softc), // or 0 if no softc * }; * * PLATFORM_DEF(chrp_platform); */ #include struct platform_kobj { /* * A platform instance is a kernel object */ KOBJ_FIELDS; /* * Utility elements that an instance may use */ struct mtx platform_mtx; /* available for instance use */ void *platform_iptr; /* instance data pointer */ /* * Opaque data that can be overlaid with an instance-private * structure. Platform code can test that this is large enough at * compile time with a sizeof() test againt it's softc. There * is also a run-time test when the platform kernel object is * registered. */ #define PLATFORM_OPAQUESZ 64 u_int platform_opaque[PLATFORM_OPAQUESZ]; }; typedef struct platform_kobj *platform_t; typedef struct kobj_class platform_def_t; #define platform_method_t kobj_method_t #define PLATFORMMETHOD KOBJMETHOD #define PLATFORMMETHOD_END KOBJMETHOD_END #define PLATFORM_DEF(name) DATA_SET(platform_set, name) #endif /* _MACHINE_PLATFORMVAR_H_ */ Index: head/sys/powerpc/include/pmap.h =================================================================== --- head/sys/powerpc/include/pmap.h (revision 326260) +++ head/sys/powerpc/include/pmap.h (revision 326261) @@ -1,289 +1,289 @@ /*- - * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: BSD-3-Clause AND BSD-4-Clause * * Copyright (C) 2006 Semihalf, Marian Balakowicz * All rights reserved. * * Adapted for Freescale's e500 core CPUs. * * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /*- * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * from: $NetBSD: pmap.h,v 1.17 2000/03/30 16:18:24 jdolecek Exp $ */ #ifndef _MACHINE_PMAP_H_ #define _MACHINE_PMAP_H_ #include #include #include #include #include #include #include #include #include struct pmap; typedef struct pmap *pmap_t; #if defined(AIM) #if !defined(NPMAPS) #define NPMAPS 32768 #endif /* !defined(NPMAPS) */ struct slbtnode; struct pvo_entry { LIST_ENTRY(pvo_entry) pvo_vlink; /* Link to common virt page */ #ifndef __powerpc64__ LIST_ENTRY(pvo_entry) pvo_olink; /* Link to overflow entry */ #endif RB_ENTRY(pvo_entry) pvo_plink; /* Link to pmap entries */ struct { #ifndef __powerpc64__ /* 32-bit fields */ struct pte pte; #endif /* 64-bit fields */ uintptr_t slot; vm_paddr_t pa; vm_prot_t prot; } pvo_pte; pmap_t pvo_pmap; /* Owning pmap */ vm_offset_t pvo_vaddr; /* VA of entry */ uint64_t pvo_vpn; /* Virtual page number */ }; LIST_HEAD(pvo_head, pvo_entry); RB_HEAD(pvo_tree, pvo_entry); int pvo_vaddr_compare(struct pvo_entry *, struct pvo_entry *); RB_PROTOTYPE(pvo_tree, pvo_entry, pvo_plink, pvo_vaddr_compare); /* Used by 32-bit PMAP */ #define PVO_PTEGIDX_MASK 0x007UL /* which PTEG slot */ #define PVO_PTEGIDX_VALID 0x008UL /* slot is valid */ /* Used by 64-bit PMAP */ #define PVO_HID 0x008UL /* PVO entry in alternate hash*/ /* Used by both */ #define PVO_WIRED 0x010UL /* PVO entry is wired */ #define PVO_MANAGED 0x020UL /* PVO entry is managed */ #define PVO_BOOTSTRAP 0x080UL /* PVO entry allocated during bootstrap */ #define PVO_DEAD 0x100UL /* waiting to be deleted */ #define PVO_LARGE 0x200UL /* large page */ #define PVO_VADDR(pvo) ((pvo)->pvo_vaddr & ~ADDR_POFF) #define PVO_PTEGIDX_GET(pvo) ((pvo)->pvo_vaddr & PVO_PTEGIDX_MASK) #define PVO_PTEGIDX_ISSET(pvo) ((pvo)->pvo_vaddr & PVO_PTEGIDX_VALID) #define PVO_PTEGIDX_CLR(pvo) \ ((void)((pvo)->pvo_vaddr &= ~(PVO_PTEGIDX_VALID|PVO_PTEGIDX_MASK))) #define PVO_PTEGIDX_SET(pvo, i) \ ((void)((pvo)->pvo_vaddr |= (i)|PVO_PTEGIDX_VALID)) #define PVO_VSID(pvo) ((pvo)->pvo_vpn >> 16) struct pmap { struct pmap_statistics pm_stats; struct mtx pm_mtx; #ifdef __powerpc64__ struct slbtnode *pm_slb_tree_root; struct slb **pm_slb; int pm_slb_len; #else register_t pm_sr[16]; #endif cpuset_t pm_active; struct pmap *pmap_phys; struct pvo_tree pmap_pvo; }; struct md_page { volatile int32_t mdpg_attrs; vm_memattr_t mdpg_cache_attrs; struct pvo_head mdpg_pvoh; }; #define pmap_page_get_memattr(m) ((m)->md.mdpg_cache_attrs) #define pmap_page_is_mapped(m) (!LIST_EMPTY(&(m)->md.mdpg_pvoh)) /* * Return the VSID corresponding to a given virtual address. * If no VSID is currently defined, it will allocate one, and add * it to a free slot if available. * * NB: The PMAP MUST be locked already. */ uint64_t va_to_vsid(pmap_t pm, vm_offset_t va); /* Lock-free, non-allocating lookup routines */ uint64_t kernel_va_to_slbv(vm_offset_t va); struct slb *user_va_to_slb_entry(pmap_t pm, vm_offset_t va); uint64_t allocate_user_vsid(pmap_t pm, uint64_t esid, int large); void free_vsid(pmap_t pm, uint64_t esid, int large); void slb_insert_user(pmap_t pm, struct slb *slb); void slb_insert_kernel(uint64_t slbe, uint64_t slbv); struct slbtnode *slb_alloc_tree(void); void slb_free_tree(pmap_t pm); struct slb **slb_alloc_user_cache(void); void slb_free_user_cache(struct slb **); #elif defined(BOOKE) struct pmap { struct pmap_statistics pm_stats; /* pmap statistics */ struct mtx pm_mtx; /* pmap mutex */ tlbtid_t pm_tid[MAXCPU]; /* TID to identify this pmap entries in TLB */ cpuset_t pm_active; /* active on cpus */ #ifdef __powerpc64__ /* Page table directory, array of pointers to page directories. */ pte_t **pm_pp2d[PP2D_NENTRIES]; /* List of allocated pdir bufs (pdir kva regions). */ TAILQ_HEAD(, ptbl_buf) pm_pdir_list; #else /* Page table directory, array of pointers to page tables. */ pte_t *pm_pdir[PDIR_NENTRIES]; #endif /* List of allocated ptbl bufs (ptbl kva regions). */ TAILQ_HEAD(, ptbl_buf) pm_ptbl_list; }; struct pv_entry { pmap_t pv_pmap; vm_offset_t pv_va; TAILQ_ENTRY(pv_entry) pv_link; }; typedef struct pv_entry *pv_entry_t; struct md_page { TAILQ_HEAD(, pv_entry) pv_list; int pv_tracked; }; #define pmap_page_get_memattr(m) VM_MEMATTR_DEFAULT #define pmap_page_is_mapped(m) (!TAILQ_EMPTY(&(m)->md.pv_list)) #else /* * Common pmap members between AIM and BOOKE. * libkvm needs pm_stats at the same location between both, as it doesn't define * AIM nor BOOKE, and is expected to work across all. */ struct pmap { struct pmap_statistics pm_stats; /* pmap statistics */ struct mtx pm_mtx; /* pmap mutex */ }; #endif /* AIM */ extern struct pmap kernel_pmap_store; #define kernel_pmap (&kernel_pmap_store) #ifdef _KERNEL #define PMAP_LOCK(pmap) mtx_lock(&(pmap)->pm_mtx) #define PMAP_LOCK_ASSERT(pmap, type) \ mtx_assert(&(pmap)->pm_mtx, (type)) #define PMAP_LOCK_DESTROY(pmap) mtx_destroy(&(pmap)->pm_mtx) #define PMAP_LOCK_INIT(pmap) mtx_init(&(pmap)->pm_mtx, \ (pmap == kernel_pmap) ? "kernelpmap" : \ "pmap", NULL, MTX_DEF) #define PMAP_LOCKED(pmap) mtx_owned(&(pmap)->pm_mtx) #define PMAP_MTX(pmap) (&(pmap)->pm_mtx) #define PMAP_TRYLOCK(pmap) mtx_trylock(&(pmap)->pm_mtx) #define PMAP_UNLOCK(pmap) mtx_unlock(&(pmap)->pm_mtx) #define pmap_page_is_write_mapped(m) (((m)->aflags & PGA_WRITEABLE) != 0) void pmap_bootstrap(vm_offset_t, vm_offset_t); void pmap_kenter(vm_offset_t va, vm_paddr_t pa); void pmap_kenter_attr(vm_offset_t va, vm_paddr_t pa, vm_memattr_t); void pmap_kremove(vm_offset_t); void *pmap_mapdev(vm_paddr_t, vm_size_t); void *pmap_mapdev_attr(vm_paddr_t, vm_size_t, vm_memattr_t); void pmap_unmapdev(vm_offset_t, vm_size_t); void pmap_page_set_memattr(vm_page_t, vm_memattr_t); int pmap_change_attr(vm_offset_t, vm_size_t, vm_memattr_t); void pmap_deactivate(struct thread *); vm_paddr_t pmap_kextract(vm_offset_t); int pmap_dev_direct_mapped(vm_paddr_t, vm_size_t); boolean_t pmap_mmu_install(char *name, int prio); #define vtophys(va) pmap_kextract((vm_offset_t)(va)) #define PHYS_AVAIL_SZ 256 /* Allows up to 16GB Ram on pSeries with * logical memory block size of 64MB. * For more Ram increase the lmb or this value. */ extern vm_paddr_t phys_avail[PHYS_AVAIL_SZ]; extern vm_offset_t virtual_avail; extern vm_offset_t virtual_end; extern vm_offset_t msgbuf_phys; extern int pmap_bootstrapped; vm_offset_t pmap_early_io_map(vm_paddr_t pa, vm_size_t size); void pmap_early_io_unmap(vm_offset_t va, vm_size_t size); void pmap_track_page(pmap_t pmap, vm_offset_t va); #endif #endif /* !_MACHINE_PMAP_H_ */ Index: head/sys/powerpc/include/rtas.h =================================================================== --- head/sys/powerpc/include/rtas.h (revision 326260) +++ head/sys/powerpc/include/rtas.h (revision 326261) @@ -1,61 +1,63 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_RTAS_H_ #define _MACHINE_RTAS_H_ #include #include #include /* * RTAS functions are defined by 32-bit integer tokens. These vary from * system to system, and can be looked up from their standardized names * using rtas_token_lookup(). If RTAS is not available, rtas_token_lookup() * and rtas_call_method() return -1; this can be checked in advance using * rtas_exists(). Otherwise, rtas_call_method() returns one of the RTAS * status codes from the bottom of this file. */ int rtas_exists(void); int rtas_call_method(cell_t token, int nargs, int nreturns, ...); cell_t rtas_token_lookup(const char *method); /* RTAS Status Codes: see CHRP or PAPR specification */ #define RTAS_OK 0 #define RTAS_HW_ERROR -1 #define RTAS_BUSY -2 #define RTAS_PARAM_ERROR -3 #define RTAS_STATE_CHANGE -7 #define RTAS_VENDOR_BEGIN 9000 #define RTAS_EXTENDED_DELAY 9900 #define RTAS_ISOLATION_ERROR -9000 #define RTAS_VENDOR_ERROR_BEGIN -9004 #endif /* _MACHINE_RTAS_H_ */ Index: head/sys/powerpc/include/runq.h =================================================================== --- head/sys/powerpc/include/runq.h (revision 326260) +++ head/sys/powerpc/include/runq.h (revision 326261) @@ -1,55 +1,57 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2001 Jake Burkholder * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_RUNQ_H_ #define _MACHINE_RUNQ_H_ #ifdef __powerpc64__ #define RQB_LEN (1UL) /* Number of priority status words. */ #define RQB_L2BPW (6UL) /* Log2(sizeof(rqb_word_t) * NBBY)). */ #else #define RQB_LEN (2) /* Number of priority status words. */ #define RQB_L2BPW (5) /* Log2(sizeof(rqb_word_t) * NBBY)). */ #endif #define RQB_BPW (1UL<> RQB_L2BPW) #define RQB_FFS(word) (ffsl(word) - 1) /* * Type of run queue status word. */ #ifdef __powerpc64__ typedef u_int64_t rqb_word_t; #else typedef u_int32_t rqb_word_t; #endif #endif Index: head/sys/powerpc/include/sc_machdep.h =================================================================== --- head/sys/powerpc/include/sc_machdep.h (revision 326260) +++ head/sys/powerpc/include/sc_machdep.h (revision 326261) @@ -1,71 +1,73 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2003 Jake Burkholder. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_SC_MACHDEP_H_ #define _MACHINE_SC_MACHDEP_H_ /* Color attributes for foreground text */ #define FG_BLACK 0x0 #define FG_BLUE 0x1 #define FG_GREEN 0x2 #define FG_CYAN 0x3 #define FG_RED 0x4 #define FG_MAGENTA 0x5 #define FG_BROWN 0x6 #define FG_LIGHTGREY 0x7 /* aka white */ #define FG_DARKGREY 0x8 #define FG_LIGHTBLUE 0x9 #define FG_LIGHTGREEN 0xa #define FG_LIGHTCYAN 0xb #define FG_LIGHTRED 0xc #define FG_LIGHTMAGENTA 0xd #define FG_YELLOW 0xe #define FG_WHITE 0xf /* aka bright white */ #define FG_BLINK 0x80 /* Color attributes for text background */ #define BG_BLACK 0x00 #define BG_BLUE 0x10 #define BG_GREEN 0x20 #define BG_CYAN 0x30 #define BG_RED 0x40 #define BG_MAGENTA 0x50 #define BG_BROWN 0x60 #define BG_LIGHTGREY 0x70 #define BG_DARKGREY 0x80 #define BG_LIGHTBLUE 0x90 #define BG_LIGHTGREEN 0xa0 #define BG_LIGHTCYAN 0xb0 #define BG_LIGHTRED 0xc0 #define BG_LIGHTMAGENTA 0xd0 #define BG_YELLOW 0xe0 #define BG_WHITE 0xf0 #endif /* !_MACHINE_SC_MACHDEP_H_ */ Index: head/sys/powerpc/include/slb.h =================================================================== --- head/sys/powerpc/include/slb.h (revision 326260) +++ head/sys/powerpc/include/slb.h (revision 326261) @@ -1,80 +1,82 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2009 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_SLB_H_ #define _MACHINE_SLB_H_ /* * Bit definitions for segment lookaside buffer entries. * * PowerPC Microprocessor Family: The Programming Environments for 64-bit * Microprocessors, section 7.4.2.1 * * Note that these bitmasks are relative to the values for one of the two * values for slbmte, slbmfee, and slbmfev, not the internal SLB * representation. */ #define SLBV_KS 0x0000000000000800UL /* Supervisor-state prot key */ #define SLBV_KP 0x0000000000000400UL /* User-state prot key */ #define SLBV_N 0x0000000000000200UL /* No-execute protection */ #define SLBV_L 0x0000000000000100UL /* Large page selector */ #define SLBV_CLASS 0x0000000000000080UL /* Class selector */ #define SLBV_VSID_MASK 0xfffffffffffff000UL /* Virtual segment ID mask */ #define SLBV_VSID_SHIFT 12 /* * Make a predictable 1:1 map from ESIDs to VSIDs for the kernel. Hash table * coverage is increased by swizzling the ESID and multiplying by a prime * number (0x13bb). */ #define KERNEL_VSID_BIT 0x0000001000000000UL /* Bit set in all kernel VSIDs */ #define KERNEL_VSID(esid) ((((((uint64_t)esid << 8) | ((uint64_t)esid >> 28)) \ * 0x13bbUL) & (KERNEL_VSID_BIT - 1)) | \ KERNEL_VSID_BIT) #define SLBE_VALID 0x0000000008000000UL /* SLB entry valid */ #define SLBE_INDEX_MASK 0x0000000000000fffUL /* SLB index mask*/ #define SLBE_ESID_MASK 0xfffffffff0000000UL /* Effective segment ID mask */ #define SLBE_ESID_SHIFT 28 /* Virtual real-mode VSID in LPARs */ #define VSID_VRMA 0x1ffffff /* * User segment for copyin/out */ #define USER_SLB_SLOT 0 #define USER_SLB_SLBE (((USER_ADDR >> ADDR_SR_SHFT) << SLBE_ESID_SHIFT) | \ SLBE_VALID | USER_SLB_SLOT) struct slb { uint64_t slbv; uint64_t slbe; }; #endif /* !_MACHINE_SLB_H_ */ Index: head/sys/powerpc/include/smp.h =================================================================== --- head/sys/powerpc/include/smp.h (revision 326260) +++ head/sys/powerpc/include/smp.h (revision 326261) @@ -1,62 +1,64 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2008 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_SMP_H_ #define _MACHINE_SMP_H_ #ifdef _KERNEL #define IPI_AST 0 #define IPI_PREEMPT 1 #define IPI_RENDEZVOUS 2 #define IPI_STOP 3 #define IPI_STOP_HARD 3 #define IPI_HARDCLOCK 4 #ifndef LOCORE #include void ipi_all_but_self(int ipi); void ipi_cpu(int cpu, u_int ipi); void ipi_selected(cpuset_t cpus, int ipi); struct cpuref { uintptr_t cr_hwref; u_int cr_cpuid; }; void pmap_cpu_bootstrap(int); void cpudep_ap_early_bootstrap(void); uintptr_t cpudep_ap_bootstrap(void); void cpudep_ap_setup(void); void machdep_ap_bootstrap(void); #endif /* !LOCORE */ #endif /* _KERNEL */ #endif /* !_MACHINE_SMP_H */ Index: head/sys/powerpc/include/spr.h =================================================================== --- head/sys/powerpc/include/spr.h (revision 326260) +++ head/sys/powerpc/include/spr.h (revision 326261) @@ -1,830 +1,832 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2001 The NetBSD Foundation, Inc. * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $NetBSD: spr.h,v 1.25 2002/08/14 15:38:40 matt Exp $ * $FreeBSD$ */ #ifndef _POWERPC_SPR_H_ #define _POWERPC_SPR_H_ #ifndef _LOCORE #define mtspr(reg, val) \ __asm __volatile("mtspr %0,%1" : : "K"(reg), "r"(val)) #define mfspr(reg) \ ( { register_t val; \ __asm __volatile("mfspr %0,%1" : "=r"(val) : "K"(reg)); \ val; } ) #ifndef __powerpc64__ /* The following routines allow manipulation of the full 64-bit width * of SPRs on 64 bit CPUs in bridge mode */ #define mtspr64(reg,valhi,vallo,scratch) \ __asm __volatile(" \ mfmsr %0; \ insrdi %0,%5,1,0; \ mtmsrd %0; \ isync; \ \ sld %1,%1,%4; \ or %1,%1,%2; \ mtspr %3,%1; \ srd %1,%1,%4; \ \ clrldi %0,%0,1; \ mtmsrd %0; \ isync;" \ : "=r"(scratch), "=r"(valhi) : "r"(vallo), "K"(reg), "r"(32), "r"(1)) #define mfspr64upper(reg,scratch) \ ( { register_t val; \ __asm __volatile(" \ mfmsr %0; \ insrdi %0,%4,1,0; \ mtmsrd %0; \ isync; \ \ mfspr %1,%2; \ srd %1,%1,%3; \ \ clrldi %0,%0,1; \ mtmsrd %0; \ isync;" \ : "=r"(scratch), "=r"(val) : "K"(reg), "r"(32), "r"(1)); \ val; } ) #endif #endif /* _LOCORE */ /* * Special Purpose Register declarations. * * The first column in the comments indicates which PowerPC * architectures the SPR is valid on - 4 for 4xx series, * 6 for 6xx/7xx series and 8 for 8xx and 8xxx series. */ #define SPR_MQ 0x000 /* .6. 601 MQ register */ #define SPR_XER 0x001 /* 468 Fixed Point Exception Register */ #define SPR_RTCU_R 0x004 /* .6. 601 RTC Upper - Read */ #define SPR_RTCL_R 0x005 /* .6. 601 RTC Lower - Read */ #define SPR_LR 0x008 /* 468 Link Register */ #define SPR_CTR 0x009 /* 468 Count Register */ #define SPR_DSISR 0x012 /* .68 DSI exception source */ #define DSISR_DIRECT 0x80000000 /* Direct-store error exception */ #define DSISR_NOTFOUND 0x40000000 /* Translation not found */ #define DSISR_PROTECT 0x08000000 /* Memory access not permitted */ #define DSISR_INVRX 0x04000000 /* Reserve-indexed insn direct-store access */ #define DSISR_STORE 0x02000000 /* Store operation */ #define DSISR_DABR 0x00400000 /* DABR match */ #define DSISR_SEGMENT 0x00200000 /* XXX; not in 6xx PEM */ #define DSISR_EAR 0x00100000 /* eciwx/ecowx && EAR[E] == 0 */ #define SPR_DAR 0x013 /* .68 Data Address Register */ #define SPR_RTCU_W 0x014 /* .6. 601 RTC Upper - Write */ #define SPR_RTCL_W 0x015 /* .6. 601 RTC Lower - Write */ #define SPR_DEC 0x016 /* .68 DECrementer register */ #define SPR_SDR1 0x019 /* .68 Page table base address register */ #define SPR_SRR0 0x01a /* 468 Save/Restore Register 0 */ #define SPR_SRR1 0x01b /* 468 Save/Restore Register 1 */ #define SRR1_ISI_PFAULT 0x40000000 /* ISI page not found */ #define SRR1_ISI_NOEXECUTE 0x10000000 /* Memory marked no-execute */ #define SRR1_ISI_PP 0x08000000 /* PP bits forbid access */ #define SPR_DECAR 0x036 /* ..8 Decrementer auto reload */ #define SPR_EIE 0x050 /* ..8 Exception Interrupt ??? */ #define SPR_EID 0x051 /* ..8 Exception Interrupt ??? */ #define SPR_NRI 0x052 /* ..8 Exception Interrupt ??? */ #define SPR_USPRG0 0x100 /* 4.. User SPR General 0 */ #define SPR_VRSAVE 0x100 /* .6. AltiVec VRSAVE */ #define SPR_SPRG0 0x110 /* 468 SPR General 0 */ #define SPR_SPRG1 0x111 /* 468 SPR General 1 */ #define SPR_SPRG2 0x112 /* 468 SPR General 2 */ #define SPR_SPRG3 0x113 /* 468 SPR General 3 */ #define SPR_SPRG4 0x114 /* 4.. SPR General 4 */ #define SPR_SPRG5 0x115 /* 4.. SPR General 5 */ #define SPR_SPRG6 0x116 /* 4.. SPR General 6 */ #define SPR_SPRG7 0x117 /* 4.. SPR General 7 */ #define SPR_SCOMC 0x114 /* ... SCOM Address Register (970) */ #define SPR_SCOMD 0x115 /* ... SCOM Data Register (970) */ #define SPR_ASR 0x118 /* ... Address Space Register (PPC64) */ #define SPR_EAR 0x11a /* .68 External Access Register */ #define SPR_PVR 0x11f /* 468 Processor Version Register */ #define MPC601 0x0001 #define MPC603 0x0003 #define MPC604 0x0004 #define MPC602 0x0005 #define MPC603e 0x0006 #define MPC603ev 0x0007 #define MPC750 0x0008 #define MPC750CL 0x7000 /* Nintendo Wii's Broadway */ #define MPC604ev 0x0009 #define MPC7400 0x000c #define MPC620 0x0014 #define IBM403 0x0020 #define IBM401A1 0x0021 #define IBM401B2 0x0022 #define IBM401C2 0x0023 #define IBM401D2 0x0024 #define IBM401E2 0x0025 #define IBM401F2 0x0026 #define IBM401G2 0x0027 #define IBMRS64II 0x0033 #define IBMRS64III 0x0034 #define IBMPOWER4 0x0035 #define IBMRS64III_2 0x0036 #define IBMRS64IV 0x0037 #define IBMPOWER4PLUS 0x0038 #define IBM970 0x0039 #define IBMPOWER5 0x003a #define IBMPOWER5PLUS 0x003b #define IBM970FX 0x003c #define IBMPOWER6 0x003e #define IBMPOWER7 0x003f #define IBMPOWER3 0x0040 #define IBMPOWER3PLUS 0x0041 #define IBM970MP 0x0044 #define IBM970GX 0x0045 #define IBMPOWER7PLUS 0x004a #define IBMPOWER8E 0x004b #define IBMPOWER8 0x004d #define IBMPOWER9 0x004e #define MPC860 0x0050 #define IBMCELLBE 0x0070 #define MPC8240 0x0081 #define PA6T 0x0090 #define IBM405GP 0x4011 #define IBM405L 0x4161 #define IBM750FX 0x7000 #define MPC745X_P(v) ((v & 0xFFF8) == 0x8000) #define MPC7450 0x8000 #define MPC7455 0x8001 #define MPC7457 0x8002 #define MPC7447A 0x8003 #define MPC7448 0x8004 #define MPC7410 0x800c #define MPC8245 0x8081 #define FSL_E500v1 0x8020 #define FSL_E500v2 0x8021 #define FSL_E500mc 0x8023 #define FSL_E5500 0x8024 #define FSL_E6500 0x8040 #define FSL_E300C1 0x8083 #define FSL_E300C2 0x8084 #define FSL_E300C3 0x8085 #define FSL_E300C4 0x8086 #define SPR_EPCR 0x133 #define EPCR_EXTGS 0x80000000 #define EPCR_DTLBGS 0x40000000 #define EPCR_ITLBGS 0x20000000 #define EPCR_DSIGS 0x10000000 #define EPCR_ISIGS 0x08000000 #define EPCR_DUVGS 0x04000000 #define EPCR_ICM 0x02000000 #define EPCR_GICMGS 0x01000000 #define EPCR_DGTMI 0x00800000 #define EPCR_DMIUH 0x00400000 #define EPCR_PMGS 0x00200000 #define SPR_SPEFSCR 0x200 /* ..8 Signal Processing Engine FSCR. */ #define SPR_LPCR 0x13e /* Logical Partitioning Control */ #define LPCR_LPES 0x008 /* Bit 60 */ #define SPR_LPID 0x13f /* Logical Partitioning Control */ #define SPR_IBAT0U 0x210 /* .68 Instruction BAT Reg 0 Upper */ #define SPR_IBAT0U 0x210 /* .6. Instruction BAT Reg 0 Upper */ #define SPR_IBAT0L 0x211 /* .6. Instruction BAT Reg 0 Lower */ #define SPR_IBAT1U 0x212 /* .6. Instruction BAT Reg 1 Upper */ #define SPR_IBAT1L 0x213 /* .6. Instruction BAT Reg 1 Lower */ #define SPR_IBAT2U 0x214 /* .6. Instruction BAT Reg 2 Upper */ #define SPR_IBAT2L 0x215 /* .6. Instruction BAT Reg 2 Lower */ #define SPR_IBAT3U 0x216 /* .6. Instruction BAT Reg 3 Upper */ #define SPR_IBAT3L 0x217 /* .6. Instruction BAT Reg 3 Lower */ #define SPR_DBAT0U 0x218 /* .6. Data BAT Reg 0 Upper */ #define SPR_DBAT0L 0x219 /* .6. Data BAT Reg 0 Lower */ #define SPR_DBAT1U 0x21a /* .6. Data BAT Reg 1 Upper */ #define SPR_DBAT1L 0x21b /* .6. Data BAT Reg 1 Lower */ #define SPR_DBAT2U 0x21c /* .6. Data BAT Reg 2 Upper */ #define SPR_DBAT2L 0x21d /* .6. Data BAT Reg 2 Lower */ #define SPR_DBAT3U 0x21e /* .6. Data BAT Reg 3 Upper */ #define SPR_DBAT3L 0x21f /* .6. Data BAT Reg 3 Lower */ #define SPR_IC_CST 0x230 /* ..8 Instruction Cache CSR */ #define IC_CST_IEN 0x80000000 /* I cache is ENabled (RO) */ #define IC_CST_CMD_INVALL 0x0c000000 /* I cache invalidate all */ #define IC_CST_CMD_UNLOCKALL 0x0a000000 /* I cache unlock all */ #define IC_CST_CMD_UNLOCK 0x08000000 /* I cache unlock block */ #define IC_CST_CMD_LOADLOCK 0x06000000 /* I cache load & lock block */ #define IC_CST_CMD_DISABLE 0x04000000 /* I cache disable */ #define IC_CST_CMD_ENABLE 0x02000000 /* I cache enable */ #define IC_CST_CCER1 0x00200000 /* I cache error type 1 (RO) */ #define IC_CST_CCER2 0x00100000 /* I cache error type 2 (RO) */ #define IC_CST_CCER3 0x00080000 /* I cache error type 3 (RO) */ #define SPR_IBAT4U 0x230 /* .6. Instruction BAT Reg 4 Upper */ #define SPR_IC_ADR 0x231 /* ..8 Instruction Cache Address */ #define SPR_IBAT4L 0x231 /* .6. Instruction BAT Reg 4 Lower */ #define SPR_IC_DAT 0x232 /* ..8 Instruction Cache Data */ #define SPR_IBAT5U 0x232 /* .6. Instruction BAT Reg 5 Upper */ #define SPR_IBAT5L 0x233 /* .6. Instruction BAT Reg 5 Lower */ #define SPR_IBAT6U 0x234 /* .6. Instruction BAT Reg 6 Upper */ #define SPR_IBAT6L 0x235 /* .6. Instruction BAT Reg 6 Lower */ #define SPR_IBAT7U 0x236 /* .6. Instruction BAT Reg 7 Upper */ #define SPR_IBAT7L 0x237 /* .6. Instruction BAT Reg 7 Lower */ #define SPR_DC_CST 0x230 /* ..8 Data Cache CSR */ #define DC_CST_DEN 0x80000000 /* D cache ENabled (RO) */ #define DC_CST_DFWT 0x40000000 /* D cache Force Write-Thru (RO) */ #define DC_CST_LES 0x20000000 /* D cache Little Endian Swap (RO) */ #define DC_CST_CMD_FLUSH 0x0e000000 /* D cache invalidate all */ #define DC_CST_CMD_INVALL 0x0c000000 /* D cache invalidate all */ #define DC_CST_CMD_UNLOCKALL 0x0a000000 /* D cache unlock all */ #define DC_CST_CMD_UNLOCK 0x08000000 /* D cache unlock block */ #define DC_CST_CMD_CLRLESWAP 0x07000000 /* D cache clr little-endian swap */ #define DC_CST_CMD_LOADLOCK 0x06000000 /* D cache load & lock block */ #define DC_CST_CMD_SETLESWAP 0x05000000 /* D cache set little-endian swap */ #define DC_CST_CMD_DISABLE 0x04000000 /* D cache disable */ #define DC_CST_CMD_CLRFWT 0x03000000 /* D cache clear forced write-thru */ #define DC_CST_CMD_ENABLE 0x02000000 /* D cache enable */ #define DC_CST_CMD_SETFWT 0x01000000 /* D cache set forced write-thru */ #define DC_CST_CCER1 0x00200000 /* D cache error type 1 (RO) */ #define DC_CST_CCER2 0x00100000 /* D cache error type 2 (RO) */ #define DC_CST_CCER3 0x00080000 /* D cache error type 3 (RO) */ #define SPR_DBAT4U 0x238 /* .6. Data BAT Reg 4 Upper */ #define SPR_DC_ADR 0x231 /* ..8 Data Cache Address */ #define SPR_DBAT4L 0x239 /* .6. Data BAT Reg 4 Lower */ #define SPR_DC_DAT 0x232 /* ..8 Data Cache Data */ #define SPR_DBAT5U 0x23a /* .6. Data BAT Reg 5 Upper */ #define SPR_DBAT5L 0x23b /* .6. Data BAT Reg 5 Lower */ #define SPR_DBAT6U 0x23c /* .6. Data BAT Reg 6 Upper */ #define SPR_DBAT6L 0x23d /* .6. Data BAT Reg 6 Lower */ #define SPR_DBAT7U 0x23e /* .6. Data BAT Reg 7 Upper */ #define SPR_DBAT7L 0x23f /* .6. Data BAT Reg 7 Lower */ #define SPR_SPRG8 0x25c /* ..8 SPR General 8 */ #define SPR_MI_CTR 0x310 /* ..8 IMMU control */ #define Mx_CTR_GPM 0x80000000 /* Group Protection Mode */ #define Mx_CTR_PPM 0x40000000 /* Page Protection Mode */ #define Mx_CTR_CIDEF 0x20000000 /* Cache-Inhibit DEFault */ #define MD_CTR_WTDEF 0x20000000 /* Write-Through DEFault */ #define Mx_CTR_RSV4 0x08000000 /* Reserve 4 TLB entries */ #define MD_CTR_TWAM 0x04000000 /* TableWalk Assist Mode */ #define Mx_CTR_PPCS 0x02000000 /* Priv/user state compare mode */ #define Mx_CTR_TLB_INDX 0x000001f0 /* TLB index mask */ #define Mx_CTR_TLB_INDX_BITPOS 8 /* TLB index shift */ #define SPR_MI_AP 0x312 /* ..8 IMMU access protection */ #define Mx_GP_SUPER(n) (0 << (2*(15-(n)))) /* access is supervisor */ #define Mx_GP_PAGE (1 << (2*(15-(n)))) /* access is page protect */ #define Mx_GP_SWAPPED (2 << (2*(15-(n)))) /* access is swapped */ #define Mx_GP_USER (3 << (2*(15-(n)))) /* access is user */ #define SPR_MI_EPN 0x313 /* ..8 IMMU effective number */ #define Mx_EPN_EPN 0xfffff000 /* Effective Page Number mask */ #define Mx_EPN_EV 0x00000020 /* Entry Valid */ #define Mx_EPN_ASID 0x0000000f /* Address Space ID */ #define SPR_MI_TWC 0x315 /* ..8 IMMU tablewalk control */ #define MD_TWC_L2TB 0xfffff000 /* Level-2 Tablewalk Base */ #define Mx_TWC_APG 0x000001e0 /* Access Protection Group */ #define Mx_TWC_G 0x00000010 /* Guarded memory */ #define Mx_TWC_PS 0x0000000c /* Page Size (L1) */ #define MD_TWC_WT 0x00000002 /* Write-Through */ #define Mx_TWC_V 0x00000001 /* Entry Valid */ #define SPR_MI_RPN 0x316 /* ..8 IMMU real (phys) page number */ #define Mx_RPN_RPN 0xfffff000 /* Real Page Number */ #define Mx_RPN_PP 0x00000ff0 /* Page Protection */ #define Mx_RPN_SPS 0x00000008 /* Small Page Size */ #define Mx_RPN_SH 0x00000004 /* SHared page */ #define Mx_RPN_CI 0x00000002 /* Cache Inhibit */ #define Mx_RPN_V 0x00000001 /* Valid */ #define SPR_MD_CTR 0x318 /* ..8 DMMU control */ #define SPR_M_CASID 0x319 /* ..8 CASID */ #define M_CASID 0x0000000f /* Current AS Id */ #define SPR_MD_AP 0x31a /* ..8 DMMU access protection */ #define SPR_MD_EPN 0x31b /* ..8 DMMU effective number */ #define SPR_970MMCR0 0x31b /* ... Monitor Mode Control Register 0 (PPC 970) */ #define SPR_970MMCR0_PMC1SEL(x) ((x) << 8) /* PMC1 selector (970) */ #define SPR_970MMCR0_PMC2SEL(x) ((x) << 1) /* PMC2 selector (970) */ #define SPR_970MMCR1 0x31e /* ... Monitor Mode Control Register 1 (PPC 970) */ #define SPR_970MMCR1_PMC3SEL(x) (((x) & 0x1f) << 27) /* PMC 3 selector */ #define SPR_970MMCR1_PMC4SEL(x) (((x) & 0x1f) << 22) /* PMC 4 selector */ #define SPR_970MMCR1_PMC5SEL(x) (((x) & 0x1f) << 17) /* PMC 5 selector */ #define SPR_970MMCR1_PMC6SEL(x) (((x) & 0x1f) << 12) /* PMC 6 selector */ #define SPR_970MMCR1_PMC7SEL(x) (((x) & 0x1f) << 7) /* PMC 7 selector */ #define SPR_970MMCR1_PMC8SEL(x) (((x) & 0x1f) << 2) /* PMC 8 selector */ #define SPR_970MMCRA 0x312 /* ... Monitor Mode Control Register 2 (PPC 970) */ #define SPR_970PMC1 0x313 /* ... PMC 1 */ #define SPR_970PMC2 0x314 /* ... PMC 2 */ #define SPR_970PMC3 0x315 /* ... PMC 3 */ #define SPR_970PMC4 0x316 /* ... PMC 4 */ #define SPR_970PMC5 0x317 /* ... PMC 5 */ #define SPR_970PMC6 0x318 /* ... PMC 6 */ #define SPR_970PMC7 0x319 /* ... PMC 7 */ #define SPR_970PMC8 0x31a /* ... PMC 8 */ #define SPR_M_TWB 0x31c /* ..8 MMU tablewalk base */ #define M_TWB_L1TB 0xfffff000 /* level-1 translation base */ #define M_TWB_L1INDX 0x00000ffc /* level-1 index */ #define SPR_MD_TWC 0x31d /* ..8 DMMU tablewalk control */ #define SPR_MD_RPN 0x31e /* ..8 DMMU real (phys) page number */ #define SPR_MD_TW 0x31f /* ..8 MMU tablewalk scratch */ #define SPR_MI_CAM 0x330 /* ..8 IMMU CAM entry read */ #define SPR_MI_RAM0 0x331 /* ..8 IMMU RAM entry read reg 0 */ #define SPR_MI_RAM1 0x332 /* ..8 IMMU RAM entry read reg 1 */ #define SPR_MD_CAM 0x338 /* ..8 IMMU CAM entry read */ #define SPR_MD_RAM0 0x339 /* ..8 IMMU RAM entry read reg 0 */ #define SPR_MD_RAM1 0x33a /* ..8 IMMU RAM entry read reg 1 */ #define SPR_UMMCR2 0x3a0 /* .6. User Monitor Mode Control Register 2 */ #define SPR_UMMCR0 0x3a8 /* .6. User Monitor Mode Control Register 0 */ #define SPR_USIA 0x3ab /* .6. User Sampled Instruction Address */ #define SPR_UMMCR1 0x3ac /* .6. User Monitor Mode Control Register 1 */ #define SPR_ZPR 0x3b0 /* 4.. Zone Protection Register */ #define SPR_MMCR2 0x3b0 /* .6. Monitor Mode Control Register 2 */ #define SPR_MMCR2_THRESHMULT_32 0x80000000 /* Multiply MMCR0 threshold by 32 */ #define SPR_MMCR2_THRESHMULT_2 0x00000000 /* Multiply MMCR0 threshold by 2 */ #define SPR_PID 0x3b1 /* 4.. Process ID */ #define SPR_PMC5 0x3b1 /* .6. Performance Counter Register 5 */ #define SPR_PMC6 0x3b2 /* .6. Performance Counter Register 6 */ #define SPR_CCR0 0x3b3 /* 4.. Core Configuration Register 0 */ #define SPR_IAC3 0x3b4 /* 4.. Instruction Address Compare 3 */ #define SPR_IAC4 0x3b5 /* 4.. Instruction Address Compare 4 */ #define SPR_DVC1 0x3b6 /* 4.. Data Value Compare 1 */ #define SPR_DVC2 0x3b7 /* 4.. Data Value Compare 2 */ #define SPR_MMCR0 0x3b8 /* .6. Monitor Mode Control Register 0 */ #define SPR_MMCR0_FC 0x80000000 /* Freeze counters */ #define SPR_MMCR0_FCS 0x40000000 /* Freeze counters in supervisor mode */ #define SPR_MMCR0_FCP 0x20000000 /* Freeze counters in user mode */ #define SPR_MMCR0_FCM1 0x10000000 /* Freeze counters when mark=1 */ #define SPR_MMCR0_FCM0 0x08000000 /* Freeze counters when mark=0 */ #define SPR_MMCR0_PMXE 0x04000000 /* Enable PM interrupt */ #define SPR_MMCR0_FCECE 0x02000000 /* Freeze counters after event */ #define SPR_MMCR0_TBSEL_15 0x01800000 /* Count bit 15 of TBL */ #define SPR_MMCR0_TBSEL_19 0x01000000 /* Count bit 19 of TBL */ #define SPR_MMCR0_TBSEL_23 0x00800000 /* Count bit 23 of TBL */ #define SPR_MMCR0_TBSEL_31 0x00000000 /* Count bit 31 of TBL */ #define SPR_MMCR0_TBEE 0x00400000 /* Time-base event enable */ #define SPR_MMCRO_THRESHOLD(x) ((x) << 16) /* Threshold value */ #define SPR_MMCR0_PMC1CE 0x00008000 /* PMC1 condition enable */ #define SPR_MMCR0_PMCNCE 0x00004000 /* PMCn condition enable */ #define SPR_MMCR0_TRIGGER 0x00002000 /* Trigger */ #define SPR_MMCR0_PMC1SEL(x) (((x) & 0x3f) << 6) /* PMC1 selector */ #define SPR_MMCR0_PMC2SEL(x) (((x) & 0x3f) << 0) /* PMC2 selector */ #define SPR_SGR 0x3b9 /* 4.. Storage Guarded Register */ #define SPR_PMC1 0x3b9 /* .6. Performance Counter Register 1 */ #define SPR_DCWR 0x3ba /* 4.. Data Cache Write-through Register */ #define SPR_PMC2 0x3ba /* .6. Performance Counter Register 2 */ #define SPR_SLER 0x3bb /* 4.. Storage Little Endian Register */ #define SPR_SIA 0x3bb /* .6. Sampled Instruction Address */ #define SPR_MMCR1 0x3bc /* .6. Monitor Mode Control Register 2 */ #define SPR_MMCR1_PMC3SEL(x) (((x) & 0x1f) << 27) /* PMC 3 selector */ #define SPR_MMCR1_PMC4SEL(x) (((x) & 0x1f) << 22) /* PMC 4 selector */ #define SPR_MMCR1_PMC5SEL(x) (((x) & 0x1f) << 17) /* PMC 5 selector */ #define SPR_MMCR1_PMC6SEL(x) (((x) & 0x3f) << 11) /* PMC 6 selector */ #define SPR_SU0R 0x3bc /* 4.. Storage User-defined 0 Register */ #define SPR_PMC3 0x3bd /* .6. Performance Counter Register 3 */ #define SPR_PMC4 0x3be /* .6. Performance Counter Register 4 */ #define SPR_DMISS 0x3d0 /* .68 Data TLB Miss Address Register */ #define SPR_DCMP 0x3d1 /* .68 Data TLB Compare Register */ #define SPR_HASH1 0x3d2 /* .68 Primary Hash Address Register */ #define SPR_ICDBDR 0x3d3 /* 4.. Instruction Cache Debug Data Register */ #define SPR_HASH2 0x3d3 /* .68 Secondary Hash Address Register */ #define SPR_IMISS 0x3d4 /* .68 Instruction TLB Miss Address Register */ #define SPR_TLBMISS 0x3d4 /* .6. TLB Miss Address Register */ #define SPR_DEAR 0x3d5 /* 4.. Data Error Address Register */ #define SPR_ICMP 0x3d5 /* .68 Instruction TLB Compare Register */ #define SPR_PTEHI 0x3d5 /* .6. Instruction TLB Compare Register */ #define SPR_EVPR 0x3d6 /* 4.. Exception Vector Prefix Register */ #define SPR_RPA 0x3d6 /* .68 Required Physical Address Register */ #define SPR_PTELO 0x3d6 /* .6. Required Physical Address Register */ #define SPR_TSR 0x150 /* ..8 Timer Status Register */ #define SPR_TCR 0x154 /* ..8 Timer Control Register */ #define TSR_ENW 0x80000000 /* Enable Next Watchdog */ #define TSR_WIS 0x40000000 /* Watchdog Interrupt Status */ #define TSR_WRS_MASK 0x30000000 /* Watchdog Reset Status */ #define TSR_WRS_NONE 0x00000000 /* No watchdog reset has occurred */ #define TSR_WRS_CORE 0x10000000 /* Core reset was forced by the watchdog */ #define TSR_WRS_CHIP 0x20000000 /* Chip reset was forced by the watchdog */ #define TSR_WRS_SYSTEM 0x30000000 /* System reset was forced by the watchdog */ #define TSR_PIS 0x08000000 /* PIT Interrupt Status */ #define TSR_DIS 0x08000000 /* Decrementer Interrupt Status */ #define TSR_FIS 0x04000000 /* FIT Interrupt Status */ #define TCR_WP_MASK 0xc0000000 /* Watchdog Period mask */ #define TCR_WP_2_17 0x00000000 /* 2**17 clocks */ #define TCR_WP_2_21 0x40000000 /* 2**21 clocks */ #define TCR_WP_2_25 0x80000000 /* 2**25 clocks */ #define TCR_WP_2_29 0xc0000000 /* 2**29 clocks */ #define TCR_WRC_MASK 0x30000000 /* Watchdog Reset Control mask */ #define TCR_WRC_NONE 0x00000000 /* No watchdog reset */ #define TCR_WRC_CORE 0x10000000 /* Core reset */ #define TCR_WRC_CHIP 0x20000000 /* Chip reset */ #define TCR_WRC_SYSTEM 0x30000000 /* System reset */ #define TCR_WIE 0x08000000 /* Watchdog Interrupt Enable */ #define TCR_PIE 0x04000000 /* PIT Interrupt Enable */ #define TCR_DIE 0x04000000 /* Pecrementer Interrupt Enable */ #define TCR_FP_MASK 0x03000000 /* FIT Period */ #define TCR_FP_2_9 0x00000000 /* 2**9 clocks */ #define TCR_FP_2_13 0x01000000 /* 2**13 clocks */ #define TCR_FP_2_17 0x02000000 /* 2**17 clocks */ #define TCR_FP_2_21 0x03000000 /* 2**21 clocks */ #define TCR_FIE 0x00800000 /* FIT Interrupt Enable */ #define TCR_ARE 0x00400000 /* Auto Reload Enable */ #define SPR_PIT 0x3db /* 4.. Programmable Interval Timer */ #define SPR_SRR2 0x3de /* 4.. Save/Restore Register 2 */ #define SPR_SRR3 0x3df /* 4.. Save/Restore Register 3 */ #define SPR_HID0 0x3f0 /* ..8 Hardware Implementation Register 0 */ #define SPR_HID1 0x3f1 /* ..8 Hardware Implementation Register 1 */ #define SPR_HID2 0x3f3 /* ..8 Hardware Implementation Register 2 */ #define SPR_HID4 0x3f4 /* ..8 Hardware Implementation Register 4 */ #define SPR_HID5 0x3f6 /* ..8 Hardware Implementation Register 5 */ #define SPR_HID6 0x3f9 /* ..8 Hardware Implementation Register 6 */ #define SPR_CELL_TSRL 0x380 /* ... Cell BE Thread Status Register */ #define SPR_CELL_TSCR 0x399 /* ... Cell BE Thread Switch Register */ #if defined(AIM) #define SPR_DBSR 0x3f0 /* 4.. Debug Status Register */ #define DBSR_IC 0x80000000 /* Instruction completion debug event */ #define DBSR_BT 0x40000000 /* Branch Taken debug event */ #define DBSR_EDE 0x20000000 /* Exception debug event */ #define DBSR_TIE 0x10000000 /* Trap Instruction debug event */ #define DBSR_UDE 0x08000000 /* Unconditional debug event */ #define DBSR_IA1 0x04000000 /* IAC1 debug event */ #define DBSR_IA2 0x02000000 /* IAC2 debug event */ #define DBSR_DR1 0x01000000 /* DAC1 Read debug event */ #define DBSR_DW1 0x00800000 /* DAC1 Write debug event */ #define DBSR_DR2 0x00400000 /* DAC2 Read debug event */ #define DBSR_DW2 0x00200000 /* DAC2 Write debug event */ #define DBSR_IDE 0x00100000 /* Imprecise debug event */ #define DBSR_IA3 0x00080000 /* IAC3 debug event */ #define DBSR_IA4 0x00040000 /* IAC4 debug event */ #define DBSR_MRR 0x00000300 /* Most recent reset */ #define SPR_DBCR0 0x3f2 /* 4.. Debug Control Register 0 */ #define SPR_DBCR1 0x3bd /* 4.. Debug Control Register 1 */ #define SPR_IAC1 0x3f4 /* 4.. Instruction Address Compare 1 */ #define SPR_IAC2 0x3f5 /* 4.. Instruction Address Compare 2 */ #define SPR_DAC1 0x3f6 /* 4.. Data Address Compare 1 */ #define SPR_DAC2 0x3f7 /* 4.. Data Address Compare 2 */ #define SPR_PIR 0x3ff /* .6. Processor Identification Register */ #elif defined(BOOKE) #define SPR_PIR 0x11e /* ..8 Processor Identification Register */ #define SPR_DBSR 0x130 /* ..8 Debug Status Register */ #define DBSR_IDE 0x80000000 /* Imprecise debug event. */ #define DBSR_UDE 0x40000000 /* Unconditional debug event. */ #define DBSR_MRR 0x30000000 /* Most recent Reset (mask). */ #define DBSR_ICMP 0x08000000 /* Instr. complete debug event. */ #define DBSR_BRT 0x04000000 /* Branch taken debug event. */ #define DBSR_IRPT 0x02000000 /* Interrupt taken debug event. */ #define DBSR_TRAP 0x01000000 /* Trap instr. debug event. */ #define DBSR_IAC1 0x00800000 /* Instr. address compare #1. */ #define DBSR_IAC2 0x00400000 /* Instr. address compare #2. */ #define DBSR_IAC3 0x00200000 /* Instr. address compare #3. */ #define DBSR_IAC4 0x00100000 /* Instr. address compare #4. */ #define DBSR_DAC1R 0x00080000 /* Data addr. read compare #1. */ #define DBSR_DAC1W 0x00040000 /* Data addr. write compare #1. */ #define DBSR_DAC2R 0x00020000 /* Data addr. read compare #2. */ #define DBSR_DAC2W 0x00010000 /* Data addr. write compare #2. */ #define DBSR_RET 0x00008000 /* Return debug event. */ #define SPR_DBCR0 0x134 /* ..8 Debug Control Register 0 */ #define SPR_DBCR1 0x135 /* ..8 Debug Control Register 1 */ #define SPR_IAC1 0x138 /* ..8 Instruction Address Compare 1 */ #define SPR_IAC2 0x139 /* ..8 Instruction Address Compare 2 */ #define SPR_DAC1 0x13c /* ..8 Data Address Compare 1 */ #define SPR_DAC2 0x13d /* ..8 Data Address Compare 2 */ #endif #define DBCR0_EDM 0x80000000 /* External Debug Mode */ #define DBCR0_IDM 0x40000000 /* Internal Debug Mode */ #define DBCR0_RST_MASK 0x30000000 /* ReSeT */ #define DBCR0_RST_NONE 0x00000000 /* No action */ #define DBCR0_RST_CORE 0x10000000 /* Core reset */ #define DBCR0_RST_CHIP 0x20000000 /* Chip reset */ #define DBCR0_RST_SYSTEM 0x30000000 /* System reset */ #define DBCR0_IC 0x08000000 /* Instruction Completion debug event */ #define DBCR0_BT 0x04000000 /* Branch Taken debug event */ #define DBCR0_EDE 0x02000000 /* Exception Debug Event */ #define DBCR0_TDE 0x01000000 /* Trap Debug Event */ #define DBCR0_IA1 0x00800000 /* IAC (Instruction Address Compare) 1 debug event */ #define DBCR0_IA2 0x00400000 /* IAC 2 debug event */ #define DBCR0_IA12 0x00200000 /* Instruction Address Range Compare 1-2 */ #define DBCR0_IA12X 0x00100000 /* IA12 eXclusive */ #define DBCR0_IA3 0x00080000 /* IAC 3 debug event */ #define DBCR0_IA4 0x00040000 /* IAC 4 debug event */ #define DBCR0_IA34 0x00020000 /* Instruction Address Range Compare 3-4 */ #define DBCR0_IA34X 0x00010000 /* IA34 eXclusive */ #define DBCR0_IA12T 0x00008000 /* Instruction Address Range Compare 1-2 range Toggle */ #define DBCR0_IA34T 0x00004000 /* Instruction Address Range Compare 3-4 range Toggle */ #define DBCR0_FT 0x00000001 /* Freeze Timers on debug event */ #define SPR_IABR 0x3f2 /* ..8 Instruction Address Breakpoint Register 0 */ #define SPR_DABR 0x3f5 /* .6. Data Address Breakpoint Register */ #define SPR_MSSCR0 0x3f6 /* .6. Memory SubSystem Control Register */ #define MSSCR0_SHDEN 0x80000000 /* 0: Shared-state enable */ #define MSSCR0_SHDPEN3 0x40000000 /* 1: ~SHD[01] signal enable in MEI mode */ #define MSSCR0_L1INTVEN 0x38000000 /* 2-4: L1 data cache ~HIT intervention enable */ #define MSSCR0_L2INTVEN 0x07000000 /* 5-7: L2 data cache ~HIT intervention enable*/ #define MSSCR0_DL1HWF 0x00800000 /* 8: L1 data cache hardware flush */ #define MSSCR0_MBO 0x00400000 /* 9: must be one */ #define MSSCR0_EMODE 0x00200000 /* 10: MPX bus mode (read-only) */ #define MSSCR0_ABD 0x00100000 /* 11: address bus driven (read-only) */ #define MSSCR0_MBZ 0x000fffff /* 12-31: must be zero */ #define MSSCR0_L2PFE 0x00000003 /* 30-31: L2 prefetch enable */ #define SPR_MSSSR0 0x3f7 /* .6. Memory Subsystem Status Register (MPC745x) */ #define MSSSR0_L2TAG 0x00040000 /* 13: L2 tag parity error */ #define MSSSR0_L2DAT 0x00020000 /* 14: L2 data parity error */ #define MSSSR0_L3TAG 0x00010000 /* 15: L3 tag parity error */ #define MSSSR0_L3DAT 0x00008000 /* 16: L3 data parity error */ #define MSSSR0_APE 0x00004000 /* 17: Address parity error */ #define MSSSR0_DPE 0x00002000 /* 18: Data parity error */ #define MSSSR0_TEA 0x00001000 /* 19: Bus transfer error acknowledge */ #define SPR_LDSTCR 0x3f8 /* .6. Load/Store Control Register */ #define SPR_L2PM 0x3f8 /* .6. L2 Private Memory Control Register */ #define SPR_L2CR 0x3f9 /* .6. L2 Control Register */ #define L2CR_L2E 0x80000000 /* 0: L2 enable */ #define L2CR_L2PE 0x40000000 /* 1: L2 data parity enable */ #define L2CR_L2SIZ 0x30000000 /* 2-3: L2 size */ #define L2SIZ_2M 0x00000000 #define L2SIZ_256K 0x10000000 #define L2SIZ_512K 0x20000000 #define L2SIZ_1M 0x30000000 #define L2CR_L2CLK 0x0e000000 /* 4-6: L2 clock ratio */ #define L2CLK_DIS 0x00000000 /* disable L2 clock */ #define L2CLK_10 0x02000000 /* core clock / 1 */ #define L2CLK_15 0x04000000 /* / 1.5 */ #define L2CLK_20 0x08000000 /* / 2 */ #define L2CLK_25 0x0a000000 /* / 2.5 */ #define L2CLK_30 0x0c000000 /* / 3 */ #define L2CR_L2RAM 0x01800000 /* 7-8: L2 RAM type */ #define L2RAM_FLOWTHRU_BURST 0x00000000 #define L2RAM_PIPELINE_BURST 0x01000000 #define L2RAM_PIPELINE_LATE 0x01800000 #define L2CR_L2DO 0x00400000 /* 9: L2 data-only. Setting this bit disables instruction caching. */ #define L2CR_L2I 0x00200000 /* 10: L2 global invalidate. */ #define L2CR_L2IO_7450 0x00010000 /* 11: L2 instruction-only (MPC745x). */ #define L2CR_L2CTL 0x00100000 /* 11: L2 RAM control (ZZ enable). Enables automatic operation of the L2ZZ (low-power mode) signal. */ #define L2CR_L2WT 0x00080000 /* 12: L2 write-through. */ #define L2CR_L2TS 0x00040000 /* 13: L2 test support. */ #define L2CR_L2OH 0x00030000 /* 14-15: L2 output hold. */ #define L2CR_L2DO_7450 0x00010000 /* 15: L2 data-only (MPC745x). */ #define L2CR_L2SL 0x00008000 /* 16: L2 DLL slow. */ #define L2CR_L2DF 0x00004000 /* 17: L2 differential clock. */ #define L2CR_L2BYP 0x00002000 /* 18: L2 DLL bypass. */ #define L2CR_L2FA 0x00001000 /* 19: L2 flush assist (for software flush). */ #define L2CR_L2HWF 0x00000800 /* 20: L2 hardware flush. */ #define L2CR_L2IO 0x00000400 /* 21: L2 instruction-only. */ #define L2CR_L2CLKSTP 0x00000200 /* 22: L2 clock stop. */ #define L2CR_L2DRO 0x00000100 /* 23: L2DLL rollover checkstop enable. */ #define L2CR_L2IP 0x00000001 /* 31: L2 global invalidate in */ /* progress (read only). */ #define SPR_L3CR 0x3fa /* .6. L3 Control Register */ #define L3CR_L3E 0x80000000 /* 0: L3 enable */ #define L3CR_L3PE 0x40000000 /* 1: L3 data parity enable */ #define L3CR_L3APE 0x20000000 #define L3CR_L3SIZ 0x10000000 /* 3: L3 size (0=1MB, 1=2MB) */ #define L3CR_L3CLKEN 0x08000000 /* 4: Enables L3_CLK[0:1] */ #define L3CR_L3CLK 0x03800000 #define L3CR_L3IO 0x00400000 #define L3CR_L3CLKEXT 0x00200000 #define L3CR_L3CKSPEXT 0x00100000 #define L3CR_L3OH1 0x00080000 #define L3CR_L3SPO 0x00040000 #define L3CR_L3CKSP 0x00030000 #define L3CR_L3PSP 0x0000e000 #define L3CR_L3REP 0x00001000 #define L3CR_L3HWF 0x00000800 #define L3CR_L3I 0x00000400 /* 21: L3 global invalidate */ #define L3CR_L3RT 0x00000300 #define L3CR_L3NIRCA 0x00000080 #define L3CR_L3DO 0x00000040 #define L3CR_PMEN 0x00000004 #define L3CR_PMSIZ 0x00000003 #define SPR_DCCR 0x3fa /* 4.. Data Cache Cachability Register */ #define SPR_ICCR 0x3fb /* 4.. Instruction Cache Cachability Register */ #define SPR_THRM1 0x3fc /* .6. Thermal Management Register */ #define SPR_THRM2 0x3fd /* .6. Thermal Management Register */ #define SPR_THRM_TIN 0x80000000 /* Thermal interrupt bit (RO) */ #define SPR_THRM_TIV 0x40000000 /* Thermal interrupt valid (RO) */ #define SPR_THRM_THRESHOLD(x) ((x) << 23) /* Thermal sensor threshold */ #define SPR_THRM_TID 0x00000004 /* Thermal interrupt direction */ #define SPR_THRM_TIE 0x00000002 /* Thermal interrupt enable */ #define SPR_THRM_VALID 0x00000001 /* Valid bit */ #define SPR_THRM3 0x3fe /* .6. Thermal Management Register */ #define SPR_THRM_TIMER(x) ((x) << 1) /* Sampling interval timer */ #define SPR_THRM_ENABLE 0x00000001 /* TAU Enable */ #define SPR_FPECR 0x3fe /* .6. Floating-Point Exception Cause Register */ /* Time Base Register declarations */ #define TBR_TBL 0x10c /* 468 Time Base Lower - read */ #define TBR_TBU 0x10d /* 468 Time Base Upper - read */ #define TBR_TBWL 0x11c /* 468 Time Base Lower - supervisor, write */ #define TBR_TBWU 0x11d /* 468 Time Base Upper - supervisor, write */ /* Performance counter declarations */ #define PMC_OVERFLOW 0x80000000 /* Counter has overflowed */ /* The first five countable [non-]events are common to many PMC's */ #define PMCN_NONE 0 /* Count nothing */ #define PMCN_CYCLES 1 /* Processor cycles */ #define PMCN_ICOMP 2 /* Instructions completed */ #define PMCN_TBLTRANS 3 /* TBL bit transitions */ #define PCMN_IDISPATCH 4 /* Instructions dispatched */ /* Similar things for the 970 PMC direct counters */ #define PMC970N_NONE 0x8 /* Count nothing */ #define PMC970N_CYCLES 0xf /* Processor cycles */ #define PMC970N_ICOMP 0x9 /* Instructions completed */ #if defined(AIM) #define SPR_ESR 0x3d4 /* 4.. Exception Syndrome Register */ #define ESR_MCI 0x80000000 /* Machine check - instruction */ #define ESR_PIL 0x08000000 /* Program interrupt - illegal */ #define ESR_PPR 0x04000000 /* Program interrupt - privileged */ #define ESR_PTR 0x02000000 /* Program interrupt - trap */ #define ESR_ST 0x01000000 /* Store operation */ #define ESR_DST 0x00800000 /* Data storage interrupt - store fault */ #define ESR_DIZ 0x00800000 /* Data/instruction storage interrupt - zone fault */ #define ESR_U0F 0x00008000 /* Data storage interrupt - U0 fault */ #elif defined(BOOKE) #define SPR_MCARU 0x239 /* ..8 Machine Check Address register upper bits */ #define SPR_MCSR 0x23c /* ..8 Machine Check Syndrome register */ #define SPR_MCAR 0x23d /* ..8 Machine Check Address register */ #define SPR_ESR 0x003e /* ..8 Exception Syndrome Register */ #define ESR_PIL 0x08000000 /* Program interrupt - illegal */ #define ESR_PPR 0x04000000 /* Program interrupt - privileged */ #define ESR_PTR 0x02000000 /* Program interrupt - trap */ #define ESR_ST 0x00800000 /* Store operation */ #define ESR_DLK 0x00200000 /* Data storage, D cache locking */ #define ESR_ILK 0x00100000 /* Data storage, I cache locking */ #define ESR_BO 0x00020000 /* Data/instruction storage, byte ordering */ #define ESR_SPE 0x00000080 /* SPE exception bit */ #define SPR_CSRR0 0x03a /* ..8 58 Critical SRR0 */ #define SPR_CSRR1 0x03b /* ..8 59 Critical SRR1 */ #define SPR_MCSRR0 0x23a /* ..8 570 Machine check SRR0 */ #define SPR_MCSRR1 0x23b /* ..8 571 Machine check SRR1 */ #define SPR_DSRR0 0x23e /* ..8 574 Debug SRR0 */ #define SPR_DSRR1 0x23f /* ..8 575 Debug SRR1 */ #define SPR_MMUCR 0x3b2 /* 4.. MMU Control Register */ #define MMUCR_SWOA (0x80000000 >> 7) #define MMUCR_U1TE (0x80000000 >> 9) #define MMUCR_U2SWOAE (0x80000000 >> 10) #define MMUCR_DULXE (0x80000000 >> 12) #define MMUCR_IULXE (0x80000000 >> 13) #define MMUCR_STS (0x80000000 >> 15) #define MMUCR_STID_MASK (0xFF000000 >> 24) #define SPR_MMUCSR0 0x3f4 /* ..8 1012 MMU Control and Status Register 0 */ #define MMUCSR0_L2TLB0_FI 0x04 /* TLB0 flash invalidate */ #define MMUCSR0_L2TLB1_FI 0x02 /* TLB1 flash invalidate */ #define SPR_SVR 0x3ff /* ..8 1023 System Version Register */ #define SVR_MPC8533 0x8034 #define SVR_MPC8533E 0x803c #define SVR_MPC8541 0x8072 #define SVR_MPC8541E 0x807a #define SVR_MPC8548 0x8031 #define SVR_MPC8548E 0x8039 #define SVR_MPC8555 0x8071 #define SVR_MPC8555E 0x8079 #define SVR_MPC8572 0x80e0 #define SVR_MPC8572E 0x80e8 #define SVR_P1011 0x80e5 #define SVR_P1011E 0x80ed #define SVR_P1013 0x80e7 #define SVR_P1013E 0x80ef #define SVR_P1020 0x80e4 #define SVR_P1020E 0x80ec #define SVR_P1022 0x80e6 #define SVR_P1022E 0x80ee #define SVR_P2010 0x80e3 #define SVR_P2010E 0x80eb #define SVR_P2020 0x80e2 #define SVR_P2020E 0x80ea #define SVR_P2041 0x8210 #define SVR_P2041E 0x8218 #define SVR_P3041 0x8211 #define SVR_P3041E 0x8219 #define SVR_P4040 0x8200 #define SVR_P4040E 0x8208 #define SVR_P4080 0x8201 #define SVR_P4080E 0x8209 #define SVR_P5010 0x8221 #define SVR_P5010E 0x8229 #define SVR_P5020 0x8220 #define SVR_P5020E 0x8228 #define SVR_P5021 0x8205 #define SVR_P5021E 0x820d #define SVR_P5040 0x8204 #define SVR_P5040E 0x820c #define SVR_VER(svr) (((svr) >> 16) & 0xffff) #define SPR_PID0 0x030 /* ..8 Process ID Register 0 */ #define SPR_PID1 0x279 /* ..8 Process ID Register 1 */ #define SPR_PID2 0x27a /* ..8 Process ID Register 2 */ #define SPR_TLB0CFG 0x2B0 /* ..8 TLB 0 Config Register */ #define SPR_TLB1CFG 0x2B1 /* ..8 TLB 1 Config Register */ #define TLBCFG_ASSOC_MASK 0xff000000 /* Associativity of TLB */ #define TLBCFG_ASSOC_SHIFT 24 #define TLBCFG_NENTRY_MASK 0x00000fff /* Number of entries in TLB */ #define SPR_IVPR 0x03f /* ..8 Interrupt Vector Prefix Register */ #define SPR_IVOR0 0x190 /* ..8 Critical input */ #define SPR_IVOR1 0x191 /* ..8 Machine check */ #define SPR_IVOR2 0x192 #define SPR_IVOR3 0x193 #define SPR_IVOR4 0x194 #define SPR_IVOR5 0x195 #define SPR_IVOR6 0x196 #define SPR_IVOR7 0x197 #define SPR_IVOR8 0x198 #define SPR_IVOR9 0x199 #define SPR_IVOR10 0x19a #define SPR_IVOR11 0x19b #define SPR_IVOR12 0x19c #define SPR_IVOR13 0x19d #define SPR_IVOR14 0x19e #define SPR_IVOR15 0x19f #define SPR_IVOR32 0x210 #define SPR_IVOR33 0x211 #define SPR_IVOR34 0x212 #define SPR_IVOR35 0x213 #define SPR_MAS0 0x270 /* ..8 MMU Assist Register 0 Book-E/e500 */ #define SPR_MAS1 0x271 /* ..8 MMU Assist Register 1 Book-E/e500 */ #define SPR_MAS2 0x272 /* ..8 MMU Assist Register 2 Book-E/e500 */ #define SPR_MAS3 0x273 /* ..8 MMU Assist Register 3 Book-E/e500 */ #define SPR_MAS4 0x274 /* ..8 MMU Assist Register 4 Book-E/e500 */ #define SPR_MAS5 0x275 /* ..8 MMU Assist Register 5 Book-E */ #define SPR_MAS6 0x276 /* ..8 MMU Assist Register 6 Book-E/e500 */ #define SPR_MAS7 0x3B0 /* ..8 MMU Assist Register 7 Book-E/e500 */ #define SPR_MAS8 0x155 /* ..8 MMU Assist Register 8 Book-E/e500 */ #define SPR_L1CFG0 0x203 /* ..8 L1 cache configuration register 0 */ #define SPR_L1CFG1 0x204 /* ..8 L1 cache configuration register 1 */ #define SPR_CCR1 0x378 #define CCR1_L2COBE 0x00000040 #define DCR_L2DCDCRAI 0x0000 /* L2 D-Cache DCR Address Pointer */ #define DCR_L2DCDCRDI 0x0001 /* L2 D-Cache DCR Data Indirect */ #define DCR_L2CR0 0x00 /* L2 Cache Configuration Register 0 */ #define L2CR0_AS 0x30000000 #define SPR_L1CSR0 0x3F2 /* ..8 L1 Cache Control and Status Register 0 */ #define L1CSR0_DCPE 0x00010000 /* Data Cache Parity Enable */ #define L1CSR0_DCLFR 0x00000100 /* Data Cache Lock Bits Flash Reset */ #define L1CSR0_DCFI 0x00000002 /* Data Cache Flash Invalidate */ #define L1CSR0_DCE 0x00000001 /* Data Cache Enable */ #define SPR_L1CSR1 0x3F3 /* ..8 L1 Cache Control and Status Register 1 */ #define L1CSR1_ICPE 0x00010000 /* Instruction Cache Parity Enable */ #define L1CSR1_ICUL 0x00000400 /* Instr Cache Unable to Lock */ #define L1CSR1_ICLFR 0x00000100 /* Instruction Cache Lock Bits Flash Reset */ #define L1CSR1_ICFI 0x00000002 /* Instruction Cache Flash Invalidate */ #define L1CSR1_ICE 0x00000001 /* Instruction Cache Enable */ #define SPR_L2CSR0 0x3F9 /* ..8 L2 Cache Control and Status Register 0 */ #define L2CSR0_L2E 0x80000000 /* L2 Cache Enable */ #define L2CSR0_L2PE 0x40000000 /* L2 Cache Parity Enable */ #define L2CSR0_L2FI 0x00200000 /* L2 Cache Flash Invalidate */ #define L2CSR0_L2LFC 0x00000400 /* L2 Cache Lock Flags Clear */ #define SPR_BUCSR 0x3F5 /* ..8 Branch Unit Control and Status Register */ #define BUCSR_BPEN 0x00000001 /* Branch Prediction Enable */ #define BUCSR_BBFI 0x00000200 /* Branch Buffer Flash Invalidate */ #endif /* BOOKE */ #endif /* !_POWERPC_SPR_H_ */ Index: head/sys/powerpc/include/sr.h =================================================================== --- head/sys/powerpc/include/sr.h (revision 326260) +++ head/sys/powerpc/include/sr.h (revision 326261) @@ -1,62 +1,64 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2002 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_SR_H_ #define _MACHINE_SR_H_ /* * Bit definitions for segment registers. * * PowerPC Microprocessor Family: The Programming Environments for 32-bit * Microprocessors, section 2.3.5 */ #define SR_TYPE 0x80000000 /* Type selector */ #define SR_KS 0x40000000 /* Supervisor-state protection key */ #define SR_KP 0x20000000 /* User-state protection key */ #define SR_N 0x10000000 /* No-execute protection */ #define SR_VSID_MASK 0x00ffffff /* Virtual Segment ID mask */ /* Kernel segment register usage */ #define USER_SR 12 #define KERNEL_SR 13 #define KERNEL2_SR 14 #define KERNEL3_SR 15 #define KERNEL_VSIDBITS 0xfffffUL #define KERNEL_SEGMENT (0xfffff0 + KERNEL_SR) #define KERNEL2_SEGMENT (0xfffff0 + KERNEL2_SR) #define EMPTY_SEGMENT 0xfffff0 #ifdef __powerpc64__ #define USER_ADDR 0xcffffffff0000000UL #else #define USER_ADDR ((uintptr_t)USER_SR << ADDR_SR_SHFT) #endif #define SEGMENT_LENGTH 0x10000000UL #define SEGMENT_INVMASK 0x0fffffffUL #define SEGMENT_MASK ~SEGMENT_INVMASK #endif /* !_MACHINE_SR_H_ */ Index: head/sys/powerpc/include/vdso.h =================================================================== --- head/sys/powerpc/include/vdso.h (revision 326260) +++ head/sys/powerpc/include/vdso.h (revision 326261) @@ -1,41 +1,43 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 2012 Konstantin Belousov . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _POWERPC_VDSO_H #define _POWERPC_VDSO_H #define VDSO_TIMEHANDS_MD \ uint32_t th_res[8]; #ifdef _KERNEL #ifdef COMPAT_FREEBSD32 #define VDSO_TIMEHANDS_MD32 VDSO_TIMEHANDS_MD #endif #endif #endif Index: head/sys/powerpc/include/vm.h =================================================================== --- head/sys/powerpc/include/vm.h (revision 326260) +++ head/sys/powerpc/include/vm.h (revision 326261) @@ -1,43 +1,45 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2009 Alan L. Cox * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_VM_H_ #define _MACHINE_VM_H_ #include /* Memory attributes. */ #define VM_MEMATTR_DEFAULT 0 #define VM_MEMATTR_UNCACHEABLE 0x01 #define VM_MEMATTR_CACHEABLE 0x02 #define VM_MEMATTR_WRITE_COMBINING 0x04 #define VM_MEMATTR_WRITE_BACK 0x08 #define VM_MEMATTR_WRITE_THROUGH 0x10 #define VM_MEMATTR_PREFETCHABLE 0x20 #endif /* !_MACHINE_VM_H_ */ Index: head/sys/powerpc/mambo/mambo.c =================================================================== --- head/sys/powerpc/mambo/mambo.c (revision 326260) +++ head/sys/powerpc/mambo/mambo.c (revision 326261) @@ -1,99 +1,101 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 2008 by Nathan Whitehorn. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include /* * Mambo interface */ static int mambobus_probe(device_t); static int mambobus_attach(device_t); static device_method_t mambobus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mambobus_probe), DEVMETHOD(device_attach, mambobus_attach), /* Bus interface */ DEVMETHOD(bus_add_child, bus_generic_add_child), DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), 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_END }; static driver_t mambobus_driver = { "mambo", mambobus_methods, 0 }; static devclass_t mambobus_devclass; DRIVER_MODULE(mambo, ofwbus, mambobus_driver, mambobus_devclass, 0, 0); static int mambobus_probe(device_t dev) { const char *name = ofw_bus_get_name(dev); if (name && !strcmp(name, "mambo")) { device_set_desc(dev, "Mambo Simulator"); return (0); } return (ENXIO); } static int mambobus_attach(device_t dev) { bus_generic_probe(dev); return (bus_generic_attach(dev)); } Index: head/sys/powerpc/mambo/mambo_console.c =================================================================== --- head/sys/powerpc/mambo/mambo_console.c (revision 326260) +++ head/sys/powerpc/mambo/mambo_console.c (revision 326261) @@ -1,185 +1,187 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2008 by Nathan Whitehorn. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "mambocall.h" #define MAMBOBURSTLEN 128 /* max number of bytes to write in one chunk */ #define MAMBO_CONSOLE_WRITE 0 #define MAMBO_CONSOLE_READ 60 static tsw_outwakeup_t mambotty_outwakeup; static struct ttydevsw mambo_ttydevsw = { .tsw_flags = TF_NOPREFIX, .tsw_outwakeup = mambotty_outwakeup, }; static int polltime; static struct callout mambo_callout; static struct tty *tp = NULL; #if defined(KDB) static int alt_break_state; #endif static void mambo_timeout(void *); static cn_probe_t mambo_cnprobe; static cn_init_t mambo_cninit; static cn_term_t mambo_cnterm; static cn_getc_t mambo_cngetc; static cn_putc_t mambo_cnputc; static cn_grab_t mambo_cngrab; static cn_ungrab_t mambo_cnungrab; CONSOLE_DRIVER(mambo); static void cn_drvinit(void *unused) { if (mambo_consdev.cn_pri != CN_DEAD && mambo_consdev.cn_name[0] != '\0') { if (OF_finddevice("/mambo") == -1) return; tp = tty_alloc(&mambo_ttydevsw, NULL); tty_init_console(tp, 0); tty_makedev(tp, NULL, "%s", "mambocons"); polltime = 1; callout_init(&mambo_callout, 1); callout_reset(&mambo_callout, polltime, mambo_timeout, NULL); } } SYSINIT(cndev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, cn_drvinit, NULL); static void mambotty_outwakeup(struct tty *tp) { int len; u_char buf[MAMBOBURSTLEN]; for (;;) { len = ttydisc_getc(tp, buf, sizeof buf); if (len == 0) break; mambocall(MAMBO_CONSOLE_WRITE, buf, (register_t)len, 1UL); } } static void mambo_timeout(void *v) { int c; tty_lock(tp); while ((c = mambo_cngetc(NULL)) != -1) ttydisc_rint(tp, c, 0); ttydisc_rint_done(tp); tty_unlock(tp); callout_reset(&mambo_callout, polltime, mambo_timeout, NULL); } static void mambo_cnprobe(struct consdev *cp) { if (OF_finddevice("/mambo") == -1) { cp->cn_pri = CN_DEAD; return; } cp->cn_pri = CN_NORMAL; } static void mambo_cninit(struct consdev *cp) { /* XXX: This is the alias, but that should be good enough */ strcpy(cp->cn_name, "mambocons"); } static void mambo_cnterm(struct consdev *cp) { } static void mambo_cngrab(struct consdev *cp) { } static void mambo_cnungrab(struct consdev *cp) { } static int mambo_cngetc(struct consdev *cp) { int ch; ch = mambocall(MAMBO_CONSOLE_READ); if (ch > 0 && ch < 0xff) { #if defined(KDB) kdb_alt_break(ch, &alt_break_state); #endif return (ch); } return (-1); } static void mambo_cnputc(struct consdev *cp, int c) { char cbuf; cbuf = c; mambocall(MAMBO_CONSOLE_WRITE, &cbuf, 1UL, 1UL); } Index: head/sys/powerpc/mambo/mambo_disk.c =================================================================== --- head/sys/powerpc/mambo/mambo_disk.c (revision 326260) +++ head/sys/powerpc/mambo/mambo_disk.c (revision 326261) @@ -1,277 +1,279 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2008 Nathan Whitehorn. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include struct mambodisk_softc { device_t dev; struct mtx sc_mtx; struct disk *disk; struct proc *p; struct bio_queue_head bio_queue; int running; int maxblocks; }; #define MAMBO_DISK_READ 116 #define MAMBO_DISK_WRITE 117 #define MAMBO_DISK_INFO 118 #define MAMBO_INFO_STATUS 1 #define MAMBO_INFO_BLKSZ 2 #define MAMBO_INFO_DEVSZ 3 /* bus entry points */ static void mambodisk_identify(driver_t *driver, device_t parent); static int mambodisk_probe(device_t dev); static int mambodisk_attach(device_t dev); /* disk routines */ static int mambodisk_open(struct disk *dp); static int mambodisk_close(struct disk *dp); static void mambodisk_strategy(struct bio *bp); static void mambodisk_task(void *arg); #define MBODISK_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define MBODISK_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define MBODISK_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ "mambodisk", MTX_DEF) #define MBODISK_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); #define MBODISK_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); #define MBODISK_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); static void mambodisk_identify(driver_t *driver, device_t parent) { int i = 0; for (i = 0; mambocall(MAMBO_DISK_INFO,MAMBO_INFO_DEVSZ,i) > 0; i++) BUS_ADD_CHILD(parent,0,"mambodisk",i); } static int mambodisk_probe(device_t dev) { device_set_desc(dev, "Mambo Simulated Block Device"); return (0); } static int mambodisk_attach(device_t dev) { struct mambodisk_softc *sc; struct disk *d; intmax_t mb; char unit; sc = device_get_softc(dev); sc->dev = dev; MBODISK_LOCK_INIT(sc); d = sc->disk = disk_alloc(); d->d_open = mambodisk_open; d->d_close = mambodisk_close; d->d_strategy = mambodisk_strategy; d->d_name = "mambodisk"; d->d_drv1 = sc; d->d_maxsize = MAXPHYS; /* Maybe ask bridge? */ d->d_sectorsize = 512; sc->maxblocks = mambocall(MAMBO_DISK_INFO,MAMBO_INFO_BLKSZ,d->d_unit) / 512; d->d_unit = device_get_unit(dev); d->d_mediasize = mambocall(MAMBO_DISK_INFO,MAMBO_INFO_DEVSZ,d->d_unit) * 1024ULL; /* Mambo gives size in KB */ mb = d->d_mediasize >> 20; /* 1MiB == 1 << 20 */ unit = 'M'; if (mb >= 10240) { /* 1GiB = 1024 MiB */ unit = 'G'; mb /= 1024; } device_printf(dev, "%ju%cB, %d byte sectors\n", mb, unit, d->d_sectorsize); disk_create(d, DISK_VERSION); bioq_init(&sc->bio_queue); sc->running = 1; kproc_create(&mambodisk_task, sc, &sc->p, 0, 0, "task: mambo hd"); return (0); } static int mambodisk_detach(device_t dev) { struct mambodisk_softc *sc = device_get_softc(dev); /* kill thread */ MBODISK_LOCK(sc); sc->running = 0; wakeup(sc); MBODISK_UNLOCK(sc); /* wait for thread to finish. XXX probably want timeout. -sorbo */ MBODISK_LOCK(sc); while (sc->running != -1) msleep(sc, &sc->sc_mtx, PRIBIO, "detach", 0); MBODISK_UNLOCK(sc); /* kill disk */ disk_destroy(sc->disk); /* XXX destroy anything in queue */ MBODISK_LOCK_DESTROY(sc); return (0); } static int mambodisk_open(struct disk *dp) { return (0); } static int mambodisk_close(struct disk *dp) { return (0); } static void mambodisk_strategy(struct bio *bp) { struct mambodisk_softc *sc; sc = (struct mambodisk_softc *)bp->bio_disk->d_drv1; MBODISK_LOCK(sc); bioq_disksort(&sc->bio_queue, bp); wakeup(sc); MBODISK_UNLOCK(sc); } static void mambodisk_task(void *arg) { struct mambodisk_softc *sc = (struct mambodisk_softc*)arg; struct bio *bp; size_t sz; int result; daddr_t block, end; device_t dev; u_long unit; dev = sc->dev; unit = device_get_unit(dev); while (sc->running) { MBODISK_LOCK(sc); do { bp = bioq_first(&sc->bio_queue); if (bp == NULL) msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0); } while (bp == NULL && sc->running); if (bp) bioq_remove(&sc->bio_queue, bp); MBODISK_UNLOCK(sc); if (!sc->running) break; sz = sc->disk->d_sectorsize; end = bp->bio_pblkno + (bp->bio_bcount / sz); for (block = bp->bio_pblkno; block < end;) { u_long numblocks; char *vaddr = bp->bio_data + (block - bp->bio_pblkno) * sz; numblocks = end - block; if (numblocks > sc->maxblocks) numblocks = sc->maxblocks; if (bp->bio_cmd == BIO_READ) { result = mambocall(MAMBO_DISK_READ, vaddr, (u_long)block, (numblocks << 16) | unit); } else if (bp->bio_cmd == BIO_WRITE) { result = mambocall(MAMBO_DISK_WRITE, vaddr, (u_long)block, (numblocks << 16) | unit); } else { result = 1; } if (result) break; block += numblocks; } if (block < end) { bp->bio_error = EIO; bp->bio_resid = (end - block) * sz; bp->bio_flags |= BIO_ERROR; } biodone(bp); } /* tell parent we're done */ MBODISK_LOCK(sc); sc->running = -1; wakeup(sc); MBODISK_UNLOCK(sc); kproc_exit(0); } static device_method_t mambodisk_methods[] = { DEVMETHOD(device_identify, mambodisk_identify), DEVMETHOD(device_probe, mambodisk_probe), DEVMETHOD(device_attach, mambodisk_attach), DEVMETHOD(device_detach, mambodisk_detach), {0, 0}, }; static driver_t mambodisk_driver = { "mambodisk", mambodisk_methods, sizeof(struct mambodisk_softc), }; static devclass_t mambodisk_devclass; DRIVER_MODULE(mambodisk, mambo, mambodisk_driver, mambodisk_devclass, 0, 0); Index: head/sys/powerpc/mambo/mambocall.h =================================================================== --- head/sys/powerpc/mambo/mambocall.h (revision 326260) +++ head/sys/powerpc/mambo/mambocall.h (revision 326261) @@ -1,33 +1,35 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 2008 by Nathan Whitehorn. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MAMBO_MAMBOCALL_H_ #define _MAMBO_MAMBOCALL_H_ long mambocall(int op, ...); #endif /* _MAMBO_MAMBOCALL_H_ */ Index: head/sys/powerpc/mpc85xx/atpic.c =================================================================== --- head/sys/powerpc/mpc85xx/atpic.c (revision 326260) +++ head/sys/powerpc/mpc85xx/atpic.c (revision 326261) @@ -1,364 +1,366 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2009 Marcel Moolenaar * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" #define ATPIC_MASTER 0 #define ATPIC_SLAVE 1 struct atpic_softc { device_t sc_dev; /* I/O port resources for master & slave. */ struct resource *sc_res[2]; int sc_rid[2]; /* Our "routing" interrupt */ struct resource *sc_ires; void *sc_icookie; int sc_irid; int sc_vector[16]; uint8_t sc_mask[2]; }; static int atpic_isa_attach(device_t); static void atpic_isa_identify(driver_t *, device_t); static int atpic_isa_probe(device_t); static void atpic_config(device_t, u_int, enum intr_trigger, enum intr_polarity); static void atpic_dispatch(device_t, struct trapframe *); static void atpic_enable(device_t, u_int, u_int); static void atpic_eoi(device_t, u_int); static void atpic_ipi(device_t, u_int); static void atpic_mask(device_t, u_int); static void atpic_unmask(device_t, u_int); static void atpic_ofw_translate_code(device_t, u_int irq, int code, enum intr_trigger *trig, enum intr_polarity *pol); static device_method_t atpic_isa_methods[] = { /* Device interface */ DEVMETHOD(device_identify, atpic_isa_identify), DEVMETHOD(device_probe, atpic_isa_probe), DEVMETHOD(device_attach, atpic_isa_attach), /* PIC interface */ DEVMETHOD(pic_config, atpic_config), DEVMETHOD(pic_dispatch, atpic_dispatch), DEVMETHOD(pic_enable, atpic_enable), DEVMETHOD(pic_eoi, atpic_eoi), DEVMETHOD(pic_ipi, atpic_ipi), DEVMETHOD(pic_mask, atpic_mask), DEVMETHOD(pic_unmask, atpic_unmask), DEVMETHOD(pic_translate_code, atpic_ofw_translate_code), { 0, 0 }, }; static driver_t atpic_isa_driver = { "atpic", atpic_isa_methods, sizeof(struct atpic_softc) }; static devclass_t atpic_devclass; DRIVER_MODULE(atpic, isa, atpic_isa_driver, atpic_devclass, 0, 0); static struct isa_pnp_id atpic_ids[] = { { 0x0000d041 /* PNP0000 */, "AT interrupt controller" }, { 0 } }; static __inline uint8_t atpic_read(struct atpic_softc *sc, int icu, int ofs) { uint8_t val; val = bus_read_1(sc->sc_res[icu], ofs); return (val); } static __inline void atpic_write(struct atpic_softc *sc, int icu, int ofs, uint8_t val) { bus_write_1(sc->sc_res[icu], ofs, val); bus_barrier(sc->sc_res[icu], ofs, 2 - ofs, BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE); } static void atpic_intr(void *arg) { atpic_dispatch(arg, NULL); } static void atpic_isa_identify(driver_t *drv, device_t parent) { device_t child; child = BUS_ADD_CHILD(parent, ISA_ORDER_SENSITIVE, drv->name, -1); device_set_driver(child, drv); isa_set_logicalid(child, atpic_ids[0].ip_id); isa_set_vendorid(child, atpic_ids[0].ip_id); bus_set_resource(child, SYS_RES_IOPORT, ATPIC_MASTER, IO_ICU1, 2); bus_set_resource(child, SYS_RES_IOPORT, ATPIC_SLAVE, IO_ICU2, 2); /* ISA interrupts are routed through external interrupt 0. */ bus_set_resource(child, SYS_RES_IRQ, 0, 16, 1); } static int atpic_isa_probe(device_t dev) { int res; res = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids); if (res > 0) return (res); device_set_desc(dev, "PC/AT compatible PIC"); return (res); } static void atpic_init(struct atpic_softc *sc, int icu) { sc->sc_mask[icu] = 0xff - ((icu == ATPIC_MASTER) ? 4 : 0); atpic_write(sc, icu, 0, ICW1_RESET | ICW1_IC4); atpic_write(sc, icu, 1, (icu == ATPIC_SLAVE) ? 8 : 0); atpic_write(sc, icu, 1, (icu == ATPIC_SLAVE) ? 2 : 4); atpic_write(sc, icu, 1, ICW4_8086); atpic_write(sc, icu, 1, sc->sc_mask[icu]); atpic_write(sc, icu, 0, OCW3_SEL | OCW3_RR); } static int atpic_isa_attach(device_t dev) { struct atpic_softc *sc; int error; sc = device_get_softc(dev); sc->sc_dev = dev; error = ENXIO; sc->sc_rid[ATPIC_MASTER] = 0; sc->sc_res[ATPIC_MASTER] = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->sc_rid[ATPIC_MASTER], RF_ACTIVE); if (sc->sc_res[ATPIC_MASTER] == NULL) goto fail; sc->sc_rid[ATPIC_SLAVE] = 1; sc->sc_res[ATPIC_SLAVE] = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->sc_rid[ATPIC_SLAVE], RF_ACTIVE); if (sc->sc_res[ATPIC_SLAVE] == NULL) goto fail; sc->sc_irid = 0; sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid, RF_ACTIVE); if (sc->sc_ires == NULL) goto fail; error = bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_MISC | INTR_MPSAFE, NULL, atpic_intr, dev, &sc->sc_icookie); if (error) goto fail; atpic_init(sc, ATPIC_SLAVE); atpic_init(sc, ATPIC_MASTER); powerpc_register_pic(dev, 0, 16, 0, TRUE); return (0); fail: if (sc->sc_ires != NULL) bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, sc->sc_ires); if (sc->sc_res[ATPIC_SLAVE] != NULL) bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid[ATPIC_SLAVE], sc->sc_res[ATPIC_SLAVE]); if (sc->sc_res[ATPIC_MASTER] != NULL) bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid[ATPIC_MASTER], sc->sc_res[ATPIC_MASTER]); return (error); } /* * PIC interface. */ static void atpic_config(device_t dev, u_int irq, enum intr_trigger trig, enum intr_polarity pol) { } static void atpic_dispatch(device_t dev, struct trapframe *tf) { struct atpic_softc *sc; uint8_t irq; sc = device_get_softc(dev); atpic_write(sc, ATPIC_MASTER, 0, OCW3_SEL | OCW3_P); irq = atpic_read(sc, ATPIC_MASTER, 0); atpic_write(sc, ATPIC_MASTER, 0, OCW3_SEL | OCW3_RR); if ((irq & 0x80) == 0) return; if (irq == 0x82) { atpic_write(sc, ATPIC_SLAVE, 0, OCW3_SEL | OCW3_P); irq = atpic_read(sc, ATPIC_SLAVE, 0) + 8; atpic_write(sc, ATPIC_SLAVE, 0, OCW3_SEL | OCW3_RR); if ((irq & 0x80) == 0) return; } powerpc_dispatch_intr(sc->sc_vector[irq & 0x0f], tf); } static void atpic_enable(device_t dev, u_int irq, u_int vector) { struct atpic_softc *sc; sc = device_get_softc(dev); sc->sc_vector[irq] = vector; atpic_unmask(dev, irq); } static void atpic_eoi(device_t dev, u_int irq) { struct atpic_softc *sc; sc = device_get_softc(dev); if (irq > 7) atpic_write(sc, ATPIC_SLAVE, 0, OCW2_EOI); atpic_write(sc, ATPIC_MASTER, 0, OCW2_EOI); } static void atpic_ipi(device_t dev, u_int cpu) { /* No SMP support. */ } static void atpic_mask(device_t dev, u_int irq) { struct atpic_softc *sc; sc = device_get_softc(dev); if (irq > 7) { sc->sc_mask[ATPIC_SLAVE] |= 1 << (irq - 8); atpic_write(sc, ATPIC_SLAVE, 1, sc->sc_mask[ATPIC_SLAVE]); } else { sc->sc_mask[ATPIC_MASTER] |= 1 << irq; atpic_write(sc, ATPIC_MASTER, 1, sc->sc_mask[ATPIC_MASTER]); } } static void atpic_unmask(device_t dev, u_int irq) { struct atpic_softc *sc; sc = device_get_softc(dev); if (irq > 7) { sc->sc_mask[ATPIC_SLAVE] &= ~(1 << (irq - 8)); atpic_write(sc, ATPIC_SLAVE, 1, sc->sc_mask[ATPIC_SLAVE]); } else { sc->sc_mask[ATPIC_MASTER] &= ~(1 << irq); atpic_write(sc, ATPIC_MASTER, 1, sc->sc_mask[ATPIC_MASTER]); } } static void atpic_ofw_translate_code(device_t dev, u_int irq, int code, enum intr_trigger *trig, enum intr_polarity *pol) { switch (code) { case 0: /* Active L level */ *trig = INTR_TRIGGER_LEVEL; *pol = INTR_POLARITY_LOW; break; case 1: /* Active H level */ *trig = INTR_TRIGGER_LEVEL; *pol = INTR_POLARITY_HIGH; break; case 2: /* H to L edge */ *trig = INTR_TRIGGER_EDGE; *pol = INTR_POLARITY_LOW; break; case 3: /* L to H edge */ *trig = INTR_TRIGGER_EDGE; *pol = INTR_POLARITY_HIGH; break; default: *trig = INTR_TRIGGER_CONFORM; *pol = INTR_POLARITY_CONFORM; } } Index: head/sys/powerpc/mpc85xx/ds1553_bus_fdt.c =================================================================== --- head/sys/powerpc/mpc85xx/ds1553_bus_fdt.c (revision 326260) +++ head/sys/powerpc/mpc85xx/ds1553_bus_fdt.c (revision 326261) @@ -1,127 +1,129 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2006-2008 Semihalf, Grzegorz Bernacki * 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 AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * From: FreeBSD: src/sys/powerpc/mpc85xx/ds1553_bus_lbc.c,v 1.2 2009/06/24 15:48:20 raj */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ds1553_reg.h" #include "clock_if.h" static devclass_t rtc_devclass; static int rtc_attach(device_t dev); static int rtc_probe(device_t dev); static device_method_t rtc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rtc_probe), DEVMETHOD(device_attach, rtc_attach), /* clock interface */ DEVMETHOD(clock_gettime, ds1553_gettime), DEVMETHOD(clock_settime, ds1553_settime), { 0, 0 } }; static driver_t rtc_driver = { "rtc", rtc_methods, sizeof(struct ds1553_softc), }; DRIVER_MODULE(rtc, lbc, rtc_driver, rtc_devclass, 0, 0); static int rtc_probe(device_t dev) { if (!ofw_bus_is_compatible(dev, "dallas,ds1553")) return (ENXIO); device_set_desc(dev, "Dallas Semiconductor DS1553 RTC"); return (BUS_PROBE_DEFAULT); } static int rtc_attach(device_t dev) { struct timespec ts; struct ds1553_softc *sc; int error; sc = device_get_softc(dev); bzero(sc, sizeof(struct ds1553_softc)); mtx_init(&sc->sc_mtx, "rtc_mtx", NULL, MTX_SPIN); sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid, RF_ACTIVE); if (sc->res == NULL) { device_printf(dev, "cannot allocate resources\n"); mtx_destroy(&sc->sc_mtx); return (ENXIO); } sc->sc_bst = rman_get_bustag(sc->res); sc->sc_bsh = rman_get_bushandle(sc->res); if ((error = ds1553_attach(dev)) != 0) { device_printf(dev, "cannot attach time of day clock\n"); bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res); mtx_destroy(&sc->sc_mtx); return (error); } clock_register(dev, 1000000); if (bootverbose) { ds1553_gettime(dev, &ts); device_printf(dev, "current time: %ld.%09ld\n", (long)ts.tv_sec, ts.tv_nsec); } return (0); } Index: head/sys/powerpc/mpc85xx/ds1553_core.c =================================================================== --- head/sys/powerpc/mpc85xx/ds1553_core.c (revision 326260) +++ head/sys/powerpc/mpc85xx/ds1553_core.c (revision 326261) @@ -1,194 +1,196 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2006-2008 Semihalf, Grzegorz Bernacki * 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 AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include static uint8_t ds1553_direct_read(device_t, bus_size_t); static void ds1553_direct_write(device_t, bus_size_t, uint8_t); int ds1553_attach(device_t dev) { struct ds1553_softc *sc; uint8_t sec, flags; sc = device_get_softc(dev); if (mtx_initialized(&sc->sc_mtx) == 0) { device_printf(dev, "%s: mutex not initialized\n", __func__); return (ENXIO); } if (sc->sc_read == NULL) sc->sc_read = ds1553_direct_read; if (sc->sc_write == NULL) sc->sc_write = ds1553_direct_write; sc->year_offset = POSIX_BASE_YEAR; mtx_lock_spin(&sc->sc_mtx); /* Turn RTC on if it was not on */ sec = (*sc->sc_read)(dev, DS1553_OFF_SECONDS); if (sec & DS1553_BIT_OSC) { sec &= ~(DS1553_BIT_OSC); (*sc->sc_write)(dev, DS1553_OFF_SECONDS, sec); } /* Low-battery check */ flags = (*sc->sc_read)(dev, DS1553_OFF_FLAGS); if (flags & DS1553_BIT_BLF) device_printf(dev, "voltage-low detected.\n"); mtx_unlock_spin(&sc->sc_mtx); return (0); } /* * Get time of day and convert it to a struct timespec. * Return 0 on success, an error number otherwise. */ int ds1553_gettime(device_t dev, struct timespec *ts) { struct clocktime ct; struct ds1553_softc *sc; uint8_t control; sc = device_get_softc(dev); mtx_lock_spin(&sc->sc_mtx); control = (*sc->sc_read)(dev, DS1553_OFF_CONTROL) | DS1553_BIT_READ; (*sc->sc_write)(dev, DS1553_OFF_CONTROL, control); ct.nsec = 0; ct.sec = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_SECONDS) & DS1553_MASK_SECONDS); ct.min = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_MINUTES) & DS1553_MASK_MINUTES); ct.hour = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_HOURS) & DS1553_MASK_HOUR); ct.dow = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_DAYOFWEEK) & DS1553_MASK_DAYOFWEEK) - 1; ct.day = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_DATE) & DS1553_MASK_DATE); ct.mon = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_MONTH) & DS1553_MASK_MONTH); ct.year = FROMBCD((*sc->sc_read)(dev, DS1553_OFF_YEAR)); control &= ~DS1553_BIT_READ; (*sc->sc_write)(dev, DS1553_OFF_CONTROL, control); ct.year += sc->year_offset; mtx_unlock_spin(&sc->sc_mtx); return (clock_ct_to_ts(&ct, ts)); } /* * Set the time of day clock based on the value of the struct timespec arg. * Return 0 on success, an error number otherwise. */ int ds1553_settime(device_t dev, struct timespec *ts) { struct clocktime ct; struct ds1553_softc *sc; uint8_t control; sc = device_get_softc(dev); bzero(&ct, sizeof(struct clocktime)); /* Accuracy is only one second. */ if (ts->tv_nsec >= 500000000) ts->tv_sec++; ts->tv_nsec = 0; clock_ts_to_ct(ts, &ct); ct.year -= sc->year_offset; mtx_lock_spin(&sc->sc_mtx); /* Halt updates to external registers */ control = (*sc->sc_read)(dev, DS1553_OFF_CONTROL) | DS1553_BIT_WRITE; (*sc->sc_write)(dev, DS1553_OFF_CONTROL, control); (*sc->sc_write)(dev, DS1553_OFF_SECONDS, TOBCD(ct.sec) & DS1553_MASK_SECONDS); (*sc->sc_write)(dev, DS1553_OFF_MINUTES, TOBCD(ct.min) & DS1553_MASK_MINUTES); (*sc->sc_write)(dev, DS1553_OFF_HOURS, TOBCD(ct.hour) & DS1553_MASK_HOUR); (*sc->sc_write)(dev, DS1553_OFF_DAYOFWEEK, TOBCD(ct.dow + 1) & DS1553_MASK_DAYOFWEEK); (*sc->sc_write)(dev, DS1553_OFF_DATE, TOBCD(ct.day) & DS1553_MASK_DATE); (*sc->sc_write)(dev, DS1553_OFF_MONTH, TOBCD(ct.mon) & DS1553_MASK_MONTH); (*sc->sc_write)(dev, DS1553_OFF_YEAR, TOBCD(ct.year)); /* Resume updates to external registers */ control &= ~DS1553_BIT_WRITE; (*sc->sc_write)(dev, DS1553_OFF_CONTROL, control); mtx_unlock_spin(&sc->sc_mtx); return (0); } static uint8_t ds1553_direct_read(device_t dev, bus_size_t off) { struct ds1553_softc *sc; sc = device_get_softc(dev); return (bus_space_read_1(sc->sc_bst, sc->sc_bsh, off)); } static void ds1553_direct_write(device_t dev, bus_size_t off, uint8_t val) { struct ds1553_softc *sc; sc = device_get_softc(dev); bus_space_write_1(sc->sc_bst, sc->sc_bsh, off, val); } Index: head/sys/powerpc/mpc85xx/ds1553_reg.h =================================================================== --- head/sys/powerpc/mpc85xx/ds1553_reg.h (revision 326260) +++ head/sys/powerpc/mpc85xx/ds1553_reg.h (revision 326261) @@ -1,107 +1,109 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2006-2008 Semihalf, Grzegorz Bernacki * 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 AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _DEV_RTC_DS1553_H_ #define _DEV_RTC_DS1553_H_ /* DS1553 registers */ #define DS1553_NVRAM_SIZE 0x1ff0 #define DS1553_OFF_FLAGS 0x1ff0 #define DS1553_OFF_ALARM_SECONDS 0x1ff2 #define DS1553_OFF_ALARM_MINUTES 0x1ff3 #define DS1553_OFF_ALARM_HOURS 0x1ff4 #define DS1553_OFF_ALARM_DATE 0x1ff5 #define DS1553_OFF_INTERRUPTS 0x1ff6 #define DS1553_OFF_WATCHDOG 0x1ff7 #define DS1553_OFF_CONTROL 0x1ff8 #define DS1553_OFF_SECONDS 0x1ff9 #define DS1553_OFF_MINUTES 0x1ffa #define DS1553_OFF_HOURS 0x1ffb #define DS1553_OFF_DAYOFWEEK 0x1ffc #define DS1553_OFF_DATE 0x1ffd #define DS1553_OFF_MONTH 0x1ffe #define DS1553_OFF_YEAR 0x1fff /* dayofweek register's bits */ #define DS1553_BIT_FREQ_TEST 0x40 /* frequency test bit */ /* seconds register's bit */ #define DS1553_BIT_OSC 0x80 /* oscillator start/stop bit */ /* control register's bits */ #define DS1553_BIT_WRITE 0x80 /* write */ #define DS1553_BIT_READ 0x40 /* read */ /* watchdog register's bits */ #define DS1553_BIT_WATCHDOG 0x80 /* watchdog steering bit */ #define DS1553_BIT_BMB4 0x40 /* watchdog multiplier bit4 */ #define DS1553_BIT_BMB3 0x20 /* watchdog multiplier bit3 */ #define DS1553_BIT_BMB2 0x10 /* watchdog multiplier bit2 */ #define DS1553_BIT_BMB1 0x8 /* watchdog multiplier bit1 */ #define DS1553_BIT_BMB0 0x4 /* watchdog multiplier bit0 */ #define DS1553_BIT_RB1 0x2 /* watchdog resolution bit1 */ #define DS1553_BIT_RB0 0x1 /* watchdog resolution bit0 */ /* alarm seconds/minutes/hours/date register's bit */ #define DS1553_BIT_AM 0x80 /* alarm mask bit */ /* flag register's bits */ #define DS1553_BIT_BLF 0x10 /* battery flag */ #define DS1553_BIT_WF 0x80 /* watchdog flag */ /* register's mask */ #define DS1553_MASK_MONTH 0x1f #define DS1553_MASK_DATE 0x3f #define DS1553_MASK_DAYOFWEEK 0x7 #define DS1553_MASK_HOUR 0x3f #define DS1553_MASK_MINUTES 0x7f #define DS1553_MASK_SECONDS 0x7f struct ds1553_softc { bus_space_tag_t sc_bst; /* bus space tag */ bus_space_handle_t sc_bsh; /* bus space handle */ int rid; /* resource id */ struct resource *res; struct mtx sc_mtx; /* hardware mutex */ uint32_t year_offset; /* read/write functions */ uint8_t (*sc_read)(device_t, bus_size_t); void (*sc_write)(device_t, bus_size_t, uint8_t); }; /* device interface */ int ds1553_attach(device_t); /* clock interface */ int ds1553_gettime(device_t, struct timespec *); int ds1553_settime(device_t, struct timespec *); #endif /* _DEV_RTC_DS1553_H_ */ Index: head/sys/powerpc/mpc85xx/i2c.c =================================================================== --- head/sys/powerpc/mpc85xx/i2c.c (revision 326260) +++ head/sys/powerpc/mpc85xx/i2c.c (revision 326261) @@ -1,437 +1,439 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2008-2009 Semihalf, Michal Hajduk * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" #include #include #define I2C_ADDR_REG 0x00 /* I2C slave address register */ #define I2C_FDR_REG 0x04 /* I2C frequency divider register */ #define I2C_CONTROL_REG 0x08 /* I2C control register */ #define I2C_STATUS_REG 0x0C /* I2C status register */ #define I2C_DATA_REG 0x10 /* I2C data register */ #define I2C_DFSRR_REG 0x14 /* I2C Digital Filter Sampling rate */ #define I2C_ENABLE 0x80 /* Module enable - interrupt disable */ #define I2CSR_RXAK 0x01 /* Received acknowledge */ #define I2CSR_MCF (1<<7) /* Data transfer */ #define I2CSR_MASS (1<<6) /* Addressed as a slave */ #define I2CSR_MBB (1<<5) /* Bus busy */ #define I2CSR_MAL (1<<4) /* Arbitration lost */ #define I2CSR_SRW (1<<2) /* Slave read/write */ #define I2CSR_MIF (1<<1) /* Module interrupt */ #define I2CCR_MEN (1<<7) /* Module enable */ #define I2CCR_MSTA (1<<5) /* Master/slave mode */ #define I2CCR_MTX (1<<4) /* Transmit/receive mode */ #define I2CCR_TXAK (1<<3) /* Transfer acknowledge */ #define I2CCR_RSTA (1<<2) /* Repeated START */ #define I2C_BAUD_RATE_FAST 0x31 #define I2C_BAUD_RATE_DEF 0x3F #define I2C_DFSSR_DIV 0x10 #ifdef DEBUG #define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0) #else #define debugf(fmt, args...) #endif struct i2c_softc { device_t dev; device_t iicbus; struct resource *res; struct mtx mutex; int rid; bus_space_handle_t bsh; bus_space_tag_t bst; }; static int i2c_probe(device_t); static int i2c_attach(device_t); static int i2c_repeated_start(device_t dev, u_char slave, int timeout); static int i2c_start(device_t dev, u_char slave, int timeout); static int i2c_stop(device_t dev); static int i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr); static int i2c_read(device_t dev, char *buf, int len, int *read, int last, int delay); static int i2c_write(device_t dev, const char *buf, int len, int *sent, int timeout); static phandle_t i2c_get_node(device_t bus, device_t dev); static device_method_t i2c_methods[] = { DEVMETHOD(device_probe, i2c_probe), DEVMETHOD(device_attach, i2c_attach), DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_repeated_start, i2c_repeated_start), DEVMETHOD(iicbus_start, i2c_start), DEVMETHOD(iicbus_stop, i2c_stop), DEVMETHOD(iicbus_reset, i2c_reset), DEVMETHOD(iicbus_read, i2c_read), DEVMETHOD(iicbus_write, i2c_write), DEVMETHOD(iicbus_transfer, iicbus_transfer_gen), DEVMETHOD(ofw_bus_get_node, i2c_get_node), { 0, 0 } }; static driver_t i2c_driver = { "iichb", i2c_methods, sizeof(struct i2c_softc), }; static devclass_t i2c_devclass; DRIVER_MODULE(i2c, simplebus, i2c_driver, i2c_devclass, 0, 0); DRIVER_MODULE(iicbus, i2c, iicbus_driver, iicbus_devclass, 0, 0); static __inline void i2c_write_reg(struct i2c_softc *sc, bus_size_t off, uint8_t val) { bus_space_write_1(sc->bst, sc->bsh, off, val); } static __inline uint8_t i2c_read_reg(struct i2c_softc *sc, bus_size_t off) { return (bus_space_read_1(sc->bst, sc->bsh, off)); } static __inline void i2c_flag_set(struct i2c_softc *sc, bus_size_t off, uint8_t mask) { uint8_t status; status = i2c_read_reg(sc, off); status |= mask; i2c_write_reg(sc, off, status); } static int i2c_do_wait(device_t dev, struct i2c_softc *sc, int write, int start) { int err; uint8_t status; status = i2c_read_reg(sc, I2C_STATUS_REG); if (status & I2CSR_MIF) { if (write && start && (status & I2CSR_RXAK)) { debugf("no ack %s", start ? "after sending slave address" : ""); err = IIC_ENOACK; goto error; } if (status & I2CSR_MAL) { debugf("arbitration lost"); err = IIC_EBUSERR; goto error; } if (!write && !(status & I2CSR_MCF)) { debugf("transfer unfinished"); err = IIC_EBUSERR; goto error; } } return (IIC_NOERR); error: i2c_write_reg(sc, I2C_STATUS_REG, 0x0); i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN | I2CCR_TXAK); return (err); } static int i2c_probe(device_t dev) { struct i2c_softc *sc; if (!ofw_bus_is_compatible(dev, "fsl-i2c")) return (ENXIO); sc = device_get_softc(dev); sc->rid = 0; sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid, RF_ACTIVE); if (sc->res == NULL) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->res); sc->bsh = rman_get_bushandle(sc->res); /* Enable I2C */ i2c_write_reg(sc, I2C_CONTROL_REG, I2C_ENABLE); bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res); device_set_desc(dev, "I2C bus controller"); return (BUS_PROBE_DEFAULT); } static int i2c_attach(device_t dev) { struct i2c_softc *sc; sc = device_get_softc(dev); sc->dev = dev; sc->rid = 0; mtx_init(&sc->mutex, device_get_nameunit(dev), "I2C", MTX_DEF); sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid, RF_ACTIVE); if (sc->res == NULL) { device_printf(dev, "could not allocate resources"); mtx_destroy(&sc->mutex); return (ENXIO); } sc->bst = rman_get_bustag(sc->res); sc->bsh = rman_get_bushandle(sc->res); sc->iicbus = device_add_child(dev, "iicbus", -1); if (sc->iicbus == NULL) { device_printf(dev, "could not add iicbus child"); mtx_destroy(&sc->mutex); return (ENXIO); } bus_generic_attach(dev); return (IIC_NOERR); } static int i2c_repeated_start(device_t dev, u_char slave, int timeout) { struct i2c_softc *sc; int error; sc = device_get_softc(dev); mtx_lock(&sc->mutex); /* Set repeated start condition */ i2c_flag_set(sc, I2C_CONTROL_REG ,I2CCR_RSTA); /* Write target address - LSB is R/W bit */ i2c_write_reg(sc, I2C_DATA_REG, slave); DELAY(1250); error = i2c_do_wait(dev, sc, 1, 1); mtx_unlock(&sc->mutex); if (error) return (error); return (IIC_NOERR); } static int i2c_start(device_t dev, u_char slave, int timeout) { struct i2c_softc *sc; uint8_t status; int error; sc = device_get_softc(dev); DELAY(1000); mtx_lock(&sc->mutex); status = i2c_read_reg(sc, I2C_STATUS_REG); /* Check if bus is idle or busy */ if (status & I2CSR_MBB) { debugf("bus busy"); mtx_unlock(&sc->mutex); i2c_stop(dev); return (IIC_EBUSERR); } /* Set start condition */ i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN | I2CCR_MSTA | I2CCR_MTX); /* Write target address - LSB is R/W bit */ i2c_write_reg(sc, I2C_DATA_REG, slave); DELAY(1250); error = i2c_do_wait(dev, sc, 1, 1); mtx_unlock(&sc->mutex); if (error) return (error); return (IIC_NOERR); } static int i2c_stop(device_t dev) { struct i2c_softc *sc; sc = device_get_softc(dev); mtx_lock(&sc->mutex); i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN | I2CCR_TXAK); DELAY(1000); mtx_unlock(&sc->mutex); return (IIC_NOERR); } static int i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldadr) { struct i2c_softc *sc; uint8_t baud_rate; sc = device_get_softc(dev); switch (speed) { case IIC_FAST: baud_rate = I2C_BAUD_RATE_FAST; break; case IIC_SLOW: case IIC_UNKNOWN: case IIC_FASTEST: default: baud_rate = I2C_BAUD_RATE_DEF; break; } mtx_lock(&sc->mutex); i2c_write_reg(sc, I2C_CONTROL_REG, 0x0); i2c_write_reg(sc, I2C_STATUS_REG, 0x0); DELAY(1000); i2c_write_reg(sc, I2C_FDR_REG, baud_rate); i2c_write_reg(sc, I2C_DFSRR_REG, I2C_DFSSR_DIV); i2c_write_reg(sc, I2C_CONTROL_REG, I2C_ENABLE); DELAY(1000); mtx_unlock(&sc->mutex); return (IIC_NOERR); } static int i2c_read(device_t dev, char *buf, int len, int *read, int last, int delay) { struct i2c_softc *sc; int error; sc = device_get_softc(dev); *read = 0; mtx_lock(&sc->mutex); if (len) { if (len == 1) i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN | I2CCR_MSTA | I2CCR_TXAK); else i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN | I2CCR_MSTA); /* dummy read */ i2c_read_reg(sc, I2C_DATA_REG); DELAY(1000); } while (*read < len) { DELAY(1000); error = i2c_do_wait(dev, sc, 0, 0); if (error) { mtx_unlock(&sc->mutex); return (error); } if ((*read == len - 2) && last) { i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN | I2CCR_MSTA | I2CCR_TXAK); } if ((*read == len - 1) && last) { i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN | I2CCR_TXAK); } *buf++ = i2c_read_reg(sc, I2C_DATA_REG); (*read)++; DELAY(1250); } mtx_unlock(&sc->mutex); return (IIC_NOERR); } static int i2c_write(device_t dev, const char *buf, int len, int *sent, int timeout) { struct i2c_softc *sc; int error; sc = device_get_softc(dev); *sent = 0; mtx_lock(&sc->mutex); while (*sent < len) { i2c_write_reg(sc, I2C_DATA_REG, *buf++); DELAY(1250); error = i2c_do_wait(dev, sc, 1, 0); if (error) { mtx_unlock(&sc->mutex); return (error); } (*sent)++; } mtx_unlock(&sc->mutex); return (IIC_NOERR); } static phandle_t i2c_get_node(device_t bus, device_t dev) { /* Share controller node with iibus device. */ return (ofw_bus_get_node(bus)); } Index: head/sys/powerpc/mpc85xx/isa.c =================================================================== --- head/sys/powerpc/mpc85xx/isa.c (revision 326260) +++ head/sys/powerpc/mpc85xx/isa.c (revision 326261) @@ -1,84 +1,86 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2009 Marcel Moolenaar * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include void isa_init(device_t dev) { } struct resource * isa_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct isa_device* idev = DEVTOISA(child); struct resource_list *rl = &idev->id_resources; int isdefault, passthrough, rids; isdefault = RMAN_IS_DEFAULT_RANGE(start, end) ? 1 : 0; passthrough = (device_get_parent(child) != bus) ? 1 : 0; if (!passthrough && !isdefault && resource_list_find(rl, type, *rid) == NULL) { switch (type) { case SYS_RES_IOPORT: rids = ISA_PNP_NPORT; break; case SYS_RES_IRQ: rids = ISA_PNP_NIRQ; break; case SYS_RES_MEMORY: rids = ISA_PNP_NMEM; break; default: rids = 0; break; } if (*rid < 0 || *rid >= rids) return (NULL); resource_list_add(rl, type, *rid, start, end, count); } return (resource_list_alloc(rl, bus, child, type, rid, start, end, count, flags)); } int isa_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { struct isa_device* idev = DEVTOISA(child); struct resource_list *rl = &idev->id_resources; return (resource_list_release(rl, bus, child, type, rid, r)); } Index: head/sys/powerpc/mpc85xx/lbc.c =================================================================== --- head/sys/powerpc/mpc85xx/lbc.c (revision 326260) +++ head/sys/powerpc/mpc85xx/lbc.c (revision 326261) @@ -1,859 +1,861 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright (c) 2006-2008, Juniper Networks, Inc. * Copyright (c) 2008 Semihalf, Rafal Czubak * Copyright (c) 2009 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Semihalf * under sponsorship from the FreeBSD Foundation. * * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ofw_bus_if.h" #include "lbc.h" #ifdef DEBUG #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ printf(fmt,##args); } while (0) #else #define debugf(fmt, args...) #endif static MALLOC_DEFINE(M_LBC, "localbus", "localbus devices information"); static int lbc_probe(device_t); static int lbc_attach(device_t); static int lbc_shutdown(device_t); static int lbc_activate_resource(device_t bus __unused, device_t child __unused, int type, int rid __unused, struct resource *r); static int lbc_deactivate_resource(device_t bus __unused, device_t child __unused, int type __unused, int rid __unused, struct resource *r); static struct resource *lbc_alloc_resource(device_t, device_t, int, int *, rman_res_t, rman_res_t, rman_res_t, u_int); static int lbc_print_child(device_t, device_t); static int lbc_release_resource(device_t, device_t, int, int, struct resource *); static const struct ofw_bus_devinfo *lbc_get_devinfo(device_t, device_t); /* * Bus interface definition */ static device_method_t lbc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, lbc_probe), DEVMETHOD(device_attach, lbc_attach), DEVMETHOD(device_shutdown, lbc_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, lbc_print_child), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, NULL), DEVMETHOD(bus_alloc_resource, lbc_alloc_resource), DEVMETHOD(bus_release_resource, lbc_release_resource), DEVMETHOD(bus_activate_resource, lbc_activate_resource), DEVMETHOD(bus_deactivate_resource, lbc_deactivate_resource), /* OFW bus interface */ DEVMETHOD(ofw_bus_get_devinfo, lbc_get_devinfo), 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), { 0, 0 } }; static driver_t lbc_driver = { "lbc", lbc_methods, sizeof(struct lbc_softc) }; devclass_t lbc_devclass; EARLY_DRIVER_MODULE(lbc, ofwbus, lbc_driver, lbc_devclass, 0, 0, BUS_PASS_BUS); /* * Calculate address mask used by OR(n) registers. Use memory region size to * determine mask value. The size must be a power of two and within the range * of 32KB - 4GB. Otherwise error code is returned. Value representing * 4GB size can be passed as 0xffffffff. */ static uint32_t lbc_address_mask(uint32_t size) { int n = 15; if (size == ~0) return (0); while (n < 32) { if (size == (1U << n)) break; n++; } if (n == 32) return (EINVAL); return (0xffff8000 << (n - 15)); } static void lbc_banks_unmap(struct lbc_softc *sc) { int r; r = 0; while (r < LBC_DEV_MAX) { if (sc->sc_range[r].size == 0) return; pmap_unmapdev(sc->sc_range[r].kva, sc->sc_range[r].size); law_disable(OCP85XX_TGTIF_LBC, sc->sc_range[r].addr, sc->sc_range[r].size); r++; } } static int lbc_banks_map(struct lbc_softc *sc) { vm_paddr_t end, start; vm_size_t size; u_int i, r, ranges, s; int error; bzero(sc->sc_range, sizeof(sc->sc_range)); /* * Determine number of discontiguous address ranges to program. */ ranges = 0; for (i = 0; i < LBC_DEV_MAX; i++) { size = sc->sc_banks[i].size; if (size == 0) continue; start = sc->sc_banks[i].addr; for (r = 0; r < ranges; r++) { /* Avoid wrap-around bugs. */ end = sc->sc_range[r].addr - 1 + sc->sc_range[r].size; if (start > 0 && end == start - 1) { sc->sc_range[r].size += size; break; } /* Avoid wrap-around bugs. */ end = start - 1 + size; if (sc->sc_range[r].addr > 0 && end == sc->sc_range[r].addr - 1) { sc->sc_range[r].addr = start; sc->sc_range[r].size += size; break; } } if (r == ranges) { /* New range; add using insertion sort */ r = 0; while (r < ranges && sc->sc_range[r].addr < start) r++; for (s = ranges; s > r; s--) sc->sc_range[s] = sc->sc_range[s-1]; sc->sc_range[r].addr = start; sc->sc_range[r].size = size; ranges++; } } /* * Ranges are sorted so quickly go over the list to merge ranges * that grew toward each other while building the ranges. */ r = 0; while (r < ranges - 1) { end = sc->sc_range[r].addr + sc->sc_range[r].size; if (end != sc->sc_range[r+1].addr) { r++; continue; } sc->sc_range[r].size += sc->sc_range[r+1].size; for (s = r + 1; s < ranges - 1; s++) sc->sc_range[s] = sc->sc_range[s+1]; bzero(&sc->sc_range[s], sizeof(sc->sc_range[s])); ranges--; } /* * Configure LAW for the LBC ranges and map the physical memory * range into KVA. */ for (r = 0; r < ranges; r++) { start = sc->sc_range[r].addr; size = sc->sc_range[r].size; error = law_enable(OCP85XX_TGTIF_LBC, start, size); if (error) return (error); sc->sc_range[r].kva = (vm_offset_t)pmap_mapdev(start, size); } /* XXX: need something better here? */ if (ranges == 0) return (EINVAL); /* Assign KVA to banks based on the enclosing range. */ for (i = 0; i < LBC_DEV_MAX; i++) { size = sc->sc_banks[i].size; if (size == 0) continue; start = sc->sc_banks[i].addr; for (r = 0; r < ranges; r++) { end = sc->sc_range[r].addr - 1 + sc->sc_range[r].size; if (start >= sc->sc_range[r].addr && start - 1 + size <= end) break; } if (r < ranges) { sc->sc_banks[i].kva = sc->sc_range[r].kva + (start - sc->sc_range[r].addr); } } return (0); } static int lbc_banks_enable(struct lbc_softc *sc) { uint32_t size; uint32_t regval; int error, i; for (i = 0; i < LBC_DEV_MAX; i++) { size = sc->sc_banks[i].size; if (size == 0) continue; /* * Compute and program BR value. */ regval = sc->sc_banks[i].addr; switch (sc->sc_banks[i].width) { case 8: regval |= (1 << 11); break; case 16: regval |= (2 << 11); break; case 32: regval |= (3 << 11); break; default: error = EINVAL; goto fail; } regval |= (sc->sc_banks[i].decc << 9); regval |= (sc->sc_banks[i].wp << 8); regval |= (sc->sc_banks[i].msel << 5); regval |= (sc->sc_banks[i].atom << 2); regval |= 1; bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_BR(i), regval); /* * Compute and program OR value. */ regval = lbc_address_mask(size); switch (sc->sc_banks[i].msel) { case LBCRES_MSEL_GPCM: /* TODO Add flag support for option registers */ regval |= 0x0ff7; break; case LBCRES_MSEL_FCM: /* TODO Add flag support for options register */ regval |= 0x0796; break; case LBCRES_MSEL_UPMA: case LBCRES_MSEL_UPMB: case LBCRES_MSEL_UPMC: printf("UPM mode not supported yet!"); error = ENOSYS; goto fail; } bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_OR(i), regval); } return (0); fail: lbc_banks_unmap(sc); return (error); } static void fdt_lbc_fixup(phandle_t node, struct lbc_softc *sc, struct lbc_devinfo *di) { pcell_t width; int bank; if (OF_getprop(node, "bank-width", (void *)&width, sizeof(width)) <= 0) return; bank = di->di_bank; if (sc->sc_banks[bank].size == 0) return; /* Express width in bits. */ sc->sc_banks[bank].width = width * 8; } static int fdt_lbc_reg_decode(phandle_t node, struct lbc_softc *sc, struct lbc_devinfo *di) { rman_res_t start, end, count; pcell_t *reg, *regptr; pcell_t addr_cells, size_cells; int tuple_size, tuples; int i, j, rv, bank; if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0) return (ENXIO); tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); tuples = OF_getencprop_alloc(node, "reg", tuple_size, (void **)®); debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells); debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size); if (tuples <= 0) /* No 'reg' property in this node. */ return (0); regptr = reg; for (i = 0; i < tuples; i++) { bank = fdt_data_get((void *)reg, 1); di->di_bank = bank; reg += 1; /* Get address/size. */ start = count = 0; for (j = 0; j < addr_cells; j++) { start <<= 32; start |= reg[j]; } for (j = 0; j < size_cells; j++) { count <<= 32; count |= reg[addr_cells + j - 1]; } reg += addr_cells - 1 + size_cells; /* Calculate address range relative to VA base. */ start = sc->sc_banks[bank].kva + start; end = start + count - 1; debugf("reg addr bank = %d, start = %jx, end = %jx, " "count = %jx\n", bank, start, end, count); /* Use bank (CS) cell as rid. */ resource_list_add(&di->di_res, SYS_RES_MEMORY, bank, start, end, count); } rv = 0; OF_prop_free(regptr); return (rv); } static void lbc_intr(void *arg) { struct lbc_softc *sc = arg; uint32_t ltesr; ltesr = bus_space_read_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LTESR); sc->sc_ltesr = ltesr; bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LTESR, ltesr); wakeup(sc->sc_dev); } static int lbc_probe(device_t dev) { if (!(ofw_bus_is_compatible(dev, "fsl,lbc") || ofw_bus_is_compatible(dev, "fsl,elbc"))) return (ENXIO); device_set_desc(dev, "Freescale Local Bus Controller"); return (BUS_PROBE_DEFAULT); } static int lbc_attach(device_t dev) { struct lbc_softc *sc; struct lbc_devinfo *di; struct rman *rm; uintmax_t offset, size; vm_paddr_t start; device_t cdev; phandle_t node, child; pcell_t *ranges, *rangesptr; int tuple_size, tuples; int par_addr_cells; int bank, error, i, j; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_mrid = 0; sc->sc_mres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_mrid, RF_ACTIVE); if (sc->sc_mres == NULL) return (ENXIO); sc->sc_bst = rman_get_bustag(sc->sc_mres); sc->sc_bsh = rman_get_bushandle(sc->sc_mres); for (bank = 0; bank < LBC_DEV_MAX; bank++) { bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_BR(bank), 0); bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_OR(bank), 0); } /* * Initialize configuration register: * - enable Local Bus * - set data buffer control signal function * - disable parity byte select * - set ECC parity type * - set bus monitor timing and timer prescale */ bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LBCR, 0); /* * Initialize clock ratio register: * - disable PLL bypass mode * - configure LCLK delay cycles for the assertion of LALE * - set system clock divider */ bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LCRR, 0x00030008); bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LTEDR, 0); bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LTESR, ~0); bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LTEIR, 0x64080001); sc->sc_irid = 0; sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid, RF_ACTIVE | RF_SHAREABLE); if (sc->sc_ires != NULL) { error = bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_MISC | INTR_MPSAFE, NULL, lbc_intr, sc, &sc->sc_icookie); if (error) { device_printf(dev, "could not activate interrupt\n"); bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, sc->sc_ires); sc->sc_ires = NULL; } } sc->sc_ltesr = ~0; rangesptr = NULL; rm = &sc->sc_rman; rm->rm_type = RMAN_ARRAY; rm->rm_descr = "Local Bus Space"; error = rman_init(rm); if (error) goto fail; error = rman_manage_region(rm, rm->rm_start, rm->rm_end); if (error) { rman_fini(rm); goto fail; } /* * Process 'ranges' property. */ node = ofw_bus_get_node(dev); if ((fdt_addrsize_cells(node, &sc->sc_addr_cells, &sc->sc_size_cells)) != 0) { error = ENXIO; goto fail; } par_addr_cells = fdt_parent_addr_cells(node); if (par_addr_cells > 2) { device_printf(dev, "unsupported parent #addr-cells\n"); error = ERANGE; goto fail; } tuple_size = sizeof(pcell_t) * (sc->sc_addr_cells + par_addr_cells + sc->sc_size_cells); tuples = OF_getencprop_alloc(node, "ranges", tuple_size, (void **)&ranges); if (tuples < 0) { device_printf(dev, "could not retrieve 'ranges' property\n"); error = ENXIO; goto fail; } rangesptr = ranges; debugf("par addr_cells = %d, addr_cells = %d, size_cells = %d, " "tuple_size = %d, tuples = %d\n", par_addr_cells, sc->sc_addr_cells, sc->sc_size_cells, tuple_size, tuples); start = 0; size = 0; for (i = 0; i < tuples; i++) { /* The first cell is the bank (chip select) number. */ bank = fdt_data_get(ranges, 1); if (bank < 0 || bank > LBC_DEV_MAX) { device_printf(dev, "bank out of range: %d\n", bank); error = ERANGE; goto fail; } ranges += 1; /* * Remaining cells of the child address define offset into * this CS. */ offset = 0; for (j = 0; j < sc->sc_addr_cells - 1; j++) { offset <<= sizeof(pcell_t) * 8; offset |= *ranges; ranges++; } /* Parent bus start address of this bank. */ start = 0; for (j = 0; j < par_addr_cells; j++) { start <<= sizeof(pcell_t) * 8; start |= *ranges; ranges++; } size = fdt_data_get((void *)ranges, sc->sc_size_cells); ranges += sc->sc_size_cells; debugf("bank = %d, start = %jx, size = %jx\n", bank, (uintmax_t)start, size); sc->sc_banks[bank].addr = start + offset; sc->sc_banks[bank].size = size; /* * Attributes for the bank. * * XXX Note there are no DT bindings defined for them at the * moment, so we need to provide some defaults. */ sc->sc_banks[bank].width = 16; sc->sc_banks[bank].msel = LBCRES_MSEL_GPCM; sc->sc_banks[bank].decc = LBCRES_DECC_DISABLED; sc->sc_banks[bank].atom = LBCRES_ATOM_DISABLED; sc->sc_banks[bank].wp = 0; } /* * Initialize mem-mappings for the LBC banks (i.e. chip selects). */ error = lbc_banks_map(sc); if (error) goto fail; /* * Walk the localbus and add direct subordinates as our children. */ for (child = OF_child(node); child != 0; child = OF_peer(child)) { di = malloc(sizeof(*di), M_LBC, M_WAITOK | M_ZERO); if (ofw_bus_gen_setup_devinfo(&di->di_ofw, child) != 0) { free(di, M_LBC); device_printf(dev, "could not set up devinfo\n"); continue; } resource_list_init(&di->di_res); if (fdt_lbc_reg_decode(child, sc, di)) { device_printf(dev, "could not process 'reg' " "property\n"); ofw_bus_gen_destroy_devinfo(&di->di_ofw); free(di, M_LBC); continue; } fdt_lbc_fixup(child, sc, di); /* Add newbus device for this FDT node */ cdev = device_add_child(dev, NULL, -1); if (cdev == NULL) { device_printf(dev, "could not add child: %s\n", di->di_ofw.obd_name); resource_list_free(&di->di_res); ofw_bus_gen_destroy_devinfo(&di->di_ofw); free(di, M_LBC); continue; } debugf("added child name='%s', node=%x\n", di->di_ofw.obd_name, child); device_set_ivars(cdev, di); } /* * Enable the LBC. */ lbc_banks_enable(sc); OF_prop_free(rangesptr); return (bus_generic_attach(dev)); fail: OF_prop_free(rangesptr); bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mrid, sc->sc_mres); return (error); } static int lbc_shutdown(device_t dev) { /* TODO */ return(0); } static struct resource * lbc_alloc_resource(device_t bus, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct lbc_softc *sc; struct lbc_devinfo *di; struct resource_list_entry *rle; struct resource *res; struct rman *rm; int needactivate; /* We only support default allocations. */ if (!RMAN_IS_DEFAULT_RANGE(start, end)) return (NULL); sc = device_get_softc(bus); if (type == SYS_RES_IRQ) return (bus_alloc_resource(bus, type, rid, start, end, count, flags)); /* * Request for the default allocation with a given rid: use resource * list stored in the local device info. */ if ((di = device_get_ivars(child)) == NULL) return (NULL); if (type == SYS_RES_IOPORT) type = SYS_RES_MEMORY; rid = &di->di_bank; rle = resource_list_find(&di->di_res, type, *rid); if (rle == NULL) { device_printf(bus, "no default resources for " "rid = %d, type = %d\n", *rid, type); return (NULL); } start = rle->start; count = rle->count; end = start + count - 1; sc = device_get_softc(bus); needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; rm = &sc->sc_rman; res = rman_reserve_resource(rm, start, end, count, flags, child); if (res == NULL) { device_printf(bus, "failed to reserve resource %#jx - %#jx " "(%#jx)\n", start, end, count); return (NULL); } rman_set_rid(res, *rid); rman_set_bustag(res, &bs_be_tag); rman_set_bushandle(res, rman_get_start(res)); if (needactivate) if (bus_activate_resource(child, type, *rid, res)) { device_printf(child, "resource activation failed\n"); rman_release_resource(res); return (NULL); } return (res); } static int lbc_print_child(device_t dev, device_t child) { struct lbc_devinfo *di; struct resource_list *rl; int rv; di = device_get_ivars(child); rl = &di->di_res; rv = 0; rv += bus_print_child_header(dev, child); rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); rv += bus_print_child_footer(dev, child); return (rv); } static int lbc_release_resource(device_t dev, device_t child, int type, int rid, struct resource *res) { int err; if (rman_get_flags(res) & RF_ACTIVE) { err = bus_deactivate_resource(child, type, rid, res); if (err) return (err); } return (rman_release_resource(res)); } static int lbc_activate_resource(device_t bus __unused, device_t child __unused, int type __unused, int rid __unused, struct resource *r) { /* Child resources were already mapped, just activate. */ return (rman_activate_resource(r)); } static int lbc_deactivate_resource(device_t bus __unused, device_t child __unused, int type __unused, int rid __unused, struct resource *r) { return (rman_deactivate_resource(r)); } static const struct ofw_bus_devinfo * lbc_get_devinfo(device_t bus, device_t child) { struct lbc_devinfo *di; di = device_get_ivars(child); return (&di->di_ofw); } void lbc_write_reg(device_t child, u_int off, uint32_t val) { device_t dev; struct lbc_softc *sc; dev = device_get_parent(child); if (off >= 0x1000) { device_printf(dev, "%s(%s): invalid offset %#x\n", __func__, device_get_nameunit(child), off); return; } sc = device_get_softc(dev); if (off == LBC85XX_LTESR && sc->sc_ltesr != ~0u) { sc->sc_ltesr ^= (val & sc->sc_ltesr); return; } if (off == LBC85XX_LTEATR && (val & 1) == 0) sc->sc_ltesr = ~0u; bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val); } uint32_t lbc_read_reg(device_t child, u_int off) { device_t dev; struct lbc_softc *sc; uint32_t val; dev = device_get_parent(child); if (off >= 0x1000) { device_printf(dev, "%s(%s): invalid offset %#x\n", __func__, device_get_nameunit(child), off); return (~0U); } sc = device_get_softc(dev); if (off == LBC85XX_LTESR && sc->sc_ltesr != ~0U) val = sc->sc_ltesr; else val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, off); return (val); } Index: head/sys/powerpc/mpc85xx/lbc.h =================================================================== --- head/sys/powerpc/mpc85xx/lbc.h (revision 326260) +++ head/sys/powerpc/mpc85xx/lbc.h (revision 326261) @@ -1,138 +1,140 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2006-2008, Juniper Networks, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_LBC_H_ #define _MACHINE_LBC_H_ /* Maximum number of devices on Local Bus */ #define LBC_DEV_MAX 8 /* Local access registers */ #define LBC85XX_BR(n) (0x0 + (8 * n)) /* Base register 0-7 */ #define LBC85XX_OR(n) (0x4 + (8 * n)) /* Options register 0-7 */ #define LBC85XX_MAR 0x068 /* UPM address register */ #define LBC85XX_MAMR 0x070 /* UPMA mode register */ #define LBC85XX_MBMR 0x074 /* UPMB mode register */ #define LBC85XX_MCMR 0x078 /* UPMC mode register */ #define LBC85XX_MRTPR 0x084 /* Memory refresh timer prescaler */ #define LBC85XX_MDR 0x088 /* UPM data register */ #define LBC85XX_LSOR 0x090 /* Special operation initiation */ #define LBC85XX_LURT 0x0a0 /* UPM refresh timer */ #define LBC85XX_LSRT 0x0a4 /* SDRAM refresh timer */ #define LBC85XX_LTESR 0x0b0 /* Transfer error status register */ #define LBC85XX_LTEDR 0x0b4 /* Transfer error disable register */ #define LBC85XX_LTEIR 0x0b8 /* Transfer error interrupt register */ #define LBC85XX_LTEATR 0x0bc /* Transfer error attributes register */ #define LBC85XX_LTEAR 0x0c0 /* Transfer error address register */ #define LBC85XX_LTECCR 0x0c4 /* Transfer error ECC register */ #define LBC85XX_LBCR 0x0d0 /* Configuration register */ #define LBC85XX_LCRR 0x0d4 /* Clock ratio register */ #define LBC85XX_FMR 0x0e0 /* Flash mode register */ #define LBC85XX_FIR 0x0e4 /* Flash instruction register */ #define LBC85XX_FCR 0x0e8 /* Flash command register */ #define LBC85XX_FBAR 0x0ec /* Flash block address register */ #define LBC85XX_FPAR 0x0f0 /* Flash page address register */ #define LBC85XX_FBCR 0x0f4 /* Flash byte count register */ #define LBC85XX_FECC0 0x100 /* Flash ECC block 0 register */ #define LBC85XX_FECC1 0x104 /* Flash ECC block 0 register */ #define LBC85XX_FECC2 0x108 /* Flash ECC block 0 register */ #define LBC85XX_FECC3 0x10c /* Flash ECC block 0 register */ /* LBC machine select */ #define LBCRES_MSEL_GPCM 0 #define LBCRES_MSEL_FCM 1 #define LBCRES_MSEL_UPMA 8 #define LBCRES_MSEL_UPMB 9 #define LBCRES_MSEL_UPMC 10 /* LBC data error checking modes */ #define LBCRES_DECC_DISABLED 0 #define LBCRES_DECC_NORMAL 1 #define LBCRES_DECC_RMW 2 /* LBC atomic operation modes */ #define LBCRES_ATOM_DISABLED 0 #define LBCRES_ATOM_RAWA 1 #define LBCRES_ATOM_WARA 2 struct lbc_memrange { vm_paddr_t addr; vm_size_t size; vm_offset_t kva; }; struct lbc_bank { vm_paddr_t addr; /* physical addr of the bank */ vm_size_t size; /* bank size */ vm_offset_t kva; /* VA of the bank */ /* * XXX the following bank attributes do not have properties specified * in the LBC DTS bindings yet (11.2009), so they are mainly a * placeholder for future extensions. */ int width; /* data bus width */ uint8_t msel; /* machine select */ uint8_t atom; /* atomic op mode */ uint8_t wp; /* write protect */ uint8_t decc; /* data error checking */ }; struct lbc_softc { device_t sc_dev; struct resource *sc_mres; bus_space_handle_t sc_bsh; bus_space_tag_t sc_bst; int sc_mrid; int sc_irid; struct resource *sc_ires; void *sc_icookie; struct rman sc_rman; int sc_addr_cells; int sc_size_cells; struct lbc_memrange sc_range[LBC_DEV_MAX]; struct lbc_bank sc_banks[LBC_DEV_MAX]; uint32_t sc_ltesr; }; struct lbc_devinfo { struct ofw_bus_devinfo di_ofw; struct resource_list di_res; int di_bank; }; uint32_t lbc_read_reg(device_t child, u_int off); void lbc_write_reg(device_t child, u_int off, uint32_t val); #endif /* _MACHINE_LBC_H_ */ Index: head/sys/powerpc/mpc85xx/mpc85xx.c =================================================================== --- head/sys/powerpc/mpc85xx/mpc85xx.c (revision 326260) +++ head/sys/powerpc/mpc85xx/mpc85xx.c (revision 326261) @@ -1,469 +1,471 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2008 Semihalf, Rafal Jaworowski * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * MPC85xx system specific routines */ uint32_t ccsr_read4(uintptr_t addr) { volatile uint32_t *ptr = (void *)addr; return (*ptr); } void ccsr_write4(uintptr_t addr, uint32_t val) { volatile uint32_t *ptr = (void *)addr; *ptr = val; powerpc_iomb(); } int law_getmax(void) { uint32_t ver; int law_max; ver = SVR_VER(mfspr(SPR_SVR)); switch (ver) { case SVR_MPC8555: case SVR_MPC8555E: law_max = 8; break; case SVR_MPC8533: case SVR_MPC8533E: case SVR_MPC8548: case SVR_MPC8548E: law_max = 10; break; case SVR_P5020: case SVR_P5020E: case SVR_P5021: case SVR_P5021E: case SVR_P5040: case SVR_P5040E: law_max = 32; break; default: law_max = 8; } return (law_max); } static inline void law_write(uint32_t n, uint64_t bar, uint32_t sr) { if (mpc85xx_is_qoriq()) { ccsr_write4(OCP85XX_LAWBARH(n), bar >> 32); ccsr_write4(OCP85XX_LAWBARL(n), bar); ccsr_write4(OCP85XX_LAWSR_QORIQ(n), sr); ccsr_read4(OCP85XX_LAWSR_QORIQ(n)); } else { ccsr_write4(OCP85XX_LAWBAR(n), bar >> 12); ccsr_write4(OCP85XX_LAWSR_85XX(n), sr); ccsr_read4(OCP85XX_LAWSR_85XX(n)); } /* * The last write to LAWAR should be followed by a read * of LAWAR before any device try to use any of windows. * What more the read of LAWAR should be followed by isync * instruction. */ isync(); } static inline void law_read(uint32_t n, uint64_t *bar, uint32_t *sr) { if (mpc85xx_is_qoriq()) { *bar = (uint64_t)ccsr_read4(OCP85XX_LAWBARH(n)) << 32 | ccsr_read4(OCP85XX_LAWBARL(n)); *sr = ccsr_read4(OCP85XX_LAWSR_QORIQ(n)); } else { *bar = (uint64_t)ccsr_read4(OCP85XX_LAWBAR(n)) << 12; *sr = ccsr_read4(OCP85XX_LAWSR_85XX(n)); } } static int law_find_free(void) { uint32_t i,sr; uint64_t bar; int law_max; law_max = law_getmax(); /* Find free LAW */ for (i = 0; i < law_max; i++) { law_read(i, &bar, &sr); if ((sr & 0x80000000) == 0) break; } return (i); } #define _LAW_SR(trgt,size) (0x80000000 | (trgt << 20) | \ (flsl(size + (size - 1)) - 2)) int law_enable(int trgt, uint64_t bar, uint32_t size) { uint64_t bar_tmp; uint32_t sr, sr_tmp; int i, law_max; if (size == 0) return (0); law_max = law_getmax(); sr = _LAW_SR(trgt, size); /* Bail if already programmed. */ for (i = 0; i < law_max; i++) { law_read(i, &bar_tmp, &sr_tmp); if (sr == sr_tmp && bar == bar_tmp) return (0); } /* Find an unused access window. */ i = law_find_free(); if (i == law_max) return (ENOSPC); law_write(i, bar, sr); return (0); } int law_disable(int trgt, uint64_t bar, uint32_t size) { uint64_t bar_tmp; uint32_t sr, sr_tmp; int i, law_max; law_max = law_getmax(); sr = _LAW_SR(trgt, size); /* Find and disable requested LAW. */ for (i = 0; i < law_max; i++) { law_read(i, &bar_tmp, &sr_tmp); if (sr == sr_tmp && bar == bar_tmp) { law_write(i, 0, 0); return (0); } } return (ENOENT); } int law_pci_target(struct resource *res, int *trgt_mem, int *trgt_io) { u_long start; uint32_t ver; int trgt, rv; ver = SVR_VER(mfspr(SPR_SVR)); start = rman_get_start(res) & 0xf000; rv = 0; trgt = -1; switch (start) { case 0x0000: case 0x8000: trgt = 0; break; case 0x1000: case 0x9000: trgt = 1; break; case 0x2000: case 0xa000: if (ver == SVR_MPC8548E || ver == SVR_MPC8548) trgt = 3; else trgt = 2; break; case 0x3000: case 0xb000: if (ver == SVR_MPC8548E || ver == SVR_MPC8548) rv = EINVAL; else trgt = 3; break; default: rv = ENXIO; } if (rv == 0) { *trgt_mem = trgt; *trgt_io = trgt; } return (rv); } static void l3cache_inval(void) { /* Flash invalidate the CPC and clear all the locks */ ccsr_write4(OCP85XX_CPC_CSR0, OCP85XX_CPC_CSR0_FI | OCP85XX_CPC_CSR0_LFC); while (ccsr_read4(OCP85XX_CPC_CSR0) & (OCP85XX_CPC_CSR0_FI | OCP85XX_CPC_CSR0_LFC)) ; } static void l3cache_enable(void) { ccsr_write4(OCP85XX_CPC_CSR0, OCP85XX_CPC_CSR0_CE | OCP85XX_CPC_CSR0_PE); /* Read back to sync write */ ccsr_read4(OCP85XX_CPC_CSR0); } void mpc85xx_enable_l3_cache(void) { uint32_t csr, size, ver; /* Enable L3 CoreNet Platform Cache (CPC) */ ver = SVR_VER(mfspr(SPR_SVR)); if (ver == SVR_P2041 || ver == SVR_P2041E || ver == SVR_P3041 || ver == SVR_P3041E || ver == SVR_P5020 || ver == SVR_P5020E) { csr = ccsr_read4(OCP85XX_CPC_CSR0); if ((csr & OCP85XX_CPC_CSR0_CE) == 0) { l3cache_inval(); l3cache_enable(); } csr = ccsr_read4(OCP85XX_CPC_CSR0); if ((boothowto & RB_VERBOSE) != 0 || (csr & OCP85XX_CPC_CSR0_CE) == 0) { size = OCP85XX_CPC_CFG0_SZ_K(ccsr_read4(OCP85XX_CPC_CFG0)); printf("L3 Corenet Platform Cache: %d KB %sabled\n", size, (csr & OCP85XX_CPC_CSR0_CE) == 0 ? "dis" : "en"); } } } int mpc85xx_is_qoriq(void) { uint16_t pvr = mfpvr() >> 16; /* QorIQ register set is only in e500mc and derivative core based SoCs. */ if (pvr == FSL_E500mc || pvr == FSL_E5500 || pvr == FSL_E6500) return (1); return (0); } static void mpc85xx_dataloss_erratum_spr976(void) { uint32_t svr = SVR_VER(mfspr(SPR_SVR)); /* Ignore whether it's the E variant */ svr &= ~0x8; if (svr != SVR_P3041 && svr != SVR_P4040 && svr != SVR_P4080 && svr != SVR_P5020) return; mb(); isync(); mtspr(976, (mfspr(976) & ~0x1f8) | 0x48); isync(); } static vm_offset_t mpc85xx_map_dcsr(void) { phandle_t node; u_long b, s; int err; /* * Try to access the dcsr node directly i.e. through /aliases/. */ if ((node = OF_finddevice("dcsr")) != -1) if (fdt_is_compatible_strict(node, "fsl,dcsr")) goto moveon; /* * Find the node the long way. */ if ((node = OF_finddevice("/")) == -1) return (0); if ((node = ofw_bus_find_compatible(node, "fsl,dcsr")) == 0) return (0); moveon: err = fdt_get_range(node, 0, &b, &s); if (err != 0) return (0); law_enable(OCP85XX_TGTIF_DCSR, b, 0x400000); return pmap_early_io_map(b, 0x400000); } void mpc85xx_fix_errata(vm_offset_t va_ccsr) { uint32_t svr = SVR_VER(mfspr(SPR_SVR)); vm_offset_t va_dcsr; /* Ignore whether it's the E variant */ svr &= ~0x8; if (svr != SVR_P3041 && svr != SVR_P4040 && svr != SVR_P4080 && svr != SVR_P5020) return; if (mfmsr() & PSL_EE) return; /* * dcsr region need to be mapped thus patch can refer to. * Align dcsr right after ccsbar. */ va_dcsr = mpc85xx_map_dcsr(); if (va_dcsr == 0) goto err; /* * As A004510 errata specify, special purpose register 976 * SPR976[56:60] = 6'b001001 must be set. e500mc core reference manual * does not document SPR976 register. */ mpc85xx_dataloss_erratum_spr976(); /* * Specific settings in the CCF and core platform cache (CPC) * are required to reconfigure the CoreNet coherency fabric. * The register settings that should be updated are described * in errata and relay on base address, offset and updated value. * Special conditions must be used to update these registers correctly. */ dataloss_erratum_access(va_dcsr + 0xb0e08, 0xe0201800); dataloss_erratum_access(va_dcsr + 0xb0e18, 0xe0201800); dataloss_erratum_access(va_dcsr + 0xb0e38, 0xe0400000); dataloss_erratum_access(va_dcsr + 0xb0008, 0x00900000); dataloss_erratum_access(va_dcsr + 0xb0e40, 0xe00a0000); switch (svr) { case SVR_P5020: dataloss_erratum_access(va_ccsr + 0x18600, 0xc0000000); break; case SVR_P4040: case SVR_P4080: dataloss_erratum_access(va_ccsr + 0x18600, 0xff000000); break; case SVR_P3041: dataloss_erratum_access(va_ccsr + 0x18600, 0xf0000000); } dataloss_erratum_access(va_ccsr + 0x10f00, 0x415e5000); dataloss_erratum_access(va_ccsr + 0x11f00, 0x415e5000); err: return; } uint32_t mpc85xx_get_platform_clock(void) { phandle_t soc; static uint32_t freq; if (freq != 0) return (freq); soc = OF_finddevice("/soc"); /* freq isn't modified on error. */ OF_getencprop(soc, "bus-frequency", (void *)&freq, sizeof(freq)); return (freq); } uint32_t mpc85xx_get_system_clock(void) { uint32_t freq; freq = mpc85xx_get_platform_clock(); return (freq / 2); } Index: head/sys/powerpc/mpc85xx/mpc85xx.h =================================================================== --- head/sys/powerpc/mpc85xx/mpc85xx.h (revision 326260) +++ head/sys/powerpc/mpc85xx/mpc85xx.h (revision 326261) @@ -1,177 +1,179 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2008 Semihalf, Rafal Jaworowski * Copyright 2006 by Juniper Networks. * 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 AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MPC85XX_H_ #define _MPC85XX_H_ #include /* * Configuration control and status registers */ extern vm_offset_t ccsrbar_va; extern vm_paddr_t ccsrbar_pa; extern vm_size_t ccsrbar_size; #define CCSRBAR_VA ccsrbar_va #define OCP85XX_CCSRBAR (CCSRBAR_VA + 0x0) #define OCP85XX_BPTR (CCSRBAR_VA + 0x20) #define OCP85XX_BSTRH (CCSRBAR_VA + 0x20) #define OCP85XX_BSTRL (CCSRBAR_VA + 0x24) #define OCP85XX_BSTAR (CCSRBAR_VA + 0x28) #define OCP85XX_COREDISR (CCSRBAR_VA + 0xE0094) #define OCP85XX_BRR (CCSRBAR_VA + 0xE00E4) /* * Run Control and Power Management registers */ #define CCSR_CTBENR (CCSRBAR_VA + 0xE2084) #define CCSR_CTBCKSELR (CCSRBAR_VA + 0xE208C) #define CCSR_CTBCHLTCR (CCSRBAR_VA + 0xE2094) /* * DDR Memory controller. */ #define OCP85XX_DDR1_CS0_CONFIG (CCSRBAR_VA + 0x8080) /* * E500 Coherency Module registers */ #define OCP85XX_EEBPCR (CCSRBAR_VA + 0x1010) /* * Local access registers */ /* Write order: OCP_LAWBARH -> OCP_LAWBARL -> OCP_LAWSR */ #define OCP85XX_LAWBARH(n) (CCSRBAR_VA + 0xc00 + 0x10 * (n)) #define OCP85XX_LAWBARL(n) (CCSRBAR_VA + 0xc04 + 0x10 * (n)) #define OCP85XX_LAWSR_QORIQ(n) (CCSRBAR_VA + 0xc08 + 0x10 * (n)) #define OCP85XX_LAWBAR(n) (CCSRBAR_VA + 0xc08 + 0x10 * (n)) #define OCP85XX_LAWSR_85XX(n) (CCSRBAR_VA + 0xc10 + 0x10 * (n)) #define OCP85XX_LAWSR(n) (mpc85xx_is_qoriq() ? OCP85XX_LAWSR_QORIQ(n) : \ OCP85XX_LAWSR_85XX(n)) /* Attribute register */ #define OCP85XX_ENA_MASK 0x80000000 #define OCP85XX_DIS_MASK 0x7fffffff #define OCP85XX_TGTIF_LBC_QORIQ 0x1f #define OCP85XX_TGTIF_RAM_INTL_QORIQ 0x14 #define OCP85XX_TGTIF_RAM1_QORIQ 0x10 #define OCP85XX_TGTIF_RAM2_QORIQ 0x11 #define OCP85XX_TGTIF_BMAN 0x18 #define OCP85XX_TGTIF_DCSR 0x1D #define OCP85XX_TGTIF_QMAN 0x3C #define OCP85XX_TRGT_SHIFT_QORIQ 20 #define OCP85XX_TGTIF_LBC_85XX 0x04 #define OCP85XX_TGTIF_RAM_INTL_85XX 0x0b #define OCP85XX_TGTIF_RIO_85XX 0x0c #define OCP85XX_TGTIF_RAM1_85XX 0x0f #define OCP85XX_TGTIF_RAM2_85XX 0x16 #define OCP85XX_TGTIF_LBC \ (mpc85xx_is_qoriq() ? OCP85XX_TGTIF_LBC_QORIQ : OCP85XX_TGTIF_LBC_85XX) #define OCP85XX_TGTIF_RAM_INTL \ (mpc85xx_is_qoriq() ? OCP85XX_TGTIF_RAM_INTL_QORIQ : OCP85XX_TGTIF_RAM_INTL_85XX) #define OCP85XX_TGTIF_RIO \ (mpc85xx_is_qoriq() ? OCP85XX_TGTIF_RIO_QORIQ : OCP85XX_TGTIF_RIO_85XX) #define OCP85XX_TGTIF_RAM1 \ (mpc85xx_is_qoriq() ? OCP85XX_TGTIF_RAM1_QORIQ : OCP85XX_TGTIF_RAM1_85XX) #define OCP85XX_TGTIF_RAM2 \ (mpc85xx_is_qoriq() ? OCP85XX_TGTIF_RAM2_QORIQ : OCP85XX_TGTIF_RAM2_85XX) /* * L2 cache registers */ #define OCP85XX_L2CTL (CCSRBAR_VA + 0x20000) /* * L3 CoreNet platform cache (CPC) registers */ #define OCP85XX_CPC_CSR0 (CCSRBAR_VA + 0x10000) #define OCP85XX_CPC_CSR0_CE 0x80000000 #define OCP85XX_CPC_CSR0_PE 0x40000000 #define OCP85XX_CPC_CSR0_FI 0x00200000 #define OCP85XX_CPC_CSR0_WT 0x00080000 #define OCP85XX_CPC_CSR0_FL 0x00000800 #define OCP85XX_CPC_CSR0_LFC 0x00000400 #define OCP85XX_CPC_CFG0 (CCSRBAR_VA + 0x10008) #define OCP85XX_CPC_CFG_SZ_MASK 0x00003fff #define OCP85XX_CPC_CFG0_SZ_K(x) (((x) & OCP85XX_CPC_CFG_SZ_MASK) << 6) /* * Power-On Reset configuration */ #define OCP85XX_PORDEVSR (CCSRBAR_VA + 0xe000c) #define OCP85XX_PORDEVSR_IO_SEL 0x00780000 #define OCP85XX_PORDEVSR_IO_SEL_SHIFT 19 #define OCP85XX_PORDEVSR2 (CCSRBAR_VA + 0xe0014) /* * Status Registers. */ #define OCP85XX_RSTCR (CCSRBAR_VA + 0xe00b0) #define OCP85XX_CLKDVDR (CCSRBAR_VA + 0xe0800) #define OCP85XX_CLKDVDR_PXCKEN 0x80000000 #define OCP85XX_CLKDVDR_SSICKEN 0x20000000 #define OCP85XX_CLKDVDR_PXCKINV 0x10000000 #define OCP85XX_CLKDVDR_PXCLK_MASK 0x00FF0000 #define OCP85XX_CLKDVDR_SSICLK_MASK 0x000000FF /* * Run Control/Power Management Registers. */ #define OCP85XX_RCPM_CDOZSR (CCSRBAR_VA + 0xe2004) #define OCP85XX_RCPM_CDOZCR (CCSRBAR_VA + 0xe200c) /* * Prototypes. */ uint32_t ccsr_read4(uintptr_t addr); void ccsr_write4(uintptr_t addr, uint32_t val); int law_enable(int trgt, uint64_t bar, uint32_t size); int law_disable(int trgt, uint64_t bar, uint32_t size); int law_getmax(void); int law_pci_target(struct resource *, int *, int *); DECLARE_CLASS(mpc85xx_platform); int mpc85xx_attach(platform_t); void mpc85xx_enable_l3_cache(void); void mpc85xx_fix_errata(vm_offset_t); void dataloss_erratum_access(vm_offset_t, uint32_t); int mpc85xx_is_qoriq(void); uint32_t mpc85xx_get_platform_clock(void); uint32_t mpc85xx_get_system_clock(void); #endif /* _MPC85XX_H_ */ Index: head/sys/powerpc/mpc85xx/pci_mpc85xx.c =================================================================== --- head/sys/powerpc/mpc85xx/pci_mpc85xx.c (revision 326260) +++ head/sys/powerpc/mpc85xx/pci_mpc85xx.c (revision 326261) @@ -1,828 +1,830 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2006-2007 by Juniper Networks. * Copyright 2008 Semihalf. * Copyright 2010 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Semihalf * under sponsorship from the FreeBSD Foundation. * * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * From: FreeBSD: src/sys/powerpc/mpc85xx/pci_ocp.c,v 1.9 2010/03/23 23:46:28 marcel */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ofw_bus_if.h" #include "pcib_if.h" #include #include #include #include #define REG_CFG_ADDR 0x0000 #define CONFIG_ACCESS_ENABLE 0x80000000 #define REG_CFG_DATA 0x0004 #define REG_INT_ACK 0x0008 #define REG_POTAR(n) (0x0c00 + 0x20 * (n)) #define REG_POTEAR(n) (0x0c04 + 0x20 * (n)) #define REG_POWBAR(n) (0x0c08 + 0x20 * (n)) #define REG_POWAR(n) (0x0c10 + 0x20 * (n)) #define REG_PITAR(n) (0x0e00 - 0x20 * (n)) #define REG_PIWBAR(n) (0x0e08 - 0x20 * (n)) #define REG_PIWBEAR(n) (0x0e0c - 0x20 * (n)) #define REG_PIWAR(n) (0x0e10 - 0x20 * (n)) #define REG_PEX_MES_DR 0x0020 #define REG_PEX_MES_IER 0x0028 #define REG_PEX_ERR_DR 0x0e00 #define REG_PEX_ERR_EN 0x0e08 #define REG_PEX_ERR_DR 0x0e00 #define REG_PEX_ERR_DR_ME 0x80000000 #define REG_PEX_ERR_DR_PCT 0x800000 #define REG_PEX_ERR_DR_PAT 0x400000 #define REG_PEX_ERR_DR_PCAC 0x200000 #define REG_PEX_ERR_DR_PNM 0x100000 #define REG_PEX_ERR_DR_CDNSC 0x80000 #define REG_PEX_ERR_DR_CRSNC 0x40000 #define REG_PEX_ERR_DR_ICCA 0x20000 #define REG_PEX_ERR_DR_IACA 0x10000 #define REG_PEX_ERR_DR_CRST 0x8000 #define REG_PEX_ERR_DR_MIS 0x4000 #define REG_PEX_ERR_DR_IOIS 0x2000 #define REG_PEX_ERR_DR_CIS 0x1000 #define REG_PEX_ERR_DR_CIEP 0x800 #define REG_PEX_ERR_DR_IOIEP 0x400 #define REG_PEX_ERR_DR_OAC 0x200 #define REG_PEX_ERR_DR_IOIA 0x100 #define REG_PEX_ERR_DR_IMBA 0x80 #define REG_PEX_ERR_DR_IIOBA 0x40 #define REG_PEX_ERR_DR_LDDE 0x20 #define REG_PEX_ERR_EN 0x0e08 #define PCIR_LTSSM 0x404 #define LTSSM_STAT_L0 0x16 #define DEVFN(b, s, f) ((b << 16) | (s << 8) | f) struct fsl_pcib_softc { struct ofw_pci_softc pci_sc; device_t sc_dev; int sc_iomem_target; bus_addr_t sc_iomem_start, sc_iomem_end; int sc_ioport_target; bus_addr_t sc_ioport_start, sc_ioport_end; struct resource *sc_res; bus_space_handle_t sc_bsh; bus_space_tag_t sc_bst; int sc_rid; struct resource *sc_irq_res; void *sc_ih; int sc_busnr; int sc_pcie; uint8_t sc_pcie_capreg; /* PCI-E Capability Reg Set */ /* Devices that need special attention. */ int sc_devfn_tundra; int sc_devfn_via_ide; }; struct fsl_pcib_err_dr { const char *msg; uint32_t err_dr_mask; }; static const struct fsl_pcib_err_dr pci_err[] = { {"ME", REG_PEX_ERR_DR_ME}, {"PCT", REG_PEX_ERR_DR_PCT}, {"PAT", REG_PEX_ERR_DR_PAT}, {"PCAC", REG_PEX_ERR_DR_PCAC}, {"PNM", REG_PEX_ERR_DR_PNM}, {"CDNSC", REG_PEX_ERR_DR_CDNSC}, {"CRSNC", REG_PEX_ERR_DR_CRSNC}, {"ICCA", REG_PEX_ERR_DR_ICCA}, {"IACA", REG_PEX_ERR_DR_IACA}, {"CRST", REG_PEX_ERR_DR_CRST}, {"MIS", REG_PEX_ERR_DR_MIS}, {"IOIS", REG_PEX_ERR_DR_IOIS}, {"CIS", REG_PEX_ERR_DR_CIS}, {"CIEP", REG_PEX_ERR_DR_CIEP}, {"IOIEP", REG_PEX_ERR_DR_IOIEP}, {"OAC", REG_PEX_ERR_DR_OAC}, {"IOIA", REG_PEX_ERR_DR_IOIA}, {"IMBA", REG_PEX_ERR_DR_IMBA}, {"IIOBA", REG_PEX_ERR_DR_IIOBA}, {"LDDE", REG_PEX_ERR_DR_LDDE} }; /* Local forward declerations. */ static uint32_t fsl_pcib_cfgread(struct fsl_pcib_softc *, u_int, u_int, u_int, u_int, int); static void fsl_pcib_cfgwrite(struct fsl_pcib_softc *, u_int, u_int, u_int, u_int, uint32_t, int); static int fsl_pcib_decode_win(phandle_t, struct fsl_pcib_softc *); static void fsl_pcib_err_init(device_t); static void fsl_pcib_inbound(struct fsl_pcib_softc *, int, int, uint64_t, uint64_t, uint64_t); static int fsl_pcib_init(struct fsl_pcib_softc *, int, int); static void fsl_pcib_outbound(struct fsl_pcib_softc *, int, int, uint64_t, uint64_t, uint64_t); /* Forward declerations. */ static int fsl_pcib_attach(device_t); static int fsl_pcib_detach(device_t); static int fsl_pcib_probe(device_t); static int fsl_pcib_maxslots(device_t); static uint32_t fsl_pcib_read_config(device_t, u_int, u_int, u_int, u_int, int); static void fsl_pcib_write_config(device_t, u_int, u_int, u_int, u_int, uint32_t, int); /* Configuration r/w mutex. */ struct mtx pcicfg_mtx; static int mtx_initialized = 0; /* * Bus interface definitions. */ static device_method_t fsl_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fsl_pcib_probe), DEVMETHOD(device_attach, fsl_pcib_attach), DEVMETHOD(device_detach, fsl_pcib_detach), /* pcib interface */ DEVMETHOD(pcib_maxslots, fsl_pcib_maxslots), DEVMETHOD(pcib_read_config, fsl_pcib_read_config), DEVMETHOD(pcib_write_config, fsl_pcib_write_config), DEVMETHOD_END }; static devclass_t fsl_pcib_devclass; DEFINE_CLASS_1(pcib, fsl_pcib_driver, fsl_pcib_methods, sizeof(struct fsl_pcib_softc), ofw_pci_driver); DRIVER_MODULE(pcib, ofwbus, fsl_pcib_driver, fsl_pcib_devclass, 0, 0); static int fsl_pcib_err_intr(void *v) { struct fsl_pcib_softc *sc; device_t dev; uint32_t err_reg, clear_reg; uint8_t i; dev = (device_t)v; sc = device_get_softc(dev); clear_reg = 0; err_reg = bus_space_read_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR); /* Check which one error occurred */ for (i = 0; i < sizeof(pci_err)/sizeof(struct fsl_pcib_err_dr); i++) { if (err_reg & pci_err[i].err_dr_mask) { device_printf(dev, "PCI %d: report %s error\n", device_get_unit(dev), pci_err[i].msg); clear_reg |= pci_err[i].err_dr_mask; } } /* Clear pending errors */ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR, clear_reg); return (0); } static int fsl_pcib_probe(device_t dev) { if (ofw_bus_get_type(dev) == NULL || strcmp(ofw_bus_get_type(dev), "pci") != 0) return (ENXIO); if (!(ofw_bus_is_compatible(dev, "fsl,mpc8540-pci") || ofw_bus_is_compatible(dev, "fsl,mpc8540-pcie") || ofw_bus_is_compatible(dev, "fsl,mpc8548-pcie") || ofw_bus_is_compatible(dev, "fsl,p5020-pcie") || ofw_bus_is_compatible(dev, "fsl,qoriq-pcie-v2.2") || ofw_bus_is_compatible(dev, "fsl,qoriq-pcie"))) return (ENXIO); device_set_desc(dev, "Freescale Integrated PCI/PCI-E Controller"); return (BUS_PROBE_DEFAULT); } static int fsl_pcib_attach(device_t dev) { struct fsl_pcib_softc *sc; phandle_t node; uint32_t cfgreg; int error, maxslot, rid; uint8_t ltssm, capptr; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_rid = 0; sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, RF_ACTIVE); if (sc->sc_res == NULL) { device_printf(dev, "could not map I/O memory\n"); return (ENXIO); } sc->sc_bst = rman_get_bustag(sc->sc_res); sc->sc_bsh = rman_get_bushandle(sc->sc_res); sc->sc_busnr = 0; if (!mtx_initialized) { mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); mtx_initialized = 1; } cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_VENDOR, 2); if (cfgreg != 0x1057 && cfgreg != 0x1957) goto err; capptr = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_CAP_PTR, 1); while (capptr != 0) { cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, capptr, 2); switch (cfgreg & 0xff) { case PCIY_PCIX: break; case PCIY_EXPRESS: sc->sc_pcie = 1; sc->sc_pcie_capreg = capptr; break; } capptr = (cfgreg >> 8) & 0xff; } node = ofw_bus_get_node(dev); /* * Initialize generic OF PCI interface (ranges, etc.) */ error = ofw_pci_init(dev); if (error) return (error); /* * Configure decode windows for PCI(E) access. */ if (fsl_pcib_decode_win(node, sc) != 0) goto err; cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_COMMAND, 2); cfgreg |= PCIM_CMD_SERRESPEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | PCIM_CMD_PORTEN; fsl_pcib_cfgwrite(sc, 0, 0, 0, PCIR_COMMAND, cfgreg, 2); sc->sc_devfn_tundra = -1; sc->sc_devfn_via_ide = -1; /* * Scan bus using firmware configured, 0 based bus numbering. */ maxslot = (sc->sc_pcie) ? 0 : PCI_SLOTMAX; fsl_pcib_init(sc, sc->sc_busnr, maxslot); if (sc->sc_pcie) { ltssm = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_LTSSM, 1); if (ltssm < LTSSM_STAT_L0) { if (bootverbose) printf("PCI %d: no PCIE link, skipping\n", device_get_unit(dev)); return (0); } } /* Allocate irq */ rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->sc_irq_res == NULL) { error = fsl_pcib_detach(dev); if (error != 0) { device_printf(dev, "Detach of the driver failed with error %d\n", error); } return (ENXIO); } /* Setup interrupt handler */ error = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, (driver_intr_t *)fsl_pcib_err_intr, dev, &sc->sc_ih); if (error != 0) { device_printf(dev, "Could not setup irq, %d\n", error); sc->sc_ih = NULL; error = fsl_pcib_detach(dev); if (error != 0) { device_printf(dev, "Detach of the driver failed with error %d\n", error); } return (ENXIO); } fsl_pcib_err_init(dev); return (ofw_pci_attach(dev)); err: return (ENXIO); } static uint32_t fsl_pcib_cfgread(struct fsl_pcib_softc *sc, u_int bus, u_int slot, u_int func, u_int reg, int bytes) { uint32_t addr, data; addr = CONFIG_ACCESS_ENABLE; addr |= (bus & 0xff) << 16; addr |= (slot & 0x1f) << 11; addr |= (func & 0x7) << 8; addr |= reg & 0xfc; if (sc->sc_pcie) addr |= (reg & 0xf00) << 16; mtx_lock_spin(&pcicfg_mtx); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_ADDR, addr); switch (bytes) { case 1: data = bus_space_read_1(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA + (reg & 3)); break; case 2: data = le16toh(bus_space_read_2(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA + (reg & 2))); break; case 4: data = le32toh(bus_space_read_4(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA)); break; default: data = ~0; break; } mtx_unlock_spin(&pcicfg_mtx); return (data); } static void fsl_pcib_cfgwrite(struct fsl_pcib_softc *sc, u_int bus, u_int slot, u_int func, u_int reg, uint32_t data, int bytes) { uint32_t addr; addr = CONFIG_ACCESS_ENABLE; addr |= (bus & 0xff) << 16; addr |= (slot & 0x1f) << 11; addr |= (func & 0x7) << 8; addr |= reg & 0xfc; if (sc->sc_pcie) addr |= (reg & 0xf00) << 16; mtx_lock_spin(&pcicfg_mtx); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_ADDR, addr); switch (bytes) { case 1: bus_space_write_1(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA + (reg & 3), data); break; case 2: bus_space_write_2(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA + (reg & 2), htole16(data)); break; case 4: bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA, htole32(data)); break; } mtx_unlock_spin(&pcicfg_mtx); } #if 0 static void dump(struct fsl_pcib_softc *sc) { unsigned int i; #define RD(o) bus_space_read_4(sc->sc_bst, sc->sc_bsh, o) for (i = 0; i < 5; i++) { printf("POTAR%u =0x%08x\n", i, RD(REG_POTAR(i))); printf("POTEAR%u =0x%08x\n", i, RD(REG_POTEAR(i))); printf("POWBAR%u =0x%08x\n", i, RD(REG_POWBAR(i))); printf("POWAR%u =0x%08x\n", i, RD(REG_POWAR(i))); } printf("\n"); for (i = 1; i < 4; i++) { printf("PITAR%u =0x%08x\n", i, RD(REG_PITAR(i))); printf("PIWBAR%u =0x%08x\n", i, RD(REG_PIWBAR(i))); printf("PIWBEAR%u=0x%08x\n", i, RD(REG_PIWBEAR(i))); printf("PIWAR%u =0x%08x\n", i, RD(REG_PIWAR(i))); } printf("\n"); #undef RD for (i = 0; i < 0x48; i += 4) { printf("cfg%02x=0x%08x\n", i, fsl_pcib_cfgread(sc, 0, 0, 0, i, 4)); } } #endif static int fsl_pcib_maxslots(device_t dev) { struct fsl_pcib_softc *sc = device_get_softc(dev); return ((sc->sc_pcie) ? 0 : PCI_SLOTMAX); } static uint32_t fsl_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int bytes) { struct fsl_pcib_softc *sc = device_get_softc(dev); u_int devfn; if (bus == sc->sc_busnr && !sc->sc_pcie && slot < 10) return (~0); devfn = DEVFN(bus, slot, func); if (devfn == sc->sc_devfn_tundra) return (~0); if (devfn == sc->sc_devfn_via_ide && reg == PCIR_INTPIN) return (1); return (fsl_pcib_cfgread(sc, bus, slot, func, reg, bytes)); } static void fsl_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t val, int bytes) { struct fsl_pcib_softc *sc = device_get_softc(dev); if (bus == sc->sc_busnr && !sc->sc_pcie && slot < 10) return; fsl_pcib_cfgwrite(sc, bus, slot, func, reg, val, bytes); } static void fsl_pcib_init_via(struct fsl_pcib_softc *sc, uint16_t device, int bus, int slot, int fn) { if (device == 0x0686) { fsl_pcib_write_config(sc->sc_dev, bus, slot, fn, 0x52, 0x34, 1); fsl_pcib_write_config(sc->sc_dev, bus, slot, fn, 0x77, 0x00, 1); fsl_pcib_write_config(sc->sc_dev, bus, slot, fn, 0x83, 0x98, 1); fsl_pcib_write_config(sc->sc_dev, bus, slot, fn, 0x85, 0x03, 1); } else if (device == 0x0571) { sc->sc_devfn_via_ide = DEVFN(bus, slot, fn); fsl_pcib_write_config(sc->sc_dev, bus, slot, fn, 0x40, 0x0b, 1); } } static int fsl_pcib_init(struct fsl_pcib_softc *sc, int bus, int maxslot) { int secbus; int old_pribus, old_secbus, old_subbus; int new_pribus, new_secbus, new_subbus; int slot, func, maxfunc; uint16_t vendor, device; uint8_t command, hdrtype, subclass; secbus = bus; for (slot = 0; slot <= maxslot; slot++) { maxfunc = 0; for (func = 0; func <= maxfunc; func++) { hdrtype = fsl_pcib_read_config(sc->sc_dev, bus, slot, func, PCIR_HDRTYPE, 1); if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) continue; if (func == 0 && (hdrtype & PCIM_MFDEV)) maxfunc = PCI_FUNCMAX; vendor = fsl_pcib_read_config(sc->sc_dev, bus, slot, func, PCIR_VENDOR, 2); device = fsl_pcib_read_config(sc->sc_dev, bus, slot, func, PCIR_DEVICE, 2); if (vendor == 0x1957 && device == 0x3fff) { sc->sc_devfn_tundra = DEVFN(bus, slot, func); continue; } command = fsl_pcib_read_config(sc->sc_dev, bus, slot, func, PCIR_COMMAND, 1); command &= ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN); fsl_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_COMMAND, command, 1); if (vendor == 0x1106) fsl_pcib_init_via(sc, device, bus, slot, func); /* * Handle PCI-PCI bridges */ subclass = fsl_pcib_read_config(sc->sc_dev, bus, slot, func, PCIR_SUBCLASS, 1); /* Allow all DEVTYPE 1 devices */ if (hdrtype != PCIM_HDRTYPE_BRIDGE) continue; secbus++; /* Read currect bus register configuration */ old_pribus = fsl_pcib_read_config(sc->sc_dev, bus, slot, func, PCIR_PRIBUS_1, 1); old_secbus = fsl_pcib_read_config(sc->sc_dev, bus, slot, func, PCIR_SECBUS_1, 1); old_subbus = fsl_pcib_read_config(sc->sc_dev, bus, slot, func, PCIR_SUBBUS_1, 1); if (bootverbose) printf("PCI: reading firmware bus numbers for " "secbus = %d (bus/sec/sub) = (%d/%d/%d)\n", secbus, old_pribus, old_secbus, old_subbus); new_pribus = bus; new_secbus = secbus; secbus = fsl_pcib_init(sc, secbus, (subclass == PCIS_BRIDGE_PCI) ? PCI_SLOTMAX : 0); new_subbus = secbus; if (bootverbose) printf("PCI: translate firmware bus numbers " "for secbus %d (%d/%d/%d) -> (%d/%d/%d)\n", secbus, old_pribus, old_secbus, old_subbus, new_pribus, new_secbus, new_subbus); fsl_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PRIBUS_1, new_pribus, 1); fsl_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_SECBUS_1, new_secbus, 1); fsl_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_SUBBUS_1, new_subbus, 1); } } return (secbus); } static void fsl_pcib_inbound(struct fsl_pcib_softc *sc, int wnd, int tgt, uint64_t start, uint64_t size, uint64_t pci_start) { uint32_t attr, bar, tar; KASSERT(wnd > 0, ("%s: inbound window 0 is invalid", __func__)); switch (tgt) { /* XXX OCP85XX_TGTIF_RAM2, OCP85XX_TGTIF_RAM_INTL should be handled */ case OCP85XX_TGTIF_RAM1_85XX: case OCP85XX_TGTIF_RAM1_QORIQ: attr = 0xa0f55000 | (ffsl(size) - 2); break; default: attr = 0; break; } tar = start >> 12; bar = pci_start >> 12; bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PITAR(wnd), tar); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWBEAR(wnd), 0); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWBAR(wnd), bar); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWAR(wnd), attr); } static void fsl_pcib_outbound(struct fsl_pcib_softc *sc, int wnd, int res, uint64_t start, uint64_t size, uint64_t pci_start) { uint32_t attr, bar, tar; switch (res) { case SYS_RES_MEMORY: attr = 0x80044000 | (ffsll(size) - 2); break; case SYS_RES_IOPORT: attr = 0x80088000 | (ffsll(size) - 2); break; default: attr = 0x0004401f; break; } bar = start >> 12; tar = pci_start >> 12; bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POTAR(wnd), tar); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POTEAR(wnd), 0); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POWBAR(wnd), bar); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POWAR(wnd), attr); } static void fsl_pcib_err_init(device_t dev) { struct fsl_pcib_softc *sc; uint16_t sec_stat, dsr; uint32_t dcr, err_en; sc = device_get_softc(dev); sec_stat = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_SECSTAT_1, 2); if (sec_stat) fsl_pcib_cfgwrite(sc, 0, 0, 0, PCIR_SECSTAT_1, 0xffff, 2); if (sc->sc_pcie) { /* Clear error bits */ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_MES_IER, 0xffffffff); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_MES_DR, 0xffffffff); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR, 0xffffffff); dsr = fsl_pcib_cfgread(sc, 0, 0, 0, sc->sc_pcie_capreg + PCIER_DEVICE_STA, 2); if (dsr) fsl_pcib_cfgwrite(sc, 0, 0, 0, sc->sc_pcie_capreg + PCIER_DEVICE_STA, 0xffff, 2); /* Enable all errors reporting */ err_en = 0x00bfff00; bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_EN, err_en); /* Enable error reporting: URR, FER, NFER */ dcr = fsl_pcib_cfgread(sc, 0, 0, 0, sc->sc_pcie_capreg + PCIER_DEVICE_CTL, 4); dcr |= PCIEM_CTL_URR_ENABLE | PCIEM_CTL_FER_ENABLE | PCIEM_CTL_NFER_ENABLE; fsl_pcib_cfgwrite(sc, 0, 0, 0, sc->sc_pcie_capreg + PCIER_DEVICE_CTL, dcr, 4); } } static int fsl_pcib_detach(device_t dev) { if (mtx_initialized) { mtx_destroy(&pcicfg_mtx); mtx_initialized = 0; } return (bus_generic_detach(dev)); } static int fsl_pcib_decode_win(phandle_t node, struct fsl_pcib_softc *sc) { device_t dev; int error, i, trgt; dev = sc->sc_dev; fsl_pcib_outbound(sc, 0, -1, 0, 0, 0); /* * Configure LAW decode windows. */ error = law_pci_target(sc->sc_res, &sc->sc_iomem_target, &sc->sc_ioport_target); if (error != 0) { device_printf(dev, "could not retrieve PCI LAW target info\n"); return (error); } for (i = 0; i < sc->pci_sc.sc_nrange; i++) { switch (sc->pci_sc.sc_range[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { case OFW_PCI_PHYS_HI_SPACE_CONFIG: continue; case OFW_PCI_PHYS_HI_SPACE_IO: trgt = sc->sc_ioport_target; fsl_pcib_outbound(sc, 2, SYS_RES_IOPORT, sc->pci_sc.sc_range[i].host, sc->pci_sc.sc_range[i].size, sc->pci_sc.sc_range[i].pci); sc->sc_ioport_start = sc->pci_sc.sc_range[i].pci; sc->sc_ioport_end = sc->pci_sc.sc_range[i].pci + sc->pci_sc.sc_range[i].size - 1; break; case OFW_PCI_PHYS_HI_SPACE_MEM32: case OFW_PCI_PHYS_HI_SPACE_MEM64: trgt = sc->sc_iomem_target; fsl_pcib_outbound(sc, 1, SYS_RES_MEMORY, sc->pci_sc.sc_range[i].host, sc->pci_sc.sc_range[i].size, sc->pci_sc.sc_range[i].pci); sc->sc_iomem_start = sc->pci_sc.sc_range[i].pci; sc->sc_iomem_end = sc->pci_sc.sc_range[i].pci + sc->pci_sc.sc_range[i].size - 1; break; default: panic("Unknown range type %#x\n", sc->pci_sc.sc_range[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK); } error = law_enable(trgt, sc->pci_sc.sc_range[i].host, sc->pci_sc.sc_range[i].size); if (error != 0) { device_printf(dev, "could not program LAW for range " "%d\n", i); return (error); } } /* * Set outbout and inbound windows. */ fsl_pcib_outbound(sc, 3, -1, 0, 0, 0); fsl_pcib_outbound(sc, 4, -1, 0, 0, 0); fsl_pcib_inbound(sc, 1, -1, 0, 0, 0); fsl_pcib_inbound(sc, 2, -1, 0, 0, 0); fsl_pcib_inbound(sc, 3, OCP85XX_TGTIF_RAM1, 0, 2U * 1024U * 1024U * 1024U, 0); return (0); } Index: head/sys/powerpc/mpc85xx/platform_mpc85xx.c =================================================================== --- head/sys/powerpc/mpc85xx/platform_mpc85xx.c (revision 326260) +++ head/sys/powerpc/mpc85xx/platform_mpc85xx.c (revision 326261) @@ -1,564 +1,566 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2008-2012 Semihalf. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "platform_if.h" #ifdef SMP extern void *ap_pcpu; extern vm_paddr_t kernload; /* Kernel physical load address */ extern uint8_t __boot_page[]; /* Boot page body */ extern uint32_t bp_kernload; struct cpu_release { uint32_t entry_h; uint32_t entry_l; uint32_t r3_h; uint32_t r3_l; uint32_t reserved; uint32_t pir; }; #endif extern uint32_t *bootinfo; vm_paddr_t ccsrbar_pa; vm_offset_t ccsrbar_va; vm_size_t ccsrbar_size; static int cpu, maxcpu; static int mpc85xx_probe(platform_t); static void mpc85xx_mem_regions(platform_t, struct mem_region *phys, int *physsz, struct mem_region *avail, int *availsz); static u_long mpc85xx_timebase_freq(platform_t, struct cpuref *cpuref); static int mpc85xx_smp_first_cpu(platform_t, struct cpuref *cpuref); static int mpc85xx_smp_next_cpu(platform_t, struct cpuref *cpuref); static int mpc85xx_smp_get_bsp(platform_t, struct cpuref *cpuref); static int mpc85xx_smp_start_cpu(platform_t, struct pcpu *cpu); static void mpc85xx_smp_timebase_sync(platform_t, u_long tb, int ap); static void mpc85xx_idle(platform_t, int cpu); static int mpc85xx_idle_wakeup(platform_t plat, int cpu); static void mpc85xx_reset(platform_t); static platform_method_t mpc85xx_methods[] = { PLATFORMMETHOD(platform_probe, mpc85xx_probe), PLATFORMMETHOD(platform_attach, mpc85xx_attach), PLATFORMMETHOD(platform_mem_regions, mpc85xx_mem_regions), PLATFORMMETHOD(platform_timebase_freq, mpc85xx_timebase_freq), PLATFORMMETHOD(platform_smp_first_cpu, mpc85xx_smp_first_cpu), PLATFORMMETHOD(platform_smp_next_cpu, mpc85xx_smp_next_cpu), PLATFORMMETHOD(platform_smp_get_bsp, mpc85xx_smp_get_bsp), PLATFORMMETHOD(platform_smp_start_cpu, mpc85xx_smp_start_cpu), PLATFORMMETHOD(platform_smp_timebase_sync, mpc85xx_smp_timebase_sync), PLATFORMMETHOD(platform_reset, mpc85xx_reset), PLATFORMMETHOD(platform_idle, mpc85xx_idle), PLATFORMMETHOD(platform_idle_wakeup, mpc85xx_idle_wakeup), PLATFORMMETHOD_END }; DEFINE_CLASS_0(mpc85xx, mpc85xx_platform, mpc85xx_methods, 0); PLATFORM_DEF(mpc85xx_platform); static int mpc85xx_probe(platform_t plat) { u_int pvr = (mfpvr() >> 16) & 0xFFFF; switch (pvr) { case FSL_E500v1: case FSL_E500v2: case FSL_E500mc: case FSL_E5500: case FSL_E6500: return (BUS_PROBE_DEFAULT); } return (ENXIO); } int mpc85xx_attach(platform_t plat) { phandle_t cpus, child, ccsr; const char *soc_name_guesses[] = {"/soc", "soc", NULL}; const char **name; pcell_t ranges[6], acells, pacells, scells; uint64_t ccsrbar, ccsrsize; int i; if ((cpus = OF_finddevice("/cpus")) != -1) { for (maxcpu = 0, child = OF_child(cpus); child != 0; child = OF_peer(child), maxcpu++) ; } else maxcpu = 1; /* * Locate CCSR region. Irritatingly, there is no way to find it * unless you already know where it is. Try to infer its location * from the device tree. */ ccsr = -1; for (name = soc_name_guesses; *name != NULL && ccsr == -1; name++) ccsr = OF_finddevice(*name); if (ccsr == -1) { char type[64]; /* That didn't work. Search for devices of type "soc" */ child = OF_child(OF_peer(0)); for (OF_child(child); child != 0; child = OF_peer(child)) { if (OF_getprop(child, "device_type", type, sizeof(type)) <= 0) continue; if (strcmp(type, "soc") == 0) { ccsr = child; break; } } } if (ccsr == -1) panic("Could not locate CCSR window!"); OF_getprop(ccsr, "#size-cells", &scells, sizeof(scells)); OF_getprop(ccsr, "#address-cells", &acells, sizeof(acells)); OF_searchprop(OF_parent(ccsr), "#address-cells", &pacells, sizeof(pacells)); OF_getprop(ccsr, "ranges", ranges, sizeof(ranges)); ccsrbar = ccsrsize = 0; for (i = acells; i < acells + pacells; i++) { ccsrbar <<= 32; ccsrbar |= ranges[i]; } for (i = acells + pacells; i < acells + pacells + scells; i++) { ccsrsize <<= 32; ccsrsize |= ranges[i]; } ccsrbar_va = pmap_early_io_map(ccsrbar, ccsrsize); ccsrbar_pa = ccsrbar; ccsrbar_size = ccsrsize; #if 0 mpc85xx_fix_errata(ccsrbar_va); #endif mpc85xx_enable_l3_cache(); return (0); } void mpc85xx_mem_regions(platform_t plat, struct mem_region *phys, int *physsz, struct mem_region *avail, int *availsz) { ofw_mem_regions(phys, physsz, avail, availsz); } static u_long mpc85xx_timebase_freq(platform_t plat, struct cpuref *cpuref) { u_long ticks; phandle_t cpus, child; pcell_t freq; if (bootinfo != NULL) { if (bootinfo[0] == 1) { /* Backward compatibility. See 8-STABLE. */ ticks = bootinfo[3] >> 3; } else { /* Compatibility with Juniper's loader. */ ticks = bootinfo[5] >> 3; } } else ticks = 0; if ((cpus = OF_finddevice("/cpus")) == -1) goto out; if ((child = OF_child(cpus)) == 0) goto out; switch (OF_getproplen(child, "timebase-frequency")) { case 4: { uint32_t tbase; OF_getprop(child, "timebase-frequency", &tbase, sizeof(tbase)); ticks = tbase; return (ticks); } case 8: { uint64_t tbase; OF_getprop(child, "timebase-frequency", &tbase, sizeof(tbase)); ticks = tbase; return (ticks); } default: break; } freq = 0; if (OF_getprop(child, "bus-frequency", (void *)&freq, sizeof(freq)) <= 0) goto out; if (freq == 0) goto out; /* * Time Base and Decrementer are updated every 8 CCB bus clocks. * HID0[SEL_TBCLK] = 0 */ if (mpc85xx_is_qoriq()) ticks = freq / 32; else ticks = freq / 8; out: if (ticks <= 0) panic("Unable to determine timebase frequency!"); return (ticks); } static int mpc85xx_smp_first_cpu(platform_t plat, struct cpuref *cpuref) { cpu = 0; cpuref->cr_cpuid = cpu; cpuref->cr_hwref = cpuref->cr_cpuid; if (bootverbose) printf("powerpc_smp_first_cpu: cpuid %d\n", cpuref->cr_cpuid); cpu++; return (0); } static int mpc85xx_smp_next_cpu(platform_t plat, struct cpuref *cpuref) { if (cpu >= maxcpu) return (ENOENT); cpuref->cr_cpuid = cpu++; cpuref->cr_hwref = cpuref->cr_cpuid; if (bootverbose) printf("powerpc_smp_next_cpu: cpuid %d\n", cpuref->cr_cpuid); return (0); } static int mpc85xx_smp_get_bsp(platform_t plat, struct cpuref *cpuref) { cpuref->cr_cpuid = mfspr(SPR_PIR); cpuref->cr_hwref = cpuref->cr_cpuid; return (0); } #ifdef SMP static int mpc85xx_smp_start_cpu_epapr(platform_t plat, struct pcpu *pc) { vm_paddr_t rel_pa, bptr; volatile struct cpu_release *rel; vm_offset_t rel_va, rel_page; phandle_t node; int i; /* If we're calling this, the node already exists. */ node = OF_finddevice("/cpus"); for (i = 0, node = OF_child(node); i < pc->pc_cpuid; i++, node = OF_peer(node)) ; if (OF_getencprop(node, "cpu-release-addr", (pcell_t *)&rel_pa, sizeof(rel_pa)) == -1) { return (ENOENT); } rel_page = kva_alloc(PAGE_SIZE); if (rel_page == 0) return (ENOMEM); critical_enter(); rel_va = rel_page + (rel_pa & PAGE_MASK); pmap_kenter(rel_page, rel_pa & ~PAGE_MASK); rel = (struct cpu_release *)rel_va; bptr = ((vm_paddr_t)(uintptr_t)__boot_page - KERNBASE) + kernload; cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel)); rel->pir = pc->pc_cpuid; __asm __volatile("sync"); rel->entry_h = (bptr >> 32); rel->entry_l = bptr; __asm __volatile("sync"); cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel)); if (bootverbose) printf("Waking up CPU %d via CPU release page %p\n", pc->pc_cpuid, rel); critical_exit(); pmap_kremove(rel_page); kva_free(rel_page, PAGE_SIZE); return (0); } #endif static int mpc85xx_smp_start_cpu(platform_t plat, struct pcpu *pc) { #ifdef SMP vm_paddr_t bptr; uint32_t reg; int timeout; uintptr_t brr; int cpuid; int epapr_boot = 0; uint32_t tgt; if (mpc85xx_is_qoriq()) { reg = ccsr_read4(OCP85XX_COREDISR); cpuid = pc->pc_cpuid; if ((reg & (1 << cpuid)) != 0) { printf("%s: CPU %d is disabled!\n", __func__, pc->pc_cpuid); return (-1); } brr = OCP85XX_BRR; } else { brr = OCP85XX_EEBPCR; cpuid = pc->pc_cpuid + 24; } bp_kernload = kernload; /* * bp_kernload is in the boot page. Sync the cache because ePAPR * booting has the other core(s) already running. */ cpu_flush_dcache(&bp_kernload, sizeof(bp_kernload)); ap_pcpu = pc; __asm __volatile("msync; isync"); /* First try the ePAPR way. */ if (mpc85xx_smp_start_cpu_epapr(plat, pc) == 0) { epapr_boot = 1; goto spin_wait; } reg = ccsr_read4(brr); if ((reg & (1 << cpuid)) != 0) { printf("SMP: CPU %d already out of hold-off state!\n", pc->pc_cpuid); return (ENXIO); } /* Flush caches to have our changes hit DRAM. */ cpu_flush_dcache(__boot_page, 4096); bptr = ((vm_paddr_t)(uintptr_t)__boot_page - KERNBASE) + kernload; KASSERT((bptr & 0xfff) == 0, ("%s: boot page is not aligned (%#jx)", __func__, (uintmax_t)bptr)); if (mpc85xx_is_qoriq()) { /* * Read DDR controller configuration to select proper BPTR target ID. * * On P5020 bit 29 of DDR1_CS0_CONFIG enables DDR controllers * interleaving. If this bit is set, we have to use * OCP85XX_TGTIF_RAM_INTL as BPTR target ID. On other QorIQ DPAA SoCs, * this bit is reserved and always 0. */ reg = ccsr_read4(OCP85XX_DDR1_CS0_CONFIG); if (reg & (1 << 29)) tgt = OCP85XX_TGTIF_RAM_INTL; else tgt = OCP85XX_TGTIF_RAM1; /* * Set BSTR to the physical address of the boot page */ ccsr_write4(OCP85XX_BSTRH, bptr >> 32); ccsr_write4(OCP85XX_BSTRL, bptr); ccsr_write4(OCP85XX_BSTAR, OCP85XX_ENA_MASK | (tgt << OCP85XX_TRGT_SHIFT_QORIQ) | (ffsl(PAGE_SIZE) - 2)); /* Read back OCP85XX_BSTAR to synchronize write */ ccsr_read4(OCP85XX_BSTAR); /* * Enable and configure time base on new CPU. */ /* Set TB clock source to platform clock / 32 */ reg = ccsr_read4(CCSR_CTBCKSELR); ccsr_write4(CCSR_CTBCKSELR, reg & ~(1 << pc->pc_cpuid)); /* Enable TB */ reg = ccsr_read4(CCSR_CTBENR); ccsr_write4(CCSR_CTBENR, reg | (1 << pc->pc_cpuid)); } else { /* * Set BPTR to the physical address of the boot page */ bptr = (bptr >> 12) | 0x80000000u; ccsr_write4(OCP85XX_BPTR, bptr); __asm __volatile("isync; msync"); } /* * Release AP from hold-off state */ reg = ccsr_read4(brr); ccsr_write4(brr, reg | (1 << cpuid)); __asm __volatile("isync; msync"); spin_wait: timeout = 500; while (!pc->pc_awake && timeout--) DELAY(1000); /* wait 1ms */ /* * Disable boot page translation so that the 4K page at the default * address (= 0xfffff000) isn't permanently remapped and thus not * usable otherwise. */ if (!epapr_boot) { if (mpc85xx_is_qoriq()) ccsr_write4(OCP85XX_BSTAR, 0); else ccsr_write4(OCP85XX_BPTR, 0); __asm __volatile("isync; msync"); } if (!pc->pc_awake) panic("SMP: CPU %d didn't wake up.\n", pc->pc_cpuid); return ((pc->pc_awake) ? 0 : EBUSY); #else /* No SMP support */ return (ENXIO); #endif } static void mpc85xx_reset(platform_t plat) { /* * Try the dedicated reset register first. * If the SoC doesn't have one, we'll fall * back to using the debug control register. */ ccsr_write4(OCP85XX_RSTCR, 2); /* Clear DBCR0, disables debug interrupts and events. */ mtspr(SPR_DBCR0, 0); __asm __volatile("isync"); /* Enable Debug Interrupts in MSR. */ mtmsr(mfmsr() | PSL_DE); /* Enable debug interrupts and issue reset. */ mtspr(SPR_DBCR0, mfspr(SPR_DBCR0) | DBCR0_IDM | DBCR0_RST_SYSTEM); printf("Reset failed...\n"); while (1) ; } static void mpc85xx_smp_timebase_sync(platform_t plat, u_long tb, int ap) { mttb(tb); } static void mpc85xx_idle(platform_t plat, int cpu) { uint32_t reg; if (mpc85xx_is_qoriq()) { /* * Base binutils doesn't know what the 'wait' instruction is, so * use the opcode encoding here. */ __asm __volatile("wrteei 1; .long 0x7c00007c"); } else { reg = mfmsr(); /* Freescale E500 core RM section 6.4.1. */ __asm __volatile("msync; mtmsr %0; isync" :: "r" (reg | PSL_WE)); } } static int mpc85xx_idle_wakeup(platform_t plat, int cpu) { return (0); } Index: head/sys/powerpc/ofw/ofw_machdep.c =================================================================== --- head/sys/powerpc/ofw/ofw_machdep.c (revision 326260) +++ head/sys/powerpc/ofw/ofw_machdep.c (revision 326261) @@ -1,615 +1,617 @@ /*- + * SPDX-License-Identifier: BSD-4-Clause + * * Copyright (C) 1996 Wolfgang Solfrank. * Copyright (C) 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: ofw_machdep.c,v 1.5 2000/05/23 13:25:43 tsubai Exp $ */ #include __FBSDID("$FreeBSD$"); #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 static void *fdt; int ofw_real_mode; #ifdef AIM extern register_t ofmsr[5]; extern void *openfirmware_entry; char save_trap_init[0x2f00]; /* EXC_LAST */ char save_trap_of[0x2f00]; /* EXC_LAST */ int ofwcall(void *); static int openfirmware(void *args); __inline void ofw_save_trap_vec(char *save_trap_vec) { if (!ofw_real_mode) return; bcopy((void *)EXC_RST, save_trap_vec, EXC_LAST - EXC_RST); } static __inline void ofw_restore_trap_vec(char *restore_trap_vec) { if (!ofw_real_mode) return; bcopy(restore_trap_vec, (void *)EXC_RST, EXC_LAST - EXC_RST); __syncicache(EXC_RSVD, EXC_LAST - EXC_RSVD); } /* * Saved SPRG0-3 from OpenFirmware. Will be restored prior to the callback. */ register_t ofw_sprg0_save; static __inline void ofw_sprg_prepare(void) { if (ofw_real_mode) return; /* * Assume that interrupt are disabled at this point, or * SPRG1-3 could be trashed */ #ifdef __powerpc64__ __asm __volatile("mtsprg1 %0\n\t" "mtsprg2 %1\n\t" "mtsprg3 %2\n\t" : : "r"(ofmsr[2]), "r"(ofmsr[3]), "r"(ofmsr[4])); #else __asm __volatile("mfsprg0 %0\n\t" "mtsprg0 %1\n\t" "mtsprg1 %2\n\t" "mtsprg2 %3\n\t" "mtsprg3 %4\n\t" : "=&r"(ofw_sprg0_save) : "r"(ofmsr[1]), "r"(ofmsr[2]), "r"(ofmsr[3]), "r"(ofmsr[4])); #endif } static __inline void ofw_sprg_restore(void) { if (ofw_real_mode) return; /* * Note that SPRG1-3 contents are irrelevant. They are scratch * registers used in the early portion of trap handling when * interrupts are disabled. * * PCPU data cannot be used until this routine is called ! */ #ifndef __powerpc64__ __asm __volatile("mtsprg0 %0" :: "r"(ofw_sprg0_save)); #endif } #endif static int parse_ofw_memory(phandle_t node, const char *prop, struct mem_region *output) { cell_t address_cells, size_cells; cell_t OFmem[4 * PHYS_AVAIL_SZ]; int sz, i, j; phandle_t phandle; sz = 0; /* * Get #address-cells from root node, defaulting to 1 if it cannot * be found. */ phandle = OF_finddevice("/"); if (OF_getencprop(phandle, "#address-cells", &address_cells, sizeof(address_cells)) < (ssize_t)sizeof(address_cells)) address_cells = 1; if (OF_getencprop(phandle, "#size-cells", &size_cells, sizeof(size_cells)) < (ssize_t)sizeof(size_cells)) size_cells = 1; /* * Get memory. */ if (node == -1 || (sz = OF_getencprop(node, prop, OFmem, sizeof(OFmem))) <= 0) panic("Physical memory map not found"); i = 0; j = 0; while (i < sz/sizeof(cell_t)) { output[j].mr_start = OFmem[i++]; if (address_cells == 2) { output[j].mr_start <<= 32; output[j].mr_start += OFmem[i++]; } output[j].mr_size = OFmem[i++]; if (size_cells == 2) { output[j].mr_size <<= 32; output[j].mr_size += OFmem[i++]; } if (output[j].mr_start > BUS_SPACE_MAXADDR) continue; /* * Constrain memory to that which we can access. * 32-bit AIM can only reference 32 bits of address currently, * but Book-E can access 36 bits. */ if (((uint64_t)output[j].mr_start + (uint64_t)output[j].mr_size - 1) > BUS_SPACE_MAXADDR) { output[j].mr_size = BUS_SPACE_MAXADDR - output[j].mr_start + 1; } j++; } sz = j*sizeof(output[0]); return (sz); } static int excise_fdt_reserved(struct mem_region *avail, int asz) { struct { uint64_t address; uint64_t size; } fdtmap[16]; ssize_t fdtmapsize; phandle_t chosen; int i, j, k; chosen = OF_finddevice("/chosen"); fdtmapsize = OF_getprop(chosen, "fdtmemreserv", fdtmap, sizeof(fdtmap)); for (j = 0; j < fdtmapsize/sizeof(fdtmap[0]); j++) { fdtmap[j].address = be64toh(fdtmap[j].address) & ~PAGE_MASK; fdtmap[j].size = round_page(be64toh(fdtmap[j].size)); } KASSERT(j*sizeof(fdtmap[0]) < sizeof(fdtmap), ("Exceeded number of FDT reservations")); /* Add a virtual entry for the FDT itself */ if (fdt != NULL) { fdtmap[j].address = (vm_offset_t)fdt & ~PAGE_MASK; fdtmap[j].size = round_page(fdt_totalsize(fdt)); fdtmapsize += sizeof(fdtmap[0]); } for (i = 0; i < asz; i++) { for (j = 0; j < fdtmapsize/sizeof(fdtmap[0]); j++) { /* * Case 1: Exclusion region encloses complete * available entry. Drop it and move on. */ if (fdtmap[j].address <= avail[i].mr_start && fdtmap[j].address + fdtmap[j].size >= avail[i].mr_start + avail[i].mr_size) { for (k = i+1; k < asz; k++) avail[k-1] = avail[k]; asz--; i--; /* Repeat some entries */ continue; } /* * Case 2: Exclusion region starts in available entry. * Trim it to where the entry begins and append * a new available entry with the region after * the excluded region, if any. */ if (fdtmap[j].address >= avail[i].mr_start && fdtmap[j].address < avail[i].mr_start + avail[i].mr_size) { if (fdtmap[j].address + fdtmap[j].size < avail[i].mr_start + avail[i].mr_size) { avail[asz].mr_start = fdtmap[j].address + fdtmap[j].size; avail[asz].mr_size = avail[i].mr_start + avail[i].mr_size - avail[asz].mr_start; asz++; } avail[i].mr_size = fdtmap[j].address - avail[i].mr_start; } /* * Case 3: Exclusion region ends in available entry. * Move start point to where the exclusion zone ends. * The case of a contained exclusion zone has already * been caught in case 2. */ if (fdtmap[j].address + fdtmap[j].size >= avail[i].mr_start && fdtmap[j].address + fdtmap[j].size < avail[i].mr_start + avail[i].mr_size) { avail[i].mr_size += avail[i].mr_start; avail[i].mr_start = fdtmap[j].address + fdtmap[j].size; avail[i].mr_size -= avail[i].mr_start; } } } return (asz); } /* * This is called during powerpc_init, before the system is really initialized. * It shall provide the total and the available regions of RAM. * The available regions need not take the kernel into account. */ void ofw_mem_regions(struct mem_region *memp, int *memsz, struct mem_region *availp, int *availsz) { phandle_t phandle; int asz, msz; int res; char name[31]; asz = msz = 0; /* * Get memory from all the /memory nodes. */ for (phandle = OF_child(OF_peer(0)); phandle != 0; phandle = OF_peer(phandle)) { if (OF_getprop(phandle, "name", name, sizeof(name)) <= 0) continue; if (strncmp(name, "memory", sizeof(name)) != 0 && strncmp(name, "memory@", strlen("memory@")) != 0) continue; res = parse_ofw_memory(phandle, "reg", &memp[msz]); msz += res/sizeof(struct mem_region); if (OF_getproplen(phandle, "available") >= 0) res = parse_ofw_memory(phandle, "available", &availp[asz]); else res = parse_ofw_memory(phandle, "reg", &availp[asz]); asz += res/sizeof(struct mem_region); } phandle = OF_finddevice("/chosen"); if (OF_hasprop(phandle, "fdtmemreserv")) asz = excise_fdt_reserved(availp, asz); *memsz = msz; *availsz = asz; } void OF_initial_setup(void *fdt_ptr, void *junk, int (*openfirm)(void *)) { #ifdef AIM ofmsr[0] = mfmsr(); #ifdef __powerpc64__ ofmsr[0] &= ~PSL_SF; #else __asm __volatile("mfsprg0 %0" : "=&r"(ofmsr[1])); #endif __asm __volatile("mfsprg1 %0" : "=&r"(ofmsr[2])); __asm __volatile("mfsprg2 %0" : "=&r"(ofmsr[3])); __asm __volatile("mfsprg3 %0" : "=&r"(ofmsr[4])); openfirmware_entry = openfirm; if (ofmsr[0] & PSL_DR) ofw_real_mode = 0; else ofw_real_mode = 1; ofw_save_trap_vec(save_trap_init); #else ofw_real_mode = 1; #endif fdt = fdt_ptr; #ifdef FDT_DTB_STATIC /* Check for a statically included blob */ if (fdt == NULL) fdt = &fdt_static_dtb; #endif } boolean_t OF_bootstrap() { boolean_t status = FALSE; #ifdef AIM if (openfirmware_entry != NULL) { if (ofw_real_mode) { status = OF_install(OFW_STD_REAL, 0); } else { #ifdef __powerpc64__ status = OF_install(OFW_STD_32BIT, 0); #else status = OF_install(OFW_STD_DIRECT, 0); #endif } if (status != TRUE) return status; OF_init(openfirmware); } else #endif if (fdt != NULL) { status = OF_install(OFW_FDT, 0); if (status != TRUE) return status; OF_init(fdt); OF_interpret("perform-fixup", 0); } return (status); } #ifdef AIM void ofw_quiesce(void) { struct { cell_t name; cell_t nargs; cell_t nreturns; } args; KASSERT(!pmap_bootstrapped, ("Cannot call ofw_quiesce after VM is up")); args.name = (cell_t)(uintptr_t)"quiesce"; args.nargs = 0; args.nreturns = 0; openfirmware(&args); } static int openfirmware_core(void *args) { int result; register_t oldmsr; if (openfirmware_entry == NULL) return (-1); /* * Turn off exceptions - we really don't want to end up * anywhere unexpected with PCPU set to something strange * or the stack pointer wrong. */ oldmsr = intr_disable(); ofw_sprg_prepare(); /* Save trap vectors */ ofw_save_trap_vec(save_trap_of); /* Restore initially saved trap vectors */ ofw_restore_trap_vec(save_trap_init); #ifndef __powerpc64__ /* * Clear battable[] translations */ if (!(cpu_features & PPC_FEATURE_64)) __asm __volatile("mtdbatu 2, %0\n" "mtdbatu 3, %0" : : "r" (0)); isync(); #endif result = ofwcall(args); /* Restore trap vecotrs */ ofw_restore_trap_vec(save_trap_of); ofw_sprg_restore(); intr_restore(oldmsr); return (result); } #ifdef SMP struct ofw_rv_args { void *args; int retval; volatile int in_progress; }; static void ofw_rendezvous_dispatch(void *xargs) { struct ofw_rv_args *rv_args = xargs; /* NOTE: Interrupts are disabled here */ if (PCPU_GET(cpuid) == 0) { /* * Execute all OF calls on CPU 0 */ rv_args->retval = openfirmware_core(rv_args->args); rv_args->in_progress = 0; } else { /* * Spin with interrupts off on other CPUs while OF has * control of the machine. */ while (rv_args->in_progress) cpu_spinwait(); } } #endif static int openfirmware(void *args) { int result; #ifdef SMP struct ofw_rv_args rv_args; #endif if (openfirmware_entry == NULL) return (-1); #ifdef SMP if (cold) { result = openfirmware_core(args); } else { rv_args.args = args; rv_args.in_progress = 1; smp_rendezvous(smp_no_rendezvous_barrier, ofw_rendezvous_dispatch, smp_no_rendezvous_barrier, &rv_args); result = rv_args.retval; } #else result = openfirmware_core(args); #endif return (result); } void OF_reboot() { struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t arg; } args; args.name = (cell_t)(uintptr_t)"interpret"; args.nargs = 1; args.nreturns = 0; args.arg = (cell_t)(uintptr_t)"reset-all"; openfirmware_core(&args); /* Don't do rendezvous! */ for (;;); /* just in case */ } #endif /* AIM */ void OF_getetheraddr(device_t dev, u_char *addr) { phandle_t node; node = ofw_bus_get_node(dev); OF_getprop(node, "local-mac-address", addr, ETHER_ADDR_LEN); } /* * Return a bus handle and bus tag that corresponds to the register * numbered regno for the device referenced by the package handle * dev. This function is intended to be used by console drivers in * early boot only. It works by mapping the address of the device's * register in the address space of its parent and recursively walk * the device tree upward this way. */ int OF_decode_addr(phandle_t dev, int regno, bus_space_tag_t *tag, bus_space_handle_t *handle, bus_size_t *sz) { bus_addr_t addr; bus_size_t size; pcell_t pci_hi; int flags, res; res = ofw_reg_to_paddr(dev, regno, &addr, &size, &pci_hi); if (res < 0) return (res); if (pci_hi == OFW_PADDR_NOT_PCI) { *tag = &bs_be_tag; flags = 0; } else { *tag = &bs_le_tag; flags = (pci_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) ? BUS_SPACE_MAP_PREFETCHABLE: 0; } if (sz != NULL) *sz = size; return (bus_space_map(*tag, addr, size, flags, handle)); } Index: head/sys/powerpc/ofw/ofw_pcib_pci.c =================================================================== --- head/sys/powerpc/ofw/ofw_pcib_pci.c (revision 326260) +++ head/sys/powerpc/ofw/ofw_pcib_pci.c (revision 326261) @@ -1,173 +1,175 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2000 Michael Smith * Copyright (c) 2000 BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" static int ofw_pcib_pci_probe(device_t bus); static int ofw_pcib_pci_attach(device_t bus); static phandle_t ofw_pcib_pci_get_node(device_t bus, device_t dev); static int ofw_pcib_pci_route_interrupt(device_t bridge, device_t dev, int intpin); static device_method_t ofw_pcib_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ofw_pcib_pci_probe), DEVMETHOD(device_attach, ofw_pcib_pci_attach), /* pcib interface */ DEVMETHOD(pcib_route_interrupt, ofw_pcib_pci_route_interrupt), DEVMETHOD(pcib_request_feature, pcib_request_feature_allow), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, ofw_pcib_pci_get_node), DEVMETHOD_END }; static devclass_t pcib_devclass; struct ofw_pcib_softc { /* * This is here so that we can use pci bridge methods, too - the * generic routines only need the dev, secbus and subbus members * filled. */ struct pcib_softc ops_pcib_sc; phandle_t ops_node; struct ofw_bus_iinfo ops_iinfo; }; DEFINE_CLASS_1(pcib, ofw_pcib_pci_driver, ofw_pcib_pci_methods, sizeof(struct ofw_pcib_softc), pcib_driver); DRIVER_MODULE(ofw_pcib, pci, ofw_pcib_pci_driver, pcib_devclass, 0, 0); static int ofw_pcib_pci_probe(device_t dev) { if ((pci_get_class(dev) != PCIC_BRIDGE) || (pci_get_subclass(dev) != PCIS_BRIDGE_PCI)) { return (ENXIO); } if (ofw_bus_get_node(dev) == -1) return (ENXIO); device_set_desc(dev, "OFW PCI-PCI bridge"); return (0); } static int ofw_pcib_pci_attach(device_t dev) { struct ofw_pcib_softc *sc; sc = device_get_softc(dev); sc->ops_pcib_sc.dev = dev; sc->ops_node = ofw_bus_get_node(dev); ofw_bus_setup_iinfo(sc->ops_node, &sc->ops_iinfo, sizeof(cell_t)); pcib_attach_common(dev); return (pcib_attach_child(dev)); } static phandle_t ofw_pcib_pci_get_node(device_t bridge, device_t dev) { /* We have only one child, the PCI bus, so pass it our node */ return (ofw_bus_get_node(bridge)); } static int ofw_pcib_pci_route_interrupt(device_t bridge, device_t dev, int intpin) { struct ofw_pcib_softc *sc; struct ofw_bus_iinfo *ii; struct ofw_pci_register reg; cell_t pintr, mintr[2]; int intrcells; phandle_t iparent; sc = device_get_softc(bridge); ii = &sc->ops_iinfo; if (ii->opi_imapsz > 0) { pintr = intpin; /* Fabricate imap information if this isn't an OFW device */ bzero(®, sizeof(reg)); reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) | (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) | (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT); intrcells = ofw_bus_lookup_imap(ofw_bus_get_node(dev), ii, ®, sizeof(reg), &pintr, sizeof(pintr), mintr, sizeof(mintr), &iparent); if (intrcells) { /* * If we've found a mapping, return it and don't map * it again on higher levels - that causes problems * in some cases, and never seems to be required. */ mintr[0] = ofw_bus_map_intr(dev, iparent, intrcells, mintr); return (mintr[0]); } } else if (intpin >= 1 && intpin <= 4) { /* * When an interrupt map is missing, we need to do the * standard PCI swizzle and continue mapping at the parent. */ return (pcib_route_interrupt(bridge, dev, intpin)); } return (PCIB_ROUTE_INTERRUPT(device_get_parent(device_get_parent( bridge)), bridge, intpin)); } Index: head/sys/powerpc/ofw/ofw_pcibus.c =================================================================== --- head/sys/powerpc/ofw/ofw_pcibus.c (revision 326260) +++ head/sys/powerpc/ofw/ofw_pcibus.c (revision 326261) @@ -1,381 +1,383 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 1997, Stefan Esser * Copyright (c) 2000, Michael Smith * Copyright (c) 2000, BSDi * Copyright (c) 2003, Thomas Moestl * 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 unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ofw_pcibus.h" #include "pcib_if.h" #include "pci_if.h" typedef uint32_t ofw_pci_intr_t; /* Methods */ static device_probe_t ofw_pcibus_probe; static device_attach_t ofw_pcibus_attach; static pci_alloc_devinfo_t ofw_pcibus_alloc_devinfo; static pci_assign_interrupt_t ofw_pcibus_assign_interrupt; static ofw_bus_get_devinfo_t ofw_pcibus_get_devinfo; static bus_child_deleted_t ofw_pcibus_child_deleted; static int ofw_pcibus_child_pnpinfo_str_method(device_t cbdev, device_t child, char *buf, size_t buflen); static void ofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno); static void ofw_pcibus_enum_bus(device_t dev, u_int domain, u_int busno); static device_method_t ofw_pcibus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ofw_pcibus_probe), DEVMETHOD(device_attach, ofw_pcibus_attach), /* Bus interface */ DEVMETHOD(bus_child_deleted, ofw_pcibus_child_deleted), DEVMETHOD(bus_child_pnpinfo_str, ofw_pcibus_child_pnpinfo_str_method), DEVMETHOD(bus_rescan, bus_null_rescan), /* PCI interface */ DEVMETHOD(pci_alloc_devinfo, ofw_pcibus_alloc_devinfo), DEVMETHOD(pci_assign_interrupt, ofw_pcibus_assign_interrupt), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, ofw_pcibus_get_devinfo), 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 pci_devclass; DEFINE_CLASS_1(pci, ofw_pcibus_driver, ofw_pcibus_methods, sizeof(struct pci_softc), pci_driver); DRIVER_MODULE(ofw_pcibus, pcib, ofw_pcibus_driver, pci_devclass, 0, 0); MODULE_VERSION(ofw_pcibus, 1); MODULE_DEPEND(ofw_pcibus, pci, 1, 1, 1); static int ofw_devices_only = 0; TUNABLE_INT("hw.pci.ofw_devices_only", &ofw_devices_only); static int ofw_pcibus_probe(device_t dev) { if (ofw_bus_get_node(dev) == -1) return (ENXIO); device_set_desc(dev, "OFW PCI bus"); return (BUS_PROBE_DEFAULT); } static int ofw_pcibus_attach(device_t dev) { u_int busno, domain; int error; error = pci_attach_common(dev); if (error) return (error); domain = pcib_get_domain(dev); busno = pcib_get_bus(dev); /* * Attach those children represented in the device tree. */ ofw_pcibus_enum_devtree(dev, domain, busno); /* * We now attach any laggard devices. FDT, for instance, allows * the device tree to enumerate only some PCI devices. Apple's * OF device tree on some Grackle-based hardware can also miss * functions on multi-function cards. */ if (!ofw_devices_only) ofw_pcibus_enum_bus(dev, domain, busno); return (bus_generic_attach(dev)); } struct pci_devinfo * ofw_pcibus_alloc_devinfo(device_t dev) { struct ofw_pcibus_devinfo *dinfo; dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO); return (&dinfo->opd_dinfo); } static void ofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno) { device_t pcib; struct ofw_pci_register pcir; struct ofw_pcibus_devinfo *dinfo; phandle_t node, child; u_int func, slot; int intline; pcib = device_get_parent(dev); node = ofw_bus_get_node(dev); for (child = OF_child(node); child != 0; child = OF_peer(child)) { if (OF_getencprop(child, "reg", (pcell_t *)&pcir, sizeof(pcir)) == -1) continue; slot = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi); func = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi); /* Some OFW device trees contain dupes. */ if (pci_find_dbsf(domain, busno, slot, func) != NULL) continue; /* * The preset in the intline register is usually bogus. Reset * it such that the PCI code will reroute the interrupt if * needed. */ intline = PCI_INVALID_IRQ; if (OF_getproplen(child, "interrupts") > 0) intline = 0; PCIB_WRITE_CONFIG(pcib, busno, slot, func, PCIR_INTLINE, intline, 1); /* * Now set up the PCI and OFW bus layer devinfo and add it * to the PCI bus. */ dinfo = (struct ofw_pcibus_devinfo *)pci_read_device(pcib, dev, domain, busno, slot, func); if (dinfo == NULL) continue; if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, child) != 0) { pci_freecfg((struct pci_devinfo *)dinfo); continue; } dinfo->opd_dma_tag = NULL; pci_add_child(dev, (struct pci_devinfo *)dinfo); /* * Some devices don't have an intpin set, but do have * interrupts. These are fully specified, and set in the * interrupts property, so add that value to the device's * resource list. */ if (dinfo->opd_dinfo.cfg.intpin == 0) ofw_bus_intr_to_rl(dev, child, &dinfo->opd_dinfo.resources, NULL); } } /* * The following is an almost exact clone of pci_add_children(), with the * addition that it (a) will not add children that have already been added, * and (b) will set up the OFW devinfo to point to invalid values. This is * to handle non-enumerated PCI children as exist in FDT and on the second * function of the Rage 128 in my Blue & White G3. */ static void ofw_pcibus_enum_bus(device_t dev, u_int domain, u_int busno) { device_t pcib; struct ofw_pcibus_devinfo *dinfo; int maxslots; int s, f, pcifunchigh; uint8_t hdrtype; pcib = device_get_parent(dev); maxslots = PCIB_MAXSLOTS(pcib); for (s = 0; s <= maxslots; s++) { pcifunchigh = 0; f = 0; DELAY(1); hdrtype = PCIB_READ_CONFIG(pcib, busno, s, f, PCIR_HDRTYPE, 1); if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) continue; if (hdrtype & PCIM_MFDEV) pcifunchigh = PCI_FUNCMAX; for (f = 0; f <= pcifunchigh; f++) { /* Filter devices we have already added */ if (pci_find_dbsf(domain, busno, s, f) != NULL) continue; dinfo = (struct ofw_pcibus_devinfo *)pci_read_device( pcib, dev, domain, busno, s, f); if (dinfo == NULL) continue; dinfo->opd_dma_tag = NULL; dinfo->opd_obdinfo.obd_node = -1; dinfo->opd_obdinfo.obd_name = NULL; dinfo->opd_obdinfo.obd_compat = NULL; dinfo->opd_obdinfo.obd_type = NULL; dinfo->opd_obdinfo.obd_model = NULL; /* * For non OFW-devices, don't believe 0 * for an interrupt. */ if (dinfo->opd_dinfo.cfg.intline == 0) { dinfo->opd_dinfo.cfg.intline = PCI_INVALID_IRQ; PCIB_WRITE_CONFIG(pcib, busno, s, f, PCIR_INTLINE, PCI_INVALID_IRQ, 1); } pci_add_child(dev, (struct pci_devinfo *)dinfo); } } } static void ofw_pcibus_child_deleted(device_t dev, device_t child) { struct ofw_pcibus_devinfo *dinfo; dinfo = device_get_ivars(dev); ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo); pci_child_deleted(dev, child); } static int ofw_pcibus_child_pnpinfo_str_method(device_t cbdev, device_t child, char *buf, size_t buflen) { pci_child_pnpinfo_str_method(cbdev, child, buf, buflen); if (ofw_bus_get_node(child) != -1) { strlcat(buf, " ", buflen); /* Separate info */ ofw_bus_gen_child_pnpinfo_str(cbdev, child, buf, buflen); } return (0); } static int ofw_pcibus_assign_interrupt(device_t dev, device_t child) { ofw_pci_intr_t intr[2]; phandle_t node, iparent; int isz, icells; node = ofw_bus_get_node(child); if (node == -1) { /* Non-firmware enumerated child, use standard routing */ intr[0] = pci_get_intpin(child); return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, intr[0])); } /* * Try to determine the node's interrupt parent so we know which * PIC to use. */ iparent = -1; if (OF_getencprop(node, "interrupt-parent", &iparent, sizeof(iparent)) < 0) iparent = -1; icells = 1; if (iparent != -1) OF_getencprop(OF_node_from_xref(iparent), "#interrupt-cells", &icells, sizeof(icells)); /* * Any AAPL,interrupts property gets priority and is * fully specified (i.e. does not need routing) */ isz = OF_getencprop(node, "AAPL,interrupts", intr, sizeof(intr)); if (isz == sizeof(intr[0])*icells) return ((iparent == -1) ? intr[0] : ofw_bus_map_intr(dev, iparent, icells, intr)); isz = OF_getencprop(node, "interrupts", intr, sizeof(intr)); if (isz == sizeof(intr[0])*icells) { if (iparent != -1) intr[0] = ofw_bus_map_intr(dev, iparent, icells, intr); } else { /* No property: our best guess is the intpin. */ intr[0] = pci_get_intpin(child); } /* * If we got intr from a property, it may or may not be an intpin. * For on-board devices, it frequently is not, and is completely out * of the valid intpin range. For PCI slots, it hopefully is, * otherwise we will have trouble interfacing with non-OFW buses * such as cardbus. * Since we cannot tell which it is without violating layering, we * will always use the route_interrupt method, and treat exceptions * on the level they become apparent. */ return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, intr[0])); } static const struct ofw_bus_devinfo * ofw_pcibus_get_devinfo(device_t bus, device_t dev) { struct ofw_pcibus_devinfo *dinfo; dinfo = device_get_ivars(dev); return (&dinfo->opd_obdinfo); } Index: head/sys/powerpc/ofw/ofw_pcibus.h =================================================================== --- head/sys/powerpc/ofw/ofw_pcibus.h (revision 326260) +++ head/sys/powerpc/ofw/ofw_pcibus.h (revision 326261) @@ -1,51 +1,53 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef POWERPC_OFW_OFW_PCIBUS_H #define POWERPC_OFW_OFW_PCIBUS_H #include #include #include #include #include /* * Export class definition for inheritance purposes */ DECLARE_CLASS(ofw_pcibus_driver); /* PCI Bus Enumerators */ struct ofw_pcibus_devinfo { struct pci_devinfo opd_dinfo; struct ofw_bus_devinfo opd_obdinfo; bus_dma_tag_t opd_dma_tag; }; #endif // POWERPC_OFW_OFW_PCIBUS_H Index: head/sys/powerpc/ofw/ofw_real.c =================================================================== --- head/sys/powerpc/ofw/ofw_real.c (revision 326260) +++ head/sys/powerpc/ofw/ofw_real.c (revision 326261) @@ -1,1101 +1,1103 @@ /* $NetBSD: Locore.c,v 1.7 2000/08/20 07:04:59 tsubai Exp $ */ /*- + * SPDX-License-Identifier:BSD-4-Clause AND BSD-2-Clause-FreeBSD + * * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. */ /*- * Copyright (C) 2000 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ofw_if.h" static int ofw_real_init(ofw_t, void *openfirm); static int ofw_real_test(ofw_t, const char *name); static phandle_t ofw_real_peer(ofw_t, phandle_t node); static phandle_t ofw_real_child(ofw_t, phandle_t node); static phandle_t ofw_real_parent(ofw_t, phandle_t node); static phandle_t ofw_real_instance_to_package(ofw_t, ihandle_t instance); static ssize_t ofw_real_getproplen(ofw_t, phandle_t package, const char *propname); static ssize_t ofw_real_getprop(ofw_t, phandle_t package, const char *propname, void *buf, size_t buflen); static int ofw_real_nextprop(ofw_t, phandle_t package, const char *previous, char *buf, size_t); static int ofw_real_setprop(ofw_t, phandle_t package, const char *propname, const void *buf, size_t len); static ssize_t ofw_real_canon(ofw_t, const char *device, char *buf, size_t len); static phandle_t ofw_real_finddevice(ofw_t, const char *device); static ssize_t ofw_real_instance_to_path(ofw_t, ihandle_t instance, char *buf, size_t len); static ssize_t ofw_real_package_to_path(ofw_t, phandle_t package, char *buf, size_t len); static int ofw_real_call_method(ofw_t, ihandle_t instance, const char *method, int nargs, int nreturns, cell_t *args_and_returns); static int ofw_real_interpret(ofw_t ofw, const char *cmd, int nreturns, cell_t *returns); static ihandle_t ofw_real_open(ofw_t, const char *device); static void ofw_real_close(ofw_t, ihandle_t instance); static ssize_t ofw_real_read(ofw_t, ihandle_t instance, void *addr, size_t len); static ssize_t ofw_real_write(ofw_t, ihandle_t instance, const void *addr, size_t len); static int ofw_real_seek(ofw_t, ihandle_t instance, u_int64_t pos); static caddr_t ofw_real_claim(ofw_t, void *virt, size_t size, u_int align); static void ofw_real_release(ofw_t, void *virt, size_t size); static void ofw_real_enter(ofw_t); static void ofw_real_exit(ofw_t); static ofw_method_t ofw_real_methods[] = { OFWMETHOD(ofw_init, ofw_real_init), OFWMETHOD(ofw_peer, ofw_real_peer), OFWMETHOD(ofw_child, ofw_real_child), OFWMETHOD(ofw_parent, ofw_real_parent), OFWMETHOD(ofw_instance_to_package, ofw_real_instance_to_package), OFWMETHOD(ofw_getproplen, ofw_real_getproplen), OFWMETHOD(ofw_getprop, ofw_real_getprop), OFWMETHOD(ofw_nextprop, ofw_real_nextprop), OFWMETHOD(ofw_setprop, ofw_real_setprop), OFWMETHOD(ofw_canon, ofw_real_canon), OFWMETHOD(ofw_finddevice, ofw_real_finddevice), OFWMETHOD(ofw_instance_to_path, ofw_real_instance_to_path), OFWMETHOD(ofw_package_to_path, ofw_real_package_to_path), OFWMETHOD(ofw_test, ofw_real_test), OFWMETHOD(ofw_call_method, ofw_real_call_method), OFWMETHOD(ofw_interpret, ofw_real_interpret), OFWMETHOD(ofw_open, ofw_real_open), OFWMETHOD(ofw_close, ofw_real_close), OFWMETHOD(ofw_read, ofw_real_read), OFWMETHOD(ofw_write, ofw_real_write), OFWMETHOD(ofw_seek, ofw_real_seek), OFWMETHOD(ofw_claim, ofw_real_claim), OFWMETHOD(ofw_release, ofw_real_release), OFWMETHOD(ofw_enter, ofw_real_enter), OFWMETHOD(ofw_exit, ofw_real_exit), { 0, 0 } }; static ofw_def_t ofw_real = { OFW_STD_REAL, ofw_real_methods, 0 }; OFW_DEF(ofw_real); static ofw_def_t ofw_32bit = { OFW_STD_32BIT, ofw_real_methods, 0 }; OFW_DEF(ofw_32bit); static MALLOC_DEFINE(M_OFWREAL, "ofwreal", "Open Firmware Real Mode Bounce Page"); static int (*openfirmware)(void *); static vm_offset_t of_bounce_phys; static caddr_t of_bounce_virt; static off_t of_bounce_offset; static size_t of_bounce_size; static struct mtx of_bounce_mtx; extern int ofw_real_mode; /* * After the VM is up, allocate a wired, low memory bounce page. */ static void ofw_real_bounce_alloc(void *); SYSINIT(ofw_real_bounce_alloc, SI_SUB_KMEM, SI_ORDER_ANY, ofw_real_bounce_alloc, NULL); static void ofw_real_start(void) { mtx_lock(&of_bounce_mtx); of_bounce_offset = 0; } static void ofw_real_stop(void) { mtx_unlock(&of_bounce_mtx); } static void ofw_real_bounce_alloc(void *junk) { /* * Check that ofw_real is actually in use before allocating wads * of memory. Do this by checking if our mutex has been set up. */ if (!mtx_initialized(&of_bounce_mtx)) return; /* * Allocate a page of contiguous, wired physical memory that can * fit into a 32-bit address space and accessed from real mode. */ mtx_lock(&of_bounce_mtx); of_bounce_virt = contigmalloc(4 * PAGE_SIZE, M_OFWREAL, 0, 0, ulmin(platform_real_maxaddr(), BUS_SPACE_MAXADDR_32BIT), PAGE_SIZE, 4 * PAGE_SIZE); of_bounce_phys = vtophys(of_bounce_virt); of_bounce_size = 4 * PAGE_SIZE; /* * For virtual-mode OF, direct map this physical address so that * we have a 32-bit virtual address to give OF. */ if (!ofw_real_mode && !hw_direct_map) pmap_kenter(of_bounce_phys, of_bounce_phys); mtx_unlock(&of_bounce_mtx); } static cell_t ofw_real_map(const void *buf, size_t len) { static char emergency_buffer[255]; cell_t phys; mtx_assert(&of_bounce_mtx, MA_OWNED); if (of_bounce_virt == NULL) { /* * If we haven't set up the MMU, then buf is guaranteed * to be accessible to OF, because the only memory we * can use right now is memory mapped by firmware. */ if (!pmap_bootstrapped) return (cell_t)(uintptr_t)buf; /* * XXX: It is possible for us to get called before the VM has * come online, but after the MMU is up. We don't have the * bounce buffer yet, but can no longer presume a 1:1 mapping. * Copy into the emergency buffer, and reset at the end. */ of_bounce_virt = emergency_buffer; of_bounce_phys = (vm_offset_t)of_bounce_virt; of_bounce_size = sizeof(emergency_buffer); } /* * Make sure the bounce page offset satisfies any reasonable * alignment constraint. */ of_bounce_offset += sizeof(register_t) - (of_bounce_offset % sizeof(register_t)); if (of_bounce_offset + len > of_bounce_size) { panic("Oversize Open Firmware call!"); return 0; } if (buf != NULL) memcpy(of_bounce_virt + of_bounce_offset, buf, len); else return (0); phys = of_bounce_phys + of_bounce_offset; of_bounce_offset += len; return (phys); } static void ofw_real_unmap(cell_t physaddr, void *buf, size_t len) { mtx_assert(&of_bounce_mtx, MA_OWNED); if (of_bounce_virt == NULL) return; if (physaddr == 0) return; memcpy(buf,of_bounce_virt + (physaddr - of_bounce_phys),len); } /* Initialiser */ static int ofw_real_init(ofw_t ofw, void *openfirm) { openfirmware = (int (*)(void *))openfirm; mtx_init(&of_bounce_mtx, "OF Bounce Page", NULL, MTX_DEF); of_bounce_virt = NULL; return (0); } /* * Generic functions */ /* Test to see if a service exists. */ static int ofw_real_test(ofw_t ofw, const char *name) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t service; cell_t missing; } args; args.name = (cell_t)(uintptr_t)"test"; args.nargs = 1; args.nreturns = 1; ofw_real_start(); args.service = ofw_real_map(name, strlen(name) + 1); argsptr = ofw_real_map(&args, sizeof(args)); if (args.service == 0 || openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_stop(); return (args.missing); } /* * Device tree functions */ /* Return the next sibling of this node or 0. */ static phandle_t ofw_real_peer(ofw_t ofw, phandle_t node) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t node; cell_t next; } args; args.name = (cell_t)(uintptr_t)"peer"; args.nargs = 1; args.nreturns = 1; args.node = node; ofw_real_start(); argsptr = ofw_real_map(&args, sizeof(args)); if (openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_stop(); return (args.next); } /* Return the first child of this node or 0. */ static phandle_t ofw_real_child(ofw_t ofw, phandle_t node) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t node; cell_t child; } args; args.name = (cell_t)(uintptr_t)"child"; args.nargs = 1; args.nreturns = 1; args.node = node; ofw_real_start(); argsptr = ofw_real_map(&args, sizeof(args)); if (openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_stop(); return (args.child); } /* Return the parent of this node or 0. */ static phandle_t ofw_real_parent(ofw_t ofw, phandle_t node) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t node; cell_t parent; } args; args.name = (cell_t)(uintptr_t)"parent"; args.nargs = 1; args.nreturns = 1; args.node = node; ofw_real_start(); argsptr = ofw_real_map(&args, sizeof(args)); if (openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_stop(); return (args.parent); } /* Return the package handle that corresponds to an instance handle. */ static phandle_t ofw_real_instance_to_package(ofw_t ofw, ihandle_t instance) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t package; } args; args.name = (cell_t)(uintptr_t)"instance-to-package"; args.nargs = 1; args.nreturns = 1; args.instance = instance; ofw_real_start(); argsptr = ofw_real_map(&args, sizeof(args)); if (openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_stop(); return (args.package); } /* Get the length of a property of a package. */ static ssize_t ofw_real_getproplen(ofw_t ofw, phandle_t package, const char *propname) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t package; cell_t propname; int32_t proplen; } args; args.name = (cell_t)(uintptr_t)"getproplen"; args.nargs = 2; args.nreturns = 1; ofw_real_start(); args.package = package; args.propname = ofw_real_map(propname, strlen(propname) + 1); argsptr = ofw_real_map(&args, sizeof(args)); if (args.propname == 0 || openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_stop(); return (args.proplen); } /* Get the value of a property of a package. */ static ssize_t ofw_real_getprop(ofw_t ofw, phandle_t package, const char *propname, void *buf, size_t buflen) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t package; cell_t propname; cell_t buf; cell_t buflen; int32_t size; } args; args.name = (cell_t)(uintptr_t)"getprop"; args.nargs = 4; args.nreturns = 1; ofw_real_start(); args.package = package; args.propname = ofw_real_map(propname, strlen(propname) + 1); args.buf = ofw_real_map(buf, buflen); args.buflen = buflen; argsptr = ofw_real_map(&args, sizeof(args)); if (args.propname == 0 || args.buf == 0 || openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_unmap(args.buf, buf, buflen); ofw_real_stop(); return (args.size); } /* Get the next property of a package. */ static int ofw_real_nextprop(ofw_t ofw, phandle_t package, const char *previous, char *buf, size_t size) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t package; cell_t previous; cell_t buf; cell_t flag; } args; args.name = (cell_t)(uintptr_t)"nextprop"; args.nargs = 3; args.nreturns = 1; ofw_real_start(); args.package = package; args.previous = ofw_real_map(previous, (previous != NULL) ? (strlen(previous) + 1) : 0); args.buf = ofw_real_map(buf, size); argsptr = ofw_real_map(&args, sizeof(args)); if (args.buf == 0 || openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_unmap(args.buf, buf, size); ofw_real_stop(); return (args.flag); } /* Set the value of a property of a package. */ /* XXX Has a bug on FirePower */ static int ofw_real_setprop(ofw_t ofw, phandle_t package, const char *propname, const void *buf, size_t len) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t package; cell_t propname; cell_t buf; cell_t len; cell_t size; } args; args.name = (cell_t)(uintptr_t)"setprop"; args.nargs = 4; args.nreturns = 1; ofw_real_start(); args.package = package; args.propname = ofw_real_map(propname, strlen(propname) + 1); args.buf = ofw_real_map(buf, len); args.len = len; argsptr = ofw_real_map(&args, sizeof(args)); if (args.propname == 0 || args.buf == 0 || openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_stop(); return (args.size); } /* Convert a device specifier to a fully qualified pathname. */ static ssize_t ofw_real_canon(ofw_t ofw, const char *device, char *buf, size_t len) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t device; cell_t buf; cell_t len; int32_t size; } args; args.name = (cell_t)(uintptr_t)"canon"; args.nargs = 3; args.nreturns = 1; ofw_real_start(); args.device = ofw_real_map(device, strlen(device) + 1); args.buf = ofw_real_map(buf, len); args.len = len; argsptr = ofw_real_map(&args, sizeof(args)); if (args.device == 0 || args.buf == 0 || openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_unmap(args.buf, buf, len); ofw_real_stop(); return (args.size); } /* Return a package handle for the specified device. */ static phandle_t ofw_real_finddevice(ofw_t ofw, const char *device) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t device; cell_t package; } args; args.name = (cell_t)(uintptr_t)"finddevice"; args.nargs = 1; args.nreturns = 1; ofw_real_start(); args.device = ofw_real_map(device, strlen(device) + 1); argsptr = ofw_real_map(&args, sizeof(args)); if (args.device == 0 || openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_stop(); return (args.package); } /* Return the fully qualified pathname corresponding to an instance. */ static ssize_t ofw_real_instance_to_path(ofw_t ofw, ihandle_t instance, char *buf, size_t len) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t buf; cell_t len; int32_t size; } args; args.name = (cell_t)(uintptr_t)"instance-to-path"; args.nargs = 3; args.nreturns = 1; ofw_real_start(); args.instance = instance; args.buf = ofw_real_map(buf, len); args.len = len; argsptr = ofw_real_map(&args, sizeof(args)); if (args.buf == 0 || openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_unmap(args.buf, buf, len); ofw_real_stop(); return (args.size); } /* Return the fully qualified pathname corresponding to a package. */ static ssize_t ofw_real_package_to_path(ofw_t ofw, phandle_t package, char *buf, size_t len) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t package; cell_t buf; cell_t len; int32_t size; } args; args.name = (cell_t)(uintptr_t)"package-to-path"; args.nargs = 3; args.nreturns = 1; ofw_real_start(); args.package = package; args.buf = ofw_real_map(buf, len); args.len = len; argsptr = ofw_real_map(&args, sizeof(args)); if (args.buf == 0 || openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_unmap(args.buf, buf, len); ofw_real_stop(); return (args.size); } /* Call the method in the scope of a given instance. */ static int ofw_real_call_method(ofw_t ofw, ihandle_t instance, const char *method, int nargs, int nreturns, cell_t *args_and_returns) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t method; cell_t instance; cell_t args_n_results[12]; } args; cell_t *ap, *cp; int n; args.name = (cell_t)(uintptr_t)"call-method"; args.nargs = 2; args.nreturns = 1; if (nargs > 6) return (-1); ofw_real_start(); args.nargs = nargs + 2; args.nreturns = nreturns + 1; args.method = ofw_real_map(method, strlen(method) + 1); args.instance = instance; ap = args_and_returns; for (cp = args.args_n_results + (n = nargs); --n >= 0;) *--cp = *(ap++); argsptr = ofw_real_map(&args, sizeof(args)); if (args.method == 0 || openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_stop(); if (args.args_n_results[nargs]) return (args.args_n_results[nargs]); for (cp = args.args_n_results + nargs + (n = args.nreturns); --n > 0;) *(ap++) = *--cp; return (0); } static int ofw_real_interpret(ofw_t ofw, const char *cmd, int nreturns, cell_t *returns) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t slot[16]; } args; cell_t status; int i = 0, j = 0; args.name = (cell_t)(uintptr_t)"interpret"; args.nargs = 1; ofw_real_start(); args.nreturns = ++nreturns; args.slot[i++] = ofw_real_map(cmd, strlen(cmd) + 1); argsptr = ofw_real_map(&args, sizeof(args)); if (openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_stop(); status = args.slot[i++]; while (i < 1 + nreturns) returns[j++] = args.slot[i++]; return (status); } /* * Device I/O functions */ /* Open an instance for a device. */ static ihandle_t ofw_real_open(ofw_t ofw, const char *device) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t device; cell_t instance; } args; args.name = (cell_t)(uintptr_t)"open"; args.nargs = 1; args.nreturns = 1; ofw_real_start(); args.device = ofw_real_map(device, strlen(device) + 1); argsptr = ofw_real_map(&args, sizeof(args)); if (args.device == 0 || openfirmware((void *)argsptr) == -1 || args.instance == 0) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_stop(); return (args.instance); } /* Close an instance. */ static void ofw_real_close(ofw_t ofw, ihandle_t instance) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; } args; args.name = (cell_t)(uintptr_t)"close"; args.nargs = 1; args.nreturns = 0; args.instance = instance; ofw_real_start(); argsptr = ofw_real_map(&args, sizeof(args)); openfirmware((void *)argsptr); ofw_real_stop(); } /* Read from an instance. */ static ssize_t ofw_real_read(ofw_t ofw, ihandle_t instance, void *addr, size_t len) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t addr; cell_t len; int32_t actual; } args; args.name = (cell_t)(uintptr_t)"read"; args.nargs = 3; args.nreturns = 1; ofw_real_start(); args.instance = instance; args.addr = ofw_real_map(addr, len); args.len = len; argsptr = ofw_real_map(&args, sizeof(args)); if (args.addr == 0 || openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_unmap(args.addr, addr, len); ofw_real_stop(); return (args.actual); } /* Write to an instance. */ static ssize_t ofw_real_write(ofw_t ofw, ihandle_t instance, const void *addr, size_t len) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t addr; cell_t len; int32_t actual; } args; args.name = (cell_t)(uintptr_t)"write"; args.nargs = 3; args.nreturns = 1; ofw_real_start(); args.instance = instance; args.addr = ofw_real_map(addr, len); args.len = len; argsptr = ofw_real_map(&args, sizeof(args)); if (args.addr == 0 || openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_stop(); return (args.actual); } /* Seek to a position. */ static int ofw_real_seek(ofw_t ofw, ihandle_t instance, u_int64_t pos) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t poshi; cell_t poslo; cell_t status; } args; args.name = (cell_t)(uintptr_t)"seek"; args.nargs = 3; args.nreturns = 1; args.instance = instance; args.poshi = pos >> 32; args.poslo = pos; ofw_real_start(); argsptr = ofw_real_map(&args, sizeof(args)); if (openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return (-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_stop(); return (args.status); } /* * Memory functions */ /* Claim an area of memory. */ static caddr_t ofw_real_claim(ofw_t ofw, void *virt, size_t size, u_int align) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t virt; cell_t size; cell_t align; cell_t baseaddr; } args; args.name = (cell_t)(uintptr_t)"claim"; args.nargs = 3; args.nreturns = 1; args.virt = (cell_t)(uintptr_t)virt; args.size = size; args.align = align; ofw_real_start(); argsptr = ofw_real_map(&args, sizeof(args)); if (openfirmware((void *)argsptr) == -1) { ofw_real_stop(); return ((void *)-1); } ofw_real_unmap(argsptr, &args, sizeof(args)); ofw_real_stop(); return ((void *)(uintptr_t)args.baseaddr); } /* Release an area of memory. */ static void ofw_real_release(ofw_t ofw, void *virt, size_t size) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t virt; cell_t size; } args; args.name = (cell_t)(uintptr_t)"release"; args.nargs = 2; args.nreturns = 0; args.virt = (cell_t)(uintptr_t)virt; args.size = size; ofw_real_start(); argsptr = ofw_real_map(&args, sizeof(args)); openfirmware((void *)argsptr); ofw_real_stop(); } /* * Control transfer functions */ /* Suspend and drop back to the Open Firmware interface. */ static void ofw_real_enter(ofw_t ofw) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; } args; args.name = (cell_t)(uintptr_t)"enter"; args.nargs = 0; args.nreturns = 0; ofw_real_start(); argsptr = ofw_real_map(&args, sizeof(args)); openfirmware((void *)argsptr); /* We may come back. */ ofw_real_stop(); } /* Shut down and drop back to the Open Firmware interface. */ static void ofw_real_exit(ofw_t ofw) { vm_offset_t argsptr; struct { cell_t name; cell_t nargs; cell_t nreturns; } args; args.name = (cell_t)(uintptr_t)"exit"; args.nargs = 0; args.nreturns = 0; ofw_real_start(); argsptr = ofw_real_map(&args, sizeof(args)); openfirmware((void *)argsptr); for (;;) /* just in case */ ; ofw_real_stop(); } Index: head/sys/powerpc/ofw/ofw_syscons.c =================================================================== --- head/sys/powerpc/ofw/ofw_syscons.c (revision 326260) +++ head/sys/powerpc/ofw/ofw_syscons.c (revision 326261) @@ -1,1102 +1,1104 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2003 Peter Grehan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int ofwfb_ignore_mmap_checks = 1; static int ofwfb_reset_on_switch = 1; static SYSCTL_NODE(_hw, OID_AUTO, ofwfb, CTLFLAG_RD, 0, "ofwfb"); SYSCTL_INT(_hw_ofwfb, OID_AUTO, relax_mmap, CTLFLAG_RW, &ofwfb_ignore_mmap_checks, 0, "relaxed mmap bounds checking"); SYSCTL_INT(_hw_ofwfb, OID_AUTO, reset_on_mode_switch, CTLFLAG_RW, &ofwfb_reset_on_switch, 0, "reset the framebuffer driver on mode switch"); extern u_char dflt_font_16[]; extern u_char dflt_font_14[]; extern u_char dflt_font_8[]; static int ofwfb_configure(int flags); static vi_probe_t ofwfb_probe; static vi_init_t ofwfb_init; static vi_get_info_t ofwfb_get_info; static vi_query_mode_t ofwfb_query_mode; static vi_set_mode_t ofwfb_set_mode; static vi_save_font_t ofwfb_save_font; static vi_load_font_t ofwfb_load_font; static vi_show_font_t ofwfb_show_font; static vi_save_palette_t ofwfb_save_palette; static vi_load_palette_t ofwfb_load_palette; static vi_set_border_t ofwfb_set_border; static vi_save_state_t ofwfb_save_state; static vi_load_state_t ofwfb_load_state; static vi_set_win_org_t ofwfb_set_win_org; static vi_read_hw_cursor_t ofwfb_read_hw_cursor; static vi_set_hw_cursor_t ofwfb_set_hw_cursor; static vi_set_hw_cursor_shape_t ofwfb_set_hw_cursor_shape; static vi_blank_display_t ofwfb_blank_display; static vi_mmap_t ofwfb_mmap; static vi_ioctl_t ofwfb_ioctl; static vi_clear_t ofwfb_clear; static vi_fill_rect_t ofwfb_fill_rect; static vi_bitblt_t ofwfb_bitblt; static vi_diag_t ofwfb_diag; static vi_save_cursor_palette_t ofwfb_save_cursor_palette; static vi_load_cursor_palette_t ofwfb_load_cursor_palette; static vi_copy_t ofwfb_copy; static vi_putp_t ofwfb_putp; static vi_putc_t ofwfb_putc; static vi_puts_t ofwfb_puts; static vi_putm_t ofwfb_putm; static video_switch_t ofwfbvidsw = { .probe = ofwfb_probe, .init = ofwfb_init, .get_info = ofwfb_get_info, .query_mode = ofwfb_query_mode, .set_mode = ofwfb_set_mode, .save_font = ofwfb_save_font, .load_font = ofwfb_load_font, .show_font = ofwfb_show_font, .save_palette = ofwfb_save_palette, .load_palette = ofwfb_load_palette, .set_border = ofwfb_set_border, .save_state = ofwfb_save_state, .load_state = ofwfb_load_state, .set_win_org = ofwfb_set_win_org, .read_hw_cursor = ofwfb_read_hw_cursor, .set_hw_cursor = ofwfb_set_hw_cursor, .set_hw_cursor_shape = ofwfb_set_hw_cursor_shape, .blank_display = ofwfb_blank_display, .mmap = ofwfb_mmap, .ioctl = ofwfb_ioctl, .clear = ofwfb_clear, .fill_rect = ofwfb_fill_rect, .bitblt = ofwfb_bitblt, .diag = ofwfb_diag, .save_cursor_palette = ofwfb_save_cursor_palette, .load_cursor_palette = ofwfb_load_cursor_palette, .copy = ofwfb_copy, .putp = ofwfb_putp, .putc = ofwfb_putc, .puts = ofwfb_puts, .putm = ofwfb_putm, }; /* * bitmap depth-specific routines */ static vi_blank_display_t ofwfb_blank_display8; static vi_putc_t ofwfb_putc8; static vi_putm_t ofwfb_putm8; static vi_set_border_t ofwfb_set_border8; static vi_blank_display_t ofwfb_blank_display32; static vi_putc_t ofwfb_putc32; static vi_putm_t ofwfb_putm32; static vi_set_border_t ofwfb_set_border32; VIDEO_DRIVER(ofwfb, ofwfbvidsw, ofwfb_configure); extern sc_rndr_sw_t txtrndrsw; RENDERER(ofwfb, 0, txtrndrsw, gfb_set); RENDERER_MODULE(ofwfb, gfb_set); /* * Define the iso6429-1983 colormap */ static struct { uint8_t red; uint8_t green; uint8_t blue; } ofwfb_cmap[16] = { /* # R G B Color */ /* - - - - ----- */ { 0x00, 0x00, 0x00 }, /* 0 0 0 0 Black */ { 0x00, 0x00, 0xaa }, /* 1 0 0 2/3 Blue */ { 0x00, 0xaa, 0x00 }, /* 2 0 2/3 0 Green */ { 0x00, 0xaa, 0xaa }, /* 3 0 2/3 2/3 Cyan */ { 0xaa, 0x00, 0x00 }, /* 4 2/3 0 0 Red */ { 0xaa, 0x00, 0xaa }, /* 5 2/3 0 2/3 Magenta */ { 0xaa, 0x55, 0x00 }, /* 6 2/3 1/3 0 Brown */ { 0xaa, 0xaa, 0xaa }, /* 7 2/3 2/3 2/3 White */ { 0x55, 0x55, 0x55 }, /* 8 1/3 1/3 1/3 Gray */ { 0x55, 0x55, 0xff }, /* 9 1/3 1/3 1 Bright Blue */ { 0x55, 0xff, 0x55 }, /* 10 1/3 1 1/3 Bright Green */ { 0x55, 0xff, 0xff }, /* 11 1/3 1 1 Bright Cyan */ { 0xff, 0x55, 0x55 }, /* 12 1 1/3 1/3 Bright Red */ { 0xff, 0x55, 0xff }, /* 13 1 1/3 1 Bright Magenta */ { 0xff, 0xff, 0x80 }, /* 14 1 1 1/3 Bright Yellow */ { 0xff, 0xff, 0xff } /* 15 1 1 1 Bright White */ }; #define TODO printf("%s: unimplemented\n", __func__) static u_int16_t ofwfb_static_window[ROW*COL]; static struct ofwfb_softc ofwfb_softc; static __inline int ofwfb_background(uint8_t attr) { return (attr >> 4); } static __inline int ofwfb_foreground(uint8_t attr) { return (attr & 0x0f); } static u_int ofwfb_pix32(struct ofwfb_softc *sc, int attr) { u_int retval; if (sc->sc_tag == &bs_le_tag) retval = (ofwfb_cmap[attr].red << 16) | (ofwfb_cmap[attr].green << 8) | ofwfb_cmap[attr].blue; else retval = (ofwfb_cmap[attr].blue << 16) | (ofwfb_cmap[attr].green << 8) | ofwfb_cmap[attr].red; return (retval); } static int ofwfb_configure(int flags) { struct ofwfb_softc *sc; phandle_t chosen; ihandle_t stdout; phandle_t node; uint32_t fb_phys; int depth; int disable; int len; int i; char type[16]; static int done = 0; disable = 0; TUNABLE_INT_FETCH("hw.syscons.disable", &disable); if (disable != 0) return (0); if (done != 0) return (0); done = 1; sc = &ofwfb_softc; chosen = OF_finddevice("/chosen"); OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)); node = OF_instance_to_package(stdout); if (node == -1) { /* * The "/chosen/stdout" does not exist try * using "screen" directly. */ node = OF_finddevice("screen"); } OF_getprop(node, "device_type", type, sizeof(type)); if (strcmp(type, "display") != 0) return (0); /* Only support 8 and 32-bit framebuffers */ OF_getprop(node, "depth", &depth, sizeof(depth)); if (depth == 8) { sc->sc_blank = ofwfb_blank_display8; sc->sc_putc = ofwfb_putc8; sc->sc_putm = ofwfb_putm8; sc->sc_set_border = ofwfb_set_border8; } else if (depth == 32) { sc->sc_blank = ofwfb_blank_display32; sc->sc_putc = ofwfb_putc32; sc->sc_putm = ofwfb_putm32; sc->sc_set_border = ofwfb_set_border32; } else return (0); if (OF_getproplen(node, "height") != sizeof(sc->sc_height) || OF_getproplen(node, "width") != sizeof(sc->sc_width) || OF_getproplen(node, "linebytes") != sizeof(sc->sc_stride)) return (0); sc->sc_depth = depth; sc->sc_node = node; sc->sc_console = 1; OF_getprop(node, "height", &sc->sc_height, sizeof(sc->sc_height)); OF_getprop(node, "width", &sc->sc_width, sizeof(sc->sc_width)); OF_getprop(node, "linebytes", &sc->sc_stride, sizeof(sc->sc_stride)); /* * Get the PCI addresses of the adapter. The node may be the * child of the PCI device: in that case, try the parent for * the assigned-addresses property. */ len = OF_getprop(node, "assigned-addresses", sc->sc_pciaddrs, sizeof(sc->sc_pciaddrs)); if (len == -1) { len = OF_getprop(OF_parent(node), "assigned-addresses", sc->sc_pciaddrs, sizeof(sc->sc_pciaddrs)); } if (len == -1) len = 0; sc->sc_num_pciaddrs = len / sizeof(struct ofw_pci_register); /* * Grab the physical address of the framebuffer, and then map it * into our memory space. If the MMU is not yet up, it will be * remapped for us when relocation turns on. * * XXX We assume #address-cells is 1 at this point. */ if (OF_getproplen(node, "address") == sizeof(fb_phys)) { OF_getprop(node, "address", &fb_phys, sizeof(fb_phys)); sc->sc_tag = &bs_be_tag; bus_space_map(sc->sc_tag, fb_phys, sc->sc_height * sc->sc_stride, BUS_SPACE_MAP_PREFETCHABLE, &sc->sc_addr); } else { /* * Some IBM systems don't have an address property. Try to * guess the framebuffer region from the assigned addresses. * This is ugly, but there doesn't seem to be an alternative. * Linux does the same thing. */ fb_phys = sc->sc_num_pciaddrs; for (i = 0; i < sc->sc_num_pciaddrs; i++) { /* If it is too small, not the framebuffer */ if (sc->sc_pciaddrs[i].size_lo < sc->sc_stride*sc->sc_height) continue; /* If it is not memory, it isn't either */ if (!(sc->sc_pciaddrs[i].phys_hi & OFW_PCI_PHYS_HI_SPACE_MEM32)) continue; /* This could be the framebuffer */ fb_phys = i; /* If it is prefetchable, it certainly is */ if (sc->sc_pciaddrs[i].phys_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) break; } if (fb_phys == sc->sc_num_pciaddrs) return (0); OF_decode_addr(node, fb_phys, &sc->sc_tag, &sc->sc_addr, NULL); } ofwfb_init(0, &sc->sc_va, 0); return (0); } static int ofwfb_probe(int unit, video_adapter_t **adp, void *arg, int flags) { TODO; return (0); } static int ofwfb_init(int unit, video_adapter_t *adp, int flags) { struct ofwfb_softc *sc; video_info_t *vi; int cborder; int font_height; sc = (struct ofwfb_softc *)adp; vi = &adp->va_info; vid_init_struct(adp, "ofwfb", -1, unit); /* The default font size can be overridden by loader */ font_height = 16; TUNABLE_INT_FETCH("hw.syscons.fsize", &font_height); if (font_height == 8) { sc->sc_font = dflt_font_8; sc->sc_font_height = 8; } else if (font_height == 14) { sc->sc_font = dflt_font_14; sc->sc_font_height = 14; } else { /* default is 8x16 */ sc->sc_font = dflt_font_16; sc->sc_font_height = 16; } /* The user can set a border in chars - default is 1 char width */ cborder = 1; TUNABLE_INT_FETCH("hw.syscons.border", &cborder); vi->vi_cheight = sc->sc_font_height; vi->vi_width = sc->sc_width/8 - 2*cborder; vi->vi_height = sc->sc_height/sc->sc_font_height - 2*cborder; vi->vi_cwidth = 8; /* * Clamp width/height to syscons maximums */ if (vi->vi_width > COL) vi->vi_width = COL; if (vi->vi_height > ROW) vi->vi_height = ROW; sc->sc_xmargin = (sc->sc_width - (vi->vi_width * vi->vi_cwidth)) / 2; sc->sc_ymargin = (sc->sc_height - (vi->vi_height * vi->vi_cheight))/2; /* * Avoid huge amounts of conditional code in syscons by * defining a dummy h/w text display buffer. */ adp->va_window = (vm_offset_t) ofwfb_static_window; /* * Enable future font-loading and flag color support, as well as * adding V_ADP_MODECHANGE so that we ofwfb_set_mode() gets called * when the X server shuts down. This enables us to get the console * back when X disappears. */ adp->va_flags |= V_ADP_FONT | V_ADP_COLOR | V_ADP_MODECHANGE; ofwfb_set_mode(&sc->sc_va, 0); vid_register(&sc->sc_va); return (0); } static int ofwfb_get_info(video_adapter_t *adp, int mode, video_info_t *info) { bcopy(&adp->va_info, info, sizeof(*info)); return (0); } static int ofwfb_query_mode(video_adapter_t *adp, video_info_t *info) { TODO; return (0); } static int ofwfb_set_mode(video_adapter_t *adp, int mode) { struct ofwfb_softc *sc; char name[64]; ihandle_t ih; int i, retval; sc = (struct ofwfb_softc *)adp; if (ofwfb_reset_on_switch) { /* * Open the display device, which will initialize it. */ memset(name, 0, sizeof(name)); OF_package_to_path(sc->sc_node, name, sizeof(name)); ih = OF_open(name); if (sc->sc_depth == 8) { /* * Install the ISO6429 colormap - older OFW systems * don't do this by default */ for (i = 0; i < 16; i++) { OF_call_method("color!", ih, 4, 1, ofwfb_cmap[i].red, ofwfb_cmap[i].green, ofwfb_cmap[i].blue, i, &retval); } } } ofwfb_blank_display(&sc->sc_va, V_DISPLAY_ON); return (0); } static int ofwfb_save_font(video_adapter_t *adp, int page, int size, int width, u_char *data, int c, int count) { TODO; return (0); } static int ofwfb_load_font(video_adapter_t *adp, int page, int size, int width, u_char *data, int c, int count) { struct ofwfb_softc *sc; sc = (struct ofwfb_softc *)adp; /* * syscons code has already determined that current width/height * are unchanged for this new font */ sc->sc_font = data; return (0); } static int ofwfb_show_font(video_adapter_t *adp, int page) { return (0); } static int ofwfb_save_palette(video_adapter_t *adp, u_char *palette) { /* TODO; */ return (0); } static int ofwfb_load_palette(video_adapter_t *adp, u_char *palette) { /* TODO; */ return (0); } static int ofwfb_set_border8(video_adapter_t *adp, int border) { struct ofwfb_softc *sc; int i, j; uint8_t *addr; uint8_t bground; sc = (struct ofwfb_softc *)adp; bground = ofwfb_background(border); /* Set top margin */ addr = (uint8_t *) sc->sc_addr; for (i = 0; i < sc->sc_ymargin; i++) { for (j = 0; j < sc->sc_width; j++) { *(addr + j) = bground; } addr += sc->sc_stride; } /* bottom margin */ addr = (uint8_t *) sc->sc_addr + (sc->sc_height - sc->sc_ymargin)*sc->sc_stride; for (i = 0; i < sc->sc_ymargin; i++) { for (j = 0; j < sc->sc_width; j++) { *(addr + j) = bground; } addr += sc->sc_stride; } /* remaining left and right borders */ addr = (uint8_t *) sc->sc_addr + sc->sc_ymargin*sc->sc_stride; for (i = 0; i < sc->sc_height - 2*sc->sc_xmargin; i++) { for (j = 0; j < sc->sc_xmargin; j++) { *(addr + j) = bground; *(addr + j + sc->sc_width - sc->sc_xmargin) = bground; } addr += sc->sc_stride; } return (0); } static int ofwfb_set_border32(video_adapter_t *adp, int border) { /* XXX Be lazy for now and blank entire screen */ return (ofwfb_blank_display32(adp, border)); } static int ofwfb_set_border(video_adapter_t *adp, int border) { struct ofwfb_softc *sc; sc = (struct ofwfb_softc *)adp; return ((*sc->sc_set_border)(adp, border)); } static int ofwfb_save_state(video_adapter_t *adp, void *p, size_t size) { TODO; return (0); } static int ofwfb_load_state(video_adapter_t *adp, void *p) { TODO; return (0); } static int ofwfb_set_win_org(video_adapter_t *adp, off_t offset) { TODO; return (0); } static int ofwfb_read_hw_cursor(video_adapter_t *adp, int *col, int *row) { *col = 0; *row = 0; return (0); } static int ofwfb_set_hw_cursor(video_adapter_t *adp, int col, int row) { return (0); } static int ofwfb_set_hw_cursor_shape(video_adapter_t *adp, int base, int height, int celsize, int blink) { return (0); } static int ofwfb_blank_display8(video_adapter_t *adp, int mode) { struct ofwfb_softc *sc; int i; uint32_t *addr; uint32_t color; uint32_t end; sc = (struct ofwfb_softc *)adp; addr = (uint32_t *) sc->sc_addr; end = (sc->sc_stride/4) * sc->sc_height; /* Splat 4 pixels at once. */ color = (ofwfb_background(SC_NORM_ATTR) << 24) | (ofwfb_background(SC_NORM_ATTR) << 16) | (ofwfb_background(SC_NORM_ATTR) << 8) | (ofwfb_background(SC_NORM_ATTR)); for (i = 0; i < end; i++) *(addr + i) = color; return (0); } static int ofwfb_blank_display32(video_adapter_t *adp, int mode) { struct ofwfb_softc *sc; int i; uint32_t *addr, blank; sc = (struct ofwfb_softc *)adp; addr = (uint32_t *) sc->sc_addr; blank = ofwfb_pix32(sc, ofwfb_background(SC_NORM_ATTR)); for (i = 0; i < (sc->sc_stride/4)*sc->sc_height; i++) *(addr + i) = blank; return (0); } static int ofwfb_blank_display(video_adapter_t *adp, int mode) { struct ofwfb_softc *sc; sc = (struct ofwfb_softc *)adp; return ((*sc->sc_blank)(adp, mode)); } static int ofwfb_mmap(video_adapter_t *adp, vm_ooffset_t offset, vm_paddr_t *paddr, int prot, vm_memattr_t *memattr) { struct ofwfb_softc *sc; int i; sc = (struct ofwfb_softc *)adp; /* * Make sure the requested address lies within the PCI device's * assigned addrs */ for (i = 0; i < sc->sc_num_pciaddrs; i++) if (offset >= sc->sc_pciaddrs[i].phys_lo && offset < (sc->sc_pciaddrs[i].phys_lo + sc->sc_pciaddrs[i].size_lo)) { /* * If this is a prefetchable BAR, we can (and should) * enable write-combining. */ if (sc->sc_pciaddrs[i].phys_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) *memattr = VM_MEMATTR_WRITE_COMBINING; *paddr = offset; return (0); } /* * Hack for Radeon... */ if (ofwfb_ignore_mmap_checks) { *paddr = offset; return (0); } /* * This might be a legacy VGA mem request: if so, just point it at the * framebuffer, since it shouldn't be touched */ if (offset < sc->sc_stride*sc->sc_height) { *paddr = sc->sc_addr + offset; return (0); } /* * Error if we didn't have a better idea. */ if (sc->sc_num_pciaddrs == 0) return (ENOMEM); return (EINVAL); } static int ofwfb_ioctl(video_adapter_t *adp, u_long cmd, caddr_t data) { return (0); } static int ofwfb_clear(video_adapter_t *adp) { TODO; return (0); } static int ofwfb_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy) { TODO; return (0); } static int ofwfb_bitblt(video_adapter_t *adp, ...) { TODO; return (0); } static int ofwfb_diag(video_adapter_t *adp, int level) { TODO; return (0); } static int ofwfb_save_cursor_palette(video_adapter_t *adp, u_char *palette) { TODO; return (0); } static int ofwfb_load_cursor_palette(video_adapter_t *adp, u_char *palette) { TODO; return (0); } static int ofwfb_copy(video_adapter_t *adp, vm_offset_t src, vm_offset_t dst, int n) { TODO; return (0); } static int ofwfb_putp(video_adapter_t *adp, vm_offset_t off, uint32_t p, uint32_t a, int size, int bpp, int bit_ltor, int byte_ltor) { TODO; return (0); } static int ofwfb_putc8(video_adapter_t *adp, vm_offset_t off, uint8_t c, uint8_t a) { struct ofwfb_softc *sc; int row; int col; int i; uint32_t *addr; u_char *p, fg, bg; union { uint32_t l; uint8_t c[4]; } ch1, ch2; sc = (struct ofwfb_softc *)adp; row = (off / adp->va_info.vi_width) * adp->va_info.vi_cheight; col = (off % adp->va_info.vi_width) * adp->va_info.vi_cwidth; p = sc->sc_font + c*sc->sc_font_height; addr = (u_int32_t *)((uintptr_t)sc->sc_addr + (row + sc->sc_ymargin)*sc->sc_stride + col + sc->sc_xmargin); fg = ofwfb_foreground(a); bg = ofwfb_background(a); for (i = 0; i < sc->sc_font_height; i++) { u_char fline = p[i]; /* * Assume that there is more background than foreground * in characters and init accordingly */ ch1.l = ch2.l = (bg << 24) | (bg << 16) | (bg << 8) | bg; /* * Calculate 2 x 4-chars at a time, and then * write these out. */ if (fline & 0x80) ch1.c[0] = fg; if (fline & 0x40) ch1.c[1] = fg; if (fline & 0x20) ch1.c[2] = fg; if (fline & 0x10) ch1.c[3] = fg; if (fline & 0x08) ch2.c[0] = fg; if (fline & 0x04) ch2.c[1] = fg; if (fline & 0x02) ch2.c[2] = fg; if (fline & 0x01) ch2.c[3] = fg; addr[0] = ch1.l; addr[1] = ch2.l; addr += (sc->sc_stride / sizeof(u_int32_t)); } return (0); } static int ofwfb_putc32(video_adapter_t *adp, vm_offset_t off, uint8_t c, uint8_t a) { struct ofwfb_softc *sc; int row; int col; int i, j, k; uint32_t *addr, fg, bg; u_char *p; sc = (struct ofwfb_softc *)adp; row = (off / adp->va_info.vi_width) * adp->va_info.vi_cheight; col = (off % adp->va_info.vi_width) * adp->va_info.vi_cwidth; p = sc->sc_font + c*sc->sc_font_height; addr = (uint32_t *)sc->sc_addr + (row + sc->sc_ymargin)*(sc->sc_stride/4) + col + sc->sc_xmargin; fg = ofwfb_pix32(sc, ofwfb_foreground(a)); bg = ofwfb_pix32(sc, ofwfb_background(a)); for (i = 0; i < sc->sc_font_height; i++) { for (j = 0, k = 7; j < 8; j++, k--) { if ((p[i] & (1 << k)) == 0) *(addr + j) = bg; else *(addr + j) = fg; } addr += (sc->sc_stride/4); } return (0); } static int ofwfb_putc(video_adapter_t *adp, vm_offset_t off, uint8_t c, uint8_t a) { struct ofwfb_softc *sc; sc = (struct ofwfb_softc *)adp; return ((*sc->sc_putc)(adp, off, c, a)); } static int ofwfb_puts(video_adapter_t *adp, vm_offset_t off, u_int16_t *s, int len) { int i; for (i = 0; i < len; i++) { ofwfb_putc(adp, off + i, s[i] & 0xff, (s[i] & 0xff00) >> 8); } return (0); } static int ofwfb_putm(video_adapter_t *adp, int x, int y, uint8_t *pixel_image, uint32_t pixel_mask, int size, int width) { struct ofwfb_softc *sc; sc = (struct ofwfb_softc *)adp; return ((*sc->sc_putm)(adp, x, y, pixel_image, pixel_mask, size, width)); } static int ofwfb_putm8(video_adapter_t *adp, int x, int y, uint8_t *pixel_image, uint32_t pixel_mask, int size, int width) { struct ofwfb_softc *sc; int i, j, k; uint8_t *addr; u_char fg, bg; sc = (struct ofwfb_softc *)adp; addr = (u_int8_t *)((uintptr_t)sc->sc_addr + (y + sc->sc_ymargin)*sc->sc_stride + x + sc->sc_xmargin); fg = ofwfb_foreground(SC_NORM_ATTR); bg = ofwfb_background(SC_NORM_ATTR); for (i = 0; i < size && i+y < sc->sc_height - 2*sc->sc_ymargin; i++) { /* * Calculate 2 x 4-chars at a time, and then * write these out. */ for (j = 0, k = width; j < 8; j++, k--) { if (x + j >= sc->sc_width - 2*sc->sc_xmargin) continue; if (pixel_image[i] & (1 << k)) addr[j] = (addr[j] == fg) ? bg : fg; } addr += (sc->sc_stride / sizeof(u_int8_t)); } return (0); } static int ofwfb_putm32(video_adapter_t *adp, int x, int y, uint8_t *pixel_image, uint32_t pixel_mask, int size, int width) { struct ofwfb_softc *sc; int i, j, k; uint32_t fg, bg; uint32_t *addr; sc = (struct ofwfb_softc *)adp; addr = (uint32_t *)sc->sc_addr + (y + sc->sc_ymargin)*(sc->sc_stride/4) + x + sc->sc_xmargin; fg = ofwfb_pix32(sc, ofwfb_foreground(SC_NORM_ATTR)); bg = ofwfb_pix32(sc, ofwfb_background(SC_NORM_ATTR)); for (i = 0; i < size && i+y < sc->sc_height - 2*sc->sc_ymargin; i++) { for (j = 0, k = width; j < 8; j++, k--) { if (x + j >= sc->sc_width - 2*sc->sc_xmargin) continue; if (pixel_image[i] & (1 << k)) *(addr + j) = (*(addr + j) == fg) ? bg : fg; } addr += (sc->sc_stride/4); } return (0); } /* * Define the syscons nexus device attachment */ static void ofwfb_scidentify(driver_t *driver, device_t parent) { device_t child; /* * Add with a priority guaranteed to make it last on * the device list */ child = BUS_ADD_CHILD(parent, INT_MAX, SC_DRIVER_NAME, 0); } static int ofwfb_scprobe(device_t dev) { int error; device_set_desc(dev, "System console"); error = sc_probe_unit(device_get_unit(dev), device_get_flags(dev) | SC_AUTODETECT_KBD); if (error != 0) return (error); /* This is a fake device, so make sure we added it ourselves */ return (BUS_PROBE_NOWILDCARD); } static int ofwfb_scattach(device_t dev) { return (sc_attach_unit(device_get_unit(dev), device_get_flags(dev) | SC_AUTODETECT_KBD)); } static device_method_t ofwfb_sc_methods[] = { DEVMETHOD(device_identify, ofwfb_scidentify), DEVMETHOD(device_probe, ofwfb_scprobe), DEVMETHOD(device_attach, ofwfb_scattach), { 0, 0 } }; static driver_t ofwfb_sc_driver = { SC_DRIVER_NAME, ofwfb_sc_methods, sizeof(sc_softc_t), }; static devclass_t sc_devclass; DRIVER_MODULE(ofwfb, nexus, ofwfb_sc_driver, sc_devclass, 0, 0); /* * Define a stub keyboard driver in case one hasn't been * compiled into the kernel */ #include #include static int dummy_kbd_configure(int flags); keyboard_switch_t dummysw; static int dummy_kbd_configure(int flags) { return (0); } KEYBOARD_DRIVER(dummy, dummysw, dummy_kbd_configure); /* * Utility routines from */ void ofwfb_bcopy(const void *s, void *d, size_t c) { bcopy(s, d, c); } void ofwfb_bzero(void *d, size_t c) { bzero(d, c); } void ofwfb_fillw(int pat, void *base, size_t cnt) { u_int16_t *bptr = base; while (cnt--) *bptr++ = pat; } u_int16_t ofwfb_readw(u_int16_t *addr) { return (*addr); } void ofwfb_writew(u_int16_t *addr, u_int16_t val) { *addr = val; } Index: head/sys/powerpc/ofw/ofw_syscons.h =================================================================== --- head/sys/powerpc/ofw/ofw_syscons.h (revision 326260) +++ head/sys/powerpc/ofw/ofw_syscons.h (revision 326261) @@ -1,63 +1,65 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2003 Peter Grehan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _OFW_SYSCONS_H_ #define _OFW_SYSCONS_H_ struct ofwfb_softc { video_adapter_t sc_va; struct cdev *sc_si; bus_space_tag_t sc_tag; phandle_t sc_node; int sc_console; intptr_t sc_addr; int sc_depth; int sc_height; int sc_width; int sc_stride; int sc_ncol; int sc_nrow; int sc_xmargin; int sc_ymargin; u_char *sc_font; int sc_font_height; vi_blank_display_t *sc_blank; vi_putc_t *sc_putc; vi_putm_t *sc_putm; vi_set_border_t *sc_set_border; #define OFWSC_MAXADDR 8 int sc_num_pciaddrs; struct ofw_pci_register sc_pciaddrs[OFWSC_MAXADDR]; }; #endif Index: head/sys/powerpc/ofw/openpic_ofw.c =================================================================== --- head/sys/powerpc/ofw/openpic_ofw.c (revision 326260) +++ head/sys/powerpc/ofw/openpic_ofw.c (revision 326261) @@ -1,168 +1,170 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2003 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" /* * OFW interface */ static int openpic_ofw_probe(device_t); static int openpic_ofw_attach(device_t); static void openpic_ofw_translate_code(device_t, u_int irq, int code, enum intr_trigger *trig, enum intr_polarity *pol); static device_method_t openpic_ofw_methods[] = { /* Device interface */ DEVMETHOD(device_probe, openpic_ofw_probe), DEVMETHOD(device_attach, openpic_ofw_attach), DEVMETHOD(device_suspend, openpic_suspend), DEVMETHOD(device_resume, openpic_resume), /* PIC interface */ DEVMETHOD(pic_bind, openpic_bind), DEVMETHOD(pic_config, openpic_config), DEVMETHOD(pic_dispatch, openpic_dispatch), DEVMETHOD(pic_enable, openpic_enable), DEVMETHOD(pic_eoi, openpic_eoi), DEVMETHOD(pic_ipi, openpic_ipi), DEVMETHOD(pic_mask, openpic_mask), DEVMETHOD(pic_unmask, openpic_unmask), DEVMETHOD(pic_translate_code, openpic_ofw_translate_code), DEVMETHOD_END }; static driver_t openpic_ofw_driver = { "openpic", openpic_ofw_methods, sizeof(struct openpic_softc), }; DRIVER_MODULE(openpic, ofwbus, openpic_ofw_driver, openpic_devclass, 0, 0); DRIVER_MODULE(openpic, simplebus, openpic_ofw_driver, openpic_devclass, 0, 0); DRIVER_MODULE(openpic, macio, openpic_ofw_driver, openpic_devclass, 0, 0); static int openpic_ofw_probe(device_t dev) { const char *type = ofw_bus_get_type(dev); if (type == NULL) return (ENXIO); if (!ofw_bus_is_compatible(dev, "chrp,open-pic") && strcmp(type, "open-pic") != 0) return (ENXIO); /* * On some U4 systems, there is a phantom MPIC in the mac-io cell. * The uninorth driver will pick up the real PIC, so ignore it here. */ if (OF_finddevice("/u4") != (phandle_t)-1) return (ENXIO); device_set_desc(dev, OPENPIC_DEVSTR); return (0); } static int openpic_ofw_attach(device_t dev) { phandle_t xref, node; node = ofw_bus_get_node(dev); if (OF_getencprop(node, "phandle", &xref, sizeof(xref)) == -1 && OF_getencprop(node, "ibm,phandle", &xref, sizeof(xref)) == -1 && OF_getencprop(node, "linux,phandle", &xref, sizeof(xref)) == -1) xref = node; return (openpic_common_attach(dev, xref)); } static void openpic_ofw_translate_code(device_t dev, u_int irq, int code, enum intr_trigger *trig, enum intr_polarity *pol) { switch (code) { case 0: /* L to H edge */ *trig = INTR_TRIGGER_EDGE; *pol = INTR_POLARITY_HIGH; break; case 1: /* Active L level */ *trig = INTR_TRIGGER_LEVEL; *pol = INTR_POLARITY_LOW; break; case 2: /* Active H level */ *trig = INTR_TRIGGER_LEVEL; *pol = INTR_POLARITY_HIGH; break; case 3: /* H to L edge */ *trig = INTR_TRIGGER_EDGE; *pol = INTR_POLARITY_LOW; break; default: *trig = INTR_TRIGGER_CONFORM; *pol = INTR_POLARITY_CONFORM; } } Index: head/sys/powerpc/ofw/rtas.c =================================================================== --- head/sys/powerpc/ofw/rtas.c (revision 326260) +++ head/sys/powerpc/ofw/rtas.c (revision 326261) @@ -1,270 +1,272 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_RTAS, "rtas", "Run Time Abstraction Service"); static vm_offset_t rtas_bounce_phys; static caddr_t rtas_bounce_virt; static off_t rtas_bounce_offset; static size_t rtas_bounce_size; static uintptr_t rtas_private_data; static struct mtx rtas_mtx; static phandle_t rtas; /* From ofwcall.S */ int rtascall(vm_offset_t callbuffer, uintptr_t rtas_privdat); extern uintptr_t rtas_entry; extern register_t rtasmsr; /* * After the VM is up, allocate RTAS memory and instantiate it */ static void rtas_setup(void *); SYSINIT(rtas_setup, SI_SUB_KMEM, SI_ORDER_ANY, rtas_setup, NULL); static void rtas_setup(void *junk) { ihandle_t rtasi; cell_t rtas_size = 0, rtas_ptr; char path[31]; int result; rtas = OF_finddevice("/rtas"); if (rtas == -1) { rtas = 0; return; } OF_package_to_path(rtas, path, sizeof(path)); mtx_init(&rtas_mtx, "RTAS", NULL, MTX_SPIN); /* RTAS must be called with everything turned off in MSR */ rtasmsr = mfmsr(); rtasmsr &= ~(PSL_IR | PSL_DR | PSL_EE | PSL_SE); #ifdef __powerpc64__ rtasmsr &= ~PSL_SF; #endif /* * Allocate rtas_size + one page of contiguous, wired physical memory * that can fit into a 32-bit address space and accessed from real mode. * This is used both to bounce arguments and for RTAS private data. * * It must be 4KB-aligned and not cross a 256 MB boundary. */ OF_getencprop(rtas, "rtas-size", &rtas_size, sizeof(rtas_size)); rtas_size = round_page(rtas_size); rtas_bounce_virt = contigmalloc(rtas_size + PAGE_SIZE, M_RTAS, 0, 0, ulmin(platform_real_maxaddr(), BUS_SPACE_MAXADDR_32BIT), 4096, 256*1024*1024); rtas_private_data = vtophys(rtas_bounce_virt); rtas_bounce_virt += rtas_size; /* Actual bounce area */ rtas_bounce_phys = vtophys(rtas_bounce_virt); rtas_bounce_size = PAGE_SIZE; /* * Instantiate RTAS. We always use the 32-bit version. */ if (OF_hasprop(rtas, "linux,rtas-entry") && OF_hasprop(rtas, "linux,rtas-base")) { OF_getencprop(rtas, "linux,rtas-base", &rtas_ptr, sizeof(rtas_ptr)); rtas_private_data = rtas_ptr; OF_getencprop(rtas, "linux,rtas-entry", &rtas_ptr, sizeof(rtas_ptr)); } else { rtasi = OF_open(path); if (rtasi == 0) { rtas = 0; printf("Error initializing RTAS: could not open " "node\n"); return; } result = OF_call_method("instantiate-rtas", rtasi, 1, 1, (cell_t)rtas_private_data, &rtas_ptr); OF_close(rtasi); if (result != 0) { rtas = 0; rtas_ptr = 0; printf("Error initializing RTAS (%d)\n", result); return; } } rtas_entry = (uintptr_t)(rtas_ptr); } static cell_t rtas_real_map(const void *buf, size_t len) { cell_t phys; mtx_assert(&rtas_mtx, MA_OWNED); /* * Make sure the bounce page offset satisfies any reasonable * alignment constraint. */ rtas_bounce_offset += sizeof(register_t) - (rtas_bounce_offset % sizeof(register_t)); if (rtas_bounce_offset + len > rtas_bounce_size) { panic("Oversize RTAS call!"); return 0; } if (buf != NULL) memcpy(rtas_bounce_virt + rtas_bounce_offset, buf, len); else return (0); phys = rtas_bounce_phys + rtas_bounce_offset; rtas_bounce_offset += len; return (phys); } static void rtas_real_unmap(cell_t physaddr, void *buf, size_t len) { mtx_assert(&rtas_mtx, MA_OWNED); if (physaddr == 0) return; memcpy(buf, rtas_bounce_virt + (physaddr - rtas_bounce_phys), len); } /* Check if we have RTAS */ int rtas_exists(void) { return (rtas != 0); } /* Call an RTAS method by token */ int rtas_call_method(cell_t token, int nargs, int nreturns, ...) { vm_offset_t argsptr; jmp_buf env, *oldfaultbuf; va_list ap; struct { cell_t token; cell_t nargs; cell_t nreturns; cell_t args_n_results[12]; } args; int n, result; if (!rtas_exists() || nargs + nreturns > 12) return (-1); args.token = token; va_start(ap, nreturns); mtx_lock_spin(&rtas_mtx); rtas_bounce_offset = 0; args.nargs = nargs; args.nreturns = nreturns; for (n = 0; n < nargs; n++) args.args_n_results[n] = va_arg(ap, cell_t); argsptr = rtas_real_map(&args, sizeof(args)); /* Get rid of any stale machine checks that have been waiting. */ __asm __volatile ("sync; isync"); oldfaultbuf = curthread->td_pcb->pcb_onfault; curthread->td_pcb->pcb_onfault = &env; if (!setjmp(env)) { __asm __volatile ("sync"); result = rtascall(argsptr, rtas_private_data); __asm __volatile ("sync; isync"); } else { result = RTAS_HW_ERROR; } curthread->td_pcb->pcb_onfault = oldfaultbuf; __asm __volatile ("sync"); rtas_real_unmap(argsptr, &args, sizeof(args)); mtx_unlock_spin(&rtas_mtx); if (result < 0) return (result); for (n = nargs; n < nargs + nreturns; n++) *va_arg(ap, cell_t *) = args.args_n_results[n]; return (result); } /* Look up an RTAS token */ cell_t rtas_token_lookup(const char *method) { cell_t token; if (!rtas_exists()) return (-1); if (OF_getencprop(rtas, method, &token, sizeof(token)) == -1) return (-1); return (token); } Index: head/sys/powerpc/powermac/ata_dbdma.c =================================================================== --- head/sys/powerpc/powermac/ata_dbdma.c (revision 326260) +++ head/sys/powerpc/powermac/ata_dbdma.c (revision 326261) @@ -1,286 +1,288 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2008 by Nathan Whitehorn. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("* $FreeBSD$"); /* * Common routines for the DMA engine on both the Apple Kauai and MacIO * ATA controllers. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ata_dbdma.h" struct ata_dbdma_dmaload_args { struct ata_dbdma_channel *sc; int write; int nsegs; }; static void ata_dbdma_setprd(void *xarg, bus_dma_segment_t *segs, int nsegs, int error) { struct ata_dbdma_dmaload_args *arg = xarg; struct ata_dbdma_channel *sc = arg->sc; int branch_type, command; int prev_stop; int i; mtx_lock(&sc->dbdma_mtx); prev_stop = sc->next_dma_slot-1; if (prev_stop < 0) prev_stop = 0xff; for (i = 0; i < nsegs; i++) { /* Loop back to the beginning if this is our last slot */ if (sc->next_dma_slot == 0xff) branch_type = DBDMA_ALWAYS; else branch_type = DBDMA_NEVER; if (arg->write) { command = (i + 1 < nsegs) ? DBDMA_OUTPUT_MORE : DBDMA_OUTPUT_LAST; } else { command = (i + 1 < nsegs) ? DBDMA_INPUT_MORE : DBDMA_INPUT_LAST; } dbdma_insert_command(sc->dbdma, sc->next_dma_slot++, command, 0, segs[i].ds_addr, segs[i].ds_len, DBDMA_NEVER, branch_type, DBDMA_NEVER, 0); if (branch_type == DBDMA_ALWAYS) sc->next_dma_slot = 0; } /* We have a corner case where the STOP command is the last slot, * but you can't branch in STOP commands. So add a NOP branch here * and the STOP in slot 0. */ if (sc->next_dma_slot == 0xff) { dbdma_insert_branch(sc->dbdma, sc->next_dma_slot, 0); sc->next_dma_slot = 0; } #if 0 dbdma_insert_command(sc->dbdma, sc->next_dma_slot++, DBDMA_NOP, 0, 0, 0, DBDMA_ALWAYS, DBDMA_NEVER, DBDMA_NEVER, 0); #endif dbdma_insert_stop(sc->dbdma, sc->next_dma_slot++); dbdma_insert_nop(sc->dbdma, prev_stop); dbdma_sync_commands(sc->dbdma, BUS_DMASYNC_PREWRITE); mtx_unlock(&sc->dbdma_mtx); arg->nsegs = nsegs; } static int ata_dbdma_status(device_t dev) { struct ata_dbdma_channel *sc = device_get_softc(dev); struct ata_channel *ch = device_get_softc(dev); if (sc->sc_ch.dma.flags & ATA_DMA_ACTIVE) { return (!(dbdma_get_chan_status(sc->dbdma) & DBDMA_STATUS_ACTIVE)); } if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) { DELAY(100); if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) return 0; } return 1; } static int ata_dbdma_start(struct ata_request *request) { struct ata_dbdma_channel *sc = device_get_softc(request->parent); sc->sc_ch.dma.flags |= ATA_DMA_ACTIVE; dbdma_wake(sc->dbdma); return 0; } static void ata_dbdma_reset(device_t dev) { struct ata_dbdma_channel *sc = device_get_softc(dev); mtx_lock(&sc->dbdma_mtx); dbdma_stop(sc->dbdma); dbdma_insert_stop(sc->dbdma, 0); sc->next_dma_slot=1; dbdma_set_current_cmd(sc->dbdma, 0); sc->sc_ch.dma.flags &= ~ATA_DMA_ACTIVE; mtx_unlock(&sc->dbdma_mtx); } static int ata_dbdma_stop(struct ata_request *request) { struct ata_dbdma_channel *sc = device_get_softc(request->parent); uint16_t status; status = dbdma_get_chan_status(sc->dbdma); dbdma_pause(sc->dbdma); sc->sc_ch.dma.flags &= ~ATA_DMA_ACTIVE; if (status & DBDMA_STATUS_DEAD) { device_printf(request->parent,"DBDMA dead, resetting " "channel...\n"); ata_dbdma_reset(request->parent); return ATA_S_ERROR; } if (!(status & DBDMA_STATUS_RUN)) { device_printf(request->parent,"DBDMA confused, stop called " "when channel is not running!\n"); return ATA_S_ERROR; } if (status & DBDMA_STATUS_ACTIVE) { device_printf(request->parent,"DBDMA channel stopped " "prematurely\n"); return ATA_S_ERROR; } return 0; } static int ata_dbdma_load(struct ata_request *request, void *addr, int *entries) { struct ata_channel *ch = device_get_softc(request->parent); struct ata_dbdma_dmaload_args args; int error; args.sc = device_get_softc(request->parent); args.write = !(request->flags & ATA_R_READ); if (!request->bytecount) { device_printf(request->dev, "FAILURE - zero length DMA transfer attempted\n"); return EIO; } if (((uintptr_t)(request->data) & (ch->dma.alignment - 1)) || (request->bytecount & (ch->dma.alignment - 1))) { device_printf(request->dev, "FAILURE - non aligned DMA transfer attempted\n"); return EIO; } if (request->bytecount > ch->dma.max_iosize) { device_printf(request->dev, "FAILURE - oversized DMA transfer attempt %d > %d\n", request->bytecount, ch->dma.max_iosize); return EIO; } request->dma = &ch->dma.slot[0]; if ((error = bus_dmamap_load(request->dma->data_tag, request->dma->data_map, request->data, request->bytecount, &ata_dbdma_setprd, &args, BUS_DMA_NOWAIT))) { device_printf(request->dev, "FAILURE - load data\n"); goto error; } if (entries) *entries = args.nsegs; bus_dmamap_sync(request->dma->sg_tag, request->dma->sg_map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(request->dma->data_tag, request->dma->data_map, (request->flags & ATA_R_READ) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); return 0; error: ch->dma.unload(request); return EIO; } void ata_dbdma_dmainit(device_t dev) { struct ata_dbdma_channel *sc = device_get_softc(dev); int error; error = dbdma_allocate_channel(sc->dbdma_regs, sc->dbdma_offset, bus_get_dma_tag(dev), 256, &sc->dbdma); dbdma_set_wait_selector(sc->dbdma,1 << 7, 1 << 7); dbdma_insert_stop(sc->dbdma,0); sc->next_dma_slot=1; sc->sc_ch.dma.start = ata_dbdma_start; sc->sc_ch.dma.stop = ata_dbdma_stop; sc->sc_ch.dma.load = ata_dbdma_load; sc->sc_ch.dma.reset = ata_dbdma_reset; /* * DBDMA's field for transfer size is 16 bits. This will overflow * if we try to do a 64K transfer, so stop short of 64K. */ sc->sc_ch.dma.segsize = 126 * DEV_BSIZE; ata_dmainit(dev); sc->sc_ch.hw.status = ata_dbdma_status; mtx_init(&sc->dbdma_mtx, "ATA DBDMA", NULL, MTX_DEF); } Index: head/sys/powerpc/powermac/ata_dbdma.h =================================================================== --- head/sys/powerpc/powermac/ata_dbdma.h (revision 326260) +++ head/sys/powerpc/powermac/ata_dbdma.h (revision 326261) @@ -1,55 +1,57 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2008 by Nathan Whitehorn. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef ATA_DBDMA_H #define ATA_DBDMA_H #include #include #include #include struct ata_dbdma_channel { struct ata_channel sc_ch; int dbdma_rid; struct resource *dbdma_regs; u_int dbdma_offset; dbdma_channel_t *dbdma; int next_dma_slot; struct mtx dbdma_mtx; }; void ata_dbdma_dmainit(device_t dev); #endif /* ATA_DBDMA_H */ Index: head/sys/powerpc/powermac/ata_kauai.c =================================================================== --- head/sys/powerpc/powermac/ata_kauai.c (revision 326260) +++ head/sys/powerpc/powermac/ata_kauai.c (revision 326261) @@ -1,374 +1,376 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2004 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); /* * Mac 'Kauai' PCI ATA controller */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ata_dbdma.h" #define ATA_KAUAI_REGOFFSET 0x2000 #define ATA_KAUAI_DBDMAOFFSET 0x1000 /* * Offset to alt-control register from base */ #define ATA_KAUAI_ALTOFFSET (ATA_KAUAI_REGOFFSET + 0x160) /* * Define the gap between registers */ #define ATA_KAUAI_REGGAP 16 /* * PIO and DMA access registers */ #define PIO_CONFIG_REG (ATA_KAUAI_REGOFFSET + 0x200) #define UDMA_CONFIG_REG (ATA_KAUAI_REGOFFSET + 0x210) #define DMA_IRQ_REG (ATA_KAUAI_REGOFFSET + 0x300) #define USE_DBDMA_IRQ 0 /* * Define the kauai pci bus attachment. */ static int ata_kauai_probe(device_t dev); static int ata_kauai_attach(device_t dev); static int ata_kauai_setmode(device_t dev, int target, int mode); static int ata_kauai_begin_transaction(struct ata_request *request); static device_method_t ata_kauai_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ata_kauai_probe), DEVMETHOD(device_attach, ata_kauai_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* ATA interface */ DEVMETHOD(ata_setmode, ata_kauai_setmode), DEVMETHOD_END }; struct ata_kauai_softc { struct ata_dbdma_channel sc_ch; struct resource *sc_memr; int shasta; uint32_t udmaconf[2]; uint32_t wdmaconf[2]; uint32_t pioconf[2]; }; static driver_t ata_kauai_driver = { "ata", ata_kauai_methods, sizeof(struct ata_kauai_softc), }; DRIVER_MODULE(ata, pci, ata_kauai_driver, ata_devclass, NULL, NULL); MODULE_DEPEND(ata, ata, 1, 1, 1); /* * PCI ID search table */ static const struct kauai_pci_dev { u_int32_t kpd_devid; const char *kpd_desc; } kauai_pci_devlist[] = { { 0x0033106b, "Uninorth2 Kauai ATA Controller" }, { 0x003b106b, "Intrepid Kauai ATA Controller" }, { 0x0043106b, "K2 Kauai ATA Controller" }, { 0x0050106b, "Shasta Kauai ATA Controller" }, { 0x0069106b, "Intrepid-2 Kauai ATA Controller" }, { 0, NULL } }; /* * IDE transfer timings */ #define KAUAI_PIO_MASK 0xff000fff #define KAUAI_DMA_MASK 0x00fff000 #define KAUAI_UDMA_MASK 0x0000ffff static const u_int pio_timing_kauai[] = { 0x08000a92, /* PIO0 */ 0x0800060f, /* PIO1 */ 0x0800038b, /* PIO2 */ 0x05000249, /* PIO3 */ 0x04000148 /* PIO4 */ }; static const u_int pio_timing_shasta[] = { 0x0a000c97, /* PIO0 */ 0x07000712, /* PIO1 */ 0x040003cd, /* PIO2 */ 0x0400028b, /* PIO3 */ 0x0400010a /* PIO4 */ }; static const u_int dma_timing_kauai[] = { 0x00618000, /* WDMA0 */ 0x00209000, /* WDMA1 */ 0x00148000 /* WDMA2 */ }; static const u_int dma_timing_shasta[] = { 0x00820800, /* WDMA0 */ 0x0028b000, /* WDMA1 */ 0x001ca000 /* WDMA2 */ }; static const u_int udma_timing_kauai[] = { 0x000070c1, /* UDMA0 */ 0x00005d81, /* UDMA1 */ 0x00004a61, /* UDMA2 */ 0x00003a51, /* UDMA3 */ 0x00002a31, /* UDMA4 */ 0x00002921 /* UDMA5 */ }; static const u_int udma_timing_shasta[] = { 0x00035901, /* UDMA0 */ 0x000348b1, /* UDMA1 */ 0x00033881, /* UDMA2 */ 0x00033861, /* UDMA3 */ 0x00033841, /* UDMA4 */ 0x00033031, /* UDMA5 */ 0x00033021 /* UDMA6 */ }; static int ata_kauai_probe(device_t dev) { struct ata_kauai_softc *sc; u_int32_t devid; phandle_t node; const char *compatstring = NULL; int i, found; found = 0; devid = pci_get_devid(dev); for (i = 0; kauai_pci_devlist[i].kpd_desc != NULL; i++) { if (devid == kauai_pci_devlist[i].kpd_devid) { found = 1; device_set_desc(dev, kauai_pci_devlist[i].kpd_desc); } } if (!found) return (ENXIO); node = ofw_bus_get_node(dev); sc = device_get_softc(dev); bzero(sc, sizeof(struct ata_kauai_softc)); compatstring = ofw_bus_get_compat(dev); if (compatstring != NULL && strcmp(compatstring,"shasta-ata") == 0) sc->shasta = 1; /* Pre-K2 controllers apparently need this hack */ if (!sc->shasta && (compatstring == NULL || strcmp(compatstring, "K2-UATA") != 0)) bus_set_resource(dev, SYS_RES_IRQ, 0, 39, 1); return (ata_probe(dev)); } #if USE_DBDMA_IRQ static int ata_kauai_dma_interrupt(struct ata_kauai_softc *sc) { /* Clear the DMA interrupt bits */ bus_write_4(sc->sc_memr, DMA_IRQ_REG, 0x80000000); return ata_interrupt(sc); } #endif static int ata_kauai_attach(device_t dev) { struct ata_kauai_softc *sc = device_get_softc(dev); struct ata_channel *ch; int i, rid; #if USE_DBDMA_IRQ int dbdma_irq_rid = 1; struct resource *dbdma_irq; void *cookie; #endif ch = &sc->sc_ch.sc_ch; rid = PCIR_BARS; sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_memr == NULL) { device_printf(dev, "could not allocate memory\n"); return (ENXIO); } /* * Set up the resource vectors */ for (i = ATA_DATA; i <= ATA_COMMAND; i++) { ch->r_io[i].res = sc->sc_memr; ch->r_io[i].offset = i*ATA_KAUAI_REGGAP + ATA_KAUAI_REGOFFSET; } ch->r_io[ATA_CONTROL].res = sc->sc_memr; ch->r_io[ATA_CONTROL].offset = ATA_KAUAI_ALTOFFSET; ata_default_registers(dev); ch->unit = 0; ch->flags |= ATA_USE_16BIT; /* XXX: ATAPI DMA is unreliable. We should find out why. */ ch->flags |= ATA_NO_ATAPI_DMA; ata_generic_hw(dev); pci_enable_busmaster(dev); /* Init DMA engine */ sc->sc_ch.dbdma_rid = 1; sc->sc_ch.dbdma_regs = sc->sc_memr; sc->sc_ch.dbdma_offset = ATA_KAUAI_DBDMAOFFSET; ata_dbdma_dmainit(dev); #if USE_DBDMA_IRQ /* Bind to DBDMA interrupt as well */ if ((dbdma_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &dbdma_irq_rid, RF_SHAREABLE | RF_ACTIVE)) != NULL) { bus_setup_intr(dev, dbdma_irq, ATA_INTR_FLAGS, NULL, (driver_intr_t *)ata_kauai_dma_interrupt, sc,&cookie); } #endif /* Set up initial mode */ sc->pioconf[0] = sc->pioconf[1] = bus_read_4(sc->sc_memr, PIO_CONFIG_REG) & 0x0f000fff; sc->udmaconf[0] = sc->udmaconf[1] = 0; sc->wdmaconf[0] = sc->wdmaconf[1] = 0; /* Magic FCR value from Apple */ bus_write_4(sc->sc_memr, 0, 0x00000007); /* Set begin_transaction */ sc->sc_ch.sc_ch.hw.begin_transaction = ata_kauai_begin_transaction; return ata_attach(dev); } static int ata_kauai_setmode(device_t dev, int target, int mode) { struct ata_kauai_softc *sc = device_get_softc(dev); mode = min(mode,sc->shasta ? ATA_UDMA6 : ATA_UDMA5); if (sc->shasta) { switch (mode & ATA_DMA_MASK) { case ATA_UDMA0: sc->udmaconf[target] = udma_timing_shasta[mode & ATA_MODE_MASK]; break; case ATA_WDMA0: sc->udmaconf[target] = 0; sc->wdmaconf[target] = dma_timing_shasta[mode & ATA_MODE_MASK]; break; default: sc->pioconf[target] = pio_timing_shasta[(mode & ATA_MODE_MASK) - ATA_PIO0]; break; } } else { switch (mode & ATA_DMA_MASK) { case ATA_UDMA0: sc->udmaconf[target] = udma_timing_kauai[mode & ATA_MODE_MASK]; break; case ATA_WDMA0: sc->udmaconf[target] = 0; sc->wdmaconf[target] = dma_timing_kauai[mode & ATA_MODE_MASK]; break; default: sc->pioconf[target] = pio_timing_kauai[(mode & ATA_MODE_MASK) - ATA_PIO0]; break; } } return (mode); } static int ata_kauai_begin_transaction(struct ata_request *request) { struct ata_kauai_softc *sc = device_get_softc(request->parent); bus_write_4(sc->sc_memr, UDMA_CONFIG_REG, sc->udmaconf[request->unit]); bus_write_4(sc->sc_memr, PIO_CONFIG_REG, sc->wdmaconf[request->unit] | sc->pioconf[request->unit]); return ata_begin_transaction(request); } Index: head/sys/powerpc/powermac/ata_macio.c =================================================================== --- head/sys/powerpc/powermac/ata_macio.c (revision 326260) +++ head/sys/powerpc/powermac/ata_macio.c (revision 326261) @@ -1,373 +1,375 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2002 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Mac-io ATA controller */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ata_dbdma.h" /* * Offset to control registers from base */ #define ATA_MACIO_ALTOFFSET 0x160 /* * Define the gap between registers */ #define ATA_MACIO_REGGAP 16 /* * Whether or not to bind to the DBDMA IRQ */ #define USE_DBDMA_IRQ 0 /* * Timing register */ #define ATA_MACIO_TIMINGREG 0x200 #define ATA_TIME_TO_TICK(rev,time) howmany(time, (rev == 4) ? 15 : 30) #define PIO_REC_OFFSET 4 #define PIO_REC_MIN 1 #define PIO_ACT_MIN 1 #define DMA_REC_OFFSET 1 #define DMA_REC_MIN 1 #define DMA_ACT_MIN 1 struct ide_timings { int cycle; /* minimum cycle time [ns] */ int active; /* minimum command active time [ns] */ }; static const struct ide_timings pio_timings[5] = { { 600, 180 }, /* PIO 0 */ { 390, 150 }, /* PIO 1 */ { 240, 105 }, /* PIO 2 */ { 180, 90 }, /* PIO 3 */ { 120, 75 } /* PIO 4 */ }; static const struct ide_timings dma_timings[3] = { { 480, 240 }, /* WDMA 0 */ { 165, 90 }, /* WDMA 1 */ { 120, 75 } /* WDMA 2 */ }; static const struct ide_timings udma_timings[5] = { { 120, 180 }, /* UDMA 0 */ { 90, 150 }, /* UDMA 1 */ { 60, 120 }, /* UDMA 2 */ { 45, 90 }, /* UDMA 3 */ { 30, 90 } /* UDMA 4 */ }; /* * Define the macio ata bus attachment. */ static int ata_macio_probe(device_t dev); static int ata_macio_setmode(device_t dev, int target, int mode); static int ata_macio_attach(device_t dev); static int ata_macio_begin_transaction(struct ata_request *request); static int ata_macio_suspend(device_t dev); static int ata_macio_resume(device_t dev); static device_method_t ata_macio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ata_macio_probe), DEVMETHOD(device_attach, ata_macio_attach), DEVMETHOD(device_suspend, ata_macio_suspend), DEVMETHOD(device_resume, ata_macio_resume), /* ATA interface */ DEVMETHOD(ata_setmode, ata_macio_setmode), DEVMETHOD_END }; struct ata_macio_softc { struct ata_dbdma_channel sc_ch; int rev; int max_mode; struct resource *sc_mem; uint32_t udmaconf[2]; uint32_t wdmaconf[2]; uint32_t pioconf[2]; }; static driver_t ata_macio_driver = { "ata", ata_macio_methods, sizeof(struct ata_macio_softc), }; DRIVER_MODULE(ata, macio, ata_macio_driver, ata_devclass, NULL, NULL); MODULE_DEPEND(ata, ata, 1, 1, 1); static int ata_macio_probe(device_t dev) { const char *type = ofw_bus_get_type(dev); const char *name = ofw_bus_get_name(dev); struct ata_macio_softc *sc; if (strcmp(type, "ata") != 0 && strcmp(type, "ide") != 0) return (ENXIO); sc = device_get_softc(dev); bzero(sc, sizeof(struct ata_macio_softc)); if (strcmp(name,"ata-4") == 0) { device_set_desc(dev,"Apple MacIO Ultra ATA Controller"); sc->rev = 4; sc->max_mode = ATA_UDMA4; } else { device_set_desc(dev,"Apple MacIO ATA Controller"); sc->rev = 3; sc->max_mode = ATA_WDMA2; } return (ata_probe(dev)); } static int ata_macio_attach(device_t dev) { struct ata_macio_softc *sc = device_get_softc(dev); uint32_t timingreg; struct ata_channel *ch; int rid, i; /* * Allocate resources */ rid = 0; ch = &sc->sc_ch.sc_ch; sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem == NULL) { device_printf(dev, "could not allocate memory\n"); return (ENXIO); } /* * Set up the resource vectors */ for (i = ATA_DATA; i <= ATA_COMMAND; i++) { ch->r_io[i].res = sc->sc_mem; ch->r_io[i].offset = i * ATA_MACIO_REGGAP; } ch->r_io[ATA_CONTROL].res = sc->sc_mem; ch->r_io[ATA_CONTROL].offset = ATA_MACIO_ALTOFFSET; ata_default_registers(dev); ch->unit = 0; ch->flags |= ATA_USE_16BIT | ATA_NO_ATAPI_DMA; ata_generic_hw(dev); #if USE_DBDMA_IRQ int dbdma_irq_rid = 1; struct resource *dbdma_irq; void *cookie; #endif /* Init DMA engine */ sc->sc_ch.dbdma_rid = 1; sc->sc_ch.dbdma_regs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_ch.dbdma_rid, RF_ACTIVE); ata_dbdma_dmainit(dev); /* Configure initial timings */ timingreg = bus_read_4(sc->sc_mem, ATA_MACIO_TIMINGREG); if (sc->rev == 4) { sc->udmaconf[0] = sc->udmaconf[1] = timingreg & 0x1ff00000; sc->wdmaconf[0] = sc->wdmaconf[1] = timingreg & 0x001ffc00; sc->pioconf[0] = sc->pioconf[1] = timingreg & 0x000003ff; } else { sc->udmaconf[0] = sc->udmaconf[1] = 0; sc->wdmaconf[0] = sc->wdmaconf[1] = timingreg & 0xfffff800; sc->pioconf[0] = sc->pioconf[1] = timingreg & 0x000007ff; } #if USE_DBDMA_IRQ /* Bind to DBDMA interrupt as well */ if ((dbdma_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &dbdma_irq_rid, RF_SHAREABLE | RF_ACTIVE)) != NULL) { bus_setup_intr(dev, dbdma_irq, ATA_INTR_FLAGS, NULL, (driver_intr_t *)ata_interrupt, sc,&cookie); } #endif /* Set begin_transaction */ sc->sc_ch.sc_ch.hw.begin_transaction = ata_macio_begin_transaction; return ata_attach(dev); } static int ata_macio_setmode(device_t dev, int target, int mode) { struct ata_macio_softc *sc = device_get_softc(dev); int min_cycle = 0, min_active = 0; int cycle_tick = 0, act_tick = 0, inact_tick = 0, half_tick; mode = min(mode, sc->max_mode); if ((mode & ATA_DMA_MASK) == ATA_UDMA0) { min_cycle = udma_timings[mode & ATA_MODE_MASK].cycle; min_active = udma_timings[mode & ATA_MODE_MASK].active; cycle_tick = ATA_TIME_TO_TICK(sc->rev,min_cycle); act_tick = ATA_TIME_TO_TICK(sc->rev,min_active); /* mask: 0x1ff00000 */ sc->udmaconf[target] = (cycle_tick << 21) | (act_tick << 25) | 0x100000; } else if ((mode & ATA_DMA_MASK) == ATA_WDMA0) { min_cycle = dma_timings[mode & ATA_MODE_MASK].cycle; min_active = dma_timings[mode & ATA_MODE_MASK].active; cycle_tick = ATA_TIME_TO_TICK(sc->rev,min_cycle); act_tick = ATA_TIME_TO_TICK(sc->rev,min_active); if (sc->rev == 4) { inact_tick = cycle_tick - act_tick; /* mask: 0x001ffc00 */ sc->wdmaconf[target] = (act_tick << 10) | (inact_tick << 15); } else { inact_tick = cycle_tick - act_tick - DMA_REC_OFFSET; if (inact_tick < DMA_REC_MIN) inact_tick = DMA_REC_MIN; half_tick = 0; /* XXX */ /* mask: 0xfffff800 */ sc->wdmaconf[target] = (half_tick << 21) | (inact_tick << 16) | (act_tick << 11); } } else { min_cycle = pio_timings[(mode & ATA_MODE_MASK) - ATA_PIO0].cycle; min_active = pio_timings[(mode & ATA_MODE_MASK) - ATA_PIO0].active; cycle_tick = ATA_TIME_TO_TICK(sc->rev,min_cycle); act_tick = ATA_TIME_TO_TICK(sc->rev,min_active); if (sc->rev == 4) { inact_tick = cycle_tick - act_tick; /* mask: 0x000003ff */ sc->pioconf[target] = (inact_tick << 5) | act_tick; } else { if (act_tick < PIO_ACT_MIN) act_tick = PIO_ACT_MIN; inact_tick = cycle_tick - act_tick - PIO_REC_OFFSET; if (inact_tick < PIO_REC_MIN) inact_tick = PIO_REC_MIN; /* mask: 0x000007ff */ sc->pioconf[target] = (inact_tick << 5) | act_tick; } } return (mode); } static int ata_macio_begin_transaction(struct ata_request *request) { struct ata_macio_softc *sc = device_get_softc(request->parent); bus_write_4(sc->sc_mem, ATA_MACIO_TIMINGREG, sc->udmaconf[request->unit] | sc->wdmaconf[request->unit] | sc->pioconf[request->unit]); return ata_begin_transaction(request); } static int ata_macio_suspend(device_t dev) { struct ata_dbdma_channel *ch = device_get_softc(dev); int error; if (!ch->sc_ch.attached) return (0); error = ata_suspend(dev); dbdma_save_state(ch->dbdma); return (error); } static int ata_macio_resume(device_t dev) { struct ata_dbdma_channel *ch = device_get_softc(dev); int error; if (!ch->sc_ch.attached) return (0); dbdma_restore_state(ch->dbdma); error = ata_resume(dev); return (error); } Index: head/sys/powerpc/powermac/atibl.c =================================================================== --- head/sys/powerpc/powermac/atibl.c (revision 326260) +++ head/sys/powerpc/powermac/atibl.c (revision 326261) @@ -1,321 +1,323 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2012 Justin Hibbits * 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$"); #include #include #include #include #include #include #include #include #include #include #ifndef PCI_VENDOR_ID_ATI #define PCI_VENDOR_ID_ATI 0x1002 #endif /* From the xf86-video-ati driver's radeon_reg.h */ #define RADEON_LVDS_GEN_CNTL 0x02d0 #define RADEON_LVDS_ON (1 << 0) #define RADEON_LVDS_DISPLAY_DIS (1 << 1) #define RADEON_LVDS_PANEL_TYPE (1 << 2) #define RADEON_LVDS_PANEL_FORMAT (1 << 3) #define RADEON_LVDS_RST_FM (1 << 6) #define RADEON_LVDS_EN (1 << 7) #define RADEON_LVDS_BL_MOD_LEVEL_SHIFT 8 #define RADEON_LVDS_BL_MOD_LEVEL_MASK (0xff << 8) #define RADEON_LVDS_BL_MOD_EN (1 << 16) #define RADEON_LVDS_DIGON (1 << 18) #define RADEON_LVDS_BLON (1 << 19) #define RADEON_LVDS_PLL_CNTL 0x02d4 #define RADEON_LVDS_PLL_EN (1 << 16) #define RADEON_LVDS_PLL_RESET (1 << 17) #define RADEON_PIXCLKS_CNTL 0x002d #define RADEON_PIXCLK_LVDS_ALWAYS_ONb (1 << 14) #define RADEON_DISP_PWR_MAN 0x0d08 #define RADEON_AUTO_PWRUP_EN (1 << 26) #define RADEON_CLOCK_CNTL_DATA 0x000c #define RADEON_CLOCK_CNTL_INDEX 0x0008 #define RADEON_PLL_WR_EN (1 << 7) #define RADEON_CRTC_GEN_CNTL 0x0050 struct atibl_softc { struct resource *sc_memr; int sc_level; }; static void atibl_identify(driver_t *driver, device_t parent); static int atibl_probe(device_t dev); static int atibl_attach(device_t dev); static int atibl_setlevel(struct atibl_softc *sc, int newlevel); static int atibl_getlevel(struct atibl_softc *sc); static int atibl_resume(device_t dev); static int atibl_suspend(device_t dev); static int atibl_sysctl(SYSCTL_HANDLER_ARGS); static device_method_t atibl_methods[] = { /* Device interface */ DEVMETHOD(device_identify, atibl_identify), DEVMETHOD(device_probe, atibl_probe), DEVMETHOD(device_attach, atibl_attach), DEVMETHOD(device_suspend, atibl_suspend), DEVMETHOD(device_resume, atibl_resume), {0, 0}, }; static driver_t atibl_driver = { "backlight", atibl_methods, sizeof(struct atibl_softc) }; static devclass_t atibl_devclass; DRIVER_MODULE(atibl, vgapci, atibl_driver, atibl_devclass, 0, 0); static void atibl_identify(driver_t *driver, device_t parent) { if (OF_finddevice("mac-io/backlight") == -1) return; if (device_find_child(parent, "backlight", -1) == NULL) device_add_child(parent, "backlight", -1); } static int atibl_probe(device_t dev) { char control[8]; phandle_t handle; handle = OF_finddevice("mac-io/backlight"); if (handle == -1) return (ENXIO); if (OF_getprop(handle, "backlight-control", &control, sizeof(control)) < 0) return (ENXIO); if (strcmp(control, "ati") != 0 && (strcmp(control, "mnca") != 0 || pci_get_vendor(device_get_parent(dev)) != 0x1002)) return (ENXIO); device_set_desc(dev, "PowerBook backlight for ATI graphics"); return (0); } static int atibl_attach(device_t dev) { struct atibl_softc *sc; struct sysctl_ctx_list *ctx; struct sysctl_oid *tree; int rid; sc = device_get_softc(dev); rid = 0x18; /* BAR[2], for the MMIO register */ sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->sc_memr == NULL) { device_printf(dev, "Could not alloc mem resource!\n"); return (ENXIO); } ctx = device_get_sysctl_ctx(dev); tree = device_get_sysctl_tree(dev); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "level", CTLTYPE_INT | CTLFLAG_RW, sc, 0, atibl_sysctl, "I", "Backlight level (0-100)"); return (0); } static uint32_t __inline atibl_pll_rreg(struct atibl_softc *sc, uint32_t reg) { uint32_t data, save, tmp; bus_write_1(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, (reg & 0x3f)); (void)bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA); (void)bus_read_4(sc->sc_memr, RADEON_CRTC_GEN_CNTL); data = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA); /* Only necessary on R300, but won't hurt others. */ save = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX); tmp = save & (~0x3f | RADEON_PLL_WR_EN); bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, tmp); tmp = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA); bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, save); return data; } static void __inline atibl_pll_wreg(struct atibl_softc *sc, uint32_t reg, uint32_t val) { uint32_t save, tmp; bus_write_1(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, ((reg & 0x3f) | RADEON_PLL_WR_EN)); (void)bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA); (void)bus_read_4(sc->sc_memr, RADEON_CRTC_GEN_CNTL); bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA, val); DELAY(5000); /* Only necessary on R300, but won't hurt others. */ save = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX); tmp = save & (~0x3f | RADEON_PLL_WR_EN); bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, tmp); tmp = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA); bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, save); } static int atibl_setlevel(struct atibl_softc *sc, int newlevel) { uint32_t lvds_gen_cntl; uint32_t lvds_pll_cntl; uint32_t pixclks_cntl; uint32_t disp_pwr_reg; if (newlevel > 100) newlevel = 100; if (newlevel < 0) newlevel = 0; lvds_gen_cntl = bus_read_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL); if (newlevel > 0) { newlevel = (newlevel * 5) / 2 + 5; disp_pwr_reg = bus_read_4(sc->sc_memr, RADEON_DISP_PWR_MAN); disp_pwr_reg |= RADEON_AUTO_PWRUP_EN; bus_write_4(sc->sc_memr, RADEON_DISP_PWR_MAN, disp_pwr_reg); lvds_pll_cntl = bus_read_4(sc->sc_memr, RADEON_LVDS_PLL_CNTL); lvds_pll_cntl |= RADEON_LVDS_PLL_EN; bus_write_4(sc->sc_memr, RADEON_LVDS_PLL_CNTL, lvds_pll_cntl); lvds_pll_cntl &= ~RADEON_LVDS_PLL_RESET; bus_write_4(sc->sc_memr, RADEON_LVDS_PLL_CNTL, lvds_pll_cntl); DELAY(1000); lvds_gen_cntl &= ~(RADEON_LVDS_DISPLAY_DIS | RADEON_LVDS_BL_MOD_LEVEL_MASK); lvds_gen_cntl |= RADEON_LVDS_ON | RADEON_LVDS_EN | RADEON_LVDS_DIGON | RADEON_LVDS_BLON; lvds_gen_cntl |= (newlevel << RADEON_LVDS_BL_MOD_LEVEL_SHIFT) & RADEON_LVDS_BL_MOD_LEVEL_MASK; lvds_gen_cntl |= RADEON_LVDS_BL_MOD_EN; DELAY(200000); bus_write_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); } else { pixclks_cntl = atibl_pll_rreg(sc, RADEON_PIXCLKS_CNTL); atibl_pll_wreg(sc, RADEON_PIXCLKS_CNTL, pixclks_cntl & ~RADEON_PIXCLK_LVDS_ALWAYS_ONb); lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS; lvds_gen_cntl &= ~(RADEON_LVDS_BL_MOD_EN | RADEON_LVDS_BL_MOD_LEVEL_MASK); bus_write_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); lvds_gen_cntl &= ~(RADEON_LVDS_ON | RADEON_LVDS_EN); DELAY(200000); bus_write_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); atibl_pll_wreg(sc, RADEON_PIXCLKS_CNTL, pixclks_cntl); DELAY(200000); } return (0); } static int atibl_getlevel(struct atibl_softc *sc) { uint32_t lvds_gen_cntl; int level; lvds_gen_cntl = bus_read_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL); level = ((lvds_gen_cntl & RADEON_LVDS_BL_MOD_LEVEL_MASK) >> RADEON_LVDS_BL_MOD_LEVEL_SHIFT); if (level != 0) level = ((level - 5) * 2) / 5; return (level); } static int atibl_suspend(device_t dev) { struct atibl_softc *sc; sc = device_get_softc(dev); sc->sc_level = atibl_getlevel(sc); atibl_setlevel(sc, 0); return (0); } static int atibl_resume(device_t dev) { struct atibl_softc *sc; sc = device_get_softc(dev); atibl_setlevel(sc, sc->sc_level); return (0); } static int atibl_sysctl(SYSCTL_HANDLER_ARGS) { struct atibl_softc *sc; int newlevel, error; sc = arg1; newlevel = atibl_getlevel(sc); error = sysctl_handle_int(oidp, &newlevel, 0, req); if (error || !req->newptr) return (error); return (atibl_setlevel(sc, newlevel)); } Index: head/sys/powerpc/powermac/cpcht.c =================================================================== --- head/sys/powerpc/powermac/cpcht.c (revision 326260) +++ head/sys/powerpc/powermac/cpcht.c (revision 326261) @@ -1,737 +1,739 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2008-2010 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #include #include "pic_if.h" /* * IBM CPC9X5 Hypertransport Device interface. */ static int cpcht_probe(device_t); static int cpcht_attach(device_t); static void cpcht_configure_htbridge(device_t, phandle_t); /* * pcib interface. */ static u_int32_t cpcht_read_config(device_t, u_int, u_int, u_int, u_int, int); static void cpcht_write_config(device_t, u_int, u_int, u_int, u_int, u_int32_t, int); static int cpcht_route_interrupt(device_t, device_t, int); static int cpcht_alloc_msi(device_t dev, device_t child, int count, int maxcount, int *irqs); static int cpcht_release_msi(device_t dev, device_t child, int count, int *irqs); static int cpcht_alloc_msix(device_t dev, device_t child, int *irq); static int cpcht_release_msix(device_t dev, device_t child, int irq); static int cpcht_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, uint32_t *data); /* * Driver methods. */ static device_method_t cpcht_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cpcht_probe), DEVMETHOD(device_attach, cpcht_attach), /* pcib interface */ DEVMETHOD(pcib_read_config, cpcht_read_config), DEVMETHOD(pcib_write_config, cpcht_write_config), DEVMETHOD(pcib_route_interrupt, cpcht_route_interrupt), DEVMETHOD(pcib_alloc_msi, cpcht_alloc_msi), DEVMETHOD(pcib_release_msi, cpcht_release_msi), DEVMETHOD(pcib_alloc_msix, cpcht_alloc_msix), DEVMETHOD(pcib_release_msix, cpcht_release_msix), DEVMETHOD(pcib_map_msi, cpcht_map_msi), DEVMETHOD(pcib_request_feature, pcib_request_feature_allow), DEVMETHOD_END }; struct cpcht_irq { enum { IRQ_NONE, IRQ_HT, IRQ_MSI, IRQ_INTERNAL } irq_type; int ht_source; vm_offset_t ht_base; vm_offset_t apple_eoi; uint32_t eoi_data; int edge; }; static struct cpcht_irq *cpcht_irqmap = NULL; uint32_t cpcht_msipic = 0; struct cpcht_softc { struct ofw_pci_softc pci_sc; vm_offset_t sc_data; uint64_t sc_populated_slots; struct cpcht_irq htirq_map[128]; struct mtx htirq_mtx; }; static devclass_t cpcht_devclass; DEFINE_CLASS_1(pcib, cpcht_driver, cpcht_methods, sizeof(struct cpcht_softc), ofw_pci_driver); DRIVER_MODULE(cpcht, ofwbus, cpcht_driver, cpcht_devclass, 0, 0); #define CPCHT_IOPORT_BASE 0xf4000000UL /* Hardwired */ #define CPCHT_IOPORT_SIZE 0x00400000UL #define HTAPIC_REQUEST_EOI 0x20 #define HTAPIC_TRIGGER_LEVEL 0x02 #define HTAPIC_MASK 0x01 static int cpcht_probe(device_t dev) { const char *type, *compatible; type = ofw_bus_get_type(dev); compatible = ofw_bus_get_compat(dev); if (type == NULL || compatible == NULL) return (ENXIO); if (strcmp(type, "ht") != 0) return (ENXIO); if (strcmp(compatible, "u3-ht") != 0) return (ENXIO); device_set_desc(dev, "IBM CPC9X5 HyperTransport Tunnel"); return (0); } static int cpcht_attach(device_t dev) { struct cpcht_softc *sc; phandle_t node, child; u_int32_t reg[3]; int i; node = ofw_bus_get_node(dev); sc = device_get_softc(dev); if (OF_getencprop(node, "reg", reg, sizeof(reg)) < 12) return (ENXIO); if (OF_getproplen(node, "ranges") <= 0) sc->pci_sc.sc_quirks = OFW_PCI_QUIRK_RANGES_ON_CHILDREN; sc->sc_populated_slots = 0; sc->sc_data = (vm_offset_t)pmap_mapdev(reg[1], reg[2]); /* * Set up the resource manager and the HT->MPIC mapping. For cpcht, * the ranges are properties of the child bridges, and this is also * where we get the HT interrupts properties. */ #if 0 /* I/O port mappings are usually not in the device tree */ rman_manage_region(&sc->pci_sc.sc_io_rman, 0, CPCHT_IOPORT_SIZE - 1); #endif bzero(sc->htirq_map, sizeof(sc->htirq_map)); mtx_init(&sc->htirq_mtx, "cpcht irq", NULL, MTX_DEF); for (i = 0; i < 8; i++) sc->htirq_map[i].irq_type = IRQ_INTERNAL; for (child = OF_child(node); child != 0; child = OF_peer(child)) cpcht_configure_htbridge(dev, child); /* Now make the mapping table available to the MPIC */ cpcht_irqmap = sc->htirq_map; return (ofw_pci_attach(dev)); } static void cpcht_configure_htbridge(device_t dev, phandle_t child) { struct cpcht_softc *sc; struct ofw_pci_register pcir; int ptr, nextptr; uint32_t vend, val; int i, nirq, irq; u_int b, f, s; sc = device_get_softc(dev); if (OF_getencprop(child, "reg", (pcell_t *)&pcir, sizeof(pcir)) == -1) return; b = OFW_PCI_PHYS_HI_BUS(pcir.phys_hi); s = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi); f = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi); /* * Mark this slot is populated. The remote south bridge does * not like us talking to unpopulated slots on the root bus. */ sc->sc_populated_slots |= (1 << s); /* * Next build up any HT->MPIC mappings for this sub-bus. One would * naively hope that enabling, disabling, and EOIing interrupts would * cause the appropriate HT bus transactions to that effect. This is * not the case. * * Instead, we have to muck about on the HT peer's root PCI bridges, * figure out what interrupts they send, enable them, and cache * the location of their WaitForEOI registers so that we can * send EOIs later. */ /* All the devices we are interested in have caps */ if (!(PCIB_READ_CONFIG(dev, b, s, f, PCIR_STATUS, 2) & PCIM_STATUS_CAPPRESENT)) return; nextptr = PCIB_READ_CONFIG(dev, b, s, f, PCIR_CAP_PTR, 1); while (nextptr != 0) { ptr = nextptr; nextptr = PCIB_READ_CONFIG(dev, b, s, f, ptr + PCICAP_NEXTPTR, 1); /* Find the HT IRQ capabilities */ if (PCIB_READ_CONFIG(dev, b, s, f, ptr + PCICAP_ID, 1) != PCIY_HT) continue; val = PCIB_READ_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 2); if ((val & PCIM_HTCMD_CAP_MASK) != PCIM_HTCAP_INTERRUPT) continue; /* Ask for the IRQ count */ PCIB_WRITE_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 0x1, 1); nirq = PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4); nirq = ((nirq >> 16) & 0xff) + 1; device_printf(dev, "%d HT IRQs on device %d.%d\n", nirq, s, f); for (i = 0; i < nirq; i++) { PCIB_WRITE_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 0x10 + (i << 1), 1); irq = PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4); /* * Mask this interrupt for now. */ PCIB_WRITE_CONFIG(dev, b, s, f, ptr + 4, irq | HTAPIC_MASK, 4); irq = (irq >> 16) & 0xff; sc->htirq_map[irq].irq_type = IRQ_HT; sc->htirq_map[irq].ht_source = i; sc->htirq_map[irq].ht_base = sc->sc_data + (((((s & 0x1f) << 3) | (f & 0x07)) << 8) | (ptr)); PCIB_WRITE_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 0x11 + (i << 1), 1); sc->htirq_map[irq].eoi_data = PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4) | 0x80000000; /* * Apple uses a non-compliant IO/APIC that differs * in how we signal EOIs. Check if this device was * made by Apple, and act accordingly. */ vend = PCIB_READ_CONFIG(dev, b, s, f, PCIR_DEVVENDOR, 4); if ((vend & 0xffff) == 0x106b) sc->htirq_map[irq].apple_eoi = (sc->htirq_map[irq].ht_base - ptr) + 0x60; } } } static u_int32_t cpcht_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int width) { struct cpcht_softc *sc; vm_offset_t caoff; sc = device_get_softc(dev); caoff = sc->sc_data + (((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg); if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0)) return (0xffffffff); if (bus > 0) caoff += 0x01000000UL + (bus << 16); switch (width) { case 1: return (in8rb(caoff)); break; case 2: return (in16rb(caoff)); break; case 4: return (in32rb(caoff)); break; } return (0xffffffff); } static void cpcht_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, u_int32_t val, int width) { struct cpcht_softc *sc; vm_offset_t caoff; sc = device_get_softc(dev); caoff = sc->sc_data + (((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg); if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0)) return; if (bus > 0) caoff += 0x01000000UL + (bus << 16); switch (width) { case 1: out8rb(caoff, val); break; case 2: out16rb(caoff, val); break; case 4: out32rb(caoff, val); break; } } static int cpcht_route_interrupt(device_t bus, device_t dev, int pin) { return (pin); } static int cpcht_alloc_msi(device_t dev, device_t child, int count, int maxcount, int *irqs) { struct cpcht_softc *sc; int i, j; sc = device_get_softc(dev); j = 0; /* Bail if no MSI PIC yet */ if (cpcht_msipic == 0) return (ENXIO); mtx_lock(&sc->htirq_mtx); for (i = 8; i < 124 - count; i++) { for (j = 0; j < count; j++) { if (sc->htirq_map[i+j].irq_type != IRQ_NONE) break; } if (j == count) break; i += j; /* We know there isn't a large enough run */ } if (j != count) { mtx_unlock(&sc->htirq_mtx); return (ENXIO); } for (j = 0; j < count; j++) { irqs[j] = MAP_IRQ(cpcht_msipic, i+j); sc->htirq_map[i+j].irq_type = IRQ_MSI; } mtx_unlock(&sc->htirq_mtx); return (0); } static int cpcht_release_msi(device_t dev, device_t child, int count, int *irqs) { struct cpcht_softc *sc; int i; sc = device_get_softc(dev); mtx_lock(&sc->htirq_mtx); for (i = 0; i < count; i++) sc->htirq_map[irqs[i] & 0xff].irq_type = IRQ_NONE; mtx_unlock(&sc->htirq_mtx); return (0); } static int cpcht_alloc_msix(device_t dev, device_t child, int *irq) { struct cpcht_softc *sc; int i; sc = device_get_softc(dev); /* Bail if no MSI PIC yet */ if (cpcht_msipic == 0) return (ENXIO); mtx_lock(&sc->htirq_mtx); for (i = 8; i < 124; i++) { if (sc->htirq_map[i].irq_type == IRQ_NONE) { sc->htirq_map[i].irq_type = IRQ_MSI; *irq = MAP_IRQ(cpcht_msipic, i); mtx_unlock(&sc->htirq_mtx); return (0); } } mtx_unlock(&sc->htirq_mtx); return (ENXIO); } static int cpcht_release_msix(device_t dev, device_t child, int irq) { struct cpcht_softc *sc; sc = device_get_softc(dev); mtx_lock(&sc->htirq_mtx); sc->htirq_map[irq & 0xff].irq_type = IRQ_NONE; mtx_unlock(&sc->htirq_mtx); return (0); } static int cpcht_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, uint32_t *data) { device_t pcib; struct pci_devinfo *dinfo; struct pcicfg_ht *ht = NULL; for (pcib = child; pcib != dev; pcib = device_get_parent(device_get_parent(pcib))) { dinfo = device_get_ivars(pcib); ht = &dinfo->cfg.ht; if (ht == NULL) continue; } if (ht == NULL) return (ENXIO); *addr = ht->ht_msiaddr; *data = irq & 0xff; return (0); } /* * Driver for the integrated MPIC on U3/U4 (CPC925/CPC945) */ static int openpic_cpcht_probe(device_t); static int openpic_cpcht_attach(device_t); static void openpic_cpcht_config(device_t, u_int irq, enum intr_trigger trig, enum intr_polarity pol); static void openpic_cpcht_enable(device_t, u_int irq, u_int vector); static void openpic_cpcht_unmask(device_t, u_int irq); static void openpic_cpcht_eoi(device_t, u_int irq); static device_method_t openpic_cpcht_methods[] = { /* Device interface */ DEVMETHOD(device_probe, openpic_cpcht_probe), DEVMETHOD(device_attach, openpic_cpcht_attach), /* PIC interface */ DEVMETHOD(pic_bind, openpic_bind), DEVMETHOD(pic_config, openpic_cpcht_config), DEVMETHOD(pic_dispatch, openpic_dispatch), DEVMETHOD(pic_enable, openpic_cpcht_enable), DEVMETHOD(pic_eoi, openpic_cpcht_eoi), DEVMETHOD(pic_ipi, openpic_ipi), DEVMETHOD(pic_mask, openpic_mask), DEVMETHOD(pic_unmask, openpic_cpcht_unmask), { 0, 0 }, }; struct openpic_cpcht_softc { struct openpic_softc sc_openpic; struct mtx sc_ht_mtx; }; static driver_t openpic_cpcht_driver = { "htpic", openpic_cpcht_methods, sizeof(struct openpic_cpcht_softc), }; DRIVER_MODULE(openpic, unin, openpic_cpcht_driver, openpic_devclass, 0, 0); static int openpic_cpcht_probe(device_t dev) { const char *type = ofw_bus_get_type(dev); if (strcmp(type, "open-pic") != 0) return (ENXIO); device_set_desc(dev, OPENPIC_DEVSTR); return (0); } static int openpic_cpcht_attach(device_t dev) { struct openpic_cpcht_softc *sc; phandle_t node; int err, irq; node = ofw_bus_get_node(dev); err = openpic_common_attach(dev, node); if (err != 0) return (err); /* * The HT APIC stuff is not thread-safe, so we need a mutex to * protect it. */ sc = device_get_softc(dev); mtx_init(&sc->sc_ht_mtx, "htpic", NULL, MTX_SPIN); /* * Interrupts 0-3 are internally sourced and are level triggered * active low. Interrupts 4-123 are connected to a pulse generator * and should be programmed as edge triggered low-to-high. * * IBM CPC945 Manual, Section 9.3. */ for (irq = 0; irq < 4; irq++) openpic_config(dev, irq, INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW); for (irq = 4; irq < 124; irq++) openpic_config(dev, irq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW); /* * Use this PIC for MSI only if it is the root PIC. This may not * be necessary, but Linux does it, and I cannot find any U3 machines * with MSI devices to test. */ if (dev == root_pic) cpcht_msipic = node; return (0); } static void openpic_cpcht_config(device_t dev, u_int irq, enum intr_trigger trig, enum intr_polarity pol) { struct openpic_cpcht_softc *sc; uint32_t ht_irq; /* * The interrupt settings for the MPIC are completely determined * by the internal wiring in the northbridge. Real changes to these * settings need to be negotiated with the remote IO-APIC on the HT * link. */ sc = device_get_softc(dev); if (cpcht_irqmap != NULL && irq < 128 && cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) { mtx_lock_spin(&sc->sc_ht_mtx); /* Program the data port */ out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, 0x10 + (cpcht_irqmap[irq].ht_source << 1)); /* Grab the IRQ config register */ ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4); /* Mask the IRQ while we fiddle settings */ out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq | HTAPIC_MASK); /* Program the interrupt sense */ ht_irq &= ~(HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI); if (trig == INTR_TRIGGER_EDGE) { cpcht_irqmap[irq].edge = 1; } else { cpcht_irqmap[irq].edge = 0; ht_irq |= HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI; } out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq); mtx_unlock_spin(&sc->sc_ht_mtx); } } static void openpic_cpcht_enable(device_t dev, u_int irq, u_int vec) { struct openpic_cpcht_softc *sc; uint32_t ht_irq; openpic_enable(dev, irq, vec); sc = device_get_softc(dev); if (cpcht_irqmap != NULL && irq < 128 && cpcht_irqmap[irq].ht_base > 0) { mtx_lock_spin(&sc->sc_ht_mtx); /* Program the data port */ out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, 0x10 + (cpcht_irqmap[irq].ht_source << 1)); /* Unmask the interrupt */ ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4); ht_irq &= ~HTAPIC_MASK; out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq); mtx_unlock_spin(&sc->sc_ht_mtx); } openpic_cpcht_eoi(dev, irq); } static void openpic_cpcht_unmask(device_t dev, u_int irq) { struct openpic_cpcht_softc *sc; uint32_t ht_irq; openpic_unmask(dev, irq); sc = device_get_softc(dev); if (cpcht_irqmap != NULL && irq < 128 && cpcht_irqmap[irq].ht_base > 0) { mtx_lock_spin(&sc->sc_ht_mtx); /* Program the data port */ out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, 0x10 + (cpcht_irqmap[irq].ht_source << 1)); /* Unmask the interrupt */ ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4); ht_irq &= ~HTAPIC_MASK; out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq); mtx_unlock_spin(&sc->sc_ht_mtx); } openpic_cpcht_eoi(dev, irq); } static void openpic_cpcht_eoi(device_t dev, u_int irq) { struct openpic_cpcht_softc *sc; uint32_t off, mask; if (irq == 255) return; sc = device_get_softc(dev); if (cpcht_irqmap != NULL && irq < 128 && cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) { /* If this is an HT IRQ, acknowledge it at the remote APIC */ if (cpcht_irqmap[irq].apple_eoi) { off = (cpcht_irqmap[irq].ht_source >> 3) & ~3; mask = 1 << (cpcht_irqmap[irq].ht_source & 0x1f); out32rb(cpcht_irqmap[irq].apple_eoi + off, mask); } else { mtx_lock_spin(&sc->sc_ht_mtx); out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, 0x11 + (cpcht_irqmap[irq].ht_source << 1)); out32rb(cpcht_irqmap[irq].ht_base + 4, cpcht_irqmap[irq].eoi_data); mtx_unlock_spin(&sc->sc_ht_mtx); } } openpic_eoi(dev, irq); } Index: head/sys/powerpc/powermac/cuda.c =================================================================== --- head/sys/powerpc/powermac/cuda.c (revision 326260) +++ head/sys/powerpc/powermac/cuda.c (revision 326261) @@ -1,793 +1,795 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright (c) 2006 Michael Lorenz * Copyright 2008 by Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clock_if.h" #include "cudavar.h" #include "viareg.h" /* * MacIO interface */ static int cuda_probe(device_t); static int cuda_attach(device_t); static int cuda_detach(device_t); static u_int cuda_adb_send(device_t dev, u_char command_byte, int len, u_char *data, u_char poll); static u_int cuda_adb_autopoll(device_t dev, uint16_t mask); static u_int cuda_poll(device_t dev); static void cuda_send_inbound(struct cuda_softc *sc); static void cuda_send_outbound(struct cuda_softc *sc); static void cuda_shutdown(void *xsc, int howto); /* * Clock interface */ static int cuda_gettime(device_t dev, struct timespec *ts); static int cuda_settime(device_t dev, struct timespec *ts); static device_method_t cuda_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cuda_probe), DEVMETHOD(device_attach, cuda_attach), DEVMETHOD(device_detach, cuda_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* ADB bus interface */ DEVMETHOD(adb_hb_send_raw_packet, cuda_adb_send), DEVMETHOD(adb_hb_controller_poll, cuda_poll), DEVMETHOD(adb_hb_set_autopoll_mask, cuda_adb_autopoll), /* Clock interface */ DEVMETHOD(clock_gettime, cuda_gettime), DEVMETHOD(clock_settime, cuda_settime), DEVMETHOD_END }; static driver_t cuda_driver = { "cuda", cuda_methods, sizeof(struct cuda_softc), }; static devclass_t cuda_devclass; DRIVER_MODULE(cuda, macio, cuda_driver, cuda_devclass, 0, 0); DRIVER_MODULE(adb, cuda, adb_driver, adb_devclass, 0, 0); static void cuda_intr(void *arg); static uint8_t cuda_read_reg(struct cuda_softc *sc, u_int offset); static void cuda_write_reg(struct cuda_softc *sc, u_int offset, uint8_t value); static void cuda_idle(struct cuda_softc *); static void cuda_tip(struct cuda_softc *); static void cuda_clear_tip(struct cuda_softc *); static void cuda_in(struct cuda_softc *); static void cuda_out(struct cuda_softc *); static void cuda_toggle_ack(struct cuda_softc *); static void cuda_ack_off(struct cuda_softc *); static int cuda_intr_state(struct cuda_softc *); static int cuda_probe(device_t dev) { const char *type = ofw_bus_get_type(dev); if (strcmp(type, "via-cuda") != 0) return (ENXIO); device_set_desc(dev, CUDA_DEVSTR); return (0); } static int cuda_attach(device_t dev) { struct cuda_softc *sc; volatile int i; uint8_t reg; phandle_t node,child; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_memrid = 0; sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_memrid, RF_ACTIVE); if (sc->sc_memr == NULL) { device_printf(dev, "Could not alloc mem resource!\n"); return (ENXIO); } sc->sc_irqrid = 0; sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqrid, RF_ACTIVE); if (sc->sc_irq == NULL) { device_printf(dev, "could not allocate interrupt\n"); return (ENXIO); } if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE | INTR_ENTROPY, NULL, cuda_intr, dev, &sc->sc_ih) != 0) { device_printf(dev, "could not setup interrupt\n"); bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq); return (ENXIO); } mtx_init(&sc->sc_mutex,"cuda",NULL,MTX_DEF | MTX_RECURSE); sc->sc_sent = 0; sc->sc_received = 0; sc->sc_waiting = 0; sc->sc_polling = 0; sc->sc_state = CUDA_NOTREADY; sc->sc_autopoll = 0; sc->sc_rtc = -1; STAILQ_INIT(&sc->sc_inq); STAILQ_INIT(&sc->sc_outq); STAILQ_INIT(&sc->sc_freeq); for (i = 0; i < CUDA_MAXPACKETS; i++) STAILQ_INSERT_TAIL(&sc->sc_freeq, &sc->sc_pkts[i], pkt_q); /* Init CUDA */ reg = cuda_read_reg(sc, vDirB); reg |= 0x30; /* register B bits 4 and 5: outputs */ cuda_write_reg(sc, vDirB, reg); reg = cuda_read_reg(sc, vDirB); reg &= 0xf7; /* register B bit 3: input */ cuda_write_reg(sc, vDirB, reg); reg = cuda_read_reg(sc, vACR); reg &= ~vSR_OUT; /* make sure SR is set to IN */ cuda_write_reg(sc, vACR, reg); cuda_write_reg(sc, vACR, (cuda_read_reg(sc, vACR) | 0x0c) & ~0x10); sc->sc_state = CUDA_IDLE; /* used by all types of hardware */ cuda_write_reg(sc, vIER, 0x84); /* make sure VIA interrupts are on */ cuda_idle(sc); /* reset ADB */ /* Reset CUDA */ i = cuda_read_reg(sc, vSR); /* clear interrupt */ cuda_write_reg(sc, vIER, 0x04); /* no interrupts while clearing */ cuda_idle(sc); /* reset state to idle */ DELAY(150); cuda_tip(sc); /* signal start of frame */ DELAY(150); cuda_toggle_ack(sc); DELAY(150); cuda_clear_tip(sc); DELAY(150); cuda_idle(sc); /* back to idle state */ i = cuda_read_reg(sc, vSR); /* clear interrupt */ cuda_write_reg(sc, vIER, 0x84); /* ints ok now */ /* Initialize child buses (ADB) */ node = ofw_bus_get_node(dev); for (child = OF_child(node); child != 0; child = OF_peer(child)) { char name[32]; memset(name, 0, sizeof(name)); OF_getprop(child, "name", name, sizeof(name)); if (bootverbose) device_printf(dev, "CUDA child <%s>\n",name); if (strncmp(name, "adb", 4) == 0) { sc->adb_bus = device_add_child(dev,"adb",-1); } } clock_register(dev, 1000); EVENTHANDLER_REGISTER(shutdown_final, cuda_shutdown, sc, SHUTDOWN_PRI_LAST); return (bus_generic_attach(dev)); } static int cuda_detach(device_t dev) { struct cuda_softc *sc; sc = device_get_softc(dev); bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih); bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq); bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_memrid, sc->sc_memr); mtx_destroy(&sc->sc_mutex); return (bus_generic_detach(dev)); } static uint8_t cuda_read_reg(struct cuda_softc *sc, u_int offset) { return (bus_read_1(sc->sc_memr, offset)); } static void cuda_write_reg(struct cuda_softc *sc, u_int offset, uint8_t value) { bus_write_1(sc->sc_memr, offset, value); } static void cuda_idle(struct cuda_softc *sc) { uint8_t reg; reg = cuda_read_reg(sc, vBufB); reg |= (vPB4 | vPB5); cuda_write_reg(sc, vBufB, reg); } static void cuda_tip(struct cuda_softc *sc) { uint8_t reg; reg = cuda_read_reg(sc, vBufB); reg &= ~vPB5; cuda_write_reg(sc, vBufB, reg); } static void cuda_clear_tip(struct cuda_softc *sc) { uint8_t reg; reg = cuda_read_reg(sc, vBufB); reg |= vPB5; cuda_write_reg(sc, vBufB, reg); } static void cuda_in(struct cuda_softc *sc) { uint8_t reg; reg = cuda_read_reg(sc, vACR); reg &= ~vSR_OUT; cuda_write_reg(sc, vACR, reg); } static void cuda_out(struct cuda_softc *sc) { uint8_t reg; reg = cuda_read_reg(sc, vACR); reg |= vSR_OUT; cuda_write_reg(sc, vACR, reg); } static void cuda_toggle_ack(struct cuda_softc *sc) { uint8_t reg; reg = cuda_read_reg(sc, vBufB); reg ^= vPB4; cuda_write_reg(sc, vBufB, reg); } static void cuda_ack_off(struct cuda_softc *sc) { uint8_t reg; reg = cuda_read_reg(sc, vBufB); reg |= vPB4; cuda_write_reg(sc, vBufB, reg); } static int cuda_intr_state(struct cuda_softc *sc) { return ((cuda_read_reg(sc, vBufB) & vPB3) == 0); } static int cuda_send(void *cookie, int poll, int length, uint8_t *msg) { struct cuda_softc *sc = cookie; device_t dev = sc->sc_dev; struct cuda_packet *pkt; if (sc->sc_state == CUDA_NOTREADY) return (-1); mtx_lock(&sc->sc_mutex); pkt = STAILQ_FIRST(&sc->sc_freeq); if (pkt == NULL) { mtx_unlock(&sc->sc_mutex); return (-1); } pkt->len = length - 1; pkt->type = msg[0]; memcpy(pkt->data, &msg[1], pkt->len); STAILQ_REMOVE_HEAD(&sc->sc_freeq, pkt_q); STAILQ_INSERT_TAIL(&sc->sc_outq, pkt, pkt_q); /* * If we already are sending a packet, we should bail now that this * one has been added to the queue. */ if (sc->sc_waiting) { mtx_unlock(&sc->sc_mutex); return (0); } cuda_send_outbound(sc); mtx_unlock(&sc->sc_mutex); if (sc->sc_polling || poll || cold) cuda_poll(dev); return (0); } static void cuda_send_outbound(struct cuda_softc *sc) { struct cuda_packet *pkt; mtx_assert(&sc->sc_mutex, MA_OWNED); pkt = STAILQ_FIRST(&sc->sc_outq); if (pkt == NULL) return; sc->sc_out_length = pkt->len + 1; memcpy(sc->sc_out, &pkt->type, pkt->len + 1); sc->sc_sent = 0; STAILQ_REMOVE_HEAD(&sc->sc_outq, pkt_q); STAILQ_INSERT_TAIL(&sc->sc_freeq, pkt, pkt_q); sc->sc_waiting = 1; cuda_poll(sc->sc_dev); DELAY(150); if (sc->sc_state == CUDA_IDLE && !cuda_intr_state(sc)) { sc->sc_state = CUDA_OUT; cuda_out(sc); cuda_write_reg(sc, vSR, sc->sc_out[0]); cuda_ack_off(sc); cuda_tip(sc); } } static void cuda_send_inbound(struct cuda_softc *sc) { device_t dev; struct cuda_packet *pkt; dev = sc->sc_dev; mtx_lock(&sc->sc_mutex); while ((pkt = STAILQ_FIRST(&sc->sc_inq)) != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_inq, pkt_q); mtx_unlock(&sc->sc_mutex); /* check if we have a handler for this message */ switch (pkt->type) { case CUDA_ADB: if (pkt->len > 2) { adb_receive_raw_packet(sc->adb_bus, pkt->data[0],pkt->data[1], pkt->len - 2,&pkt->data[2]); } else { adb_receive_raw_packet(sc->adb_bus, pkt->data[0],pkt->data[1],0,NULL); } break; case CUDA_PSEUDO: mtx_lock(&sc->sc_mutex); switch (pkt->data[1]) { case CMD_AUTOPOLL: sc->sc_autopoll = 1; break; case CMD_READ_RTC: memcpy(&sc->sc_rtc, &pkt->data[2], sizeof(sc->sc_rtc)); wakeup(&sc->sc_rtc); break; case CMD_WRITE_RTC: break; } mtx_unlock(&sc->sc_mutex); break; case CUDA_ERROR: /* * CUDA will throw errors if we miss a race between * sending and receiving packets. This is already * handled when we abort packet output to handle * this packet in cuda_intr(). Thus, we ignore * these messages. */ break; default: device_printf(dev,"unknown CUDA command %d\n", pkt->type); break; } mtx_lock(&sc->sc_mutex); STAILQ_INSERT_TAIL(&sc->sc_freeq, pkt, pkt_q); } mtx_unlock(&sc->sc_mutex); } static u_int cuda_poll(device_t dev) { struct cuda_softc *sc = device_get_softc(dev); if (sc->sc_state == CUDA_IDLE && !cuda_intr_state(sc) && !sc->sc_waiting) return (0); cuda_intr(dev); return (0); } static void cuda_intr(void *arg) { device_t dev; struct cuda_softc *sc; int i, ending, restart_send, process_inbound; uint8_t reg; dev = (device_t)arg; sc = device_get_softc(dev); mtx_lock(&sc->sc_mutex); restart_send = 0; process_inbound = 0; reg = cuda_read_reg(sc, vIFR); if ((reg & vSR_INT) != vSR_INT) { mtx_unlock(&sc->sc_mutex); return; } cuda_write_reg(sc, vIFR, 0x7f); /* Clear interrupt */ switch_start: switch (sc->sc_state) { case CUDA_IDLE: /* * This is an unexpected packet, so grab the first (dummy) * byte, set up the proper vars, and tell the chip we are * starting to receive the packet by setting the TIP bit. */ sc->sc_in[1] = cuda_read_reg(sc, vSR); if (cuda_intr_state(sc) == 0) { /* must have been a fake start */ if (sc->sc_waiting) { /* start over */ DELAY(150); sc->sc_state = CUDA_OUT; sc->sc_sent = 0; cuda_out(sc); cuda_write_reg(sc, vSR, sc->sc_out[1]); cuda_ack_off(sc); cuda_tip(sc); } break; } cuda_in(sc); cuda_tip(sc); sc->sc_received = 1; sc->sc_state = CUDA_IN; break; case CUDA_IN: sc->sc_in[sc->sc_received] = cuda_read_reg(sc, vSR); ending = 0; if (sc->sc_received > 255) { /* bitch only once */ if (sc->sc_received == 256) { device_printf(dev,"input overflow\n"); ending = 1; } } else sc->sc_received++; /* intr off means this is the last byte (end of frame) */ if (cuda_intr_state(sc) == 0) { ending = 1; } else { cuda_toggle_ack(sc); } if (ending == 1) { /* end of message? */ struct cuda_packet *pkt; /* reset vars and signal the end of this frame */ cuda_idle(sc); /* Queue up the packet */ pkt = STAILQ_FIRST(&sc->sc_freeq); if (pkt != NULL) { /* If we have a free packet, process it */ pkt->len = sc->sc_received - 2; pkt->type = sc->sc_in[1]; memcpy(pkt->data, &sc->sc_in[2], pkt->len); STAILQ_REMOVE_HEAD(&sc->sc_freeq, pkt_q); STAILQ_INSERT_TAIL(&sc->sc_inq, pkt, pkt_q); process_inbound = 1; } sc->sc_state = CUDA_IDLE; sc->sc_received = 0; /* * If there is something waiting to be sent out, * set everything up and send the first byte. */ if (sc->sc_waiting == 1) { DELAY(1500); /* required */ sc->sc_sent = 0; sc->sc_state = CUDA_OUT; /* * If the interrupt is on, we were too slow * and the chip has already started to send * something to us, so back out of the write * and start a read cycle. */ if (cuda_intr_state(sc)) { cuda_in(sc); cuda_idle(sc); sc->sc_sent = 0; sc->sc_state = CUDA_IDLE; sc->sc_received = 0; DELAY(150); goto switch_start; } /* * If we got here, it's ok to start sending * so load the first byte and tell the chip * we want to send. */ cuda_out(sc); cuda_write_reg(sc, vSR, sc->sc_out[sc->sc_sent]); cuda_ack_off(sc); cuda_tip(sc); } } break; case CUDA_OUT: i = cuda_read_reg(sc, vSR); /* reset SR-intr in IFR */ sc->sc_sent++; if (cuda_intr_state(sc)) { /* ADB intr low during write */ cuda_in(sc); /* make sure SR is set to IN */ cuda_idle(sc); sc->sc_sent = 0; /* must start all over */ sc->sc_state = CUDA_IDLE; /* new state */ sc->sc_received = 0; sc->sc_waiting = 1; /* must retry when done with * read */ DELAY(150); goto switch_start; /* process next state right * now */ break; } if (sc->sc_out_length == sc->sc_sent) { /* check for done */ sc->sc_waiting = 0; /* done writing */ sc->sc_state = CUDA_IDLE; /* signal bus is idle */ cuda_in(sc); cuda_idle(sc); } else { /* send next byte */ cuda_write_reg(sc, vSR, sc->sc_out[sc->sc_sent]); cuda_toggle_ack(sc); /* signal byte ready to * shift */ } break; case CUDA_NOTREADY: break; default: break; } mtx_unlock(&sc->sc_mutex); if (process_inbound) cuda_send_inbound(sc); mtx_lock(&sc->sc_mutex); /* If we have another packet waiting, set it up */ if (!sc->sc_waiting && sc->sc_state == CUDA_IDLE) cuda_send_outbound(sc); mtx_unlock(&sc->sc_mutex); } static u_int cuda_adb_send(device_t dev, u_char command_byte, int len, u_char *data, u_char poll) { struct cuda_softc *sc = device_get_softc(dev); uint8_t packet[16]; int i; /* construct an ADB command packet and send it */ packet[0] = CUDA_ADB; packet[1] = command_byte; for (i = 0; i < len; i++) packet[i + 2] = data[i]; cuda_send(sc, poll, len + 2, packet); return (0); } static u_int cuda_adb_autopoll(device_t dev, uint16_t mask) { struct cuda_softc *sc = device_get_softc(dev); uint8_t cmd[] = {CUDA_PSEUDO, CMD_AUTOPOLL, mask != 0}; mtx_lock(&sc->sc_mutex); if (cmd[2] == sc->sc_autopoll) { mtx_unlock(&sc->sc_mutex); return (0); } sc->sc_autopoll = -1; cuda_send(sc, 1, 3, cmd); mtx_unlock(&sc->sc_mutex); return (0); } static void cuda_shutdown(void *xsc, int howto) { struct cuda_softc *sc = xsc; uint8_t cmd[] = {CUDA_PSEUDO, 0}; cmd[1] = (howto & RB_HALT) ? CMD_POWEROFF : CMD_RESET; cuda_poll(sc->sc_dev); cuda_send(sc, 1, 2, cmd); while (1) cuda_poll(sc->sc_dev); } #define DIFF19041970 2082844800 static int cuda_gettime(device_t dev, struct timespec *ts) { struct cuda_softc *sc = device_get_softc(dev); uint8_t cmd[] = {CUDA_PSEUDO, CMD_READ_RTC}; mtx_lock(&sc->sc_mutex); sc->sc_rtc = -1; cuda_send(sc, 1, 2, cmd); if (sc->sc_rtc == -1) mtx_sleep(&sc->sc_rtc, &sc->sc_mutex, 0, "rtc", 100); ts->tv_sec = sc->sc_rtc - DIFF19041970; ts->tv_nsec = 0; mtx_unlock(&sc->sc_mutex); return (0); } static int cuda_settime(device_t dev, struct timespec *ts) { struct cuda_softc *sc = device_get_softc(dev); uint8_t cmd[] = {CUDA_PSEUDO, CMD_WRITE_RTC, 0, 0, 0, 0}; uint32_t sec; sec = ts->tv_sec + DIFF19041970; memcpy(&cmd[2], &sec, sizeof(sec)); mtx_lock(&sc->sc_mutex); cuda_send(sc, 0, 6, cmd); mtx_unlock(&sc->sc_mutex); return (0); } Index: head/sys/powerpc/powermac/dbdma.c =================================================================== --- head/sys/powerpc/powermac/dbdma.c (revision 326260) +++ head/sys/powerpc/powermac/dbdma.c (revision 326261) @@ -1,383 +1,385 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2008 Nathan Whitehorn * All rights reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "dbdmavar.h" static MALLOC_DEFINE(M_DBDMA, "dbdma", "DBDMA Command List"); static uint32_t dbdma_read_reg(dbdma_channel_t *, u_int); static void dbdma_write_reg(dbdma_channel_t *, u_int, uint32_t); static void dbdma_phys_callback(void *, bus_dma_segment_t *, int, int); static void dbdma_phys_callback(void *chan, bus_dma_segment_t *segs, int nsegs, int error) { dbdma_channel_t *channel = (dbdma_channel_t *)(chan); channel->sc_slots_pa = segs[0].ds_addr; dbdma_write_reg(channel, CHAN_CMDPTR, channel->sc_slots_pa); } int dbdma_allocate_channel(struct resource *dbdma_regs, u_int offset, bus_dma_tag_t parent_dma, int slots, dbdma_channel_t **chan) { int error = 0; dbdma_channel_t *channel; channel = *chan = malloc(sizeof(struct dbdma_channel), M_DBDMA, M_WAITOK | M_ZERO); channel->sc_regs = dbdma_regs; channel->sc_off = offset; dbdma_stop(channel); channel->sc_slots_pa = 0; error = bus_dma_tag_create(parent_dma, 16, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, PAGE_SIZE, 1, PAGE_SIZE, 0, NULL, NULL, &(channel->sc_dmatag)); error = bus_dmamem_alloc(channel->sc_dmatag, (void **)&channel->sc_slots, BUS_DMA_WAITOK | BUS_DMA_ZERO, &channel->sc_dmamap); error = bus_dmamap_load(channel->sc_dmatag, channel->sc_dmamap, channel->sc_slots, PAGE_SIZE, dbdma_phys_callback, channel, 0); dbdma_write_reg(channel, CHAN_CMDPTR_HI, 0); channel->sc_nslots = slots; return (error); } int dbdma_resize_channel(dbdma_channel_t *chan, int newslots) { if (newslots > (PAGE_SIZE / sizeof(struct dbdma_command))) return (-1); chan->sc_nslots = newslots; return (0); } int dbdma_free_channel(dbdma_channel_t *chan) { dbdma_stop(chan); bus_dmamem_free(chan->sc_dmatag, chan->sc_slots, chan->sc_dmamap); bus_dma_tag_destroy(chan->sc_dmatag); free(chan, M_DBDMA); return (0); } uint16_t dbdma_get_cmd_status(dbdma_channel_t *chan, int slot) { bus_dmamap_sync(chan->sc_dmatag, chan->sc_dmamap, BUS_DMASYNC_POSTREAD); /* * I really did mean to swap resCount and xferStatus here, to * account for the quad-word little endian fields. */ return (le16toh(chan->sc_slots[slot].resCount)); } void dbdma_clear_cmd_status(dbdma_channel_t *chan, int slot) { /* See endian note above */ chan->sc_slots[slot].resCount = 0; } uint16_t dbdma_get_residuals(dbdma_channel_t *chan, int slot) { bus_dmamap_sync(chan->sc_dmatag, chan->sc_dmamap, BUS_DMASYNC_POSTREAD); return (le16toh(chan->sc_slots[slot].xferStatus)); } void dbdma_reset(dbdma_channel_t *chan) { dbdma_stop(chan); dbdma_set_current_cmd(chan, 0); dbdma_run(chan); } void dbdma_run(dbdma_channel_t *chan) { uint32_t control_reg; control_reg = DBDMA_STATUS_RUN | DBDMA_STATUS_PAUSE | DBDMA_STATUS_WAKE | DBDMA_STATUS_DEAD; control_reg <<= DBDMA_REG_MASK_SHIFT; control_reg |= DBDMA_STATUS_RUN; dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg); } void dbdma_pause(dbdma_channel_t *chan) { uint32_t control_reg; control_reg = DBDMA_STATUS_PAUSE; control_reg <<= DBDMA_REG_MASK_SHIFT; control_reg |= DBDMA_STATUS_PAUSE; dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg); } void dbdma_wake(dbdma_channel_t *chan) { uint32_t control_reg; control_reg = DBDMA_STATUS_WAKE | DBDMA_STATUS_PAUSE | DBDMA_STATUS_RUN | DBDMA_STATUS_DEAD; control_reg <<= DBDMA_REG_MASK_SHIFT; control_reg |= DBDMA_STATUS_WAKE | DBDMA_STATUS_RUN; dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg); } void dbdma_stop(dbdma_channel_t *chan) { uint32_t control_reg; control_reg = DBDMA_STATUS_RUN; control_reg <<= DBDMA_REG_MASK_SHIFT; dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg); while (dbdma_read_reg(chan, CHAN_STATUS_REG) & DBDMA_STATUS_ACTIVE) DELAY(5); } void dbdma_set_current_cmd(dbdma_channel_t *chan, int slot) { uint32_t cmd; cmd = chan->sc_slots_pa + slot * sizeof(struct dbdma_command); dbdma_write_reg(chan, CHAN_CMDPTR, cmd); } uint16_t dbdma_get_chan_status(dbdma_channel_t *chan) { uint32_t status_reg; status_reg = dbdma_read_reg(chan, CHAN_STATUS_REG); return (status_reg & 0x0000ffff); } uint8_t dbdma_get_device_status(dbdma_channel_t *chan) { return (dbdma_get_chan_status(chan) & 0x00ff); } void dbdma_set_device_status(dbdma_channel_t *chan, uint8_t mask, uint8_t value) { uint32_t control_reg; control_reg = mask; control_reg <<= DBDMA_REG_MASK_SHIFT; control_reg |= value; dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg); } void dbdma_set_interrupt_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t val) { uint32_t intr_select; intr_select = mask; intr_select <<= DBDMA_REG_MASK_SHIFT; intr_select |= val; dbdma_write_reg(chan, CHAN_INTR_SELECT, intr_select); } void dbdma_set_branch_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t val) { uint32_t br_select; br_select = mask; br_select <<= DBDMA_REG_MASK_SHIFT; br_select |= val; dbdma_write_reg(chan, CHAN_BRANCH_SELECT, br_select); } void dbdma_set_wait_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t val) { uint32_t wait_select; wait_select = mask; wait_select <<= DBDMA_REG_MASK_SHIFT; wait_select |= val; dbdma_write_reg(chan, CHAN_WAIT_SELECT, wait_select); } void dbdma_insert_command(dbdma_channel_t *chan, int slot, int command, int stream, bus_addr_t data, size_t count, uint8_t interrupt, uint8_t branch, uint8_t wait, uint32_t branch_slot) { struct dbdma_command cmd; uint32_t *flip; cmd.cmd = command; cmd.key = stream; cmd.intr = interrupt; cmd.branch = branch; cmd.wait = wait; cmd.reqCount = count; cmd.address = (uint32_t)(data); if (command != DBDMA_STORE_QUAD && command != DBDMA_LOAD_QUAD) cmd.cmdDep = chan->sc_slots_pa + branch_slot * sizeof(struct dbdma_command); else cmd.cmdDep = branch_slot; cmd.resCount = 0; cmd.xferStatus = 0; /* * Move quadwords to little-endian. God only knows why * Apple thought this was a good idea. */ flip = (uint32_t *)(&cmd); flip[0] = htole32(flip[0]); flip[1] = htole32(flip[1]); flip[2] = htole32(flip[2]); chan->sc_slots[slot] = cmd; } void dbdma_insert_stop(dbdma_channel_t *chan, int slot) { dbdma_insert_command(chan, slot, DBDMA_STOP, 0, 0, 0, DBDMA_NEVER, DBDMA_NEVER, DBDMA_NEVER, 0); } void dbdma_insert_nop(dbdma_channel_t *chan, int slot) { dbdma_insert_command(chan, slot, DBDMA_NOP, 0, 0, 0, DBDMA_NEVER, DBDMA_NEVER, DBDMA_NEVER, 0); } void dbdma_insert_branch(dbdma_channel_t *chan, int slot, int to_slot) { dbdma_insert_command(chan, slot, DBDMA_NOP, 0, 0, 0, DBDMA_NEVER, DBDMA_ALWAYS, DBDMA_NEVER, to_slot); } void dbdma_sync_commands(dbdma_channel_t *chan, bus_dmasync_op_t op) { bus_dmamap_sync(chan->sc_dmatag, chan->sc_dmamap, op); } void dbdma_save_state(dbdma_channel_t *chan) { chan->sc_saved_regs[0] = dbdma_read_reg(chan, CHAN_CMDPTR); chan->sc_saved_regs[1] = dbdma_read_reg(chan, CHAN_CMDPTR_HI); chan->sc_saved_regs[2] = dbdma_read_reg(chan, CHAN_INTR_SELECT); chan->sc_saved_regs[3] = dbdma_read_reg(chan, CHAN_BRANCH_SELECT); chan->sc_saved_regs[4] = dbdma_read_reg(chan, CHAN_WAIT_SELECT); dbdma_stop(chan); } void dbdma_restore_state(dbdma_channel_t *chan) { dbdma_wake(chan); dbdma_write_reg(chan, CHAN_CMDPTR, chan->sc_saved_regs[0]); dbdma_write_reg(chan, CHAN_CMDPTR_HI, chan->sc_saved_regs[1]); dbdma_write_reg(chan, CHAN_INTR_SELECT, chan->sc_saved_regs[2]); dbdma_write_reg(chan, CHAN_BRANCH_SELECT, chan->sc_saved_regs[3]); dbdma_write_reg(chan, CHAN_WAIT_SELECT, chan->sc_saved_regs[4]); } static uint32_t dbdma_read_reg(dbdma_channel_t *chan, u_int offset) { return (bus_read_4(chan->sc_regs, chan->sc_off + offset)); } static void dbdma_write_reg(dbdma_channel_t *chan, u_int offset, uint32_t val) { bus_write_4(chan->sc_regs, chan->sc_off + offset, val); } Index: head/sys/powerpc/powermac/dbdmavar.h =================================================================== --- head/sys/powerpc/powermac/dbdmavar.h (revision 326260) +++ head/sys/powerpc/powermac/dbdmavar.h (revision 326261) @@ -1,106 +1,108 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2008 Nathan Whitehorn * All rights reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _POWERPC_POWERMAC_DBDMAVAR_H_ #define _POWERPC_POWERMAC_DBDMAVAR_H_ struct dbdma_command { uint8_t cmd:4; /* DBDMA command */ uint8_t _resd1:1; uint8_t key:3; /* Stream number, or 6 for KEY_SYSTEM */ uint8_t _resd2:2; /* Interrupt, branch, and wait flags */ uint8_t intr:2; uint8_t branch:2; uint8_t wait:2; uint16_t reqCount; /* Bytes to transfer */ uint32_t address; /* 32-bit system physical address */ uint32_t cmdDep; /* Branch address or quad word to load/store */ uint16_t xferStatus; /* Contents of channel status after completion */ uint16_t resCount; /* Number of residual bytes outstanding */ }; struct dbdma_channel { struct resource *sc_regs; u_int sc_off; struct dbdma_command *sc_slots; int sc_nslots; bus_addr_t sc_slots_pa; bus_dma_tag_t sc_dmatag; bus_dmamap_t sc_dmamap; uint32_t sc_saved_regs[5]; }; /* DBDMA registers are found at 0x8000 + n*0x100 in the macio register space, and are laid out as follows within each block: Address: Description: Length (bytes): 0x000 Channel Control 4 0x004 Channel Status 4 0x00C Command Phys Addr 4 0x010 Interrupt Select 4 0x014 Branch Select 4 0x018 Wait Select 4 */ #define CHAN_CONTROL_REG 0x00 #define CHAN_STATUS_REG 0x04 #define CHAN_CMDPTR_HI 0x08 #define CHAN_CMDPTR 0x0C #define CHAN_INTR_SELECT 0x10 #define CHAN_BRANCH_SELECT 0x14 #define CHAN_WAIT_SELECT 0x18 /* Channel control is the write channel to channel status, the upper 16 bits are a mask of which bytes to change */ #define DBDMA_REG_MASK_SHIFT 16 /* Status bits 0-7 are device dependent status bits */ /* The Interrupt/Branch/Wait Select triggers the corresponding condition bits in the event that (select.mask & device dependent status) == select.value They are defined a follows: Byte 1: Reserved Byte 2: Mask Byte 3: Reserved Byte 4: Value */ #endif /* _POWERPC_POWERMAC_DBDMAVAR_H_ */ Index: head/sys/powerpc/powermac/fcu.c =================================================================== --- head/sys/powerpc/powermac/fcu.c (revision 326260) +++ head/sys/powerpc/powermac/fcu.c (revision 326261) @@ -1,677 +1,679 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2010 Andreas Tobler * 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$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* FCU registers * /u3@0,f8000000/i2c@f8001000/fan@15e */ #define FCU_RPM_FAIL 0x0b /* fans states in bits 0<1-6>7 */ #define FCU_RPM_AVAILABLE 0x0c #define FCU_RPM_ACTIVE 0x0d #define FCU_RPM_READ(x) 0x11 + (x) * 2 #define FCU_RPM_SET(x) 0x10 + (x) * 2 #define FCU_PWM_FAIL 0x2b #define FCU_PWM_AVAILABLE 0x2c #define FCU_PWM_ACTIVE 0x2d #define FCU_PWM_RPM(x) 0x31 + (x) * 2 /* Get RPM. */ #define FCU_PWM_SGET(x) 0x30 + (x) * 2 /* Set or get PWM. */ struct fcu_fan { struct pmac_fan fan; device_t dev; int id; enum { FCU_FAN_RPM, FCU_FAN_PWM } type; int setpoint; int rpm; }; struct fcu_softc { device_t sc_dev; struct intr_config_hook enum_hook; uint32_t sc_addr; struct fcu_fan *sc_fans; int sc_nfans; }; /* We can read the PWM and the RPM from a PWM controlled fan. * Offer both values via sysctl. */ enum { FCU_PWM_SYSCTL_PWM = 1 << 8, FCU_PWM_SYSCTL_RPM = 2 << 8 }; static int fcu_rpm_shift; /* Regular bus attachment functions */ static int fcu_probe(device_t); static int fcu_attach(device_t); /* Utility functions */ static void fcu_attach_fans(device_t dev); static int fcu_fill_fan_prop(device_t dev); static int fcu_fan_set_rpm(struct fcu_fan *fan, int rpm); static int fcu_fan_get_rpm(struct fcu_fan *fan); static int fcu_fan_set_pwm(struct fcu_fan *fan, int pwm); static int fcu_fan_get_pwm(device_t dev, struct fcu_fan *fan, int *pwm, int *rpm); static int fcu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS); static void fcu_start(void *xdev); static int fcu_write(device_t dev, uint32_t addr, uint8_t reg, uint8_t *buf, int len); static int fcu_read_1(device_t dev, uint32_t addr, uint8_t reg, uint8_t *data); static device_method_t fcu_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fcu_probe), DEVMETHOD(device_attach, fcu_attach), { 0, 0 }, }; static driver_t fcu_driver = { "fcu", fcu_methods, sizeof(struct fcu_softc) }; static devclass_t fcu_devclass; DRIVER_MODULE(fcu, iicbus, fcu_driver, fcu_devclass, 0, 0); static MALLOC_DEFINE(M_FCU, "fcu", "FCU Sensor Information"); static int fcu_write(device_t dev, uint32_t addr, uint8_t reg, uint8_t *buff, int len) { unsigned char buf[4]; int try = 0; struct iic_msg msg[] = { { addr, IIC_M_WR, 0, buf } }; msg[0].len = len + 1; buf[0] = reg; memcpy(buf + 1, buff, len); for (;;) { if (iicbus_transfer(dev, msg, 1) == 0) return (0); if (++try > 5) { device_printf(dev, "iicbus write failed\n"); return (-1); } pause("fcu_write", hz); } } static int fcu_read_1(device_t dev, uint32_t addr, uint8_t reg, uint8_t *data) { uint8_t buf[4]; int err, try = 0; struct iic_msg msg[2] = { { addr, IIC_M_WR | IIC_M_NOSTOP, 1, ® }, { addr, IIC_M_RD, 1, buf }, }; for (;;) { err = iicbus_transfer(dev, msg, 2); if (err != 0) goto retry; *data = *((uint8_t*)buf); return (0); retry: if (++try > 5) { device_printf(dev, "iicbus read failed\n"); return (-1); } pause("fcu_read_1", hz); } } static int fcu_probe(device_t dev) { const char *name, *compatible; struct fcu_softc *sc; name = ofw_bus_get_name(dev); compatible = ofw_bus_get_compat(dev); if (!name) return (ENXIO); if (strcmp(name, "fan") != 0 || strcmp(compatible, "fcu") != 0) return (ENXIO); sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_addr = iicbus_get_addr(dev); device_set_desc(dev, "Apple Fan Control Unit"); return (0); } static int fcu_attach(device_t dev) { struct fcu_softc *sc; sc = device_get_softc(dev); sc->enum_hook.ich_func = fcu_start; sc->enum_hook.ich_arg = dev; /* We have to wait until interrupts are enabled. I2C read and write * only works if the interrupts are available. * The unin/i2c is controlled by the htpic on unin. But this is not * the master. The openpic on mac-io is controlling the htpic. * This one gets attached after the mac-io probing and then the * interrupts will be available. */ if (config_intrhook_establish(&sc->enum_hook) != 0) return (ENOMEM); return (0); } static void fcu_start(void *xdev) { unsigned char buf[1] = { 0xff }; struct fcu_softc *sc; device_t dev = (device_t)xdev; sc = device_get_softc(dev); /* Start the fcu device. */ fcu_write(sc->sc_dev, sc->sc_addr, 0xe, buf, 1); fcu_write(sc->sc_dev, sc->sc_addr, 0x2e, buf, 1); fcu_read_1(sc->sc_dev, sc->sc_addr, 0, buf); fcu_rpm_shift = (buf[0] == 1) ? 2 : 3; device_printf(dev, "FCU initialized, RPM shift: %d\n", fcu_rpm_shift); /* Detect and attach child devices. */ fcu_attach_fans(dev); config_intrhook_disestablish(&sc->enum_hook); } static int fcu_fan_set_rpm(struct fcu_fan *fan, int rpm) { uint8_t reg; struct fcu_softc *sc; unsigned char buf[2]; sc = device_get_softc(fan->dev); /* Clamp to allowed range */ rpm = max(fan->fan.min_rpm, rpm); rpm = min(fan->fan.max_rpm, rpm); if (fan->type == FCU_FAN_RPM) { reg = FCU_RPM_SET(fan->id); fan->setpoint = rpm; } else { device_printf(fan->dev, "Unknown fan type: %d\n", fan->type); return (ENXIO); } buf[0] = rpm >> (8 - fcu_rpm_shift); buf[1] = rpm << fcu_rpm_shift; if (fcu_write(sc->sc_dev, sc->sc_addr, reg, buf, 2) < 0) return (EIO); return (0); } static int fcu_fan_get_rpm(struct fcu_fan *fan) { uint8_t reg; struct fcu_softc *sc; uint8_t buff[2] = { 0, 0 }; uint8_t active = 0, avail = 0, fail = 0; int rpm; sc = device_get_softc(fan->dev); if (fan->type == FCU_FAN_RPM) { /* Check if the fan is available. */ reg = FCU_RPM_AVAILABLE; if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &avail) < 0) return (-1); if ((avail & (1 << fan->id)) == 0) { device_printf(fan->dev, "RPM Fan not available ID: %d\n", fan->id); return (-1); } /* Check if we have a failed fan. */ reg = FCU_RPM_FAIL; if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &fail) < 0) return (-1); if ((fail & (1 << fan->id)) != 0) { device_printf(fan->dev, "RPM Fan failed ID: %d\n", fan->id); return (-1); } /* Check if fan is active. */ reg = FCU_RPM_ACTIVE; if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &active) < 0) return (-1); if ((active & (1 << fan->id)) == 0) { device_printf(fan->dev, "RPM Fan not active ID: %d\n", fan->id); return (-1); } reg = FCU_RPM_READ(fan->id); } else { device_printf(fan->dev, "Unknown fan type: %d\n", fan->type); return (-1); } /* It seems that we can read the fans rpm. */ if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, buff) < 0) return (-1); rpm = (buff[0] << (8 - fcu_rpm_shift)) | buff[1] >> fcu_rpm_shift; return (rpm); } static int fcu_fan_set_pwm(struct fcu_fan *fan, int pwm) { uint8_t reg; struct fcu_softc *sc; uint8_t buf[2]; sc = device_get_softc(fan->dev); /* Clamp to allowed range */ pwm = max(fan->fan.min_rpm, pwm); pwm = min(fan->fan.max_rpm, pwm); if (fan->type == FCU_FAN_PWM) { reg = FCU_PWM_SGET(fan->id); if (pwm > 100) pwm = 100; if (pwm < 30) pwm = 30; fan->setpoint = pwm; } else { device_printf(fan->dev, "Unknown fan type: %d\n", fan->type); return (EIO); } buf[0] = (pwm * 2550) / 1000; if (fcu_write(sc->sc_dev, sc->sc_addr, reg, buf, 1) < 0) return (EIO); return (0); } static int fcu_fan_get_pwm(device_t dev, struct fcu_fan *fan, int *pwm, int *rpm) { uint8_t reg; struct fcu_softc *sc; uint8_t buf[2]; uint8_t active = 0, avail = 0, fail = 0; sc = device_get_softc(dev); if (fan->type == FCU_FAN_PWM) { /* Check if the fan is available. */ reg = FCU_PWM_AVAILABLE; if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &avail) < 0) return (-1); if ((avail & (1 << fan->id)) == 0) { device_printf(dev, "PWM Fan not available ID: %d\n", fan->id); return (-1); } /* Check if we have a failed fan. */ reg = FCU_PWM_FAIL; if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &fail) < 0) return (-1); if ((fail & (1 << fan->id)) != 0) { device_printf(dev, "PWM Fan failed ID: %d\n", fan->id); return (-1); } /* Check if fan is active. */ reg = FCU_PWM_ACTIVE; if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &active) < 0) return (-1); if ((active & (1 << fan->id)) == 0) { device_printf(dev, "PWM Fan not active ID: %d\n", fan->id); return (-1); } reg = FCU_PWM_SGET(fan->id); } else { device_printf(dev, "Unknown fan type: %d\n", fan->type); return (EIO); } /* It seems that we can read the fans pwm. */ if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, buf) < 0) return (-1); *pwm = (buf[0] * 1000) / 2550; /* Now read the rpm. */ reg = FCU_PWM_RPM(fan->id); if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, buf) < 0) return (-1); *rpm = (buf[0] << (8 - fcu_rpm_shift)) | buf[1] >> fcu_rpm_shift; return (0); } /* * This function returns the number of fans. If we call it the second time * and we have allocated memory for sc->sc_fans, we fill in the properties. */ static int fcu_fill_fan_prop(device_t dev) { phandle_t child; struct fcu_softc *sc; u_int id[8]; char location[96]; char type[64]; int i = 0, j, len = 0, prop_len, prev_len = 0; sc = device_get_softc(dev); child = ofw_bus_get_node(dev); /* Fill the fan location property. */ prop_len = OF_getprop(child, "hwctrl-location", location, sizeof(location)); while (len < prop_len) { if (sc->sc_fans != NULL) { strcpy(sc->sc_fans[i].fan.name, location + len); } prev_len = strlen(location + len) + 1; len += prev_len; i++; } if (sc->sc_fans == NULL) return (i); /* Fill the fan type property. */ len = 0; i = 0; prev_len = 0; prop_len = OF_getprop(child, "hwctrl-type", type, sizeof(type)); while (len < prop_len) { if (strcmp(type + len, "fan-rpm") == 0) sc->sc_fans[i].type = FCU_FAN_RPM; else sc->sc_fans[i].type = FCU_FAN_PWM; prev_len = strlen(type + len) + 1; len += prev_len; i++; } /* Fill the fan ID property. */ prop_len = OF_getprop(child, "hwctrl-id", id, sizeof(id)); for (j = 0; j < i; j++) sc->sc_fans[j].id = ((id[j] >> 8) & 0x0f) % 8; /* Fill the fan zone property. */ prop_len = OF_getprop(child, "hwctrl-zone", id, sizeof(id)); for (j = 0; j < i; j++) sc->sc_fans[j].fan.zone = id[j]; /* Finish setting up fan properties */ for (j = 0; j < i; j++) { sc->sc_fans[j].dev = sc->sc_dev; if (sc->sc_fans[j].type == FCU_FAN_RPM) { sc->sc_fans[j].fan.min_rpm = 4800 >> fcu_rpm_shift; sc->sc_fans[j].fan.max_rpm = 56000 >> fcu_rpm_shift; sc->sc_fans[j].setpoint = fcu_fan_get_rpm(&sc->sc_fans[j]); sc->sc_fans[j].fan.read = (int (*)(struct pmac_fan *))(fcu_fan_get_rpm); sc->sc_fans[j].fan.set = (int (*)(struct pmac_fan *, int))(fcu_fan_set_rpm); } else { sc->sc_fans[j].fan.min_rpm = 30; /* Percent */ sc->sc_fans[j].fan.max_rpm = 100; sc->sc_fans[j].fan.read = NULL; sc->sc_fans[j].fan.set = (int (*)(struct pmac_fan *, int))(fcu_fan_set_pwm); } sc->sc_fans[j].fan.default_rpm = sc->sc_fans[j].fan.max_rpm; } return (i); } static int fcu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS) { device_t fcu; struct fcu_softc *sc; struct fcu_fan *fan; int rpm = 0, pwm = 0, error = 0; fcu = arg1; sc = device_get_softc(fcu); fan = &sc->sc_fans[arg2 & 0x00ff]; if (fan->type == FCU_FAN_RPM) { rpm = fcu_fan_get_rpm(fan); if (rpm < 0) return (EIO); error = sysctl_handle_int(oidp, &rpm, 0, req); } else { error = fcu_fan_get_pwm(fcu, fan, &pwm, &rpm); if (error < 0) return (EIO); switch (arg2 & 0xff00) { case FCU_PWM_SYSCTL_PWM: error = sysctl_handle_int(oidp, &pwm, 0, req); break; case FCU_PWM_SYSCTL_RPM: error = sysctl_handle_int(oidp, &rpm, 0, req); break; default: /* This should never happen */ return (EINVAL); } } /* We can only read the RPM from a PWM controlled fan, so return. */ if ((arg2 & 0xff00) == FCU_PWM_SYSCTL_RPM) return (0); if (error || !req->newptr) return (error); if (fan->type == FCU_FAN_RPM) return (fcu_fan_set_rpm(fan, rpm)); else return (fcu_fan_set_pwm(fan, pwm)); } static void fcu_attach_fans(device_t dev) { struct fcu_softc *sc; struct sysctl_oid *oid, *fanroot_oid; struct sysctl_ctx_list *ctx; char sysctl_name[32]; int i, j; sc = device_get_softc(dev); sc->sc_nfans = 0; /* Count the actual number of fans. */ sc->sc_nfans = fcu_fill_fan_prop(dev); device_printf(dev, "%d fans detected!\n", sc->sc_nfans); if (sc->sc_nfans == 0) { device_printf(dev, "WARNING: No fans detected!\n"); return; } sc->sc_fans = malloc(sc->sc_nfans * sizeof(struct fcu_fan), M_FCU, M_WAITOK | M_ZERO); ctx = device_get_sysctl_ctx(dev); fanroot_oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "fans", CTLFLAG_RD, 0, "FCU Fan Information"); /* Now we can fill the properties into the allocated struct. */ sc->sc_nfans = fcu_fill_fan_prop(dev); /* Register fans with pmac_thermal */ for (i = 0; i < sc->sc_nfans; i++) pmac_thermal_fan_register(&sc->sc_fans[i].fan); /* Add sysctls for the fans. */ for (i = 0; i < sc->sc_nfans; i++) { for (j = 0; j < strlen(sc->sc_fans[i].fan.name); j++) { sysctl_name[j] = tolower(sc->sc_fans[i].fan.name[j]); if (isspace(sysctl_name[j])) sysctl_name[j] = '_'; } sysctl_name[j] = 0; if (sc->sc_fans[i].type == FCU_FAN_RPM) { oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(fanroot_oid), OID_AUTO, sysctl_name, CTLFLAG_RD, 0, "Fan Information"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "minrpm", CTLFLAG_RD, &(sc->sc_fans[i].fan.min_rpm), 0, "Minimum allowed RPM"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "maxrpm", CTLFLAG_RD, &(sc->sc_fans[i].fan.max_rpm), 0, "Maximum allowed RPM"); /* I use i to pass the fan id. */ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "rpm", CTLTYPE_INT | CTLFLAG_RW, dev, i, fcu_fanrpm_sysctl, "I", "Fan RPM"); } else { fcu_fan_get_pwm(dev, &sc->sc_fans[i], &sc->sc_fans[i].setpoint, &sc->sc_fans[i].rpm); oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(fanroot_oid), OID_AUTO, sysctl_name, CTLFLAG_RD, 0, "Fan Information"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "minpwm", CTLFLAG_RD, &(sc->sc_fans[i].fan.min_rpm), 0, "Minimum allowed PWM in %"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "maxpwm", CTLFLAG_RD, &(sc->sc_fans[i].fan.max_rpm), 0, "Maximum allowed PWM in %"); /* I use i to pass the fan id or'ed with the type * of info I want to display/modify. */ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "pwm", CTLTYPE_INT | CTLFLAG_RW, dev, FCU_PWM_SYSCTL_PWM | i, fcu_fanrpm_sysctl, "I", "Fan PWM in %"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "rpm", CTLTYPE_INT | CTLFLAG_RD, dev, FCU_PWM_SYSCTL_RPM | i, fcu_fanrpm_sysctl, "I", "Fan RPM"); } } /* Dump fan location, type & RPM. */ if (bootverbose) { device_printf(dev, "Fans\n"); for (i = 0; i < sc->sc_nfans; i++) { device_printf(dev, "Location: %s type: %d ID: %d " "RPM: %d\n", sc->sc_fans[i].fan.name, sc->sc_fans[i].type, sc->sc_fans[i].id, (sc->sc_fans[i].type == FCU_FAN_RPM) ? sc->sc_fans[i].setpoint : sc->sc_fans[i].rpm ); } } } Index: head/sys/powerpc/powermac/grackle.c =================================================================== --- head/sys/powerpc/powermac/grackle.c (revision 326260) +++ head/sys/powerpc/powermac/grackle.c (revision 326261) @@ -1,323 +1,325 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2003 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" /* * Device interface. */ static int grackle_probe(device_t); static int grackle_attach(device_t); /* * pcib interface. */ static u_int32_t grackle_read_config(device_t, u_int, u_int, u_int, u_int, int); static void grackle_write_config(device_t, u_int, u_int, u_int, u_int, u_int32_t, int); /* * Local routines. */ static int grackle_enable_config(struct grackle_softc *, u_int, u_int, u_int, u_int); static void grackle_disable_config(struct grackle_softc *); static int badaddr(void *, size_t); /* * Driver methods. */ static device_method_t grackle_methods[] = { /* Device interface */ DEVMETHOD(device_probe, grackle_probe), DEVMETHOD(device_attach, grackle_attach), /* pcib interface */ DEVMETHOD(pcib_read_config, grackle_read_config), DEVMETHOD(pcib_write_config, grackle_write_config), DEVMETHOD_END }; static devclass_t grackle_devclass; DEFINE_CLASS_1(pcib, grackle_driver, grackle_methods, sizeof(struct grackle_softc), ofw_pci_driver); DRIVER_MODULE(grackle, ofwbus, grackle_driver, grackle_devclass, 0, 0); static int grackle_probe(device_t dev) { const char *type, *compatible; type = ofw_bus_get_type(dev); compatible = ofw_bus_get_compat(dev); if (type == NULL || compatible == NULL) return (ENXIO); if (strcmp(type, "pci") != 0 || strcmp(compatible, "grackle") != 0) return (ENXIO); device_set_desc(dev, "MPC106 (Grackle) Host-PCI bridge"); return (0); } static int grackle_attach(device_t dev) { struct grackle_softc *sc; sc = device_get_softc(dev); /* * The Grackle PCI config addr/data registers are actually in * PCI space, but since they are needed to actually probe the * PCI bus, use the fact that they are also available directly * on the processor bus and map them */ sc->sc_addr = (vm_offset_t)pmap_mapdev(GRACKLE_ADDR, PAGE_SIZE); sc->sc_data = (vm_offset_t)pmap_mapdev(GRACKLE_DATA, PAGE_SIZE); return (ofw_pci_attach(dev)); } static u_int32_t grackle_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int width) { struct grackle_softc *sc; vm_offset_t caoff; u_int32_t retval = 0xffffffff; sc = device_get_softc(dev); caoff = sc->sc_data + (reg & 0x03); if (grackle_enable_config(sc, bus, slot, func, reg) != 0) { /* * Config probes to non-existent devices on the * secondary bus generates machine checks. Be sure * to catch these. */ if (bus > 0) { if (badaddr((void *)sc->sc_data, 4)) { return (retval); } } switch (width) { case 1: retval = (in8rb(caoff)); break; case 2: retval = (in16rb(caoff)); break; case 4: retval = (in32rb(caoff)); break; } } grackle_disable_config(sc); return (retval); } static void grackle_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, u_int32_t val, int width) { struct grackle_softc *sc; vm_offset_t caoff; sc = device_get_softc(dev); caoff = sc->sc_data + (reg & 0x03); if (grackle_enable_config(sc, bus, slot, func, reg)) { switch (width) { case 1: out8rb(caoff, val); (void)in8rb(caoff); break; case 2: out16rb(caoff, val); (void)in16rb(caoff); break; case 4: out32rb(caoff, val); (void)in32rb(caoff); break; } } grackle_disable_config(sc); } static int grackle_enable_config(struct grackle_softc *sc, u_int bus, u_int slot, u_int func, u_int reg) { u_int32_t cfgval; /* * Unlike UniNorth, the format of the config word is the same * for local (0) and remote busses. */ cfgval = (bus << 16) | (slot << 11) | (func << 8) | (reg & 0xFC) | GRACKLE_CFG_ENABLE; out32rb(sc->sc_addr, cfgval); (void) in32rb(sc->sc_addr); return (1); } static void grackle_disable_config(struct grackle_softc *sc) { /* * Clear the GRACKLE_CFG_ENABLE bit to prevent stray * accesses from causing config cycles */ out32rb(sc->sc_addr, 0); } static int badaddr(void *addr, size_t size) { struct thread *td; jmp_buf env, *oldfaultbuf; int x; /* Get rid of any stale machine checks that have been waiting. */ __asm __volatile ("sync; isync"); td = curthread; oldfaultbuf = td->td_pcb->pcb_onfault; td->td_pcb->pcb_onfault = &env; if (setjmp(env)) { td->td_pcb->pcb_onfault = oldfaultbuf; __asm __volatile ("sync"); return 1; } __asm __volatile ("sync"); switch (size) { case 1: x = *(volatile int8_t *)addr; break; case 2: x = *(volatile int16_t *)addr; break; case 4: x = *(volatile int32_t *)addr; break; default: panic("badaddr: invalid size (%zd)", size); } /* Make sure we took the machine check, if we caused one. */ __asm __volatile ("sync; isync"); td->td_pcb->pcb_onfault = oldfaultbuf; __asm __volatile ("sync"); /* To be sure. */ return (0); } /* * Driver to swallow Grackle host bridges from the PCI bus side. */ static int grackle_hb_probe(device_t dev) { if (pci_get_devid(dev) == 0x00021057) { device_set_desc(dev, "Grackle Host to PCI bridge"); device_quiet(dev); return (0); } return (ENXIO); } static int grackle_hb_attach(device_t dev) { return (0); } static device_method_t grackle_hb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, grackle_hb_probe), DEVMETHOD(device_attach, grackle_hb_attach), { 0, 0 } }; static driver_t grackle_hb_driver = { "grackle_hb", grackle_hb_methods, 1, }; static devclass_t grackle_hb_devclass; DRIVER_MODULE(grackle_hb, pci, grackle_hb_driver, grackle_hb_devclass, 0, 0); Index: head/sys/powerpc/powermac/gracklevar.h =================================================================== --- head/sys/powerpc/powermac/gracklevar.h (revision 326260) +++ head/sys/powerpc/powermac/gracklevar.h (revision 326261) @@ -1,51 +1,53 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2003 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _POWERPC_POWERMAC_GRACKLEVAR_H_ #define _POWERPC_POWERMAC_GRACKLEVAR_H_ struct grackle_softc { struct ofw_pci_softc pci_sc; vm_offset_t sc_addr; vm_offset_t sc_data; }; /* * Apple uses address map B for the MPC106 */ #define GRACKLE_ADDR 0xFEC00000 #define GRACKLE_DATA 0xFEE00000 /* * The high bit of the config word is 'Enable'. This should always be * set. */ #define GRACKLE_CFG_ENABLE 0x80000000 #endif /* _POWERPC_POWERMAC_GRACKLEVAR_H_ */ Index: head/sys/powerpc/powermac/hrowpic.c =================================================================== --- head/sys/powerpc/powermac/hrowpic.c (revision 326260) +++ head/sys/powerpc/powermac/hrowpic.c (revision 326261) @@ -1,280 +1,282 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2003 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * A driver for the PIC found in the Heathrow/Paddington MacIO chips. * This was superseded by an OpenPIC in the Keylargo and beyond * MacIO versions. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" /* * MacIO interface */ static int hrowpic_probe(device_t); static int hrowpic_attach(device_t); static void hrowpic_dispatch(device_t, struct trapframe *); static void hrowpic_enable(device_t, u_int, u_int); static void hrowpic_eoi(device_t, u_int); static void hrowpic_ipi(device_t, u_int); static void hrowpic_mask(device_t, u_int); static void hrowpic_unmask(device_t, u_int); static device_method_t hrowpic_methods[] = { /* Device interface */ DEVMETHOD(device_probe, hrowpic_probe), DEVMETHOD(device_attach, hrowpic_attach), /* PIC interface */ DEVMETHOD(pic_dispatch, hrowpic_dispatch), DEVMETHOD(pic_enable, hrowpic_enable), DEVMETHOD(pic_eoi, hrowpic_eoi), DEVMETHOD(pic_ipi, hrowpic_ipi), DEVMETHOD(pic_mask, hrowpic_mask), DEVMETHOD(pic_unmask, hrowpic_unmask), { 0, 0 }, }; static driver_t hrowpic_driver = { "hrowpic", hrowpic_methods, sizeof(struct hrowpic_softc) }; static devclass_t hrowpic_devclass; DRIVER_MODULE(hrowpic, macio, hrowpic_driver, hrowpic_devclass, 0, 0); static uint32_t hrowpic_read_reg(struct hrowpic_softc *sc, u_int reg, u_int bank) { if (bank == HPIC_PRIMARY) reg += HPIC_1ST_OFFSET; return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); } static void hrowpic_write_reg(struct hrowpic_softc *sc, u_int reg, u_int bank, uint32_t val) { if (bank == HPIC_PRIMARY) reg += HPIC_1ST_OFFSET; bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); /* XXX Issue a read to force the write to complete. */ bus_space_read_4(sc->sc_bt, sc->sc_bh, reg); } static int hrowpic_probe(device_t dev) { const char *type = ofw_bus_get_type(dev); /* * OpenPIC cells have a type of "open-pic", so this * is sufficient to identify a Heathrow cell */ if (strcmp(type, "interrupt-controller") != 0) return (ENXIO); /* * The description was already printed out in the nexus * probe, so don't do it again here */ device_set_desc(dev, "Heathrow MacIO interrupt controller"); return (0); } static int hrowpic_attach(device_t dev) { struct hrowpic_softc *sc; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_rrid = 0; sc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rrid, RF_ACTIVE); if (sc->sc_rres == NULL) { device_printf(dev, "Could not alloc mem resource!\n"); return (ENXIO); } sc->sc_bt = rman_get_bustag(sc->sc_rres); sc->sc_bh = rman_get_bushandle(sc->sc_rres); /* * Disable all interrupt sources and clear outstanding interrupts */ hrowpic_write_reg(sc, HPIC_ENABLE, HPIC_PRIMARY, 0); hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_PRIMARY, 0xffffffff); hrowpic_write_reg(sc, HPIC_ENABLE, HPIC_SECONDARY, 0); hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_SECONDARY, 0xffffffff); powerpc_register_pic(dev, ofw_bus_get_node(dev), 64, 0, FALSE); return (0); } /* * Local routines */ static void hrowpic_toggle_irq(struct hrowpic_softc *sc, int irq, int enable) { u_int roffset; u_int rbit; KASSERT((irq > 0) && (irq <= HROWPIC_IRQMAX), ("en irq out of range")); /* * Humor the SMP layer if it wants to set up an IPI handler. */ if (irq == HROWPIC_IRQMAX) return; /* * Calculate prim/sec register bank for the IRQ, update soft copy, * and enable the IRQ as an interrupt source */ roffset = HPIC_INT_TO_BANK(irq); rbit = HPIC_INT_TO_REGBIT(irq); if (enable) sc->sc_softreg[roffset] |= (1 << rbit); else sc->sc_softreg[roffset] &= ~(1 << rbit); hrowpic_write_reg(sc, HPIC_ENABLE, roffset, sc->sc_softreg[roffset]); } /* * PIC I/F methods. */ static void hrowpic_dispatch(device_t dev, struct trapframe *tf) { struct hrowpic_softc *sc; uint64_t mask; uint32_t reg; u_int irq; sc = device_get_softc(dev); while (1) { mask = hrowpic_read_reg(sc, HPIC_STATUS, HPIC_SECONDARY); reg = hrowpic_read_reg(sc, HPIC_STATUS, HPIC_PRIMARY); mask = (mask << 32) | reg; if (mask == 0) break; irq = 0; while (irq < HROWPIC_IRQMAX) { if (mask & 1) powerpc_dispatch_intr(sc->sc_vector[irq], tf); mask >>= 1; irq++; } } } static void hrowpic_enable(device_t dev, u_int irq, u_int vector) { struct hrowpic_softc *sc; sc = device_get_softc(dev); sc->sc_vector[irq] = vector; hrowpic_toggle_irq(sc, irq, 1); } static void hrowpic_eoi(device_t dev, u_int irq) { struct hrowpic_softc *sc; int bank; sc = device_get_softc(dev); bank = (irq >= 32) ? HPIC_SECONDARY : HPIC_PRIMARY ; hrowpic_write_reg(sc, HPIC_CLEAR, bank, 1U << (irq & 0x1f)); } static void hrowpic_ipi(device_t dev, u_int irq) { /* No SMP support. */ } static void hrowpic_mask(device_t dev, u_int irq) { struct hrowpic_softc *sc; sc = device_get_softc(dev); hrowpic_toggle_irq(sc, irq, 0); } static void hrowpic_unmask(device_t dev, u_int irq) { struct hrowpic_softc *sc; sc = device_get_softc(dev); hrowpic_toggle_irq(sc, irq, 1); } Index: head/sys/powerpc/powermac/hrowpicvar.h =================================================================== --- head/sys/powerpc/powermac/hrowpicvar.h (revision 326260) +++ head/sys/powerpc/powermac/hrowpicvar.h (revision 326261) @@ -1,75 +1,77 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2003 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _POWERPC_POWERMAC_HROWPICVAR_H_ #define _POWERPC_POWERMAC_HROWPICVAR_H_ #define HROWPIC_IRQMAX 64 #define HROWPIC_IRQ_REGNUM 32 /* irqs per register */ #define HROWPIC_IRQ_SHIFT 5 /* high or low irq word */ #define HROWPIC_IRQ_MASK ((HROWPIC_IRQMAX-1) >> 1) /* irq bit pos in word */ /* * Register offsets within bank. There are two identical banks, * separated by 16 bytes. Interrupts 0->31 are processed in the * second bank, and 32->63 in the first bank. */ #define HPIC_STATUS 0x00 /* active interrupt sources */ #define HPIC_ENABLE 0x04 /* interrupt asserts ppc EXTINT */ #define HPIC_CLEAR 0x08 /* clear int source */ #define HPIC_TRIGGER 0x0c /* edge/level int trigger */ #define HPIC_PRIMARY 1 /* primary register bank */ #define HPIC_SECONDARY 0 /* secondary register bank */ /* * Convert an interrupt into a prim/sec bank number */ #define HPIC_INT_TO_BANK(x) \ (((x) >> HROWPIC_IRQ_SHIFT) ^ 1) /* * Convert an interrupt into the bit number within a bank register */ #define HPIC_INT_TO_REGBIT(x) \ ((x) & HROWPIC_IRQ_MASK) #define HPIC_1ST_OFFSET 0x10 /* offset to primary reg bank */ struct hrowpic_softc { device_t sc_dev; /* macio device */ struct resource *sc_rres; /* macio bus resource */ bus_space_tag_t sc_bt; /* macio bus tag/handle */ bus_space_handle_t sc_bh; int sc_rrid; uint32_t sc_softreg[2]; /* ENABLE reg copy */ u_int sc_vector[HROWPIC_IRQMAX]; }; #endif /* _POWERPC_POWERMAC_HROWPICVAR_H_ */ Index: head/sys/powerpc/powermac/kiic.c =================================================================== --- head/sys/powerpc/powermac/kiic.c (revision 326260) +++ head/sys/powerpc/powermac/kiic.c (revision 326261) @@ -1,444 +1,446 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright (c) 2001 Tsubai Masanari. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * NetBSD: ki2c.c,v 1.11 2007/12/06 17:00:33 ad Exp * Id: ki2c.c,v 1.7 2002/10/05 09:56:05 tsubai Exp */ /* * Support routines for the Keywest I2C controller. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" /* Keywest I2C Register offsets */ #define MODE 0 #define CONTROL 1 #define STATUS 2 #define ISR 3 #define IER 4 #define ADDR 5 #define SUBADDR 6 #define DATA 7 #define REV 8 /* MODE */ #define I2C_SPEED 0x03 /* Speed mask */ #define I2C_100kHz 0x00 #define I2C_50kHz 0x01 #define I2C_25kHz 0x02 #define I2C_MODE 0x0c /* Mode mask */ #define I2C_DUMBMODE 0x00 /* Dumb mode */ #define I2C_STDMODE 0x04 /* Standard mode */ #define I2C_STDSUBMODE 0x08 /* Standard mode + sub address */ #define I2C_COMBMODE 0x0c /* Combined mode */ #define I2C_PORT 0xf0 /* Port mask */ /* CONTROL */ #define I2C_CT_AAK 0x01 /* Send AAK */ #define I2C_CT_ADDR 0x02 /* Send address(es) */ #define I2C_CT_STOP 0x04 /* Send STOP */ #define I2C_CT_START 0x08 /* Send START */ /* STATUS */ #define I2C_ST_BUSY 0x01 /* Busy */ #define I2C_ST_LASTAAK 0x02 /* Last AAK */ #define I2C_ST_LASTRW 0x04 /* Last R/W */ #define I2C_ST_SDA 0x08 /* SDA */ #define I2C_ST_SCL 0x10 /* SCL */ /* ISR/IER */ #define I2C_INT_DATA 0x01 /* Data byte sent/received */ #define I2C_INT_ADDR 0x02 /* Address sent */ #define I2C_INT_STOP 0x04 /* STOP condition sent */ #define I2C_INT_START 0x08 /* START condition sent */ /* I2C flags */ #define I2C_BUSY 0x01 #define I2C_READING 0x02 #define I2C_ERROR 0x04 #define I2C_SELECTED 0x08 struct kiic_softc { device_t sc_dev; phandle_t sc_node; struct mtx sc_mutex; struct resource *sc_reg; int sc_irqrid; struct resource *sc_irq; void *sc_ih; u_int sc_regstep; u_int sc_flags; u_char *sc_data; int sc_resid; uint16_t sc_i2c_base; device_t sc_iicbus; }; static int kiic_probe(device_t dev); static int kiic_attach(device_t dev); static void kiic_writereg(struct kiic_softc *sc, u_int, u_int); static u_int kiic_readreg(struct kiic_softc *, u_int); static void kiic_setport(struct kiic_softc *, u_int); static void kiic_setmode(struct kiic_softc *, u_int); static void kiic_setspeed(struct kiic_softc *, u_int); static void kiic_intr(void *xsc); static int kiic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs); static phandle_t kiic_get_node(device_t bus, device_t dev); static device_method_t kiic_methods[] = { /* device interface */ DEVMETHOD(device_probe, kiic_probe), DEVMETHOD(device_attach, kiic_attach), /* iicbus interface */ DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_transfer, kiic_transfer), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, kiic_get_node), { 0, 0 } }; static driver_t kiic_driver = { "iichb", kiic_methods, sizeof(struct kiic_softc) }; static devclass_t kiic_devclass; DRIVER_MODULE(kiic, macio, kiic_driver, kiic_devclass, 0, 0); DRIVER_MODULE(kiic, unin, kiic_driver, kiic_devclass, 0, 0); static int kiic_probe(device_t self) { const char *name; name = ofw_bus_get_name(self); if (name && strcmp(name, "i2c") == 0) { device_set_desc(self, "Keywest I2C controller"); return (0); } return (ENXIO); } static int kiic_attach(device_t self) { struct kiic_softc *sc = device_get_softc(self); int rid, rate; phandle_t node; char name[64]; bzero(sc, sizeof(*sc)); sc->sc_dev = self; node = ofw_bus_get_node(self); if (node == 0 || node == -1) { return (EINVAL); } rid = 0; sc->sc_reg = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_reg == NULL) { return (ENOMEM); } if (OF_getencprop(node, "AAPL,i2c-rate", &rate, 4) != 4) { device_printf(self, "cannot get i2c-rate\n"); return (ENXIO); } if (OF_getencprop(node, "AAPL,address-step", &sc->sc_regstep, 4) != 4) { device_printf(self, "unable to find i2c address step\n"); return (ENXIO); } /* * Some Keywest I2C devices have their children attached directly * underneath them. Some have a single 'iicbus' child with the * devices underneath that. Sort this out, and make sure that the * OFW I2C layer has the correct node. * * Note: the I2C children of the Uninorth bridges have two ports. * In general, the port is designated in the 9th bit of the I2C * address. However, for kiic devices with children attached below * an i2c-bus node, the port is indicated in the 'reg' property * of the i2c-bus node. */ sc->sc_node = node; node = OF_child(node); if (OF_getprop(node, "name", name, sizeof(name)) > 0) { if (strcmp(name,"i2c-bus") == 0) { phandle_t reg; if (OF_getprop(node, "reg", ®, sizeof(reg)) > 0) sc->sc_i2c_base = reg << 8; sc->sc_node = node; } } mtx_init(&sc->sc_mutex, "kiic", NULL, MTX_DEF); sc->sc_irq = bus_alloc_resource_any(self, SYS_RES_IRQ, &sc->sc_irqrid, RF_ACTIVE); bus_setup_intr(self, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE, NULL, kiic_intr, sc, &sc->sc_ih); kiic_writereg(sc, ISR, kiic_readreg(sc, ISR)); kiic_writereg(sc, STATUS, 0); kiic_writereg(sc, IER, 0); kiic_setmode(sc, I2C_STDMODE); kiic_setspeed(sc, I2C_100kHz); /* XXX rate */ kiic_writereg(sc, IER, I2C_INT_DATA | I2C_INT_ADDR | I2C_INT_STOP); if (bootverbose) device_printf(self, "Revision: %02X\n", kiic_readreg(sc, REV)); /* Add the IIC bus layer */ sc->sc_iicbus = device_add_child(self, "iicbus", -1); return (bus_generic_attach(self)); } static void kiic_writereg(struct kiic_softc *sc, u_int reg, u_int val) { bus_write_4(sc->sc_reg, sc->sc_regstep * reg, val); DELAY(100); /* register access delay */ } static u_int kiic_readreg(struct kiic_softc *sc, u_int reg) { return bus_read_4(sc->sc_reg, sc->sc_regstep * reg) & 0xff; } static void kiic_setmode(struct kiic_softc *sc, u_int mode) { u_int x; KASSERT((mode & ~I2C_MODE) == 0, ("bad mode")); x = kiic_readreg(sc, MODE); x &= ~I2C_MODE; x |= mode; kiic_writereg(sc, MODE, x); } static void kiic_setport(struct kiic_softc *sc, u_int port) { u_int x; KASSERT(port == 1 || port == 0, ("bad port")); x = kiic_readreg(sc, MODE); x &= ~I2C_PORT; x |= (port << 4); kiic_writereg(sc, MODE, x); } static void kiic_setspeed(struct kiic_softc *sc, u_int speed) { u_int x; KASSERT((speed & ~I2C_SPEED) == 0, ("bad speed")); x = kiic_readreg(sc, MODE); x &= ~I2C_SPEED; x |= speed; kiic_writereg(sc, MODE, x); } static void kiic_intr(void *xsc) { struct kiic_softc *sc = xsc; u_int isr; uint32_t x; mtx_lock(&sc->sc_mutex); isr = kiic_readreg(sc, ISR); if (isr & I2C_INT_ADDR) { sc->sc_flags |= I2C_SELECTED; if (sc->sc_flags & I2C_READING) { if (sc->sc_resid > 1) { x = kiic_readreg(sc, CONTROL); x |= I2C_CT_AAK; kiic_writereg(sc, CONTROL, x); } } else { kiic_writereg(sc, DATA, *sc->sc_data++); sc->sc_resid--; } } if (isr & I2C_INT_DATA) { if (sc->sc_flags & I2C_READING) { if (sc->sc_resid > 0) { *sc->sc_data++ = kiic_readreg(sc, DATA); sc->sc_resid--; } if (sc->sc_resid == 0) /* done */ kiic_writereg(sc, CONTROL, 0); } else { if (sc->sc_resid == 0) { x = kiic_readreg(sc, CONTROL); x |= I2C_CT_STOP; kiic_writereg(sc, CONTROL, x); } else { kiic_writereg(sc, DATA, *sc->sc_data++); sc->sc_resid--; } } } if (isr & I2C_INT_STOP) { kiic_writereg(sc, CONTROL, 0); sc->sc_flags &= ~I2C_SELECTED; wakeup(sc->sc_dev); } kiic_writereg(sc, ISR, isr); mtx_unlock(&sc->sc_mutex); } static int kiic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { struct kiic_softc *sc; int i, x, timo, err; uint16_t addr; uint8_t subaddr; sc = device_get_softc(dev); timo = 100; subaddr = 0; mtx_lock(&sc->sc_mutex); if (sc->sc_flags & I2C_BUSY) mtx_sleep(dev, &sc->sc_mutex, 0, "kiic", timo); if (sc->sc_flags & I2C_BUSY) { mtx_unlock(&sc->sc_mutex); return (ETIMEDOUT); } sc->sc_flags = I2C_BUSY; /* Clear pending interrupts, and reset controller */ kiic_writereg(sc, ISR, kiic_readreg(sc, ISR)); kiic_writereg(sc, STATUS, 0); for (i = 0; i < nmsgs; i++) { if (msgs[i].flags & IIC_M_NOSTOP) { if (msgs[i+1].flags & IIC_M_RD) kiic_setmode(sc, I2C_COMBMODE); else kiic_setmode(sc, I2C_STDSUBMODE); KASSERT(msgs[i].len == 1, ("oversize I2C message")); subaddr = msgs[i].buf[0]; i++; } else { kiic_setmode(sc, I2C_STDMODE); } sc->sc_data = msgs[i].buf; sc->sc_resid = msgs[i].len; sc->sc_flags = I2C_BUSY; addr = msgs[i].slave; timo = 1000 + sc->sc_resid * 200; timo += 100000; if (msgs[i].flags & IIC_M_RD) { sc->sc_flags |= I2C_READING; addr |= 1; } addr |= sc->sc_i2c_base; kiic_setport(sc, (addr & 0x100) >> 8); kiic_writereg(sc, ADDR, addr & 0xff); kiic_writereg(sc, SUBADDR, subaddr); x = kiic_readreg(sc, CONTROL) | I2C_CT_ADDR; kiic_writereg(sc, CONTROL, x); err = mtx_sleep(dev, &sc->sc_mutex, 0, "kiic", timo); msgs[i].len -= sc->sc_resid; if ((sc->sc_flags & I2C_ERROR) || err == EWOULDBLOCK) { device_printf(sc->sc_dev, "I2C error\n"); sc->sc_flags = 0; mtx_unlock(&sc->sc_mutex); return (EIO); } } sc->sc_flags = 0; mtx_unlock(&sc->sc_mutex); return (0); } static phandle_t kiic_get_node(device_t bus, device_t dev) { struct kiic_softc *sc; sc = device_get_softc(bus); /* We only have one child, the I2C bus, which needs our own node. */ return sc->sc_node; } Index: head/sys/powerpc/powermac/macgpio.c =================================================================== --- head/sys/powerpc/powermac/macgpio.c (revision 326260) +++ head/sys/powerpc/powermac/macgpio.c (revision 326261) @@ -1,403 +1,405 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 2008 by Nathan Whitehorn. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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$ */ /* * Driver for MacIO GPIO controller */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Macgpio softc */ struct macgpio_softc { phandle_t sc_node; struct resource *sc_gpios; int sc_gpios_rid; uint32_t sc_saved_gpio_levels[2]; uint32_t sc_saved_gpios[GPIO_COUNT]; uint32_t sc_saved_extint_gpios[GPIO_EXTINT_COUNT]; }; static MALLOC_DEFINE(M_MACGPIO, "macgpio", "macgpio device information"); static int macgpio_probe(device_t); static int macgpio_attach(device_t); static int macgpio_print_child(device_t dev, device_t child); static void macgpio_probe_nomatch(device_t, device_t); static struct resource *macgpio_alloc_resource(device_t, device_t, int, int *, rman_res_t, rman_res_t, rman_res_t, u_int); static int macgpio_activate_resource(device_t, device_t, int, int, struct resource *); static int macgpio_deactivate_resource(device_t, device_t, int, int, struct resource *); static ofw_bus_get_devinfo_t macgpio_get_devinfo; static int macgpio_suspend(device_t dev); static int macgpio_resume(device_t dev); /* * Bus interface definition */ static device_method_t macgpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, macgpio_probe), DEVMETHOD(device_attach, macgpio_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, macgpio_suspend), DEVMETHOD(device_resume, macgpio_resume), /* Bus interface */ DEVMETHOD(bus_print_child, macgpio_print_child), DEVMETHOD(bus_probe_nomatch, macgpio_probe_nomatch), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, macgpio_alloc_resource), DEVMETHOD(bus_activate_resource, macgpio_activate_resource), DEVMETHOD(bus_deactivate_resource, macgpio_deactivate_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, macgpio_get_devinfo), 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), { 0, 0 } }; static driver_t macgpio_pci_driver = { "macgpio", macgpio_methods, sizeof(struct macgpio_softc) }; devclass_t macgpio_devclass; DRIVER_MODULE(macgpio, macio, macgpio_pci_driver, macgpio_devclass, 0, 0); struct macgpio_devinfo { struct ofw_bus_devinfo mdi_obdinfo; struct resource_list mdi_resources; int gpio_num; }; static int macgpio_probe(device_t dev) { const char *name; name = ofw_bus_get_name(dev); if (name && strcmp(name, "gpio") == 0) { device_set_desc(dev, "MacIO GPIO Controller"); return (0); } return (ENXIO); } /* * Scan Open Firmware child nodes, and attach these as children * of the macgpio bus */ static int macgpio_attach(device_t dev) { struct macgpio_softc *sc; struct macgpio_devinfo *dinfo; phandle_t root, child, iparent; device_t cdev; uint32_t irq; sc = device_get_softc(dev); root = sc->sc_node = ofw_bus_get_node(dev); sc->sc_gpios = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_gpios_rid, RF_ACTIVE); /* * Iterate through the sub-devices */ for (child = OF_child(root); child != 0; child = OF_peer(child)) { dinfo = malloc(sizeof(*dinfo), M_MACGPIO, M_WAITOK | M_ZERO); if (ofw_bus_gen_setup_devinfo(&dinfo->mdi_obdinfo, child) != 0) { free(dinfo, M_MACGPIO); continue; } if (OF_getencprop(child, "reg", &dinfo->gpio_num, sizeof(dinfo->gpio_num)) != sizeof(dinfo->gpio_num)) { /* * Some early GPIO controllers don't provide GPIO * numbers for GPIOs designed only to provide * interrupt resources. We should still allow these * to attach, but with caution. */ dinfo->gpio_num = -1; } resource_list_init(&dinfo->mdi_resources); if (OF_getencprop(child, "interrupts", &irq, sizeof(irq)) == sizeof(irq)) { OF_searchencprop(child, "interrupt-parent", &iparent, sizeof(iparent)); resource_list_add(&dinfo->mdi_resources, SYS_RES_IRQ, 0, MAP_IRQ(iparent, irq), MAP_IRQ(iparent, irq), 1); } /* Fix messed-up offsets */ if (dinfo->gpio_num > 0x50) dinfo->gpio_num -= 0x50; cdev = device_add_child(dev, NULL, -1); if (cdev == NULL) { device_printf(dev, "<%s>: device_add_child failed\n", dinfo->mdi_obdinfo.obd_name); ofw_bus_gen_destroy_devinfo(&dinfo->mdi_obdinfo); free(dinfo, M_MACGPIO); continue; } device_set_ivars(cdev, dinfo); } return (bus_generic_attach(dev)); } static int macgpio_print_child(device_t dev, device_t child) { struct macgpio_devinfo *dinfo; int retval = 0; dinfo = device_get_ivars(child); retval += bus_print_child_header(dev, child); if (dinfo->gpio_num >= GPIO_BASE) printf(" gpio %d", dinfo->gpio_num - GPIO_BASE); else if (dinfo->gpio_num >= GPIO_EXTINT_BASE) printf(" extint-gpio %d", dinfo->gpio_num - GPIO_EXTINT_BASE); else if (dinfo->gpio_num >= 0) printf(" addr 0x%02x", dinfo->gpio_num); /* should not happen */ resource_list_print_type(&dinfo->mdi_resources, "irq", SYS_RES_IRQ, "%jd"); retval += bus_print_child_footer(dev, child); return (retval); } static void macgpio_probe_nomatch(device_t dev, device_t child) { struct macgpio_devinfo *dinfo; const char *type; if (bootverbose) { dinfo = device_get_ivars(child); if ((type = ofw_bus_get_type(child)) == NULL) type = "(unknown)"; device_printf(dev, "<%s, %s>", type, ofw_bus_get_name(child)); if (dinfo->gpio_num >= 0) printf(" gpio %d",dinfo->gpio_num); resource_list_print_type(&dinfo->mdi_resources, "irq", SYS_RES_IRQ, "%jd"); printf(" (no driver attached)\n"); } } static struct resource * macgpio_alloc_resource(device_t bus, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct macgpio_devinfo *dinfo; dinfo = device_get_ivars(child); if (type != SYS_RES_IRQ) return (NULL); return (resource_list_alloc(&dinfo->mdi_resources, bus, child, type, rid, start, end, count, flags)); } static int macgpio_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { struct macgpio_softc *sc; struct macgpio_devinfo *dinfo; u_char val; sc = device_get_softc(bus); dinfo = device_get_ivars(child); if (type != SYS_RES_IRQ) return ENXIO; if (dinfo->gpio_num >= 0) { val = bus_read_1(sc->sc_gpios,dinfo->gpio_num); val |= 0x80; bus_write_1(sc->sc_gpios,dinfo->gpio_num,val); } return (bus_activate_resource(bus, type, rid, res)); } static int macgpio_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { struct macgpio_softc *sc; struct macgpio_devinfo *dinfo; u_char val; sc = device_get_softc(bus); dinfo = device_get_ivars(child); if (type != SYS_RES_IRQ) return ENXIO; if (dinfo->gpio_num >= 0) { val = bus_read_1(sc->sc_gpios,dinfo->gpio_num); val &= ~0x80; bus_write_1(sc->sc_gpios,dinfo->gpio_num,val); } return (bus_deactivate_resource(bus, type, rid, res)); } uint8_t macgpio_read(device_t dev) { struct macgpio_softc *sc; struct macgpio_devinfo *dinfo; sc = device_get_softc(device_get_parent(dev)); dinfo = device_get_ivars(dev); if (dinfo->gpio_num < 0) return (0); return (bus_read_1(sc->sc_gpios,dinfo->gpio_num)); } void macgpio_write(device_t dev, uint8_t val) { struct macgpio_softc *sc; struct macgpio_devinfo *dinfo; sc = device_get_softc(device_get_parent(dev)); dinfo = device_get_ivars(dev); if (dinfo->gpio_num < 0) return; bus_write_1(sc->sc_gpios,dinfo->gpio_num,val); } static const struct ofw_bus_devinfo * macgpio_get_devinfo(device_t dev, device_t child) { struct macgpio_devinfo *dinfo; dinfo = device_get_ivars(child); return (&dinfo->mdi_obdinfo); } static int macgpio_suspend(device_t dev) { struct macgpio_softc *sc; int i; sc = device_get_softc(dev); sc->sc_saved_gpio_levels[0] = bus_read_4(sc->sc_gpios, GPIO_LEVELS_0); sc->sc_saved_gpio_levels[1] = bus_read_4(sc->sc_gpios, GPIO_LEVELS_1); for (i = 0; i < GPIO_COUNT; i++) sc->sc_saved_gpios[i] = bus_read_1(sc->sc_gpios, GPIO_BASE + i); for (i = 0; i < GPIO_EXTINT_COUNT; i++) sc->sc_saved_extint_gpios[i] = bus_read_1(sc->sc_gpios, GPIO_EXTINT_BASE + i); return (0); } static int macgpio_resume(device_t dev) { struct macgpio_softc *sc; int i; sc = device_get_softc(dev); bus_write_4(sc->sc_gpios, GPIO_LEVELS_0, sc->sc_saved_gpio_levels[0]); bus_write_4(sc->sc_gpios, GPIO_LEVELS_1, sc->sc_saved_gpio_levels[1]); for (i = 0; i < GPIO_COUNT; i++) bus_write_1(sc->sc_gpios, GPIO_BASE + i, sc->sc_saved_gpios[i]); for (i = 0; i < GPIO_EXTINT_COUNT; i++) bus_write_1(sc->sc_gpios, GPIO_EXTINT_BASE + i, sc->sc_saved_extint_gpios[i]); return (0); } Index: head/sys/powerpc/powermac/macgpiovar.h =================================================================== --- head/sys/powerpc/powermac/macgpiovar.h (revision 326260) +++ head/sys/powerpc/powermac/macgpiovar.h (revision 326261) @@ -1,50 +1,52 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 2008 by Nathan Whitehorn. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACIO_MACGPIOVAR_H_ #define _MACIO_MACGPIOVAR_H_ /* relative offsets into gpio space */ #define GPIO_EXTINT_BASE 0x08 #define GPIO_BASE 0x1a #define GPIO_EXTINT_COUNT 0x12 #define GPIO_COUNT 0x11 #define GPIO_LEVELS_0 0x50 #define GPIO_LEVELS_1 0x54 /* gpio bit definitions */ #define GPIO_DATA 0x01 /* GPIO data */ #define GPIO_LEVEL_RO 0x02 /* read-only level on pin */ #define GPIO_DDR_INPUT 0x00 /* use for input */ #define GPIO_DDR_OUTPUT 0x04 /* use for output */ uint8_t macgpio_read(device_t dev); void macgpio_write(device_t dev,uint8_t); #endif /* _MACIO_MACGPIOVAR_H_ */ Index: head/sys/powerpc/powermac/macio.c =================================================================== --- head/sys/powerpc/powermac/macio.c (revision 326260) +++ head/sys/powerpc/powermac/macio.c (revision 326261) @@ -1,703 +1,705 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2002 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Driver for KeyLargo/Pangea, the MacPPC south bridge ASIC. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Macio softc */ struct macio_softc { phandle_t sc_node; vm_offset_t sc_base; vm_offset_t sc_size; struct rman sc_mem_rman; /* FCR registers */ int sc_memrid; struct resource *sc_memr; }; static MALLOC_DEFINE(M_MACIO, "macio", "macio device information"); static int macio_probe(device_t); static int macio_attach(device_t); static int macio_print_child(device_t dev, device_t child); static void macio_probe_nomatch(device_t, device_t); static struct resource *macio_alloc_resource(device_t, device_t, int, int *, rman_res_t, rman_res_t, rman_res_t, u_int); static int macio_activate_resource(device_t, device_t, int, int, struct resource *); static int macio_deactivate_resource(device_t, device_t, int, int, struct resource *); static int macio_release_resource(device_t, device_t, int, int, struct resource *); static struct resource_list *macio_get_resource_list (device_t, device_t); static ofw_bus_get_devinfo_t macio_get_devinfo; /* * Bus interface definition */ static device_method_t macio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, macio_probe), DEVMETHOD(device_attach, macio_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, macio_print_child), DEVMETHOD(bus_probe_nomatch, macio_probe_nomatch), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, macio_alloc_resource), DEVMETHOD(bus_release_resource, macio_release_resource), DEVMETHOD(bus_activate_resource, macio_activate_resource), DEVMETHOD(bus_deactivate_resource, macio_deactivate_resource), DEVMETHOD(bus_get_resource_list, macio_get_resource_list), DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, macio_get_devinfo), 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), { 0, 0 } }; static driver_t macio_pci_driver = { "macio", macio_methods, sizeof(struct macio_softc) }; devclass_t macio_devclass; DRIVER_MODULE(macio, pci, macio_pci_driver, macio_devclass, 0, 0); /* * PCI ID search table */ static struct macio_pci_dev { u_int32_t mpd_devid; char *mpd_desc; } macio_pci_devlist[] = { { 0x0017106b, "Paddington I/O Controller" }, { 0x0022106b, "KeyLargo I/O Controller" }, { 0x0025106b, "Pangea I/O Controller" }, { 0x003e106b, "Intrepid I/O Controller" }, { 0x0041106b, "K2 KeyLargo I/O Controller" }, { 0x004f106b, "Shasta I/O Controller" }, { 0, NULL } }; /* * Devices to exclude from the probe * XXX some of these may be required in the future... */ #define MACIO_QUIRK_IGNORE 0x00000001 #define MACIO_QUIRK_CHILD_HAS_INTR 0x00000002 #define MACIO_QUIRK_USE_CHILD_REG 0x00000004 struct macio_quirk_entry { const char *mq_name; int mq_quirks; }; static struct macio_quirk_entry macio_quirks[] = { { "escc-legacy", MACIO_QUIRK_IGNORE }, { "timer", MACIO_QUIRK_IGNORE }, { "escc", MACIO_QUIRK_CHILD_HAS_INTR }, { "i2s", MACIO_QUIRK_CHILD_HAS_INTR | MACIO_QUIRK_USE_CHILD_REG }, { NULL, 0 } }; static int macio_get_quirks(const char *name) { struct macio_quirk_entry *mqe; for (mqe = macio_quirks; mqe->mq_name != NULL; mqe++) if (strcmp(name, mqe->mq_name) == 0) return (mqe->mq_quirks); return (0); } /* * Add an interrupt to the dev's resource list if present */ static void macio_add_intr(phandle_t devnode, struct macio_devinfo *dinfo) { phandle_t iparent; int *intr; int i, nintr; int icells; if (dinfo->mdi_ninterrupts >= 6) { printf("macio: device has more than 6 interrupts\n"); return; } nintr = OF_getprop_alloc(devnode, "interrupts", sizeof(*intr), (void **)&intr); if (nintr == -1) { nintr = OF_getprop_alloc(devnode, "AAPL,interrupts", sizeof(*intr), (void **)&intr); if (nintr == -1) return; } if (intr[0] == -1) return; if (OF_getprop(devnode, "interrupt-parent", &iparent, sizeof(iparent)) <= 0) panic("Interrupt but no interrupt parent!\n"); if (OF_getprop(OF_node_from_xref(iparent), "#interrupt-cells", &icells, sizeof(icells)) <= 0) icells = 1; for (i = 0; i < nintr; i+=icells) { u_int irq = MAP_IRQ(iparent, intr[i]); resource_list_add(&dinfo->mdi_resources, SYS_RES_IRQ, dinfo->mdi_ninterrupts, irq, irq, 1); dinfo->mdi_interrupts[dinfo->mdi_ninterrupts] = irq; dinfo->mdi_ninterrupts++; } } static void macio_add_reg(phandle_t devnode, struct macio_devinfo *dinfo) { struct macio_reg *reg, *regp; phandle_t child; char buf[8]; int i, layout_id = 0, nreg, res; nreg = OF_getprop_alloc(devnode, "reg", sizeof(*reg), (void **)®); if (nreg == -1) return; /* * Some G5's have broken properties in the i2s-a area. If so we try * to fix it. Right now we know of two different cases, one for * sound layout-id 36 and the other one for sound layout-id 76. * What is missing is the base address for the memory addresses. * We take them from the parent node (i2s) and use the size * information from the child. */ if (reg[0].mr_base == 0) { child = OF_child(devnode); while (child != 0) { res = OF_getprop(child, "name", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "sound") == 0) break; child = OF_peer(child); } res = OF_getprop(child, "layout-id", &layout_id, sizeof(layout_id)); if (res > 0 && (layout_id == 36 || layout_id == 76)) { res = OF_getprop_alloc(OF_parent(devnode), "reg", sizeof(*regp), (void **)®p); reg[0] = regp[0]; reg[1].mr_base = regp[1].mr_base; reg[2].mr_base = regp[1].mr_base + reg[1].mr_size; } } for (i = 0; i < nreg; i++) { resource_list_add(&dinfo->mdi_resources, SYS_RES_MEMORY, i, reg[i].mr_base, reg[i].mr_base + reg[i].mr_size, reg[i].mr_size); } } /* * PCI probe */ static int macio_probe(device_t dev) { int i; u_int32_t devid; devid = pci_get_devid(dev); for (i = 0; macio_pci_devlist[i].mpd_desc != NULL; i++) { if (devid == macio_pci_devlist[i].mpd_devid) { device_set_desc(dev, macio_pci_devlist[i].mpd_desc); return (0); } } return (ENXIO); } /* * PCI attach: scan Open Firmware child nodes, and attach these as children * of the macio bus */ static int macio_attach(device_t dev) { struct macio_softc *sc; struct macio_devinfo *dinfo; phandle_t root; phandle_t child; phandle_t subchild; device_t cdev; u_int reg[3]; char compat[32]; int error, quirks; sc = device_get_softc(dev); root = sc->sc_node = ofw_bus_get_node(dev); /* * Locate the device node and it's base address */ if (OF_getprop(root, "assigned-addresses", reg, sizeof(reg)) < (ssize_t)sizeof(reg)) { return (ENXIO); } /* Used later to see if we have to enable the I2S part. */ OF_getprop(root, "compatible", compat, sizeof(compat)); sc->sc_base = reg[2]; sc->sc_size = MACIO_REG_SIZE; sc->sc_memrid = PCIR_BAR(0); sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_memrid, RF_ACTIVE); sc->sc_mem_rman.rm_type = RMAN_ARRAY; sc->sc_mem_rman.rm_descr = "MacIO Device Memory"; error = rman_init(&sc->sc_mem_rman); if (error) { device_printf(dev, "rman_init() failed. error = %d\n", error); return (error); } error = rman_manage_region(&sc->sc_mem_rman, 0, sc->sc_size); if (error) { device_printf(dev, "rman_manage_region() failed. error = %d\n", error); return (error); } /* * Iterate through the sub-devices */ for (child = OF_child(root); child != 0; child = OF_peer(child)) { dinfo = malloc(sizeof(*dinfo), M_MACIO, M_WAITOK | M_ZERO); if (ofw_bus_gen_setup_devinfo(&dinfo->mdi_obdinfo, child) != 0) { free(dinfo, M_MACIO); continue; } quirks = macio_get_quirks(dinfo->mdi_obdinfo.obd_name); if ((quirks & MACIO_QUIRK_IGNORE) != 0) { ofw_bus_gen_destroy_devinfo(&dinfo->mdi_obdinfo); free(dinfo, M_MACIO); continue; } resource_list_init(&dinfo->mdi_resources); dinfo->mdi_ninterrupts = 0; macio_add_intr(child, dinfo); if ((quirks & MACIO_QUIRK_USE_CHILD_REG) != 0) macio_add_reg(OF_child(child), dinfo); else macio_add_reg(child, dinfo); if ((quirks & MACIO_QUIRK_CHILD_HAS_INTR) != 0) for (subchild = OF_child(child); subchild != 0; subchild = OF_peer(subchild)) macio_add_intr(subchild, dinfo); cdev = device_add_child(dev, NULL, -1); if (cdev == NULL) { device_printf(dev, "<%s>: device_add_child failed\n", dinfo->mdi_obdinfo.obd_name); resource_list_free(&dinfo->mdi_resources); ofw_bus_gen_destroy_devinfo(&dinfo->mdi_obdinfo); free(dinfo, M_MACIO); continue; } device_set_ivars(cdev, dinfo); /* Set FCRs to enable some devices */ if (sc->sc_memr == NULL) continue; if (strcmp(ofw_bus_get_name(cdev), "bmac") == 0 || strcmp(ofw_bus_get_compat(cdev), "bmac+") == 0) { uint32_t fcr; fcr = bus_read_4(sc->sc_memr, HEATHROW_FCR); fcr |= FCR_ENET_ENABLE & ~FCR_ENET_RESET; bus_write_4(sc->sc_memr, HEATHROW_FCR, fcr); DELAY(50000); fcr |= FCR_ENET_RESET; bus_write_4(sc->sc_memr, HEATHROW_FCR, fcr); DELAY(50000); fcr &= ~FCR_ENET_RESET; bus_write_4(sc->sc_memr, HEATHROW_FCR, fcr); DELAY(50000); bus_write_4(sc->sc_memr, HEATHROW_FCR, fcr); } /* * Make sure the I2S0 and the I2S0_CLK are enabled. * On certain G5's they are not. */ if ((strcmp(ofw_bus_get_name(cdev), "i2s") == 0) && (strcmp(compat, "K2-Keylargo") == 0)) { uint32_t fcr1; fcr1 = bus_read_4(sc->sc_memr, KEYLARGO_FCR1); fcr1 |= FCR1_I2S0_CLK_ENABLE | FCR1_I2S0_ENABLE; bus_write_4(sc->sc_memr, KEYLARGO_FCR1, fcr1); } } return (bus_generic_attach(dev)); } static int macio_print_child(device_t dev, device_t child) { struct macio_devinfo *dinfo; struct resource_list *rl; int retval = 0; dinfo = device_get_ivars(child); rl = &dinfo->mdi_resources; retval += bus_print_child_header(dev, child); retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); retval += bus_print_child_footer(dev, child); return (retval); } static void macio_probe_nomatch(device_t dev, device_t child) { struct macio_devinfo *dinfo; struct resource_list *rl; const char *type; if (bootverbose) { dinfo = device_get_ivars(child); rl = &dinfo->mdi_resources; if ((type = ofw_bus_get_type(child)) == NULL) type = "(unknown)"; device_printf(dev, "<%s, %s>", type, ofw_bus_get_name(child)); resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); printf(" (no driver attached)\n"); } } static struct resource * macio_alloc_resource(device_t bus, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct macio_softc *sc; int needactivate; struct resource *rv; struct rman *rm; u_long adjstart, adjend, adjcount; struct macio_devinfo *dinfo; struct resource_list_entry *rle; sc = device_get_softc(bus); dinfo = device_get_ivars(child); needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; switch (type) { case SYS_RES_MEMORY: case SYS_RES_IOPORT: rle = resource_list_find(&dinfo->mdi_resources, SYS_RES_MEMORY, *rid); if (rle == NULL) { device_printf(bus, "no rle for %s memory %d\n", device_get_nameunit(child), *rid); return (NULL); } if (start < rle->start) adjstart = rle->start; else if (start > rle->end) adjstart = rle->end; else adjstart = start; if (end < rle->start) adjend = rle->start; else if (end > rle->end) adjend = rle->end; else adjend = end; adjcount = adjend - adjstart; rm = &sc->sc_mem_rman; break; case SYS_RES_IRQ: /* Check for passthrough from subattachments like macgpio */ if (device_get_parent(child) != bus) return BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid, start, end, count, flags); rle = resource_list_find(&dinfo->mdi_resources, SYS_RES_IRQ, *rid); if (rle == NULL) { if (dinfo->mdi_ninterrupts >= 6) { device_printf(bus, "%s has more than 6 interrupts\n", device_get_nameunit(child)); return (NULL); } resource_list_add(&dinfo->mdi_resources, SYS_RES_IRQ, dinfo->mdi_ninterrupts, start, start, 1); dinfo->mdi_interrupts[dinfo->mdi_ninterrupts] = start; dinfo->mdi_ninterrupts++; } return (resource_list_alloc(&dinfo->mdi_resources, bus, child, type, rid, start, end, count, flags)); default: device_printf(bus, "unknown resource request from %s\n", device_get_nameunit(child)); return (NULL); } rv = rman_reserve_resource(rm, adjstart, adjend, adjcount, flags, child); if (rv == NULL) { device_printf(bus, "failed to reserve resource %#lx - %#lx (%#lx) for %s\n", adjstart, adjend, adjcount, device_get_nameunit(child)); return (NULL); } rman_set_rid(rv, *rid); if (needactivate) { if (bus_activate_resource(child, type, *rid, rv) != 0) { device_printf(bus, "failed to activate resource for %s\n", device_get_nameunit(child)); rman_release_resource(rv); return (NULL); } } return (rv); } static int macio_release_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { if (rman_get_flags(res) & RF_ACTIVE) { int error = bus_deactivate_resource(child, type, rid, res); if (error) return error; } return (rman_release_resource(res)); } static int macio_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { struct macio_softc *sc; void *p; sc = device_get_softc(bus); if (type == SYS_RES_IRQ) return (bus_activate_resource(bus, type, rid, res)); if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { p = pmap_mapdev((vm_offset_t)rman_get_start(res) + sc->sc_base, (vm_size_t)rman_get_size(res)); if (p == NULL) return (ENOMEM); rman_set_virtual(res, p); rman_set_bustag(res, &bs_le_tag); rman_set_bushandle(res, (u_long)p); } return (rman_activate_resource(res)); } static int macio_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { /* * If this is a memory resource, unmap it. */ if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { u_int32_t psize; psize = rman_get_size(res); pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize); } return (rman_deactivate_resource(res)); } static struct resource_list * macio_get_resource_list (device_t dev, device_t child) { struct macio_devinfo *dinfo; dinfo = device_get_ivars(child); return (&dinfo->mdi_resources); } static const struct ofw_bus_devinfo * macio_get_devinfo(device_t dev, device_t child) { struct macio_devinfo *dinfo; dinfo = device_get_ivars(child); return (&dinfo->mdi_obdinfo); } int macio_enable_wireless(device_t dev, bool enable) { struct macio_softc *sc = device_get_softc(dev); uint32_t x; if (enable) { x = bus_read_4(sc->sc_memr, KEYLARGO_FCR2); x |= 0x4; bus_write_4(sc->sc_memr, KEYLARGO_FCR2, x); /* Enable card slot. */ bus_write_1(sc->sc_memr, KEYLARGO_GPIO_BASE + 0x0f, 5); DELAY(1000); bus_write_1(sc->sc_memr, KEYLARGO_GPIO_BASE + 0x0f, 4); DELAY(1000); x = bus_read_4(sc->sc_memr, KEYLARGO_FCR2); x &= ~0x80000000; bus_write_4(sc->sc_memr, KEYLARGO_FCR2, x); /* out8(gpio + 0x10, 4); */ bus_write_1(sc->sc_memr, KEYLARGO_EXTINT_GPIO_REG_BASE + 0x0b, 0); bus_write_1(sc->sc_memr, KEYLARGO_EXTINT_GPIO_REG_BASE + 0x0a, 0x28); bus_write_1(sc->sc_memr, KEYLARGO_EXTINT_GPIO_REG_BASE + 0x0d, 0x28); bus_write_1(sc->sc_memr, KEYLARGO_GPIO_BASE + 0x0d, 0x28); bus_write_1(sc->sc_memr, KEYLARGO_GPIO_BASE + 0x0e, 0x28); bus_write_4(sc->sc_memr, 0x1c000, 0); /* Initialize the card. */ bus_write_4(sc->sc_memr, 0x1a3e0, 0x41); x = bus_read_4(sc->sc_memr, KEYLARGO_FCR2); x |= 0x80000000; bus_write_4(sc->sc_memr, KEYLARGO_FCR2, x); } else { x = bus_read_4(sc->sc_memr, KEYLARGO_FCR2); x &= ~0x4; bus_write_4(sc->sc_memr, KEYLARGO_FCR2, x); /* out8(gpio + 0x10, 0); */ } return (0); } Index: head/sys/powerpc/powermac/maciovar.h =================================================================== --- head/sys/powerpc/powermac/maciovar.h (revision 326260) +++ head/sys/powerpc/powermac/maciovar.h (revision 326261) @@ -1,79 +1,81 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2002 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACIO_MACIOVAR_H_ #define _MACIO_MACIOVAR_H_ /* * The addr space size * XXX it would be better if this could be determined by querying the * PCI device, but there isn't an access method for this */ #define MACIO_REG_SIZE 0x7ffff /* * Feature Control Registers (FCR) */ #define HEATHROW_FCR 0x38 #define KEYLARGO_FCR0 0x38 #define KEYLARGO_FCR1 0x3c #define KEYLARGO_FCR2 0x40 #define FCR_ENET_ENABLE 0x60000000 #define FCR_ENET_RESET 0x80000000 #define FCR1_I2S0_CLK_ENABLE 0x00001000 #define FCR1_I2S0_ENABLE 0x00002000 /* Used only by macio_enable_wireless() for now. */ #define KEYLARGO_GPIO_BASE 0x6a #define KEYLARGO_EXTINT_GPIO_REG_BASE 0x58 /* * Format of a macio reg property entry. */ struct macio_reg { u_int32_t mr_base; u_int32_t mr_size; }; /* * Per macio device structure. */ struct macio_devinfo { int mdi_interrupts[6]; int mdi_ninterrupts; int mdi_base; struct ofw_bus_devinfo mdi_obdinfo; struct resource_list mdi_resources; }; extern int macio_enable_wireless(device_t dev, bool enable); #endif /* _MACIO_MACIOVAR_H_ */ Index: head/sys/powerpc/powermac/nvbl.c =================================================================== --- head/sys/powerpc/powermac/nvbl.c (revision 326260) +++ head/sys/powerpc/powermac/nvbl.c (revision 326261) @@ -1,203 +1,205 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2012 Justin Hibbits * 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$"); #include #include #include #include #include #include #include #include #include #include #define PCI_VENDOR_ID_NVIDIA 0x10de #define NVIDIA_BRIGHT_MIN (0x0ec) #define NVIDIA_BRIGHT_MAX (0x538) #define NVIDIA_BRIGHT_SCALE ((NVIDIA_BRIGHT_MAX - NVIDIA_BRIGHT_MIN)/100) /* nVidia's MMIO registers are at PCI BAR[0] */ #define NVIDIA_MMIO_PMC (0x0) #define NVIDIA_PMC_OFF (NVIDIA_MMIO_PMC + 0x10f0) #define NVIDIA_PMC_BL_SHIFT (16) #define NVIDIA_PMC_BL_EN (1U << 31) struct nvbl_softc { device_t dev; struct resource *sc_memr; }; static void nvbl_identify(driver_t *driver, device_t parent); static int nvbl_probe(device_t dev); static int nvbl_attach(device_t dev); static int nvbl_setlevel(struct nvbl_softc *sc, int newlevel); static int nvbl_getlevel(struct nvbl_softc *sc); static int nvbl_sysctl(SYSCTL_HANDLER_ARGS); static device_method_t nvbl_methods[] = { /* Device interface */ DEVMETHOD(device_identify, nvbl_identify), DEVMETHOD(device_probe, nvbl_probe), DEVMETHOD(device_attach, nvbl_attach), {0, 0}, }; static driver_t nvbl_driver = { "backlight", nvbl_methods, sizeof(struct nvbl_softc) }; static devclass_t nvbl_devclass; DRIVER_MODULE(nvbl, vgapci, nvbl_driver, nvbl_devclass, 0, 0); static void nvbl_identify(driver_t *driver, device_t parent) { if (OF_finddevice("mac-io/backlight") == -1) return; if (device_find_child(parent, "backlight", -1) == NULL) device_add_child(parent, "backlight", -1); } static int nvbl_probe(device_t dev) { char control[8]; phandle_t handle; handle = OF_finddevice("mac-io/backlight"); if (handle == -1) return (ENXIO); if (OF_getprop(handle, "backlight-control", &control, sizeof(control)) < 0) return (ENXIO); if ((strcmp(control, "mnca") != 0) || pci_get_vendor(device_get_parent(dev)) != PCI_VENDOR_ID_NVIDIA) return (ENXIO); device_set_desc(dev, "PowerBook backlight for nVidia graphics"); return (0); } static int nvbl_attach(device_t dev) { struct nvbl_softc *sc; struct sysctl_ctx_list *ctx; struct sysctl_oid *tree; int rid; sc = device_get_softc(dev); rid = 0x10; /* BAR[0], for the MMIO register */ sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->sc_memr == NULL) { device_printf(dev, "Could not alloc mem resource!\n"); return (ENXIO); } /* Turn on big-endian mode */ if (!(bus_read_stream_4(sc->sc_memr, NVIDIA_MMIO_PMC + 4) & 0x01000001)) { bus_write_stream_4(sc->sc_memr, NVIDIA_MMIO_PMC + 4, 0x01000001); mb(); } ctx = device_get_sysctl_ctx(dev); tree = device_get_sysctl_tree(dev); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "level", CTLTYPE_INT | CTLFLAG_RW, sc, 0, nvbl_sysctl, "I", "Backlight level (0-100)"); return (0); } static int nvbl_setlevel(struct nvbl_softc *sc, int newlevel) { uint32_t pmc_reg; if (newlevel > 100) newlevel = 100; if (newlevel < 0) newlevel = 0; if (newlevel > 0) newlevel = (newlevel * NVIDIA_BRIGHT_SCALE) + NVIDIA_BRIGHT_MIN; pmc_reg = bus_read_stream_4(sc->sc_memr, NVIDIA_PMC_OFF) & 0xffff; pmc_reg |= NVIDIA_PMC_BL_EN | (newlevel << NVIDIA_PMC_BL_SHIFT); bus_write_stream_4(sc->sc_memr, NVIDIA_PMC_OFF, pmc_reg); return (0); } static int nvbl_getlevel(struct nvbl_softc *sc) { uint16_t level; level = bus_read_stream_2(sc->sc_memr, NVIDIA_PMC_OFF) & 0x7fff; if (level < NVIDIA_BRIGHT_MIN) return 0; level = (level - NVIDIA_BRIGHT_MIN) / NVIDIA_BRIGHT_SCALE; return (level); } static int nvbl_sysctl(SYSCTL_HANDLER_ARGS) { struct nvbl_softc *sc; int newlevel, error; sc = arg1; newlevel = nvbl_getlevel(sc); error = sysctl_handle_int(oidp, &newlevel, 0, req); if (error || !req->newptr) return (error); return (nvbl_setlevel(sc, newlevel)); } Index: head/sys/powerpc/powermac/platform_powermac.c =================================================================== --- head/sys/powerpc/powermac/platform_powermac.c (revision 326260) +++ head/sys/powerpc/powermac/platform_powermac.c (revision 326261) @@ -1,414 +1,416 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2008 Marcel Moolenaar * Copyright (c) 2009 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include /* For save_vec() */ #include #include #include /* For save_fpu() */ #include #include #include #include #include #include #include #include "platform_if.h" extern void *ap_pcpu; static int powermac_probe(platform_t); static int powermac_attach(platform_t); void powermac_mem_regions(platform_t, struct mem_region *phys, int *physsz, struct mem_region *avail, int *availsz); static u_long powermac_timebase_freq(platform_t, struct cpuref *cpuref); static int powermac_smp_first_cpu(platform_t, struct cpuref *cpuref); static int powermac_smp_next_cpu(platform_t, struct cpuref *cpuref); static int powermac_smp_get_bsp(platform_t, struct cpuref *cpuref); static int powermac_smp_start_cpu(platform_t, struct pcpu *cpu); static void powermac_smp_timebase_sync(platform_t, u_long tb, int ap); static void powermac_reset(platform_t); static void powermac_sleep(platform_t); static platform_method_t powermac_methods[] = { PLATFORMMETHOD(platform_probe, powermac_probe), PLATFORMMETHOD(platform_attach, powermac_attach), PLATFORMMETHOD(platform_mem_regions, powermac_mem_regions), PLATFORMMETHOD(platform_timebase_freq, powermac_timebase_freq), PLATFORMMETHOD(platform_smp_first_cpu, powermac_smp_first_cpu), PLATFORMMETHOD(platform_smp_next_cpu, powermac_smp_next_cpu), PLATFORMMETHOD(platform_smp_get_bsp, powermac_smp_get_bsp), PLATFORMMETHOD(platform_smp_start_cpu, powermac_smp_start_cpu), PLATFORMMETHOD(platform_smp_timebase_sync, powermac_smp_timebase_sync), PLATFORMMETHOD(platform_reset, powermac_reset), PLATFORMMETHOD(platform_sleep, powermac_sleep), PLATFORMMETHOD_END }; static platform_def_t powermac_platform = { "powermac", powermac_methods, 0 }; PLATFORM_DEF(powermac_platform); static int powermac_probe(platform_t plat) { char compat[255]; ssize_t compatlen; char *curstr; phandle_t root; root = OF_peer(0); if (root == 0) return (ENXIO); compatlen = OF_getprop(root, "compatible", compat, sizeof(compat)); for (curstr = compat; curstr < compat + compatlen; curstr += strlen(curstr) + 1) { if (strncmp(curstr, "MacRISC", 7) == 0) return (BUS_PROBE_SPECIFIC); } return (ENXIO); } void powermac_mem_regions(platform_t plat, struct mem_region *phys, int *physsz, struct mem_region *avail, int *availsz) { phandle_t memory; cell_t memoryprop[PHYS_AVAIL_SZ * 2]; ssize_t propsize, i, j; int physacells = 1; memory = OF_finddevice("/memory"); if (memory == -1) memory = OF_finddevice("/memory@0"); /* "reg" has variable #address-cells, but #size-cells is always 1 */ OF_getprop(OF_parent(memory), "#address-cells", &physacells, sizeof(physacells)); propsize = OF_getprop(memory, "reg", memoryprop, sizeof(memoryprop)); propsize /= sizeof(cell_t); for (i = 0, j = 0; i < propsize; i += physacells+1, j++) { phys[j].mr_start = memoryprop[i]; if (physacells == 2) { #ifndef __powerpc64__ /* On 32-bit PPC, ignore regions starting above 4 GB */ if (memoryprop[i] != 0) { j--; continue; } #else phys[j].mr_start <<= 32; #endif phys[j].mr_start |= memoryprop[i+1]; } phys[j].mr_size = memoryprop[i + physacells]; } *physsz = j; /* "available" always has #address-cells = 1 */ propsize = OF_getprop(memory, "available", memoryprop, sizeof(memoryprop)); if (propsize <= 0) { for (i = 0; i < *physsz; i++) { avail[i].mr_start = phys[i].mr_start; avail[i].mr_size = phys[i].mr_size; } *availsz = *physsz; } else { propsize /= sizeof(cell_t); for (i = 0, j = 0; i < propsize; i += 2, j++) { avail[j].mr_start = memoryprop[i]; avail[j].mr_size = memoryprop[i + 1]; } #ifdef __powerpc64__ /* Add in regions above 4 GB to the available list */ for (i = 0; i < *physsz; i++) { if (phys[i].mr_start > BUS_SPACE_MAXADDR_32BIT) { avail[j].mr_start = phys[i].mr_start; avail[j].mr_size = phys[i].mr_size; j++; } } #endif *availsz = j; } } static int powermac_attach(platform_t plat) { phandle_t rootnode; char model[32]; /* * Quiesce Open Firmware on PowerMac11,2 and 12,1. It is * necessary there to shut down a background thread doing fan * management, and is harmful on other machines (it will make OF * shut off power to various system components it had turned on). * * Note: we don't need to worry about which OF module we are * using since this is called only from very early boot, within * OF's boot context. */ rootnode = OF_finddevice("/"); if (OF_getprop(rootnode, "model", model, sizeof(model)) > 0) { if (strcmp(model, "PowerMac11,2") == 0 || strcmp(model, "PowerMac12,1") == 0) { ofw_quiesce(); } } return (0); } static u_long powermac_timebase_freq(platform_t plat, struct cpuref *cpuref) { phandle_t phandle; int32_t ticks = -1; phandle = cpuref->cr_hwref; OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks)); if (ticks <= 0) panic("Unable to determine timebase frequency!"); return (ticks); } static int powermac_smp_fill_cpuref(struct cpuref *cpuref, phandle_t cpu) { cell_t cpuid; int res; cpuref->cr_hwref = cpu; res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); /* * psim doesn't have a reg property, so assume 0 as for the * uniprocessor case in the CHRP spec. */ if (res < 0) { cpuid = 0; } cpuref->cr_cpuid = cpuid & 0xff; return (0); } static int powermac_smp_first_cpu(platform_t plat, struct cpuref *cpuref) { char buf[8]; phandle_t cpu, dev, root; int res; root = OF_peer(0); dev = OF_child(root); while (dev != 0) { res = OF_getprop(dev, "name", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpus") == 0) break; dev = OF_peer(dev); } if (dev == 0) { /* * psim doesn't have a name property on the /cpus node, * but it can be found directly */ dev = OF_finddevice("/cpus"); if (dev == -1) return (ENOENT); } cpu = OF_child(dev); while (cpu != 0) { res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpu") == 0) break; cpu = OF_peer(cpu); } if (cpu == 0) return (ENOENT); return (powermac_smp_fill_cpuref(cpuref, cpu)); } static int powermac_smp_next_cpu(platform_t plat, struct cpuref *cpuref) { char buf[8]; phandle_t cpu; int res; cpu = OF_peer(cpuref->cr_hwref); while (cpu != 0) { res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpu") == 0) break; cpu = OF_peer(cpu); } if (cpu == 0) return (ENOENT); return (powermac_smp_fill_cpuref(cpuref, cpu)); } static int powermac_smp_get_bsp(platform_t plat, struct cpuref *cpuref) { ihandle_t inst; phandle_t bsp, chosen; int res; chosen = OF_finddevice("/chosen"); if (chosen == -1) return (ENXIO); res = OF_getprop(chosen, "cpu", &inst, sizeof(inst)); if (res < 0) return (ENXIO); bsp = OF_instance_to_package(inst); return (powermac_smp_fill_cpuref(cpuref, bsp)); } static int powermac_smp_start_cpu(platform_t plat, struct pcpu *pc) { #ifdef SMP phandle_t cpu; volatile uint8_t *rstvec; static volatile uint8_t *rstvec_virtbase = NULL; int res, reset, timeout; cpu = pc->pc_hwref; res = OF_getprop(cpu, "soft-reset", &reset, sizeof(reset)); if (res < 0) { reset = 0x58; switch (pc->pc_cpuid) { case 0: reset += 0x03; break; case 1: reset += 0x04; break; case 2: reset += 0x0f; break; case 3: reset += 0x10; break; default: return (ENXIO); } } ap_pcpu = pc; if (rstvec_virtbase == NULL) rstvec_virtbase = pmap_mapdev(0x80000000, PAGE_SIZE); rstvec = rstvec_virtbase + reset; *rstvec = 4; powerpc_sync(); (void)(*rstvec); powerpc_sync(); DELAY(1); *rstvec = 0; powerpc_sync(); (void)(*rstvec); powerpc_sync(); timeout = 10000; while (!pc->pc_awake && timeout--) DELAY(100); return ((pc->pc_awake) ? 0 : EBUSY); #else /* No SMP support */ return (ENXIO); #endif } static void powermac_smp_timebase_sync(platform_t plat, u_long tb, int ap) { mttb(tb); } static void powermac_reset(platform_t platform) { OF_reboot(); } void powermac_sleep(platform_t platform) { *(unsigned long *)0x80 = 0x100; cpu_sleep(); } Index: head/sys/powerpc/powermac/pmu.c =================================================================== --- head/sys/powerpc/powermac/pmu.c (revision 326260) +++ head/sys/powerpc/powermac/pmu.c (revision 326261) @@ -1,1134 +1,1136 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2006 Michael Lorenz * Copyright 2008 by Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clock_if.h" #include "pmuvar.h" #include "viareg.h" #include "uninorthvar.h" /* For unin_chip_sleep()/unin_chip_wake() */ #define PMU_DEFAULTS PMU_INT_TICK | PMU_INT_ADB | \ PMU_INT_PCEJECT | PMU_INT_SNDBRT | \ PMU_INT_BATTERY | PMU_INT_ENVIRONMENT /* * Bus interface */ static int pmu_probe(device_t); static int pmu_attach(device_t); static int pmu_detach(device_t); /* * Clock interface */ static int pmu_gettime(device_t dev, struct timespec *ts); static int pmu_settime(device_t dev, struct timespec *ts); /* * ADB Interface */ static u_int pmu_adb_send(device_t dev, u_char command_byte, int len, u_char *data, u_char poll); static u_int pmu_adb_autopoll(device_t dev, uint16_t mask); static u_int pmu_poll(device_t dev); /* * Power interface */ static void pmu_shutdown(void *xsc, int howto); static void pmu_set_sleepled(void *xsc, int onoff); static int pmu_server_mode(SYSCTL_HANDLER_ARGS); static int pmu_acline_state(SYSCTL_HANDLER_ARGS); static int pmu_query_battery(struct pmu_softc *sc, int batt, struct pmu_battstate *info); static int pmu_battquery_sysctl(SYSCTL_HANDLER_ARGS); static int pmu_battmon(SYSCTL_HANDLER_ARGS); static void pmu_battquery_proc(void); static void pmu_battery_notify(struct pmu_battstate *batt, struct pmu_battstate *old); /* * List of battery-related sysctls we might ask for */ enum { PMU_BATSYSCTL_PRESENT = 1 << 8, PMU_BATSYSCTL_CHARGING = 2 << 8, PMU_BATSYSCTL_CHARGE = 3 << 8, PMU_BATSYSCTL_MAXCHARGE = 4 << 8, PMU_BATSYSCTL_CURRENT = 5 << 8, PMU_BATSYSCTL_VOLTAGE = 6 << 8, PMU_BATSYSCTL_TIME = 7 << 8, PMU_BATSYSCTL_LIFE = 8 << 8 }; static device_method_t pmu_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pmu_probe), DEVMETHOD(device_attach, pmu_attach), DEVMETHOD(device_detach, pmu_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* ADB bus interface */ DEVMETHOD(adb_hb_send_raw_packet, pmu_adb_send), DEVMETHOD(adb_hb_controller_poll, pmu_poll), DEVMETHOD(adb_hb_set_autopoll_mask, pmu_adb_autopoll), /* Clock interface */ DEVMETHOD(clock_gettime, pmu_gettime), DEVMETHOD(clock_settime, pmu_settime), DEVMETHOD_END }; static driver_t pmu_driver = { "pmu", pmu_methods, sizeof(struct pmu_softc), }; static devclass_t pmu_devclass; DRIVER_MODULE(pmu, macio, pmu_driver, pmu_devclass, 0, 0); DRIVER_MODULE(adb, pmu, adb_driver, adb_devclass, 0, 0); static int pmuextint_probe(device_t); static int pmuextint_attach(device_t); static device_method_t pmuextint_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pmuextint_probe), DEVMETHOD(device_attach, pmuextint_attach), {0,0} }; static driver_t pmuextint_driver = { "pmuextint", pmuextint_methods, 0 }; static devclass_t pmuextint_devclass; DRIVER_MODULE(pmuextint, macgpio, pmuextint_driver, pmuextint_devclass, 0, 0); /* Make sure uhid is loaded, as it turns off some of the ADB emulation */ MODULE_DEPEND(pmu, usb, 1, 1, 1); static void pmu_intr(void *arg); static void pmu_in(struct pmu_softc *sc); static void pmu_out(struct pmu_softc *sc); static void pmu_ack_on(struct pmu_softc *sc); static void pmu_ack_off(struct pmu_softc *sc); static int pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg, int rlen, uint8_t *out_msg); static uint8_t pmu_read_reg(struct pmu_softc *sc, u_int offset); static void pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value); static int pmu_intr_state(struct pmu_softc *); /* these values shows that number of data returned after 'send' cmd is sent */ static signed char pm_send_cmd_type[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x01, 0x01, -1, -1, -1, -1, -1, -1, 0x00, 0x00, -1, -1, -1, -1, -1, 0x00, -1, 0x00, 0x02, 0x01, 0x01, -1, -1, -1, 0x00, -1, -1, -1, -1, -1, -1, -1, 0x04, 0x14, -1, 0x03, -1, -1, -1, -1, 0x00, 0x00, 0x02, 0x02, -1, -1, -1, -1, 0x01, 0x01, -1, -1, -1, -1, -1, -1, 0x00, 0x00, -1, -1, 0x01, -1, -1, -1, 0x01, 0x00, 0x02, 0x02, -1, 0x01, 0x03, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, -1, -1, -1, 0x02, -1, -1, -1, -1, -1, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1, 0x01, 0x01, 0x01, -1, -1, -1, -1, -1, 0x00, 0x00, -1, -1, -1, 0x05, 0x04, 0x04, 0x04, -1, 0x00, -1, -1, -1, -1, -1, 0x00, -1, -1, -1, -1, -1, -1, -1, 0x01, 0x02, -1, -1, -1, -1, -1, -1, 0x00, 0x00, -1, -1, -1, -1, -1, -1, 0x02, 0x02, 0x02, 0x04, -1, 0x00, -1, -1, 0x01, 0x01, 0x03, 0x02, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x00, -1, -1, -1, -1, -1, -1, -1, 0x01, 0x01, -1, -1, 0x00, 0x00, -1, -1, -1, 0x04, 0x00, -1, -1, -1, -1, -1, 0x03, -1, 0x00, -1, 0x00, -1, -1, 0x00, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; /* these values shows that number of data returned after 'receive' cmd is sent */ static signed char pm_receive_cmd_type[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1, -1, -1, -1, -1, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, -1, -1, -1, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1, -1, -1, -1, -1, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, -1, 0x02, -1, -1, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, -1, -1, -1, -1, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x03, -1, -1, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x03, 0x09, -1, -1, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1, -1, -1, -1, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, -1, -1, -1, -1, -1, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, -1, -1, -1, -1, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -1, -1, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1, -1, -1, -1, -1, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1, -1, -1, -1, -1, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, -1, -1, 0x02, -1, -1, -1, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1, 0x02, -1, -1, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1, -1, -1, -1, -1, -1, -1, }; static int pmu_battmon_enabled = 1; static struct proc *pmubattproc; static struct kproc_desc pmu_batt_kp = { "pmu_batt", pmu_battquery_proc, &pmubattproc }; /* We only have one of each device, so globals are safe */ static device_t pmu = NULL; static device_t pmu_extint = NULL; static int pmuextint_probe(device_t dev) { const char *type = ofw_bus_get_type(dev); if (strcmp(type, "extint-gpio1") != 0) return (ENXIO); device_set_desc(dev, "Apple PMU99 External Interrupt"); return (0); } static int pmu_probe(device_t dev) { const char *type = ofw_bus_get_type(dev); if (strcmp(type, "via-pmu") != 0) return (ENXIO); device_set_desc(dev, "Apple PMU99 Controller"); return (0); } static int setup_pmu_intr(device_t dev, device_t extint) { struct pmu_softc *sc; sc = device_get_softc(dev); sc->sc_irqrid = 0; sc->sc_irq = bus_alloc_resource_any(extint, SYS_RES_IRQ, &sc->sc_irqrid, RF_ACTIVE); if (sc->sc_irq == NULL) { device_printf(dev, "could not allocate interrupt\n"); return (ENXIO); } if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE | INTR_ENTROPY, NULL, pmu_intr, dev, &sc->sc_ih) != 0) { device_printf(dev, "could not setup interrupt\n"); bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq); return (ENXIO); } return (0); } static int pmuextint_attach(device_t dev) { pmu_extint = dev; if (pmu) return (setup_pmu_intr(pmu,dev)); return (0); } static int pmu_attach(device_t dev) { struct pmu_softc *sc; int i; uint8_t reg; uint8_t cmd[2] = {2, 0}; uint8_t resp[16]; phandle_t node,child; struct sysctl_ctx_list *ctx; struct sysctl_oid *tree; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_memrid = 0; sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_memrid, RF_ACTIVE); mtx_init(&sc->sc_mutex,"pmu",NULL,MTX_DEF | MTX_RECURSE); if (sc->sc_memr == NULL) { device_printf(dev, "Could not alloc mem resource!\n"); return (ENXIO); } /* * Our interrupt is attached to a GPIO pin. Depending on probe order, * we may not have found it yet. If we haven't, it will find us, and * attach our interrupt then. */ pmu = dev; if (pmu_extint != NULL) { if (setup_pmu_intr(dev,pmu_extint) != 0) return (ENXIO); } sc->sc_autopoll = 0; sc->sc_batteries = 0; sc->adb_bus = NULL; sc->sc_leddev = NULL; /* Init PMU */ pmu_write_reg(sc, vBufB, pmu_read_reg(sc, vBufB) | vPB4); pmu_write_reg(sc, vDirB, (pmu_read_reg(sc, vDirB) | vPB4) & ~vPB3); reg = PMU_DEFAULTS; pmu_send(sc, PMU_SET_IMASK, 1, ®, 16, resp); pmu_write_reg(sc, vIER, 0x94); /* make sure VIA interrupts are on */ pmu_send(sc, PMU_SYSTEM_READY, 1, cmd, 16, resp); pmu_send(sc, PMU_GET_VERSION, 0, cmd, 16, resp); /* Initialize child buses (ADB) */ node = ofw_bus_get_node(dev); for (child = OF_child(node); child != 0; child = OF_peer(child)) { char name[32]; memset(name, 0, sizeof(name)); OF_getprop(child, "name", name, sizeof(name)); if (bootverbose) device_printf(dev, "PMU child <%s>\n",name); if (strncmp(name, "adb", 4) == 0) { sc->adb_bus = device_add_child(dev,"adb",-1); } if (strncmp(name, "power-mgt", 9) == 0) { uint32_t prim_info[9]; if (OF_getprop(child, "prim-info", prim_info, sizeof(prim_info)) >= 7) sc->sc_batteries = (prim_info[6] >> 16) & 0xff; if (bootverbose && sc->sc_batteries > 0) device_printf(dev, "%d batteries detected\n", sc->sc_batteries); } } /* * Set up sysctls */ ctx = device_get_sysctl_ctx(dev); tree = device_get_sysctl_tree(dev); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "server_mode", CTLTYPE_INT | CTLFLAG_RW, sc, 0, pmu_server_mode, "I", "Enable reboot after power failure"); if (sc->sc_batteries > 0) { struct sysctl_oid *oid, *battroot; char battnum[2]; /* Only start the battery monitor if we have a battery. */ kproc_start(&pmu_batt_kp); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "monitor_batteries", CTLTYPE_INT | CTLFLAG_RW, sc, 0, pmu_battmon, "I", "Post battery events to devd"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "acline", CTLTYPE_INT | CTLFLAG_RD, sc, 0, pmu_acline_state, "I", "AC Line Status"); battroot = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "batteries", CTLFLAG_RD, 0, "Battery Information"); for (i = 0; i < sc->sc_batteries; i++) { battnum[0] = i + '0'; battnum[1] = '\0'; oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(battroot), OID_AUTO, battnum, CTLFLAG_RD, 0, "Battery Information"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "present", CTLTYPE_INT | CTLFLAG_RD, sc, PMU_BATSYSCTL_PRESENT | i, pmu_battquery_sysctl, "I", "Battery present"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "charging", CTLTYPE_INT | CTLFLAG_RD, sc, PMU_BATSYSCTL_CHARGING | i, pmu_battquery_sysctl, "I", "Battery charging"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "charge", CTLTYPE_INT | CTLFLAG_RD, sc, PMU_BATSYSCTL_CHARGE | i, pmu_battquery_sysctl, "I", "Battery charge (mAh)"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "maxcharge", CTLTYPE_INT | CTLFLAG_RD, sc, PMU_BATSYSCTL_MAXCHARGE | i, pmu_battquery_sysctl, "I", "Maximum battery capacity (mAh)"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "rate", CTLTYPE_INT | CTLFLAG_RD, sc, PMU_BATSYSCTL_CURRENT | i, pmu_battquery_sysctl, "I", "Battery discharge rate (mA)"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "voltage", CTLTYPE_INT | CTLFLAG_RD, sc, PMU_BATSYSCTL_VOLTAGE | i, pmu_battquery_sysctl, "I", "Battery voltage (mV)"); /* Knobs for mental compatibility with ACPI */ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "time", CTLTYPE_INT | CTLFLAG_RD, sc, PMU_BATSYSCTL_TIME | i, pmu_battquery_sysctl, "I", "Time Remaining (minutes)"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "life", CTLTYPE_INT | CTLFLAG_RD, sc, PMU_BATSYSCTL_LIFE | i, pmu_battquery_sysctl, "I", "Capacity remaining (percent)"); } } /* * Set up LED interface */ sc->sc_leddev = led_create(pmu_set_sleepled, sc, "sleepled"); /* * Register RTC */ clock_register(dev, 1000); /* * Register power control handler */ EVENTHANDLER_REGISTER(shutdown_final, pmu_shutdown, sc, SHUTDOWN_PRI_LAST); return (bus_generic_attach(dev)); } static int pmu_detach(device_t dev) { struct pmu_softc *sc; sc = device_get_softc(dev); if (sc->sc_leddev != NULL) led_destroy(sc->sc_leddev); bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih); bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq); bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_memrid, sc->sc_memr); mtx_destroy(&sc->sc_mutex); return (bus_generic_detach(dev)); } static uint8_t pmu_read_reg(struct pmu_softc *sc, u_int offset) { return (bus_read_1(sc->sc_memr, offset)); } static void pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value) { bus_write_1(sc->sc_memr, offset, value); } static int pmu_send_byte(struct pmu_softc *sc, uint8_t data) { pmu_out(sc); pmu_write_reg(sc, vSR, data); pmu_ack_off(sc); /* wait for intr to come up */ /* XXX should add a timeout and bail if it expires */ do {} while (pmu_intr_state(sc) == 0); pmu_ack_on(sc); do {} while (pmu_intr_state(sc)); pmu_ack_on(sc); return 0; } static inline int pmu_read_byte(struct pmu_softc *sc, uint8_t *data) { volatile uint8_t scratch; pmu_in(sc); scratch = pmu_read_reg(sc, vSR); pmu_ack_off(sc); /* wait for intr to come up */ do {} while (pmu_intr_state(sc) == 0); pmu_ack_on(sc); do {} while (pmu_intr_state(sc)); *data = pmu_read_reg(sc, vSR); return 0; } static int pmu_intr_state(struct pmu_softc *sc) { return ((pmu_read_reg(sc, vBufB) & vPB3) == 0); } static int pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg, int rlen, uint8_t *out_msg) { struct pmu_softc *sc = cookie; int i, rcv_len = -1; uint8_t out_len, intreg; intreg = pmu_read_reg(sc, vIER); intreg &= 0x10; pmu_write_reg(sc, vIER, intreg); /* wait idle */ do {} while (pmu_intr_state(sc)); /* send command */ pmu_send_byte(sc, cmd); /* send length if necessary */ if (pm_send_cmd_type[cmd] < 0) { pmu_send_byte(sc, length); } for (i = 0; i < length; i++) { pmu_send_byte(sc, in_msg[i]); } /* see if there's data to read */ rcv_len = pm_receive_cmd_type[cmd]; if (rcv_len == 0) goto done; /* read command */ if (rcv_len == 1) { pmu_read_byte(sc, out_msg); goto done; } else out_msg[0] = cmd; if (rcv_len < 0) { pmu_read_byte(sc, &out_len); rcv_len = out_len + 1; } for (i = 1; i < min(rcv_len, rlen); i++) pmu_read_byte(sc, &out_msg[i]); done: pmu_write_reg(sc, vIER, (intreg == 0) ? 0 : 0x90); return rcv_len; } static u_int pmu_poll(device_t dev) { pmu_intr(dev); return (0); } static void pmu_in(struct pmu_softc *sc) { uint8_t reg; reg = pmu_read_reg(sc, vACR); reg &= ~vSR_OUT; reg |= 0x0c; pmu_write_reg(sc, vACR, reg); } static void pmu_out(struct pmu_softc *sc) { uint8_t reg; reg = pmu_read_reg(sc, vACR); reg |= vSR_OUT; reg |= 0x0c; pmu_write_reg(sc, vACR, reg); } static void pmu_ack_off(struct pmu_softc *sc) { uint8_t reg; reg = pmu_read_reg(sc, vBufB); reg &= ~vPB4; pmu_write_reg(sc, vBufB, reg); } static void pmu_ack_on(struct pmu_softc *sc) { uint8_t reg; reg = pmu_read_reg(sc, vBufB); reg |= vPB4; pmu_write_reg(sc, vBufB, reg); } static void pmu_intr(void *arg) { device_t dev; struct pmu_softc *sc; unsigned int len; uint8_t resp[16]; uint8_t junk[16]; dev = (device_t)arg; sc = device_get_softc(dev); mtx_lock(&sc->sc_mutex); pmu_write_reg(sc, vIFR, 0x90); /* Clear 'em */ len = pmu_send(sc, PMU_INT_ACK, 0, NULL, 16, resp); mtx_unlock(&sc->sc_mutex); if ((len < 1) || (resp[1] == 0)) { return; } if (resp[1] & PMU_INT_ADB) { /* * the PMU will turn off autopolling after each command that * it did not issue, so we assume any but TALK R0 is ours and * re-enable autopoll here whenever we receive an ACK for a * non TR0 command. */ mtx_lock(&sc->sc_mutex); if ((resp[2] & 0x0f) != (ADB_COMMAND_TALK << 2)) { if (sc->sc_autopoll) { uint8_t cmd[] = {0, PMU_SET_POLL_MASK, (sc->sc_autopoll >> 8) & 0xff, sc->sc_autopoll & 0xff}; pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, junk); } } mtx_unlock(&sc->sc_mutex); adb_receive_raw_packet(sc->adb_bus,resp[1],resp[2], len - 3,&resp[3]); } if (resp[1] & PMU_INT_ENVIRONMENT) { /* if the lid was just closed, notify devd. */ if ((resp[2] & PMU_ENV_LID_CLOSED) && (!sc->lid_closed)) { sc->lid_closed = 1; devctl_notify("PMU", "lid", "close", NULL); } else if (!(resp[2] & PMU_ENV_LID_CLOSED) && (sc->lid_closed)) { /* if the lid was just opened, notify devd. */ sc->lid_closed = 0; devctl_notify("PMU", "lid", "open", NULL); } if (resp[2] & PMU_ENV_POWER) devctl_notify("PMU", "Button", "pressed", NULL); } } static u_int pmu_adb_send(device_t dev, u_char command_byte, int len, u_char *data, u_char poll) { struct pmu_softc *sc = device_get_softc(dev); int i,replen; uint8_t packet[16], resp[16]; /* construct an ADB command packet and send it */ packet[0] = command_byte; packet[1] = 0; packet[2] = len; for (i = 0; i < len; i++) packet[i + 3] = data[i]; mtx_lock(&sc->sc_mutex); replen = pmu_send(sc, PMU_ADB_CMD, len + 3, packet, 16, resp); mtx_unlock(&sc->sc_mutex); if (poll) pmu_poll(dev); return 0; } static u_int pmu_adb_autopoll(device_t dev, uint16_t mask) { struct pmu_softc *sc = device_get_softc(dev); /* magical incantation to re-enable autopolling */ uint8_t cmd[] = {0, PMU_SET_POLL_MASK, (mask >> 8) & 0xff, mask & 0xff}; uint8_t resp[16]; mtx_lock(&sc->sc_mutex); if (sc->sc_autopoll == mask) { mtx_unlock(&sc->sc_mutex); return 0; } sc->sc_autopoll = mask & 0xffff; if (mask) pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, resp); else pmu_send(sc, PMU_ADB_POLL_OFF, 0, NULL, 16, resp); mtx_unlock(&sc->sc_mutex); return 0; } static void pmu_shutdown(void *xsc, int howto) { struct pmu_softc *sc = xsc; uint8_t cmd[] = {'M', 'A', 'T', 'T'}; if (howto & RB_HALT) pmu_send(sc, PMU_POWER_OFF, 4, cmd, 0, NULL); else pmu_send(sc, PMU_RESET_CPU, 0, NULL, 0, NULL); for (;;); } static void pmu_set_sleepled(void *xsc, int onoff) { struct pmu_softc *sc = xsc; uint8_t cmd[] = {4, 0, 0}; cmd[2] = onoff; mtx_lock(&sc->sc_mutex); pmu_send(sc, PMU_SET_SLEEPLED, 3, cmd, 0, NULL); mtx_unlock(&sc->sc_mutex); } static int pmu_server_mode(SYSCTL_HANDLER_ARGS) { struct pmu_softc *sc = arg1; u_int server_mode = 0; uint8_t getcmd[] = {PMU_PWR_GET_POWERUP_EVENTS}; uint8_t setcmd[] = {0, 0, PMU_PWR_WAKEUP_AC_INSERT}; uint8_t resp[3]; int error, len; mtx_lock(&sc->sc_mutex); len = pmu_send(sc, PMU_POWER_EVENTS, 1, getcmd, 3, resp); mtx_unlock(&sc->sc_mutex); if (len == 3) server_mode = (resp[2] & PMU_PWR_WAKEUP_AC_INSERT) ? 1 : 0; error = sysctl_handle_int(oidp, &server_mode, 0, req); if (len != 3) return (EINVAL); if (error || !req->newptr) return (error); if (server_mode == 1) setcmd[0] = PMU_PWR_SET_POWERUP_EVENTS; else if (server_mode == 0) setcmd[0] = PMU_PWR_CLR_POWERUP_EVENTS; else return (EINVAL); setcmd[1] = resp[1]; mtx_lock(&sc->sc_mutex); pmu_send(sc, PMU_POWER_EVENTS, 3, setcmd, 2, resp); mtx_unlock(&sc->sc_mutex); return (0); } static int pmu_query_battery(struct pmu_softc *sc, int batt, struct pmu_battstate *info) { uint8_t reg; uint8_t resp[16]; int len; reg = batt + 1; mtx_lock(&sc->sc_mutex); len = pmu_send(sc, PMU_SMART_BATTERY_STATE, 1, ®, 16, resp); mtx_unlock(&sc->sc_mutex); if (len < 3) return (-1); /* All PMU battery info replies share a common header: * Byte 1 Payload Format * Byte 2 Battery Flags */ info->state = resp[2]; switch (resp[1]) { case 3: case 4: /* * Formats 3 and 4 appear to be the same: * Byte 3 Charge * Byte 4 Max Charge * Byte 5 Current * Byte 6 Voltage */ info->charge = resp[3]; info->maxcharge = resp[4]; /* Current can be positive or negative */ info->current = (int8_t)resp[5]; info->voltage = resp[6]; break; case 5: /* * Formats 5 is a wider version of formats 3 and 4 * Byte 3-4 Charge * Byte 5-6 Max Charge * Byte 7-8 Current * Byte 9-10 Voltage */ info->charge = (resp[3] << 8) | resp[4]; info->maxcharge = (resp[5] << 8) | resp[6]; /* Current can be positive or negative */ info->current = (int16_t)((resp[7] << 8) | resp[8]); info->voltage = (resp[9] << 8) | resp[10]; break; default: device_printf(sc->sc_dev, "Unknown battery info format (%d)!\n", resp[1]); return (-1); } return (0); } static void pmu_battery_notify(struct pmu_battstate *batt, struct pmu_battstate *old) { char notify_buf[16]; int new_acline, old_acline; new_acline = (batt->state & PMU_PWR_AC_PRESENT) ? 1 : 0; old_acline = (old->state & PMU_PWR_AC_PRESENT) ? 1 : 0; if (new_acline != old_acline) { snprintf(notify_buf, sizeof(notify_buf), "notify=0x%02x", new_acline); devctl_notify("PMU", "POWER", "ACLINE", notify_buf); } } static void pmu_battquery_proc() { struct pmu_softc *sc; struct pmu_battstate batt; struct pmu_battstate cur_batt; int error; sc = device_get_softc(pmu); bzero(&cur_batt, sizeof(cur_batt)); while (1) { kproc_suspend_check(curproc); error = pmu_query_battery(sc, 0, &batt); pmu_battery_notify(&batt, &cur_batt); cur_batt = batt; pause("pmu_batt", hz); } } static int pmu_battmon(SYSCTL_HANDLER_ARGS) { struct pmu_softc *sc; int error, result; sc = arg1; result = pmu_battmon_enabled; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if (!result && pmu_battmon_enabled) error = kproc_suspend(pmubattproc, hz); else if (result && pmu_battmon_enabled == 0) error = kproc_resume(pmubattproc); pmu_battmon_enabled = (result != 0); return (error); } static int pmu_acline_state(SYSCTL_HANDLER_ARGS) { struct pmu_softc *sc; struct pmu_battstate batt; int error, result; sc = arg1; /* The PMU treats the AC line status as a property of the battery */ error = pmu_query_battery(sc, 0, &batt); if (error != 0) return (error); result = (batt.state & PMU_PWR_AC_PRESENT) ? 1 : 0; error = sysctl_handle_int(oidp, &result, 0, req); return (error); } static int pmu_battquery_sysctl(SYSCTL_HANDLER_ARGS) { struct pmu_softc *sc; struct pmu_battstate batt; int error, result; sc = arg1; error = pmu_query_battery(sc, arg2 & 0x00ff, &batt); if (error != 0) return (error); switch (arg2 & 0xff00) { case PMU_BATSYSCTL_PRESENT: result = (batt.state & PMU_PWR_BATT_PRESENT) ? 1 : 0; break; case PMU_BATSYSCTL_CHARGING: result = (batt.state & PMU_PWR_BATT_CHARGING) ? 1 : 0; break; case PMU_BATSYSCTL_CHARGE: result = batt.charge; break; case PMU_BATSYSCTL_MAXCHARGE: result = batt.maxcharge; break; case PMU_BATSYSCTL_CURRENT: result = batt.current; break; case PMU_BATSYSCTL_VOLTAGE: result = batt.voltage; break; case PMU_BATSYSCTL_TIME: /* Time remaining until full charge/discharge, in minutes */ if (batt.current >= 0) result = (batt.maxcharge - batt.charge) /* mAh */ * 60 / batt.current /* mA */; else result = (batt.charge /* mAh */ * 60) / (-batt.current /* mA */); break; case PMU_BATSYSCTL_LIFE: /* Battery charge fraction, in percent */ result = (batt.charge * 100) / batt.maxcharge; break; default: /* This should never happen */ result = -1; } error = sysctl_handle_int(oidp, &result, 0, req); return (error); } #define DIFF19041970 2082844800 static int pmu_gettime(device_t dev, struct timespec *ts) { struct pmu_softc *sc = device_get_softc(dev); uint8_t resp[16]; uint32_t sec; mtx_lock(&sc->sc_mutex); pmu_send(sc, PMU_READ_RTC, 0, NULL, 16, resp); mtx_unlock(&sc->sc_mutex); memcpy(&sec, &resp[1], 4); ts->tv_sec = sec - DIFF19041970; ts->tv_nsec = 0; return (0); } static int pmu_settime(device_t dev, struct timespec *ts) { struct pmu_softc *sc = device_get_softc(dev); uint32_t sec; sec = ts->tv_sec + DIFF19041970; mtx_lock(&sc->sc_mutex); pmu_send(sc, PMU_SET_RTC, sizeof(sec), (uint8_t *)&sec, 0, NULL); mtx_unlock(&sc->sc_mutex); return (0); } int pmu_set_speed(int low_speed) { struct pmu_softc *sc; uint8_t sleepcmd[] = {'W', 'O', 'O', 'F', 0}; uint8_t resp[16]; sc = device_get_softc(pmu); pmu_write_reg(sc, vIER, 0x10); spinlock_enter(); mtdec(0x7fffffff); mb(); mtdec(0x7fffffff); sleepcmd[4] = low_speed; pmu_send(sc, PMU_CPU_SPEED, 5, sleepcmd, 16, resp); unin_chip_sleep(NULL, 1); platform_sleep(); unin_chip_wake(NULL); mtdec(1); /* Force a decrementer exception */ spinlock_exit(); pmu_write_reg(sc, vIER, 0x90); return (0); } Index: head/sys/powerpc/powermac/pmuvar.h =================================================================== --- head/sys/powerpc/powermac/pmuvar.h (revision 326260) +++ head/sys/powerpc/powermac/pmuvar.h (revision 326261) @@ -1,179 +1,181 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2006 Michael Lorenz * Copyright (c) 2008 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ #ifndef PMUVAR_H #define PMUVAR_H /* PMU commands */ #define PMU_POWER_CTRL0 0x10 /* control power of some devices */ #define PMU_POWER_CTRL 0x11 /* control power of some devices */ #define PMU_POWER_OFF 0x7e /* Turn Power off */ #define PMU_RESET_CPU 0xd0 /* Reset CPU */ #define PMU_SET_RTC 0x30 /* Set realtime clock */ #define PMU_READ_RTC 0x38 /* Read realtime clock */ #define PMU_WRITE_PRAM 0x32 /* Write PRAM */ #define PMU_READ_PRAM 0x3a /* Read PRAM */ #define PMU_WRITE_NVRAM 0x33 /* Write NVRAM */ #define PMU_READ_NVRAM 0x3b /* Read NVRAM */ #define PMU_EJECT_PCMCIA 0x4c /* Eject PCMCIA slot */ #define PMU_SET_BRIGHTNESS 0x41 /* Set backlight brightness */ #define PMU_READ_BRIGHTNESS 0xd9 /* Read brightness button position */ #define PMU_POWER_EVENTS 0x8f /* Send power-event commands to PMU */ #define PMU_SYSTEM_READY 0xdf /* tell PMU we are awake */ #define PMU_BATTERY_STATE 0x6b /* Read old battery state */ #define PMU_SMART_BATTERY_STATE 0x6f /* Read battery state */ #define PMU_ADB_CMD 0x20 /* Send ADB packet */ #define PMU_ADB_POLL_OFF 0x21 /* Disable ADB auto-poll */ #define PMU_SET_VOL 0x40 /* Set volume button position */ #define PMU_GET_VOL 0x48 /* Get volume button position */ #define PMU_SET_IMASK 0x70 /* Set interrupt mask */ #define PMU_INT_ACK 0x78 /* Read interrupt bits */ #define PMU_CPU_SPEED 0x7d /* Control CPU speed on some models */ #define PMU_SLEEP 0x7f /* Put CPU to sleep */ #define PMU_SET_POLL_MASK 0x86 /* * 16bit mask enables autopolling per * device */ #define PMU_I2C_CMD 0x9a /* i2c commands */ #define PMU_GET_LID_STATE 0xdc /* Report lid state */ #define PMU_GET_VERSION 0xea /* Identify thyself */ #define PMU_SET_SLEEPLED 0xee /* Set sleep LED on/off */ /* Bits in PMU interrupt and interrupt mask bytes */ #define PMU_INT_ADB_AUTO 0x04 /* ADB autopoll, when PMU_INT_ADB */ #define PMU_INT_PCEJECT 0x04 /* PC-card eject buttons */ #define PMU_INT_SNDBRT 0x08 /* sound/brightness up/down buttons */ #define PMU_INT_ADB 0x10 /* ADB autopoll or reply data */ #define PMU_INT_BATTERY 0x20 #define PMU_INT_ENVIRONMENT 0x40 #define PMU_INT_TICK 0x80 /* 1-second tick interrupt */ /* Bits to use with the PMU_POWER_CTRL0 command */ #define PMU_POW0_ON 0x80 /* OR this to power ON the device */ #define PMU_POW0_OFF 0x00 /* leave bit 7 to 0 to power it OFF */ #define PMU_POW0_HARD_DRIVE 0x04 /* wallstreet/lombard? */ /* Bits to use with the PMU_POWER_CTRL command */ #define PMU_POW_ON 0x80 /* OR this to power ON the device */ #define PMU_POW_OFF 0x00 /* leave bit 7 to 0 to power it OFF */ #define PMU_POW_BACKLIGHT 0x01 /* backlight power */ #define PMU_POW_CHARGER 0x02 /* battery charger power */ #define PMU_POW_IRLED 0x04 /* IR led power (on wallstreet) */ #define PMU_POW_MEDIABAY 0x08 /* media bay power (wallstreet/lombard ?) */ /* Bits from PMU_GET_LID_STATE or PMU_INT_ENVIRONMENT on core99 */ #define PMU_ENV_LID_CLOSED 0x01 /* The lid is closed */ #define PMU_ENV_POWER 0x08 /* Power Button pressed */ /* PMU PMU_POWER_EVENTS commands */ enum { PMU_PWR_GET_POWERUP_EVENTS = 0x00, PMU_PWR_SET_POWERUP_EVENTS = 0x01, PMU_PWR_CLR_POWERUP_EVENTS = 0x02, PMU_PWR_GET_WAKEUP_EVENTS = 0x03, PMU_PWR_SET_WAKEUP_EVENTS = 0x04, PMU_PWR_CLR_WAKEUP_EVENTS = 0x05, }; /* PMU Power Information */ #define PMU_PWR_AC_PRESENT (1 << 0) #define PMU_PWR_BATT_CHARGING (1 << 1) #define PMU_PWR_BATT_PRESENT (1 << 2) #define PMU_PWR_BATT_FULL (1 << 5) #define PMU_PWR_PCHARGE_RESET (1 << 6) #define PMU_PWR_BATT_EXIST (1 << 7) /* I2C related definitions */ #define PMU_I2C_MODE_SIMPLE 0 #define PMU_I2C_MODE_STDSUB 1 #define PMU_I2C_MODE_COMBINED 2 #define PMU_I2C_BUS_STATUS 0 #define PMU_I2C_BUS_SYSCLK 1 #define PMU_I2C_BUS_POWER 2 #define PMU_I2C_STATUS_OK 0 #define PMU_I2C_STATUS_DATAREAD 1 #define PMU_I2C_STATUS_BUSY 0xfe /* Power events wakeup bits */ enum { PMU_PWR_WAKEUP_KEY = 0x01, /* Wake on key press */ PMU_PWR_WAKEUP_AC_INSERT = 0x02, /* Wake on AC adapter plug */ PMU_PWR_WAKEUP_AC_CHANGE = 0x04, PMU_PWR_WAKEUP_LID_OPEN = 0x08, PMU_PWR_WAKEUP_RING = 0x10, }; #define PMU_NOTREADY 0x1 /* has not been initialized yet */ #define PMU_IDLE 0x2 /* the bus is currently idle */ #define PMU_OUT 0x3 /* sending out a command */ #define PMU_IN 0x4 /* receiving data */ struct pmu_softc { device_t sc_dev; int sc_memrid; struct resource *sc_memr; int sc_irqrid; struct resource *sc_irq; void *sc_ih; struct mtx sc_mutex; device_t adb_bus; volatile int sc_autopoll; int sc_batteries; struct cdev *sc_leddev; int lid_closed; uint8_t saved_regs[9]; }; struct pmu_battstate { int state; int charge; int maxcharge; int current; int voltage; }; int pmu_set_speed(int low_speed); #endif /* PMUVAR_H */ Index: head/sys/powerpc/powermac/powermac_thermal.c =================================================================== --- head/sys/powerpc/powermac/powermac_thermal.c (revision 326260) +++ head/sys/powerpc/powermac/powermac_thermal.c (revision 326261) @@ -1,218 +1,220 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2009-2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "powermac_thermal.h" /* A 10 second timer for spinning down fans. */ #define FAN_HYSTERESIS_TIMER 10 static void fan_management_proc(void); static void pmac_therm_manage_fans(void); static struct proc *pmac_them_proc; static int enable_pmac_thermal = 1; static struct kproc_desc pmac_therm_kp = { "pmac_thermal", fan_management_proc, &pmac_them_proc }; SYSINIT(pmac_therm_setup, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, kproc_start, &pmac_therm_kp); SYSCTL_INT(_machdep, OID_AUTO, manage_fans, CTLFLAG_RW | CTLFLAG_TUN, &enable_pmac_thermal, 1, "Enable automatic fan management"); static MALLOC_DEFINE(M_PMACTHERM, "pmactherm", "Powermac Thermal Management"); struct pmac_fan_le { struct pmac_fan *fan; int last_val; int timer; SLIST_ENTRY(pmac_fan_le) entries; }; struct pmac_sens_le { struct pmac_therm *sensor; int last_val; #define MAX_CRITICAL_COUNT 6 int critical_count; SLIST_ENTRY(pmac_sens_le) entries; }; static SLIST_HEAD(pmac_fans, pmac_fan_le) fans = SLIST_HEAD_INITIALIZER(fans); static SLIST_HEAD(pmac_sensors, pmac_sens_le) sensors = SLIST_HEAD_INITIALIZER(sensors); static void fan_management_proc(void) { /* Nothing to manage? */ if (SLIST_EMPTY(&fans)) kproc_exit(0); while (1) { pmac_therm_manage_fans(); pause("pmac_therm", hz); } } static void pmac_therm_manage_fans(void) { struct pmac_sens_le *sensor; struct pmac_fan_le *fan; int average_excess, max_excess_zone, frac_excess; int fan_speed; int nsens, nsens_zone; int temp; if (!enable_pmac_thermal) return; /* Read all the sensors */ SLIST_FOREACH(sensor, &sensors, entries) { temp = sensor->sensor->read(sensor->sensor); if (temp > 0) /* Use the previous temp in case of error */ sensor->last_val = temp; if (sensor->last_val > sensor->sensor->max_temp) { sensor->critical_count++; printf("WARNING: Current temperature (%s: %d.%d C) " "exceeds critical temperature (%d.%d C); " "count=%d\n", sensor->sensor->name, (sensor->last_val - ZERO_C_TO_K) / 10, (sensor->last_val - ZERO_C_TO_K) % 10, (sensor->sensor->max_temp - ZERO_C_TO_K) / 10, (sensor->sensor->max_temp - ZERO_C_TO_K) % 10, sensor->critical_count); if (sensor->critical_count >= MAX_CRITICAL_COUNT) { printf("WARNING: %s temperature exceeded " "critical temperature %d times in a row; " "shutting down!\n", sensor->sensor->name, sensor->critical_count); shutdown_nice(RB_POWEROFF); } } else { if (sensor->critical_count > 0) sensor->critical_count--; } } /* Set all the fans */ SLIST_FOREACH(fan, &fans, entries) { nsens = nsens_zone = 0; average_excess = max_excess_zone = 0; SLIST_FOREACH(sensor, &sensors, entries) { temp = imin(sensor->last_val, sensor->sensor->max_temp); frac_excess = (temp - sensor->sensor->target_temp)*100 / (sensor->sensor->max_temp - temp + 1); if (frac_excess < 0) frac_excess = 0; if (sensor->sensor->zone == fan->fan->zone) { max_excess_zone = imax(max_excess_zone, frac_excess); nsens_zone++; } average_excess += frac_excess; nsens++; } average_excess /= nsens; /* If there are no sensors in this zone, use the average */ if (nsens_zone == 0) max_excess_zone = average_excess; /* No sensors at all? Use default */ if (nsens == 0) { fan->fan->set(fan->fan, fan->fan->default_rpm); continue; } /* * Scale the fan linearly in the max temperature in its * thermal zone. */ max_excess_zone = imin(max_excess_zone, 100); fan_speed = max_excess_zone * (fan->fan->max_rpm - fan->fan->min_rpm)/100 + fan->fan->min_rpm; if (fan_speed >= fan->last_val) { fan->timer = FAN_HYSTERESIS_TIMER; fan->last_val = fan_speed; } else { fan->timer--; if (fan->timer == 0) { fan->last_val = fan_speed; fan->timer = FAN_HYSTERESIS_TIMER; } } fan->fan->set(fan->fan, fan->last_val); } } void pmac_thermal_fan_register(struct pmac_fan *fan) { struct pmac_fan_le *list_entry; list_entry = malloc(sizeof(struct pmac_fan_le), M_PMACTHERM, M_ZERO | M_WAITOK); list_entry->fan = fan; SLIST_INSERT_HEAD(&fans, list_entry, entries); } void pmac_thermal_sensor_register(struct pmac_therm *sensor) { struct pmac_sens_le *list_entry; list_entry = malloc(sizeof(struct pmac_sens_le), M_PMACTHERM, M_ZERO | M_WAITOK); list_entry->sensor = sensor; list_entry->last_val = 0; list_entry->critical_count = 0; SLIST_INSERT_HEAD(&sensors, list_entry, entries); } Index: head/sys/powerpc/powermac/powermac_thermal.h =================================================================== --- head/sys/powerpc/powermac/powermac_thermal.h (revision 326260) +++ head/sys/powerpc/powermac/powermac_thermal.h (revision 326261) @@ -1,56 +1,58 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2009-2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _POWERPC_POWERMAC_POWERMAC_THERMAL_H #define _POWERPC_POWERMAC_POWERMAC_THERMAL_H #define ZERO_C_TO_K 2731 struct pmac_fan { int min_rpm, max_rpm, default_rpm; char name[32]; int zone; int (*read)(struct pmac_fan *); int (*set)(struct pmac_fan *, int value); }; struct pmac_therm { int target_temp, max_temp; /* Tenths of a degree K */ char name[32]; int zone; int (*read)(struct pmac_therm *); }; void pmac_thermal_fan_register(struct pmac_fan *); void pmac_thermal_sensor_register(struct pmac_therm *); #endif Index: head/sys/powerpc/powermac/pswitch.c =================================================================== --- head/sys/powerpc/powermac/pswitch.c (revision 326260) +++ head/sys/powerpc/powermac/pswitch.c (revision 326261) @@ -1,144 +1,146 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2002 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include struct pswitch_softc { int sc_irqrid; struct resource *sc_irq; void *sc_ih; }; static int pswitch_probe(device_t); static int pswitch_attach(device_t); static int pswitch_intr(void *); static device_method_t pswitch_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pswitch_probe), DEVMETHOD(device_attach, pswitch_attach), { 0, 0 } }; static driver_t pswitch_driver = { "pswitch", pswitch_methods, sizeof(struct pswitch_softc) }; static devclass_t pswitch_devclass; DRIVER_MODULE(pswitch, macio, pswitch_driver, pswitch_devclass, 0, 0); static int pswitch_probe(device_t dev) { char *type = macio_get_devtype(dev); if (strcmp(type, "gpio") != 0) return (ENXIO); device_set_desc(dev, "GPIO Programmer's Switch"); return (0); } static int pswitch_attach(device_t dev) { struct pswitch_softc *sc; phandle_t node, child; char type[32]; u_int irq[2]; sc = device_get_softc(dev); node = macio_get_node(dev); for (child = OF_child(node); child != 0; child = OF_peer(child)) { if (OF_getprop(child, "device_type", type, 32) == -1) continue; if (strcmp(type, "programmer-switch") == 0) break; } if (child == 0) { device_printf(dev, "could not find correct node\n"); return (ENXIO); } if (OF_getprop(child, "interrupts", irq, sizeof(irq)) == -1) { device_printf(dev, "could not get interrupt\n"); return (ENXIO); } sc->sc_irqrid = 0; sc->sc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->sc_irqrid, irq[0], irq[0], 1, RF_ACTIVE); if (sc->sc_irq == NULL) { device_printf(dev, "could not allocate interrupt\n"); return (ENXIO); } if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC, pswitch_intr, NULL, dev, &sc->sc_ih) != 0) { device_printf(dev, "could not setup interrupt\n"); bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq); return (ENXIO); } return (0); } static int pswitch_intr(void *arg) { device_t dev; dev = (device_t)arg; kdb_enter(KDB_WHY_POWERPC, device_get_nameunit(dev)); return (FILTER_HANDLED); } Index: head/sys/powerpc/powermac/smu.c =================================================================== --- head/sys/powerpc/powermac/smu.c (revision 326260) +++ head/sys/powerpc/powermac/smu.c (revision 326261) @@ -1,1579 +1,1581 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2009 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clock_if.h" #include "iicbus_if.h" struct smu_cmd { volatile uint8_t cmd; uint8_t len; uint8_t data[254]; STAILQ_ENTRY(smu_cmd) cmd_q; }; STAILQ_HEAD(smu_cmdq, smu_cmd); struct smu_fan { struct pmac_fan fan; device_t dev; cell_t reg; enum { SMU_FAN_RPM, SMU_FAN_PWM } type; int setpoint; int old_style; int rpm; }; /* We can read the PWM and the RPM from a PWM controlled fan. * Offer both values via sysctl. */ enum { SMU_PWM_SYSCTL_PWM = 1 << 8, SMU_PWM_SYSCTL_RPM = 2 << 8 }; struct smu_sensor { struct pmac_therm therm; device_t dev; cell_t reg; enum { SMU_CURRENT_SENSOR, SMU_VOLTAGE_SENSOR, SMU_POWER_SENSOR, SMU_TEMP_SENSOR } type; }; struct smu_softc { device_t sc_dev; struct mtx sc_mtx; struct resource *sc_memr; int sc_memrid; int sc_u3; bus_dma_tag_t sc_dmatag; bus_space_tag_t sc_bt; bus_space_handle_t sc_mailbox; struct smu_cmd *sc_cmd, *sc_cur_cmd; bus_addr_t sc_cmd_phys; bus_dmamap_t sc_cmd_dmamap; struct smu_cmdq sc_cmdq; struct smu_fan *sc_fans; int sc_nfans; int old_style_fans; struct smu_sensor *sc_sensors; int sc_nsensors; int sc_doorbellirqid; struct resource *sc_doorbellirq; void *sc_doorbellirqcookie; struct proc *sc_fanmgt_proc; time_t sc_lastuserchange; /* Calibration data */ uint16_t sc_cpu_diode_scale; int16_t sc_cpu_diode_offset; uint16_t sc_cpu_volt_scale; int16_t sc_cpu_volt_offset; uint16_t sc_cpu_curr_scale; int16_t sc_cpu_curr_offset; uint16_t sc_slots_pow_scale; int16_t sc_slots_pow_offset; struct cdev *sc_leddev; }; /* regular bus attachment functions */ static int smu_probe(device_t); static int smu_attach(device_t); static const struct ofw_bus_devinfo * smu_get_devinfo(device_t bus, device_t dev); /* cpufreq notification hooks */ static void smu_cpufreq_pre_change(device_t, const struct cf_level *level); static void smu_cpufreq_post_change(device_t, const struct cf_level *level); /* clock interface */ static int smu_gettime(device_t dev, struct timespec *ts); static int smu_settime(device_t dev, struct timespec *ts); /* utility functions */ static int smu_run_cmd(device_t dev, struct smu_cmd *cmd, int wait); static int smu_get_datablock(device_t dev, int8_t id, uint8_t *buf, size_t len); static void smu_attach_i2c(device_t dev, phandle_t i2croot); static void smu_attach_fans(device_t dev, phandle_t fanroot); static void smu_attach_sensors(device_t dev, phandle_t sensroot); static void smu_set_sleepled(void *xdev, int onoff); static int smu_server_mode(SYSCTL_HANDLER_ARGS); static void smu_doorbell_intr(void *xdev); static void smu_shutdown(void *xdev, int howto); /* where to find the doorbell GPIO */ static device_t smu_doorbell = NULL; static device_method_t smu_methods[] = { /* Device interface */ DEVMETHOD(device_probe, smu_probe), DEVMETHOD(device_attach, smu_attach), /* Clock interface */ DEVMETHOD(clock_gettime, smu_gettime), DEVMETHOD(clock_settime, smu_settime), /* ofw_bus interface */ DEVMETHOD(bus_child_pnpinfo_str,ofw_bus_gen_child_pnpinfo_str), DEVMETHOD(ofw_bus_get_devinfo, smu_get_devinfo), 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), { 0, 0 }, }; static driver_t smu_driver = { "smu", smu_methods, sizeof(struct smu_softc) }; static devclass_t smu_devclass; DRIVER_MODULE(smu, ofwbus, smu_driver, smu_devclass, 0, 0); static MALLOC_DEFINE(M_SMU, "smu", "SMU Sensor Information"); #define SMU_MAILBOX 0x8000860c #define SMU_FANMGT_INTERVAL 1000 /* ms */ /* Command types */ #define SMU_ADC 0xd8 #define SMU_FAN 0x4a #define SMU_RPM_STATUS 0x01 #define SMU_RPM_SETPOINT 0x02 #define SMU_PWM_STATUS 0x11 #define SMU_PWM_SETPOINT 0x12 #define SMU_I2C 0x9a #define SMU_I2C_SIMPLE 0x00 #define SMU_I2C_NORMAL 0x01 #define SMU_I2C_COMBINED 0x02 #define SMU_MISC 0xee #define SMU_MISC_GET_DATA 0x02 #define SMU_MISC_LED_CTRL 0x04 #define SMU_POWER 0xaa #define SMU_POWER_EVENTS 0x8f #define SMU_PWR_GET_POWERUP 0x00 #define SMU_PWR_SET_POWERUP 0x01 #define SMU_PWR_CLR_POWERUP 0x02 #define SMU_RTC 0x8e #define SMU_RTC_GET 0x81 #define SMU_RTC_SET 0x80 /* Power event types */ #define SMU_WAKEUP_KEYPRESS 0x01 #define SMU_WAKEUP_AC_INSERT 0x02 #define SMU_WAKEUP_AC_CHANGE 0x04 #define SMU_WAKEUP_RING 0x10 /* Data blocks */ #define SMU_CPUTEMP_CAL 0x18 #define SMU_CPUVOLT_CAL 0x21 #define SMU_SLOTPW_CAL 0x78 /* Partitions */ #define SMU_PARTITION 0x3e #define SMU_PARTITION_LATEST 0x01 #define SMU_PARTITION_BASE 0x02 #define SMU_PARTITION_UPDATE 0x03 static int smu_probe(device_t dev) { const char *name = ofw_bus_get_name(dev); if (strcmp(name, "smu") != 0) return (ENXIO); device_set_desc(dev, "Apple System Management Unit"); return (0); } static void smu_phys_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) { struct smu_softc *sc = xsc; sc->sc_cmd_phys = segs[0].ds_addr; } static int smu_attach(device_t dev) { struct smu_softc *sc; phandle_t node, child; uint8_t data[12]; sc = device_get_softc(dev); mtx_init(&sc->sc_mtx, "smu", NULL, MTX_DEF); sc->sc_cur_cmd = NULL; sc->sc_doorbellirqid = -1; sc->sc_u3 = 0; if (OF_finddevice("/u3") != -1) sc->sc_u3 = 1; /* * Map the mailbox area. This should be determined from firmware, * but I have not found a simple way to do that. */ bus_dma_tag_create(NULL, 16, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, PAGE_SIZE, 1, PAGE_SIZE, 0, NULL, NULL, &(sc->sc_dmatag)); sc->sc_bt = &bs_le_tag; bus_space_map(sc->sc_bt, SMU_MAILBOX, 4, 0, &sc->sc_mailbox); /* * Allocate the command buffer. This can be anywhere in the low 4 GB * of memory. */ bus_dmamem_alloc(sc->sc_dmatag, (void **)&sc->sc_cmd, BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->sc_cmd_dmamap); bus_dmamap_load(sc->sc_dmatag, sc->sc_cmd_dmamap, sc->sc_cmd, PAGE_SIZE, smu_phys_callback, sc, 0); STAILQ_INIT(&sc->sc_cmdq); /* * Set up handlers to change CPU voltage when CPU frequency is changed. */ EVENTHANDLER_REGISTER(cpufreq_pre_change, smu_cpufreq_pre_change, dev, EVENTHANDLER_PRI_ANY); EVENTHANDLER_REGISTER(cpufreq_post_change, smu_cpufreq_post_change, dev, EVENTHANDLER_PRI_ANY); node = ofw_bus_get_node(dev); /* Some SMUs have RPM and PWM controlled fans which do not sit * under the same node. So we have to attach them separately. */ smu_attach_fans(dev, node); /* * Now detect and attach the other child devices. */ for (child = OF_child(node); child != 0; child = OF_peer(child)) { char name[32]; memset(name, 0, sizeof(name)); OF_getprop(child, "name", name, sizeof(name)); if (strncmp(name, "sensors", 8) == 0) smu_attach_sensors(dev, child); if (strncmp(name, "smu-i2c-control", 15) == 0) smu_attach_i2c(dev, child); } /* Some SMUs have the I2C children directly under the bus. */ smu_attach_i2c(dev, node); /* * Collect calibration constants. */ smu_get_datablock(dev, SMU_CPUTEMP_CAL, data, sizeof(data)); sc->sc_cpu_diode_scale = (data[4] << 8) + data[5]; sc->sc_cpu_diode_offset = (data[6] << 8) + data[7]; smu_get_datablock(dev, SMU_CPUVOLT_CAL, data, sizeof(data)); sc->sc_cpu_volt_scale = (data[4] << 8) + data[5]; sc->sc_cpu_volt_offset = (data[6] << 8) + data[7]; sc->sc_cpu_curr_scale = (data[8] << 8) + data[9]; sc->sc_cpu_curr_offset = (data[10] << 8) + data[11]; smu_get_datablock(dev, SMU_SLOTPW_CAL, data, sizeof(data)); sc->sc_slots_pow_scale = (data[4] << 8) + data[5]; sc->sc_slots_pow_offset = (data[6] << 8) + data[7]; /* * Set up LED interface */ sc->sc_leddev = led_create(smu_set_sleepled, dev, "sleepled"); /* * Reset on power loss behavior */ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "server_mode", CTLTYPE_INT | CTLFLAG_RW, dev, 0, smu_server_mode, "I", "Enable reboot after power failure"); /* * Set up doorbell interrupt. */ sc->sc_doorbellirqid = 0; sc->sc_doorbellirq = bus_alloc_resource_any(smu_doorbell, SYS_RES_IRQ, &sc->sc_doorbellirqid, RF_ACTIVE); bus_setup_intr(smu_doorbell, sc->sc_doorbellirq, INTR_TYPE_MISC | INTR_MPSAFE, NULL, smu_doorbell_intr, dev, &sc->sc_doorbellirqcookie); powerpc_config_intr(rman_get_start(sc->sc_doorbellirq), INTR_TRIGGER_EDGE, INTR_POLARITY_LOW); /* * Connect RTC interface. */ clock_register(dev, 1000); /* * Learn about shutdown events */ EVENTHANDLER_REGISTER(shutdown_final, smu_shutdown, dev, SHUTDOWN_PRI_LAST); return (bus_generic_attach(dev)); } static const struct ofw_bus_devinfo * smu_get_devinfo(device_t bus, device_t dev) { return (device_get_ivars(dev)); } static void smu_send_cmd(device_t dev, struct smu_cmd *cmd) { struct smu_softc *sc; sc = device_get_softc(dev); mtx_assert(&sc->sc_mtx, MA_OWNED); if (sc->sc_u3) powerpc_pow_enabled = 0; /* SMU cannot work if we go to NAP */ sc->sc_cur_cmd = cmd; /* Copy the command to the mailbox */ sc->sc_cmd->cmd = cmd->cmd; sc->sc_cmd->len = cmd->len; memcpy(sc->sc_cmd->data, cmd->data, sizeof(cmd->data)); bus_dmamap_sync(sc->sc_dmatag, sc->sc_cmd_dmamap, BUS_DMASYNC_PREWRITE); bus_space_write_4(sc->sc_bt, sc->sc_mailbox, 0, sc->sc_cmd_phys); /* Flush the cacheline it is in -- SMU bypasses the cache */ __asm __volatile("sync; dcbf 0,%0; sync" :: "r"(sc->sc_cmd): "memory"); /* Ring SMU doorbell */ macgpio_write(smu_doorbell, GPIO_DDR_OUTPUT); } static void smu_doorbell_intr(void *xdev) { device_t smu; struct smu_softc *sc; int doorbell_ack; smu = xdev; doorbell_ack = macgpio_read(smu_doorbell); sc = device_get_softc(smu); if (doorbell_ack != (GPIO_DDR_OUTPUT | GPIO_LEVEL_RO | GPIO_DATA)) return; mtx_lock(&sc->sc_mtx); if (sc->sc_cur_cmd == NULL) /* spurious */ goto done; /* Check result. First invalidate the cache again... */ __asm __volatile("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory"); bus_dmamap_sync(sc->sc_dmatag, sc->sc_cmd_dmamap, BUS_DMASYNC_POSTREAD); sc->sc_cur_cmd->cmd = sc->sc_cmd->cmd; sc->sc_cur_cmd->len = sc->sc_cmd->len; memcpy(sc->sc_cur_cmd->data, sc->sc_cmd->data, sizeof(sc->sc_cmd->data)); wakeup(sc->sc_cur_cmd); sc->sc_cur_cmd = NULL; if (sc->sc_u3) powerpc_pow_enabled = 1; done: /* Queue next command if one is pending */ if (STAILQ_FIRST(&sc->sc_cmdq) != NULL) { sc->sc_cur_cmd = STAILQ_FIRST(&sc->sc_cmdq); STAILQ_REMOVE_HEAD(&sc->sc_cmdq, cmd_q); smu_send_cmd(smu, sc->sc_cur_cmd); } mtx_unlock(&sc->sc_mtx); } static int smu_run_cmd(device_t dev, struct smu_cmd *cmd, int wait) { struct smu_softc *sc; uint8_t cmd_code; int error; sc = device_get_softc(dev); cmd_code = cmd->cmd; mtx_lock(&sc->sc_mtx); if (sc->sc_cur_cmd != NULL) { STAILQ_INSERT_TAIL(&sc->sc_cmdq, cmd, cmd_q); } else smu_send_cmd(dev, cmd); mtx_unlock(&sc->sc_mtx); if (!wait) return (0); if (sc->sc_doorbellirqid < 0) { /* Poll if the IRQ has not been set up yet */ do { DELAY(50); smu_doorbell_intr(dev); } while (sc->sc_cur_cmd != NULL); } else { /* smu_doorbell_intr will wake us when the command is ACK'ed */ error = tsleep(cmd, 0, "smu", 800 * hz / 1000); if (error != 0) smu_doorbell_intr(dev); /* One last chance */ if (error != 0) { mtx_lock(&sc->sc_mtx); if (cmd->cmd == cmd_code) { /* Never processed */ /* Abort this command if we timed out */ if (sc->sc_cur_cmd == cmd) sc->sc_cur_cmd = NULL; else STAILQ_REMOVE(&sc->sc_cmdq, cmd, smu_cmd, cmd_q); mtx_unlock(&sc->sc_mtx); return (error); } error = 0; mtx_unlock(&sc->sc_mtx); } } /* SMU acks the command by inverting the command bits */ if (cmd->cmd == ((~cmd_code) & 0xff)) error = 0; else error = EIO; return (error); } static int smu_get_datablock(device_t dev, int8_t id, uint8_t *buf, size_t len) { struct smu_cmd cmd; uint8_t addr[4]; cmd.cmd = SMU_PARTITION; cmd.len = 2; cmd.data[0] = SMU_PARTITION_LATEST; cmd.data[1] = id; smu_run_cmd(dev, &cmd, 1); addr[0] = addr[1] = 0; addr[2] = cmd.data[0]; addr[3] = cmd.data[1]; cmd.cmd = SMU_MISC; cmd.len = 7; cmd.data[0] = SMU_MISC_GET_DATA; cmd.data[1] = sizeof(addr); memcpy(&cmd.data[2], addr, sizeof(addr)); cmd.data[6] = len; smu_run_cmd(dev, &cmd, 1); memcpy(buf, cmd.data, len); return (0); } static void smu_slew_cpu_voltage(device_t dev, int to) { struct smu_cmd cmd; cmd.cmd = SMU_POWER; cmd.len = 8; cmd.data[0] = 'V'; cmd.data[1] = 'S'; cmd.data[2] = 'L'; cmd.data[3] = 'E'; cmd.data[4] = 'W'; cmd.data[5] = 0xff; cmd.data[6] = 1; cmd.data[7] = to; smu_run_cmd(dev, &cmd, 1); } static void smu_cpufreq_pre_change(device_t dev, const struct cf_level *level) { /* * Make sure the CPU voltage is raised before we raise * the clock. */ if (level->rel_set[0].freq == 10000 /* max */) smu_slew_cpu_voltage(dev, 0); } static void smu_cpufreq_post_change(device_t dev, const struct cf_level *level) { /* We are safe to reduce CPU voltage after a downward transition */ if (level->rel_set[0].freq < 10000 /* max */) smu_slew_cpu_voltage(dev, 1); /* XXX: 1/4 voltage for 970MP? */ } /* Routines for probing the SMU doorbell GPIO */ static int doorbell_probe(device_t dev); static int doorbell_attach(device_t dev); static device_method_t doorbell_methods[] = { /* Device interface */ DEVMETHOD(device_probe, doorbell_probe), DEVMETHOD(device_attach, doorbell_attach), { 0, 0 }, }; static driver_t doorbell_driver = { "smudoorbell", doorbell_methods, 0 }; static devclass_t doorbell_devclass; DRIVER_MODULE(smudoorbell, macgpio, doorbell_driver, doorbell_devclass, 0, 0); static int doorbell_probe(device_t dev) { const char *name = ofw_bus_get_name(dev); if (strcmp(name, "smu-doorbell") != 0) return (ENXIO); device_set_desc(dev, "SMU Doorbell GPIO"); device_quiet(dev); return (0); } static int doorbell_attach(device_t dev) { smu_doorbell = dev; return (0); } /* * Sensor and fan management */ static int smu_fan_check_old_style(struct smu_fan *fan) { device_t smu = fan->dev; struct smu_softc *sc = device_get_softc(smu); struct smu_cmd cmd; int error; if (sc->old_style_fans != -1) return (sc->old_style_fans); /* * Apple has two fan control mechanisms. We can't distinguish * them except by seeing if the new one fails. If the new one * fails, use the old one. */ cmd.cmd = SMU_FAN; cmd.len = 2; cmd.data[0] = 0x31; cmd.data[1] = fan->reg; do { error = smu_run_cmd(smu, &cmd, 1); } while (error == EWOULDBLOCK); sc->old_style_fans = (error != 0); return (sc->old_style_fans); } static int smu_fan_set_rpm(struct smu_fan *fan, int rpm) { device_t smu = fan->dev; struct smu_cmd cmd; int error; cmd.cmd = SMU_FAN; error = EIO; /* Clamp to allowed range */ rpm = max(fan->fan.min_rpm, rpm); rpm = min(fan->fan.max_rpm, rpm); smu_fan_check_old_style(fan); if (!fan->old_style) { cmd.len = 4; cmd.data[0] = 0x30; cmd.data[1] = fan->reg; cmd.data[2] = (rpm >> 8) & 0xff; cmd.data[3] = rpm & 0xff; error = smu_run_cmd(smu, &cmd, 1); if (error && error != EWOULDBLOCK) fan->old_style = 1; } else { cmd.len = 14; cmd.data[0] = 0x00; /* RPM fan. */ cmd.data[1] = 1 << fan->reg; cmd.data[2 + 2*fan->reg] = (rpm >> 8) & 0xff; cmd.data[3 + 2*fan->reg] = rpm & 0xff; error = smu_run_cmd(smu, &cmd, 1); } if (error == 0) fan->setpoint = rpm; return (error); } static int smu_fan_read_rpm(struct smu_fan *fan) { device_t smu = fan->dev; struct smu_cmd cmd; int rpm, error; smu_fan_check_old_style(fan); if (!fan->old_style) { cmd.cmd = SMU_FAN; cmd.len = 2; cmd.data[0] = 0x31; cmd.data[1] = fan->reg; error = smu_run_cmd(smu, &cmd, 1); if (error && error != EWOULDBLOCK) fan->old_style = 1; rpm = (cmd.data[0] << 8) | cmd.data[1]; } if (fan->old_style) { cmd.cmd = SMU_FAN; cmd.len = 1; cmd.data[0] = SMU_RPM_STATUS; error = smu_run_cmd(smu, &cmd, 1); if (error) return (error); rpm = (cmd.data[fan->reg*2+1] << 8) | cmd.data[fan->reg*2+2]; } return (rpm); } static int smu_fan_set_pwm(struct smu_fan *fan, int pwm) { device_t smu = fan->dev; struct smu_cmd cmd; int error; cmd.cmd = SMU_FAN; error = EIO; /* Clamp to allowed range */ pwm = max(fan->fan.min_rpm, pwm); pwm = min(fan->fan.max_rpm, pwm); /* * Apple has two fan control mechanisms. We can't distinguish * them except by seeing if the new one fails. If the new one * fails, use the old one. */ if (!fan->old_style) { cmd.len = 4; cmd.data[0] = 0x30; cmd.data[1] = fan->reg; cmd.data[2] = (pwm >> 8) & 0xff; cmd.data[3] = pwm & 0xff; error = smu_run_cmd(smu, &cmd, 1); if (error && error != EWOULDBLOCK) fan->old_style = 1; } if (fan->old_style) { cmd.len = 14; cmd.data[0] = 0x10; /* PWM fan. */ cmd.data[1] = 1 << fan->reg; cmd.data[2 + 2*fan->reg] = (pwm >> 8) & 0xff; cmd.data[3 + 2*fan->reg] = pwm & 0xff; error = smu_run_cmd(smu, &cmd, 1); } if (error == 0) fan->setpoint = pwm; return (error); } static int smu_fan_read_pwm(struct smu_fan *fan, int *pwm, int *rpm) { device_t smu = fan->dev; struct smu_cmd cmd; int error; if (!fan->old_style) { cmd.cmd = SMU_FAN; cmd.len = 2; cmd.data[0] = 0x31; cmd.data[1] = fan->reg; error = smu_run_cmd(smu, &cmd, 1); if (error && error != EWOULDBLOCK) fan->old_style = 1; *rpm = (cmd.data[0] << 8) | cmd.data[1]; } if (fan->old_style) { cmd.cmd = SMU_FAN; cmd.len = 1; cmd.data[0] = SMU_PWM_STATUS; error = smu_run_cmd(smu, &cmd, 1); if (error) return (error); *rpm = (cmd.data[fan->reg*2+1] << 8) | cmd.data[fan->reg*2+2]; } if (fan->old_style) { cmd.cmd = SMU_FAN; cmd.len = 14; cmd.data[0] = SMU_PWM_SETPOINT; cmd.data[1] = 1 << fan->reg; error = smu_run_cmd(smu, &cmd, 1); if (error) return (error); *pwm = cmd.data[fan->reg*2+2]; } return (0); } static int smu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS) { device_t smu; struct smu_softc *sc; struct smu_fan *fan; int pwm = 0, rpm, error = 0; smu = arg1; sc = device_get_softc(smu); fan = &sc->sc_fans[arg2 & 0xff]; if (fan->type == SMU_FAN_RPM) { rpm = smu_fan_read_rpm(fan); if (rpm < 0) return (rpm); error = sysctl_handle_int(oidp, &rpm, 0, req); } else { error = smu_fan_read_pwm(fan, &pwm, &rpm); if (error < 0) return (EIO); switch (arg2 & 0xff00) { case SMU_PWM_SYSCTL_PWM: error = sysctl_handle_int(oidp, &pwm, 0, req); break; case SMU_PWM_SYSCTL_RPM: error = sysctl_handle_int(oidp, &rpm, 0, req); break; default: /* This should never happen */ return (EINVAL); } } /* We can only read the RPM from a PWM controlled fan, so return. */ if ((arg2 & 0xff00) == SMU_PWM_SYSCTL_RPM) return (0); if (error || !req->newptr) return (error); sc->sc_lastuserchange = time_uptime; if (fan->type == SMU_FAN_RPM) return (smu_fan_set_rpm(fan, rpm)); else return (smu_fan_set_pwm(fan, pwm)); } static void smu_fill_fan_prop(device_t dev, phandle_t child, int id) { struct smu_fan *fan; struct smu_softc *sc; char type[32]; sc = device_get_softc(dev); fan = &sc->sc_fans[id]; OF_getprop(child, "device_type", type, sizeof(type)); /* We have either RPM or PWM controlled fans. */ if (strcmp(type, "fan-rpm-control") == 0) fan->type = SMU_FAN_RPM; else fan->type = SMU_FAN_PWM; fan->dev = dev; fan->old_style = 0; OF_getprop(child, "reg", &fan->reg, sizeof(cell_t)); OF_getprop(child, "min-value", &fan->fan.min_rpm, sizeof(int)); OF_getprop(child, "max-value", &fan->fan.max_rpm, sizeof(int)); OF_getprop(child, "zone", &fan->fan.zone, sizeof(int)); if (OF_getprop(child, "unmanaged-value", &fan->fan.default_rpm, sizeof(int)) != sizeof(int)) fan->fan.default_rpm = fan->fan.max_rpm; OF_getprop(child, "location", fan->fan.name, sizeof(fan->fan.name)); if (fan->type == SMU_FAN_RPM) fan->setpoint = smu_fan_read_rpm(fan); else smu_fan_read_pwm(fan, &fan->setpoint, &fan->rpm); } /* On the first call count the number of fans. In the second call, * after allocating the fan struct, fill the properties of the fans. */ static int smu_count_fans(device_t dev) { struct smu_softc *sc; phandle_t child, node, root; int nfans = 0; node = ofw_bus_get_node(dev); sc = device_get_softc(dev); /* First find the fanroots and count the number of fans. */ for (root = OF_child(node); root != 0; root = OF_peer(root)) { char name[32]; memset(name, 0, sizeof(name)); OF_getprop(root, "name", name, sizeof(name)); if (strncmp(name, "rpm-fans", 9) == 0 || strncmp(name, "pwm-fans", 9) == 0 || strncmp(name, "fans", 5) == 0) for (child = OF_child(root); child != 0; child = OF_peer(child)) { nfans++; /* When allocated, fill the fan properties. */ if (sc->sc_fans != NULL) { smu_fill_fan_prop(dev, child, nfans - 1); } } } if (nfans == 0) { device_printf(dev, "WARNING: No fans detected!\n"); return (0); } return (nfans); } static void smu_attach_fans(device_t dev, phandle_t fanroot) { struct smu_fan *fan; struct smu_softc *sc; struct sysctl_oid *oid, *fanroot_oid; struct sysctl_ctx_list *ctx; char sysctl_name[32]; int i, j; sc = device_get_softc(dev); /* Get the number of fans. */ sc->sc_nfans = smu_count_fans(dev); if (sc->sc_nfans == 0) return; /* Now we're able to allocate memory for the fans struct. */ sc->sc_fans = malloc(sc->sc_nfans * sizeof(struct smu_fan), M_SMU, M_WAITOK | M_ZERO); /* Now fill in the properties. */ smu_count_fans(dev); /* Register fans with pmac_thermal */ for (i = 0; i < sc->sc_nfans; i++) pmac_thermal_fan_register(&sc->sc_fans[i].fan); ctx = device_get_sysctl_ctx(dev); fanroot_oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "fans", CTLFLAG_RD, 0, "SMU Fan Information"); /* Add sysctls */ for (i = 0; i < sc->sc_nfans; i++) { fan = &sc->sc_fans[i]; for (j = 0; j < strlen(fan->fan.name); j++) { sysctl_name[j] = tolower(fan->fan.name[j]); if (isspace(sysctl_name[j])) sysctl_name[j] = '_'; } sysctl_name[j] = 0; if (fan->type == SMU_FAN_RPM) { oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(fanroot_oid), OID_AUTO, sysctl_name, CTLFLAG_RD, 0, "Fan Information"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "minrpm", CTLFLAG_RD, &fan->fan.min_rpm, 0, "Minimum allowed RPM"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "maxrpm", CTLFLAG_RD, &fan->fan.max_rpm, 0, "Maximum allowed RPM"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "rpm",CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, i, smu_fanrpm_sysctl, "I", "Fan RPM"); fan->fan.read = (int (*)(struct pmac_fan *))smu_fan_read_rpm; fan->fan.set = (int (*)(struct pmac_fan *, int))smu_fan_set_rpm; } else { oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(fanroot_oid), OID_AUTO, sysctl_name, CTLFLAG_RD, 0, "Fan Information"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "minpwm", CTLFLAG_RD, &fan->fan.min_rpm, 0, "Minimum allowed PWM in %"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "maxpwm", CTLFLAG_RD, &fan->fan.max_rpm, 0, "Maximum allowed PWM in %"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "pwm",CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, SMU_PWM_SYSCTL_PWM | i, smu_fanrpm_sysctl, "I", "Fan PWM in %"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "rpm",CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, SMU_PWM_SYSCTL_RPM | i, smu_fanrpm_sysctl, "I", "Fan RPM"); fan->fan.read = NULL; fan->fan.set = (int (*)(struct pmac_fan *, int))smu_fan_set_pwm; } if (bootverbose) device_printf(dev, "Fan: %s type: %d\n", fan->fan.name, fan->type); } } static int smu_sensor_read(struct smu_sensor *sens) { device_t smu = sens->dev; struct smu_cmd cmd; struct smu_softc *sc; int64_t value; int error; cmd.cmd = SMU_ADC; cmd.len = 1; cmd.data[0] = sens->reg; error = 0; error = smu_run_cmd(smu, &cmd, 1); if (error != 0) return (-1); sc = device_get_softc(smu); value = (cmd.data[0] << 8) | cmd.data[1]; switch (sens->type) { case SMU_TEMP_SENSOR: value *= sc->sc_cpu_diode_scale; value >>= 3; value += ((int64_t)sc->sc_cpu_diode_offset) << 9; value <<= 1; /* Convert from 16.16 fixed point degC into integer 0.1 K. */ value = 10*(value >> 16) + ((10*(value & 0xffff)) >> 16) + 2731; break; case SMU_VOLTAGE_SENSOR: value *= sc->sc_cpu_volt_scale; value += sc->sc_cpu_volt_offset; value <<= 4; /* Convert from 16.16 fixed point V into mV. */ value *= 15625; value /= 1024; value /= 1000; break; case SMU_CURRENT_SENSOR: value *= sc->sc_cpu_curr_scale; value += sc->sc_cpu_curr_offset; value <<= 4; /* Convert from 16.16 fixed point A into mA. */ value *= 15625; value /= 1024; value /= 1000; break; case SMU_POWER_SENSOR: value *= sc->sc_slots_pow_scale; value += sc->sc_slots_pow_offset; value <<= 4; /* Convert from 16.16 fixed point W into mW. */ value *= 15625; value /= 1024; value /= 1000; break; } return (value); } static int smu_sensor_sysctl(SYSCTL_HANDLER_ARGS) { device_t smu; struct smu_softc *sc; struct smu_sensor *sens; int value, error; smu = arg1; sc = device_get_softc(smu); sens = &sc->sc_sensors[arg2]; value = smu_sensor_read(sens); if (value < 0) return (EBUSY); error = sysctl_handle_int(oidp, &value, 0, req); return (error); } static void smu_attach_sensors(device_t dev, phandle_t sensroot) { struct smu_sensor *sens; struct smu_softc *sc; struct sysctl_oid *sensroot_oid; struct sysctl_ctx_list *ctx; phandle_t child; char type[32]; int i; sc = device_get_softc(dev); sc->sc_nsensors = 0; for (child = OF_child(sensroot); child != 0; child = OF_peer(child)) sc->sc_nsensors++; if (sc->sc_nsensors == 0) { device_printf(dev, "WARNING: No sensors detected!\n"); return; } sc->sc_sensors = malloc(sc->sc_nsensors * sizeof(struct smu_sensor), M_SMU, M_WAITOK | M_ZERO); sens = sc->sc_sensors; sc->sc_nsensors = 0; ctx = device_get_sysctl_ctx(dev); sensroot_oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensors", CTLFLAG_RD, 0, "SMU Sensor Information"); for (child = OF_child(sensroot); child != 0; child = OF_peer(child)) { char sysctl_name[40], sysctl_desc[40]; const char *units; sens->dev = dev; OF_getprop(child, "device_type", type, sizeof(type)); if (strcmp(type, "current-sensor") == 0) { sens->type = SMU_CURRENT_SENSOR; units = "mA"; } else if (strcmp(type, "temp-sensor") == 0) { sens->type = SMU_TEMP_SENSOR; units = "C"; } else if (strcmp(type, "voltage-sensor") == 0) { sens->type = SMU_VOLTAGE_SENSOR; units = "mV"; } else if (strcmp(type, "power-sensor") == 0) { sens->type = SMU_POWER_SENSOR; units = "mW"; } else { continue; } OF_getprop(child, "reg", &sens->reg, sizeof(cell_t)); OF_getprop(child, "zone", &sens->therm.zone, sizeof(int)); OF_getprop(child, "location", sens->therm.name, sizeof(sens->therm.name)); for (i = 0; i < strlen(sens->therm.name); i++) { sysctl_name[i] = tolower(sens->therm.name[i]); if (isspace(sysctl_name[i])) sysctl_name[i] = '_'; } sysctl_name[i] = 0; sprintf(sysctl_desc,"%s (%s)", sens->therm.name, units); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensroot_oid), OID_AUTO, sysctl_name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, sc->sc_nsensors, smu_sensor_sysctl, (sens->type == SMU_TEMP_SENSOR) ? "IK" : "I", sysctl_desc); if (sens->type == SMU_TEMP_SENSOR) { /* Make up some numbers */ sens->therm.target_temp = 500 + 2731; /* 50 C */ sens->therm.max_temp = 900 + 2731; /* 90 C */ sens->therm.read = (int (*)(struct pmac_therm *))smu_sensor_read; pmac_thermal_sensor_register(&sens->therm); } sens++; sc->sc_nsensors++; } } static void smu_set_sleepled(void *xdev, int onoff) { static struct smu_cmd cmd; device_t smu = xdev; cmd.cmd = SMU_MISC; cmd.len = 3; cmd.data[0] = SMU_MISC_LED_CTRL; cmd.data[1] = 0; cmd.data[2] = onoff; smu_run_cmd(smu, &cmd, 0); } static int smu_server_mode(SYSCTL_HANDLER_ARGS) { struct smu_cmd cmd; u_int server_mode; device_t smu = arg1; int error; cmd.cmd = SMU_POWER_EVENTS; cmd.len = 1; cmd.data[0] = SMU_PWR_GET_POWERUP; error = smu_run_cmd(smu, &cmd, 1); if (error) return (error); server_mode = (cmd.data[1] & SMU_WAKEUP_AC_INSERT) ? 1 : 0; error = sysctl_handle_int(oidp, &server_mode, 0, req); if (error || !req->newptr) return (error); if (server_mode == 1) cmd.data[0] = SMU_PWR_SET_POWERUP; else if (server_mode == 0) cmd.data[0] = SMU_PWR_CLR_POWERUP; else return (EINVAL); cmd.len = 3; cmd.data[1] = 0; cmd.data[2] = SMU_WAKEUP_AC_INSERT; return (smu_run_cmd(smu, &cmd, 1)); } static void smu_shutdown(void *xdev, int howto) { device_t smu = xdev; struct smu_cmd cmd; cmd.cmd = SMU_POWER; if (howto & RB_HALT) strcpy(cmd.data, "SHUTDOWN"); else strcpy(cmd.data, "RESTART"); cmd.len = strlen(cmd.data); smu_run_cmd(smu, &cmd, 1); for (;;); } static int smu_gettime(device_t dev, struct timespec *ts) { struct smu_cmd cmd; struct clocktime ct; cmd.cmd = SMU_RTC; cmd.len = 1; cmd.data[0] = SMU_RTC_GET; if (smu_run_cmd(dev, &cmd, 1) != 0) return (ENXIO); ct.nsec = 0; ct.sec = bcd2bin(cmd.data[0]); ct.min = bcd2bin(cmd.data[1]); ct.hour = bcd2bin(cmd.data[2]); ct.dow = bcd2bin(cmd.data[3]); ct.day = bcd2bin(cmd.data[4]); ct.mon = bcd2bin(cmd.data[5]); ct.year = bcd2bin(cmd.data[6]) + 2000; return (clock_ct_to_ts(&ct, ts)); } static int smu_settime(device_t dev, struct timespec *ts) { static struct smu_cmd cmd; struct clocktime ct; cmd.cmd = SMU_RTC; cmd.len = 8; cmd.data[0] = SMU_RTC_SET; clock_ts_to_ct(ts, &ct); cmd.data[1] = bin2bcd(ct.sec); cmd.data[2] = bin2bcd(ct.min); cmd.data[3] = bin2bcd(ct.hour); cmd.data[4] = bin2bcd(ct.dow); cmd.data[5] = bin2bcd(ct.day); cmd.data[6] = bin2bcd(ct.mon); cmd.data[7] = bin2bcd(ct.year - 2000); return (smu_run_cmd(dev, &cmd, 0)); } /* SMU I2C Interface */ static int smuiic_probe(device_t dev); static int smuiic_attach(device_t dev); static int smuiic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs); static phandle_t smuiic_get_node(device_t bus, device_t dev); static device_method_t smuiic_methods[] = { /* device interface */ DEVMETHOD(device_probe, smuiic_probe), DEVMETHOD(device_attach, smuiic_attach), /* iicbus interface */ DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_transfer, smuiic_transfer), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, smuiic_get_node), { 0, 0 } }; struct smuiic_softc { struct mtx sc_mtx; volatile int sc_iic_inuse; int sc_busno; }; static driver_t smuiic_driver = { "iichb", smuiic_methods, sizeof(struct smuiic_softc) }; static devclass_t smuiic_devclass; DRIVER_MODULE(smuiic, smu, smuiic_driver, smuiic_devclass, 0, 0); static void smu_attach_i2c(device_t smu, phandle_t i2croot) { phandle_t child; device_t cdev; struct ofw_bus_devinfo *dinfo; char name[32]; for (child = OF_child(i2croot); child != 0; child = OF_peer(child)) { if (OF_getprop(child, "name", name, sizeof(name)) <= 0) continue; if (strcmp(name, "i2c-bus") != 0 && strcmp(name, "i2c") != 0) continue; dinfo = malloc(sizeof(struct ofw_bus_devinfo), M_SMU, M_WAITOK | M_ZERO); if (ofw_bus_gen_setup_devinfo(dinfo, child) != 0) { free(dinfo, M_SMU); continue; } cdev = device_add_child(smu, NULL, -1); if (cdev == NULL) { device_printf(smu, "<%s>: device_add_child failed\n", dinfo->obd_name); ofw_bus_gen_destroy_devinfo(dinfo); free(dinfo, M_SMU); continue; } device_set_ivars(cdev, dinfo); } } static int smuiic_probe(device_t dev) { const char *name; name = ofw_bus_get_name(dev); if (name == NULL) return (ENXIO); if (strcmp(name, "i2c-bus") == 0 || strcmp(name, "i2c") == 0) { device_set_desc(dev, "SMU I2C controller"); return (0); } return (ENXIO); } static int smuiic_attach(device_t dev) { struct smuiic_softc *sc = device_get_softc(dev); mtx_init(&sc->sc_mtx, "smuiic", NULL, MTX_DEF); sc->sc_iic_inuse = 0; /* Get our bus number */ OF_getprop(ofw_bus_get_node(dev), "reg", &sc->sc_busno, sizeof(sc->sc_busno)); /* Add the IIC bus layer */ device_add_child(dev, "iicbus", -1); return (bus_generic_attach(dev)); } static int smuiic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { struct smuiic_softc *sc = device_get_softc(dev); struct smu_cmd cmd; int i, j, error; mtx_lock(&sc->sc_mtx); while (sc->sc_iic_inuse) mtx_sleep(sc, &sc->sc_mtx, 0, "smuiic", 100); sc->sc_iic_inuse = 1; error = 0; for (i = 0; i < nmsgs; i++) { cmd.cmd = SMU_I2C; cmd.data[0] = sc->sc_busno; if (msgs[i].flags & IIC_M_NOSTOP) cmd.data[1] = SMU_I2C_COMBINED; else cmd.data[1] = SMU_I2C_SIMPLE; cmd.data[2] = msgs[i].slave; if (msgs[i].flags & IIC_M_RD) cmd.data[2] |= 1; if (msgs[i].flags & IIC_M_NOSTOP) { KASSERT(msgs[i].len < 4, ("oversize I2C combined message")); cmd.data[3] = min(msgs[i].len, 3); memcpy(&cmd.data[4], msgs[i].buf, min(msgs[i].len, 3)); i++; /* Advance to next part of message */ } else { cmd.data[3] = 0; memset(&cmd.data[4], 0, 3); } cmd.data[7] = msgs[i].slave; if (msgs[i].flags & IIC_M_RD) cmd.data[7] |= 1; cmd.data[8] = msgs[i].len; if (msgs[i].flags & IIC_M_RD) { memset(&cmd.data[9], 0xff, msgs[i].len); cmd.len = 9; } else { memcpy(&cmd.data[9], msgs[i].buf, msgs[i].len); cmd.len = 9 + msgs[i].len; } mtx_unlock(&sc->sc_mtx); smu_run_cmd(device_get_parent(dev), &cmd, 1); mtx_lock(&sc->sc_mtx); for (j = 0; j < 10; j++) { cmd.cmd = SMU_I2C; cmd.len = 1; cmd.data[0] = 0; memset(&cmd.data[1], 0xff, msgs[i].len); mtx_unlock(&sc->sc_mtx); smu_run_cmd(device_get_parent(dev), &cmd, 1); mtx_lock(&sc->sc_mtx); if (!(cmd.data[0] & 0x80)) break; mtx_sleep(sc, &sc->sc_mtx, 0, "smuiic", 10); } if (cmd.data[0] & 0x80) { error = EIO; msgs[i].len = 0; goto exit; } memcpy(msgs[i].buf, &cmd.data[1], msgs[i].len); msgs[i].len = cmd.len - 1; } exit: sc->sc_iic_inuse = 0; mtx_unlock(&sc->sc_mtx); wakeup(sc); return (error); } static phandle_t smuiic_get_node(device_t bus, device_t dev) { return (ofw_bus_get_node(bus)); } Index: head/sys/powerpc/powermac/smusat.c =================================================================== --- head/sys/powerpc/powermac/smusat.c (revision 326260) +++ head/sys/powerpc/powermac/smusat.c (revision 326261) @@ -1,293 +1,295 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2010 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct smu_sensor { struct pmac_therm therm; device_t dev; cell_t reg; enum { SMU_CURRENT_SENSOR, SMU_VOLTAGE_SENSOR, SMU_POWER_SENSOR, SMU_TEMP_SENSOR } type; }; static int smusat_probe(device_t); static int smusat_attach(device_t); static int smusat_sensor_sysctl(SYSCTL_HANDLER_ARGS); static int smusat_sensor_read(struct smu_sensor *sens); static MALLOC_DEFINE(M_SMUSAT, "smusat", "SMU Sattelite Sensors"); static device_method_t smusat_methods[] = { /* Device interface */ DEVMETHOD(device_probe, smusat_probe), DEVMETHOD(device_attach, smusat_attach), { 0, 0 }, }; struct smusat_softc { struct smu_sensor *sc_sensors; int sc_nsensors; uint8_t sc_cache[16]; time_t sc_last_update; }; static driver_t smusat_driver = { "smusat", smusat_methods, sizeof(struct smusat_softc) }; static devclass_t smusat_devclass; DRIVER_MODULE(smusat, iicbus, smusat_driver, smusat_devclass, 0, 0); static int smusat_probe(device_t dev) { const char *compat = ofw_bus_get_compat(dev); if (compat == NULL || strcmp(compat, "smu-sat") != 0) return (ENXIO); device_set_desc(dev, "SMU Satellite Sensors"); return (0); } static int smusat_attach(device_t dev) { phandle_t child; struct smu_sensor *sens; struct smusat_softc *sc; struct sysctl_oid *sensroot_oid; struct sysctl_ctx_list *ctx; char type[32]; int i; sc = device_get_softc(dev); sc->sc_nsensors = 0; sc->sc_last_update = 0; for (child = OF_child(ofw_bus_get_node(dev)); child != 0; child = OF_peer(child)) sc->sc_nsensors++; if (sc->sc_nsensors == 0) { device_printf(dev, "WARNING: No sensors detected!\n"); return (-1); } sc->sc_sensors = malloc(sc->sc_nsensors * sizeof(struct smu_sensor), M_SMUSAT, M_WAITOK | M_ZERO); sens = sc->sc_sensors; sc->sc_nsensors = 0; ctx = device_get_sysctl_ctx(dev); sensroot_oid = device_get_sysctl_tree(dev); for (child = OF_child(ofw_bus_get_node(dev)); child != 0; child = OF_peer(child)) { char sysctl_name[40], sysctl_desc[40]; const char *units; sens->dev = dev; sens->reg = 0; OF_getprop(child, "reg", &sens->reg, sizeof(sens->reg)); if (sens->reg < 0x30) continue; sens->reg -= 0x30; OF_getprop(child, "zone", &sens->therm.zone, sizeof(int)); OF_getprop(child, "location", sens->therm.name, sizeof(sens->therm.name)); OF_getprop(child, "device_type", type, sizeof(type)); if (strcmp(type, "current-sensor") == 0) { sens->type = SMU_CURRENT_SENSOR; units = "mA"; } else if (strcmp(type, "temp-sensor") == 0) { sens->type = SMU_TEMP_SENSOR; units = "C"; } else if (strcmp(type, "voltage-sensor") == 0) { sens->type = SMU_VOLTAGE_SENSOR; units = "mV"; } else if (strcmp(type, "power-sensor") == 0) { sens->type = SMU_POWER_SENSOR; units = "mW"; } else { continue; } for (i = 0; i < strlen(sens->therm.name); i++) { sysctl_name[i] = tolower(sens->therm.name[i]); if (isspace(sysctl_name[i])) sysctl_name[i] = '_'; } sysctl_name[i] = 0; sprintf(sysctl_desc,"%s (%s)", sens->therm.name, units); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensroot_oid), OID_AUTO, sysctl_name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, sc->sc_nsensors, smusat_sensor_sysctl, (sens->type == SMU_TEMP_SENSOR) ? "IK" : "I", sysctl_desc); if (sens->type == SMU_TEMP_SENSOR) { /* Make up some numbers */ sens->therm.target_temp = 500 + 2731; /* 50 C */ sens->therm.max_temp = 900 + 2731; /* 90 C */ sens->therm.read = (int (*)(struct pmac_therm *))smusat_sensor_read; pmac_thermal_sensor_register(&sens->therm); } sens++; sc->sc_nsensors++; } return (0); } static int smusat_updatecache(device_t dev) { uint8_t reg = 0x3f; uint8_t value[16]; struct smusat_softc *sc = device_get_softc(dev); int error; struct iic_msg msgs[2] = { {0, IIC_M_WR | IIC_M_NOSTOP, 1, ®}, {0, IIC_M_RD, 16, value}, }; msgs[0].slave = msgs[1].slave = iicbus_get_addr(dev); error = iicbus_transfer(dev, msgs, 2); if (error) return (error); sc->sc_last_update = time_uptime; memcpy(sc->sc_cache, value, sizeof(value)); return (0); } static int smusat_sensor_read(struct smu_sensor *sens) { int value, error; device_t dev; struct smusat_softc *sc; dev = sens->dev; sc = device_get_softc(dev); error = 0; if (time_uptime - sc->sc_last_update > 1) error = smusat_updatecache(dev); if (error) return (-error); value = (sc->sc_cache[sens->reg*2] << 8) + sc->sc_cache[sens->reg*2 + 1]; if (value == 0xffff) { sc->sc_last_update = 0; /* Result was bad, don't cache */ return (-EINVAL); } switch (sens->type) { case SMU_TEMP_SENSOR: /* 16.16 */ value <<= 10; /* From 16.16 to 0.1 C */ value = 10*(value >> 16) + ((10*(value & 0xffff)) >> 16) + 2731; break; case SMU_VOLTAGE_SENSOR: /* 16.16 */ value <<= 4; /* Kill the .16 */ value >>= 16; break; case SMU_CURRENT_SENSOR: /* 16.16 */ value <<= 8; /* Kill the .16 */ value >>= 16; break; case SMU_POWER_SENSOR: /* Doesn't exist */ break; } return (value); } static int smusat_sensor_sysctl(SYSCTL_HANDLER_ARGS) { device_t dev; struct smusat_softc *sc; struct smu_sensor *sens; int value, error; dev = arg1; sc = device_get_softc(dev); sens = &sc->sc_sensors[arg2]; value = smusat_sensor_read(sens); if (value < 0) return (EBUSY); error = sysctl_handle_int(oidp, &value, 0, req); return (error); } Index: head/sys/powerpc/powermac/uninorth.c =================================================================== --- head/sys/powerpc/powermac/uninorth.c (revision 326260) +++ head/sys/powerpc/powermac/uninorth.c (revision 326261) @@ -1,674 +1,676 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2002 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Driver for the Uninorth chip itself. */ static MALLOC_DEFINE(M_UNIN, "unin", "unin device information"); /* * Device interface. */ static int unin_chip_probe(device_t); static int unin_chip_attach(device_t); /* * Bus interface. */ static int unin_chip_print_child(device_t dev, device_t child); static void unin_chip_probe_nomatch(device_t, device_t); static struct resource *unin_chip_alloc_resource(device_t, device_t, int, int *, rman_res_t, rman_res_t, rman_res_t, u_int); static int unin_chip_activate_resource(device_t, device_t, int, int, struct resource *); static int unin_chip_deactivate_resource(device_t, device_t, int, int, struct resource *); static int unin_chip_release_resource(device_t, device_t, int, int, struct resource *); static struct resource_list *unin_chip_get_resource_list (device_t, device_t); /* * OFW Bus interface */ static ofw_bus_get_devinfo_t unin_chip_get_devinfo; /* * Local routines */ static void unin_enable_gmac(device_t dev); static void unin_enable_mpic(device_t dev); /* * Driver methods. */ static device_method_t unin_chip_methods[] = { /* Device interface */ DEVMETHOD(device_probe, unin_chip_probe), DEVMETHOD(device_attach, unin_chip_attach), /* Bus interface */ DEVMETHOD(bus_print_child, unin_chip_print_child), DEVMETHOD(bus_probe_nomatch, unin_chip_probe_nomatch), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, unin_chip_alloc_resource), DEVMETHOD(bus_release_resource, unin_chip_release_resource), DEVMETHOD(bus_activate_resource, unin_chip_activate_resource), DEVMETHOD(bus_deactivate_resource, unin_chip_deactivate_resource), DEVMETHOD(bus_get_resource_list, unin_chip_get_resource_list), DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, unin_chip_get_devinfo), 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), { 0, 0 } }; static driver_t unin_chip_driver = { "unin", unin_chip_methods, sizeof(struct unin_chip_softc) }; static devclass_t unin_chip_devclass; /* * Assume there is only one unin chip in a PowerMac, so that pmu.c functions can * suspend the chip after the whole rest of the device tree is suspended, not * earlier. */ static device_t unin_chip; DRIVER_MODULE(unin, ofwbus, unin_chip_driver, unin_chip_devclass, 0, 0); /* * Add an interrupt to the dev's resource list if present */ static void unin_chip_add_intr(phandle_t devnode, struct unin_chip_devinfo *dinfo) { phandle_t iparent; int *intr; int i, nintr; int icells; if (dinfo->udi_ninterrupts >= 6) { printf("unin: device has more than 6 interrupts\n"); return; } nintr = OF_getprop_alloc(devnode, "interrupts", sizeof(*intr), (void **)&intr); if (nintr == -1) { nintr = OF_getprop_alloc(devnode, "AAPL,interrupts", sizeof(*intr), (void **)&intr); if (nintr == -1) return; } if (intr[0] == -1) return; if (OF_getprop(devnode, "interrupt-parent", &iparent, sizeof(iparent)) <= 0) panic("Interrupt but no interrupt parent!\n"); if (OF_searchprop(iparent, "#interrupt-cells", &icells, sizeof(icells)) <= 0) icells = 1; for (i = 0; i < nintr; i+=icells) { u_int irq = MAP_IRQ(iparent, intr[i]); resource_list_add(&dinfo->udi_resources, SYS_RES_IRQ, dinfo->udi_ninterrupts, irq, irq, 1); if (icells > 1) { powerpc_config_intr(irq, (intr[i+1] & 1) ? INTR_TRIGGER_LEVEL : INTR_TRIGGER_EDGE, INTR_POLARITY_LOW); } dinfo->udi_interrupts[dinfo->udi_ninterrupts] = irq; dinfo->udi_ninterrupts++; } } static void unin_chip_add_reg(phandle_t devnode, struct unin_chip_devinfo *dinfo) { struct unin_chip_reg *reg; int i, nreg; nreg = OF_getprop_alloc(devnode, "reg", sizeof(*reg), (void **)®); if (nreg == -1) return; for (i = 0; i < nreg; i++) { resource_list_add(&dinfo->udi_resources, SYS_RES_MEMORY, i, reg[i].mr_base, reg[i].mr_base + reg[i].mr_size, reg[i].mr_size); } } static void unin_update_reg(device_t dev, uint32_t regoff, uint32_t set, uint32_t clr) { volatile u_int *reg; struct unin_chip_softc *sc; u_int32_t tmpl; sc = device_get_softc(dev); reg = (void *)(sc->sc_addr + regoff); tmpl = inl(reg); tmpl &= ~clr; tmpl |= set; outl(reg, tmpl); } static void unin_enable_gmac(device_t dev) { unin_update_reg(dev, UNIN_CLOCKCNTL, UNIN_CLOCKCNTL_GMAC, 0); } static void unin_enable_mpic(device_t dev) { unin_update_reg(dev, UNIN_TOGGLE_REG, UNIN_MPIC_RESET | UNIN_MPIC_OUTPUT_ENABLE, 0); } static int unin_chip_probe(device_t dev) { const char *name; name = ofw_bus_get_name(dev); if (name == NULL) return (ENXIO); if (strcmp(name, "uni-n") != 0 && strcmp(name, "u3") != 0 && strcmp(name, "u4") != 0) return (ENXIO); device_set_desc(dev, "Apple UniNorth System Controller"); return (0); } static int unin_chip_attach(device_t dev) { struct unin_chip_softc *sc; struct unin_chip_devinfo *dinfo; phandle_t root; phandle_t child; phandle_t iparent; device_t cdev; cell_t acells, scells; char compat[32]; char name[32]; u_int irq, reg[3]; int error, i = 0; sc = device_get_softc(dev); root = ofw_bus_get_node(dev); if (OF_getprop(root, "reg", reg, sizeof(reg)) < 8) return (ENXIO); acells = scells = 1; OF_getprop(OF_parent(root), "#address-cells", &acells, sizeof(acells)); OF_getprop(OF_parent(root), "#size-cells", &scells, sizeof(scells)); i = 0; sc->sc_physaddr = reg[i++]; if (acells == 2) { sc->sc_physaddr <<= 32; sc->sc_physaddr |= reg[i++]; } sc->sc_size = reg[i++]; if (scells == 2) { sc->sc_size <<= 32; sc->sc_size |= reg[i++]; } sc->sc_mem_rman.rm_type = RMAN_ARRAY; sc->sc_mem_rman.rm_descr = "UniNorth Device Memory"; error = rman_init(&sc->sc_mem_rman); if (error) { device_printf(dev, "rman_init() failed. error = %d\n", error); return (error); } error = rman_manage_region(&sc->sc_mem_rman, sc->sc_physaddr, sc->sc_physaddr + sc->sc_size - 1); if (error) { device_printf(dev, "rman_manage_region() failed. error = %d\n", error); return (error); } if (unin_chip == NULL) unin_chip = dev; /* * Iterate through the sub-devices */ for (child = OF_child(root); child != 0; child = OF_peer(child)) { dinfo = malloc(sizeof(*dinfo), M_UNIN, M_WAITOK | M_ZERO); if (ofw_bus_gen_setup_devinfo(&dinfo->udi_obdinfo, child) != 0) { free(dinfo, M_UNIN); continue; } resource_list_init(&dinfo->udi_resources); dinfo->udi_ninterrupts = 0; unin_chip_add_intr(child, dinfo); /* * Some Apple machines do have a bug in OF, they miss * the interrupt entries on the U3 I2C node. That means they * do not have an entry with number of interrupts nor the * entry of the interrupt parent handle. * We define an interrupt and hardwire it to the /u3/mpic * handle. */ if (OF_getprop(child, "name", name, sizeof(name)) <= 0) device_printf(dev, "device has no name!\n"); if (dinfo->udi_ninterrupts == 0 && (strcmp(name, "i2c-bus") == 0 || strcmp(name, "i2c") == 0)) { if (OF_getprop(child, "interrupt-parent", &iparent, sizeof(iparent)) <= 0) { iparent = OF_finddevice("/u3/mpic"); device_printf(dev, "Set /u3/mpic as iparent!\n"); } /* Add an interrupt number 0 to the parent. */ irq = MAP_IRQ(iparent, 0); resource_list_add(&dinfo->udi_resources, SYS_RES_IRQ, dinfo->udi_ninterrupts, irq, irq, 1); dinfo->udi_interrupts[dinfo->udi_ninterrupts] = irq; dinfo->udi_ninterrupts++; } unin_chip_add_reg(child, dinfo); cdev = device_add_child(dev, NULL, -1); if (cdev == NULL) { device_printf(dev, "<%s>: device_add_child failed\n", dinfo->udi_obdinfo.obd_name); resource_list_free(&dinfo->udi_resources); ofw_bus_gen_destroy_devinfo(&dinfo->udi_obdinfo); free(dinfo, M_UNIN); continue; } device_set_ivars(cdev, dinfo); } /* * Only map the first page, since that is where the registers * of interest lie. */ sc->sc_addr = (vm_offset_t)pmap_mapdev(sc->sc_physaddr, PAGE_SIZE); sc->sc_version = *(u_int *)sc->sc_addr; device_printf(dev, "Version %d\n", sc->sc_version); /* * Enable the GMAC Ethernet cell and the integrated OpenPIC * if Open Firmware says they are used. */ for (child = OF_child(root); child; child = OF_peer(child)) { memset(compat, 0, sizeof(compat)); OF_getprop(child, "compatible", compat, sizeof(compat)); if (strcmp(compat, "gmac") == 0) unin_enable_gmac(dev); if (strcmp(compat, "chrp,open-pic") == 0) unin_enable_mpic(dev); } /* * GMAC lives under the PCI bus, so just check if enet is gmac. */ child = OF_finddevice("enet"); memset(compat, 0, sizeof(compat)); OF_getprop(child, "compatible", compat, sizeof(compat)); if (strcmp(compat, "gmac") == 0) unin_enable_gmac(dev); return (bus_generic_attach(dev)); } static int unin_chip_print_child(device_t dev, device_t child) { struct unin_chip_devinfo *dinfo; struct resource_list *rl; int retval = 0; dinfo = device_get_ivars(child); rl = &dinfo->udi_resources; retval += bus_print_child_header(dev, child); retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); retval += bus_print_child_footer(dev, child); return (retval); } static void unin_chip_probe_nomatch(device_t dev, device_t child) { struct unin_chip_devinfo *dinfo; struct resource_list *rl; const char *type; if (bootverbose) { dinfo = device_get_ivars(child); rl = &dinfo->udi_resources; if ((type = ofw_bus_get_type(child)) == NULL) type = "(unknown)"; device_printf(dev, "<%s, %s>", type, ofw_bus_get_name(child)); resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); printf(" (no driver attached)\n"); } } static struct resource * unin_chip_alloc_resource(device_t bus, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct unin_chip_softc *sc; int needactivate; struct resource *rv; struct rman *rm; u_long adjstart, adjend, adjcount; struct unin_chip_devinfo *dinfo; struct resource_list_entry *rle; sc = device_get_softc(bus); dinfo = device_get_ivars(child); needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; switch (type) { case SYS_RES_MEMORY: case SYS_RES_IOPORT: rle = resource_list_find(&dinfo->udi_resources, SYS_RES_MEMORY, *rid); if (rle == NULL) { device_printf(bus, "no rle for %s memory %d\n", device_get_nameunit(child), *rid); return (NULL); } rle->end = rle->end - 1; /* Hack? */ if (start < rle->start) adjstart = rle->start; else if (start > rle->end) adjstart = rle->end; else adjstart = start; if (end < rle->start) adjend = rle->start; else if (end > rle->end) adjend = rle->end; else adjend = end; adjcount = adjend - adjstart; rm = &sc->sc_mem_rman; break; case SYS_RES_IRQ: /* Check for passthrough from subattachments. */ if (device_get_parent(child) != bus) return BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid, start, end, count, flags); rle = resource_list_find(&dinfo->udi_resources, SYS_RES_IRQ, *rid); if (rle == NULL) { if (dinfo->udi_ninterrupts >= 6) { device_printf(bus, "%s has more than 6 interrupts\n", device_get_nameunit(child)); return (NULL); } resource_list_add(&dinfo->udi_resources, SYS_RES_IRQ, dinfo->udi_ninterrupts, start, start, 1); dinfo->udi_interrupts[dinfo->udi_ninterrupts] = start; dinfo->udi_ninterrupts++; } return (resource_list_alloc(&dinfo->udi_resources, bus, child, type, rid, start, end, count, flags)); default: device_printf(bus, "unknown resource request from %s\n", device_get_nameunit(child)); return (NULL); } rv = rman_reserve_resource(rm, adjstart, adjend, adjcount, flags, child); if (rv == NULL) { device_printf(bus, "failed to reserve resource %#lx - %#lx (%#lx)" " for %s\n", adjstart, adjend, adjcount, device_get_nameunit(child)); return (NULL); } rman_set_rid(rv, *rid); if (needactivate) { if (bus_activate_resource(child, type, *rid, rv) != 0) { device_printf(bus, "failed to activate resource for %s\n", device_get_nameunit(child)); rman_release_resource(rv); return (NULL); } } return (rv); } static int unin_chip_release_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { if (rman_get_flags(res) & RF_ACTIVE) { int error = bus_deactivate_resource(child, type, rid, res); if (error) return error; } return (rman_release_resource(res)); } static int unin_chip_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { void *p; if (type == SYS_RES_IRQ) return (bus_activate_resource(bus, type, rid, res)); if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { vm_offset_t start; start = (vm_offset_t) rman_get_start(res); if (bootverbose) printf("unin mapdev: start %zx, len %jd\n", start, rman_get_size(res)); p = pmap_mapdev(start, (vm_size_t) rman_get_size(res)); if (p == NULL) return (ENOMEM); rman_set_virtual(res, p); rman_set_bustag(res, &bs_be_tag); rman_set_bushandle(res, (u_long)p); } return (rman_activate_resource(res)); } static int unin_chip_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { /* * If this is a memory resource, unmap it. */ if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { u_int32_t psize; psize = rman_get_size(res); pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize); } return (rman_deactivate_resource(res)); } static struct resource_list * unin_chip_get_resource_list (device_t dev, device_t child) { struct unin_chip_devinfo *dinfo; dinfo = device_get_ivars(child); return (&dinfo->udi_resources); } static const struct ofw_bus_devinfo * unin_chip_get_devinfo(device_t dev, device_t child) { struct unin_chip_devinfo *dinfo; dinfo = device_get_ivars(child); return (&dinfo->udi_obdinfo); } int unin_chip_wake(device_t dev) { if (dev == NULL) dev = unin_chip; unin_update_reg(dev, UNIN_PWR_MGMT, UNIN_PWR_NORMAL, UNIN_PWR_MASK); DELAY(10); unin_update_reg(dev, UNIN_HWINIT_STATE, UNIN_RUNNING, 0); DELAY(100); return (0); } int unin_chip_sleep(device_t dev, int idle) { if (dev == NULL) dev = unin_chip; unin_update_reg(dev, UNIN_HWINIT_STATE, UNIN_SLEEPING, 0); DELAY(10); if (idle) unin_update_reg(dev, UNIN_PWR_MGMT, UNIN_PWR_IDLE2, UNIN_PWR_MASK); else unin_update_reg(dev, UNIN_PWR_MGMT, UNIN_PWR_SLEEP, UNIN_PWR_MASK); DELAY(10); return (0); } Index: head/sys/powerpc/powermac/uninorthpci.c =================================================================== --- head/sys/powerpc/powermac/uninorthpci.c (revision 326260) +++ head/sys/powerpc/powermac/uninorthpci.c (revision 326261) @@ -1,263 +1,265 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2002 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #define UNINORTH_DEBUG 0 /* * Device interface. */ static int uninorth_probe(device_t); static int uninorth_attach(device_t); /* * pcib interface. */ static u_int32_t uninorth_read_config(device_t, u_int, u_int, u_int, u_int, int); static void uninorth_write_config(device_t, u_int, u_int, u_int, u_int, u_int32_t, int); /* * Local routines. */ static int uninorth_enable_config(struct uninorth_softc *, u_int, u_int, u_int, u_int); /* * Driver methods. */ static device_method_t uninorth_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uninorth_probe), DEVMETHOD(device_attach, uninorth_attach), /* pcib interface */ DEVMETHOD(pcib_read_config, uninorth_read_config), DEVMETHOD(pcib_write_config, uninorth_write_config), DEVMETHOD_END }; static devclass_t uninorth_devclass; DEFINE_CLASS_1(pcib, uninorth_driver, uninorth_methods, sizeof(struct uninorth_softc), ofw_pci_driver); DRIVER_MODULE(uninorth, ofwbus, uninorth_driver, uninorth_devclass, 0, 0); static int uninorth_probe(device_t dev) { const char *type, *compatible; type = ofw_bus_get_type(dev); compatible = ofw_bus_get_compat(dev); if (type == NULL || compatible == NULL) return (ENXIO); if (strcmp(type, "pci") != 0) return (ENXIO); if (strcmp(compatible, "uni-north") == 0) { device_set_desc(dev, "Apple UniNorth Host-PCI bridge"); return (0); } else if (strcmp(compatible, "u3-agp") == 0) { device_set_desc(dev, "Apple U3 Host-AGP bridge"); return (0); } else if (strcmp(compatible, "u4-pcie") == 0) { device_set_desc(dev, "IBM CPC945 PCI Express Root"); return (0); } return (ENXIO); } static int uninorth_attach(device_t dev) { struct uninorth_softc *sc; const char *compatible; phandle_t node; uint32_t reg[3]; uint64_t regbase; cell_t acells; node = ofw_bus_get_node(dev); sc = device_get_softc(dev); if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8) return (ENXIO); sc->sc_ver = 0; compatible = ofw_bus_get_compat(dev); if (strcmp(compatible, "u3-agp") == 0) sc->sc_ver = 3; if (strcmp(compatible, "u4-pcie") == 0) sc->sc_ver = 4; acells = 1; OF_getprop(OF_parent(node), "#address-cells", &acells, sizeof(acells)); regbase = reg[0]; if (acells == 2) { regbase <<= 32; regbase |= reg[1]; } sc->sc_addr = (vm_offset_t)pmap_mapdev(regbase + 0x800000, PAGE_SIZE); sc->sc_data = (vm_offset_t)pmap_mapdev(regbase + 0xc00000, PAGE_SIZE); return (ofw_pci_attach(dev)); } static u_int32_t uninorth_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int width) { struct uninorth_softc *sc; vm_offset_t caoff; sc = device_get_softc(dev); caoff = sc->sc_data + (reg & 0x07); if (uninorth_enable_config(sc, bus, slot, func, reg) != 0) { switch (width) { case 1: return (in8rb(caoff)); break; case 2: return (in16rb(caoff)); break; case 4: return (in32rb(caoff)); break; } } return (0xffffffff); } static void uninorth_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, u_int32_t val, int width) { struct uninorth_softc *sc; vm_offset_t caoff; sc = device_get_softc(dev); caoff = sc->sc_data + (reg & 0x07); if (uninorth_enable_config(sc, bus, slot, func, reg)) { switch (width) { case 1: out8rb(caoff, val); break; case 2: out16rb(caoff, val); break; case 4: out32rb(caoff, val); break; } } } static int uninorth_enable_config(struct uninorth_softc *sc, u_int bus, u_int slot, u_int func, u_int reg) { uint32_t cfgval; uint32_t pass; if (resource_int_value(device_get_name(sc->pci_sc.sc_dev), device_get_unit(sc->pci_sc.sc_dev), "skipslot", &pass) == 0) { if (pass == slot) return (0); } /* * Issue type 0 configuration space accesses for the root bus. * * NOTE: On U4, issue only type 1 accesses. There is a secret * PCI Express <-> PCI Express bridge not present in the device tree, * and we need to route all of our configuration space through it. */ if (sc->pci_sc.sc_bus == bus && sc->sc_ver < 4) { /* * No slots less than 11 on the primary bus on U3 and lower */ if (slot < 11) return (0); cfgval = (1 << slot) | (func << 8) | (reg & 0xfc); } else { cfgval = (bus << 16) | (slot << 11) | (func << 8) | (reg & 0xfc) | 1; } /* Set extended register bits on U4 */ if (sc->sc_ver == 4) cfgval |= (reg >> 8) << 28; do { out32rb(sc->sc_addr, cfgval); } while (in32rb(sc->sc_addr) != cfgval); return (1); } Index: head/sys/powerpc/powermac/uninorthvar.h =================================================================== --- head/sys/powerpc/powermac/uninorthvar.h (revision 326260) +++ head/sys/powerpc/powermac/uninorthvar.h (revision 326261) @@ -1,107 +1,109 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2002 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _POWERPC_POWERMAC_UNINORTHVAR_H_ #define _POWERPC_POWERMAC_UNINORTHVAR_H_ #include #include #include struct uninorth_softc { struct ofw_pci_softc pci_sc; vm_offset_t sc_addr; vm_offset_t sc_data; int sc_ver; }; struct unin_chip_softc { uint64_t sc_physaddr; uint64_t sc_size; vm_offset_t sc_addr; struct rman sc_mem_rman; int sc_version; }; /* * Format of a unin reg property entry. */ struct unin_chip_reg { u_int32_t mr_base; u_int32_t mr_size; }; /* * Per unin device structure. */ struct unin_chip_devinfo { int udi_interrupts[6]; int udi_ninterrupts; int udi_base; struct ofw_bus_devinfo udi_obdinfo; struct resource_list udi_resources; }; /* * Version register */ #define UNIN_VERS 0x0 /* * Clock-control register */ #define UNIN_CLOCKCNTL 0x20 #define UNIN_CLOCKCNTL_GMAC 0x2 /* * Power management register */ #define UNIN_PWR_MGMT 0x30 #define UNIN_PWR_NORMAL 0x00 #define UNIN_PWR_IDLE2 0x01 #define UNIN_PWR_SLEEP 0x02 #define UNIN_PWR_SAVE 0x03 #define UNIN_PWR_MASK 0x03 /* * Hardware initialization state register */ #define UNIN_HWINIT_STATE 0x70 #define UNIN_SLEEPING 0x01 #define UNIN_RUNNING 0x02 /* * Toggle registers */ #define UNIN_TOGGLE_REG 0xe0 #define UNIN_MPIC_RESET 0x2 #define UNIN_MPIC_OUTPUT_ENABLE 0x4 extern int unin_chip_sleep(device_t dev, int idle); extern int unin_chip_wake(device_t dev); #endif /* _POWERPC_POWERMAC_UNINORTHVAR_H_ */ Index: head/sys/powerpc/powermac/vcoregpio.c =================================================================== --- head/sys/powerpc/powermac/vcoregpio.c (revision 326260) +++ head/sys/powerpc/powermac/vcoregpio.c (revision 326261) @@ -1,112 +1,114 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2009 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include static int vcoregpio_probe(device_t); static int vcoregpio_attach(device_t); static void vcoregpio_pre_change(device_t, const struct cf_level *level); static void vcoregpio_post_change(device_t, const struct cf_level *level); static device_method_t vcoregpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, vcoregpio_probe), DEVMETHOD(device_attach, vcoregpio_attach), { 0, 0 }, }; static driver_t vcoregpio_driver = { "vcoregpio", vcoregpio_methods, 0 }; static devclass_t vcoregpio_devclass; DRIVER_MODULE(vcoregpio, macgpio, vcoregpio_driver, vcoregpio_devclass, 0, 0); static int vcoregpio_probe(device_t dev) { const char *name = ofw_bus_get_name(dev); if (strcmp(name, "cpu-vcore-select") != 0) return (ENXIO); device_set_desc(dev, "CPU Core Voltage Control"); return (0); } static int vcoregpio_attach(device_t dev) { EVENTHANDLER_REGISTER(cpufreq_pre_change, vcoregpio_pre_change, dev, EVENTHANDLER_PRI_ANY); EVENTHANDLER_REGISTER(cpufreq_post_change, vcoregpio_post_change, dev, EVENTHANDLER_PRI_ANY); return (0); } static void vcoregpio_pre_change(device_t dev, const struct cf_level *level) { if (level->rel_set[0].freq == 10000 /* max */) { /* * Make sure the CPU voltage is raised before we raise * the clock. */ macgpio_write(dev, GPIO_DDR_OUTPUT | 1); DELAY(1000); } } static void vcoregpio_post_change(device_t dev, const struct cf_level *level) { if (level->rel_set[0].freq < 10000 /* max */) { DELAY(1000); /* We are safe to reduce CPU voltage now */ macgpio_write(dev, GPIO_DDR_OUTPUT | 0); } } Index: head/sys/powerpc/powermac/viareg.h =================================================================== --- head/sys/powerpc/powermac/viareg.h (revision 326260) +++ head/sys/powerpc/powermac/viareg.h (revision 326261) @@ -1,51 +1,53 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2008 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _POWERPC_VIAREG_H_ #define _POWERPC_VIAREG_H_ /* VIA interface registers */ #define vBufB 0x0000 /* register B */ #define vDirB 0x0400 /* data direction register */ #define vDirA 0x0600 /* data direction register */ #define vT1C 0x0800 /* Timer 1 counter Lo */ #define vT1CH 0x0a00 /* Timer 1 counter Hi */ #define vSR 0x1400 /* shift register */ #define vACR 0x1600 /* aux control register */ #define vPCR 0x1800 /* peripheral control register */ #define vIFR 0x1a00 /* interrupt flag register */ #define vIER 0x1c00 /* interrupt enable register */ #define vBufA 0x1e00 /* register A */ #define vPB 0x0000 #define vPB3 0x08 #define vPB4 0x10 #define vPB5 0x20 #define vSR_INT 0x04 #define vSR_OUT 0x10 #endif /* _POWERPC_VIAREG_H_ */ Index: head/sys/powerpc/powerpc/altivec.c =================================================================== --- head/sys/powerpc/powerpc/altivec.c (revision 326260) +++ head/sys/powerpc/powerpc/altivec.c (revision 326261) @@ -1,179 +1,181 @@ /*- + * SPDX-License-Identifier: BSD-4-Clause + * * Copyright (C) 1996 Wolfgang Solfrank. * Copyright (C) 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: fpu.c,v 1.5 2001/07/22 11:29:46 wiz Exp $ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include static void save_vec_int(struct thread *td) { int msr; struct pcb *pcb; pcb = td->td_pcb; /* * Temporarily re-enable the vector unit during the save */ msr = mfmsr(); mtmsr(msr | PSL_VEC); isync(); /* * Save the vector registers and VSCR to the PCB */ #define STVX(n) __asm ("stvx %1,0,%0" \ :: "b"(pcb->pcb_vec.vr[n]), "n"(n)); STVX(0); STVX(1); STVX(2); STVX(3); STVX(4); STVX(5); STVX(6); STVX(7); STVX(8); STVX(9); STVX(10); STVX(11); STVX(12); STVX(13); STVX(14); STVX(15); STVX(16); STVX(17); STVX(18); STVX(19); STVX(20); STVX(21); STVX(22); STVX(23); STVX(24); STVX(25); STVX(26); STVX(27); STVX(28); STVX(29); STVX(30); STVX(31); #undef STVX __asm __volatile("mfvscr 0; stvewx 0,0,%0" :: "b"(&pcb->pcb_vec.vscr)); /* * Disable vector unit again */ isync(); mtmsr(msr); } void enable_vec(struct thread *td) { int msr; struct pcb *pcb; struct trapframe *tf; pcb = td->td_pcb; tf = trapframe(td); /* * Save the thread's Altivec CPU number, and set the CPU's current * vector thread */ td->td_pcb->pcb_veccpu = PCPU_GET(cpuid); PCPU_SET(vecthread, td); /* * Enable the vector unit for when the thread returns from the * exception. If this is the first time the unit has been used by * the thread, initialise the vector registers and VSCR to 0, and * set the flag to indicate that the vector unit is in use. */ tf->srr1 |= PSL_VEC; if (!(pcb->pcb_flags & PCB_VEC)) { memset(&pcb->pcb_vec, 0, sizeof pcb->pcb_vec); pcb->pcb_flags |= PCB_VEC; } /* * Temporarily enable the vector unit so the registers * can be restored. */ msr = mfmsr(); mtmsr(msr | PSL_VEC); isync(); /* * Restore VSCR by first loading it into a vector and then into VSCR. * (this needs to done before loading the user's vector registers * since we need to use a scratch vector register) */ __asm __volatile("vxor 0,0,0; lvewx 0,0,%0; mtvscr 0" \ :: "b"(&pcb->pcb_vec.vscr)); #define LVX(n) __asm ("lvx " #n ",0,%0" \ :: "b"(&pcb->pcb_vec.vr[n])); LVX(0); LVX(1); LVX(2); LVX(3); LVX(4); LVX(5); LVX(6); LVX(7); LVX(8); LVX(9); LVX(10); LVX(11); LVX(12); LVX(13); LVX(14); LVX(15); LVX(16); LVX(17); LVX(18); LVX(19); LVX(20); LVX(21); LVX(22); LVX(23); LVX(24); LVX(25); LVX(26); LVX(27); LVX(28); LVX(29); LVX(30); LVX(31); #undef LVX isync(); mtmsr(msr); } void save_vec(struct thread *td) { struct pcb *pcb; pcb = td->td_pcb; save_vec_int(td); /* * Clear the current vec thread and pcb's CPU id * XXX should this be left clear to allow lazy save/restore ? */ pcb->pcb_veccpu = INT_MAX; PCPU_SET(vecthread, NULL); } /* * Save altivec state without dropping ownership. This will only save state if * the current vector-thread is `td'. */ void save_vec_nodrop(struct thread *td) { struct thread *vtd; vtd = PCPU_GET(vecthread); if (td != vtd) { return; } save_vec_int(td); } Index: head/sys/powerpc/powerpc/autoconf.c =================================================================== --- head/sys/powerpc/powerpc/autoconf.c (revision 326260) +++ head/sys/powerpc/powerpc/autoconf.c (revision 326261) @@ -1,94 +1,96 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 1998 Doug Rabson * 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_isa.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #ifdef DEV_ISA extern void isa_probe_children(device_t dev); device_t isa_bus_device; #endif static device_t nexusdev; static void configure_first(void *); static void configure(void *); static void configure_final(void *); SYSINIT(configure1, SI_SUB_CONFIGURE, SI_ORDER_FIRST, configure_first, NULL); /* SI_ORDER_SECOND is hookable */ SYSINIT(configure2, SI_SUB_CONFIGURE, SI_ORDER_THIRD, configure, NULL); /* SI_ORDER_MIDDLE is hookable */ SYSINIT(configure3, SI_SUB_CONFIGURE, SI_ORDER_ANY, configure_final, NULL); /* * Determine i/o configuration for a machine. */ static void configure_first(void *dummy) { nexusdev = device_add_child(root_bus, "nexus", 0); } static void configure(void *dummy) { root_bus_configure(); #ifdef DEV_ISA if (isa_bus_device) isa_probe_children(isa_bus_device); #endif } static void configure_final(void *dummy) { /* * Now that we're guaranteed to have a PIC driver (or we'll never * have one), program it with all the previously setup interrupts. */ powerpc_enable_intr(); /* Enable external interrupts. */ mtmsr(mfmsr() | PSL_EE); cninit_finish(); cold = 0; } Index: head/sys/powerpc/powerpc/bus_machdep.c =================================================================== --- head/sys/powerpc/powerpc/bus_machdep.c (revision 326260) +++ head/sys/powerpc/powerpc/bus_machdep.c (revision 326261) @@ -1,961 +1,963 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2006 Semihalf, Rafal Jaworowski * Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #define KTR_BE_IO 0 #define KTR_LE_IO 0 #include #include #include #include #include #include #include #include #include #define TODO panic("%s: not implemented", __func__) #define MAX_EARLYBOOT_MAPPINGS 6 static struct { vm_offset_t virt; bus_addr_t addr; bus_size_t size; int flags; } earlyboot_mappings[MAX_EARLYBOOT_MAPPINGS]; static int earlyboot_map_idx = 0; void bs_remap_earlyboot(void); static __inline void * __ppc_ba(bus_space_handle_t bsh, bus_size_t ofs) { return ((void *)(bsh + ofs)); } static int bs_gen_map(bus_addr_t addr, bus_size_t size, int flags, bus_space_handle_t *bshp) { vm_memattr_t ma; /* * Record what we did if we haven't enabled the MMU yet. We * will need to remap it as soon as the MMU comes up. */ if (!pmap_bootstrapped) { KASSERT(earlyboot_map_idx < MAX_EARLYBOOT_MAPPINGS, ("%s: too many early boot mapping requests", __func__)); earlyboot_mappings[earlyboot_map_idx].addr = addr; earlyboot_mappings[earlyboot_map_idx].virt = pmap_early_io_map(addr, size); earlyboot_mappings[earlyboot_map_idx].size = size; earlyboot_mappings[earlyboot_map_idx].flags = flags; *bshp = earlyboot_mappings[earlyboot_map_idx].virt; earlyboot_map_idx++; } else { ma = VM_MEMATTR_DEFAULT; switch (flags) { case BUS_SPACE_MAP_CACHEABLE: ma = VM_MEMATTR_CACHEABLE; break; case BUS_SPACE_MAP_PREFETCHABLE: ma = VM_MEMATTR_PREFETCHABLE; break; } *bshp = (bus_space_handle_t)pmap_mapdev_attr(addr, size, ma); } return (0); } void bs_remap_earlyboot(void) { vm_paddr_t pa, spa; vm_offset_t va; int i; vm_memattr_t ma; for (i = 0; i < earlyboot_map_idx; i++) { spa = earlyboot_mappings[i].addr; if (spa == earlyboot_mappings[i].virt && pmap_dev_direct_mapped(spa, earlyboot_mappings[i].size) == 0) continue; ma = VM_MEMATTR_DEFAULT; switch (earlyboot_mappings[i].flags) { case BUS_SPACE_MAP_CACHEABLE: ma = VM_MEMATTR_CACHEABLE; break; case BUS_SPACE_MAP_PREFETCHABLE: ma = VM_MEMATTR_PREFETCHABLE; break; } pa = trunc_page(spa); va = trunc_page(earlyboot_mappings[i].virt); while (pa < spa + earlyboot_mappings[i].size) { pmap_kenter_attr(va, pa, ma); va += PAGE_SIZE; pa += PAGE_SIZE; } } } static void bs_gen_unmap(bus_size_t size __unused) { } static int bs_gen_subregion(bus_space_handle_t bsh, bus_size_t ofs, bus_size_t size __unused, bus_space_handle_t *nbshp) { *nbshp = bsh + ofs; return (0); } static int bs_gen_alloc(bus_addr_t rstart __unused, bus_addr_t rend __unused, bus_size_t size __unused, bus_size_t alignment __unused, bus_size_t boundary __unused, int flags __unused, bus_addr_t *bpap __unused, bus_space_handle_t *bshp __unused) { TODO; } static void bs_gen_free(bus_space_handle_t bsh __unused, bus_size_t size __unused) { TODO; } static void bs_gen_barrier(bus_space_handle_t bsh __unused, bus_size_t ofs __unused, bus_size_t size __unused, int flags __unused) { powerpc_iomb(); } /* * Big-endian access functions */ static uint8_t bs_be_rs_1(bus_space_handle_t bsh, bus_size_t ofs) { volatile uint8_t *addr; uint8_t res; addr = __ppc_ba(bsh, ofs); res = *addr; powerpc_iomb(); CTR4(KTR_BE_IO, "%s(bsh=%#x, ofs=%#x) = %#x", __func__, bsh, ofs, res); return (res); } static uint16_t bs_be_rs_2(bus_space_handle_t bsh, bus_size_t ofs) { volatile uint16_t *addr; uint16_t res; addr = __ppc_ba(bsh, ofs); res = *addr; powerpc_iomb(); CTR4(KTR_BE_IO, "%s(bsh=%#x, ofs=%#x) = %#x", __func__, bsh, ofs, res); return (res); } static uint32_t bs_be_rs_4(bus_space_handle_t bsh, bus_size_t ofs) { volatile uint32_t *addr; uint32_t res; addr = __ppc_ba(bsh, ofs); res = *addr; powerpc_iomb(); CTR4(KTR_BE_IO, "%s(bsh=%#x, ofs=%#x) = %#x", __func__, bsh, ofs, res); return (res); } static uint64_t bs_be_rs_8(bus_space_handle_t bsh, bus_size_t ofs) { volatile uint64_t *addr; uint64_t res; addr = __ppc_ba(bsh, ofs); res = *addr; powerpc_iomb(); return (res); } static void bs_be_rm_1(bus_space_handle_t bsh, bus_size_t ofs, uint8_t *addr, size_t cnt) { ins8(__ppc_ba(bsh, ofs), addr, cnt); } static void bs_be_rm_2(bus_space_handle_t bsh, bus_size_t ofs, uint16_t *addr, size_t cnt) { ins16(__ppc_ba(bsh, ofs), addr, cnt); } static void bs_be_rm_4(bus_space_handle_t bsh, bus_size_t ofs, uint32_t *addr, size_t cnt) { ins32(__ppc_ba(bsh, ofs), addr, cnt); } static void bs_be_rm_8(bus_space_handle_t bsh, bus_size_t ofs, uint64_t *addr, size_t cnt) { ins64(__ppc_ba(bsh, ofs), addr, cnt); } static void bs_be_rr_1(bus_space_handle_t bsh, bus_size_t ofs, uint8_t *addr, size_t cnt) { volatile uint8_t *s = __ppc_ba(bsh, ofs); while (cnt--) *addr++ = *s++; powerpc_iomb(); } static void bs_be_rr_2(bus_space_handle_t bsh, bus_size_t ofs, uint16_t *addr, size_t cnt) { volatile uint16_t *s = __ppc_ba(bsh, ofs); while (cnt--) *addr++ = *s++; powerpc_iomb(); } static void bs_be_rr_4(bus_space_handle_t bsh, bus_size_t ofs, uint32_t *addr, size_t cnt) { volatile uint32_t *s = __ppc_ba(bsh, ofs); while (cnt--) *addr++ = *s++; powerpc_iomb(); } static void bs_be_rr_8(bus_space_handle_t bsh, bus_size_t ofs, uint64_t *addr, size_t cnt) { volatile uint64_t *s = __ppc_ba(bsh, ofs); while (cnt--) *addr++ = *s++; powerpc_iomb(); } static void bs_be_ws_1(bus_space_handle_t bsh, bus_size_t ofs, uint8_t val) { volatile uint8_t *addr; addr = __ppc_ba(bsh, ofs); *addr = val; powerpc_iomb(); CTR4(KTR_BE_IO, "%s(bsh=%#x, ofs=%#x, val=%#x)", __func__, bsh, ofs, val); } static void bs_be_ws_2(bus_space_handle_t bsh, bus_size_t ofs, uint16_t val) { volatile uint16_t *addr; addr = __ppc_ba(bsh, ofs); *addr = val; powerpc_iomb(); CTR4(KTR_BE_IO, "%s(bsh=%#x, ofs=%#x, val=%#x)", __func__, bsh, ofs, val); } static void bs_be_ws_4(bus_space_handle_t bsh, bus_size_t ofs, uint32_t val) { volatile uint32_t *addr; addr = __ppc_ba(bsh, ofs); *addr = val; powerpc_iomb(); CTR4(KTR_BE_IO, "%s(bsh=%#x, ofs=%#x, val=%#x)", __func__, bsh, ofs, val); } static void bs_be_ws_8(bus_space_handle_t bsh, bus_size_t ofs, uint64_t val) { volatile uint64_t *addr; addr = __ppc_ba(bsh, ofs); *addr = val; powerpc_iomb(); } static void bs_be_wm_1(bus_space_handle_t bsh, bus_size_t ofs, const uint8_t *addr, bus_size_t cnt) { outsb(__ppc_ba(bsh, ofs), addr, cnt); } static void bs_be_wm_2(bus_space_handle_t bsh, bus_size_t ofs, const uint16_t *addr, bus_size_t cnt) { outsw(__ppc_ba(bsh, ofs), addr, cnt); } static void bs_be_wm_4(bus_space_handle_t bsh, bus_size_t ofs, const uint32_t *addr, bus_size_t cnt) { outsl(__ppc_ba(bsh, ofs), addr, cnt); } static void bs_be_wm_8(bus_space_handle_t bsh, bus_size_t ofs, const uint64_t *addr, bus_size_t cnt) { outsll(__ppc_ba(bsh, ofs), addr, cnt); } static void bs_be_wr_1(bus_space_handle_t bsh, bus_size_t ofs, const uint8_t *addr, size_t cnt) { volatile uint8_t *d = __ppc_ba(bsh, ofs); while (cnt--) *d++ = *addr++; powerpc_iomb(); } static void bs_be_wr_2(bus_space_handle_t bsh, bus_size_t ofs, const uint16_t *addr, size_t cnt) { volatile uint16_t *d = __ppc_ba(bsh, ofs); while (cnt--) *d++ = *addr++; powerpc_iomb(); } static void bs_be_wr_4(bus_space_handle_t bsh, bus_size_t ofs, const uint32_t *addr, size_t cnt) { volatile uint32_t *d = __ppc_ba(bsh, ofs); while (cnt--) *d++ = *addr++; powerpc_iomb(); } static void bs_be_wr_8(bus_space_handle_t bsh, bus_size_t ofs, const uint64_t *addr, size_t cnt) { volatile uint64_t *d = __ppc_ba(bsh, ofs); while (cnt--) *d++ = *addr++; powerpc_iomb(); } static void bs_be_sm_1(bus_space_handle_t bsh, bus_size_t ofs, uint8_t val, size_t cnt) { volatile uint8_t *d = __ppc_ba(bsh, ofs); while (cnt--) *d = val; powerpc_iomb(); } static void bs_be_sm_2(bus_space_handle_t bsh, bus_size_t ofs, uint16_t val, size_t cnt) { volatile uint16_t *d = __ppc_ba(bsh, ofs); while (cnt--) *d = val; powerpc_iomb(); } static void bs_be_sm_4(bus_space_handle_t bsh, bus_size_t ofs, uint32_t val, size_t cnt) { volatile uint32_t *d = __ppc_ba(bsh, ofs); while (cnt--) *d = val; powerpc_iomb(); } static void bs_be_sm_8(bus_space_handle_t bsh, bus_size_t ofs, uint64_t val, size_t cnt) { volatile uint64_t *d = __ppc_ba(bsh, ofs); while (cnt--) *d = val; powerpc_iomb(); } static void bs_be_sr_1(bus_space_handle_t bsh, bus_size_t ofs, uint8_t val, size_t cnt) { volatile uint8_t *d = __ppc_ba(bsh, ofs); while (cnt--) *d++ = val; powerpc_iomb(); } static void bs_be_sr_2(bus_space_handle_t bsh, bus_size_t ofs, uint16_t val, size_t cnt) { volatile uint16_t *d = __ppc_ba(bsh, ofs); while (cnt--) *d++ = val; powerpc_iomb(); } static void bs_be_sr_4(bus_space_handle_t bsh, bus_size_t ofs, uint32_t val, size_t cnt) { volatile uint32_t *d = __ppc_ba(bsh, ofs); while (cnt--) *d++ = val; powerpc_iomb(); } static void bs_be_sr_8(bus_space_handle_t bsh, bus_size_t ofs, uint64_t val, size_t cnt) { volatile uint64_t *d = __ppc_ba(bsh, ofs); while (cnt--) *d++ = val; powerpc_iomb(); } /* * Little-endian access functions */ static uint8_t bs_le_rs_1(bus_space_handle_t bsh, bus_size_t ofs) { volatile uint8_t *addr; uint8_t res; addr = __ppc_ba(bsh, ofs); res = *addr; powerpc_iomb(); CTR4(KTR_LE_IO, "%s(bsh=%#x, ofs=%#x) = %#x", __func__, bsh, ofs, res); return (res); } static uint16_t bs_le_rs_2(bus_space_handle_t bsh, bus_size_t ofs) { volatile uint16_t *addr; uint16_t res; addr = __ppc_ba(bsh, ofs); __asm __volatile("lhbrx %0, 0, %1" : "=r"(res) : "r"(addr)); powerpc_iomb(); CTR4(KTR_LE_IO, "%s(bsh=%#x, ofs=%#x) = %#x", __func__, bsh, ofs, res); return (res); } static uint32_t bs_le_rs_4(bus_space_handle_t bsh, bus_size_t ofs) { volatile uint32_t *addr; uint32_t res; addr = __ppc_ba(bsh, ofs); __asm __volatile("lwbrx %0, 0, %1" : "=r"(res) : "r"(addr)); powerpc_iomb(); CTR4(KTR_LE_IO, "%s(bsh=%#x, ofs=%#x) = %#x", __func__, bsh, ofs, res); return (res); } static uint64_t bs_le_rs_8(bus_space_handle_t bsh, bus_size_t ofs) { TODO; } static void bs_le_rm_1(bus_space_handle_t bsh, bus_size_t ofs, uint8_t *addr, size_t cnt) { ins8(__ppc_ba(bsh, ofs), addr, cnt); } static void bs_le_rm_2(bus_space_handle_t bsh, bus_size_t ofs, uint16_t *addr, size_t cnt) { ins16rb(__ppc_ba(bsh, ofs), addr, cnt); } static void bs_le_rm_4(bus_space_handle_t bsh, bus_size_t ofs, uint32_t *addr, size_t cnt) { ins32rb(__ppc_ba(bsh, ofs), addr, cnt); } static void bs_le_rm_8(bus_space_handle_t bshh, bus_size_t ofs, uint64_t *addr, size_t cnt) { TODO; } static void bs_le_rr_1(bus_space_handle_t bsh, bus_size_t ofs, uint8_t *addr, size_t cnt) { volatile uint8_t *s = __ppc_ba(bsh, ofs); while (cnt--) *addr++ = *s++; powerpc_iomb(); } static void bs_le_rr_2(bus_space_handle_t bsh, bus_size_t ofs, uint16_t *addr, size_t cnt) { volatile uint16_t *s = __ppc_ba(bsh, ofs); while (cnt--) *addr++ = in16rb(s++); powerpc_iomb(); } static void bs_le_rr_4(bus_space_handle_t bsh, bus_size_t ofs, uint32_t *addr, size_t cnt) { volatile uint32_t *s = __ppc_ba(bsh, ofs); while (cnt--) *addr++ = in32rb(s++); powerpc_iomb(); } static void bs_le_rr_8(bus_space_handle_t bsh, bus_size_t ofs, uint64_t *addr, size_t cnt) { TODO; } static void bs_le_ws_1(bus_space_handle_t bsh, bus_size_t ofs, uint8_t val) { volatile uint8_t *addr; addr = __ppc_ba(bsh, ofs); *addr = val; powerpc_iomb(); CTR4(KTR_LE_IO, "%s(bsh=%#x, ofs=%#x, val=%#x)", __func__, bsh, ofs, val); } static void bs_le_ws_2(bus_space_handle_t bsh, bus_size_t ofs, uint16_t val) { volatile uint16_t *addr; addr = __ppc_ba(bsh, ofs); __asm __volatile("sthbrx %0, 0, %1" :: "r"(val), "r"(addr)); powerpc_iomb(); CTR4(KTR_LE_IO, "%s(bsh=%#x, ofs=%#x, val=%#x)", __func__, bsh, ofs, val); } static void bs_le_ws_4(bus_space_handle_t bsh, bus_size_t ofs, uint32_t val) { volatile uint32_t *addr; addr = __ppc_ba(bsh, ofs); __asm __volatile("stwbrx %0, 0, %1" :: "r"(val), "r"(addr)); powerpc_iomb(); CTR4(KTR_LE_IO, "%s(bsh=%#x, ofs=%#x, val=%#x)", __func__, bsh, ofs, val); } static void bs_le_ws_8(bus_space_handle_t bsh, bus_size_t ofs, uint64_t val) { TODO; } static void bs_le_wm_1(bus_space_handle_t bsh, bus_size_t ofs, const uint8_t *addr, bus_size_t cnt) { outs8(__ppc_ba(bsh, ofs), addr, cnt); } static void bs_le_wm_2(bus_space_handle_t bsh, bus_size_t ofs, const uint16_t *addr, bus_size_t cnt) { outs16rb(__ppc_ba(bsh, ofs), addr, cnt); } static void bs_le_wm_4(bus_space_handle_t bsh, bus_size_t ofs, const uint32_t *addr, bus_size_t cnt) { outs32rb(__ppc_ba(bsh, ofs), addr, cnt); } static void bs_le_wm_8(bus_space_handle_t bsh, bus_size_t ofs, const uint64_t *addr, bus_size_t cnt) { TODO; } static void bs_le_wr_1(bus_space_handle_t bsh, bus_size_t ofs, const uint8_t *addr, size_t cnt) { volatile uint8_t *d = __ppc_ba(bsh, ofs); while (cnt--) *d++ = *addr++; powerpc_iomb(); } static void bs_le_wr_2(bus_space_handle_t bsh, bus_size_t ofs, const uint16_t *addr, size_t cnt) { volatile uint16_t *d = __ppc_ba(bsh, ofs); while (cnt--) out16rb(d++, *addr++); powerpc_iomb(); } static void bs_le_wr_4(bus_space_handle_t bsh, bus_size_t ofs, const uint32_t *addr, size_t cnt) { volatile uint32_t *d = __ppc_ba(bsh, ofs); while (cnt--) out32rb(d++, *addr++); powerpc_iomb(); } static void bs_le_wr_8(bus_space_handle_t bsh, bus_size_t ofs, const uint64_t *addr, size_t cnt) { TODO; } static void bs_le_sm_1(bus_space_handle_t bsh, bus_size_t ofs, uint8_t val, size_t cnt) { volatile uint8_t *d = __ppc_ba(bsh, ofs); while (cnt--) *d = val; powerpc_iomb(); } static void bs_le_sm_2(bus_space_handle_t bsh, bus_size_t ofs, uint16_t val, size_t cnt) { volatile uint16_t *d = __ppc_ba(bsh, ofs); while (cnt--) out16rb(d, val); powerpc_iomb(); } static void bs_le_sm_4(bus_space_handle_t bsh, bus_size_t ofs, uint32_t val, size_t cnt) { volatile uint32_t *d = __ppc_ba(bsh, ofs); while (cnt--) out32rb(d, val); powerpc_iomb(); } static void bs_le_sm_8(bus_space_handle_t bsh, bus_size_t ofs, uint64_t val, size_t cnt) { TODO; } static void bs_le_sr_1(bus_space_handle_t bsh, bus_size_t ofs, uint8_t val, size_t cnt) { volatile uint8_t *d = __ppc_ba(bsh, ofs); while (cnt--) *d++ = val; powerpc_iomb(); } static void bs_le_sr_2(bus_space_handle_t bsh, bus_size_t ofs, uint16_t val, size_t cnt) { volatile uint16_t *d = __ppc_ba(bsh, ofs); while (cnt--) out16rb(d++, val); powerpc_iomb(); } static void bs_le_sr_4(bus_space_handle_t bsh, bus_size_t ofs, uint32_t val, size_t cnt) { volatile uint32_t *d = __ppc_ba(bsh, ofs); while (cnt--) out32rb(d++, val); powerpc_iomb(); } static void bs_le_sr_8(bus_space_handle_t bsh, bus_size_t ofs, uint64_t val, size_t cnt) { TODO; } struct bus_space bs_be_tag = { /* mapping/unmapping */ bs_gen_map, bs_gen_unmap, bs_gen_subregion, /* allocation/deallocation */ bs_gen_alloc, bs_gen_free, /* barrier */ bs_gen_barrier, /* read (single) */ bs_be_rs_1, bs_be_rs_2, bs_be_rs_4, bs_be_rs_8, bs_be_rs_2, bs_be_rs_4, bs_be_rs_8, /* read multiple */ bs_be_rm_1, bs_be_rm_2, bs_be_rm_4, bs_be_rm_8, bs_be_rm_2, bs_be_rm_4, bs_be_rm_8, /* read region */ bs_be_rr_1, bs_be_rr_2, bs_be_rr_4, bs_be_rr_8, bs_be_rr_2, bs_be_rr_4, bs_be_rr_8, /* write (single) */ bs_be_ws_1, bs_be_ws_2, bs_be_ws_4, bs_be_ws_8, bs_be_ws_2, bs_be_ws_4, bs_be_ws_8, /* write multiple */ bs_be_wm_1, bs_be_wm_2, bs_be_wm_4, bs_be_wm_8, bs_be_wm_2, bs_be_wm_4, bs_be_wm_8, /* write region */ bs_be_wr_1, bs_be_wr_2, bs_be_wr_4, bs_be_wr_8, bs_be_wr_2, bs_be_wr_4, bs_be_wr_8, /* set multiple */ bs_be_sm_1, bs_be_sm_2, bs_be_sm_4, bs_be_sm_8, bs_be_sm_2, bs_be_sm_4, bs_be_sm_8, /* set region */ bs_be_sr_1, bs_be_sr_2, bs_be_sr_4, bs_be_sr_8, bs_be_sr_2, bs_be_sr_4, bs_be_sr_8, }; struct bus_space bs_le_tag = { /* mapping/unmapping */ bs_gen_map, bs_gen_unmap, bs_gen_subregion, /* allocation/deallocation */ bs_gen_alloc, bs_gen_free, /* barrier */ bs_gen_barrier, /* read (single) */ bs_le_rs_1, bs_le_rs_2, bs_le_rs_4, bs_le_rs_8, bs_be_rs_2, bs_be_rs_4, bs_be_rs_8, /* read multiple */ bs_le_rm_1, bs_le_rm_2, bs_le_rm_4, bs_le_rm_8, bs_be_rm_2, bs_be_rm_4, bs_be_rm_8, /* read region */ bs_le_rr_1, bs_le_rr_2, bs_le_rr_4, bs_le_rr_8, bs_be_rr_2, bs_be_rr_4, bs_be_rr_8, /* write (single) */ bs_le_ws_1, bs_le_ws_2, bs_le_ws_4, bs_le_ws_8, bs_be_ws_2, bs_be_ws_4, bs_be_ws_8, /* write multiple */ bs_le_wm_1, bs_le_wm_2, bs_le_wm_4, bs_le_wm_8, bs_be_wm_2, bs_be_wm_4, bs_be_wm_8, /* write region */ bs_le_wr_1, bs_le_wr_2, bs_le_wr_4, bs_le_wr_8, bs_be_wr_2, bs_be_wr_4, bs_be_wr_8, /* set multiple */ bs_le_sm_1, bs_le_sm_2, bs_le_sm_4, bs_le_sm_8, bs_be_sm_2, bs_be_sm_4, bs_be_sm_8, /* set region */ bs_le_sr_1, bs_le_sr_2, bs_le_sr_4, bs_le_sr_8, bs_be_sr_2, bs_be_sr_4, bs_be_sr_8, }; Index: head/sys/powerpc/powerpc/busdma_machdep.c =================================================================== --- head/sys/powerpc/powerpc/busdma_machdep.c (revision 326260) +++ head/sys/powerpc/powerpc/busdma_machdep.c (revision 326261) @@ -1,1220 +1,1222 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 1997, 1998 Justin T. Gibbs. * 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, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 amd64/busdma_machdep.c, r204214 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iommu_if.h" #define MAX_BPAGES MIN(8192, physmem/40) struct bounce_zone; struct bus_dma_tag { bus_dma_tag_t parent; bus_size_t alignment; bus_addr_t boundary; bus_addr_t lowaddr; bus_addr_t highaddr; bus_dma_filter_t *filter; void *filterarg; bus_size_t maxsize; u_int nsegments; bus_size_t maxsegsz; int flags; int ref_count; int map_count; bus_dma_lock_t *lockfunc; void *lockfuncarg; struct bounce_zone *bounce_zone; device_t iommu; void *iommu_cookie; }; struct bounce_page { vm_offset_t vaddr; /* kva of bounce buffer */ bus_addr_t busaddr; /* Physical address */ vm_offset_t datavaddr; /* kva of client data */ vm_page_t datapage; /* physical page of client data */ vm_offset_t dataoffs; /* page offset of client data */ bus_size_t datacount; /* client data count */ STAILQ_ENTRY(bounce_page) links; }; int busdma_swi_pending; struct bounce_zone { STAILQ_ENTRY(bounce_zone) links; STAILQ_HEAD(bp_list, bounce_page) bounce_page_list; int total_bpages; int free_bpages; int reserved_bpages; int active_bpages; int total_bounced; int total_deferred; int map_count; bus_size_t alignment; bus_addr_t lowaddr; char zoneid[8]; char lowaddrid[20]; struct sysctl_ctx_list sysctl_tree; struct sysctl_oid *sysctl_tree_top; }; static struct mtx bounce_lock; static int total_bpages; static int busdma_zonecount; static STAILQ_HEAD(, bounce_zone) bounce_zone_list; static SYSCTL_NODE(_hw, OID_AUTO, busdma, CTLFLAG_RD, 0, "Busdma parameters"); SYSCTL_INT(_hw_busdma, OID_AUTO, total_bpages, CTLFLAG_RD, &total_bpages, 0, "Total bounce pages"); struct bus_dmamap { struct bp_list bpages; int pagesneeded; int pagesreserved; bus_dma_tag_t dmat; struct memdesc mem; bus_dma_segment_t *segments; int nsegs; bus_dmamap_callback_t *callback; void *callback_arg; STAILQ_ENTRY(bus_dmamap) links; int contigalloc; }; static STAILQ_HEAD(, bus_dmamap) bounce_map_waitinglist; static STAILQ_HEAD(, bus_dmamap) bounce_map_callbacklist; static void init_bounce_pages(void *dummy); static int alloc_bounce_zone(bus_dma_tag_t dmat); static int alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages); static int reserve_bounce_pages(bus_dma_tag_t dmat, bus_dmamap_t map, int commit); static bus_addr_t add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map, vm_offset_t vaddr, bus_addr_t addr, bus_size_t size); static void free_bounce_page(bus_dma_tag_t dmat, struct bounce_page *bpage); static __inline int run_filter(bus_dma_tag_t dmat, bus_addr_t paddr); /* * Return true if a match is made. * * To find a match walk the chain of bus_dma_tag_t's looking for 'paddr'. * * If paddr is within the bounds of the dma tag then call the filter callback * to check for a match, if there is no filter callback then assume a match. */ static __inline int run_filter(bus_dma_tag_t dmat, bus_addr_t paddr) { int retval; retval = 0; do { if (dmat->filter == NULL && dmat->iommu == NULL && paddr > dmat->lowaddr && paddr <= dmat->highaddr) retval = 1; if (dmat->filter == NULL && (paddr & (dmat->alignment - 1)) != 0) retval = 1; if (dmat->filter != NULL && (*dmat->filter)(dmat->filterarg, paddr) != 0) retval = 1; dmat = dmat->parent; } while (retval == 0 && dmat != NULL); return (retval); } /* * Convenience function for manipulating driver locks from busdma (during * busdma_swi, for example). Drivers that don't provide their own locks * should specify &Giant to dmat->lockfuncarg. Drivers that use their own * non-mutex locking scheme don't have to use this at all. */ void busdma_lock_mutex(void *arg, bus_dma_lock_op_t op) { struct mtx *dmtx; dmtx = (struct mtx *)arg; switch (op) { case BUS_DMA_LOCK: mtx_lock(dmtx); break; case BUS_DMA_UNLOCK: mtx_unlock(dmtx); break; default: panic("Unknown operation 0x%x for busdma_lock_mutex!", op); } } /* * dflt_lock should never get called. It gets put into the dma tag when * lockfunc == NULL, which is only valid if the maps that are associated * with the tag are meant to never be defered. * XXX Should have a way to identify which driver is responsible here. */ static void dflt_lock(void *arg, bus_dma_lock_op_t op) { panic("driver error: busdma dflt_lock called"); } #define BUS_DMA_COULD_BOUNCE BUS_DMA_BUS3 #define BUS_DMA_MIN_ALLOC_COMP BUS_DMA_BUS4 /* * Allocate a device specific dma_tag. */ int bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment, bus_addr_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr, bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize, int nsegments, bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc, void *lockfuncarg, bus_dma_tag_t *dmat) { bus_dma_tag_t newtag; int error = 0; /* Basic sanity checking */ if (boundary != 0 && boundary < maxsegsz) maxsegsz = boundary; if (maxsegsz == 0) { return (EINVAL); } /* Return a NULL tag on failure */ *dmat = NULL; newtag = (bus_dma_tag_t)malloc(sizeof(*newtag), M_DEVBUF, M_ZERO | M_NOWAIT); if (newtag == NULL) { CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d", __func__, newtag, 0, error); return (ENOMEM); } newtag->parent = parent; newtag->alignment = alignment; newtag->boundary = boundary; newtag->lowaddr = trunc_page((vm_paddr_t)lowaddr) + (PAGE_SIZE - 1); newtag->highaddr = trunc_page((vm_paddr_t)highaddr) + (PAGE_SIZE - 1); newtag->filter = filter; newtag->filterarg = filterarg; newtag->maxsize = maxsize; newtag->nsegments = nsegments; newtag->maxsegsz = maxsegsz; newtag->flags = flags; newtag->ref_count = 1; /* Count ourself */ newtag->map_count = 0; if (lockfunc != NULL) { newtag->lockfunc = lockfunc; newtag->lockfuncarg = lockfuncarg; } else { newtag->lockfunc = dflt_lock; newtag->lockfuncarg = NULL; } /* Take into account any restrictions imposed by our parent tag */ if (parent != NULL) { newtag->lowaddr = MIN(parent->lowaddr, newtag->lowaddr); newtag->highaddr = MAX(parent->highaddr, newtag->highaddr); if (newtag->boundary == 0) newtag->boundary = parent->boundary; else if (parent->boundary != 0) newtag->boundary = MIN(parent->boundary, newtag->boundary); if (newtag->filter == NULL) { /* * Short circuit looking at our parent directly * since we have encapsulated all of its information */ newtag->filter = parent->filter; newtag->filterarg = parent->filterarg; newtag->parent = parent->parent; } if (newtag->parent != NULL) atomic_add_int(&parent->ref_count, 1); newtag->iommu = parent->iommu; newtag->iommu_cookie = parent->iommu_cookie; } if (newtag->lowaddr < ptoa((vm_paddr_t)Maxmem) && newtag->iommu == NULL) newtag->flags |= BUS_DMA_COULD_BOUNCE; if (newtag->alignment > 1) newtag->flags |= BUS_DMA_COULD_BOUNCE; if (((newtag->flags & BUS_DMA_COULD_BOUNCE) != 0) && (flags & BUS_DMA_ALLOCNOW) != 0) { struct bounce_zone *bz; /* Must bounce */ if ((error = alloc_bounce_zone(newtag)) != 0) { free(newtag, M_DEVBUF); return (error); } bz = newtag->bounce_zone; if (ptoa(bz->total_bpages) < maxsize) { int pages; pages = atop(maxsize) - bz->total_bpages; /* Add pages to our bounce pool */ if (alloc_bounce_pages(newtag, pages) < pages) error = ENOMEM; } /* Performed initial allocation */ newtag->flags |= BUS_DMA_MIN_ALLOC_COMP; } if (error != 0) { free(newtag, M_DEVBUF); } else { *dmat = newtag; } CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d", __func__, newtag, (newtag != NULL ? newtag->flags : 0), error); return (error); } int bus_dma_tag_destroy(bus_dma_tag_t dmat) { bus_dma_tag_t dmat_copy; int error; error = 0; dmat_copy = dmat; if (dmat != NULL) { if (dmat->map_count != 0) { error = EBUSY; goto out; } while (dmat != NULL) { bus_dma_tag_t parent; parent = dmat->parent; atomic_subtract_int(&dmat->ref_count, 1); if (dmat->ref_count == 0) { free(dmat, M_DEVBUF); /* * Last reference count, so * release our reference * count on our parent. */ dmat = parent; } else dmat = NULL; } } out: CTR3(KTR_BUSDMA, "%s tag %p error %d", __func__, dmat_copy, error); return (error); } /* * Allocate a handle for mapping from kva/uva/physical * address space into bus device space. */ int bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp) { int error; error = 0; *mapp = (bus_dmamap_t)malloc(sizeof(**mapp), M_DEVBUF, M_NOWAIT | M_ZERO); if (*mapp == NULL) { CTR3(KTR_BUSDMA, "%s: tag %p error %d", __func__, dmat, ENOMEM); return (ENOMEM); } /* * Bouncing might be required if the driver asks for an active * exclusion region, a data alignment that is stricter than 1, and/or * an active address boundary. */ if (dmat->flags & BUS_DMA_COULD_BOUNCE) { /* Must bounce */ struct bounce_zone *bz; int maxpages; if (dmat->bounce_zone == NULL) { if ((error = alloc_bounce_zone(dmat)) != 0) return (error); } bz = dmat->bounce_zone; /* Initialize the new map */ STAILQ_INIT(&((*mapp)->bpages)); /* * Attempt to add pages to our pool on a per-instance * basis up to a sane limit. */ if (dmat->alignment > 1) maxpages = MAX_BPAGES; else maxpages = MIN(MAX_BPAGES, Maxmem -atop(dmat->lowaddr)); if ((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0 || (bz->map_count > 0 && bz->total_bpages < maxpages)) { int pages; pages = MAX(atop(dmat->maxsize), 1); pages = MIN(maxpages - bz->total_bpages, pages); pages = MAX(pages, 1); if (alloc_bounce_pages(dmat, pages) < pages) error = ENOMEM; if ((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0) { if (error == 0) dmat->flags |= BUS_DMA_MIN_ALLOC_COMP; } else { error = 0; } } bz->map_count++; } (*mapp)->nsegs = 0; (*mapp)->segments = (bus_dma_segment_t *)malloc( sizeof(bus_dma_segment_t) * dmat->nsegments, M_DEVBUF, M_NOWAIT); if ((*mapp)->segments == NULL) { CTR3(KTR_BUSDMA, "%s: tag %p error %d", __func__, dmat, ENOMEM); return (ENOMEM); } if (error == 0) dmat->map_count++; CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d", __func__, dmat, dmat->flags, error); return (error); } /* * Destroy a handle for mapping from kva/uva/physical * address space into bus device space. */ int bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map) { if (dmat->flags & BUS_DMA_COULD_BOUNCE) { if (STAILQ_FIRST(&map->bpages) != NULL) { CTR3(KTR_BUSDMA, "%s: tag %p error %d", __func__, dmat, EBUSY); return (EBUSY); } if (dmat->bounce_zone) dmat->bounce_zone->map_count--; } free(map->segments, M_DEVBUF); free(map, M_DEVBUF); dmat->map_count--; CTR2(KTR_BUSDMA, "%s: tag %p error 0", __func__, dmat); return (0); } /* * Allocate a piece of memory that can be efficiently mapped into * bus device space based on the constraints lited in the dma tag. * A dmamap to for use with dmamap_load is also allocated. */ int bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags, bus_dmamap_t *mapp) { vm_memattr_t attr; int mflags; if (flags & BUS_DMA_NOWAIT) mflags = M_NOWAIT; else mflags = M_WAITOK; bus_dmamap_create(dmat, flags, mapp); if (flags & BUS_DMA_ZERO) mflags |= M_ZERO; #ifdef NOTYET if (flags & BUS_DMA_NOCACHE) attr = VM_MEMATTR_UNCACHEABLE; else #endif attr = VM_MEMATTR_DEFAULT; /* * XXX: * (dmat->alignment <= dmat->maxsize) is just a quick hack; the exact * alignment guarantees of malloc need to be nailed down, and the * code below should be rewritten to take that into account. * * In the meantime, we'll warn the user if malloc gets it wrong. */ if ((dmat->maxsize <= PAGE_SIZE) && (dmat->alignment <= dmat->maxsize) && dmat->lowaddr >= ptoa((vm_paddr_t)Maxmem) && attr == VM_MEMATTR_DEFAULT) { *vaddr = malloc(dmat->maxsize, M_DEVBUF, mflags); } else { /* * XXX Use Contigmalloc until it is merged into this facility * and handles multi-seg allocations. Nobody is doing * multi-seg allocations yet though. * XXX Certain AGP hardware does. */ *vaddr = (void *)kmem_alloc_contig(kmem_arena, dmat->maxsize, mflags, 0ul, dmat->lowaddr, dmat->alignment ? dmat->alignment : 1ul, dmat->boundary, attr); (*mapp)->contigalloc = 1; } if (*vaddr == NULL) { CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d", __func__, dmat, dmat->flags, ENOMEM); return (ENOMEM); } else if (vtophys(*vaddr) & (dmat->alignment - 1)) { printf("bus_dmamem_alloc failed to align memory properly.\n"); } CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d", __func__, dmat, dmat->flags, 0); return (0); } /* * Free a piece of memory and it's allociated dmamap, that was allocated * via bus_dmamem_alloc. Make the same choice for free/contigfree. */ void bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map) { if (!map->contigalloc) free(vaddr, M_DEVBUF); else kmem_free(kmem_arena, (vm_offset_t)vaddr, dmat->maxsize); bus_dmamap_destroy(dmat, map); CTR3(KTR_BUSDMA, "%s: tag %p flags 0x%x", __func__, dmat, dmat->flags); } static void _bus_dmamap_count_phys(bus_dma_tag_t dmat, bus_dmamap_t map, vm_paddr_t buf, bus_size_t buflen, int flags) { bus_addr_t curaddr; bus_size_t sgsize; if (map->pagesneeded == 0) { CTR4(KTR_BUSDMA, "lowaddr= %d Maxmem= %d, boundary= %d, " "alignment= %d", dmat->lowaddr, ptoa((vm_paddr_t)Maxmem), dmat->boundary, dmat->alignment); CTR2(KTR_BUSDMA, "map= %p, pagesneeded= %d", map, map->pagesneeded); /* * Count the number of bounce pages * needed in order to complete this transfer */ curaddr = buf; while (buflen != 0) { sgsize = MIN(buflen, dmat->maxsegsz); if (run_filter(dmat, curaddr) != 0) { sgsize = MIN(sgsize, PAGE_SIZE - (curaddr & PAGE_MASK)); map->pagesneeded++; } curaddr += sgsize; buflen -= sgsize; } CTR1(KTR_BUSDMA, "pagesneeded= %d\n", map->pagesneeded); } } static void _bus_dmamap_count_pages(bus_dma_tag_t dmat, bus_dmamap_t map, pmap_t pmap, void *buf, bus_size_t buflen, int flags) { vm_offset_t vaddr; vm_offset_t vendaddr; bus_addr_t paddr; if (map->pagesneeded == 0) { CTR4(KTR_BUSDMA, "lowaddr= %d Maxmem= %d, boundary= %d, " "alignment= %d", dmat->lowaddr, ptoa((vm_paddr_t)Maxmem), dmat->boundary, dmat->alignment); CTR2(KTR_BUSDMA, "map= %p, pagesneeded= %d", map, map->pagesneeded); /* * Count the number of bounce pages * needed in order to complete this transfer */ vaddr = (vm_offset_t)buf; vendaddr = (vm_offset_t)buf + buflen; while (vaddr < vendaddr) { bus_size_t sg_len; sg_len = PAGE_SIZE - ((vm_offset_t)vaddr & PAGE_MASK); if (pmap == kernel_pmap) paddr = pmap_kextract(vaddr); else paddr = pmap_extract(pmap, vaddr); if (run_filter(dmat, paddr) != 0) { sg_len = roundup2(sg_len, dmat->alignment); map->pagesneeded++; } vaddr += sg_len; } CTR1(KTR_BUSDMA, "pagesneeded= %d\n", map->pagesneeded); } } static int _bus_dmamap_reserve_pages(bus_dma_tag_t dmat, bus_dmamap_t map, int flags) { /* Reserve Necessary Bounce Pages */ mtx_lock(&bounce_lock); if (flags & BUS_DMA_NOWAIT) { if (reserve_bounce_pages(dmat, map, 0) != 0) { mtx_unlock(&bounce_lock); return (ENOMEM); } } else { if (reserve_bounce_pages(dmat, map, 1) != 0) { /* Queue us for resources */ STAILQ_INSERT_TAIL(&bounce_map_waitinglist, map, links); mtx_unlock(&bounce_lock); return (EINPROGRESS); } } mtx_unlock(&bounce_lock); return (0); } /* * Add a single contiguous physical range to the segment list. */ static int _bus_dmamap_addseg(bus_dma_tag_t dmat, bus_dmamap_t map, bus_addr_t curaddr, bus_size_t sgsize, bus_dma_segment_t *segs, int *segp) { bus_addr_t baddr, bmask; int seg; /* * Make sure we don't cross any boundaries. */ bmask = ~(dmat->boundary - 1); if (dmat->boundary > 0) { baddr = (curaddr + dmat->boundary) & bmask; if (sgsize > (baddr - curaddr)) sgsize = (baddr - curaddr); } /* * Insert chunk into a segment, coalescing with * previous segment if possible. */ seg = *segp; if (seg == -1) { seg = 0; segs[seg].ds_addr = curaddr; segs[seg].ds_len = sgsize; } else { if (curaddr == segs[seg].ds_addr + segs[seg].ds_len && (segs[seg].ds_len + sgsize) <= dmat->maxsegsz && (dmat->boundary == 0 || (segs[seg].ds_addr & bmask) == (curaddr & bmask))) segs[seg].ds_len += sgsize; else { if (++seg >= dmat->nsegments) return (0); segs[seg].ds_addr = curaddr; segs[seg].ds_len = sgsize; } } *segp = seg; return (sgsize); } /* * Utility function to load a physical buffer. segp contains * the starting segment on entrace, and the ending segment on exit. */ int _bus_dmamap_load_phys(bus_dma_tag_t dmat, bus_dmamap_t map, vm_paddr_t buf, bus_size_t buflen, int flags, bus_dma_segment_t *segs, int *segp) { bus_addr_t curaddr; bus_size_t sgsize; int error; if (segs == NULL) segs = map->segments; if ((dmat->flags & BUS_DMA_COULD_BOUNCE) != 0) { _bus_dmamap_count_phys(dmat, map, buf, buflen, flags); if (map->pagesneeded != 0) { error = _bus_dmamap_reserve_pages(dmat, map, flags); if (error) return (error); } } while (buflen > 0) { curaddr = buf; sgsize = MIN(buflen, dmat->maxsegsz); if (map->pagesneeded != 0 && run_filter(dmat, curaddr)) { sgsize = MIN(sgsize, PAGE_SIZE - (curaddr & PAGE_MASK)); curaddr = add_bounce_page(dmat, map, 0, curaddr, sgsize); } sgsize = _bus_dmamap_addseg(dmat, map, curaddr, sgsize, segs, segp); if (sgsize == 0) break; buf += sgsize; buflen -= sgsize; } /* * Did we fit? */ return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */ } int _bus_dmamap_load_ma(bus_dma_tag_t dmat, bus_dmamap_t map, struct vm_page **ma, bus_size_t tlen, int ma_offs, int flags, bus_dma_segment_t *segs, int *segp) { return (bus_dmamap_load_ma_triv(dmat, map, ma, tlen, ma_offs, flags, segs, segp)); } /* * Utility function to load a linear buffer. segp contains * the starting segment on entrance, and the ending segment on exit. */ int _bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, bus_size_t buflen, pmap_t pmap, int flags, bus_dma_segment_t *segs, int *segp) { bus_size_t sgsize; bus_addr_t curaddr; vm_offset_t kvaddr, vaddr; int error; if (segs == NULL) segs = map->segments; if ((dmat->flags & BUS_DMA_COULD_BOUNCE) != 0) { _bus_dmamap_count_pages(dmat, map, pmap, buf, buflen, flags); if (map->pagesneeded != 0) { error = _bus_dmamap_reserve_pages(dmat, map, flags); if (error) return (error); } } vaddr = (vm_offset_t)buf; while (buflen > 0) { bus_size_t max_sgsize; /* * Get the physical address for this segment. */ if (pmap == kernel_pmap) { curaddr = pmap_kextract(vaddr); kvaddr = vaddr; } else { curaddr = pmap_extract(pmap, vaddr); kvaddr = 0; } /* * Compute the segment size, and adjust counts. */ max_sgsize = MIN(buflen, dmat->maxsegsz); sgsize = PAGE_SIZE - (curaddr & PAGE_MASK); if (map->pagesneeded != 0 && run_filter(dmat, curaddr)) { sgsize = roundup2(sgsize, dmat->alignment); sgsize = MIN(sgsize, max_sgsize); curaddr = add_bounce_page(dmat, map, kvaddr, curaddr, sgsize); } else { sgsize = MIN(sgsize, max_sgsize); } sgsize = _bus_dmamap_addseg(dmat, map, curaddr, sgsize, segs, segp); if (sgsize == 0) break; vaddr += sgsize; buflen -= sgsize; } /* * Did we fit? */ return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */ } void _bus_dmamap_waitok(bus_dma_tag_t dmat, bus_dmamap_t map, struct memdesc *mem, bus_dmamap_callback_t *callback, void *callback_arg) { if (dmat->flags & BUS_DMA_COULD_BOUNCE) { map->dmat = dmat; map->mem = *mem; map->callback = callback; map->callback_arg = callback_arg; } } bus_dma_segment_t * _bus_dmamap_complete(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dma_segment_t *segs, int nsegs, int error) { map->nsegs = nsegs; if (segs != NULL) memcpy(map->segments, segs, map->nsegs*sizeof(segs[0])); if (dmat->iommu != NULL) IOMMU_MAP(dmat->iommu, map->segments, &map->nsegs, dmat->lowaddr, dmat->highaddr, dmat->alignment, dmat->boundary, dmat->iommu_cookie); if (segs != NULL) memcpy(segs, map->segments, map->nsegs*sizeof(segs[0])); else segs = map->segments; return (segs); } /* * Release the mapping held by map. */ void bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map) { struct bounce_page *bpage; if (dmat->iommu) { IOMMU_UNMAP(dmat->iommu, map->segments, map->nsegs, dmat->iommu_cookie); map->nsegs = 0; } while ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) { STAILQ_REMOVE_HEAD(&map->bpages, links); free_bounce_page(dmat, bpage); } } void bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op) { struct bounce_page *bpage; vm_offset_t datavaddr, tempvaddr; if ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) { /* * Handle data bouncing. We might also * want to add support for invalidating * the caches on broken hardware */ CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x op 0x%x " "performing bounce", __func__, dmat, dmat->flags, op); if (op & BUS_DMASYNC_PREWRITE) { while (bpage != NULL) { tempvaddr = 0; datavaddr = bpage->datavaddr; if (datavaddr == 0) { tempvaddr = pmap_quick_enter_page( bpage->datapage); datavaddr = tempvaddr | bpage->dataoffs; } bcopy((void *)datavaddr, (void *)bpage->vaddr, bpage->datacount); if (tempvaddr != 0) pmap_quick_remove_page(tempvaddr); bpage = STAILQ_NEXT(bpage, links); } dmat->bounce_zone->total_bounced++; } if (op & BUS_DMASYNC_POSTREAD) { while (bpage != NULL) { tempvaddr = 0; datavaddr = bpage->datavaddr; if (datavaddr == 0) { tempvaddr = pmap_quick_enter_page( bpage->datapage); datavaddr = tempvaddr | bpage->dataoffs; } bcopy((void *)bpage->vaddr, (void *)datavaddr, bpage->datacount); if (tempvaddr != 0) pmap_quick_remove_page(tempvaddr); bpage = STAILQ_NEXT(bpage, links); } dmat->bounce_zone->total_bounced++; } } powerpc_sync(); } static void init_bounce_pages(void *dummy __unused) { total_bpages = 0; STAILQ_INIT(&bounce_zone_list); STAILQ_INIT(&bounce_map_waitinglist); STAILQ_INIT(&bounce_map_callbacklist); mtx_init(&bounce_lock, "bounce pages lock", NULL, MTX_DEF); } SYSINIT(bpages, SI_SUB_LOCK, SI_ORDER_ANY, init_bounce_pages, NULL); static struct sysctl_ctx_list * busdma_sysctl_tree(struct bounce_zone *bz) { return (&bz->sysctl_tree); } static struct sysctl_oid * busdma_sysctl_tree_top(struct bounce_zone *bz) { return (bz->sysctl_tree_top); } static int alloc_bounce_zone(bus_dma_tag_t dmat) { struct bounce_zone *bz; /* Check to see if we already have a suitable zone */ STAILQ_FOREACH(bz, &bounce_zone_list, links) { if ((dmat->alignment <= bz->alignment) && (dmat->lowaddr >= bz->lowaddr)) { dmat->bounce_zone = bz; return (0); } } if ((bz = (struct bounce_zone *)malloc(sizeof(*bz), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) return (ENOMEM); STAILQ_INIT(&bz->bounce_page_list); bz->free_bpages = 0; bz->reserved_bpages = 0; bz->active_bpages = 0; bz->lowaddr = dmat->lowaddr; bz->alignment = MAX(dmat->alignment, PAGE_SIZE); bz->map_count = 0; snprintf(bz->zoneid, 8, "zone%d", busdma_zonecount); busdma_zonecount++; snprintf(bz->lowaddrid, 18, "%#jx", (uintmax_t)bz->lowaddr); STAILQ_INSERT_TAIL(&bounce_zone_list, bz, links); dmat->bounce_zone = bz; sysctl_ctx_init(&bz->sysctl_tree); bz->sysctl_tree_top = SYSCTL_ADD_NODE(&bz->sysctl_tree, SYSCTL_STATIC_CHILDREN(_hw_busdma), OID_AUTO, bz->zoneid, CTLFLAG_RD, 0, ""); if (bz->sysctl_tree_top == NULL) { sysctl_ctx_free(&bz->sysctl_tree); return (0); /* XXX error code? */ } SYSCTL_ADD_INT(busdma_sysctl_tree(bz), SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO, "total_bpages", CTLFLAG_RD, &bz->total_bpages, 0, "Total bounce pages"); SYSCTL_ADD_INT(busdma_sysctl_tree(bz), SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO, "free_bpages", CTLFLAG_RD, &bz->free_bpages, 0, "Free bounce pages"); SYSCTL_ADD_INT(busdma_sysctl_tree(bz), SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO, "reserved_bpages", CTLFLAG_RD, &bz->reserved_bpages, 0, "Reserved bounce pages"); SYSCTL_ADD_INT(busdma_sysctl_tree(bz), SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO, "active_bpages", CTLFLAG_RD, &bz->active_bpages, 0, "Active bounce pages"); SYSCTL_ADD_INT(busdma_sysctl_tree(bz), SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO, "total_bounced", CTLFLAG_RD, &bz->total_bounced, 0, "Total bounce requests"); SYSCTL_ADD_INT(busdma_sysctl_tree(bz), SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO, "total_deferred", CTLFLAG_RD, &bz->total_deferred, 0, "Total bounce requests that were deferred"); SYSCTL_ADD_STRING(busdma_sysctl_tree(bz), SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO, "lowaddr", CTLFLAG_RD, bz->lowaddrid, 0, ""); SYSCTL_ADD_UAUTO(busdma_sysctl_tree(bz), SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO, "alignment", CTLFLAG_RD, &bz->alignment, ""); return (0); } static int alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages) { struct bounce_zone *bz; int count; bz = dmat->bounce_zone; count = 0; while (numpages > 0) { struct bounce_page *bpage; bpage = (struct bounce_page *)malloc(sizeof(*bpage), M_DEVBUF, M_NOWAIT | M_ZERO); if (bpage == NULL) break; bpage->vaddr = (vm_offset_t)contigmalloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT, 0ul, bz->lowaddr, PAGE_SIZE, 0); if (bpage->vaddr == 0) { free(bpage, M_DEVBUF); break; } bpage->busaddr = pmap_kextract(bpage->vaddr); mtx_lock(&bounce_lock); STAILQ_INSERT_TAIL(&bz->bounce_page_list, bpage, links); total_bpages++; bz->total_bpages++; bz->free_bpages++; mtx_unlock(&bounce_lock); count++; numpages--; } return (count); } static int reserve_bounce_pages(bus_dma_tag_t dmat, bus_dmamap_t map, int commit) { struct bounce_zone *bz; int pages; mtx_assert(&bounce_lock, MA_OWNED); bz = dmat->bounce_zone; pages = MIN(bz->free_bpages, map->pagesneeded - map->pagesreserved); if (commit == 0 && map->pagesneeded > (map->pagesreserved + pages)) return (map->pagesneeded - (map->pagesreserved + pages)); bz->free_bpages -= pages; bz->reserved_bpages += pages; map->pagesreserved += pages; pages = map->pagesneeded - map->pagesreserved; return (pages); } static bus_addr_t add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map, vm_offset_t vaddr, bus_addr_t addr, bus_size_t size) { struct bounce_zone *bz; struct bounce_page *bpage; KASSERT(dmat->bounce_zone != NULL, ("no bounce zone in dma tag")); bz = dmat->bounce_zone; if (map->pagesneeded == 0) panic("add_bounce_page: map doesn't need any pages"); map->pagesneeded--; if (map->pagesreserved == 0) panic("add_bounce_page: map doesn't need any pages"); map->pagesreserved--; mtx_lock(&bounce_lock); bpage = STAILQ_FIRST(&bz->bounce_page_list); if (bpage == NULL) panic("add_bounce_page: free page list is empty"); STAILQ_REMOVE_HEAD(&bz->bounce_page_list, links); bz->reserved_bpages--; bz->active_bpages++; mtx_unlock(&bounce_lock); if (dmat->flags & BUS_DMA_KEEP_PG_OFFSET) { /* Page offset needs to be preserved. */ bpage->vaddr |= addr & PAGE_MASK; bpage->busaddr |= addr & PAGE_MASK; } bpage->datavaddr = vaddr; bpage->datapage = PHYS_TO_VM_PAGE(addr); bpage->dataoffs = addr & PAGE_MASK; bpage->datacount = size; STAILQ_INSERT_TAIL(&(map->bpages), bpage, links); return (bpage->busaddr); } static void free_bounce_page(bus_dma_tag_t dmat, struct bounce_page *bpage) { struct bus_dmamap *map; struct bounce_zone *bz; bz = dmat->bounce_zone; bpage->datavaddr = 0; bpage->datacount = 0; if (dmat->flags & BUS_DMA_KEEP_PG_OFFSET) { /* * Reset the bounce page to start at offset 0. Other uses * of this bounce page may need to store a full page of * data and/or assume it starts on a page boundary. */ bpage->vaddr &= ~PAGE_MASK; bpage->busaddr &= ~PAGE_MASK; } mtx_lock(&bounce_lock); STAILQ_INSERT_HEAD(&bz->bounce_page_list, bpage, links); bz->free_bpages++; bz->active_bpages--; if ((map = STAILQ_FIRST(&bounce_map_waitinglist)) != NULL) { if (reserve_bounce_pages(map->dmat, map, 1) == 0) { STAILQ_REMOVE_HEAD(&bounce_map_waitinglist, links); STAILQ_INSERT_TAIL(&bounce_map_callbacklist, map, links); busdma_swi_pending = 1; bz->total_deferred++; swi_sched(vm_ih, 0); } } mtx_unlock(&bounce_lock); } void busdma_swi(void) { bus_dma_tag_t dmat; struct bus_dmamap *map; mtx_lock(&bounce_lock); while ((map = STAILQ_FIRST(&bounce_map_callbacklist)) != NULL) { STAILQ_REMOVE_HEAD(&bounce_map_callbacklist, links); mtx_unlock(&bounce_lock); dmat = map->dmat; (dmat->lockfunc)(dmat->lockfuncarg, BUS_DMA_LOCK); bus_dmamap_load_mem(map->dmat, map, &map->mem, map->callback, map->callback_arg, BUS_DMA_WAITOK); (dmat->lockfunc)(dmat->lockfuncarg, BUS_DMA_UNLOCK); mtx_lock(&bounce_lock); } mtx_unlock(&bounce_lock); } int bus_dma_tag_set_iommu(bus_dma_tag_t tag, device_t iommu, void *cookie) { tag->iommu = iommu; tag->iommu_cookie = cookie; return (0); } Index: head/sys/powerpc/powerpc/clock.c =================================================================== --- head/sys/powerpc/powerpc/clock.c (revision 326260) +++ head/sys/powerpc/powerpc/clock.c (revision 326261) @@ -1,311 +1,313 @@ /*- + * SPDX-License-Identifier: BSD-4-Clause AND BSD-2-Clause-FreeBSD + * * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: clock.c,v 1.9 2000/01/19 02:52:19 msaitoh Exp $ */ /* * Copyright (C) 2001 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Initially we assume a processor with a bus frequency of 12.5 MHz. */ static int initialized = 0; static u_long ns_per_tick = 80; static u_long ticks_per_sec = 12500000; static u_long *decr_counts[MAXCPU]; static int decr_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period); static int decr_et_stop(struct eventtimer *et); static timecounter_get_t decr_get_timecount; struct decr_state { int mode; /* 0 - off, 1 - periodic, 2 - one-shot. */ int32_t div; /* Periodic divisor. */ }; static DPCPU_DEFINE(struct decr_state, decr_state); static struct eventtimer decr_et; static struct timecounter decr_tc = { decr_get_timecount, /* get_timecount */ 0, /* no poll_pps */ ~0u, /* counter_mask */ 0, /* frequency */ "timebase" /* name */ }; /* * Decrementer interrupt handler. */ void decr_intr(struct trapframe *frame) { struct decr_state *s = DPCPU_PTR(decr_state); int nticks = 0; int32_t val; if (!initialized) return; (*decr_counts[curcpu])++; #ifdef BOOKE /* * Interrupt handler must reset DIS to avoid getting another * interrupt once EE is enabled. */ mtspr(SPR_TSR, TSR_DIS); #endif if (s->mode == 1) { /* * Based on the actual time delay since the last decrementer * reload, we arrange for earlier interrupt next time. */ __asm ("mfdec %0" : "=r"(val)); while (val < 0) { val += s->div; nticks++; } mtdec(val); } else if (s->mode == 2) { nticks = 1; decr_et_stop(NULL); } else if (s->mode == 0) { /* Potemkin timer ran out without an event. Just reset it. */ decr_et_stop(NULL); } while (nticks-- > 0) { if (decr_et.et_active) decr_et.et_event_cb(&decr_et, decr_et.et_arg); } } void cpu_initclocks(void) { decr_tc_init(); cpu_initclocks_bsp(); } /* * BSP early initialization. */ void decr_init(void) { struct cpuref cpu; char buf[32]; /* * Check the BSP's timebase frequency. Sometimes we can't find the BSP, * so fall back to the first CPU in this case. */ if (platform_smp_get_bsp(&cpu) != 0) platform_smp_first_cpu(&cpu); ticks_per_sec = platform_timebase_freq(&cpu); ns_per_tick = 1000000000 / ticks_per_sec; set_cputicker(mftb, ticks_per_sec, 0); snprintf(buf, sizeof(buf), "cpu%d:decrementer", curcpu); intrcnt_add(buf, &decr_counts[curcpu]); decr_et_stop(NULL); initialized = 1; } #ifdef SMP /* * AP early initialization. */ void decr_ap_init(void) { char buf[32]; snprintf(buf, sizeof(buf), "cpu%d:decrementer", curcpu); intrcnt_add(buf, &decr_counts[curcpu]); decr_et_stop(NULL); } #endif /* * Final initialization. */ void decr_tc_init(void) { decr_tc.tc_frequency = ticks_per_sec; tc_init(&decr_tc); decr_et.et_name = "decrementer"; decr_et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; decr_et.et_quality = 1000; decr_et.et_frequency = ticks_per_sec; decr_et.et_min_period = (0x00000002LLU << 32) / ticks_per_sec; decr_et.et_max_period = (0x7fffffffLLU << 32) / ticks_per_sec; decr_et.et_start = decr_et_start; decr_et.et_stop = decr_et_stop; decr_et.et_priv = NULL; et_register(&decr_et); } /* * Event timer start method. */ static int decr_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { struct decr_state *s = DPCPU_PTR(decr_state); uint32_t fdiv; #ifdef BOOKE uint32_t tcr; #endif if (period != 0) { s->mode = 1; s->div = (decr_et.et_frequency * period) >> 32; } else { s->mode = 2; s->div = 0; } if (first != 0) fdiv = (decr_et.et_frequency * first) >> 32; else fdiv = s->div; #ifdef BOOKE tcr = mfspr(SPR_TCR); tcr |= TCR_DIE; if (s->mode == 1) { mtspr(SPR_DECAR, s->div); tcr |= TCR_ARE; } else tcr &= ~TCR_ARE; mtdec(fdiv); mtspr(SPR_TCR, tcr); #else mtdec(fdiv); #endif return (0); } /* * Event timer stop method. */ static int decr_et_stop(struct eventtimer *et) { struct decr_state *s = DPCPU_PTR(decr_state); #ifdef BOOKE uint32_t tcr; #endif s->mode = 0; s->div = 0x7fffffff; #ifdef BOOKE tcr = mfspr(SPR_TCR); tcr &= ~(TCR_DIE | TCR_ARE); mtspr(SPR_TCR, tcr); #else mtdec(s->div); #endif return (0); } /* * Timecounter get method. */ static unsigned decr_get_timecount(struct timecounter *tc) { return (mftb()); } /* * Wait for about n microseconds (at least!). */ void DELAY(int n) { u_quad_t tb, ttb; tb = mftb(); ttb = tb + howmany(n * 1000, ns_per_tick); while (tb < ttb) tb = mftb(); } Index: head/sys/powerpc/powerpc/copyinout.c =================================================================== --- head/sys/powerpc/powerpc/copyinout.c (revision 326260) +++ head/sys/powerpc/powerpc/copyinout.c (revision 326261) @@ -1,621 +1,623 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND BSD-4-Clause + * * Copyright (C) 2002 Benno Rice * 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 Benno Rice ``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 TOOLS GMBH 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. */ /*- * Copyright (C) 1993 Wolfgang Solfrank. * Copyright (C) 1993 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #ifdef AIM /* * Makes sure that the right segment of userspace is mapped in. */ #ifdef __powerpc64__ static __inline void set_user_sr(pmap_t pm, volatile const void *addr) { struct slb *slb; register_t slbv; /* Try lockless look-up first */ slb = user_va_to_slb_entry(pm, (vm_offset_t)addr); if (slb == NULL) { /* If it isn't there, we need to pre-fault the VSID */ PMAP_LOCK(pm); slbv = va_to_vsid(pm, (vm_offset_t)addr) << SLBV_VSID_SHIFT; PMAP_UNLOCK(pm); } else { slbv = slb->slbv; } /* Mark segment no-execute */ slbv |= SLBV_N; /* If we have already set this VSID, we can just return */ if (curthread->td_pcb->pcb_cpu.aim.usr_vsid == slbv) return; __asm __volatile("isync"); curthread->td_pcb->pcb_cpu.aim.usr_segm = (uintptr_t)addr >> ADDR_SR_SHFT; curthread->td_pcb->pcb_cpu.aim.usr_vsid = slbv; __asm __volatile ("slbie %0; slbmte %1, %2; isync" :: "r"(USER_ADDR), "r"(slbv), "r"(USER_SLB_SLBE)); } #else static __inline void set_user_sr(pmap_t pm, volatile const void *addr) { register_t vsid; vsid = va_to_vsid(pm, (vm_offset_t)addr); /* Mark segment no-execute */ vsid |= SR_N; /* If we have already set this VSID, we can just return */ if (curthread->td_pcb->pcb_cpu.aim.usr_vsid == vsid) return; __asm __volatile("isync"); curthread->td_pcb->pcb_cpu.aim.usr_segm = (uintptr_t)addr >> ADDR_SR_SHFT; curthread->td_pcb->pcb_cpu.aim.usr_vsid = vsid; __asm __volatile("mtsr %0,%1; isync" :: "n"(USER_SR), "r"(vsid)); } #endif static __inline int map_user_ptr(pmap_t pm, volatile const void *uaddr, void **kaddr, size_t ulen, size_t *klen) { size_t l; *kaddr = (char *)USER_ADDR + ((uintptr_t)uaddr & ~SEGMENT_MASK); l = ((char *)USER_ADDR + SEGMENT_LENGTH) - (char *)(*kaddr); if (l > ulen) l = ulen; if (klen) *klen = l; else if (l != ulen) return (EFAULT); set_user_sr(pm, uaddr); return (0); } #else /* Book-E uses a combined kernel/user mapping */ static __inline int map_user_ptr(pmap_t pm, volatile const void *uaddr, void **kaddr, size_t ulen, size_t *klen) { if ((uintptr_t)uaddr + ulen > VM_MAXUSER_ADDRESS + PAGE_SIZE) return (EFAULT); *kaddr = (void *)(uintptr_t)uaddr; if (klen) *klen = ulen; return (0); } #endif int copyout(const void *kaddr, void *udaddr, size_t len) { struct thread *td; pmap_t pm; jmp_buf env; const char *kp; char *up, *p; size_t l; td = curthread; pm = &td->td_proc->p_vmspace->vm_pmap; td->td_pcb->pcb_onfault = &env; if (setjmp(env)) { td->td_pcb->pcb_onfault = NULL; return (EFAULT); } kp = kaddr; up = udaddr; while (len > 0) { if (map_user_ptr(pm, udaddr, (void **)&p, len, &l)) { td->td_pcb->pcb_onfault = NULL; return (EFAULT); } bcopy(kp, p, l); up += l; kp += l; len -= l; } td->td_pcb->pcb_onfault = NULL; return (0); } int copyin(const void *udaddr, void *kaddr, size_t len) { struct thread *td; pmap_t pm; jmp_buf env; const char *up; char *kp, *p; size_t l; td = curthread; pm = &td->td_proc->p_vmspace->vm_pmap; td->td_pcb->pcb_onfault = &env; if (setjmp(env)) { td->td_pcb->pcb_onfault = NULL; return (EFAULT); } kp = kaddr; up = udaddr; while (len > 0) { if (map_user_ptr(pm, udaddr, (void **)&p, len, &l)) { td->td_pcb->pcb_onfault = NULL; return (EFAULT); } bcopy(p, kp, l); up += l; kp += l; len -= l; } td->td_pcb->pcb_onfault = NULL; return (0); } int copyinstr(const void *udaddr, void *kaddr, size_t len, size_t *done) { const char *up; char *kp; size_t l; int rv, c; kp = kaddr; up = udaddr; rv = ENAMETOOLONG; for (l = 0; len-- > 0; l++) { if ((c = fubyte(up++)) < 0) { rv = EFAULT; break; } if (!(*kp++ = c)) { l++; rv = 0; break; } } if (done != NULL) { *done = l; } return (rv); } int subyte(volatile void *addr, int byte) { struct thread *td; pmap_t pm; jmp_buf env; char *p; td = curthread; pm = &td->td_proc->p_vmspace->vm_pmap; td->td_pcb->pcb_onfault = &env; if (setjmp(env)) { td->td_pcb->pcb_onfault = NULL; return (-1); } if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { td->td_pcb->pcb_onfault = NULL; return (-1); } *p = (char)byte; td->td_pcb->pcb_onfault = NULL; return (0); } #ifdef __powerpc64__ int suword32(volatile void *addr, int word) { struct thread *td; pmap_t pm; jmp_buf env; int *p; td = curthread; pm = &td->td_proc->p_vmspace->vm_pmap; td->td_pcb->pcb_onfault = &env; if (setjmp(env)) { td->td_pcb->pcb_onfault = NULL; return (-1); } if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { td->td_pcb->pcb_onfault = NULL; return (-1); } *p = word; td->td_pcb->pcb_onfault = NULL; return (0); } #endif int suword(volatile void *addr, long word) { struct thread *td; pmap_t pm; jmp_buf env; long *p; td = curthread; pm = &td->td_proc->p_vmspace->vm_pmap; td->td_pcb->pcb_onfault = &env; if (setjmp(env)) { td->td_pcb->pcb_onfault = NULL; return (-1); } if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { td->td_pcb->pcb_onfault = NULL; return (-1); } *p = word; td->td_pcb->pcb_onfault = NULL; return (0); } #ifdef __powerpc64__ int suword64(volatile void *addr, int64_t word) { return (suword(addr, (long)word)); } #else int suword32(volatile void *addr, int32_t word) { return (suword(addr, (long)word)); } #endif int fubyte(volatile const void *addr) { struct thread *td; pmap_t pm; jmp_buf env; u_char *p; int val; td = curthread; pm = &td->td_proc->p_vmspace->vm_pmap; td->td_pcb->pcb_onfault = &env; if (setjmp(env)) { td->td_pcb->pcb_onfault = NULL; return (-1); } if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { td->td_pcb->pcb_onfault = NULL; return (-1); } val = *p; td->td_pcb->pcb_onfault = NULL; return (val); } int fuword16(volatile const void *addr) { struct thread *td; pmap_t pm; jmp_buf env; uint16_t *p, val; td = curthread; pm = &td->td_proc->p_vmspace->vm_pmap; td->td_pcb->pcb_onfault = &env; if (setjmp(env)) { td->td_pcb->pcb_onfault = NULL; return (-1); } if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { td->td_pcb->pcb_onfault = NULL; return (-1); } val = *p; td->td_pcb->pcb_onfault = NULL; return (val); } int fueword32(volatile const void *addr, int32_t *val) { struct thread *td; pmap_t pm; jmp_buf env; int32_t *p; td = curthread; pm = &td->td_proc->p_vmspace->vm_pmap; td->td_pcb->pcb_onfault = &env; if (setjmp(env)) { td->td_pcb->pcb_onfault = NULL; return (-1); } if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { td->td_pcb->pcb_onfault = NULL; return (-1); } *val = *p; td->td_pcb->pcb_onfault = NULL; return (0); } #ifdef __powerpc64__ int fueword64(volatile const void *addr, int64_t *val) { struct thread *td; pmap_t pm; jmp_buf env; int64_t *p; td = curthread; pm = &td->td_proc->p_vmspace->vm_pmap; td->td_pcb->pcb_onfault = &env; if (setjmp(env)) { td->td_pcb->pcb_onfault = NULL; return (-1); } if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { td->td_pcb->pcb_onfault = NULL; return (-1); } *val = *p; td->td_pcb->pcb_onfault = NULL; return (0); } #endif int fueword(volatile const void *addr, long *val) { struct thread *td; pmap_t pm; jmp_buf env; long *p; td = curthread; pm = &td->td_proc->p_vmspace->vm_pmap; td->td_pcb->pcb_onfault = &env; if (setjmp(env)) { td->td_pcb->pcb_onfault = NULL; return (-1); } if (map_user_ptr(pm, addr, (void **)&p, sizeof(*p), NULL)) { td->td_pcb->pcb_onfault = NULL; return (-1); } *val = *p; td->td_pcb->pcb_onfault = NULL; return (0); } int casueword32(volatile uint32_t *addr, uint32_t old, uint32_t *oldvalp, uint32_t new) { struct thread *td; pmap_t pm; jmp_buf env; uint32_t *p, val; td = curthread; pm = &td->td_proc->p_vmspace->vm_pmap; td->td_pcb->pcb_onfault = &env; if (setjmp(env)) { td->td_pcb->pcb_onfault = NULL; return (-1); } if (map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, sizeof(*p), NULL)) { td->td_pcb->pcb_onfault = NULL; return (-1); } __asm __volatile ( "1:\tlwarx %0, 0, %2\n\t" /* load old value */ "cmplw %3, %0\n\t" /* compare */ "bne 2f\n\t" /* exit if not equal */ "stwcx. %4, 0, %2\n\t" /* attempt to store */ "bne- 1b\n\t" /* spin if failed */ "b 3f\n\t" /* we've succeeded */ "2:\n\t" "stwcx. %0, 0, %2\n\t" /* clear reservation (74xx) */ "3:\n\t" : "=&r" (val), "=m" (*p) : "r" (p), "r" (old), "r" (new), "m" (*p) : "cr0", "memory"); td->td_pcb->pcb_onfault = NULL; *oldvalp = val; return (0); } #ifndef __powerpc64__ int casueword(volatile u_long *addr, u_long old, u_long *oldvalp, u_long new) { return (casueword32((volatile uint32_t *)addr, old, (uint32_t *)oldvalp, new)); } #else int casueword(volatile u_long *addr, u_long old, u_long *oldvalp, u_long new) { struct thread *td; pmap_t pm; jmp_buf env; u_long *p, val; td = curthread; pm = &td->td_proc->p_vmspace->vm_pmap; td->td_pcb->pcb_onfault = &env; if (setjmp(env)) { td->td_pcb->pcb_onfault = NULL; return (-1); } if (map_user_ptr(pm, (void *)(uintptr_t)addr, (void **)&p, sizeof(*p), NULL)) { td->td_pcb->pcb_onfault = NULL; return (-1); } __asm __volatile ( "1:\tldarx %0, 0, %2\n\t" /* load old value */ "cmpld %3, %0\n\t" /* compare */ "bne 2f\n\t" /* exit if not equal */ "stdcx. %4, 0, %2\n\t" /* attempt to store */ "bne- 1b\n\t" /* spin if failed */ "b 3f\n\t" /* we've succeeded */ "2:\n\t" "stdcx. %0, 0, %2\n\t" /* clear reservation (74xx) */ "3:\n\t" : "=&r" (val), "=m" (*p) : "r" (p), "r" (old), "r" (new), "m" (*p) : "cr0", "memory"); td->td_pcb->pcb_onfault = NULL; *oldvalp = val; return (0); } #endif Index: head/sys/powerpc/powerpc/copystr.c =================================================================== --- head/sys/powerpc/powerpc/copystr.c (revision 326260) +++ head/sys/powerpc/powerpc/copystr.c (revision 326261) @@ -1,68 +1,70 @@ /*- + * SPDX-License-Identifier: BSD-4-Clause + * * Copyright (C) 1995 Wolfgang Solfrank. * Copyright (C) 1995 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: copystr.c,v 1.3 2000/06/08 06:47:17 kleink Exp $ */ #include __FBSDID("$FreeBSD$"); #include #include #include /* * Emulate copyinstr. */ int copystr(kfaddr, kdaddr, len, done) const void *kfaddr; void *kdaddr; size_t len; size_t *done; { const u_char *kfp = kfaddr; u_char *kdp = kdaddr; size_t l; int rv; rv = ENAMETOOLONG; for (l = 0; len-- > 0; l++) { if (!(*kdp++ = *kfp++)) { l++; rv = 0; break; } } if (done != NULL) { *done = l; } return rv; } Index: head/sys/powerpc/powerpc/cpu.c =================================================================== --- head/sys/powerpc/powerpc/cpu.c (revision 326260) +++ head/sys/powerpc/powerpc/cpu.c (revision 326261) @@ -1,690 +1,692 @@ /*- + * SPDX-License-Identifier: BSD-4-Clause AND BSD-2-Clause-FreeBSD + * * Copyright (c) 2001 Matt Thomas. * Copyright (c) 2001 Tsubai Masanari. * Copyright (c) 1998, 1999, 2001 Internet Research Institute, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by * Internet Research Institute, Inc. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*- * Copyright (C) 2003 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * from $NetBSD: cpu_subr.c,v 1.1 2003/02/03 17:10:09 matt Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void cpu_6xx_setup(int cpuid, uint16_t vers); static void cpu_970_setup(int cpuid, uint16_t vers); static void cpu_booke_setup(int cpuid, uint16_t vers); int powerpc_pow_enabled; void (*cpu_idle_hook)(sbintime_t) = NULL; static void cpu_idle_60x(sbintime_t); static void cpu_idle_booke(sbintime_t); struct cputab { const char *name; uint16_t version; uint16_t revfmt; int features; /* Do not include PPC_FEATURE_32 or * PPC_FEATURE_HAS_MMU */ int features2; void (*cpu_setup)(int cpuid, uint16_t vers); }; #define REVFMT_MAJMIN 1 /* %u.%u */ #define REVFMT_HEX 2 /* 0x%04x */ #define REVFMT_DEC 3 /* %u */ static const struct cputab models[] = { { "Motorola PowerPC 601", MPC601, REVFMT_DEC, PPC_FEATURE_HAS_FPU | PPC_FEATURE_UNIFIED_CACHE, 0, cpu_6xx_setup }, { "Motorola PowerPC 602", MPC602, REVFMT_DEC, PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "Motorola PowerPC 603", MPC603, REVFMT_MAJMIN, PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "Motorola PowerPC 603e", MPC603e, REVFMT_MAJMIN, PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "Motorola PowerPC 603ev", MPC603ev, REVFMT_MAJMIN, PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "Motorola PowerPC 604", MPC604, REVFMT_MAJMIN, PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "Motorola PowerPC 604ev", MPC604ev, REVFMT_MAJMIN, PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "Motorola PowerPC 620", MPC620, REVFMT_HEX, PPC_FEATURE_64 | PPC_FEATURE_HAS_FPU, 0, NULL }, { "Motorola PowerPC 750", MPC750, REVFMT_MAJMIN, PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "IBM PowerPC 750FX", IBM750FX, REVFMT_MAJMIN, PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "IBM PowerPC 970", IBM970, REVFMT_MAJMIN, PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_970_setup }, { "IBM PowerPC 970FX", IBM970FX, REVFMT_MAJMIN, PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_970_setup }, { "IBM PowerPC 970GX", IBM970GX, REVFMT_MAJMIN, PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_970_setup }, { "IBM PowerPC 970MP", IBM970MP, REVFMT_MAJMIN, PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_970_setup }, { "IBM POWER4", IBMPOWER4, REVFMT_MAJMIN, PPC_FEATURE_64 | PPC_FEATURE_HAS_FPU, 0, NULL }, { "IBM POWER4+", IBMPOWER4PLUS, REVFMT_MAJMIN, PPC_FEATURE_64 | PPC_FEATURE_HAS_FPU, 0, NULL }, { "IBM POWER5", IBMPOWER5, REVFMT_MAJMIN, PPC_FEATURE_64 | PPC_FEATURE_HAS_FPU | PPC_FEATURE_SMT, 0, NULL }, { "IBM POWER5+", IBMPOWER5PLUS, REVFMT_MAJMIN, PPC_FEATURE_64 | PPC_FEATURE_HAS_FPU | PPC_FEATURE_SMT, 0, NULL }, { "IBM POWER6", IBMPOWER6, REVFMT_MAJMIN, PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU | PPC_FEATURE_SMT | PPC_FEATURE_ARCH_2_05, 0, NULL }, { "IBM POWER7", IBMPOWER7, REVFMT_MAJMIN, PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU | PPC_FEATURE_SMT | PPC_FEATURE_ARCH_2_05 | PPC_FEATURE_ARCH_2_06 | PPC_FEATURE_HAS_VSX, 0, NULL }, { "IBM POWER7+", IBMPOWER7PLUS, REVFMT_MAJMIN, PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU | PPC_FEATURE_SMT | PPC_FEATURE_ARCH_2_05 | PPC_FEATURE_ARCH_2_06 | PPC_FEATURE_HAS_VSX, 0, NULL }, { "IBM POWER8E", IBMPOWER8E, REVFMT_MAJMIN, PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU | PPC_FEATURE_SMT | PPC_FEATURE_ARCH_2_05 | PPC_FEATURE_ARCH_2_06 | PPC_FEATURE_HAS_VSX, PPC_FEATURE2_ARCH_2_07 | PPC_FEATURE2_HAS_HTM | PPC_FEATURE2_ISEL | PPC_FEATURE2_HAS_VCRYPTO, NULL }, { "IBM POWER8", IBMPOWER8, REVFMT_MAJMIN, PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU | PPC_FEATURE_SMT | PPC_FEATURE_ARCH_2_05 | PPC_FEATURE_ARCH_2_06 | PPC_FEATURE_HAS_VSX, PPC_FEATURE2_ARCH_2_07 | PPC_FEATURE2_HAS_HTM | PPC_FEATURE2_ISEL | PPC_FEATURE2_HAS_VCRYPTO, NULL }, { "Motorola PowerPC 7400", MPC7400, REVFMT_MAJMIN, PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "Motorola PowerPC 7410", MPC7410, REVFMT_MAJMIN, PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "Motorola PowerPC 7450", MPC7450, REVFMT_MAJMIN, PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "Motorola PowerPC 7455", MPC7455, REVFMT_MAJMIN, PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "Motorola PowerPC 7457", MPC7457, REVFMT_MAJMIN, PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "Motorola PowerPC 7447A", MPC7447A, REVFMT_MAJMIN, PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "Motorola PowerPC 7448", MPC7448, REVFMT_MAJMIN, PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "Motorola PowerPC 8240", MPC8240, REVFMT_MAJMIN, PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "Motorola PowerPC 8245", MPC8245, REVFMT_MAJMIN, PPC_FEATURE_HAS_FPU, 0, cpu_6xx_setup }, { "Freescale e500v1 core", FSL_E500v1, REVFMT_MAJMIN, PPC_FEATURE_BOOKE | PPC_FEATURE_HAS_SPE | PPC_FEATURE_HAS_EFP_SINGLE, PPC_FEATURE2_ISEL, cpu_booke_setup }, { "Freescale e500v2 core", FSL_E500v2, REVFMT_MAJMIN, PPC_FEATURE_BOOKE | PPC_FEATURE_HAS_SPE | PPC_FEATURE_HAS_EFP_SINGLE | PPC_FEATURE_HAS_EFP_DOUBLE, PPC_FEATURE2_ISEL, cpu_booke_setup }, { "Freescale e500mc core", FSL_E500mc, REVFMT_MAJMIN, PPC_FEATURE_BOOKE | PPC_FEATURE_HAS_FPU, PPC_FEATURE2_ISEL, cpu_booke_setup }, { "Freescale e5500 core", FSL_E5500, REVFMT_MAJMIN, PPC_FEATURE_BOOKE | PPC_FEATURE_64 | PPC_FEATURE_HAS_FPU, PPC_FEATURE2_ISEL, cpu_booke_setup }, { "Freescale e6500 core", FSL_E6500, REVFMT_MAJMIN, PPC_FEATURE_BOOKE | PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, PPC_FEATURE2_ISEL, cpu_booke_setup }, { "IBM Cell Broadband Engine", IBMCELLBE, REVFMT_MAJMIN, PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU | PPC_FEATURE_SMT, 0, NULL}, { "Unknown PowerPC CPU", 0, REVFMT_HEX, 0, 0, NULL }, }; static void cpu_6xx_print_cacheinfo(u_int, uint16_t); static int cpu_feature_bit(SYSCTL_HANDLER_ARGS); static char model[64]; SYSCTL_STRING(_hw, HW_MODEL, model, CTLFLAG_RD, model, 0, ""); int cpu_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU; int cpu_features2 = 0; SYSCTL_OPAQUE(_hw, OID_AUTO, cpu_features, CTLFLAG_RD, &cpu_features, sizeof(cpu_features), "IX", "PowerPC CPU features"); SYSCTL_OPAQUE(_hw, OID_AUTO, cpu_features2, CTLFLAG_RD, &cpu_features2, sizeof(cpu_features2), "IX", "PowerPC CPU features 2"); /* Provide some user-friendly aliases for bits in cpu_features */ SYSCTL_PROC(_hw, OID_AUTO, floatingpoint, CTLTYPE_INT | CTLFLAG_RD, 0, PPC_FEATURE_HAS_FPU, cpu_feature_bit, "I", "Floating point instructions executed in hardware"); SYSCTL_PROC(_hw, OID_AUTO, altivec, CTLTYPE_INT | CTLFLAG_RD, 0, PPC_FEATURE_HAS_ALTIVEC, cpu_feature_bit, "I", "CPU supports Altivec"); void cpu_setup(u_int cpuid) { u_int pvr, maj, min; uint16_t vers, rev, revfmt; uint64_t cps; const struct cputab *cp; const char *name; pvr = mfpvr(); vers = pvr >> 16; rev = pvr; switch (vers) { case MPC7410: min = (pvr >> 0) & 0xff; maj = min <= 4 ? 1 : 2; break; case FSL_E500v1: case FSL_E500v2: case FSL_E500mc: case FSL_E5500: maj = (pvr >> 4) & 0xf; min = (pvr >> 0) & 0xf; break; default: maj = (pvr >> 8) & 0xf; min = (pvr >> 0) & 0xf; } for (cp = models; cp->version != 0; cp++) { if (cp->version == vers) break; } revfmt = cp->revfmt; name = cp->name; if (rev == MPC750 && pvr == 15) { name = "Motorola MPC755"; revfmt = REVFMT_HEX; } strncpy(model, name, sizeof(model) - 1); printf("cpu%d: %s revision ", cpuid, name); switch (revfmt) { case REVFMT_MAJMIN: printf("%u.%u", maj, min); break; case REVFMT_HEX: printf("0x%04x", rev); break; case REVFMT_DEC: printf("%u", rev); break; } if (cpu_est_clockrate(0, &cps) == 0) printf(", %jd.%02jd MHz", cps / 1000000, (cps / 10000) % 100); printf("\n"); cpu_features |= cp->features; cpu_features2 |= cp->features2; printf("cpu%d: Features %b\n", cpuid, cpu_features, PPC_FEATURE_BITMASK); if (cpu_features2 != 0) printf("cpu%d: Features2 %b\n", cpuid, cpu_features2, PPC_FEATURE2_BITMASK); /* * Configure CPU */ if (cp->cpu_setup != NULL) cp->cpu_setup(cpuid, vers); } /* Get current clock frequency for the given cpu id. */ int cpu_est_clockrate(int cpu_id, uint64_t *cps) { uint16_t vers; register_t msr; phandle_t cpu, dev, root; int res = 0; char buf[8]; vers = mfpvr() >> 16; msr = mfmsr(); mtmsr(msr & ~PSL_EE); switch (vers) { case MPC7450: case MPC7455: case MPC7457: case MPC750: case IBM750FX: case MPC7400: case MPC7410: case MPC7447A: case MPC7448: mtspr(SPR_MMCR0, SPR_MMCR0_FC); mtspr(SPR_PMC1, 0); mtspr(SPR_MMCR0, SPR_MMCR0_PMC1SEL(PMCN_CYCLES)); DELAY(1000); *cps = (mfspr(SPR_PMC1) * 1000) + 4999; mtspr(SPR_MMCR0, SPR_MMCR0_FC); mtmsr(msr); return (0); case IBM970: case IBM970FX: case IBM970MP: isync(); mtspr(SPR_970MMCR0, SPR_MMCR0_FC); isync(); mtspr(SPR_970MMCR1, 0); mtspr(SPR_970MMCRA, 0); mtspr(SPR_970PMC1, 0); mtspr(SPR_970MMCR0, SPR_970MMCR0_PMC1SEL(PMC970N_CYCLES)); isync(); DELAY(1000); powerpc_sync(); mtspr(SPR_970MMCR0, SPR_MMCR0_FC); *cps = (mfspr(SPR_970PMC1) * 1000) + 4999; mtmsr(msr); return (0); default: root = OF_peer(0); if (root == 0) return (ENXIO); dev = OF_child(root); while (dev != 0) { res = OF_getprop(dev, "name", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpus") == 0) break; dev = OF_peer(dev); } cpu = OF_child(dev); while (cpu != 0) { res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpu") == 0) break; cpu = OF_peer(cpu); } if (cpu == 0) return (ENOENT); if (OF_getprop(cpu, "ibm,extended-clock-frequency", cps, sizeof(*cps)) >= 0) { return (0); } else if (OF_getprop(cpu, "clock-frequency", cps, sizeof(cell_t)) >= 0) { *cps >>= 32; return (0); } else { return (ENOENT); } } } void cpu_6xx_setup(int cpuid, uint16_t vers) { register_t hid0, pvr; const char *bitmask; hid0 = mfspr(SPR_HID0); pvr = mfpvr(); /* * Configure power-saving mode. */ switch (vers) { case MPC603: case MPC603e: case MPC603ev: case MPC604ev: case MPC750: case IBM750FX: case MPC7400: case MPC7410: case MPC8240: case MPC8245: /* Select DOZE mode. */ hid0 &= ~(HID0_DOZE | HID0_NAP | HID0_SLEEP); hid0 |= HID0_DOZE | HID0_DPM; powerpc_pow_enabled = 1; break; case MPC7448: case MPC7447A: case MPC7457: case MPC7455: case MPC7450: /* Enable the 7450 branch caches */ hid0 |= HID0_SGE | HID0_BTIC; hid0 |= HID0_LRSTK | HID0_FOLD | HID0_BHT; /* Disable BTIC on 7450 Rev 2.0 or earlier and on 7457 */ if (((pvr >> 16) == MPC7450 && (pvr & 0xFFFF) <= 0x0200) || (pvr >> 16) == MPC7457) hid0 &= ~HID0_BTIC; /* Select NAP mode. */ hid0 &= ~(HID0_DOZE | HID0_NAP | HID0_SLEEP); hid0 |= HID0_NAP | HID0_DPM; powerpc_pow_enabled = 1; break; default: /* No power-saving mode is available. */ ; } switch (vers) { case IBM750FX: case MPC750: hid0 &= ~HID0_DBP; /* XXX correct? */ hid0 |= HID0_EMCP | HID0_BTIC | HID0_SGE | HID0_BHT; break; case MPC7400: case MPC7410: hid0 &= ~HID0_SPD; hid0 |= HID0_EMCP | HID0_BTIC | HID0_SGE | HID0_BHT; hid0 |= HID0_EIEC; break; } mtspr(SPR_HID0, hid0); if (bootverbose) cpu_6xx_print_cacheinfo(cpuid, vers); switch (vers) { case MPC7447A: case MPC7448: case MPC7450: case MPC7455: case MPC7457: bitmask = HID0_7450_BITMASK; break; default: bitmask = HID0_BITMASK; break; } printf("cpu%d: HID0 %b\n", cpuid, (int)hid0, bitmask); if (cpu_idle_hook == NULL) cpu_idle_hook = cpu_idle_60x; } static void cpu_6xx_print_cacheinfo(u_int cpuid, uint16_t vers) { register_t hid; hid = mfspr(SPR_HID0); printf("cpu%u: ", cpuid); printf("L1 I-cache %sabled, ", (hid & HID0_ICE) ? "en" : "dis"); printf("L1 D-cache %sabled\n", (hid & HID0_DCE) ? "en" : "dis"); printf("cpu%u: ", cpuid); if (mfspr(SPR_L2CR) & L2CR_L2E) { switch (vers) { case MPC7450: case MPC7455: case MPC7457: printf("256KB L2 cache, "); if (mfspr(SPR_L3CR) & L3CR_L3E) printf("%cMB L3 backside cache", mfspr(SPR_L3CR) & L3CR_L3SIZ ? '2' : '1'); else printf("L3 cache disabled"); printf("\n"); break; case IBM750FX: printf("512KB L2 cache\n"); break; default: switch (mfspr(SPR_L2CR) & L2CR_L2SIZ) { case L2SIZ_256K: printf("256KB "); break; case L2SIZ_512K: printf("512KB "); break; case L2SIZ_1M: printf("1MB "); break; } printf("write-%s", (mfspr(SPR_L2CR) & L2CR_L2WT) ? "through" : "back"); if (mfspr(SPR_L2CR) & L2CR_L2PE) printf(", with parity"); printf(" backside cache\n"); break; } } else printf("L2 cache disabled\n"); } static void cpu_booke_setup(int cpuid, uint16_t vers) { #ifdef BOOKE_E500 register_t hid0; const char *bitmask; hid0 = mfspr(SPR_HID0); switch (vers) { case FSL_E500mc: bitmask = HID0_E500MC_BITMASK; break; case FSL_E5500: case FSL_E6500: bitmask = HID0_E5500_BITMASK; break; case FSL_E500v1: case FSL_E500v2: /* Only e500v1/v2 support HID0 power management setup. */ /* Programe power-management mode. */ hid0 &= ~(HID0_DOZE | HID0_NAP | HID0_SLEEP); hid0 |= HID0_DOZE; mtspr(SPR_HID0, hid0); default: bitmask = HID0_E500_BITMASK; break; } printf("cpu%d: HID0 %b\n", cpuid, (int)hid0, bitmask); #endif if (cpu_idle_hook == NULL) cpu_idle_hook = cpu_idle_booke; } static void cpu_970_setup(int cpuid, uint16_t vers) { #ifdef AIM uint32_t hid0_hi, hid0_lo; __asm __volatile ("mfspr %0,%2; clrldi %1,%0,32; srdi %0,%0,32;" : "=r" (hid0_hi), "=r" (hid0_lo) : "K" (SPR_HID0)); /* Configure power-saving mode */ switch (vers) { case IBM970MP: hid0_hi |= (HID0_DEEPNAP | HID0_NAP | HID0_DPM); hid0_hi &= ~HID0_DOZE; break; default: hid0_hi |= (HID0_NAP | HID0_DPM); hid0_hi &= ~(HID0_DOZE | HID0_DEEPNAP); break; } powerpc_pow_enabled = 1; __asm __volatile (" \ sync; isync; \ sldi %0,%0,32; or %0,%0,%1; \ mtspr %2, %0; \ mfspr %0, %2; mfspr %0, %2; mfspr %0, %2; \ mfspr %0, %2; mfspr %0, %2; mfspr %0, %2; \ sync; isync" :: "r" (hid0_hi), "r"(hid0_lo), "K" (SPR_HID0)); __asm __volatile ("mfspr %0,%1; srdi %0,%0,32;" : "=r" (hid0_hi) : "K" (SPR_HID0)); printf("cpu%d: HID0 %b\n", cpuid, (int)(hid0_hi), HID0_970_BITMASK); #endif cpu_idle_hook = cpu_idle_60x; } static int cpu_feature_bit(SYSCTL_HANDLER_ARGS) { int result; result = (cpu_features & arg2) ? 1 : 0; return (sysctl_handle_int(oidp, &result, 0, req)); } void cpu_idle(int busy) { sbintime_t sbt = -1; #ifdef INVARIANTS if ((mfmsr() & PSL_EE) != PSL_EE) { struct thread *td = curthread; printf("td msr %#lx\n", (u_long)td->td_md.md_saved_msr); panic("ints disabled in idleproc!"); } #endif CTR2(KTR_SPARE2, "cpu_idle(%d) at %d", busy, curcpu); if (cpu_idle_hook != NULL) { if (!busy) { critical_enter(); sbt = cpu_idleclock(); } cpu_idle_hook(sbt); if (!busy) { cpu_activeclock(); critical_exit(); } } CTR2(KTR_SPARE2, "cpu_idle(%d) at %d done", busy, curcpu); } static void cpu_idle_60x(sbintime_t sbt) { register_t msr; uint16_t vers; if (!powerpc_pow_enabled) return; msr = mfmsr(); vers = mfpvr() >> 16; #ifdef AIM switch (vers) { case IBM970: case IBM970FX: case IBM970MP: case MPC7447A: case MPC7448: case MPC7450: case MPC7455: case MPC7457: __asm __volatile("\ dssall; sync; mtmsr %0; isync" :: "r"(msr | PSL_POW)); break; default: powerpc_sync(); mtmsr(msr | PSL_POW); isync(); break; } #endif } static void cpu_idle_booke(sbintime_t sbt) { #ifdef BOOKE_E500 platform_cpu_idle(PCPU_GET(cpuid)); #endif } Index: head/sys/powerpc/powerpc/db_hwwatch.c =================================================================== --- head/sys/powerpc/powerpc/db_hwwatch.c (revision 326260) +++ head/sys/powerpc/powerpc/db_hwwatch.c (revision 326261) @@ -1,54 +1,56 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2002 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include int db_md_set_watchpoint(db_expr_t addr, db_expr_t size) { return (0); } int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size) { return (0); } void db_md_list_watchpoints(void) { } Index: head/sys/powerpc/powerpc/dump_machdep.c =================================================================== --- head/sys/powerpc/powerpc/dump_machdep.c (revision 326260) +++ head/sys/powerpc/powerpc/dump_machdep.c (revision 326261) @@ -1,38 +1,40 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2002 Marcel Moolenaar * 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$"); #include #include #include #include #include int do_minidump = 1; SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RWTUN, &do_minidump, 0, "Enable mini crash dumps"); Index: head/sys/powerpc/powerpc/elf32_machdep.c =================================================================== --- head/sys/powerpc/powerpc/elf32_machdep.c (revision 326260) +++ head/sys/powerpc/powerpc/elf32_machdep.c (revision 326261) @@ -1,321 +1,323 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #define __ELF_WORD_SIZE 32 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __powerpc64__ #include #include extern const char *freebsd32_syscallnames[]; #endif struct sysentvec elf32_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, #ifdef __powerpc64__ .sv_table = freebsd32_sysent, #else .sv_table = sysent, #endif .sv_mask = 0, .sv_errsize = 0, .sv_errtbl = NULL, .sv_transtrap = NULL, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = sendsig, .sv_sigcode = sigcode32, .sv_szsigcode = &szsigcode32, .sv_name = "FreeBSD ELF32", .sv_coredump = __elfN(coredump), .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_pagesize = PAGE_SIZE, .sv_minuser = VM_MIN_ADDRESS, .sv_stackprot = VM_PROT_ALL, #ifdef __powerpc64__ .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = FREEBSD32_USRSTACK, .sv_psstrings = FREEBSD32_PS_STRINGS, .sv_copyout_strings = freebsd32_copyout_strings, .sv_setregs = ppc32_setregs, .sv_syscallnames = freebsd32_syscallnames, #else .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs, .sv_syscallnames = syscallnames, #endif .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_FREEBSD | SV_ILP32 | SV_SHP, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_shared_page_base = FREEBSD32_SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, }; INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec); static Elf32_Brandinfo freebsd_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_PPC, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf32_freebsd_sysvec, #ifdef __powerpc64__ .interp_newpath = "/libexec/ld-elf32.so.1", #else .interp_newpath = NULL, #endif .brand_note = &elf32_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_FIRST, (sysinit_cfunc_t) elf32_insert_brand_entry, &freebsd_brand_info); static Elf32_Brandinfo freebsd_brand_oinfo = { .brand = ELFOSABI_FREEBSD, .machine = EM_PPC, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/usr/libexec/ld-elf.so.1", .sysvec = &elf32_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf32_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(oelf32, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf32_insert_brand_entry, &freebsd_brand_oinfo); void elf_reloc_self(Elf_Dyn *dynp, Elf_Addr relocbase); void elf32_dump_thread(struct thread *td, void *dst, size_t *off) { size_t len; struct pcb *pcb; len = 0; pcb = td->td_pcb; if (pcb->pcb_flags & PCB_VEC) { save_vec_nodrop(td); if (dst != NULL) { len += elf32_populate_note(NT_PPC_VMX, &pcb->pcb_vec, dst, sizeof(pcb->pcb_vec), NULL); } else len += elf32_populate_note(NT_PPC_VMX, NULL, NULL, sizeof(pcb->pcb_vec), NULL); } *off = len; } #ifndef __powerpc64__ /* Process one elf relocation with addend. */ static int elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, int local, elf_lookup_fn lookup) { Elf_Addr *where; Elf_Half *hwhere; Elf_Addr addr; Elf_Addr addend; Elf_Word rtype, symidx; const Elf_Rela *rela; int error; switch (type) { case ELF_RELOC_REL: panic("PPC only supports RELA relocations"); break; case ELF_RELOC_RELA: rela = (const Elf_Rela *)data; where = (Elf_Addr *) ((uintptr_t)relocbase + rela->r_offset); hwhere = (Elf_Half *) ((uintptr_t)relocbase + rela->r_offset); addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); break; default: panic("elf_reloc: unknown relocation mode %d\n", type); } switch (rtype) { case R_PPC_NONE: break; case R_PPC_ADDR32: /* word32 S + A */ error = lookup(lf, symidx, 1, &addr); if (error != 0) return -1; *where = elf_relocaddr(lf, addr + addend); break; case R_PPC_ADDR16_LO: /* #lo(S) */ error = lookup(lf, symidx, 1, &addr); if (error != 0) return -1; /* * addend values are sometimes relative to sections * (i.e. .rodata) in rela, where in reality they * are relative to relocbase. Detect this condition. */ if (addr > relocbase && addr <= (relocbase + addend)) addr = relocbase; addr = elf_relocaddr(lf, addr + addend); *hwhere = addr & 0xffff; break; case R_PPC_ADDR16_HA: /* #ha(S) */ error = lookup(lf, symidx, 1, &addr); if (error != 0) return -1; /* * addend values are sometimes relative to sections * (i.e. .rodata) in rela, where in reality they * are relative to relocbase. Detect this condition. */ if (addr > relocbase && addr <= (relocbase + addend)) addr = relocbase; addr = elf_relocaddr(lf, addr + addend); *hwhere = ((addr >> 16) + ((addr & 0x8000) ? 1 : 0)) & 0xffff; break; case R_PPC_RELATIVE: /* word32 B + A */ *where = elf_relocaddr(lf, relocbase + addend); break; default: printf("kldload: unexpected relocation type %d\n", (int) rtype); return -1; } return(0); } void elf_reloc_self(Elf_Dyn *dynp, Elf_Addr relocbase) { Elf_Rela *rela = NULL, *relalim; Elf_Addr relasz = 0; Elf_Addr *where; /* * Extract the rela/relasz values from the dynamic section */ for (; dynp->d_tag != DT_NULL; dynp++) { switch (dynp->d_tag) { case DT_RELA: rela = (Elf_Rela *)(relocbase+dynp->d_un.d_ptr); break; case DT_RELASZ: relasz = dynp->d_un.d_val; break; } } /* * Relocate these values */ relalim = (Elf_Rela *)((caddr_t)rela + relasz); for (; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) != R_PPC_RELATIVE) continue; where = (Elf_Addr *)(relocbase + rela->r_offset); *where = (Elf_Addr)(relocbase + rela->r_addend); } } int elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup)); } int elf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup)); } int elf_cpu_load_file(linker_file_t lf) { /* Only sync the cache for non-kernel modules */ if (lf->id != 1) __syncicache(lf->address, lf->size); return (0); } int elf_cpu_unload_file(linker_file_t lf __unused) { return (0); } #endif Index: head/sys/powerpc/powerpc/elf64_machdep.c =================================================================== --- head/sys/powerpc/powerpc/elf64_machdep.c (revision 326260) +++ head/sys/powerpc/powerpc/elf64_machdep.c (revision 326261) @@ -1,375 +1,377 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void exec_setregs_funcdesc(struct thread *td, struct image_params *imgp, u_long stack); struct sysentvec elf64_freebsd_sysvec_v1 = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, .sv_mask = 0, .sv_errsize = 0, .sv_errtbl = NULL, .sv_transtrap = NULL, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = sendsig, .sv_sigcode = sigcode64, .sv_szsigcode = &szsigcode64, .sv_name = "FreeBSD ELF64", .sv_coredump = __elfN(coredump), .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_pagesize = PAGE_SIZE, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_stackprot = VM_PROT_ALL, .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs_funcdesc, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_FREEBSD | SV_LP64 | SV_SHP, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, }; INIT_SYSENTVEC(elf64_sysvec_v1, &elf64_freebsd_sysvec_v1); struct sysentvec elf64_freebsd_sysvec_v2 = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, .sv_mask = 0, .sv_errsize = 0, .sv_errtbl = NULL, .sv_transtrap = NULL, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = sendsig, .sv_sigcode = sigcode64_elfv2, .sv_szsigcode = &szsigcode64_elfv2, .sv_name = "FreeBSD ELF64 V2", .sv_coredump = __elfN(coredump), .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_pagesize = PAGE_SIZE, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_stackprot = VM_PROT_ALL, .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_FREEBSD | SV_LP64 | SV_SHP, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, .sv_thread_detach = NULL, }; INIT_SYSENTVEC(elf64_sysvec_v2, &elf64_freebsd_sysvec_v2); static boolean_t ppc64_elfv1_header_match(struct image_params *params); static boolean_t ppc64_elfv2_header_match(struct image_params *params); static Elf64_Brandinfo freebsd_brand_info_elfv1 = { .brand = ELFOSABI_FREEBSD, .machine = EM_PPC64, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf64_freebsd_sysvec_v1, .interp_newpath = NULL, .brand_note = &elf64_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE, .header_supported = &ppc64_elfv1_header_match }; SYSINIT(elf64v1, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf64_insert_brand_entry, &freebsd_brand_info_elfv1); static Elf64_Brandinfo freebsd_brand_info_elfv2 = { .brand = ELFOSABI_FREEBSD, .machine = EM_PPC64, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf64_freebsd_sysvec_v2, .interp_newpath = NULL, .brand_note = &elf64_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE, .header_supported = &ppc64_elfv2_header_match }; SYSINIT(elf64v2, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf64_insert_brand_entry, &freebsd_brand_info_elfv2); static Elf64_Brandinfo freebsd_brand_oinfo = { .brand = ELFOSABI_FREEBSD, .machine = EM_PPC64, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/usr/libexec/ld-elf.so.1", .sysvec = &elf64_freebsd_sysvec_v1, .interp_newpath = NULL, .brand_note = &elf64_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE, .header_supported = &ppc64_elfv1_header_match }; SYSINIT(oelf64, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf64_insert_brand_entry, &freebsd_brand_oinfo); void elf_reloc_self(Elf_Dyn *dynp, Elf_Addr relocbase); static boolean_t ppc64_elfv1_header_match(struct image_params *params) { const Elf64_Ehdr *hdr = (const Elf64_Ehdr *)params->image_header; int abi = (hdr->e_flags & 3); return (abi == 0 || abi == 1); } static boolean_t ppc64_elfv2_header_match(struct image_params *params) { const Elf64_Ehdr *hdr = (const Elf64_Ehdr *)params->image_header; int abi = (hdr->e_flags & 3); return (abi == 2); } static void exec_setregs_funcdesc(struct thread *td, struct image_params *imgp, u_long stack) { struct trapframe *tf; register_t entry_desc[3]; tf = trapframe(td); exec_setregs(td, imgp, stack); /* * For 64-bit ELFv1, we need to disentangle the function * descriptor * * 0. entry point * 1. TOC value (r2) * 2. Environment pointer (r11) */ (void)copyin((void *)imgp->entry_addr, entry_desc, sizeof(entry_desc)); tf->srr0 = entry_desc[0] + imgp->reloc_base; tf->fixreg[2] = entry_desc[1] + imgp->reloc_base; tf->fixreg[11] = entry_desc[2] + imgp->reloc_base; } void elf64_dump_thread(struct thread *td, void *dst, size_t *off) { size_t len; struct pcb *pcb; len = 0; pcb = td->td_pcb; if (pcb->pcb_flags & PCB_VEC) { save_vec_nodrop(td); if (dst != NULL) { len += elf64_populate_note(NT_PPC_VMX, &pcb->pcb_vec, dst, sizeof(pcb->pcb_vec), NULL); } else len += elf64_populate_note(NT_PPC_VMX, NULL, NULL, sizeof(pcb->pcb_vec), NULL); } *off = len; } /* Process one elf relocation with addend. */ static int elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, int local, elf_lookup_fn lookup) { Elf_Addr *where; Elf_Addr addr; Elf_Addr addend; Elf_Word rtype, symidx; const Elf_Rela *rela; int error; switch (type) { case ELF_RELOC_REL: panic("PPC only supports RELA relocations"); break; case ELF_RELOC_RELA: rela = (const Elf_Rela *)data; where = (Elf_Addr *) (relocbase + rela->r_offset); addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); break; default: panic("elf_reloc: unknown relocation mode %d\n", type); } switch (rtype) { case R_PPC_NONE: break; case R_PPC64_ADDR64: /* doubleword64 S + A */ error = lookup(lf, symidx, 1, &addr); if (error != 0) return -1; addr += addend; *where = addr; break; case R_PPC_RELATIVE: /* doubleword64 B + A */ *where = elf_relocaddr(lf, relocbase + addend); break; case R_PPC_JMP_SLOT: /* function descriptor copy */ lookup(lf, symidx, 1, &addr); #if !defined(_CALL_ELF) || _CALL_ELF == 1 memcpy(where, (Elf_Addr *)addr, 3*sizeof(Elf_Addr)); #else memcpy(where, (Elf_Addr *)addr, sizeof(Elf_Addr)); #endif __asm __volatile("dcbst 0,%0; sync" :: "r"(where) : "memory"); break; default: printf("kldload: unexpected relocation type %d\n", (int) rtype); return -1; } return(0); } void elf_reloc_self(Elf_Dyn *dynp, Elf_Addr relocbase) { Elf_Rela *rela = NULL, *relalim; Elf_Addr relasz = 0; Elf_Addr *where; /* * Extract the rela/relasz values from the dynamic section */ for (; dynp->d_tag != DT_NULL; dynp++) { switch (dynp->d_tag) { case DT_RELA: rela = (Elf_Rela *)(relocbase+dynp->d_un.d_ptr); break; case DT_RELASZ: relasz = dynp->d_un.d_val; break; } } /* * Relocate these values */ relalim = (Elf_Rela *)((caddr_t)rela + relasz); for (; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) != R_PPC_RELATIVE) continue; where = (Elf_Addr *)(relocbase + rela->r_offset); *where = (Elf_Addr)(relocbase + rela->r_addend); } } int elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup)); } int elf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup)); } int elf_cpu_load_file(linker_file_t lf) { /* Only sync the cache for non-kernel modules */ if (lf->id != 1) __syncicache(lf->address, lf->size); return (0); } int elf_cpu_unload_file(linker_file_t lf __unused) { return (0); } Index: head/sys/powerpc/powerpc/exec_machdep.c =================================================================== --- head/sys/powerpc/powerpc/exec_machdep.c (revision 326260) +++ head/sys/powerpc/powerpc/exec_machdep.c (revision 326261) @@ -1,1072 +1,1074 @@ /*- + * SPDX-License-Identifier: BSD-4-Clause AND BSD-2-Clause-FreeBSD + * * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. */ /*- * Copyright (C) 2001 Benno Rice * 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 Benno Rice ``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 TOOLS GMBH 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. * $NetBSD: machdep.c,v 1.74.2.1 2000/11/01 16:13:48 tv Exp $ */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_fpu_emu.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 #ifdef FPU_EMU #include #endif #ifdef COMPAT_FREEBSD32 #include #include #include typedef struct __ucontext32 { sigset_t uc_sigmask; mcontext32_t uc_mcontext; uint32_t uc_link; struct sigaltstack32 uc_stack; uint32_t uc_flags; uint32_t __spare__[4]; } ucontext32_t; struct sigframe32 { ucontext32_t sf_uc; struct siginfo32 sf_si; }; static int grab_mcontext32(struct thread *td, mcontext32_t *, int flags); #endif static int grab_mcontext(struct thread *, mcontext_t *, int); void sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) { struct trapframe *tf; struct sigacts *psp; struct sigframe sf; struct thread *td; struct proc *p; #ifdef COMPAT_FREEBSD32 struct siginfo32 siginfo32; struct sigframe32 sf32; #endif size_t sfpsize; caddr_t sfp, usfp; int oonstack, rndfsize; int sig; int code; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); tf = td->td_frame; oonstack = sigonstack(tf->fixreg[1]); /* * Fill siginfo structure. */ ksi->ksi_info.si_signo = ksi->ksi_signo; ksi->ksi_info.si_addr = (void *)((tf->exc == EXC_DSI) ? tf->dar : tf->srr0); #ifdef COMPAT_FREEBSD32 if (SV_PROC_FLAG(p, SV_ILP32)) { siginfo_to_siginfo32(&ksi->ksi_info, &siginfo32); sig = siginfo32.si_signo; code = siginfo32.si_code; sfp = (caddr_t)&sf32; sfpsize = sizeof(sf32); rndfsize = roundup(sizeof(sf32), 16); /* * Save user context */ memset(&sf32, 0, sizeof(sf32)); grab_mcontext32(td, &sf32.sf_uc.uc_mcontext, 0); sf32.sf_uc.uc_sigmask = *mask; sf32.sf_uc.uc_stack.ss_sp = (uintptr_t)td->td_sigstk.ss_sp; sf32.sf_uc.uc_stack.ss_size = (uint32_t)td->td_sigstk.ss_size; sf32.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf32.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; } else { #endif sig = ksi->ksi_signo; code = ksi->ksi_code; sfp = (caddr_t)&sf; sfpsize = sizeof(sf); #ifdef __powerpc64__ /* * 64-bit PPC defines a 288 byte scratch region * below the stack. */ rndfsize = 288 + roundup(sizeof(sf), 48); #else rndfsize = roundup(sizeof(sf), 16); #endif /* * Save user context */ memset(&sf, 0, sizeof(sf)); grab_mcontext(td, &sf.sf_uc.uc_mcontext, 0); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = td->td_sigstk; sf.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; #ifdef COMPAT_FREEBSD32 } #endif CTR4(KTR_SIG, "sendsig: td=%p (%s) catcher=%p sig=%d", td, p->p_comm, catcher, sig); /* * Allocate and validate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { usfp = (void *)(((uintptr_t)td->td_sigstk.ss_sp + td->td_sigstk.ss_size - rndfsize) & ~0xFul); } else { usfp = (void *)((tf->fixreg[1] - rndfsize) & ~0xFul); } /* * Save the floating-point state, if necessary, then copy it. */ /* XXX */ /* * Set up the registers to return to sigcode. * * r1/sp - sigframe ptr * lr - sig function, dispatched to by blrl in trampoline * r3 - sig number * r4 - SIGINFO ? &siginfo : exception code * r5 - user context * srr0 - trampoline function addr */ tf->lr = (register_t)catcher; tf->fixreg[1] = (register_t)usfp; tf->fixreg[FIRSTARG] = sig; #ifdef COMPAT_FREEBSD32 tf->fixreg[FIRSTARG+2] = (register_t)usfp + ((SV_PROC_FLAG(p, SV_ILP32)) ? offsetof(struct sigframe32, sf_uc) : offsetof(struct sigframe, sf_uc)); #else tf->fixreg[FIRSTARG+2] = (register_t)usfp + offsetof(struct sigframe, sf_uc); #endif if (SIGISMEMBER(psp->ps_siginfo, sig)) { /* * Signal handler installed with SA_SIGINFO. */ #ifdef COMPAT_FREEBSD32 if (SV_PROC_FLAG(p, SV_ILP32)) { sf32.sf_si = siginfo32; tf->fixreg[FIRSTARG+1] = (register_t)usfp + offsetof(struct sigframe32, sf_si); sf32.sf_si = siginfo32; } else { #endif tf->fixreg[FIRSTARG+1] = (register_t)usfp + offsetof(struct sigframe, sf_si); sf.sf_si = ksi->ksi_info; #ifdef COMPAT_FREEBSD32 } #endif } else { /* Old FreeBSD-style arguments. */ tf->fixreg[FIRSTARG+1] = code; tf->fixreg[FIRSTARG+3] = (tf->exc == EXC_DSI) ? tf->dar : tf->srr0; } mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); tf->srr0 = (register_t)p->p_sysent->sv_sigcode_base; /* * copy the frame out to userland. */ if (copyout(sfp, usfp, sfpsize) != 0) { /* * Process has trashed its stack. Kill it. */ CTR2(KTR_SIG, "sendsig: sigexit td=%p sfp=%p", td, sfp); PROC_LOCK(p); sigexit(td, SIGILL); } CTR3(KTR_SIG, "sendsig: return td=%p pc=%#x sp=%#x", td, tf->srr0, tf->fixreg[1]); PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } int sys_sigreturn(struct thread *td, struct sigreturn_args *uap) { ucontext_t uc; int error; CTR2(KTR_SIG, "sigreturn: td=%p ucp=%p", td, uap->sigcntxp); if (copyin(uap->sigcntxp, &uc, sizeof(uc)) != 0) { CTR1(KTR_SIG, "sigreturn: efault td=%p", td); return (EFAULT); } error = set_mcontext(td, &uc.uc_mcontext); if (error != 0) return (error); kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); CTR3(KTR_SIG, "sigreturn: return td=%p pc=%#x sp=%#x", td, uc.uc_mcontext.mc_srr0, uc.uc_mcontext.mc_gpr[1]); return (EJUSTRETURN); } #ifdef COMPAT_FREEBSD4 int freebsd4_sigreturn(struct thread *td, struct freebsd4_sigreturn_args *uap) { return sys_sigreturn(td, (struct sigreturn_args *)uap); } #endif /* * Construct a PCB from a trapframe. This is called from kdb_trap() where * we want to start a backtrace from the function that caused us to enter * the debugger. We have the context in the trapframe, but base the trace * on the PCB. The PCB doesn't have to be perfect, as long as it contains * enough for a backtrace. */ void makectx(struct trapframe *tf, struct pcb *pcb) { pcb->pcb_lr = tf->srr0; pcb->pcb_sp = tf->fixreg[1]; } /* * get_mcontext/sendsig helper routine that doesn't touch the * proc lock */ static int grab_mcontext(struct thread *td, mcontext_t *mcp, int flags) { struct pcb *pcb; int i; pcb = td->td_pcb; memset(mcp, 0, sizeof(mcontext_t)); mcp->mc_vers = _MC_VERSION; mcp->mc_flags = 0; memcpy(&mcp->mc_frame, td->td_frame, sizeof(struct trapframe)); if (flags & GET_MC_CLEAR_RET) { mcp->mc_gpr[3] = 0; mcp->mc_gpr[4] = 0; } /* * This assumes that floating-point context is *not* lazy, * so if the thread has used FP there would have been a * FP-unavailable exception that would have set things up * correctly. */ if (pcb->pcb_flags & PCB_FPREGS) { if (pcb->pcb_flags & PCB_FPU) { KASSERT(td == curthread, ("get_mcontext: fp save not curthread")); critical_enter(); save_fpu(td); critical_exit(); } mcp->mc_flags |= _MC_FP_VALID; memcpy(&mcp->mc_fpscr, &pcb->pcb_fpu.fpscr, sizeof(double)); for (i = 0; i < 32; i++) memcpy(&mcp->mc_fpreg[i], &pcb->pcb_fpu.fpr[i].fpr, sizeof(double)); } if (pcb->pcb_flags & PCB_VSX) { for (i = 0; i < 32; i++) memcpy(&mcp->mc_vsxfpreg[i], &pcb->pcb_fpu.fpr[i].vsr[2], sizeof(double)); } /* * Repeat for Altivec context */ if (pcb->pcb_flags & PCB_VEC) { KASSERT(td == curthread, ("get_mcontext: fp save not curthread")); critical_enter(); save_vec(td); critical_exit(); mcp->mc_flags |= _MC_AV_VALID; mcp->mc_vscr = pcb->pcb_vec.vscr; mcp->mc_vrsave = pcb->pcb_vec.vrsave; memcpy(mcp->mc_avec, pcb->pcb_vec.vr, sizeof(mcp->mc_avec)); } mcp->mc_len = sizeof(*mcp); return (0); } int get_mcontext(struct thread *td, mcontext_t *mcp, int flags) { int error; error = grab_mcontext(td, mcp, flags); if (error == 0) { PROC_LOCK(curthread->td_proc); mcp->mc_onstack = sigonstack(td->td_frame->fixreg[1]); PROC_UNLOCK(curthread->td_proc); } return (error); } int set_mcontext(struct thread *td, mcontext_t *mcp) { struct pcb *pcb; struct trapframe *tf; register_t tls; int i; pcb = td->td_pcb; tf = td->td_frame; if (mcp->mc_vers != _MC_VERSION || mcp->mc_len != sizeof(*mcp)) return (EINVAL); /* * Don't let the user set privileged MSR bits */ if ((mcp->mc_srr1 & PSL_USERSTATIC) != (tf->srr1 & PSL_USERSTATIC)) { return (EINVAL); } /* Copy trapframe, preserving TLS pointer across context change */ if (SV_PROC_FLAG(td->td_proc, SV_LP64)) tls = tf->fixreg[13]; else tls = tf->fixreg[2]; memcpy(tf, mcp->mc_frame, sizeof(mcp->mc_frame)); if (SV_PROC_FLAG(td->td_proc, SV_LP64)) tf->fixreg[13] = tls; else tf->fixreg[2] = tls; if (mcp->mc_flags & _MC_FP_VALID) { /* enable_fpu() will happen lazily on a fault */ pcb->pcb_flags |= PCB_FPREGS; memcpy(&pcb->pcb_fpu.fpscr, &mcp->mc_fpscr, sizeof(double)); bzero(pcb->pcb_fpu.fpr, sizeof(pcb->pcb_fpu.fpr)); for (i = 0; i < 32; i++) { memcpy(&pcb->pcb_fpu.fpr[i].fpr, &mcp->mc_fpreg[i], sizeof(double)); memcpy(&pcb->pcb_fpu.fpr[i].vsr[2], &mcp->mc_vsxfpreg[i], sizeof(double)); } } if (mcp->mc_flags & _MC_AV_VALID) { if ((pcb->pcb_flags & PCB_VEC) != PCB_VEC) { critical_enter(); enable_vec(td); critical_exit(); } pcb->pcb_vec.vscr = mcp->mc_vscr; pcb->pcb_vec.vrsave = mcp->mc_vrsave; memcpy(pcb->pcb_vec.vr, mcp->mc_avec, sizeof(mcp->mc_avec)); } return (0); } /* * Set set up registers on exec. */ void exec_setregs(struct thread *td, struct image_params *imgp, u_long stack) { struct trapframe *tf; register_t argc; tf = trapframe(td); bzero(tf, sizeof *tf); #ifdef __powerpc64__ tf->fixreg[1] = -roundup(-stack + 48, 16); #else tf->fixreg[1] = -roundup(-stack + 8, 16); #endif /* * Set up arguments for _start(): * _start(argc, argv, envp, obj, cleanup, ps_strings); * * Notes: * - obj and cleanup are the auxilliary and termination * vectors. They are fixed up by ld.elf_so. * - ps_strings is a NetBSD extention, and will be * ignored by executables which are strictly * compliant with the SVR4 ABI. */ /* Collect argc from the user stack */ argc = fuword((void *)stack); tf->fixreg[3] = argc; tf->fixreg[4] = stack + sizeof(register_t); tf->fixreg[5] = stack + (2 + argc)*sizeof(register_t); tf->fixreg[6] = 0; /* auxillary vector */ tf->fixreg[7] = 0; /* termination vector */ tf->fixreg[8] = (register_t)imgp->ps_strings; /* NetBSD extension */ tf->srr0 = imgp->entry_addr; #ifdef __powerpc64__ tf->fixreg[12] = imgp->entry_addr; #ifdef AIM tf->srr1 = PSL_SF | PSL_USERSET | PSL_FE_DFLT; if (mfmsr() & PSL_HV) tf->srr1 |= PSL_HV; #elif defined(BOOKE) tf->srr1 = PSL_CM | PSL_USERSET | PSL_FE_DFLT; #endif #else tf->srr1 = PSL_USERSET | PSL_FE_DFLT; #endif td->td_pcb->pcb_flags = 0; } #ifdef COMPAT_FREEBSD32 void ppc32_setregs(struct thread *td, struct image_params *imgp, u_long stack) { struct trapframe *tf; uint32_t argc; tf = trapframe(td); bzero(tf, sizeof *tf); tf->fixreg[1] = -roundup(-stack + 8, 16); argc = fuword32((void *)stack); tf->fixreg[3] = argc; tf->fixreg[4] = stack + sizeof(uint32_t); tf->fixreg[5] = stack + (2 + argc)*sizeof(uint32_t); tf->fixreg[6] = 0; /* auxillary vector */ tf->fixreg[7] = 0; /* termination vector */ tf->fixreg[8] = (register_t)imgp->ps_strings; /* NetBSD extension */ tf->srr0 = imgp->entry_addr; tf->srr1 = PSL_USERSET | PSL_FE_DFLT; #ifdef AIM tf->srr1 &= ~PSL_SF; if (mfmsr() & PSL_HV) tf->srr1 |= PSL_HV; #elif defined(BOOKE) tf->srr1 &= ~PSL_CM; #endif td->td_pcb->pcb_flags = 0; } #endif int fill_regs(struct thread *td, struct reg *regs) { struct trapframe *tf; tf = td->td_frame; memcpy(regs, tf, sizeof(struct reg)); return (0); } int fill_dbregs(struct thread *td, struct dbreg *dbregs) { /* No debug registers on PowerPC */ return (ENOSYS); } int fill_fpregs(struct thread *td, struct fpreg *fpregs) { struct pcb *pcb; int i; pcb = td->td_pcb; if ((pcb->pcb_flags & PCB_FPREGS) == 0) memset(fpregs, 0, sizeof(struct fpreg)); else { memcpy(&fpregs->fpscr, &pcb->pcb_fpu.fpscr, sizeof(double)); for (i = 0; i < 32; i++) memcpy(&fpregs->fpreg[i], &pcb->pcb_fpu.fpr[i].fpr, sizeof(double)); } return (0); } int set_regs(struct thread *td, struct reg *regs) { struct trapframe *tf; tf = td->td_frame; memcpy(tf, regs, sizeof(struct reg)); return (0); } int set_dbregs(struct thread *td, struct dbreg *dbregs) { /* No debug registers on PowerPC */ return (ENOSYS); } int set_fpregs(struct thread *td, struct fpreg *fpregs) { struct pcb *pcb; int i; pcb = td->td_pcb; pcb->pcb_flags |= PCB_FPREGS; memcpy(&pcb->pcb_fpu.fpscr, &fpregs->fpscr, sizeof(double)); for (i = 0; i < 32; i++) { memcpy(&pcb->pcb_fpu.fpr[i].fpr, &fpregs->fpreg[i], sizeof(double)); } return (0); } #ifdef COMPAT_FREEBSD32 int set_regs32(struct thread *td, struct reg32 *regs) { struct trapframe *tf; int i; tf = td->td_frame; for (i = 0; i < 32; i++) tf->fixreg[i] = regs->fixreg[i]; tf->lr = regs->lr; tf->cr = regs->cr; tf->xer = regs->xer; tf->ctr = regs->ctr; tf->srr0 = regs->pc; return (0); } int fill_regs32(struct thread *td, struct reg32 *regs) { struct trapframe *tf; int i; tf = td->td_frame; for (i = 0; i < 32; i++) regs->fixreg[i] = tf->fixreg[i]; regs->lr = tf->lr; regs->cr = tf->cr; regs->xer = tf->xer; regs->ctr = tf->ctr; regs->pc = tf->srr0; return (0); } static int grab_mcontext32(struct thread *td, mcontext32_t *mcp, int flags) { mcontext_t mcp64; int i, error; error = grab_mcontext(td, &mcp64, flags); if (error != 0) return (error); mcp->mc_vers = mcp64.mc_vers; mcp->mc_flags = mcp64.mc_flags; mcp->mc_onstack = mcp64.mc_onstack; mcp->mc_len = mcp64.mc_len; memcpy(mcp->mc_avec,mcp64.mc_avec,sizeof(mcp64.mc_avec)); memcpy(mcp->mc_av,mcp64.mc_av,sizeof(mcp64.mc_av)); for (i = 0; i < 42; i++) mcp->mc_frame[i] = mcp64.mc_frame[i]; memcpy(mcp->mc_fpreg,mcp64.mc_fpreg,sizeof(mcp64.mc_fpreg)); memcpy(mcp->mc_vsxfpreg,mcp64.mc_vsxfpreg,sizeof(mcp64.mc_vsxfpreg)); return (0); } static int get_mcontext32(struct thread *td, mcontext32_t *mcp, int flags) { int error; error = grab_mcontext32(td, mcp, flags); if (error == 0) { PROC_LOCK(curthread->td_proc); mcp->mc_onstack = sigonstack(td->td_frame->fixreg[1]); PROC_UNLOCK(curthread->td_proc); } return (error); } static int set_mcontext32(struct thread *td, mcontext32_t *mcp) { mcontext_t mcp64; int i, error; mcp64.mc_vers = mcp->mc_vers; mcp64.mc_flags = mcp->mc_flags; mcp64.mc_onstack = mcp->mc_onstack; mcp64.mc_len = mcp->mc_len; memcpy(mcp64.mc_avec,mcp->mc_avec,sizeof(mcp64.mc_avec)); memcpy(mcp64.mc_av,mcp->mc_av,sizeof(mcp64.mc_av)); for (i = 0; i < 42; i++) mcp64.mc_frame[i] = mcp->mc_frame[i]; mcp64.mc_srr1 |= (td->td_frame->srr1 & 0xFFFFFFFF00000000ULL); memcpy(mcp64.mc_fpreg,mcp->mc_fpreg,sizeof(mcp64.mc_fpreg)); memcpy(mcp64.mc_vsxfpreg,mcp->mc_vsxfpreg,sizeof(mcp64.mc_vsxfpreg)); error = set_mcontext(td, &mcp64); return (error); } #endif #ifdef COMPAT_FREEBSD32 int freebsd32_sigreturn(struct thread *td, struct freebsd32_sigreturn_args *uap) { ucontext32_t uc; int error; CTR2(KTR_SIG, "sigreturn: td=%p ucp=%p", td, uap->sigcntxp); if (copyin(uap->sigcntxp, &uc, sizeof(uc)) != 0) { CTR1(KTR_SIG, "sigreturn: efault td=%p", td); return (EFAULT); } error = set_mcontext32(td, &uc.uc_mcontext); if (error != 0) return (error); kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); CTR3(KTR_SIG, "sigreturn: return td=%p pc=%#x sp=%#x", td, uc.uc_mcontext.mc_srr0, uc.uc_mcontext.mc_gpr[1]); return (EJUSTRETURN); } /* * The first two fields of a ucontext_t are the signal mask and the machine * context. The next field is uc_link; we want to avoid destroying the link * when copying out contexts. */ #define UC32_COPY_SIZE offsetof(ucontext32_t, uc_link) int freebsd32_getcontext(struct thread *td, struct freebsd32_getcontext_args *uap) { ucontext32_t uc; int ret; if (uap->ucp == NULL) ret = EINVAL; else { get_mcontext32(td, &uc.uc_mcontext, GET_MC_CLEAR_RET); PROC_LOCK(td->td_proc); uc.uc_sigmask = td->td_sigmask; PROC_UNLOCK(td->td_proc); ret = copyout(&uc, uap->ucp, UC32_COPY_SIZE); } return (ret); } int freebsd32_setcontext(struct thread *td, struct freebsd32_setcontext_args *uap) { ucontext32_t uc; int ret; if (uap->ucp == NULL) ret = EINVAL; else { ret = copyin(uap->ucp, &uc, UC32_COPY_SIZE); if (ret == 0) { ret = set_mcontext32(td, &uc.uc_mcontext); if (ret == 0) { kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); } } } return (ret == 0 ? EJUSTRETURN : ret); } int freebsd32_swapcontext(struct thread *td, struct freebsd32_swapcontext_args *uap) { ucontext32_t uc; int ret; if (uap->oucp == NULL || uap->ucp == NULL) ret = EINVAL; else { get_mcontext32(td, &uc.uc_mcontext, GET_MC_CLEAR_RET); PROC_LOCK(td->td_proc); uc.uc_sigmask = td->td_sigmask; PROC_UNLOCK(td->td_proc); ret = copyout(&uc, uap->oucp, UC32_COPY_SIZE); if (ret == 0) { ret = copyin(uap->ucp, &uc, UC32_COPY_SIZE); if (ret == 0) { ret = set_mcontext32(td, &uc.uc_mcontext); if (ret == 0) { kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); } } } } return (ret == 0 ? EJUSTRETURN : ret); } #endif void cpu_set_syscall_retval(struct thread *td, int error) { struct proc *p; struct trapframe *tf; int fixup; if (error == EJUSTRETURN) return; p = td->td_proc; tf = td->td_frame; if (tf->fixreg[0] == SYS___syscall && (SV_PROC_FLAG(p, SV_ILP32))) { int code = tf->fixreg[FIRSTARG + 1]; if (p->p_sysent->sv_mask) code &= p->p_sysent->sv_mask; fixup = ( #if defined(COMPAT_FREEBSD6) && defined(SYS_freebsd6_lseek) code != SYS_freebsd6_lseek && #endif code != SYS_lseek) ? 1 : 0; } else fixup = 0; switch (error) { case 0: if (fixup) { /* * 64-bit return, 32-bit syscall. Fixup byte order */ tf->fixreg[FIRSTARG] = 0; tf->fixreg[FIRSTARG + 1] = td->td_retval[0]; } else { tf->fixreg[FIRSTARG] = td->td_retval[0]; tf->fixreg[FIRSTARG + 1] = td->td_retval[1]; } tf->cr &= ~0x10000000; /* Unset summary overflow */ break; case ERESTART: /* * Set user's pc back to redo the system call. */ tf->srr0 -= 4; break; default: tf->fixreg[FIRSTARG] = SV_ABI_ERRNO(p, error); tf->cr |= 0x10000000; /* Set summary overflow */ break; } } /* * Threading functions */ void cpu_thread_exit(struct thread *td) { } void cpu_thread_clean(struct thread *td) { } void cpu_thread_alloc(struct thread *td) { struct pcb *pcb; pcb = (struct pcb *)((td->td_kstack + td->td_kstack_pages * PAGE_SIZE - sizeof(struct pcb)) & ~0x2fUL); td->td_pcb = pcb; td->td_frame = (struct trapframe *)pcb - 1; } void cpu_thread_free(struct thread *td) { } int cpu_set_user_tls(struct thread *td, void *tls_base) { if (SV_PROC_FLAG(td->td_proc, SV_LP64)) td->td_frame->fixreg[13] = (register_t)tls_base + 0x7010; else td->td_frame->fixreg[2] = (register_t)tls_base + 0x7008; return (0); } void cpu_copy_thread(struct thread *td, struct thread *td0) { struct pcb *pcb2; struct trapframe *tf; struct callframe *cf; pcb2 = td->td_pcb; /* Copy the upcall pcb */ bcopy(td0->td_pcb, pcb2, sizeof(*pcb2)); /* Create a stack for the new thread */ tf = td->td_frame; bcopy(td0->td_frame, tf, sizeof(struct trapframe)); tf->fixreg[FIRSTARG] = 0; tf->fixreg[FIRSTARG + 1] = 0; tf->cr &= ~0x10000000; /* Set registers for trampoline to user mode. */ cf = (struct callframe *)tf - 1; memset(cf, 0, sizeof(struct callframe)); cf->cf_func = (register_t)fork_return; cf->cf_arg0 = (register_t)td; cf->cf_arg1 = (register_t)tf; pcb2->pcb_sp = (register_t)cf; #if defined(__powerpc64__) && (!defined(_CALL_ELF) || _CALL_ELF == 1) pcb2->pcb_lr = ((register_t *)fork_trampoline)[0]; pcb2->pcb_toc = ((register_t *)fork_trampoline)[1]; #else pcb2->pcb_lr = (register_t)fork_trampoline; pcb2->pcb_context[0] = pcb2->pcb_lr; #endif pcb2->pcb_cpu.aim.usr_vsid = 0; /* Setup to release spin count in fork_exit(). */ td->td_md.md_spinlock_count = 1; td->td_md.md_saved_msr = PSL_KERNSET; } void cpu_set_upcall(struct thread *td, void (*entry)(void *), void *arg, stack_t *stack) { struct trapframe *tf; uintptr_t sp; tf = td->td_frame; /* align stack and alloc space for frame ptr and saved LR */ #ifdef __powerpc64__ sp = ((uintptr_t)stack->ss_sp + stack->ss_size - 48) & ~0x1f; #else sp = ((uintptr_t)stack->ss_sp + stack->ss_size - 8) & ~0x1f; #endif bzero(tf, sizeof(struct trapframe)); tf->fixreg[1] = (register_t)sp; tf->fixreg[3] = (register_t)arg; if (SV_PROC_FLAG(td->td_proc, SV_ILP32)) { tf->srr0 = (register_t)entry; tf->srr1 = PSL_USERSET | PSL_FE_DFLT; #ifdef __powerpc64__ tf->srr1 &= ~PSL_SF; #endif } else { #ifdef __powerpc64__ register_t entry_desc[3]; (void)copyin((void *)entry, entry_desc, sizeof(entry_desc)); tf->srr0 = entry_desc[0]; tf->fixreg[2] = entry_desc[1]; tf->fixreg[11] = entry_desc[2]; tf->srr1 = PSL_SF | PSL_USERSET | PSL_FE_DFLT; #endif } #ifdef __powerpc64__ if (mfmsr() & PSL_HV) tf->srr1 |= PSL_HV; #endif td->td_pcb->pcb_flags = 0; td->td_retval[0] = (register_t)entry; td->td_retval[1] = 0; } int ppc_instr_emulate(struct trapframe *frame, struct pcb *pcb) { uint32_t instr; int reg, sig; instr = fuword32((void *)frame->srr0); sig = SIGILL; if ((instr & 0xfc1fffff) == 0x7c1f42a6) { /* mfpvr */ reg = (instr & ~0xfc1fffff) >> 21; frame->fixreg[reg] = mfpvr(); frame->srr0 += 4; return (0); } if ((instr & 0xfc000ffe) == 0x7c0004ac) { /* various sync */ powerpc_sync(); /* Do a heavy-weight sync */ frame->srr0 += 4; return (0); } #ifdef FPU_EMU if (!(pcb->pcb_flags & PCB_FPREGS)) { bzero(&pcb->pcb_fpu, sizeof(pcb->pcb_fpu)); pcb->pcb_flags |= PCB_FPREGS; } sig = fpu_emulate(frame, &pcb->pcb_fpu); #endif return (sig); } Index: head/sys/powerpc/powerpc/fpu.c =================================================================== --- head/sys/powerpc/powerpc/fpu.c (revision 326260) +++ head/sys/powerpc/powerpc/fpu.c (revision 326261) @@ -1,189 +1,191 @@ /*- + * SPDX-License-Identifier: BSD-4-Clause + * * Copyright (C) 1996 Wolfgang Solfrank. * Copyright (C) 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: fpu.c,v 1.5 2001/07/22 11:29:46 wiz Exp $ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include void enable_fpu(struct thread *td) { int msr; struct pcb *pcb; struct trapframe *tf; pcb = td->td_pcb; tf = trapframe(td); /* * Save the thread's FPU CPU number, and set the CPU's current * FPU thread */ td->td_pcb->pcb_fpcpu = PCPU_GET(cpuid); PCPU_SET(fputhread, td); /* * Enable the FPU for when the thread returns from the exception. * If this is the first time the FPU has been used by the thread, * initialise the FPU registers and FPSCR to 0, and set the flag * to indicate that the FPU is in use. */ pcb->pcb_flags |= PCB_FPU; if (pcb->pcb_flags & PCB_VSX) tf->srr1 |= PSL_FP | PSL_VSX; else tf->srr1 |= PSL_FP; if (!(pcb->pcb_flags & PCB_FPREGS)) { memset(&pcb->pcb_fpu, 0, sizeof pcb->pcb_fpu); pcb->pcb_flags |= PCB_FPREGS; } /* * Temporarily enable floating-point so the registers * can be restored. */ msr = mfmsr(); if (pcb->pcb_flags & PCB_VSX) mtmsr(msr | PSL_FP | PSL_VSX); else mtmsr(msr | PSL_FP); isync(); /* * Load the floating point registers and FPSCR from the PCB. * (A value of 0xff for mtfsf specifies that all 8 4-bit fields * of the saved FPSCR are to be loaded from the FPU reg). */ __asm __volatile ("lfd 0,0(%0); mtfsf 0xff,0" :: "b"(&pcb->pcb_fpu.fpscr)); if (pcb->pcb_flags & PCB_VSX) { #define LFP(n) __asm ("lxvw4x " #n ", 0,%0" \ :: "b"(&pcb->pcb_fpu.fpr[n])); LFP(0); LFP(1); LFP(2); LFP(3); LFP(4); LFP(5); LFP(6); LFP(7); LFP(8); LFP(9); LFP(10); LFP(11); LFP(12); LFP(13); LFP(14); LFP(15); LFP(16); LFP(17); LFP(18); LFP(19); LFP(20); LFP(21); LFP(22); LFP(23); LFP(24); LFP(25); LFP(26); LFP(27); LFP(28); LFP(29); LFP(30); LFP(31); #undef LFP } else { #define LFP(n) __asm ("lfd " #n ", 0(%0)" \ :: "b"(&pcb->pcb_fpu.fpr[n])); LFP(0); LFP(1); LFP(2); LFP(3); LFP(4); LFP(5); LFP(6); LFP(7); LFP(8); LFP(9); LFP(10); LFP(11); LFP(12); LFP(13); LFP(14); LFP(15); LFP(16); LFP(17); LFP(18); LFP(19); LFP(20); LFP(21); LFP(22); LFP(23); LFP(24); LFP(25); LFP(26); LFP(27); LFP(28); LFP(29); LFP(30); LFP(31); #undef LFP } isync(); mtmsr(msr); } void save_fpu(struct thread *td) { int msr; struct pcb *pcb; pcb = td->td_pcb; /* * Temporarily re-enable floating-point during the save */ msr = mfmsr(); if (pcb->pcb_flags & PCB_VSX) mtmsr(msr | PSL_FP | PSL_VSX); else mtmsr(msr | PSL_FP); isync(); /* * Save the floating-point registers and FPSCR to the PCB */ if (pcb->pcb_flags & PCB_VSX) { #define SFP(n) __asm ("stxvw4x " #n ", 0,%0" \ :: "b"(&pcb->pcb_fpu.fpr[n])); SFP(0); SFP(1); SFP(2); SFP(3); SFP(4); SFP(5); SFP(6); SFP(7); SFP(8); SFP(9); SFP(10); SFP(11); SFP(12); SFP(13); SFP(14); SFP(15); SFP(16); SFP(17); SFP(18); SFP(19); SFP(20); SFP(21); SFP(22); SFP(23); SFP(24); SFP(25); SFP(26); SFP(27); SFP(28); SFP(29); SFP(30); SFP(31); #undef SFP } else { #define SFP(n) __asm ("stfd " #n ", 0(%0)" \ :: "b"(&pcb->pcb_fpu.fpr[n])); SFP(0); SFP(1); SFP(2); SFP(3); SFP(4); SFP(5); SFP(6); SFP(7); SFP(8); SFP(9); SFP(10); SFP(11); SFP(12); SFP(13); SFP(14); SFP(15); SFP(16); SFP(17); SFP(18); SFP(19); SFP(20); SFP(21); SFP(22); SFP(23); SFP(24); SFP(25); SFP(26); SFP(27); SFP(28); SFP(29); SFP(30); SFP(31); #undef SFP } __asm __volatile ("mffs 0; stfd 0,0(%0)" :: "b"(&pcb->pcb_fpu.fpscr)); /* * Disable floating-point again */ isync(); mtmsr(msr); /* * Clear the current fp thread and pcb's CPU id * XXX should this be left clear to allow lazy save/restore ? */ pcb->pcb_fpcpu = INT_MAX; PCPU_SET(fputhread, NULL); } Index: head/sys/powerpc/powerpc/fuswintr.c =================================================================== --- head/sys/powerpc/powerpc/fuswintr.c (revision 326260) +++ head/sys/powerpc/powerpc/fuswintr.c (revision 326261) @@ -1,51 +1,53 @@ /*- + * SPDX-License-Identifier: BSD-4-Clause + * * Copyright (C) 1994 Wolfgang Solfrank. * Copyright (C) 1994 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: fuswintr.c,v 1.2 2000/06/08 07:29:54 kleink Exp $ */ #include __FBSDID("$FreeBSD$"); #include #include #include /* * Emulate fuswintr * * Simply return fault for all cases */ int fuswintr(void *addr) { return -1; } Index: head/sys/powerpc/powerpc/gdb_machdep.c =================================================================== --- head/sys/powerpc/powerpc/gdb_machdep.c (revision 326260) +++ head/sys/powerpc/powerpc/gdb_machdep.c (revision 326261) @@ -1,99 +1,101 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2006 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include void * gdb_cpu_getreg(int regnum, size_t *regsz) { *regsz = gdb_cpu_regsz(regnum); if (kdb_thread == curthread) { if (regnum == 0 || (regnum >= 2 && regnum <= 31)) return (kdb_frame->fixreg + regnum); if (regnum == 64) return (&kdb_frame->srr0); if (regnum == 67) return (&kdb_frame->lr); } if (regnum == 1) return (&kdb_thrctx->pcb_sp); if (regnum >= 14 && regnum <= 31) return (kdb_thrctx->pcb_context + (regnum - 14)); if (regnum == 64) return (&kdb_thrctx->pcb_lr); return (NULL); } void gdb_cpu_setreg(int regnum, void *val) { switch (regnum) { case GDB_REG_PC: break; } } int gdb_cpu_signal(int vector, int dummy __unused) { #if defined(BOOKE) if (vector == EXC_DEBUG || vector == EXC_PGM) return (SIGTRAP); #else if (vector == EXC_TRC || vector == EXC_RUNMODETRC) return (SIGTRAP); #endif if (vector <= 255) return (vector); else return (SIGEMT); } Index: head/sys/powerpc/powerpc/mp_machdep.c =================================================================== --- head/sys/powerpc/powerpc/mp_machdep.c (revision 326260) +++ head/sys/powerpc/powerpc/mp_machdep.c (revision 326261) @@ -1,364 +1,366 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2008 Marcel Moolenaar * 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$"); #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 "pic_if.h" extern struct pcpu __pcpu[MAXCPU]; volatile static int ap_awake; volatile static u_int ap_letgo; volatile static u_quad_t ap_timebase; static u_int ipi_msg_cnt[32]; static struct mtx ap_boot_mtx; struct pcb stoppcbs[MAXCPU]; void machdep_ap_bootstrap(void) { /* Set PIR */ PCPU_SET(pir, mfspr(SPR_PIR)); PCPU_SET(awake, 1); __asm __volatile("msync; isync"); while (ap_letgo == 0) __asm __volatile("or 27,27,27"); __asm __volatile("or 6,6,6"); /* Initialize DEC and TB, sync with the BSP values */ platform_smp_timebase_sync(ap_timebase, 1); decr_ap_init(); /* Give platform code a chance to do anything necessary */ platform_smp_ap_init(); /* Serialize console output and AP count increment */ mtx_lock_spin(&ap_boot_mtx); ap_awake++; printf("SMP: AP CPU #%d launched\n", PCPU_GET(cpuid)); mtx_unlock_spin(&ap_boot_mtx); /* Start per-CPU event timers. */ cpu_initclocks_ap(); /* Announce ourselves awake, and enter the scheduler */ sched_throw(NULL); } void cpu_mp_setmaxid(void) { struct cpuref cpuref; int error; mp_ncpus = 0; mp_maxid = 0; error = platform_smp_first_cpu(&cpuref); while (!error) { mp_ncpus++; mp_maxid = max(cpuref.cr_cpuid, mp_maxid); error = platform_smp_next_cpu(&cpuref); } /* Sanity. */ if (mp_ncpus == 0) mp_ncpus = 1; } int cpu_mp_probe(void) { /* * We're not going to enable SMP if there's only 1 processor. */ return (mp_ncpus > 1); } void cpu_mp_start(void) { struct cpuref bsp, cpu; struct pcpu *pc; int error; error = platform_smp_get_bsp(&bsp); KASSERT(error == 0, ("Don't know BSP")); error = platform_smp_first_cpu(&cpu); while (!error) { if (cpu.cr_cpuid >= MAXCPU) { printf("SMP: cpu%d: skipped -- ID out of range\n", cpu.cr_cpuid); goto next; } if (CPU_ISSET(cpu.cr_cpuid, &all_cpus)) { printf("SMP: cpu%d: skipped - duplicate ID\n", cpu.cr_cpuid); goto next; } if (cpu.cr_cpuid != bsp.cr_cpuid) { void *dpcpu; pc = &__pcpu[cpu.cr_cpuid]; dpcpu = (void *)kmem_malloc(kernel_arena, DPCPU_SIZE, M_WAITOK | M_ZERO); pcpu_init(pc, cpu.cr_cpuid, sizeof(*pc)); dpcpu_init(dpcpu, cpu.cr_cpuid); } else { pc = pcpup; pc->pc_cpuid = bsp.cr_cpuid; pc->pc_bsp = 1; } pc->pc_hwref = cpu.cr_hwref; CPU_SET(pc->pc_cpuid, &all_cpus); next: error = platform_smp_next_cpu(&cpu); } } void cpu_mp_announce(void) { struct pcpu *pc; int i; if (!bootverbose) return; CPU_FOREACH(i) { pc = pcpu_find(i); if (pc == NULL) continue; printf("cpu%d: dev=%x", i, (int)pc->pc_hwref); if (pc->pc_bsp) printf(" (BSP)"); printf("\n"); } } static void cpu_mp_unleash(void *dummy) { struct pcpu *pc; int cpus, timeout; if (mp_ncpus <= 1) return; mtx_init(&ap_boot_mtx, "ap boot", NULL, MTX_SPIN); cpus = 0; smp_cpus = 0; #ifdef BOOKE tlb1_ap_prep(); #endif STAILQ_FOREACH(pc, &cpuhead, pc_allcpu) { cpus++; if (!pc->pc_bsp) { if (bootverbose) printf("Waking up CPU %d (dev=%x)\n", pc->pc_cpuid, (int)pc->pc_hwref); platform_smp_start_cpu(pc); timeout = 2000; /* wait 2sec for the AP */ while (!pc->pc_awake && --timeout > 0) DELAY(1000); } else { PCPU_SET(pir, mfspr(SPR_PIR)); pc->pc_awake = 1; } if (pc->pc_awake) { if (bootverbose) printf("Adding CPU %d, pir=%x, awake=%x\n", pc->pc_cpuid, pc->pc_pir, pc->pc_awake); smp_cpus++; } else CPU_SET(pc->pc_cpuid, &stopped_cpus); } ap_awake = 1; /* Provide our current DEC and TB values for APs */ ap_timebase = mftb() + 10; __asm __volatile("msync; isync"); /* Let APs continue */ atomic_store_rel_int(&ap_letgo, 1); platform_smp_timebase_sync(ap_timebase, 0); while (ap_awake < smp_cpus) ; if (smp_cpus != cpus || cpus != mp_ncpus) { printf("SMP: %d CPUs found; %d CPUs usable; %d CPUs woken\n", mp_ncpus, cpus, smp_cpus); } /* Let the APs get into the scheduler */ DELAY(10000); /* XXX Atomic set operation? */ smp_started = 1; } SYSINIT(start_aps, SI_SUB_SMP, SI_ORDER_FIRST, cpu_mp_unleash, NULL); int powerpc_ipi_handler(void *arg) { u_int cpuid; uint32_t ipimask; int msg; CTR2(KTR_SMP, "%s: MSR 0x%08x", __func__, mfmsr()); ipimask = atomic_readandclear_32(&(pcpup->pc_ipimask)); if (ipimask == 0) return (FILTER_STRAY); while ((msg = ffs(ipimask) - 1) != -1) { ipimask &= ~(1u << msg); ipi_msg_cnt[msg]++; switch (msg) { case IPI_AST: CTR1(KTR_SMP, "%s: IPI_AST", __func__); break; case IPI_PREEMPT: CTR1(KTR_SMP, "%s: IPI_PREEMPT", __func__); sched_preempt(curthread); break; case IPI_RENDEZVOUS: CTR1(KTR_SMP, "%s: IPI_RENDEZVOUS", __func__); smp_rendezvous_action(); break; case IPI_STOP: /* * IPI_STOP_HARD is mapped to IPI_STOP so it is not * necessary to add such case in the switch. */ CTR1(KTR_SMP, "%s: IPI_STOP or IPI_STOP_HARD (stop)", __func__); cpuid = PCPU_GET(cpuid); savectx(&stoppcbs[cpuid]); savectx(PCPU_GET(curpcb)); CPU_SET_ATOMIC(cpuid, &stopped_cpus); while (!CPU_ISSET(cpuid, &started_cpus)) cpu_spinwait(); CPU_CLR_ATOMIC(cpuid, &stopped_cpus); CPU_CLR_ATOMIC(cpuid, &started_cpus); CTR1(KTR_SMP, "%s: IPI_STOP (restart)", __func__); break; case IPI_HARDCLOCK: CTR1(KTR_SMP, "%s: IPI_HARDCLOCK", __func__); hardclockintr(); break; } } return (FILTER_HANDLED); } static void ipi_send(struct pcpu *pc, int ipi) { CTR4(KTR_SMP, "%s: pc=%p, targetcpu=%d, IPI=%d", __func__, pc, pc->pc_cpuid, ipi); atomic_set_32(&pc->pc_ipimask, (1 << ipi)); powerpc_sync(); PIC_IPI(root_pic, pc->pc_cpuid); CTR1(KTR_SMP, "%s: sent", __func__); } /* Send an IPI to a set of cpus. */ void ipi_selected(cpuset_t cpus, int ipi) { struct pcpu *pc; STAILQ_FOREACH(pc, &cpuhead, pc_allcpu) { if (CPU_ISSET(pc->pc_cpuid, &cpus)) ipi_send(pc, ipi); } } /* Send an IPI to a specific CPU. */ void ipi_cpu(int cpu, u_int ipi) { ipi_send(cpuid_to_pcpu[cpu], ipi); } /* Send an IPI to all CPUs EXCEPT myself. */ void ipi_all_but_self(int ipi) { struct pcpu *pc; STAILQ_FOREACH(pc, &cpuhead, pc_allcpu) { if (pc != pcpup) ipi_send(pc, ipi); } } Index: head/sys/powerpc/powerpc/openpic.c =================================================================== --- head/sys/powerpc/powerpc/openpic.c (revision 326260) +++ head/sys/powerpc/powerpc/openpic.c (revision 326261) @@ -1,443 +1,445 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2002 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" devclass_t openpic_devclass; /* * Local routines */ static int openpic_intr(void *arg); static __inline uint32_t openpic_read(struct openpic_softc *sc, u_int reg) { return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); } static __inline void openpic_write(struct openpic_softc *sc, u_int reg, uint32_t val) { bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); } static __inline void openpic_set_priority(struct openpic_softc *sc, int pri) { u_int tpr; uint32_t x; sched_pin(); tpr = OPENPIC_PCPU_TPR((sc->sc_dev == root_pic) ? PCPU_GET(cpuid) : 0); x = openpic_read(sc, tpr); x &= ~OPENPIC_TPR_MASK; x |= pri; openpic_write(sc, tpr, x); sched_unpin(); } int openpic_common_attach(device_t dev, uint32_t node) { struct openpic_softc *sc; u_int cpu, ipi, irq; u_int32_t x; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_rid = 0; sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, RF_ACTIVE); if (sc->sc_memr == NULL) { device_printf(dev, "Could not alloc mem resource!\n"); return (ENXIO); } sc->sc_bt = rman_get_bustag(sc->sc_memr); sc->sc_bh = rman_get_bushandle(sc->sc_memr); /* Reset the PIC */ x = openpic_read(sc, OPENPIC_CONFIG); x |= OPENPIC_CONFIG_RESET; openpic_write(sc, OPENPIC_CONFIG, x); while (openpic_read(sc, OPENPIC_CONFIG) & OPENPIC_CONFIG_RESET) { powerpc_sync(); DELAY(100); } /* Check if this is a cascaded PIC */ sc->sc_irq = 0; sc->sc_intr = NULL; do { struct resource_list *rl; rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev); if (rl == NULL) break; if (resource_list_find(rl, SYS_RES_IRQ, 0) == NULL) break; sc->sc_intr = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irq, RF_ACTIVE); /* XXX Cascaded PICs pass NULL trapframes! */ bus_setup_intr(dev, sc->sc_intr, INTR_TYPE_MISC | INTR_MPSAFE, openpic_intr, NULL, dev, &sc->sc_icookie); } while (0); /* Reset the PIC */ x = openpic_read(sc, OPENPIC_CONFIG); x |= OPENPIC_CONFIG_RESET; openpic_write(sc, OPENPIC_CONFIG, x); while (openpic_read(sc, OPENPIC_CONFIG) & OPENPIC_CONFIG_RESET) { powerpc_sync(); DELAY(100); } x = openpic_read(sc, OPENPIC_FEATURE); switch (x & OPENPIC_FEATURE_VERSION_MASK) { case 1: sc->sc_version = "1.0"; break; case 2: sc->sc_version = "1.2"; break; case 3: sc->sc_version = "1.3"; break; default: sc->sc_version = "unknown"; break; } sc->sc_ncpu = ((x & OPENPIC_FEATURE_LAST_CPU_MASK) >> OPENPIC_FEATURE_LAST_CPU_SHIFT) + 1; sc->sc_nirq = ((x & OPENPIC_FEATURE_LAST_IRQ_MASK) >> OPENPIC_FEATURE_LAST_IRQ_SHIFT) + 1; /* * PSIM seems to report 1 too many IRQs and CPUs */ if (sc->sc_psim) { sc->sc_nirq--; sc->sc_ncpu--; } if (bootverbose) device_printf(dev, "Version %s, supports %d CPUs and %d irqs\n", sc->sc_version, sc->sc_ncpu, sc->sc_nirq); for (cpu = 0; cpu < sc->sc_ncpu; cpu++) openpic_write(sc, OPENPIC_PCPU_TPR(cpu), 15); /* Reset and disable all interrupts. */ for (irq = 0; irq < sc->sc_nirq; irq++) { x = irq; /* irq == vector. */ x |= OPENPIC_IMASK; x |= OPENPIC_POLARITY_NEGATIVE; x |= OPENPIC_SENSE_LEVEL; x |= 8 << OPENPIC_PRIORITY_SHIFT; openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); } /* Reset and disable all IPIs. */ for (ipi = 0; ipi < 4; ipi++) { x = sc->sc_nirq + ipi; x |= OPENPIC_IMASK; x |= 15 << OPENPIC_PRIORITY_SHIFT; openpic_write(sc, OPENPIC_IPI_VECTOR(ipi), x); } /* we don't need 8259 passthrough mode */ x = openpic_read(sc, OPENPIC_CONFIG); x |= OPENPIC_CONFIG_8259_PASSTHRU_DISABLE; openpic_write(sc, OPENPIC_CONFIG, x); /* send all interrupts to cpu 0 */ for (irq = 0; irq < sc->sc_nirq; irq++) openpic_write(sc, OPENPIC_IDEST(irq), 1 << 0); /* clear all pending interrupts from cpu 0 */ for (irq = 0; irq < sc->sc_nirq; irq++) { (void)openpic_read(sc, OPENPIC_PCPU_IACK(0)); openpic_write(sc, OPENPIC_PCPU_EOI(0), 0); } for (cpu = 0; cpu < sc->sc_ncpu; cpu++) openpic_write(sc, OPENPIC_PCPU_TPR(cpu), 0); powerpc_register_pic(dev, node, sc->sc_nirq, 4, FALSE); /* If this is not a cascaded PIC, it must be the root PIC */ if (sc->sc_intr == NULL) root_pic = dev; return (0); } /* * PIC I/F methods */ void openpic_bind(device_t dev, u_int irq, cpuset_t cpumask) { struct openpic_softc *sc; /* If we aren't directly connected to the CPU, this won't work */ if (dev != root_pic) return; sc = device_get_softc(dev); /* * XXX: openpic_write() is very special and just needs a 32 bits mask. * For the moment, just play dirty and get the first half word. */ openpic_write(sc, OPENPIC_IDEST(irq), cpumask.__bits[0] & 0xffffffff); } void openpic_config(device_t dev, u_int irq, enum intr_trigger trig, enum intr_polarity pol) { struct openpic_softc *sc; uint32_t x; sc = device_get_softc(dev); x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq)); if (pol == INTR_POLARITY_LOW) x &= ~OPENPIC_POLARITY_POSITIVE; else x |= OPENPIC_POLARITY_POSITIVE; if (trig == INTR_TRIGGER_EDGE) x &= ~OPENPIC_SENSE_LEVEL; else x |= OPENPIC_SENSE_LEVEL; openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); } static int openpic_intr(void *arg) { device_t dev = (device_t)(arg); /* XXX Cascaded PICs do not pass non-NULL trapframes! */ openpic_dispatch(dev, NULL); return (FILTER_HANDLED); } void openpic_dispatch(device_t dev, struct trapframe *tf) { struct openpic_softc *sc; u_int cpuid, vector; CTR1(KTR_INTR, "%s: got interrupt", __func__); cpuid = (dev == root_pic) ? PCPU_GET(cpuid) : 0; sc = device_get_softc(dev); while (1) { vector = openpic_read(sc, OPENPIC_PCPU_IACK(cpuid)); vector &= OPENPIC_VECTOR_MASK; if (vector == 255) break; powerpc_dispatch_intr(vector, tf); } } void openpic_enable(device_t dev, u_int irq, u_int vector) { struct openpic_softc *sc; uint32_t x; sc = device_get_softc(dev); if (irq < sc->sc_nirq) { x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq)); x &= ~(OPENPIC_IMASK | OPENPIC_VECTOR_MASK); x |= vector; openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); } else { x = openpic_read(sc, OPENPIC_IPI_VECTOR(0)); x &= ~(OPENPIC_IMASK | OPENPIC_VECTOR_MASK); x |= vector; openpic_write(sc, OPENPIC_IPI_VECTOR(0), x); } } void openpic_eoi(device_t dev, u_int irq __unused) { struct openpic_softc *sc; u_int cpuid; cpuid = (dev == root_pic) ? PCPU_GET(cpuid) : 0; sc = device_get_softc(dev); openpic_write(sc, OPENPIC_PCPU_EOI(cpuid), 0); } void openpic_ipi(device_t dev, u_int cpu) { struct openpic_softc *sc; KASSERT(dev == root_pic, ("Cannot send IPIs from non-root OpenPIC")); sc = device_get_softc(dev); sched_pin(); openpic_write(sc, OPENPIC_PCPU_IPI_DISPATCH(PCPU_GET(cpuid), 0), 1u << cpu); sched_unpin(); } void openpic_mask(device_t dev, u_int irq) { struct openpic_softc *sc; uint32_t x; sc = device_get_softc(dev); if (irq < sc->sc_nirq) { x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq)); x |= OPENPIC_IMASK; openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); } else { x = openpic_read(sc, OPENPIC_IPI_VECTOR(0)); x |= OPENPIC_IMASK; openpic_write(sc, OPENPIC_IPI_VECTOR(0), x); } } void openpic_unmask(device_t dev, u_int irq) { struct openpic_softc *sc; uint32_t x; sc = device_get_softc(dev); if (irq < sc->sc_nirq) { x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq)); x &= ~OPENPIC_IMASK; openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); } else { x = openpic_read(sc, OPENPIC_IPI_VECTOR(0)); x &= ~OPENPIC_IMASK; openpic_write(sc, OPENPIC_IPI_VECTOR(0), x); } } int openpic_suspend(device_t dev) { struct openpic_softc *sc; int i; sc = device_get_softc(dev); sc->sc_saved_config = bus_read_4(sc->sc_memr, OPENPIC_CONFIG); for (i = 0; i < 4; i++) { sc->sc_saved_ipis[i] = bus_read_4(sc->sc_memr, OPENPIC_IPI_VECTOR(i)); } for (i = 0; i < 4; i++) { sc->sc_saved_prios[i] = bus_read_4(sc->sc_memr, OPENPIC_PCPU_TPR(i)); } for (i = 0; i < OPENPIC_TIMERS; i++) { sc->sc_saved_timers[i].tcnt = bus_read_4(sc->sc_memr, OPENPIC_TCNT(i)); sc->sc_saved_timers[i].tbase = bus_read_4(sc->sc_memr, OPENPIC_TBASE(i)); sc->sc_saved_timers[i].tvec = bus_read_4(sc->sc_memr, OPENPIC_TVEC(i)); sc->sc_saved_timers[i].tdst = bus_read_4(sc->sc_memr, OPENPIC_TDST(i)); } for (i = 0; i < OPENPIC_SRC_VECTOR_COUNT; i++) sc->sc_saved_vectors[i] = bus_read_4(sc->sc_memr, OPENPIC_SRC_VECTOR(i)) & ~OPENPIC_ACTIVITY; return (0); } int openpic_resume(device_t dev) { struct openpic_softc *sc; int i; sc = device_get_softc(dev); sc->sc_saved_config = bus_read_4(sc->sc_memr, OPENPIC_CONFIG); for (i = 0; i < 4; i++) { bus_write_4(sc->sc_memr, OPENPIC_IPI_VECTOR(i), sc->sc_saved_ipis[i]); } for (i = 0; i < 4; i++) { bus_write_4(sc->sc_memr, OPENPIC_PCPU_TPR(i), sc->sc_saved_prios[i]); } for (i = 0; i < OPENPIC_TIMERS; i++) { bus_write_4(sc->sc_memr, OPENPIC_TCNT(i), sc->sc_saved_timers[i].tcnt); bus_write_4(sc->sc_memr, OPENPIC_TBASE(i), sc->sc_saved_timers[i].tbase); bus_write_4(sc->sc_memr, OPENPIC_TVEC(i), sc->sc_saved_timers[i].tvec); bus_write_4(sc->sc_memr, OPENPIC_TDST(i), sc->sc_saved_timers[i].tdst); } for (i = 0; i < OPENPIC_SRC_VECTOR_COUNT; i++) bus_write_4(sc->sc_memr, OPENPIC_SRC_VECTOR(i), sc->sc_saved_vectors[i]); return (0); } Index: head/sys/powerpc/powerpc/platform.c =================================================================== --- head/sys/powerpc/powerpc/platform.c (revision 326260) +++ head/sys/powerpc/powerpc/platform.c (revision 326261) @@ -1,346 +1,348 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2005 Peter Grehan * Copyright (c) 2009 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); /* * Dispatch platform calls to the appropriate platform implementation * through a previously registered kernel object. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "platform_if.h" static platform_def_t *plat_def_impl; static platform_t plat_obj; static struct kobj_ops plat_kernel_kops; static struct platform_kobj plat_kernel_obj; static char plat_name[64] = ""; SYSCTL_STRING(_hw, OID_AUTO, platform, CTLFLAG_RD | CTLFLAG_TUN, plat_name, 0, "Platform currently in use"); static struct mem_region pregions[PHYS_AVAIL_SZ]; static struct mem_region aregions[PHYS_AVAIL_SZ]; static int npregions, naregions; /* * Memory region utilities: determine if two regions overlap, * and merge two overlapping regions into one */ static int memr_overlap(struct mem_region *r1, struct mem_region *r2) { if ((r1->mr_start + r1->mr_size) < r2->mr_start || (r2->mr_start + r2->mr_size) < r1->mr_start) return (FALSE); return (TRUE); } static void memr_merge(struct mem_region *from, struct mem_region *to) { vm_offset_t end; end = uqmax(to->mr_start + to->mr_size, from->mr_start + from->mr_size); to->mr_start = uqmin(from->mr_start, to->mr_start); to->mr_size = end - to->mr_start; } /* * Quick sort callout for comparing memory regions. */ static int mr_cmp(const void *a, const void *b) { const struct mem_region *regiona, *regionb; regiona = a; regionb = b; if (regiona->mr_start < regionb->mr_start) return (-1); else if (regiona->mr_start > regionb->mr_start) return (1); else return (0); } void mem_regions(struct mem_region **phys, int *physsz, struct mem_region **avail, int *availsz) { int i, j, still_merging; if (npregions == 0) { PLATFORM_MEM_REGIONS(plat_obj, pregions, &npregions, aregions, &naregions); qsort(pregions, npregions, sizeof(*pregions), mr_cmp); qsort(aregions, naregions, sizeof(*aregions), mr_cmp); /* Remove overlapping available regions */ do { still_merging = FALSE; for (i = 0; i < naregions; i++) { if (aregions[i].mr_size == 0) continue; for (j = i+1; j < naregions; j++) { if (aregions[j].mr_size == 0) continue; if (!memr_overlap(&aregions[j], &aregions[i])) continue; memr_merge(&aregions[j], &aregions[i]); /* mark inactive */ aregions[j].mr_size = 0; still_merging = TRUE; } } } while (still_merging == TRUE); /* Collapse zero-length available regions */ for (i = 0; i < naregions; i++) { if (aregions[i].mr_size == 0) { memcpy(&aregions[i], &aregions[i+1], (naregions - i - 1)*sizeof(*aregions)); naregions--; i--; } } } *phys = pregions; *avail = aregions; *physsz = npregions; *availsz = naregions; } int mem_valid(vm_offset_t addr, int len) { int i; if (npregions == 0) { struct mem_region *p, *a; int na, np; mem_regions(&p, &np, &a, &na); } for (i = 0; i < npregions; i++) if ((addr >= pregions[i].mr_start) && (addr + len <= pregions[i].mr_start + pregions[i].mr_size)) return (0); return (EFAULT); } vm_offset_t platform_real_maxaddr(void) { return (PLATFORM_REAL_MAXADDR(plat_obj)); } const char * installed_platform() { return (plat_def_impl->name); } u_long platform_timebase_freq(struct cpuref *cpu) { return (PLATFORM_TIMEBASE_FREQ(plat_obj, cpu)); } /* * Put the current CPU, as last step in suspend, to sleep */ void platform_sleep() { PLATFORM_SLEEP(plat_obj); } int platform_smp_first_cpu(struct cpuref *cpu) { return (PLATFORM_SMP_FIRST_CPU(plat_obj, cpu)); } int platform_smp_next_cpu(struct cpuref *cpu) { return (PLATFORM_SMP_NEXT_CPU(plat_obj, cpu)); } int platform_smp_get_bsp(struct cpuref *cpu) { return (PLATFORM_SMP_GET_BSP(plat_obj, cpu)); } int platform_smp_start_cpu(struct pcpu *cpu) { return (PLATFORM_SMP_START_CPU(plat_obj, cpu)); } void platform_smp_ap_init() { PLATFORM_SMP_AP_INIT(plat_obj); } #ifdef SMP struct cpu_group * cpu_topo(void) { return (PLATFORM_SMP_TOPO(plat_obj)); } #endif /* * Reset back to firmware. */ void cpu_reset() { PLATFORM_RESET(plat_obj); } int cpu_idle_wakeup(int cpu) { return (PLATFORM_IDLE_WAKEUP(plat_obj, cpu)); } void platform_cpu_idle(int cpu) { PLATFORM_IDLE(plat_obj, cpu); } void platform_smp_timebase_sync(u_long tb, int ap) { PLATFORM_SMP_TIMEBASE_SYNC(plat_obj, tb, ap); } /* * Platform install routines. Highest priority wins, using the same * algorithm as bus attachment. */ SET_DECLARE(platform_set, platform_def_t); void platform_probe_and_attach() { platform_def_t **platpp, *platp; int prio, best_prio; plat_obj = &plat_kernel_obj; best_prio = 0; /* * Try to locate the best platform kobj */ SET_FOREACH(platpp, platform_set) { platp = *platpp; /* * Take care of compiling the selected class, and * then statically initialise the MMU object */ kobj_class_compile_static(platp, &plat_kernel_kops); kobj_init_static((kobj_t)plat_obj, platp); prio = PLATFORM_PROBE(plat_obj); /* Check for errors */ if (prio > 0) continue; /* * Check if this module was specifically requested through * the loader tunable we provide. */ if (strcmp(platp->name,plat_name) == 0) { plat_def_impl = platp; break; } /* Otherwise, see if it is better than our current best */ if (plat_def_impl == NULL || prio > best_prio) { best_prio = prio; plat_def_impl = platp; } /* * We can't free the KOBJ, since it is static. Reset the ops * member of this class so that we can come back later. */ platp->ops = NULL; } if (plat_def_impl == NULL) panic("No platform module found!"); /* * Recompile to make sure we ended with the * correct one, and then attach. */ kobj_class_compile_static(plat_def_impl, &plat_kernel_kops); kobj_init_static((kobj_t)plat_obj, plat_def_impl); strlcpy(plat_name,plat_def_impl->name,sizeof(plat_name)); PLATFORM_ATTACH(plat_obj); } Index: head/sys/powerpc/powerpc/pmap_dispatch.c =================================================================== --- head/sys/powerpc/powerpc/pmap_dispatch.c (revision 326260) +++ head/sys/powerpc/powerpc/pmap_dispatch.c (revision 326261) @@ -1,604 +1,606 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2005 Peter Grehan * 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$"); /* * Dispatch MI pmap calls to the appropriate MMU implementation * through a previously registered kernel object. * * Before pmap_bootstrap() can be called, a CPU module must have * called pmap_mmu_install(). This may be called multiple times: * the highest priority call will be installed as the default * MMU handler when pmap_bootstrap() is called. * * It is required that mutex_init() be called before pmap_bootstrap(), * as the PMAP layer makes extensive use of mutexes. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mmu_if.h" static mmu_def_t *mmu_def_impl; static mmu_t mmu_obj; static struct mmu_kobj mmu_kernel_obj; static struct kobj_ops mmu_kernel_kops; /* * pmap globals */ struct pmap kernel_pmap_store; struct msgbuf *msgbufp; vm_offset_t msgbuf_phys; vm_offset_t kernel_vm_end; vm_paddr_t phys_avail[PHYS_AVAIL_SZ]; vm_offset_t virtual_avail; vm_offset_t virtual_end; int pmap_bootstrapped; #ifdef AIM int pvo_vaddr_compare(struct pvo_entry *a, struct pvo_entry *b) { if (PVO_VADDR(a) < PVO_VADDR(b)) return (-1); else if (PVO_VADDR(a) > PVO_VADDR(b)) return (1); return (0); } RB_GENERATE(pvo_tree, pvo_entry, pvo_plink, pvo_vaddr_compare); #endif void pmap_advise(pmap_t pmap, vm_offset_t start, vm_offset_t end, int advice) { CTR5(KTR_PMAP, "%s(%p, %#x, %#x, %d)", __func__, pmap, start, end, advice); MMU_ADVISE(mmu_obj, pmap, start, end, advice); } void pmap_clear_modify(vm_page_t m) { CTR2(KTR_PMAP, "%s(%p)", __func__, m); MMU_CLEAR_MODIFY(mmu_obj, m); } void pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vm_offset_t dst_addr, vm_size_t len, vm_offset_t src_addr) { CTR6(KTR_PMAP, "%s(%p, %p, %#x, %#x, %#x)", __func__, dst_pmap, src_pmap, dst_addr, len, src_addr); MMU_COPY(mmu_obj, dst_pmap, src_pmap, dst_addr, len, src_addr); } void pmap_copy_page(vm_page_t src, vm_page_t dst) { CTR3(KTR_PMAP, "%s(%p, %p)", __func__, src, dst); MMU_COPY_PAGE(mmu_obj, src, dst); } void pmap_copy_pages(vm_page_t ma[], vm_offset_t a_offset, vm_page_t mb[], vm_offset_t b_offset, int xfersize) { CTR6(KTR_PMAP, "%s(%p, %#x, %p, %#x, %#x)", __func__, ma, a_offset, mb, b_offset, xfersize); MMU_COPY_PAGES(mmu_obj, ma, a_offset, mb, b_offset, xfersize); } int pmap_enter(pmap_t pmap, vm_offset_t va, vm_page_t p, vm_prot_t prot, u_int flags, int8_t psind) { CTR6(KTR_PMAP, "pmap_enter(%p, %#x, %p, %#x, %x, %d)", pmap, va, p, prot, flags, psind); return (MMU_ENTER(mmu_obj, pmap, va, p, prot, flags, psind)); } void pmap_enter_object(pmap_t pmap, vm_offset_t start, vm_offset_t end, vm_page_t m_start, vm_prot_t prot) { CTR6(KTR_PMAP, "%s(%p, %#x, %#x, %p, %#x)", __func__, pmap, start, end, m_start, prot); MMU_ENTER_OBJECT(mmu_obj, pmap, start, end, m_start, prot); } void pmap_enter_quick(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot) { CTR5(KTR_PMAP, "%s(%p, %#x, %p, %#x)", __func__, pmap, va, m, prot); MMU_ENTER_QUICK(mmu_obj, pmap, va, m, prot); } vm_paddr_t pmap_extract(pmap_t pmap, vm_offset_t va) { CTR3(KTR_PMAP, "%s(%p, %#x)", __func__, pmap, va); return (MMU_EXTRACT(mmu_obj, pmap, va)); } vm_page_t pmap_extract_and_hold(pmap_t pmap, vm_offset_t va, vm_prot_t prot) { CTR4(KTR_PMAP, "%s(%p, %#x, %#x)", __func__, pmap, va, prot); return (MMU_EXTRACT_AND_HOLD(mmu_obj, pmap, va, prot)); } void pmap_growkernel(vm_offset_t va) { CTR2(KTR_PMAP, "%s(%#x)", __func__, va); MMU_GROWKERNEL(mmu_obj, va); } void pmap_init(void) { CTR1(KTR_PMAP, "%s()", __func__); MMU_INIT(mmu_obj); } boolean_t pmap_is_modified(vm_page_t m) { CTR2(KTR_PMAP, "%s(%p)", __func__, m); return (MMU_IS_MODIFIED(mmu_obj, m)); } boolean_t pmap_is_prefaultable(pmap_t pmap, vm_offset_t va) { CTR3(KTR_PMAP, "%s(%p, %#x)", __func__, pmap, va); return (MMU_IS_PREFAULTABLE(mmu_obj, pmap, va)); } boolean_t pmap_is_referenced(vm_page_t m) { CTR2(KTR_PMAP, "%s(%p)", __func__, m); return (MMU_IS_REFERENCED(mmu_obj, m)); } boolean_t pmap_ts_referenced(vm_page_t m) { CTR2(KTR_PMAP, "%s(%p)", __func__, m); return (MMU_TS_REFERENCED(mmu_obj, m)); } vm_offset_t pmap_map(vm_offset_t *virt, vm_paddr_t start, vm_paddr_t end, int prot) { CTR5(KTR_PMAP, "%s(%p, %#x, %#x, %#x)", __func__, virt, start, end, prot); return (MMU_MAP(mmu_obj, virt, start, end, prot)); } void pmap_object_init_pt(pmap_t pmap, vm_offset_t addr, vm_object_t object, vm_pindex_t pindex, vm_size_t size) { CTR6(KTR_PMAP, "%s(%p, %#x, %p, %u, %#x)", __func__, pmap, addr, object, pindex, size); MMU_OBJECT_INIT_PT(mmu_obj, pmap, addr, object, pindex, size); } boolean_t pmap_page_exists_quick(pmap_t pmap, vm_page_t m) { CTR3(KTR_PMAP, "%s(%p, %p)", __func__, pmap, m); return (MMU_PAGE_EXISTS_QUICK(mmu_obj, pmap, m)); } void pmap_page_init(vm_page_t m) { CTR2(KTR_PMAP, "%s(%p)", __func__, m); MMU_PAGE_INIT(mmu_obj, m); } int pmap_page_wired_mappings(vm_page_t m) { CTR2(KTR_PMAP, "%s(%p)", __func__, m); return (MMU_PAGE_WIRED_MAPPINGS(mmu_obj, m)); } int pmap_pinit(pmap_t pmap) { CTR2(KTR_PMAP, "%s(%p)", __func__, pmap); MMU_PINIT(mmu_obj, pmap); return (1); } void pmap_pinit0(pmap_t pmap) { CTR2(KTR_PMAP, "%s(%p)", __func__, pmap); MMU_PINIT0(mmu_obj, pmap); } void pmap_protect(pmap_t pmap, vm_offset_t start, vm_offset_t end, vm_prot_t prot) { CTR5(KTR_PMAP, "%s(%p, %#x, %#x, %#x)", __func__, pmap, start, end, prot); MMU_PROTECT(mmu_obj, pmap, start, end, prot); } void pmap_qenter(vm_offset_t start, vm_page_t *m, int count) { CTR4(KTR_PMAP, "%s(%#x, %p, %d)", __func__, start, m, count); MMU_QENTER(mmu_obj, start, m, count); } void pmap_qremove(vm_offset_t start, int count) { CTR3(KTR_PMAP, "%s(%#x, %d)", __func__, start, count); MMU_QREMOVE(mmu_obj, start, count); } void pmap_release(pmap_t pmap) { CTR2(KTR_PMAP, "%s(%p)", __func__, pmap); MMU_RELEASE(mmu_obj, pmap); } void pmap_remove(pmap_t pmap, vm_offset_t start, vm_offset_t end) { CTR4(KTR_PMAP, "%s(%p, %#x, %#x)", __func__, pmap, start, end); MMU_REMOVE(mmu_obj, pmap, start, end); } void pmap_remove_all(vm_page_t m) { CTR2(KTR_PMAP, "%s(%p)", __func__, m); MMU_REMOVE_ALL(mmu_obj, m); } void pmap_remove_pages(pmap_t pmap) { CTR2(KTR_PMAP, "%s(%p)", __func__, pmap); MMU_REMOVE_PAGES(mmu_obj, pmap); } void pmap_remove_write(vm_page_t m) { CTR2(KTR_PMAP, "%s(%p)", __func__, m); MMU_REMOVE_WRITE(mmu_obj, m); } void pmap_unwire(pmap_t pmap, vm_offset_t start, vm_offset_t end) { CTR4(KTR_PMAP, "%s(%p, %#x, %#x)", __func__, pmap, start, end); MMU_UNWIRE(mmu_obj, pmap, start, end); } void pmap_zero_page(vm_page_t m) { CTR2(KTR_PMAP, "%s(%p)", __func__, m); MMU_ZERO_PAGE(mmu_obj, m); } void pmap_zero_page_area(vm_page_t m, int off, int size) { CTR4(KTR_PMAP, "%s(%p, %d, %d)", __func__, m, off, size); MMU_ZERO_PAGE_AREA(mmu_obj, m, off, size); } int pmap_mincore(pmap_t pmap, vm_offset_t addr, vm_paddr_t *locked_pa) { CTR3(KTR_PMAP, "%s(%p, %#x)", __func__, pmap, addr); return (MMU_MINCORE(mmu_obj, pmap, addr, locked_pa)); } void pmap_activate(struct thread *td) { CTR2(KTR_PMAP, "%s(%p)", __func__, td); MMU_ACTIVATE(mmu_obj, td); } void pmap_deactivate(struct thread *td) { CTR2(KTR_PMAP, "%s(%p)", __func__, td); MMU_DEACTIVATE(mmu_obj, td); } /* * Increase the starting virtual address of the given mapping if a * different alignment might result in more superpage mappings. */ void pmap_align_superpage(vm_object_t object, vm_ooffset_t offset, vm_offset_t *addr, vm_size_t size) { CTR5(KTR_PMAP, "%s(%p, %#x, %p, %#x)", __func__, object, offset, addr, size); MMU_ALIGN_SUPERPAGE(mmu_obj, object, offset, addr, size); } /* * Routines used in machine-dependent code */ void pmap_bootstrap(vm_offset_t start, vm_offset_t end) { mmu_obj = &mmu_kernel_obj; /* * Take care of compiling the selected class, and * then statically initialise the MMU object */ kobj_class_compile_static(mmu_def_impl, &mmu_kernel_kops); kobj_init_static((kobj_t)mmu_obj, mmu_def_impl); MMU_BOOTSTRAP(mmu_obj, start, end); } void pmap_cpu_bootstrap(int ap) { /* * No KTR here because our console probably doesn't work yet */ return (MMU_CPU_BOOTSTRAP(mmu_obj, ap)); } void * pmap_mapdev(vm_paddr_t pa, vm_size_t size) { CTR3(KTR_PMAP, "%s(%#x, %#x)", __func__, pa, size); return (MMU_MAPDEV(mmu_obj, pa, size)); } void * pmap_mapdev_attr(vm_paddr_t pa, vm_size_t size, vm_memattr_t attr) { CTR4(KTR_PMAP, "%s(%#x, %#x, %#x)", __func__, pa, size, attr); return (MMU_MAPDEV_ATTR(mmu_obj, pa, size, attr)); } void pmap_page_set_memattr(vm_page_t m, vm_memattr_t ma) { CTR3(KTR_PMAP, "%s(%p, %#x)", __func__, m, ma); return (MMU_PAGE_SET_MEMATTR(mmu_obj, m, ma)); } void pmap_unmapdev(vm_offset_t va, vm_size_t size) { CTR3(KTR_PMAP, "%s(%#x, %#x)", __func__, va, size); MMU_UNMAPDEV(mmu_obj, va, size); } vm_paddr_t pmap_kextract(vm_offset_t va) { CTR2(KTR_PMAP, "%s(%#x)", __func__, va); return (MMU_KEXTRACT(mmu_obj, va)); } void pmap_kenter(vm_offset_t va, vm_paddr_t pa) { CTR3(KTR_PMAP, "%s(%#x, %#x)", __func__, va, pa); MMU_KENTER(mmu_obj, va, pa); } void pmap_kenter_attr(vm_offset_t va, vm_paddr_t pa, vm_memattr_t ma) { CTR4(KTR_PMAP, "%s(%#x, %#x, %#x)", __func__, va, pa, ma); MMU_KENTER_ATTR(mmu_obj, va, pa, ma); } void pmap_kremove(vm_offset_t va) { CTR2(KTR_PMAP, "%s(%#x)", __func__, va); return (MMU_KREMOVE(mmu_obj, va)); } boolean_t pmap_dev_direct_mapped(vm_paddr_t pa, vm_size_t size) { CTR3(KTR_PMAP, "%s(%#x, %#x)", __func__, pa, size); return (MMU_DEV_DIRECT_MAPPED(mmu_obj, pa, size)); } void pmap_sync_icache(pmap_t pm, vm_offset_t va, vm_size_t sz) { CTR4(KTR_PMAP, "%s(%p, %#x, %#x)", __func__, pm, va, sz); return (MMU_SYNC_ICACHE(mmu_obj, pm, va, sz)); } void dumpsys_map_chunk(vm_paddr_t pa, size_t sz, void **va) { CTR4(KTR_PMAP, "%s(%#jx, %#zx, %p)", __func__, (uintmax_t)pa, sz, va); return (MMU_DUMPSYS_MAP(mmu_obj, pa, sz, va)); } void dumpsys_unmap_chunk(vm_paddr_t pa, size_t sz, void *va) { CTR4(KTR_PMAP, "%s(%#jx, %#zx, %p)", __func__, (uintmax_t)pa, sz, va); return (MMU_DUMPSYS_UNMAP(mmu_obj, pa, sz, va)); } void dumpsys_pa_init(void) { CTR1(KTR_PMAP, "%s()", __func__); return (MMU_SCAN_INIT(mmu_obj)); } vm_offset_t pmap_quick_enter_page(vm_page_t m) { CTR2(KTR_PMAP, "%s(%p)", __func__, m); return (MMU_QUICK_ENTER_PAGE(mmu_obj, m)); } void pmap_quick_remove_page(vm_offset_t addr) { CTR2(KTR_PMAP, "%s(%#x)", __func__, addr); MMU_QUICK_REMOVE_PAGE(mmu_obj, addr); } int pmap_change_attr(vm_offset_t addr, vm_size_t size, vm_memattr_t mode) { CTR4(KTR_PMAP, "%s(%#x, %#zx, %d)", __func__, addr, size, mode); return (MMU_CHANGE_ATTR(mmu_obj, addr, size, mode)); } /* * MMU install routines. Highest priority wins, equal priority also * overrides allowing last-set to win. */ SET_DECLARE(mmu_set, mmu_def_t); boolean_t pmap_mmu_install(char *name, int prio) { mmu_def_t **mmupp, *mmup; static int curr_prio = 0; /* * Try and locate the MMU kobj corresponding to the name */ SET_FOREACH(mmupp, mmu_set) { mmup = *mmupp; if (mmup->name && !strcmp(mmup->name, name) && (prio >= curr_prio || mmu_def_impl == NULL)) { curr_prio = prio; mmu_def_impl = mmup; return (TRUE); } } return (FALSE); } int unmapped_buf_allowed; Index: head/sys/powerpc/powerpc/sc_machdep.c =================================================================== --- head/sys/powerpc/powerpc/sc_machdep.c (revision 326260) +++ head/sys/powerpc/powerpc/sc_machdep.c (revision 326261) @@ -1,87 +1,89 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2003 Jake Burkholder. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include static sc_softc_t sc_softcs[8]; int sc_get_cons_priority(int *unit, int *flags) { *unit = 0; *flags = 0; return (CN_INTERNAL); } int sc_max_unit(void) { return (1); } sc_softc_t * sc_get_softc(int unit, int flags) { sc_softc_t *sc; if (unit < 0) return (NULL); sc = &sc_softcs[unit]; sc->unit = unit; if ((sc->flags & SC_INIT_DONE) == 0) { sc->keyboard = -1; sc->adapter = -1; sc->cursor_char = SC_CURSOR_CHAR; sc->mouse_char = SC_MOUSE_CHAR; } return (sc); } void sc_get_bios_values(bios_values_t *values) { } int sc_tone(int hz) { return (0); } Index: head/sys/powerpc/powerpc/stack_machdep.c =================================================================== --- head/sys/powerpc/powerpc/stack_machdep.c (revision 326260) +++ head/sys/powerpc/powerpc/stack_machdep.c (revision 326261) @@ -1,115 +1,117 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2005 Antoine Brodin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __powerpc64__ #define CALLOFFSET 8 /* Account for the TOC reload slot */ #else #define CALLOFFSET 4 #endif static void stack_capture(struct stack *st, vm_offset_t frame) { vm_offset_t callpc; stack_zero(st); if (frame < PAGE_SIZE) return; while (1) { frame = *(vm_offset_t *)frame; if (frame < PAGE_SIZE) break; #ifdef __powerpc64__ callpc = *(vm_offset_t *)(frame + 16) - 4; #else callpc = *(vm_offset_t *)(frame + 4) - 4; #endif if ((callpc & 3) || (callpc < 0x100)) break; /* * Don't bother traversing trap-frames - there should * be enough info down to the frame to work out where * things are going wrong. Plus, prevents this shortened * version of code from accessing user-space frames */ if (callpc + CALLOFFSET == (vm_offset_t) &trapexit || callpc + CALLOFFSET == (vm_offset_t) &asttrapexit) break; if (stack_put(st, callpc) == -1) break; } } void stack_save_td(struct stack *st, struct thread *td) { vm_offset_t frame; if (TD_IS_SWAPPED(td)) panic("stack_save_td: swapped"); if (TD_IS_RUNNING(td)) panic("stack_save_td: running"); frame = td->td_pcb->pcb_sp; stack_capture(st, frame); } int stack_save_td_running(struct stack *st, struct thread *td) { return (EOPNOTSUPP); } void stack_save(struct stack *st) { register_t frame; frame = (register_t)__builtin_frame_address(0); stack_capture(st, frame); } Index: head/sys/powerpc/powerpc/suswintr.c =================================================================== --- head/sys/powerpc/powerpc/suswintr.c (revision 326260) +++ head/sys/powerpc/powerpc/suswintr.c (revision 326261) @@ -1,51 +1,53 @@ /*- + * SPDX-License-Identifier: BSD-4-Clause + * * Copyright (C) 1994 Wolfgang Solfrank. * Copyright (C) 1994 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: suswintr.c,v 1.2 2000/06/09 14:05:48 kleink Exp $ */ #include __FBSDID("$FreeBSD$"); #include #include #include /* * Emulate suswintr * * Simply return fault for all cases */ int suswintr(void *addr, int s) { return -1; } Index: head/sys/powerpc/powerpc/syncicache.c =================================================================== --- head/sys/powerpc/powerpc/syncicache.c (revision 326260) +++ head/sys/powerpc/powerpc/syncicache.c (revision 326261) @@ -1,70 +1,72 @@ /*- + * SPDX-License-Identifier: BSD-4-Clause + * * Copyright (C) 1995-1997, 1999 Wolfgang Solfrank. * Copyright (C) 1995-1997, 1999 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: syncicache.c,v 1.2 1999/05/05 12:36:40 tsubai Exp $ */ #include __FBSDID("$FreeBSD$"); #include #if defined(_KERNEL) || defined(_STANDALONE) #include #include #include #endif #include #include #include void __syncicache(void *from, int len) { register_t l, off; char *p; off = (uintptr_t)from & (cacheline_size - 1); l = len += off; p = (char *)from - off; do { __asm __volatile ("dcbst 0,%0" :: "r"(p)); p += cacheline_size; } while ((l -= cacheline_size) > 0); __asm __volatile ("sync"); p = (char *)from - off; do { __asm __volatile ("icbi 0,%0" :: "r"(p)); p += cacheline_size; } while ((len -= cacheline_size) > 0); __asm __volatile ("sync; isync"); } Index: head/sys/powerpc/powerpc/sys_machdep.c =================================================================== --- head/sys/powerpc/powerpc/sys_machdep.c (revision 326260) +++ head/sys/powerpc/powerpc/sys_machdep.c (revision 326261) @@ -1,53 +1,55 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2001 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include "opt_compat.h" #ifdef COMPAT_FREEBSD32 #include int freebsd32_sysarch(struct thread *td, struct freebsd32_sysarch_args *uap) { return (EINVAL); } #endif int sysarch(struct thread *td, struct sysarch_args *uap) { return (EINVAL); } Index: head/sys/powerpc/ps3/ehci_ps3.c =================================================================== --- head/sys/powerpc/ps3/ehci_ps3.c (revision 326260) +++ head/sys/powerpc/ps3/ehci_ps3.c (revision 326261) @@ -1,184 +1,186 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2010 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ps3bus.h" struct ps3_ehci_softc { ehci_softc_t base; struct bus_space tag; }; static void ehci_ps3_post_reset(struct ehci_softc *ehci_softc) { uint32_t usbmode; /* Select big-endian mode */ usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM); usbmode |= EHCI_UM_ES_BE; EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode); } static int ehci_ps3_probe(device_t dev) { if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_SYSBUS || ps3bus_get_devtype(dev) != PS3_DEVTYPE_USB) return (ENXIO); device_set_desc(dev, "Playstation 3 USB 2.0 controller"); return (BUS_PROBE_SPECIFIC); } static int ehci_ps3_attach(device_t dev) { ehci_softc_t *sc = device_get_softc(dev); int rid, err; sc->sc_bus.parent = dev; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc)) return (ENOMEM); rid = 1; sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(dev, "Could not map memory\n"); goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); rid = 1; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(dev, "Could not allocate irq\n"); return (ENXIO); } sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(dev, "Could not add USB device\n"); return (ENXIO); } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); sprintf(sc->sc_vendor, "Sony"); err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(dev, "Could not setup error irq, %d\n", err); goto error; } sc->sc_vendor_post_reset = ehci_ps3_post_reset; err = ehci_init(sc); if (err) { device_printf(dev, "USB init failed err=%d\n", err); goto error; } err = device_probe_and_attach(sc->sc_bus.bdev); if (err == 0) return (0); error: return (ENXIO); } static device_method_t ehci_ps3_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ehci_ps3_probe), DEVMETHOD(device_attach, ehci_ps3_attach), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ehci_ps3_driver = { .name = "ehci", .methods = ehci_ps3_methods, .size = sizeof(ehci_softc_t), }; static devclass_t ehci_ps3_devclass; DRIVER_MODULE(ehci_ps3, ps3bus, ehci_ps3_driver, ehci_ps3_devclass, 0, 0); MODULE_DEPEND(ehci_ps3, usb, 1, 1, 1); Index: head/sys/powerpc/ps3/if_glc.c =================================================================== --- head/sys/powerpc/ps3/if_glc.c (revision 326260) +++ head/sys/powerpc/ps3/if_glc.c (revision 326261) @@ -1,960 +1,962 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2010 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #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 "ps3bus.h" #include "ps3-hvcall.h" #include "if_glcreg.h" static int glc_probe(device_t); static int glc_attach(device_t); static void glc_init(void *xsc); static void glc_start(struct ifnet *ifp); static int glc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); static void glc_set_multicast(struct glc_softc *sc); static int glc_add_rxbuf(struct glc_softc *sc, int idx); static int glc_add_rxbuf_dma(struct glc_softc *sc, int idx); static int glc_encap(struct glc_softc *sc, struct mbuf **m_head, bus_addr_t *pktdesc); static int glc_intr_filter(void *xsc); static void glc_intr(void *xsc); static void glc_tick(void *xsc); static void glc_media_status(struct ifnet *ifp, struct ifmediareq *ifmr); static int glc_media_change(struct ifnet *ifp); static MALLOC_DEFINE(M_GLC, "gelic", "PS3 GELIC ethernet"); static device_method_t glc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, glc_probe), DEVMETHOD(device_attach, glc_attach), { 0, 0 } }; static driver_t glc_driver = { "glc", glc_methods, sizeof(struct glc_softc) }; static devclass_t glc_devclass; DRIVER_MODULE(glc, ps3bus, glc_driver, glc_devclass, 0, 0); static int glc_probe(device_t dev) { if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_SYSBUS || ps3bus_get_devtype(dev) != PS3_DEVTYPE_GELIC) return (ENXIO); device_set_desc(dev, "Playstation 3 GELIC Network Controller"); return (BUS_PROBE_SPECIFIC); } static void glc_getphys(void *xaddr, bus_dma_segment_t *segs, int nsegs, int error) { if (error != 0) return; *(bus_addr_t *)xaddr = segs[0].ds_addr; } static int glc_attach(device_t dev) { struct glc_softc *sc; struct glc_txsoft *txs; uint64_t mac64, val, junk; int i, err; sc = device_get_softc(dev); sc->sc_bus = ps3bus_get_bus(dev); sc->sc_dev = ps3bus_get_device(dev); sc->sc_self = dev; mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->sc_tick_ch, &sc->sc_mtx, 0); sc->next_txdma_slot = 0; sc->bsy_txdma_slots = 0; sc->sc_next_rxdma_slot = 0; sc->first_used_txdma_slot = -1; /* * Shut down existing tasks. */ lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0); lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0); sc->sc_ifp = if_alloc(IFT_ETHER); sc->sc_ifp->if_softc = sc; /* * Get MAC address and VLAN id */ lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_MAC_ADDRESS, 0, 0, 0, &mac64, &junk); memcpy(sc->sc_enaddr, &((uint8_t *)&mac64)[2], sizeof(sc->sc_enaddr)); sc->sc_tx_vlan = sc->sc_rx_vlan = -1; err = lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_VLAN_ID, GELIC_VLAN_TX_ETHERNET, 0, 0, &val, &junk); if (err == 0) sc->sc_tx_vlan = val; err = lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_VLAN_ID, GELIC_VLAN_RX_ETHERNET, 0, 0, &val, &junk); if (err == 0) sc->sc_rx_vlan = val; /* * Set up interrupt handler */ sc->sc_irqid = 0; sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqid, RF_ACTIVE); if (sc->sc_irq == NULL) { device_printf(dev, "Could not allocate IRQ!\n"); mtx_destroy(&sc->sc_mtx); return (ENXIO); } bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE | INTR_ENTROPY, glc_intr_filter, glc_intr, sc, &sc->sc_irqctx); sc->sc_hwirq_status = (uint64_t *)contigmalloc(8, M_GLC, M_ZERO, 0, BUS_SPACE_MAXADDR_32BIT, 8, PAGE_SIZE); lv1_net_set_interrupt_status_indicator(sc->sc_bus, sc->sc_dev, vtophys(sc->sc_hwirq_status), 0); lv1_net_set_interrupt_mask(sc->sc_bus, sc->sc_dev, GELIC_INT_RXDONE | GELIC_INT_RXFRAME | GELIC_INT_PHY | GELIC_INT_TX_CHAIN_END, 0); /* * Set up DMA. */ err = bus_dma_tag_create(bus_get_dma_tag(dev), 32, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, 129*sizeof(struct glc_dmadesc), 1, 128*sizeof(struct glc_dmadesc), 0, NULL,NULL, &sc->sc_dmadesc_tag); err = bus_dmamem_alloc(sc->sc_dmadesc_tag, (void **)&sc->sc_txdmadesc, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->sc_txdmadesc_map); err = bus_dmamap_load(sc->sc_dmadesc_tag, sc->sc_txdmadesc_map, sc->sc_txdmadesc, 128*sizeof(struct glc_dmadesc), glc_getphys, &sc->sc_txdmadesc_phys, 0); err = bus_dmamem_alloc(sc->sc_dmadesc_tag, (void **)&sc->sc_rxdmadesc, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->sc_rxdmadesc_map); err = bus_dmamap_load(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map, sc->sc_rxdmadesc, 128*sizeof(struct glc_dmadesc), glc_getphys, &sc->sc_rxdmadesc_phys, 0); err = bus_dma_tag_create(bus_get_dma_tag(dev), 128, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, 0, BUS_SPACE_MAXSIZE_32BIT, 0, NULL,NULL, &sc->sc_rxdma_tag); err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, 16, BUS_SPACE_MAXSIZE_32BIT, 0, NULL,NULL, &sc->sc_txdma_tag); /* init transmit descriptors */ STAILQ_INIT(&sc->sc_txfreeq); STAILQ_INIT(&sc->sc_txdirtyq); /* create TX DMA maps */ err = ENOMEM; for (i = 0; i < GLC_MAX_TX_PACKETS; i++) { txs = &sc->sc_txsoft[i]; txs->txs_mbuf = NULL; err = bus_dmamap_create(sc->sc_txdma_tag, 0, &txs->txs_dmamap); if (err) { device_printf(dev, "unable to create TX DMA map %d, error = %d\n", i, err); } STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q); } /* Create the receive buffer DMA maps. */ for (i = 0; i < GLC_MAX_RX_PACKETS; i++) { err = bus_dmamap_create(sc->sc_rxdma_tag, 0, &sc->sc_rxsoft[i].rxs_dmamap); if (err) { device_printf(dev, "unable to create RX DMA map %d, error = %d\n", i, err); } sc->sc_rxsoft[i].rxs_mbuf = NULL; } /* * Attach to network stack */ if_initname(sc->sc_ifp, device_get_name(dev), device_get_unit(dev)); sc->sc_ifp->if_mtu = ETHERMTU; sc->sc_ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; sc->sc_ifp->if_hwassist = CSUM_TCP | CSUM_UDP; sc->sc_ifp->if_capabilities = IFCAP_HWCSUM | IFCAP_RXCSUM; sc->sc_ifp->if_capenable = IFCAP_HWCSUM | IFCAP_RXCSUM; sc->sc_ifp->if_start = glc_start; sc->sc_ifp->if_ioctl = glc_ioctl; sc->sc_ifp->if_init = glc_init; ifmedia_init(&sc->sc_media, IFM_IMASK, glc_media_change, glc_media_status); ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_10_T, 0, NULL); ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL); ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_100_TX, 0, NULL); ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL); ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL); ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL); ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO); IFQ_SET_MAXLEN(&sc->sc_ifp->if_snd, GLC_MAX_TX_PACKETS); sc->sc_ifp->if_snd.ifq_drv_maxlen = GLC_MAX_TX_PACKETS; IFQ_SET_READY(&sc->sc_ifp->if_snd); ether_ifattach(sc->sc_ifp, sc->sc_enaddr); sc->sc_ifp->if_hwassist = 0; return (0); mtx_destroy(&sc->sc_mtx); if_free(sc->sc_ifp); return (ENXIO); } static void glc_init_locked(struct glc_softc *sc) { int i, error; struct glc_rxsoft *rxs; struct glc_txsoft *txs; mtx_assert(&sc->sc_mtx, MA_OWNED); lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0); lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0); glc_set_multicast(sc); for (i = 0; i < GLC_MAX_RX_PACKETS; i++) { rxs = &sc->sc_rxsoft[i]; rxs->rxs_desc_slot = i; if (rxs->rxs_mbuf == NULL) { glc_add_rxbuf(sc, i); if (rxs->rxs_mbuf == NULL) { rxs->rxs_desc_slot = -1; break; } } glc_add_rxbuf_dma(sc, i); bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map, BUS_DMASYNC_PREREAD); } /* Clear TX dirty queue */ while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q); bus_dmamap_unload(sc->sc_txdma_tag, txs->txs_dmamap); if (txs->txs_mbuf != NULL) { m_freem(txs->txs_mbuf); txs->txs_mbuf = NULL; } STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q); } sc->first_used_txdma_slot = -1; sc->bsy_txdma_slots = 0; error = lv1_net_start_rx_dma(sc->sc_bus, sc->sc_dev, sc->sc_rxsoft[0].rxs_desc, 0); if (error != 0) device_printf(sc->sc_self, "lv1_net_start_rx_dma error: %d\n", error); sc->sc_ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->sc_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->sc_ifpflags = sc->sc_ifp->if_flags; sc->sc_wdog_timer = 0; callout_reset(&sc->sc_tick_ch, hz, glc_tick, sc); } static void glc_stop(void *xsc) { struct glc_softc *sc = xsc; mtx_assert(&sc->sc_mtx, MA_OWNED); lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0); lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0); } static void glc_init(void *xsc) { struct glc_softc *sc = xsc; mtx_lock(&sc->sc_mtx); glc_init_locked(sc); mtx_unlock(&sc->sc_mtx); } static void glc_tick(void *xsc) { struct glc_softc *sc = xsc; mtx_assert(&sc->sc_mtx, MA_OWNED); /* * XXX: Sometimes the RX queue gets stuck. Poke it periodically until * we figure out why. This will fail harmlessly if the RX queue is * already running. */ lv1_net_start_rx_dma(sc->sc_bus, sc->sc_dev, sc->sc_rxsoft[sc->sc_next_rxdma_slot].rxs_desc, 0); if (sc->sc_wdog_timer == 0 || --sc->sc_wdog_timer != 0) { callout_reset(&sc->sc_tick_ch, hz, glc_tick, sc); return; } /* Problems */ device_printf(sc->sc_self, "device timeout\n"); glc_init_locked(sc); } static void glc_start_locked(struct ifnet *ifp) { struct glc_softc *sc = ifp->if_softc; bus_addr_t first, pktdesc; int kickstart = 0; int error; struct mbuf *mb_head; mtx_assert(&sc->sc_mtx, MA_OWNED); first = 0; if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) return; if (STAILQ_EMPTY(&sc->sc_txdirtyq)) kickstart = 1; while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { IFQ_DRV_DEQUEUE(&ifp->if_snd, mb_head); if (mb_head == NULL) break; /* Check if the ring buffer is full */ if (sc->bsy_txdma_slots > 125) { /* Put the packet back and stop */ ifp->if_drv_flags |= IFF_DRV_OACTIVE; IFQ_DRV_PREPEND(&ifp->if_snd, mb_head); break; } BPF_MTAP(ifp, mb_head); if (sc->sc_tx_vlan >= 0) mb_head = ether_vlanencap(mb_head, sc->sc_tx_vlan); if (glc_encap(sc, &mb_head, &pktdesc)) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } if (first == 0) first = pktdesc; } if (kickstart && first != 0) { error = lv1_net_start_tx_dma(sc->sc_bus, sc->sc_dev, first, 0); if (error != 0) device_printf(sc->sc_self, "lv1_net_start_tx_dma error: %d\n", error); sc->sc_wdog_timer = 5; } } static void glc_start(struct ifnet *ifp) { struct glc_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); glc_start_locked(ifp); mtx_unlock(&sc->sc_mtx); } static int glc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct glc_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int err = 0; switch (cmd) { case SIOCSIFFLAGS: mtx_lock(&sc->sc_mtx); if ((ifp->if_flags & IFF_UP) != 0) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 && ((ifp->if_flags ^ sc->sc_ifpflags) & (IFF_ALLMULTI | IFF_PROMISC)) != 0) glc_set_multicast(sc); else glc_init_locked(sc); } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) glc_stop(sc); sc->sc_ifpflags = ifp->if_flags; mtx_unlock(&sc->sc_mtx); break; case SIOCADDMULTI: case SIOCDELMULTI: mtx_lock(&sc->sc_mtx); glc_set_multicast(sc); mtx_unlock(&sc->sc_mtx); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: err = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); break; default: err = ether_ioctl(ifp, cmd, data); break; } return (err); } static void glc_set_multicast(struct glc_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ifmultiaddr *inm; uint64_t addr; int naddrs; /* Clear multicast filter */ lv1_net_remove_multicast_address(sc->sc_bus, sc->sc_dev, 0, 1); /* Add broadcast */ lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev, 0xffffffffffffL, 0); if ((ifp->if_flags & IFF_ALLMULTI) != 0) { lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev, 0, 1); } else { if_maddr_rlock(ifp); naddrs = 1; /* Include broadcast */ TAILQ_FOREACH(inm, &ifp->if_multiaddrs, ifma_link) { if (inm->ifma_addr->sa_family != AF_LINK) continue; addr = 0; memcpy(&((uint8_t *)(&addr))[2], LLADDR((struct sockaddr_dl *)inm->ifma_addr), ETHER_ADDR_LEN); lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev, addr, 0); /* * Filter can only hold 32 addresses, so fall back to * the IFF_ALLMULTI case if we have too many. */ if (++naddrs >= 32) { lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev, 0, 1); break; } } if_maddr_runlock(ifp); } } static int glc_add_rxbuf(struct glc_softc *sc, int idx) { struct glc_rxsoft *rxs = &sc->sc_rxsoft[idx]; struct mbuf *m; bus_dma_segment_t segs[1]; int error, nsegs; m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return (ENOBUFS); m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; if (rxs->rxs_mbuf != NULL) { bus_dmamap_sync(sc->sc_rxdma_tag, rxs->rxs_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->sc_rxdma_tag, rxs->rxs_dmamap); } error = bus_dmamap_load_mbuf_sg(sc->sc_rxdma_tag, rxs->rxs_dmamap, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_self, "cannot load RS DMA map %d, error = %d\n", idx, error); m_freem(m); return (error); } /* If nsegs is wrong then the stack is corrupt. */ KASSERT(nsegs == 1, ("%s: too many DMA segments (%d)", __func__, nsegs)); rxs->rxs_mbuf = m; rxs->segment = segs[0]; bus_dmamap_sync(sc->sc_rxdma_tag, rxs->rxs_dmamap, BUS_DMASYNC_PREREAD); return (0); } static int glc_add_rxbuf_dma(struct glc_softc *sc, int idx) { struct glc_rxsoft *rxs = &sc->sc_rxsoft[idx]; bzero(&sc->sc_rxdmadesc[idx], sizeof(sc->sc_rxdmadesc[idx])); sc->sc_rxdmadesc[idx].paddr = rxs->segment.ds_addr; sc->sc_rxdmadesc[idx].len = rxs->segment.ds_len; sc->sc_rxdmadesc[idx].next = sc->sc_rxdmadesc_phys + ((idx + 1) % GLC_MAX_RX_PACKETS)*sizeof(sc->sc_rxdmadesc[idx]); sc->sc_rxdmadesc[idx].cmd_stat = GELIC_DESCR_OWNED; rxs->rxs_desc_slot = idx; rxs->rxs_desc = sc->sc_rxdmadesc_phys + idx*sizeof(struct glc_dmadesc); return (0); } static int glc_encap(struct glc_softc *sc, struct mbuf **m_head, bus_addr_t *pktdesc) { bus_dma_segment_t segs[16]; struct glc_txsoft *txs; struct mbuf *m; bus_addr_t firstslotphys; int i, idx, nsegs, nsegs_max; int err = 0; /* Max number of segments is the number of free DMA slots */ nsegs_max = 128 - sc->bsy_txdma_slots; if (nsegs_max > 16 || sc->first_used_txdma_slot < 0) nsegs_max = 16; /* Get a work queue entry. */ if ((txs = STAILQ_FIRST(&sc->sc_txfreeq)) == NULL) { /* Ran out of descriptors. */ return (ENOBUFS); } nsegs = 0; for (m = *m_head; m != NULL; m = m->m_next) nsegs++; if (nsegs > nsegs_max) { m = m_collapse(*m_head, M_NOWAIT, nsegs_max); if (m == NULL) { m_freem(*m_head); *m_head = NULL; return (ENOBUFS); } *m_head = m; } err = bus_dmamap_load_mbuf_sg(sc->sc_txdma_tag, txs->txs_dmamap, *m_head, segs, &nsegs, BUS_DMA_NOWAIT); if (err != 0) { m_freem(*m_head); *m_head = NULL; return (err); } KASSERT(nsegs <= 128 - sc->bsy_txdma_slots, ("GLC: Mapped too many (%d) DMA segments with %d available", nsegs, 128 - sc->bsy_txdma_slots)); if (nsegs == 0) { m_freem(*m_head); *m_head = NULL; return (EIO); } txs->txs_ndescs = nsegs; txs->txs_firstdesc = sc->next_txdma_slot; idx = txs->txs_firstdesc; firstslotphys = sc->sc_txdmadesc_phys + txs->txs_firstdesc*sizeof(struct glc_dmadesc); for (i = 0; i < nsegs; i++) { bzero(&sc->sc_txdmadesc[idx], sizeof(sc->sc_txdmadesc[idx])); sc->sc_txdmadesc[idx].paddr = segs[i].ds_addr; sc->sc_txdmadesc[idx].len = segs[i].ds_len; sc->sc_txdmadesc[idx].next = sc->sc_txdmadesc_phys + ((idx + 1) % GLC_MAX_TX_PACKETS)*sizeof(struct glc_dmadesc); sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_NOIPSEC; if (i+1 == nsegs) { txs->txs_lastdesc = idx; sc->sc_txdmadesc[idx].next = 0; sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_LAST; } if ((*m_head)->m_pkthdr.csum_flags & CSUM_TCP) sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_CSUM_TCP; if ((*m_head)->m_pkthdr.csum_flags & CSUM_UDP) sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_CSUM_UDP; sc->sc_txdmadesc[idx].cmd_stat |= GELIC_DESCR_OWNED; idx = (idx + 1) % GLC_MAX_TX_PACKETS; } sc->next_txdma_slot = idx; sc->bsy_txdma_slots += nsegs; if (txs->txs_firstdesc != 0) idx = txs->txs_firstdesc - 1; else idx = GLC_MAX_TX_PACKETS - 1; if (sc->first_used_txdma_slot < 0) sc->first_used_txdma_slot = txs->txs_firstdesc; bus_dmamap_sync(sc->sc_txdma_tag, txs->txs_dmamap, BUS_DMASYNC_PREWRITE); sc->sc_txdmadesc[idx].next = firstslotphys; STAILQ_REMOVE_HEAD(&sc->sc_txfreeq, txs_q); STAILQ_INSERT_TAIL(&sc->sc_txdirtyq, txs, txs_q); txs->txs_mbuf = *m_head; *pktdesc = firstslotphys; return (0); } static void glc_rxintr(struct glc_softc *sc) { int i, restart_rxdma, error; struct mbuf *m; struct ifnet *ifp = sc->sc_ifp; bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map, BUS_DMASYNC_POSTREAD); restart_rxdma = 0; while ((sc->sc_rxdmadesc[sc->sc_next_rxdma_slot].cmd_stat & GELIC_DESCR_OWNED) == 0) { i = sc->sc_next_rxdma_slot; sc->sc_next_rxdma_slot++; if (sc->sc_next_rxdma_slot >= GLC_MAX_RX_PACKETS) sc->sc_next_rxdma_slot = 0; if (sc->sc_rxdmadesc[i].cmd_stat & GELIC_CMDSTAT_CHAIN_END) restart_rxdma = 1; if (sc->sc_rxdmadesc[i].rxerror & GELIC_RXERRORS) { if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto requeue; } m = sc->sc_rxsoft[i].rxs_mbuf; if (sc->sc_rxdmadesc[i].data_stat & GELIC_RX_IPCSUM) { m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED | CSUM_IP_VALID; } if (sc->sc_rxdmadesc[i].data_stat & GELIC_RX_TCPUDPCSUM) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } if (glc_add_rxbuf(sc, i)) { if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto requeue; } if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); m->m_pkthdr.rcvif = ifp; m->m_len = sc->sc_rxdmadesc[i].valid_size; m->m_pkthdr.len = m->m_len; /* * Remove VLAN tag. Even on early firmwares that do not allow * multiple VLANs, the VLAN tag is still in place here. */ m_adj(m, 2); mtx_unlock(&sc->sc_mtx); (*ifp->if_input)(ifp, m); mtx_lock(&sc->sc_mtx); requeue: glc_add_rxbuf_dma(sc, i); } bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map, BUS_DMASYNC_PREWRITE); if (restart_rxdma) { error = lv1_net_start_rx_dma(sc->sc_bus, sc->sc_dev, sc->sc_rxsoft[sc->sc_next_rxdma_slot].rxs_desc, 0); if (error != 0) device_printf(sc->sc_self, "lv1_net_start_rx_dma error: %d\n", error); } } static void glc_txintr(struct glc_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct glc_txsoft *txs; int progress = 0, kickstart = 0, error; bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_txdmadesc_map, BUS_DMASYNC_POSTREAD); while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) { if (sc->sc_txdmadesc[txs->txs_lastdesc].cmd_stat & GELIC_DESCR_OWNED) break; STAILQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q); bus_dmamap_unload(sc->sc_txdma_tag, txs->txs_dmamap); sc->bsy_txdma_slots -= txs->txs_ndescs; if (txs->txs_mbuf != NULL) { m_freem(txs->txs_mbuf); txs->txs_mbuf = NULL; } if ((sc->sc_txdmadesc[txs->txs_lastdesc].cmd_stat & 0xf0000000) != 0) { lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0); kickstart = 1; if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } if (sc->sc_txdmadesc[txs->txs_lastdesc].cmd_stat & GELIC_CMDSTAT_CHAIN_END) kickstart = 1; STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q); if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); progress = 1; } if (txs != NULL) sc->first_used_txdma_slot = txs->txs_firstdesc; else sc->first_used_txdma_slot = -1; if (kickstart || txs != NULL) { /* Speculatively (or necessarily) start the TX queue again */ error = lv1_net_start_tx_dma(sc->sc_bus, sc->sc_dev, sc->sc_txdmadesc_phys + txs->txs_firstdesc*sizeof(struct glc_dmadesc), 0); if (error != 0) device_printf(sc->sc_self, "lv1_net_start_tx_dma error: %d\n", error); } if (progress) { /* * We freed some descriptors, so reset IFF_DRV_OACTIVE * and restart. */ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->sc_wdog_timer = STAILQ_EMPTY(&sc->sc_txdirtyq) ? 0 : 5; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && !IFQ_DRV_IS_EMPTY(&ifp->if_snd)) glc_start_locked(ifp); } } static int glc_intr_filter(void *xsc) { struct glc_softc *sc = xsc; powerpc_sync(); atomic_set_64(&sc->sc_interrupt_status, *sc->sc_hwirq_status); return (FILTER_SCHEDULE_THREAD); } static void glc_intr(void *xsc) { struct glc_softc *sc = xsc; uint64_t status, linkstat, junk; mtx_lock(&sc->sc_mtx); status = atomic_readandclear_64(&sc->sc_interrupt_status); if (status == 0) { mtx_unlock(&sc->sc_mtx); return; } if (status & (GELIC_INT_RXDONE | GELIC_INT_RXFRAME)) glc_rxintr(sc); if (status & (GELIC_INT_TXDONE | GELIC_INT_TX_CHAIN_END)) glc_txintr(sc); if (status & GELIC_INT_PHY) { lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_LINK_STATUS, GELIC_VLAN_TX_ETHERNET, 0, 0, &linkstat, &junk); linkstat = (linkstat & GELIC_LINK_UP) ? LINK_STATE_UP : LINK_STATE_DOWN; if (linkstat != sc->sc_ifp->if_link_state) if_link_state_change(sc->sc_ifp, linkstat); } mtx_unlock(&sc->sc_mtx); } static void glc_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) { struct glc_softc *sc = ifp->if_softc; uint64_t status, junk; ifmr->ifm_status = IFM_AVALID; ifmr->ifm_active = IFM_ETHER; lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_LINK_STATUS, GELIC_VLAN_TX_ETHERNET, 0, 0, &status, &junk); if (status & GELIC_LINK_UP) ifmr->ifm_status |= IFM_ACTIVE; if (status & GELIC_SPEED_10) ifmr->ifm_active |= IFM_10_T; else if (status & GELIC_SPEED_100) ifmr->ifm_active |= IFM_100_TX; else if (status & GELIC_SPEED_1000) ifmr->ifm_active |= IFM_1000_T; if (status & GELIC_FULL_DUPLEX) ifmr->ifm_active |= IFM_FDX; else ifmr->ifm_active |= IFM_HDX; } static int glc_media_change(struct ifnet *ifp) { struct glc_softc *sc = ifp->if_softc; uint64_t mode, junk; int result; if (IFM_TYPE(sc->sc_media.ifm_media) != IFM_ETHER) return (EINVAL); switch (IFM_SUBTYPE(sc->sc_media.ifm_media)) { case IFM_AUTO: mode = GELIC_AUTO_NEG; break; case IFM_10_T: mode = GELIC_SPEED_10; break; case IFM_100_TX: mode = GELIC_SPEED_100; break; case IFM_1000_T: mode = GELIC_SPEED_1000 | GELIC_FULL_DUPLEX; break; default: return (EINVAL); } if (IFM_OPTIONS(sc->sc_media.ifm_media) & IFM_FDX) mode |= GELIC_FULL_DUPLEX; result = lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_SET_LINK_MODE, GELIC_VLAN_TX_ETHERNET, mode, 0, &junk, &junk); return (result ? EIO : 0); } Index: head/sys/powerpc/ps3/if_glcreg.h =================================================================== --- head/sys/powerpc/ps3/if_glcreg.h (revision 326260) +++ head/sys/powerpc/ps3/if_glcreg.h (revision 326261) @@ -1,160 +1,162 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2010 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _POWERPC_PS3_IF_GLCREG_H #define _POWERPC_PS3_IF_GLCREG_H #define GLC_MAX_TX_PACKETS 128 #define GLC_MAX_RX_PACKETS 128 struct glc_dmadesc; /* * software state for transmit job mbufs (may be elements of mbuf chains) */ struct glc_txsoft { struct mbuf *txs_mbuf; /* head of our mbuf chain */ bus_dmamap_t txs_dmamap; /* our DMA map */ int txs_firstdesc; /* first descriptor in packet */ int txs_lastdesc; /* last descriptor in packet */ int txs_ndescs; /* number of descriptors */ STAILQ_ENTRY(glc_txsoft) txs_q; }; STAILQ_HEAD(glc_txsq, glc_txsoft); /* * software state for receive jobs */ struct glc_rxsoft { struct mbuf *rxs_mbuf; /* head of our mbuf chain */ bus_dmamap_t rxs_dmamap; /* our DMA map */ int rxs_desc_slot; /* DMA descriptor for this packet */ bus_addr_t rxs_desc; bus_dma_segment_t segment; }; struct glc_softc { struct ifnet *sc_ifp; device_t sc_self; struct mtx sc_mtx; u_char sc_enaddr[ETHER_ADDR_LEN]; int sc_tx_vlan, sc_rx_vlan; int sc_ifpflags; uint64_t sc_dma_base[5]; bus_dma_tag_t sc_dmadesc_tag; int sc_irqid; struct resource *sc_irq; void *sc_irqctx; uint64_t *sc_hwirq_status; volatile uint64_t sc_interrupt_status; struct ifmedia sc_media; /* Transmission */ bus_dma_tag_t sc_txdma_tag; struct glc_txsoft sc_txsoft[GLC_MAX_TX_PACKETS]; struct glc_dmadesc *sc_txdmadesc; int next_txdma_slot, first_used_txdma_slot, bsy_txdma_slots; bus_dmamap_t sc_txdmadesc_map; bus_addr_t sc_txdmadesc_phys; struct glc_txsq sc_txfreeq; struct glc_txsq sc_txdirtyq; /* Reception */ bus_dma_tag_t sc_rxdma_tag; struct glc_rxsoft sc_rxsoft[GLC_MAX_RX_PACKETS]; struct glc_dmadesc *sc_rxdmadesc; int sc_next_rxdma_slot; bus_dmamap_t sc_rxdmadesc_map; bus_addr_t sc_rxdmadesc_phys; int sc_bus, sc_dev; int sc_wdog_timer; struct callout sc_tick_ch; }; #define GELIC_GET_MAC_ADDRESS 0x0001 #define GELIC_GET_LINK_STATUS 0x0002 #define GELIC_SET_LINK_MODE 0x0003 #define GELIC_LINK_UP 0x0001 #define GELIC_FULL_DUPLEX 0x0002 #define GELIC_AUTO_NEG 0x0004 #define GELIC_SPEED_10 0x0010 #define GELIC_SPEED_100 0x0020 #define GELIC_SPEED_1000 0x0040 #define GELIC_GET_VLAN_ID 0x0004 #define GELIC_VLAN_TX_ETHERNET 0x0002 #define GELIC_VLAN_RX_ETHERNET 0x0012 #define GELIC_VLAN_TX_WIRELESS 0x0003 #define GELIC_VLAN_RX_WIRELESS 0x0013 /* Command status code */ #define GELIC_DESCR_OWNED 0xa0000000 #define GELIC_CMDSTAT_DMA_DONE 0x00000000 #define GELIC_CMDSTAT_CHAIN_END 0x00000002 #define GELIC_CMDSTAT_CSUM_TCP 0x00020000 #define GELIC_CMDSTAT_CSUM_UDP 0x00030000 #define GELIC_CMDSTAT_NOIPSEC 0x00080000 #define GELIC_CMDSTAT_LAST 0x00040000 #define GELIC_RXERRORS 0x7def8000 /* RX Data Status codes */ #define GELIC_RX_IPCSUM 0x20000000 #define GELIC_RX_TCPUDPCSUM 0x10000000 /* Interrupt options */ #define GELIC_INT_RXDONE 0x0000000000004000UL #define GELIC_INT_RXFRAME 0x1000000000000000UL #define GELIC_INT_TXDONE 0x0080000000000000UL #define GELIC_INT_TX_CHAIN_END 0x0100000000000000UL #define GELIC_INT_PHY 0x0000000020000000UL /* Hardware DMA descriptor. Must be 32-byte aligned */ struct glc_dmadesc { uint32_t paddr; /* Must be 128 byte aligned for receive */ uint32_t len; uint32_t next; uint32_t cmd_stat; uint32_t result_size; uint32_t valid_size; uint32_t data_stat; uint32_t rxerror; }; #endif /* _POWERPC_PS3_IF_GLCREG_H */ Index: head/sys/powerpc/ps3/mmu_ps3.c =================================================================== --- head/sys/powerpc/ps3/mmu_ps3.c (revision 326260) +++ head/sys/powerpc/ps3/mmu_ps3.c (revision 326261) @@ -1,283 +1,285 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2010 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mmu_if.h" #include "moea64_if.h" #include "ps3-hvcall.h" #define VSID_HASH_MASK 0x0000007fffffffffUL #define PTESYNC() __asm __volatile("ptesync") extern int ps3fb_remap(void); static uint64_t mps3_vas_id; /* * Kernel MMU interface */ static void mps3_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend); static void mps3_cpu_bootstrap(mmu_t mmup, int ap); static int64_t mps3_pte_synch(mmu_t, struct pvo_entry *); static int64_t mps3_pte_clear(mmu_t, struct pvo_entry *, uint64_t ptebit); static int64_t mps3_pte_unset(mmu_t, struct pvo_entry *); static int mps3_pte_insert(mmu_t, struct pvo_entry *); static mmu_method_t mps3_methods[] = { MMUMETHOD(mmu_bootstrap, mps3_bootstrap), MMUMETHOD(mmu_cpu_bootstrap, mps3_cpu_bootstrap), MMUMETHOD(moea64_pte_synch, mps3_pte_synch), MMUMETHOD(moea64_pte_clear, mps3_pte_clear), MMUMETHOD(moea64_pte_unset, mps3_pte_unset), MMUMETHOD(moea64_pte_insert, mps3_pte_insert), { 0, 0 } }; MMU_DEF_INHERIT(ps3_mmu, "mmu_ps3", mps3_methods, 0, oea64_mmu); static struct mtx mps3_table_lock; static void mps3_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) { uint64_t final_pteg_count; mtx_init(&mps3_table_lock, "page table", NULL, MTX_DEF); moea64_early_bootstrap(mmup, kernelstart, kernelend); lv1_construct_virtual_address_space( 20 /* log_2(moea64_pteg_count) */, 2 /* n page sizes */, (24UL << 56) | (16UL << 48) /* page sizes 16 MB + 64 KB */, &mps3_vas_id, &final_pteg_count ); moea64_pteg_count = final_pteg_count / sizeof(struct lpteg); moea64_mid_bootstrap(mmup, kernelstart, kernelend); moea64_late_bootstrap(mmup, kernelstart, kernelend); } static void mps3_cpu_bootstrap(mmu_t mmup, int ap) { struct slb *slb = PCPU_GET(slb); register_t seg0; int i; mtmsr(mfmsr() & ~PSL_DR & ~PSL_IR); /* * Destroy the loader's address space if we are coming up for * the first time, and redo the FB mapping so we can continue * having a console. */ if (!ap) lv1_destruct_virtual_address_space(0); lv1_select_virtual_address_space(mps3_vas_id); if (!ap) ps3fb_remap(); /* * Install kernel SLB entries */ __asm __volatile ("slbia"); __asm __volatile ("slbmfee %0,%1; slbie %0;" : "=r"(seg0) : "r"(0)); for (i = 0; i < 64; i++) { if (!(slb[i].slbe & SLBE_VALID)) continue; __asm __volatile ("slbmte %0, %1" :: "r"(slb[i].slbv), "r"(slb[i].slbe)); } } static int64_t mps3_pte_synch_locked(struct pvo_entry *pvo) { uint64_t halfbucket[4], rcbits; PTESYNC(); lv1_read_htab_entries(mps3_vas_id, pvo->pvo_pte.slot & ~0x3UL, &halfbucket[0], &halfbucket[1], &halfbucket[2], &halfbucket[3], &rcbits); /* Check if present in page table */ if ((halfbucket[pvo->pvo_pte.slot & 0x3] & LPTE_AVPN_MASK) != ((pvo->pvo_vpn >> (ADDR_API_SHFT64 - ADDR_PIDX_SHFT)) & LPTE_AVPN_MASK)) return (-1); if (!(halfbucket[pvo->pvo_pte.slot & 0x3] & LPTE_VALID)) return (-1); /* * rcbits contains the low 12 bits of each PTE's 2nd part, * spaced at 16-bit intervals */ return ((rcbits >> ((3 - (pvo->pvo_pte.slot & 0x3))*16)) & (LPTE_CHG | LPTE_REF)); } static int64_t mps3_pte_synch(mmu_t mmu, struct pvo_entry *pvo) { int64_t retval; mtx_lock(&mps3_table_lock); retval = mps3_pte_synch_locked(pvo); mtx_unlock(&mps3_table_lock); return (retval); } static int64_t mps3_pte_clear(mmu_t mmu, struct pvo_entry *pvo, uint64_t ptebit) { int64_t refchg; struct lpte pte; mtx_lock(&mps3_table_lock); refchg = mps3_pte_synch_locked(pvo); if (refchg < 0) { mtx_unlock(&mps3_table_lock); return (refchg); } moea64_pte_from_pvo(pvo, &pte); pte.pte_lo |= refchg; pte.pte_lo &= ~ptebit; /* XXX: race on RC bits between write and sync. Anything to do? */ lv1_write_htab_entry(mps3_vas_id, pvo->pvo_pte.slot, pte.pte_hi, pte.pte_lo); mtx_unlock(&mps3_table_lock); return (refchg); } static int64_t mps3_pte_unset(mmu_t mmu, struct pvo_entry *pvo) { int64_t refchg; mtx_lock(&mps3_table_lock); refchg = mps3_pte_synch_locked(pvo); if (refchg < 0) { moea64_pte_overflow--; mtx_unlock(&mps3_table_lock); return (-1); } /* XXX: race on RC bits between unset and sync. Anything to do? */ lv1_write_htab_entry(mps3_vas_id, pvo->pvo_pte.slot, 0, 0); mtx_unlock(&mps3_table_lock); moea64_pte_valid--; return (refchg & (LPTE_REF | LPTE_CHG)); } static int mps3_pte_insert(mmu_t mmu, struct pvo_entry *pvo) { int result; struct lpte pte, evicted; uint64_t index; if (pvo->pvo_vaddr & PVO_HID) { /* Hypercall needs primary PTEG */ pvo->pvo_vaddr &= ~PVO_HID; pvo->pvo_pte.slot ^= (moea64_pteg_mask << 3); } pvo->pvo_pte.slot &= ~7UL; moea64_pte_from_pvo(pvo, &pte); evicted.pte_hi = 0; PTESYNC(); mtx_lock(&mps3_table_lock); result = lv1_insert_htab_entry(mps3_vas_id, pvo->pvo_pte.slot, pte.pte_hi, pte.pte_lo, LPTE_LOCKED | LPTE_WIRED, 0, &index, &evicted.pte_hi, &evicted.pte_lo); mtx_unlock(&mps3_table_lock); if (result != 0) { /* No freeable slots in either PTEG? We're hosed. */ panic("mps3_pte_insert: overflow (%d)", result); return (-1); } /* * See where we ended up. */ if ((index & ~7UL) != pvo->pvo_pte.slot) pvo->pvo_vaddr |= PVO_HID; pvo->pvo_pte.slot = index; moea64_pte_valid++; if (evicted.pte_hi) { KASSERT((evicted.pte_hi & (LPTE_WIRED | LPTE_LOCKED)) == 0, ("Evicted a wired PTE")); moea64_pte_valid--; moea64_pte_overflow++; } return (0); } Index: head/sys/powerpc/ps3/ohci_ps3.c =================================================================== --- head/sys/powerpc/ps3/ohci_ps3.c (revision 326260) +++ head/sys/powerpc/ps3/ohci_ps3.c (revision 326261) @@ -1,172 +1,174 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2010 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ps3bus.h" static int ohci_ps3_probe(device_t dev) { if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_SYSBUS || ps3bus_get_devtype(dev) != PS3_DEVTYPE_USB) return (ENXIO); device_set_desc(dev, "Playstation 3 USB 2.0 controller"); return (BUS_PROBE_SPECIFIC); } static int ohci_ps3_attach(device_t dev) { ohci_softc_t *sc = device_get_softc(dev); int rid, err; sc->sc_bus.parent = dev; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = OHCI_MAX_DEVICES; sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) return (ENOMEM); rid = 0; sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(dev, "Could not map memory\n"); goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(dev, "Could not allocate irq\n"); return (ENXIO); } sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(dev, "Could not add USB device\n"); return (ENXIO); } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); sprintf(sc->sc_vendor, "Sony"); err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ohci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(dev, "Could not setup error irq, %d\n", err); goto error; } //sc->sc_flags |= EHCI_SCFLG_BIGEMMIO; bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, OHCI_CONTROL, 0); err = ohci_init(sc); if (err) { device_printf(dev, "USB init failed err=%d\n", err); goto error; } err = device_probe_and_attach(sc->sc_bus.bdev); if (err == 0) return (0); error: return (ENXIO); } static device_method_t ohci_ps3_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ohci_ps3_probe), DEVMETHOD(device_attach, ohci_ps3_attach), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ohci_ps3_driver = { .name = "ohci", .methods = ohci_ps3_methods, .size = sizeof(ohci_softc_t), }; static devclass_t ohci_ps3_devclass; DRIVER_MODULE(ohci_ps3, ps3bus, ohci_ps3_driver, ohci_ps3_devclass, 0, 0); MODULE_DEPEND(ohci_ps3, usb, 1, 1, 1); Index: head/sys/powerpc/ps3/platform_ps3.c =================================================================== --- head/sys/powerpc/ps3/platform_ps3.c (revision 326260) +++ head/sys/powerpc/ps3/platform_ps3.c (revision 326261) @@ -1,269 +1,271 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2010 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "platform_if.h" #include "ps3-hvcall.h" #ifdef SMP extern void *ap_pcpu; #endif static int ps3_probe(platform_t); static int ps3_attach(platform_t); static void ps3_mem_regions(platform_t, struct mem_region *phys, int *physsz, struct mem_region *avail, int *availsz); static vm_offset_t ps3_real_maxaddr(platform_t); static u_long ps3_timebase_freq(platform_t, struct cpuref *cpuref); #ifdef SMP static int ps3_smp_first_cpu(platform_t, struct cpuref *cpuref); static int ps3_smp_next_cpu(platform_t, struct cpuref *cpuref); static int ps3_smp_get_bsp(platform_t, struct cpuref *cpuref); static int ps3_smp_start_cpu(platform_t, struct pcpu *cpu); static struct cpu_group *ps3_smp_topo(platform_t); #endif static void ps3_reset(platform_t); static void ps3_cpu_idle(sbintime_t); static platform_method_t ps3_methods[] = { PLATFORMMETHOD(platform_probe, ps3_probe), PLATFORMMETHOD(platform_attach, ps3_attach), PLATFORMMETHOD(platform_mem_regions, ps3_mem_regions), PLATFORMMETHOD(platform_real_maxaddr, ps3_real_maxaddr), PLATFORMMETHOD(platform_timebase_freq, ps3_timebase_freq), #ifdef SMP PLATFORMMETHOD(platform_smp_first_cpu, ps3_smp_first_cpu), PLATFORMMETHOD(platform_smp_next_cpu, ps3_smp_next_cpu), PLATFORMMETHOD(platform_smp_get_bsp, ps3_smp_get_bsp), PLATFORMMETHOD(platform_smp_start_cpu, ps3_smp_start_cpu), PLATFORMMETHOD(platform_smp_topo, ps3_smp_topo), #endif PLATFORMMETHOD(platform_reset, ps3_reset), PLATFORMMETHOD_END }; static platform_def_t ps3_platform = { "ps3", ps3_methods, 0 }; PLATFORM_DEF(ps3_platform); static int ps3_probe(platform_t plat) { phandle_t root; char compatible[64]; root = OF_finddevice("/"); if (OF_getprop(root, "compatible", compatible, sizeof(compatible)) <= 0) return (BUS_PROBE_NOWILDCARD); if (strncmp(compatible, "sony,ps3", sizeof(compatible)) != 0) return (BUS_PROBE_NOWILDCARD); return (BUS_PROBE_SPECIFIC); } static int ps3_attach(platform_t plat) { pmap_mmu_install("mmu_ps3", BUS_PROBE_SPECIFIC); cpu_idle_hook = ps3_cpu_idle; /* Set a breakpoint to make NULL an invalid address */ lv1_set_dabr(0x7 /* read and write, MMU on */, 2 /* kernel accesses */); return (0); } void ps3_mem_regions(platform_t plat, struct mem_region *phys, int *physsz, struct mem_region *avail_regions, int *availsz) { uint64_t lpar_id, junk, ppe_id; /* Get real mode memory region */ avail_regions[0].mr_start = 0; lv1_get_logical_partition_id(&lpar_id); lv1_get_logical_ppe_id(&ppe_id); lv1_get_repository_node_value(lpar_id, lv1_repository_string("bi") >> 32, lv1_repository_string("pu"), ppe_id, lv1_repository_string("rm_size"), &avail_regions[0].mr_size, &junk); /* Now get extended memory region */ lv1_get_repository_node_value(lpar_id, lv1_repository_string("bi") >> 32, lv1_repository_string("rgntotal"), 0, 0, &avail_regions[1].mr_size, &junk); /* Convert to maximum amount we can allocate in 16 MB pages */ avail_regions[1].mr_size -= avail_regions[0].mr_size; avail_regions[1].mr_size -= avail_regions[1].mr_size % (16*1024*1024); /* Allocate extended memory region */ lv1_allocate_memory(avail_regions[1].mr_size, 24 /* 16 MB pages */, 0, 0x04 /* any address */, &avail_regions[1].mr_start, &junk); *availsz = 2; if (phys != NULL) { memcpy(phys, avail_regions, sizeof(*phys)*2); *physsz = 2; } } static u_long ps3_timebase_freq(platform_t plat, struct cpuref *cpuref) { uint64_t ticks, node_id, junk; lv1_get_repository_node_value(PS3_LPAR_ID_PME, lv1_repository_string("be") >> 32, 0, 0, 0, &node_id, &junk); lv1_get_repository_node_value(PS3_LPAR_ID_PME, lv1_repository_string("be") >> 32, node_id, lv1_repository_string("clock"), 0, &ticks, &junk); return (ticks); } #ifdef SMP static int ps3_smp_first_cpu(platform_t plat, struct cpuref *cpuref) { cpuref->cr_cpuid = 0; cpuref->cr_hwref = cpuref->cr_cpuid; return (0); } static int ps3_smp_next_cpu(platform_t plat, struct cpuref *cpuref) { if (cpuref->cr_cpuid >= 1) return (ENOENT); cpuref->cr_cpuid++; cpuref->cr_hwref = cpuref->cr_cpuid; return (0); } static int ps3_smp_get_bsp(platform_t plat, struct cpuref *cpuref) { cpuref->cr_cpuid = 0; cpuref->cr_hwref = cpuref->cr_cpuid; return (0); } static int ps3_smp_start_cpu(platform_t plat, struct pcpu *pc) { /* loader(8) is spinning on 0x40 == 0 right now */ uint32_t *secondary_spin_sem = (uint32_t *)(0x40); int timeout; if (pc->pc_hwref != 1) return (ENXIO); ap_pcpu = pc; *secondary_spin_sem = 1; powerpc_sync(); DELAY(1); timeout = 10000; while (!pc->pc_awake && timeout--) DELAY(100); return ((pc->pc_awake) ? 0 : EBUSY); } static struct cpu_group * ps3_smp_topo(platform_t plat) { return (smp_topo_1level(CG_SHARE_L1, 2, CG_FLAG_SMT)); } #endif static void ps3_reset(platform_t plat) { lv1_panic(1); } static vm_offset_t ps3_real_maxaddr(platform_t plat) { struct mem_region *phys, *avail; int nphys, navail; mem_regions(&phys, &nphys, &avail, &navail); return (phys[0].mr_start + phys[0].mr_size); } static void ps3_cpu_idle(sbintime_t sbt) { lv1_pause(0); } Index: head/sys/powerpc/ps3/ps3_syscons.c =================================================================== --- head/sys/powerpc/ps3/ps3_syscons.c (revision 326260) +++ head/sys/powerpc/ps3/ps3_syscons.c (revision 326261) @@ -1,196 +1,198 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2011-2014 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ps3-hvcall.h" #define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_MODE_SET 0x0100 #define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC 0x0101 #define L1GPU_DISPLAY_SYNC_HSYNC 1 #define L1GPU_DISPLAY_SYNC_VSYNC 2 #define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP 0x0102 static vd_init_t ps3fb_init; static vd_probe_t ps3fb_probe; void ps3fb_remap(void); struct ps3fb_softc { struct fb_info fb_info; uint64_t sc_fbhandle; uint64_t sc_fbcontext; uint64_t sc_dma_control; uint64_t sc_driver_info; uint64_t sc_reports; uint64_t sc_reports_size; }; static struct vt_driver vt_ps3fb_driver = { .vd_name = "ps3fb", .vd_probe = ps3fb_probe, .vd_init = ps3fb_init, .vd_blank = vt_fb_blank, .vd_bitblt_text = vt_fb_bitblt_text, .vd_bitblt_bmp = vt_fb_bitblt_bitmap, .vd_drawrect = vt_fb_drawrect, .vd_setpixel = vt_fb_setpixel, .vd_fb_ioctl = vt_fb_ioctl, .vd_fb_mmap = vt_fb_mmap, /* Better than VGA, but still generic driver. */ .vd_priority = VD_PRIORITY_GENERIC + 1, }; VT_DRIVER_DECLARE(vt_ps3fb, vt_ps3fb_driver); static struct ps3fb_softc ps3fb_softc; static int ps3fb_probe(struct vt_device *vd) { struct ps3fb_softc *sc; int disable; char compatible[64]; phandle_t root; disable = 0; TUNABLE_INT_FETCH("hw.syscons.disable", &disable); if (disable != 0) return (0); sc = &ps3fb_softc; TUNABLE_STR_FETCH("hw.platform", compatible, sizeof(compatible)); if (strcmp(compatible, "ps3") == 0) return (CN_INTERNAL); root = OF_finddevice("/"); if (OF_getprop(root, "compatible", compatible, sizeof(compatible)) <= 0) return (CN_DEAD); if (strncmp(compatible, "sony,ps3", sizeof(compatible)) != 0) return (CN_DEAD); return (CN_INTERNAL); } void ps3fb_remap(void) { struct ps3fb_softc *sc; vm_offset_t va, fb_paddr; sc = &ps3fb_softc; lv1_gpu_close(); lv1_gpu_open(0); lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_MODE_SET, 0,0,0,0); lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_MODE_SET, 0,0,1,0); lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, 0,L1GPU_DISPLAY_SYNC_VSYNC,0,0); lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, 1,L1GPU_DISPLAY_SYNC_VSYNC,0,0); lv1_gpu_memory_allocate(roundup2(sc->fb_info.fb_size, 1024*1024), 0, 0, 0, 0, &sc->sc_fbhandle, &fb_paddr); lv1_gpu_context_allocate(sc->sc_fbhandle, 0, &sc->sc_fbcontext, &sc->sc_dma_control, &sc->sc_driver_info, &sc->sc_reports, &sc->sc_reports_size); lv1_gpu_context_attribute(sc->sc_fbcontext, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 0, 0, 0, 0); lv1_gpu_context_attribute(sc->sc_fbcontext, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 1, 0, 0, 0); sc->fb_info.fb_pbase = fb_paddr; for (va = 0; va < sc->fb_info.fb_size; va += PAGE_SIZE) pmap_kenter_attr(0x10000000 + va, fb_paddr + va, VM_MEMATTR_WRITE_COMBINING); sc->fb_info.fb_flags &= ~FB_FLAG_NOWRITE; } static int ps3fb_init(struct vt_device *vd) { struct ps3fb_softc *sc; /* Init softc */ vd->vd_softc = sc = &ps3fb_softc; /* XXX: get from HV repository */ sc->fb_info.fb_depth = 32; sc->fb_info.fb_height = 480; sc->fb_info.fb_width = 720; TUNABLE_INT_FETCH("hw.ps3fb.height", &sc->fb_info.fb_height); TUNABLE_INT_FETCH("hw.ps3fb.width", &sc->fb_info.fb_width); sc->fb_info.fb_stride = sc->fb_info.fb_width*4; sc->fb_info.fb_size = sc->fb_info.fb_height * sc->fb_info.fb_stride; sc->fb_info.fb_bpp = sc->fb_info.fb_stride / sc->fb_info.fb_width * 8; /* * Arbitrarily choose address for the framebuffer */ sc->fb_info.fb_vbase = 0x10000000; sc->fb_info.fb_flags |= FB_FLAG_NOWRITE; /* Not available yet */ sc->fb_info.fb_cmsize = 16; /* 32-bit VGA palette */ vt_generate_cons_palette(sc->fb_info.fb_cmap, COLOR_FORMAT_RGB, 255, 0, 255, 8, 255, 16); /* Set correct graphics context */ lv1_gpu_context_attribute(sc->sc_fbcontext, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 0, 0, 0, 0); lv1_gpu_context_attribute(sc->sc_fbcontext, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 1, 0, 0, 0); vt_fb_init(vd); return (CN_INTERNAL); } Index: head/sys/powerpc/ps3/ps3bus.c =================================================================== --- head/sys/powerpc/ps3/ps3bus.c (revision 326260) +++ head/sys/powerpc/ps3/ps3bus.c (revision 326261) @@ -1,763 +1,765 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2010 Nathan Whitehorn * Copyright (C) 2011 glevand (geoffrey.levand@mail.ru) * 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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ps3bus.h" #include "ps3-hvcall.h" #include "iommu_if.h" #include "clock_if.h" static void ps3bus_identify(driver_t *, device_t); static int ps3bus_probe(device_t); static int ps3bus_attach(device_t); static int ps3bus_print_child(device_t dev, device_t child); static int ps3bus_read_ivar(device_t bus, device_t child, int which, uintptr_t *result); static struct resource *ps3bus_alloc_resource(device_t bus, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags); static int ps3bus_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res); static bus_dma_tag_t ps3bus_get_dma_tag(device_t dev, device_t child); static int ps3_iommu_map(device_t dev, bus_dma_segment_t *segs, int *nsegs, bus_addr_t min, bus_addr_t max, bus_size_t alignment, bus_addr_t boundary, void *cookie); static int ps3_iommu_unmap(device_t dev, bus_dma_segment_t *segs, int nsegs, void *cookie); static int ps3_gettime(device_t dev, struct timespec *ts); static int ps3_settime(device_t dev, struct timespec *ts); struct ps3bus_devinfo { int bus; int dev; uint64_t bustype; uint64_t devtype; int busidx; int devidx; struct resource_list resources; bus_dma_tag_t dma_tag; struct mtx iommu_mtx; bus_addr_t dma_base[4]; }; static MALLOC_DEFINE(M_PS3BUS, "ps3bus", "PS3 system bus device information"); enum ps3bus_irq_type { SB_IRQ = 2, OHCI_IRQ = 3, EHCI_IRQ = 4, }; enum ps3bus_reg_type { OHCI_REG = 3, EHCI_REG = 4, }; static device_method_t ps3bus_methods[] = { /* Device interface */ DEVMETHOD(device_identify, ps3bus_identify), DEVMETHOD(device_probe, ps3bus_probe), DEVMETHOD(device_attach, ps3bus_attach), /* Bus interface */ DEVMETHOD(bus_add_child, bus_generic_add_child), DEVMETHOD(bus_get_dma_tag, ps3bus_get_dma_tag), DEVMETHOD(bus_print_child, ps3bus_print_child), DEVMETHOD(bus_read_ivar, ps3bus_read_ivar), DEVMETHOD(bus_alloc_resource, ps3bus_alloc_resource), DEVMETHOD(bus_activate_resource, ps3bus_activate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), /* IOMMU interface */ DEVMETHOD(iommu_map, ps3_iommu_map), DEVMETHOD(iommu_unmap, ps3_iommu_unmap), /* Clock interface */ DEVMETHOD(clock_gettime, ps3_gettime), DEVMETHOD(clock_settime, ps3_settime), DEVMETHOD_END }; struct ps3bus_softc { struct rman sc_mem_rman; struct rman sc_intr_rman; struct mem_region *regions; int rcount; }; static driver_t ps3bus_driver = { "ps3bus", ps3bus_methods, sizeof(struct ps3bus_softc) }; static devclass_t ps3bus_devclass; DRIVER_MODULE(ps3bus, nexus, ps3bus_driver, ps3bus_devclass, 0, 0); static void ps3bus_identify(driver_t *driver, device_t parent) { if (strcmp(installed_platform(), "ps3") != 0) return; if (device_find_child(parent, "ps3bus", -1) == NULL) BUS_ADD_CHILD(parent, 0, "ps3bus", 0); } static int ps3bus_probe(device_t dev) { /* Do not attach to any OF nodes that may be present */ device_set_desc(dev, "Playstation 3 System Bus"); return (BUS_PROBE_NOWILDCARD); } static void ps3bus_resources_init(struct rman *rm, int bus_index, int dev_index, struct ps3bus_devinfo *dinfo) { uint64_t irq_type, irq, outlet; uint64_t reg_type, paddr, len; uint64_t ppe, junk; int i, result; int thread; resource_list_init(&dinfo->resources); lv1_get_logical_ppe_id(&ppe); thread = 32 - fls(mfctrl()); /* Scan for interrupts */ for (i = 0; i < 10; i++) { result = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("dev") | dev_index, lv1_repository_string("intr") | i, 0, &irq_type, &irq); if (result != 0) break; switch (irq_type) { case SB_IRQ: lv1_construct_event_receive_port(&outlet); lv1_connect_irq_plug_ext(ppe, thread, outlet, outlet, 0); lv1_connect_interrupt_event_receive_port(dinfo->bus, dinfo->dev, outlet, irq); break; case OHCI_IRQ: case EHCI_IRQ: lv1_construct_io_irq_outlet(irq, &outlet); lv1_connect_irq_plug_ext(ppe, thread, outlet, outlet, 0); break; default: printf("Unknown IRQ type %ld for device %d.%d\n", irq_type, dinfo->bus, dinfo->dev); break; } resource_list_add(&dinfo->resources, SYS_RES_IRQ, i, outlet, outlet, 1); } /* Scan for registers */ for (i = 0; i < 10; i++) { result = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("dev") | dev_index, lv1_repository_string("reg") | i, lv1_repository_string("type"), ®_type, &junk); if (result != 0) break; result = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("dev") | dev_index, lv1_repository_string("reg") | i, lv1_repository_string("data"), &paddr, &len); result = lv1_map_device_mmio_region(dinfo->bus, dinfo->dev, paddr, len, 12 /* log_2(4 KB) */, &paddr); if (result != 0) { printf("Mapping registers failed for device " "%d.%d (%ld.%ld): %d\n", dinfo->bus, dinfo->dev, dinfo->bustype, dinfo->devtype, result); continue; } rman_manage_region(rm, paddr, paddr + len - 1); resource_list_add(&dinfo->resources, SYS_RES_MEMORY, i, paddr, paddr + len, len); } } static void ps3bus_resources_init_by_type(struct rman *rm, int bus_index, int dev_index, uint64_t irq_type, uint64_t reg_type, struct ps3bus_devinfo *dinfo) { uint64_t _irq_type, irq, outlet; uint64_t _reg_type, paddr, len; uint64_t ppe, junk; int i, result; int thread; resource_list_init(&dinfo->resources); lv1_get_logical_ppe_id(&ppe); thread = 32 - fls(mfctrl()); /* Scan for interrupts */ for (i = 0; i < 10; i++) { result = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("dev") | dev_index, lv1_repository_string("intr") | i, 0, &_irq_type, &irq); if (result != 0) break; if (_irq_type != irq_type) continue; lv1_construct_io_irq_outlet(irq, &outlet); lv1_connect_irq_plug_ext(ppe, thread, outlet, outlet, 0); resource_list_add(&dinfo->resources, SYS_RES_IRQ, i, outlet, outlet, 1); } /* Scan for registers */ for (i = 0; i < 10; i++) { result = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("dev") | dev_index, lv1_repository_string("reg") | i, lv1_repository_string("type"), &_reg_type, &junk); if (result != 0) break; if (_reg_type != reg_type) continue; result = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("dev") | dev_index, lv1_repository_string("reg") | i, lv1_repository_string("data"), &paddr, &len); result = lv1_map_device_mmio_region(dinfo->bus, dinfo->dev, paddr, len, 12 /* log_2(4 KB) */, &paddr); if (result != 0) { printf("Mapping registers failed for device " "%d.%d (%ld.%ld): %d\n", dinfo->bus, dinfo->dev, dinfo->bustype, dinfo->devtype, result); break; } rman_manage_region(rm, paddr, paddr + len - 1); resource_list_add(&dinfo->resources, SYS_RES_MEMORY, i, paddr, paddr + len, len); } } static int ps3bus_attach(device_t self) { struct ps3bus_softc *sc; struct ps3bus_devinfo *dinfo; int bus_index, dev_index, result; uint64_t bustype, bus, devs; uint64_t dev, devtype; uint64_t junk; device_t cdev; sc = device_get_softc(self); sc->sc_mem_rman.rm_type = RMAN_ARRAY; sc->sc_mem_rman.rm_descr = "PS3Bus Memory Mapped I/O"; sc->sc_intr_rman.rm_type = RMAN_ARRAY; sc->sc_intr_rman.rm_descr = "PS3Bus Interrupts"; rman_init(&sc->sc_mem_rman); rman_init(&sc->sc_intr_rman); rman_manage_region(&sc->sc_intr_rman, 0, ~0); /* Get memory regions for DMA */ mem_regions(&sc->regions, &sc->rcount, &sc->regions, &sc->rcount); /* * Probe all the PS3's buses. */ for (bus_index = 0; bus_index < 5; bus_index++) { result = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("type"), 0, 0, &bustype, &junk); if (result != 0) continue; result = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("id"), 0, 0, &bus, &junk); if (result != 0) continue; result = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("num_dev"), 0, 0, &devs, &junk); for (dev_index = 0; dev_index < devs; dev_index++) { result = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("dev") | dev_index, lv1_repository_string("type"), 0, &devtype, &junk); if (result != 0) continue; result = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("dev") | dev_index, lv1_repository_string("id"), 0, &dev, &junk); if (result != 0) continue; switch (devtype) { case PS3_DEVTYPE_USB: /* USB device has OHCI and EHCI USB host controllers */ lv1_open_device(bus, dev, 0); /* OHCI host controller */ dinfo = malloc(sizeof(*dinfo), M_PS3BUS, M_WAITOK | M_ZERO); dinfo->bus = bus; dinfo->dev = dev; dinfo->bustype = bustype; dinfo->devtype = devtype; dinfo->busidx = bus_index; dinfo->devidx = dev_index; ps3bus_resources_init_by_type(&sc->sc_mem_rman, bus_index, dev_index, OHCI_IRQ, OHCI_REG, dinfo); cdev = device_add_child(self, "ohci", -1); if (cdev == NULL) { device_printf(self, "device_add_child failed\n"); free(dinfo, M_PS3BUS); continue; } mtx_init(&dinfo->iommu_mtx, "iommu", NULL, MTX_DEF); device_set_ivars(cdev, dinfo); /* EHCI host controller */ dinfo = malloc(sizeof(*dinfo), M_PS3BUS, M_WAITOK | M_ZERO); dinfo->bus = bus; dinfo->dev = dev; dinfo->bustype = bustype; dinfo->devtype = devtype; dinfo->busidx = bus_index; dinfo->devidx = dev_index; ps3bus_resources_init_by_type(&sc->sc_mem_rman, bus_index, dev_index, EHCI_IRQ, EHCI_REG, dinfo); cdev = device_add_child(self, "ehci", -1); if (cdev == NULL) { device_printf(self, "device_add_child failed\n"); free(dinfo, M_PS3BUS); continue; } mtx_init(&dinfo->iommu_mtx, "iommu", NULL, MTX_DEF); device_set_ivars(cdev, dinfo); break; default: dinfo = malloc(sizeof(*dinfo), M_PS3BUS, M_WAITOK | M_ZERO); dinfo->bus = bus; dinfo->dev = dev; dinfo->bustype = bustype; dinfo->devtype = devtype; dinfo->busidx = bus_index; dinfo->devidx = dev_index; if (dinfo->bustype == PS3_BUSTYPE_SYSBUS || dinfo->bustype == PS3_BUSTYPE_STORAGE) lv1_open_device(bus, dev, 0); ps3bus_resources_init(&sc->sc_mem_rman, bus_index, dev_index, dinfo); cdev = device_add_child(self, NULL, -1); if (cdev == NULL) { device_printf(self, "device_add_child failed\n"); free(dinfo, M_PS3BUS); continue; } mtx_init(&dinfo->iommu_mtx, "iommu", NULL, MTX_DEF); device_set_ivars(cdev, dinfo); } } } clock_register(self, 1000); return (bus_generic_attach(self)); } static int ps3bus_print_child(device_t dev, device_t child) { struct ps3bus_devinfo *dinfo = device_get_ivars(child); int retval = 0; retval += bus_print_child_header(dev, child); retval += resource_list_print_type(&dinfo->resources, "mem", SYS_RES_MEMORY, "%#jx"); retval += resource_list_print_type(&dinfo->resources, "irq", SYS_RES_IRQ, "%jd"); retval += bus_print_child_footer(dev, child); return (retval); } static int ps3bus_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) { struct ps3bus_devinfo *dinfo = device_get_ivars(child); switch (which) { case PS3BUS_IVAR_BUS: *result = dinfo->bus; break; case PS3BUS_IVAR_DEVICE: *result = dinfo->dev; break; case PS3BUS_IVAR_BUSTYPE: *result = dinfo->bustype; break; case PS3BUS_IVAR_DEVTYPE: *result = dinfo->devtype; break; case PS3BUS_IVAR_BUSIDX: *result = dinfo->busidx; break; case PS3BUS_IVAR_DEVIDX: *result = dinfo->devidx; break; default: return (EINVAL); } return (0); } static struct resource * ps3bus_alloc_resource(device_t bus, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct ps3bus_devinfo *dinfo; struct ps3bus_softc *sc; int needactivate; struct resource *rv; struct rman *rm; rman_res_t adjstart, adjend, adjcount; struct resource_list_entry *rle; sc = device_get_softc(bus); dinfo = device_get_ivars(child); needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; switch (type) { case SYS_RES_MEMORY: rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY, *rid); if (rle == NULL) { device_printf(bus, "no rle for %s memory %d\n", device_get_nameunit(child), *rid); return (NULL); } if (start < rle->start) adjstart = rle->start; else if (start > rle->end) adjstart = rle->end; else adjstart = start; if (end < rle->start) adjend = rle->start; else if (end > rle->end) adjend = rle->end; else adjend = end; adjcount = adjend - adjstart; rm = &sc->sc_mem_rman; break; case SYS_RES_IRQ: rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, *rid); rm = &sc->sc_intr_rman; adjstart = rle->start; adjcount = ulmax(count, rle->count); adjend = ulmax(rle->end, rle->start + adjcount - 1); break; default: device_printf(bus, "unknown resource request from %s\n", device_get_nameunit(child)); return (NULL); } rv = rman_reserve_resource(rm, adjstart, adjend, adjcount, flags, child); if (rv == NULL) { device_printf(bus, "failed to reserve resource %#lx - %#lx (%#lx)" " for %s\n", adjstart, adjend, adjcount, device_get_nameunit(child)); return (NULL); } rman_set_rid(rv, *rid); if (needactivate) { if (bus_activate_resource(child, type, *rid, rv) != 0) { device_printf(bus, "failed to activate resource for %s\n", device_get_nameunit(child)); rman_release_resource(rv); return (NULL); } } return (rv); } static int ps3bus_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { void *p; if (type == SYS_RES_IRQ) return (bus_activate_resource(bus, type, rid, res)); if (type == SYS_RES_MEMORY) { vm_offset_t start; start = (vm_offset_t) rman_get_start(res); if (bootverbose) printf("ps3 mapdev: start %zx, len %ld\n", start, rman_get_size(res)); p = pmap_mapdev(start, (vm_size_t) rman_get_size(res)); if (p == NULL) return (ENOMEM); rman_set_virtual(res, p); rman_set_bustag(res, &bs_be_tag); rman_set_bushandle(res, (rman_res_t)p); } return (rman_activate_resource(res)); } static bus_dma_tag_t ps3bus_get_dma_tag(device_t dev, device_t child) { struct ps3bus_devinfo *dinfo = device_get_ivars(child); struct ps3bus_softc *sc = device_get_softc(dev); int i, err, flags, pagesize; if (dinfo->bustype != PS3_BUSTYPE_SYSBUS && dinfo->bustype != PS3_BUSTYPE_STORAGE) return (bus_get_dma_tag(dev)); mtx_lock(&dinfo->iommu_mtx); if (dinfo->dma_tag != NULL) { mtx_unlock(&dinfo->iommu_mtx); return (dinfo->dma_tag); } flags = 0; /* 32-bit mode */ if (dinfo->bustype == PS3_BUSTYPE_SYSBUS && dinfo->devtype == PS3_DEVTYPE_USB) flags = 2; /* 8-bit mode */ pagesize = 24; /* log_2(16 MB) */ if (dinfo->bustype == PS3_BUSTYPE_STORAGE) pagesize = 12; /* 4 KB */ for (i = 0; i < sc->rcount; i++) { err = lv1_allocate_device_dma_region(dinfo->bus, dinfo->dev, sc->regions[i].mr_size, pagesize, flags, &dinfo->dma_base[i]); if (err != 0) { device_printf(child, "could not allocate DMA region %d: %d\n", i, err); goto fail; } err = lv1_map_device_dma_region(dinfo->bus, dinfo->dev, sc->regions[i].mr_start, dinfo->dma_base[i], sc->regions[i].mr_size, 0xf800000000000800UL /* Cell Handbook Figure 7.3.4.1 */); if (err != 0) { device_printf(child, "could not map DMA region %d: %d\n", i, err); goto fail; } } err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE, 0, BUS_SPACE_MAXSIZE, 0, NULL, NULL, &dinfo->dma_tag); /* * Note: storage devices have IOMMU mappings set up by the hypervisor, * but use physical, non-translated addresses. The above IOMMU * initialization is necessary for the hypervisor to be able to set up * the mappings, but actual DMA mappings should not use the IOMMU * routines. */ if (dinfo->bustype != PS3_BUSTYPE_STORAGE) bus_dma_tag_set_iommu(dinfo->dma_tag, dev, dinfo); fail: mtx_unlock(&dinfo->iommu_mtx); if (err) return (NULL); return (dinfo->dma_tag); } static int ps3_iommu_map(device_t dev, bus_dma_segment_t *segs, int *nsegs, bus_addr_t min, bus_addr_t max, bus_size_t alignment, bus_addr_t boundary, void *cookie) { struct ps3bus_devinfo *dinfo = cookie; struct ps3bus_softc *sc = device_get_softc(dev); int i, j; for (i = 0; i < *nsegs; i++) { for (j = 0; j < sc->rcount; j++) { if (segs[i].ds_addr >= sc->regions[j].mr_start && segs[i].ds_addr < sc->regions[j].mr_start + sc->regions[j].mr_size) break; } KASSERT(j < sc->rcount, ("Trying to map address %#lx not in physical memory", segs[i].ds_addr)); segs[i].ds_addr = dinfo->dma_base[j] + (segs[i].ds_addr - sc->regions[j].mr_start); } return (0); } static int ps3_iommu_unmap(device_t dev, bus_dma_segment_t *segs, int nsegs, void *cookie) { return (0); } #define Y2K 946684800 static int ps3_gettime(device_t dev, struct timespec *ts) { uint64_t rtc, tb; int result; result = lv1_get_rtc(&rtc, &tb); if (result) return (result); ts->tv_sec = rtc + Y2K; ts->tv_nsec = 0; return (0); } static int ps3_settime(device_t dev, struct timespec *ts) { return (-1); } Index: head/sys/powerpc/ps3/ps3bus.h =================================================================== --- head/sys/powerpc/ps3/ps3bus.h (revision 326260) +++ head/sys/powerpc/ps3/ps3bus.h (revision 326261) @@ -1,69 +1,71 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2010 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _POWERPC_PS3_PS3BUS_H #define _POWERPC_PS3_PS3BUS_H enum { PS3BUS_IVAR_BUS, PS3BUS_IVAR_DEVICE, PS3BUS_IVAR_BUSTYPE, PS3BUS_IVAR_DEVTYPE, PS3BUS_IVAR_BUSIDX, PS3BUS_IVAR_DEVIDX, }; #define PS3BUS_ACCESSOR(A, B, T) \ __BUS_ACCESSOR(ps3bus, A, PS3BUS, B, T) PS3BUS_ACCESSOR(bus, BUS, int) PS3BUS_ACCESSOR(device, DEVICE, int) PS3BUS_ACCESSOR(bustype, BUSTYPE, uint64_t) PS3BUS_ACCESSOR(devtype, DEVTYPE, uint64_t) PS3BUS_ACCESSOR(busidx, BUSIDX, int) PS3BUS_ACCESSOR(devidx, DEVIDX, int) /* Bus types */ enum { PS3_BUSTYPE_SYSBUS = 4, PS3_BUSTYPE_STORAGE = 5, }; /* Device types */ enum { /* System bus devices */ PS3_DEVTYPE_GELIC = 3, PS3_DEVTYPE_USB = 4, PS3_DEVTYPE_GPIO = 6, /* Storage bus devices */ PS3_DEVTYPE_DISK = 0, PS3_DEVTYPE_CDROM = 5, PS3_DEVTYPE_FLASH = 14, }; #endif /* _POWERPC_PS3_PS3BUS_H */ Index: head/sys/powerpc/ps3/ps3cdrom.c =================================================================== --- head/sys/powerpc/ps3/ps3cdrom.c (revision 326260) +++ head/sys/powerpc/ps3/ps3cdrom.c (revision 326261) @@ -1,709 +1,711 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2010 Nathan Whitehorn * Copyright (C) 2011 glevand * 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, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ps3bus.h" #include "ps3-hvcall.h" #define PS3CDROM_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), "ps3cdrom", \ MTX_DEF) #define PS3CDROM_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); #define PS3CDROM_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define PS3CDROM_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define PS3CDROM_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); #define PS3CDROM_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); #define PS3CDROM_MAX_XFERS 3 #define LV1_STORAGE_SEND_ATAPI_COMMAND 0x01 struct ps3cdrom_softc; struct ps3cdrom_xfer { TAILQ_ENTRY(ps3cdrom_xfer) x_queue; struct ps3cdrom_softc *x_sc; union ccb *x_ccb; bus_dmamap_t x_dmamap; uint64_t x_tag; }; TAILQ_HEAD(ps3cdrom_xferq, ps3cdrom_xfer); struct ps3cdrom_softc { device_t sc_dev; struct mtx sc_mtx; uint64_t sc_blksize; uint64_t sc_nblocks; int sc_irqid; struct resource *sc_irq; void *sc_irqctx; bus_dma_tag_t sc_dmatag; struct cam_sim *sc_sim; struct cam_path *sc_path; struct ps3cdrom_xfer sc_xfer[PS3CDROM_MAX_XFERS]; struct ps3cdrom_xferq sc_active_xferq; struct ps3cdrom_xferq sc_free_xferq; }; enum lv1_ata_proto { NON_DATA_PROTO = 0x00, PIO_DATA_IN_PROTO = 0x01, PIO_DATA_OUT_PROTO = 0x02, DMA_PROTO = 0x03 }; enum lv1_ata_in_out { DIR_WRITE = 0x00, DIR_READ = 0x01 }; struct lv1_atapi_cmd { uint8_t pkt[32]; uint32_t pktlen; uint32_t nblocks; uint32_t blksize; uint32_t proto; /* enum lv1_ata_proto */ uint32_t in_out; /* enum lv1_ata_in_out */ uint64_t buf; uint32_t arglen; }; static void ps3cdrom_action(struct cam_sim *sim, union ccb *ccb); static void ps3cdrom_poll(struct cam_sim *sim); static void ps3cdrom_async(void *callback_arg, u_int32_t code, struct cam_path* path, void *arg); static void ps3cdrom_intr(void *arg); static void ps3cdrom_transfer(void *arg, bus_dma_segment_t *segs, int nsegs, int error); static int ps3cdrom_decode_lv1_status(uint64_t status, u_int8_t *sense_key, u_int8_t *asc, u_int8_t *ascq); static int ps3cdrom_probe(device_t dev) { if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_STORAGE || ps3bus_get_devtype(dev) != PS3_DEVTYPE_CDROM) return (ENXIO); device_set_desc(dev, "Playstation 3 CDROM"); return (BUS_PROBE_SPECIFIC); } static int ps3cdrom_attach(device_t dev) { struct ps3cdrom_softc *sc = device_get_softc(dev); struct cam_devq *devq; struct ps3cdrom_xfer *xp; struct ccb_setasync csa; int i, err; sc->sc_dev = dev; PS3CDROM_LOCK_INIT(sc); /* Setup interrupt handler */ sc->sc_irqid = 0; sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqid, RF_ACTIVE); if (!sc->sc_irq) { device_printf(dev, "Could not allocate IRQ\n"); err = ENXIO; goto fail_destroy_lock; } err = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_CAM | INTR_MPSAFE | INTR_ENTROPY, NULL, ps3cdrom_intr, sc, &sc->sc_irqctx); if (err) { device_printf(dev, "Could not setup IRQ\n"); err = ENXIO; goto fail_release_intr; } /* Setup DMA */ err = bus_dma_tag_create(bus_get_dma_tag(dev), 4096, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_UNRESTRICTED, 1, PAGE_SIZE, 0, busdma_lock_mutex, &sc->sc_mtx, &sc->sc_dmatag); if (err) { device_printf(dev, "Could not create DMA tag\n"); err = ENXIO; goto fail_teardown_intr; } /* Setup transfer queues */ TAILQ_INIT(&sc->sc_active_xferq); TAILQ_INIT(&sc->sc_free_xferq); for (i = 0; i < PS3CDROM_MAX_XFERS; i++) { xp = &sc->sc_xfer[i]; xp->x_sc = sc; err = bus_dmamap_create(sc->sc_dmatag, BUS_DMA_COHERENT, &xp->x_dmamap); if (err) { device_printf(dev, "Could not create DMA map (%d)\n", err); goto fail_destroy_dmamap; } TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue); } /* Setup CAM */ devq = cam_simq_alloc(PS3CDROM_MAX_XFERS - 1); if (!devq) { device_printf(dev, "Could not allocate SIM queue\n"); err = ENOMEM; goto fail_destroy_dmatag; } sc->sc_sim = cam_sim_alloc(ps3cdrom_action, ps3cdrom_poll, "ps3cdrom", sc, device_get_unit(dev), &sc->sc_mtx, PS3CDROM_MAX_XFERS - 1, 0, devq); if (!sc->sc_sim) { device_printf(dev, "Could not allocate SIM\n"); cam_simq_free(devq); err = ENOMEM; goto fail_destroy_dmatag; } /* Setup XPT */ PS3CDROM_LOCK(sc); err = xpt_bus_register(sc->sc_sim, dev, 0); if (err != CAM_SUCCESS) { device_printf(dev, "Could not register XPT bus\n"); err = ENXIO; PS3CDROM_UNLOCK(sc); goto fail_free_sim; } err = xpt_create_path(&sc->sc_path, NULL, cam_sim_path(sc->sc_sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); if (err != CAM_REQ_CMP) { device_printf(dev, "Could not create XPT path\n"); err = ENOMEM; PS3CDROM_UNLOCK(sc); goto fail_unregister_xpt_bus; } xpt_setup_ccb(&csa.ccb_h, sc->sc_path, 5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = AC_LOST_DEVICE; csa.callback = ps3cdrom_async; csa.callback_arg = sc->sc_sim; xpt_action((union ccb *) &csa); CAM_DEBUG(sc->sc_path, CAM_DEBUG_TRACE, ("registered SIM for ps3cdrom%d\n", device_get_unit(dev))); PS3CDROM_UNLOCK(sc); return (BUS_PROBE_SPECIFIC); fail_unregister_xpt_bus: xpt_bus_deregister(cam_sim_path(sc->sc_sim)); fail_free_sim: cam_sim_free(sc->sc_sim, TRUE); fail_destroy_dmamap: while ((xp = TAILQ_FIRST(&sc->sc_free_xferq))) { TAILQ_REMOVE(&sc->sc_free_xferq, xp, x_queue); bus_dmamap_destroy(sc->sc_dmatag, xp->x_dmamap); } fail_destroy_dmatag: bus_dma_tag_destroy(sc->sc_dmatag); fail_teardown_intr: bus_teardown_intr(dev, sc->sc_irq, sc->sc_irqctx); fail_release_intr: bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqid, sc->sc_irq); fail_destroy_lock: PS3CDROM_LOCK_DESTROY(sc); return (err); } static int ps3cdrom_detach(device_t dev) { struct ps3cdrom_softc *sc = device_get_softc(dev); int i; xpt_async(AC_LOST_DEVICE, sc->sc_path, NULL); xpt_free_path(sc->sc_path); xpt_bus_deregister(cam_sim_path(sc->sc_sim)); cam_sim_free(sc->sc_sim, TRUE); for (i = 0; i < PS3CDROM_MAX_XFERS; i++) bus_dmamap_destroy(sc->sc_dmatag, sc->sc_xfer[i].x_dmamap); bus_dma_tag_destroy(sc->sc_dmatag); bus_teardown_intr(dev, sc->sc_irq, sc->sc_irqctx); bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqid, sc->sc_irq); PS3CDROM_LOCK_DESTROY(sc); return (0); } static void ps3cdrom_action(struct cam_sim *sim, union ccb *ccb) { struct ps3cdrom_softc *sc = (struct ps3cdrom_softc *)cam_sim_softc(sim); device_t dev = sc->sc_dev; struct ps3cdrom_xfer *xp; int err; PS3CDROM_ASSERT_LOCKED(sc); CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("function code 0x%02x\n", ccb->ccb_h.func_code)); switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_INPROG) break; if(ccb->ccb_h.target_id > 0) { ccb->ccb_h.status = CAM_TID_INVALID; break; } if(ccb->ccb_h.target_lun > 0) { ccb->ccb_h.status = CAM_LUN_INVALID; break; } xp = TAILQ_FIRST(&sc->sc_free_xferq); KASSERT(xp != NULL, ("no free transfers")); xp->x_ccb = ccb; TAILQ_REMOVE(&sc->sc_free_xferq, xp, x_queue); err = bus_dmamap_load_ccb(sc->sc_dmatag, xp->x_dmamap, ccb, ps3cdrom_transfer, xp, 0); if (err && err != EINPROGRESS) { device_printf(dev, "Could not load DMA map (%d)\n", err); xp->x_ccb = NULL; TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue); ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; } return; case XPT_SET_TRAN_SETTINGS: ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; break; case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_SPI; cts->transport_version = 2; cts->proto_specific.valid = 0; cts->xport_specific.valid = 0; ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_RESET_BUS: case XPT_RESET_DEV: ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_CALC_GEOMETRY: cam_calc_geometry(&ccb->ccg, 1); break; case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; cpi->hba_inquiry = 0; cpi->target_sprt = 0; cpi->hba_inquiry = PI_SDTR_ABLE; cpi->hba_misc = PIM_NOBUSRESET | PIM_SEQSCAN | PIM_NO_6_BYTE; cpi->hba_eng_cnt = 0; bzero(cpi->vuhba_flags, sizeof(cpi->vuhba_flags)); cpi->max_target = 0; cpi->max_lun = 0; cpi->initiator_id = 7; cpi->bus_id = cam_sim_bus(sim); cpi->unit_number = cam_sim_unit(sim); cpi->base_transfer_speed = 150000; strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strlcpy(cpi->hba_vid, "Sony", HBA_IDLEN); strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; cpi->maxio = PAGE_SIZE; cpi->ccb_h.status = CAM_REQ_CMP; break; } default: CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("unsupported function code 0x%02x\n", ccb->ccb_h.func_code)); ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); } static void ps3cdrom_poll(struct cam_sim *sim) { ps3cdrom_intr(cam_sim_softc(sim)); } static void ps3cdrom_async(void *callback_arg, u_int32_t code, struct cam_path* path, void *arg) { switch (code) { case AC_LOST_DEVICE: xpt_print_path(path); break; default: break; } } static void ps3cdrom_intr(void *arg) { struct ps3cdrom_softc *sc = (struct ps3cdrom_softc *) arg; device_t dev = sc->sc_dev; uint64_t devid = ps3bus_get_device(dev); struct ps3cdrom_xfer *xp; union ccb *ccb; u_int8_t *cdb, sense_key, asc, ascq; uint64_t tag, status; if (lv1_storage_get_async_status(devid, &tag, &status) != 0) return; PS3CDROM_LOCK(sc); /* Find transfer with the returned tag */ TAILQ_FOREACH(xp, &sc->sc_active_xferq, x_queue) { if (xp->x_tag == tag) break; } if (xp) { ccb = xp->x_ccb; cdb = (ccb->ccb_h.flags & CAM_CDB_POINTER) ? ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("ATAPI command 0x%02x tag 0x%016lx completed (0x%016lx)\n", cdb[0], tag, status)); if (!status) { ccb->csio.scsi_status = SCSI_STATUS_OK; ccb->csio.resid = 0; ccb->ccb_h.status = CAM_REQ_CMP; } else { ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; if (!ps3cdrom_decode_lv1_status(status, &sense_key, &asc, &ascq)) { CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("sense key 0x%02x asc 0x%02x ascq 0x%02x\n", sense_key, asc, ascq)); scsi_set_sense_data(&ccb->csio.sense_data, /*sense_format*/ SSD_TYPE_NONE, /*current_error*/ 1, sense_key, asc, ascq, SSD_ELEM_NONE); ccb->csio.sense_len = SSD_FULL_SIZE; ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; } if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) ccb->csio.resid = ccb->csio.dxfer_len; } if (ccb->ccb_h.flags & CAM_DIR_IN) bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->sc_dmatag, xp->x_dmamap); xp->x_ccb = NULL; TAILQ_REMOVE(&sc->sc_active_xferq, xp, x_queue); TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue); xpt_done(ccb); } else { device_printf(dev, "Could not find transfer with tag 0x%016lx\n", tag); } PS3CDROM_UNLOCK(sc); } static void ps3cdrom_transfer(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct ps3cdrom_xfer *xp = (struct ps3cdrom_xfer *) arg; struct ps3cdrom_softc *sc = xp->x_sc; device_t dev = sc->sc_dev; uint64_t devid = ps3bus_get_device(dev); union ccb *ccb = xp->x_ccb; u_int8_t *cdb; uint64_t start_sector, block_count; int err; KASSERT(nsegs == 1 || nsegs == 0, ("ps3cdrom_transfer: invalid number of DMA segments %d", nsegs)); KASSERT(error == 0, ("ps3cdrom_transfer: DMA error %d", error)); PS3CDROM_ASSERT_LOCKED(sc); if (error) { device_printf(dev, "Could not load DMA map (%d)\n", error); xp->x_ccb = NULL; TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue); ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; xpt_done(ccb); return; } cdb = (ccb->ccb_h.flags & CAM_CDB_POINTER) ? ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("ATAPI command 0x%02x cdb_len %d dxfer_len %d\n ", cdb[0], ccb->csio.cdb_len, ccb->csio.dxfer_len)); switch (cdb[0]) { case READ_10: KASSERT(nsegs == 1, ("ps3cdrom_transfer: no data to read")); start_sector = (cdb[2] << 24) | (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]; block_count = (cdb[7] << 8) | cdb[8]; err = lv1_storage_read(devid, 0 /* region id */, start_sector, block_count, 0 /* flags */, segs[0].ds_addr, &xp->x_tag); bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap, BUS_DMASYNC_POSTREAD); break; case WRITE_10: KASSERT(nsegs == 1, ("ps3cdrom_transfer: no data to write")); start_sector = (cdb[2] << 24) | (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]; block_count = (cdb[7] << 8) | cdb[8]; bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap, BUS_DMASYNC_PREWRITE); err = lv1_storage_write(devid, 0 /* region id */, start_sector, block_count, 0 /* flags */, segs[0].ds_addr, &xp->x_tag); break; default: { struct lv1_atapi_cmd atapi_cmd; bzero(&atapi_cmd, sizeof(atapi_cmd)); atapi_cmd.pktlen = 12; bcopy(cdb, atapi_cmd.pkt, ccb->csio.cdb_len); if (ccb->ccb_h.flags & CAM_DIR_IN) { atapi_cmd.in_out = DIR_READ; atapi_cmd.proto = (ccb->csio.dxfer_len >= 2048) ? DMA_PROTO : PIO_DATA_IN_PROTO; } else if (ccb->ccb_h.flags & CAM_DIR_OUT) { atapi_cmd.in_out = DIR_WRITE; atapi_cmd.proto = (ccb->csio.dxfer_len >= 2048) ? DMA_PROTO : PIO_DATA_OUT_PROTO; } else { atapi_cmd.proto = NON_DATA_PROTO; } atapi_cmd.nblocks = atapi_cmd.arglen = (nsegs == 0) ? 0 : segs[0].ds_len; atapi_cmd.blksize = 1; atapi_cmd.buf = (nsegs == 0) ? 0 : segs[0].ds_addr; if (ccb->ccb_h.flags & CAM_DIR_OUT) bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap, BUS_DMASYNC_PREWRITE); err = lv1_storage_send_device_command(devid, LV1_STORAGE_SEND_ATAPI_COMMAND, vtophys(&atapi_cmd), sizeof(atapi_cmd), atapi_cmd.buf, atapi_cmd.arglen, &xp->x_tag); break; } } if (err) { device_printf(dev, "ATAPI command 0x%02x failed (%d)\n", cdb[0], err); bus_dmamap_unload(sc->sc_dmatag, xp->x_dmamap); xp->x_ccb = NULL; TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue); bzero(&ccb->csio.sense_data, sizeof(ccb->csio.sense_data)); /* Invalid field in parameter list */ scsi_set_sense_data(&ccb->csio.sense_data, /*sense_format*/ SSD_TYPE_NONE, /*current_error*/ 1, /*sense_key*/ SSD_KEY_ILLEGAL_REQUEST, /*asc*/ 0x26, /*ascq*/ 0x00, SSD_ELEM_NONE); ccb->csio.sense_len = SSD_FULL_SIZE; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; xpt_done(ccb); } else { CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("ATAPI command 0x%02x tag 0x%016lx submitted\n ", cdb[0], xp->x_tag)); TAILQ_INSERT_TAIL(&sc->sc_active_xferq, xp, x_queue); ccb->ccb_h.status |= CAM_SIM_QUEUED; } } static int ps3cdrom_decode_lv1_status(uint64_t status, u_int8_t *sense_key, u_int8_t *asc, u_int8_t *ascq) { if (((status >> 24) & 0xff) != SCSI_STATUS_CHECK_COND) return -1; *sense_key = (status >> 16) & 0xff; *asc = (status >> 8) & 0xff; *ascq = status & 0xff; return (0); } static device_method_t ps3cdrom_methods[] = { DEVMETHOD(device_probe, ps3cdrom_probe), DEVMETHOD(device_attach, ps3cdrom_attach), DEVMETHOD(device_detach, ps3cdrom_detach), {0, 0}, }; static driver_t ps3cdrom_driver = { "ps3cdrom", ps3cdrom_methods, sizeof(struct ps3cdrom_softc), }; static devclass_t ps3cdrom_devclass; DRIVER_MODULE(ps3cdrom, ps3bus, ps3cdrom_driver, ps3cdrom_devclass, 0, 0); MODULE_DEPEND(ps3cdrom, cam, 1, 1, 1); Index: head/sys/powerpc/ps3/ps3disk.c =================================================================== --- head/sys/powerpc/ps3/ps3disk.c (revision 326260) +++ head/sys/powerpc/ps3/ps3disk.c (revision 326261) @@ -1,702 +1,704 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2011 glevand (geoffrey.levand@mail.ru) * 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$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ps3bus.h" #include "ps3-hvcall.h" #define PS3DISK_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), "ps3disk", MTX_DEF) #define PS3DISK_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); #define PS3DISK_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define PS3DISK_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define PS3DISK_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); #define PS3DISK_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); #define LV1_STORAGE_ATA_HDDOUT 0x23 static SYSCTL_NODE(_hw, OID_AUTO, ps3disk, CTLFLAG_RD, 0, "PS3 Disk driver parameters"); #ifdef PS3DISK_DEBUG static int ps3disk_debug = 0; SYSCTL_INT(_hw_ps3disk, OID_AUTO, debug, CTLFLAG_RW, &ps3disk_debug, 0, "control debugging printfs"); TUNABLE_INT("hw.ps3disk.debug", &ps3disk_debug); enum { PS3DISK_DEBUG_INTR = 0x00000001, PS3DISK_DEBUG_TASK = 0x00000002, PS3DISK_DEBUG_READ = 0x00000004, PS3DISK_DEBUG_WRITE = 0x00000008, PS3DISK_DEBUG_FLUSH = 0x00000010, PS3DISK_DEBUG_ANY = 0xffffffff }; #define DPRINTF(sc, m, fmt, ...) \ do { \ if (sc->sc_debug & (m)) \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(sc, m, fmt, ...) #endif struct ps3disk_region { uint64_t r_id; uint64_t r_start; uint64_t r_size; uint64_t r_flags; }; struct ps3disk_softc { device_t sc_dev; struct mtx sc_mtx; uint64_t sc_blksize; uint64_t sc_nblocks; uint64_t sc_nregs; struct ps3disk_region *sc_reg; int sc_irqid; struct resource *sc_irq; void *sc_irqctx; struct disk **sc_disk; struct bio_queue_head sc_bioq; struct bio_queue_head sc_deferredq; struct proc *sc_task; bus_dma_tag_t sc_dmatag; int sc_running; int sc_debug; }; static int ps3disk_open(struct disk *dp); static int ps3disk_close(struct disk *dp); static void ps3disk_strategy(struct bio *bp); static void ps3disk_task(void *arg); static void ps3disk_intr(void *arg); static int ps3disk_get_disk_geometry(struct ps3disk_softc *sc); static int ps3disk_enum_regions(struct ps3disk_softc *sc); static void ps3disk_transfer(void *arg, bus_dma_segment_t *segs, int nsegs, int error); static void ps3disk_sysctlattach(struct ps3disk_softc *sc); static MALLOC_DEFINE(M_PS3DISK, "ps3disk", "PS3 Disk"); static int ps3disk_probe(device_t dev) { if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_STORAGE || ps3bus_get_devtype(dev) != PS3_DEVTYPE_DISK) return (ENXIO); device_set_desc(dev, "Playstation 3 Disk"); return (BUS_PROBE_SPECIFIC); } static int ps3disk_attach(device_t dev) { struct ps3disk_softc *sc; struct disk *d; intmax_t mb; uint64_t junk; char unit; int i, err; sc = device_get_softc(dev); sc->sc_dev = dev; PS3DISK_LOCK_INIT(sc); err = ps3disk_get_disk_geometry(sc); if (err) { device_printf(dev, "Could not get disk geometry\n"); err = ENXIO; goto fail_destroy_lock; } device_printf(dev, "block size %lu total blocks %lu\n", sc->sc_blksize, sc->sc_nblocks); err = ps3disk_enum_regions(sc); if (err) { device_printf(dev, "Could not enumerate disk regions\n"); err = ENXIO; goto fail_destroy_lock; } device_printf(dev, "Found %lu regions\n", sc->sc_nregs); if (!sc->sc_nregs) { err = ENXIO; goto fail_destroy_lock; } /* Setup interrupt handler */ sc->sc_irqid = 0; sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqid, RF_ACTIVE); if (!sc->sc_irq) { device_printf(dev, "Could not allocate IRQ\n"); err = ENXIO; goto fail_free_regions; } err = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_BIO | INTR_MPSAFE | INTR_ENTROPY, NULL, ps3disk_intr, sc, &sc->sc_irqctx); if (err) { device_printf(dev, "Could not setup IRQ\n"); err = ENXIO; goto fail_release_intr; } /* Setup DMA */ err = bus_dma_tag_create(bus_get_dma_tag(dev), 4096, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_UNRESTRICTED, 1, PAGE_SIZE, 0, busdma_lock_mutex, &sc->sc_mtx, &sc->sc_dmatag); if (err) { device_printf(dev, "Could not create DMA tag\n"); err = ENXIO; goto fail_teardown_intr; } /* Setup disks */ sc->sc_disk = malloc(sc->sc_nregs * sizeof(struct disk *), M_PS3DISK, M_ZERO | M_WAITOK); if (!sc->sc_disk) { device_printf(dev, "Could not allocate disk(s)\n"); err = ENOMEM; goto fail_teardown_intr; } for (i = 0; i < sc->sc_nregs; i++) { struct ps3disk_region *rp = &sc->sc_reg[i]; d = sc->sc_disk[i] = disk_alloc(); d->d_open = ps3disk_open; d->d_close = ps3disk_close; d->d_strategy = ps3disk_strategy; d->d_name = "ps3disk"; d->d_drv1 = sc; d->d_maxsize = PAGE_SIZE; d->d_sectorsize = sc->sc_blksize; d->d_unit = i; d->d_mediasize = sc->sc_reg[i].r_size * sc->sc_blksize; d->d_flags |= DISKFLAG_CANFLUSHCACHE; mb = d->d_mediasize >> 20; unit = 'M'; if (mb >= 10240) { unit = 'G'; mb /= 1024; } /* Test to see if we can read this region */ err = lv1_storage_read(ps3bus_get_device(dev), d->d_unit, 0, 0, rp->r_flags, 0, &junk); device_printf(dev, "region %d %ju%cB%s\n", i, mb, unit, (err == LV1_DENIED_BY_POLICY) ? " (hypervisor protected)" : ""); if (err != LV1_DENIED_BY_POLICY) disk_create(d, DISK_VERSION); } err = 0; bioq_init(&sc->sc_bioq); bioq_init(&sc->sc_deferredq); kproc_create(&ps3disk_task, sc, &sc->sc_task, 0, 0, "ps3disk"); ps3disk_sysctlattach(sc); sc->sc_running = 1; return (0); fail_teardown_intr: bus_teardown_intr(dev, sc->sc_irq, sc->sc_irqctx); fail_release_intr: bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqid, sc->sc_irq); fail_free_regions: free(sc->sc_reg, M_PS3DISK); fail_destroy_lock: PS3DISK_LOCK_DESTROY(sc); return (err); } static int ps3disk_detach(device_t dev) { struct ps3disk_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_nregs; i++) disk_destroy(sc->sc_disk[i]); bus_dma_tag_destroy(sc->sc_dmatag); bus_teardown_intr(dev, sc->sc_irq, sc->sc_irqctx); bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqid, sc->sc_irq); free(sc->sc_disk, M_PS3DISK); free(sc->sc_reg, M_PS3DISK); PS3DISK_LOCK_DESTROY(sc); return (0); } static int ps3disk_open(struct disk *dp) { return (0); } static int ps3disk_close(struct disk *dp) { return (0); } /* Process deferred blocks */ static void ps3disk_task(void *arg) { struct ps3disk_softc *sc = (struct ps3disk_softc *) arg; struct bio *bp; while (1) { kproc_suspend_check(sc->sc_task); tsleep(&sc->sc_deferredq, PRIBIO, "ps3disk", 10); PS3DISK_LOCK(sc); bp = bioq_takefirst(&sc->sc_deferredq); PS3DISK_UNLOCK(sc); if (bp == NULL) continue; if (bp->bio_driver1 != NULL) { bus_dmamap_unload(sc->sc_dmatag, (bus_dmamap_t) bp->bio_driver1); bus_dmamap_destroy(sc->sc_dmatag, (bus_dmamap_t) bp->bio_driver1); } ps3disk_strategy(bp); } kproc_exit(0); } static void ps3disk_strategy(struct bio *bp) { struct ps3disk_softc *sc = (struct ps3disk_softc *)bp->bio_disk->d_drv1; int err; if (sc == NULL) { bp->bio_flags |= BIO_ERROR; bp->bio_error = EINVAL; biodone(bp); return; } PS3DISK_LOCK(sc); bp->bio_resid = bp->bio_bcount; bioq_insert_tail(&sc->sc_bioq, bp); DPRINTF(sc, PS3DISK_DEBUG_TASK, "%s: bio_cmd 0x%02x\n", __func__, bp->bio_cmd); err = 0; if (bp->bio_cmd == BIO_FLUSH) { bp->bio_driver1 = 0; err = lv1_storage_send_device_command( ps3bus_get_device(sc->sc_dev), LV1_STORAGE_ATA_HDDOUT, 0, 0, 0, 0, (uint64_t *)&bp->bio_driver2); if (err == LV1_BUSY) err = EAGAIN; } else if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) { if (bp->bio_bcount % sc->sc_blksize != 0) { err = EINVAL; } else { bus_dmamap_create(sc->sc_dmatag, BUS_DMA_COHERENT, (bus_dmamap_t *)(&bp->bio_driver1)); err = bus_dmamap_load(sc->sc_dmatag, (bus_dmamap_t)(bp->bio_driver1), bp->bio_data, bp->bio_bcount, ps3disk_transfer, bp, 0); if (err == EINPROGRESS) err = 0; } } else { err = EINVAL; } if (err == EAGAIN) { bioq_remove(&sc->sc_bioq, bp); bioq_insert_tail(&sc->sc_deferredq, bp); } else if (err != 0) { bp->bio_error = err; bp->bio_flags |= BIO_ERROR; bioq_remove(&sc->sc_bioq, bp); disk_err(bp, "hard error", -1, 1); biodone(bp); } PS3DISK_UNLOCK(sc); } static void ps3disk_intr(void *arg) { struct ps3disk_softc *sc = (struct ps3disk_softc *) arg; device_t dev = sc->sc_dev; uint64_t devid = ps3bus_get_device(dev); struct bio *bp; uint64_t tag, status; if (lv1_storage_get_async_status(devid, &tag, &status) != 0) return; PS3DISK_LOCK(sc); DPRINTF(sc, PS3DISK_DEBUG_INTR, "%s: tag 0x%016lx " "status 0x%016lx\n", __func__, tag, status); /* Locate the matching request */ TAILQ_FOREACH(bp, &sc->sc_bioq.queue, bio_queue) { if ((uint64_t)bp->bio_driver2 != tag) continue; if (status != 0) { device_printf(sc->sc_dev, "%s error (%#lx)\n", (bp->bio_cmd == BIO_READ) ? "Read" : "Write", status); bp->bio_error = EIO; bp->bio_flags |= BIO_ERROR; } else { bp->bio_error = 0; bp->bio_resid = 0; bp->bio_flags |= BIO_DONE; } if (bp->bio_driver1 != NULL) { if (bp->bio_cmd == BIO_READ) bus_dmamap_sync(sc->sc_dmatag, (bus_dmamap_t) bp->bio_driver1, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->sc_dmatag, (bus_dmamap_t) bp->bio_driver1); bus_dmamap_destroy(sc->sc_dmatag, (bus_dmamap_t) bp->bio_driver1); } bioq_remove(&sc->sc_bioq, bp); biodone(bp); break; } if (bioq_first(&sc->sc_deferredq) != NULL) wakeup(&sc->sc_deferredq); PS3DISK_UNLOCK(sc); } static int ps3disk_get_disk_geometry(struct ps3disk_softc *sc) { device_t dev = sc->sc_dev; uint64_t bus_index = ps3bus_get_busidx(dev); uint64_t dev_index = ps3bus_get_devidx(dev); uint64_t junk; int err; err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("dev") | dev_index, lv1_repository_string("blk_size"), 0, &sc->sc_blksize, &junk); if (err) { device_printf(dev, "Could not get block size (0x%08x)\n", err); return (ENXIO); } err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("dev") | dev_index, lv1_repository_string("n_blocks"), 0, &sc->sc_nblocks, &junk); if (err) { device_printf(dev, "Could not get total number of blocks " "(0x%08x)\n", err); err = ENXIO; } return (err); } static int ps3disk_enum_regions(struct ps3disk_softc *sc) { device_t dev = sc->sc_dev; uint64_t bus_index = ps3bus_get_busidx(dev); uint64_t dev_index = ps3bus_get_devidx(dev); uint64_t junk; int i, err; /* Read number of regions */ err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("dev") | dev_index, lv1_repository_string("n_regs"), 0, &sc->sc_nregs, &junk); if (err) { device_printf(dev, "Could not get number of regions (0x%08x)\n", err); err = ENXIO; goto fail; } if (!sc->sc_nregs) return 0; sc->sc_reg = malloc(sc->sc_nregs * sizeof(struct ps3disk_region), M_PS3DISK, M_ZERO | M_WAITOK); if (!sc->sc_reg) { err = ENOMEM; goto fail; } /* Setup regions */ for (i = 0; i < sc->sc_nregs; i++) { err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("dev") | dev_index, lv1_repository_string("region") | i, lv1_repository_string("id"), &sc->sc_reg[i].r_id, &junk); if (err) { device_printf(dev, "Could not get region id (0x%08x)\n", err); err = ENXIO; goto fail; } err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("dev") | dev_index, lv1_repository_string("region") | i, lv1_repository_string("start"), &sc->sc_reg[i].r_start, &junk); if (err) { device_printf(dev, "Could not get region start " "(0x%08x)\n", err); err = ENXIO; goto fail; } err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, (lv1_repository_string("bus") >> 32) | bus_index, lv1_repository_string("dev") | dev_index, lv1_repository_string("region") | i, lv1_repository_string("size"), &sc->sc_reg[i].r_size, &junk); if (err) { device_printf(dev, "Could not get region size " "(0x%08x)\n", err); err = ENXIO; goto fail; } if (i == 0) sc->sc_reg[i].r_flags = 0x2; else sc->sc_reg[i].r_flags = 0; } return (0); fail: sc->sc_nregs = 0; if (sc->sc_reg) free(sc->sc_reg, M_PS3DISK); return (err); } static void ps3disk_transfer(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct bio *bp = (struct bio *)(arg); struct ps3disk_softc *sc = (struct ps3disk_softc *)bp->bio_disk->d_drv1; struct ps3disk_region *rp = &sc->sc_reg[bp->bio_disk->d_unit]; uint64_t devid = ps3bus_get_device(sc->sc_dev); uint64_t block; int i, err; /* Locks already held by busdma */ PS3DISK_ASSERT_LOCKED(sc); if (error) { bp->bio_error = error; bp->bio_flags |= BIO_ERROR; bioq_remove(&sc->sc_bioq, bp); biodone(bp); return; } block = bp->bio_pblkno; for (i = 0; i < nsegs; i++) { KASSERT((segs[i].ds_len % sc->sc_blksize) == 0, ("DMA fragments not blocksize multiples")); if (bp->bio_cmd == BIO_READ) { err = lv1_storage_read(devid, rp->r_id, block, segs[i].ds_len/sc->sc_blksize, rp->r_flags, segs[i].ds_addr, (uint64_t *)&bp->bio_driver2); } else { bus_dmamap_sync(sc->sc_dmatag, (bus_dmamap_t)bp->bio_driver1, BUS_DMASYNC_PREWRITE); err = lv1_storage_write(devid, rp->r_id, block, segs[i].ds_len/sc->sc_blksize, rp->r_flags, segs[i].ds_addr, (uint64_t *)&bp->bio_driver2); } if (err) { if (err == LV1_BUSY) { bioq_remove(&sc->sc_bioq, bp); bioq_insert_tail(&sc->sc_deferredq, bp); } else { bus_dmamap_unload(sc->sc_dmatag, (bus_dmamap_t) bp->bio_driver1); bus_dmamap_destroy(sc->sc_dmatag, (bus_dmamap_t) bp->bio_driver1); device_printf(sc->sc_dev, "Could not read " "sectors (0x%08x)\n", err); bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; bioq_remove(&sc->sc_bioq, bp); biodone(bp); } break; } DPRINTF(sc, PS3DISK_DEBUG_READ, "%s: tag 0x%016lx\n", __func__, sc->sc_bounce_tag); } } #ifdef PS3DISK_DEBUG static int ps3disk_sysctl_debug(SYSCTL_HANDLER_ARGS) { struct ps3disk_softc *sc = arg1; int debug, error; debug = sc->sc_debug; error = sysctl_handle_int(oidp, &debug, 0, req); if (error || !req->newptr) return error; sc->sc_debug = debug; return 0; } #endif static void ps3disk_sysctlattach(struct ps3disk_softc *sc) { #ifdef PS3DISK_DEBUG struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); sc->sc_debug = ps3disk_debug; SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "debug", CTLTYPE_INT | CTLFLAG_RW, sc, 0, ps3disk_sysctl_debug, "I", "control debugging printfs"); #endif } static device_method_t ps3disk_methods[] = { DEVMETHOD(device_probe, ps3disk_probe), DEVMETHOD(device_attach, ps3disk_attach), DEVMETHOD(device_detach, ps3disk_detach), {0, 0}, }; static driver_t ps3disk_driver = { "ps3disk", ps3disk_methods, sizeof(struct ps3disk_softc), }; static devclass_t ps3disk_devclass; DRIVER_MODULE(ps3disk, ps3bus, ps3disk_driver, ps3disk_devclass, 0, 0); Index: head/sys/powerpc/ps3/ps3pic.c =================================================================== --- head/sys/powerpc/ps3/ps3pic.c (revision 326260) +++ head/sys/powerpc/ps3/ps3pic.c (revision 326261) @@ -1,248 +1,250 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 2010 Nathan Whitehorn * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ps3-hvcall.h" #include "pic_if.h" static void ps3pic_identify(driver_t *driver, device_t parent); static int ps3pic_probe(device_t); static int ps3pic_attach(device_t); static void ps3pic_dispatch(device_t, struct trapframe *); static void ps3pic_enable(device_t, u_int, u_int); static void ps3pic_eoi(device_t, u_int); static void ps3pic_ipi(device_t, u_int); static void ps3pic_mask(device_t, u_int); static void ps3pic_unmask(device_t, u_int); struct ps3pic_softc { volatile uint64_t *bitmap_thread0; volatile uint64_t *mask_thread0; volatile uint64_t *bitmap_thread1; volatile uint64_t *mask_thread1; uint64_t sc_ipi_outlet[2]; uint64_t sc_ipi_virq; int sc_vector[64]; }; static device_method_t ps3pic_methods[] = { /* Device interface */ DEVMETHOD(device_identify, ps3pic_identify), DEVMETHOD(device_probe, ps3pic_probe), DEVMETHOD(device_attach, ps3pic_attach), /* PIC interface */ DEVMETHOD(pic_dispatch, ps3pic_dispatch), DEVMETHOD(pic_enable, ps3pic_enable), DEVMETHOD(pic_eoi, ps3pic_eoi), DEVMETHOD(pic_ipi, ps3pic_ipi), DEVMETHOD(pic_mask, ps3pic_mask), DEVMETHOD(pic_unmask, ps3pic_unmask), { 0, 0 }, }; static driver_t ps3pic_driver = { "ps3pic", ps3pic_methods, sizeof(struct ps3pic_softc) }; static devclass_t ps3pic_devclass; DRIVER_MODULE(ps3pic, nexus, ps3pic_driver, ps3pic_devclass, 0, 0); static MALLOC_DEFINE(M_PS3PIC, "ps3pic", "PS3 PIC"); static void ps3pic_identify(driver_t *driver, device_t parent) { if (strcmp(installed_platform(), "ps3") != 0) return; if (device_find_child(parent, "ps3pic", -1) == NULL) BUS_ADD_CHILD(parent, 0, "ps3pic", 0); } static int ps3pic_probe(device_t dev) { device_set_desc(dev, "Playstation 3 interrupt controller"); return (BUS_PROBE_NOWILDCARD); } static int ps3pic_attach(device_t dev) { struct ps3pic_softc *sc; uint64_t ppe; int thread; sc = device_get_softc(dev); sc->bitmap_thread0 = contigmalloc(128 /* 512 bits * 2 */, M_PS3PIC, M_NOWAIT | M_ZERO, 0, BUS_SPACE_MAXADDR, 64 /* alignment */, PAGE_SIZE /* boundary */); sc->mask_thread0 = sc->bitmap_thread0 + 4; sc->bitmap_thread1 = sc->bitmap_thread0 + 8; sc->mask_thread1 = sc->bitmap_thread0 + 12; lv1_get_logical_ppe_id(&ppe); thread = 32 - fls(mfctrl()); lv1_configure_irq_state_bitmap(ppe, thread, vtophys(sc->bitmap_thread0)); sc->sc_ipi_virq = 63; #ifdef SMP lv1_configure_irq_state_bitmap(ppe, !thread, vtophys(sc->bitmap_thread1)); /* Map both IPIs to the same VIRQ to avoid changes in intr_machdep */ lv1_construct_event_receive_port(&sc->sc_ipi_outlet[0]); lv1_connect_irq_plug_ext(ppe, thread, sc->sc_ipi_virq, sc->sc_ipi_outlet[0], 0); lv1_construct_event_receive_port(&sc->sc_ipi_outlet[1]); lv1_connect_irq_plug_ext(ppe, !thread, sc->sc_ipi_virq, sc->sc_ipi_outlet[1], 0); #endif powerpc_register_pic(dev, 0, sc->sc_ipi_virq, 1, FALSE); return (0); } /* * PIC I/F methods. */ static void ps3pic_dispatch(device_t dev, struct trapframe *tf) { uint64_t bitmap, mask; int irq; struct ps3pic_softc *sc; sc = device_get_softc(dev); if (PCPU_GET(cpuid) == 0) { bitmap = atomic_readandclear_64(&sc->bitmap_thread0[0]); mask = sc->mask_thread0[0]; } else { bitmap = atomic_readandclear_64(&sc->bitmap_thread1[0]); mask = sc->mask_thread1[0]; } powerpc_sync(); while ((irq = ffsl(bitmap & mask) - 1) != -1) { bitmap &= ~(1UL << irq); powerpc_dispatch_intr(sc->sc_vector[63 - irq], tf); } } static void ps3pic_enable(device_t dev, u_int irq, u_int vector) { struct ps3pic_softc *sc; sc = device_get_softc(dev); sc->sc_vector[irq] = vector; ps3pic_unmask(dev, irq); } static void ps3pic_eoi(device_t dev, u_int irq) { uint64_t ppe; int thread; lv1_get_logical_ppe_id(&ppe); thread = 32 - fls(mfctrl()); lv1_end_of_interrupt_ext(ppe, thread, irq); } static void ps3pic_ipi(device_t dev, u_int cpu) { struct ps3pic_softc *sc; sc = device_get_softc(dev); lv1_send_event_locally(sc->sc_ipi_outlet[cpu]); } static void ps3pic_mask(device_t dev, u_int irq) { struct ps3pic_softc *sc; uint64_t ppe; sc = device_get_softc(dev); /* Do not mask IPIs! */ if (irq == sc->sc_ipi_virq) return; atomic_clear_64(&sc->mask_thread0[0], 1UL << (63 - irq)); atomic_clear_64(&sc->mask_thread1[0], 1UL << (63 - irq)); lv1_get_logical_ppe_id(&ppe); lv1_did_update_interrupt_mask(ppe, 0); lv1_did_update_interrupt_mask(ppe, 1); } static void ps3pic_unmask(device_t dev, u_int irq) { struct ps3pic_softc *sc; uint64_t ppe; sc = device_get_softc(dev); atomic_set_64(&sc->mask_thread0[0], 1UL << (63 - irq)); atomic_set_64(&sc->mask_thread1[0], 1UL << (63 - irq)); lv1_get_logical_ppe_id(&ppe); lv1_did_update_interrupt_mask(ppe, 0); lv1_did_update_interrupt_mask(ppe, 1); } Index: head/sys/powerpc/pseries/mmu_phyp.c =================================================================== --- head/sys/powerpc/pseries/mmu_phyp.c (revision 326260) +++ head/sys/powerpc/pseries/mmu_phyp.c (revision 326261) @@ -1,472 +1,474 @@ -/* +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2010 Andreas Tobler * 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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mmu_if.h" #include "moea64_if.h" #include "phyp-hvcall.h" static struct rmlock mphyp_eviction_lock; /* * Kernel MMU interface */ static void mphyp_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend); static void mphyp_cpu_bootstrap(mmu_t mmup, int ap); static int64_t mphyp_pte_synch(mmu_t, struct pvo_entry *pvo); static int64_t mphyp_pte_clear(mmu_t, struct pvo_entry *pvo, uint64_t ptebit); static int64_t mphyp_pte_unset(mmu_t, struct pvo_entry *pvo); static int mphyp_pte_insert(mmu_t, struct pvo_entry *pvo); static mmu_method_t mphyp_methods[] = { MMUMETHOD(mmu_bootstrap, mphyp_bootstrap), MMUMETHOD(mmu_cpu_bootstrap, mphyp_cpu_bootstrap), MMUMETHOD(moea64_pte_synch, mphyp_pte_synch), MMUMETHOD(moea64_pte_clear, mphyp_pte_clear), MMUMETHOD(moea64_pte_unset, mphyp_pte_unset), MMUMETHOD(moea64_pte_insert, mphyp_pte_insert), /* XXX: pmap_copy_page, pmap_init_page with H_PAGE_INIT */ { 0, 0 } }; MMU_DEF_INHERIT(pseries_mmu, "mmu_phyp", mphyp_methods, 0, oea64_mmu); static int brokenkvm = 0; static void print_kvm_bug_warning(void *data) { if (brokenkvm) printf("WARNING: Running on a broken hypervisor that does " "not support mandatory H_CLEAR_MOD and H_CLEAR_REF " "hypercalls. Performance will be suboptimal.\n"); } SYSINIT(kvmbugwarn1, SI_SUB_COPYRIGHT, SI_ORDER_THIRD + 1, print_kvm_bug_warning, NULL); SYSINIT(kvmbugwarn2, SI_SUB_LAST, SI_ORDER_THIRD + 1, print_kvm_bug_warning, NULL); static void mphyp_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) { uint64_t final_pteg_count = 0; char buf[8]; uint32_t prop[2]; uint32_t nptlp, shift = 0, slb_encoding = 0; uint32_t lp_size, lp_encoding; struct lpte old; uint64_t vsid; phandle_t dev, node, root; int idx, len, res; rm_init(&mphyp_eviction_lock, "pte eviction"); moea64_early_bootstrap(mmup, kernelstart, kernelend); root = OF_peer(0); dev = OF_child(root); while (dev != 0) { res = OF_getprop(dev, "name", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpus") == 0) break; dev = OF_peer(dev); } node = OF_child(dev); while (node != 0) { res = OF_getprop(node, "device_type", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpu") == 0) break; node = OF_peer(node); } res = OF_getencprop(node, "ibm,pft-size", prop, sizeof(prop)); if (res <= 0) panic("mmu_phyp: unknown PFT size"); final_pteg_count = 1 << prop[1]; res = OF_getencprop(node, "ibm,slb-size", prop, sizeof(prop[0])); if (res > 0) n_slbs = prop[0]; moea64_pteg_count = final_pteg_count / sizeof(struct lpteg); /* Clear any old page table entries */ for (idx = 0; idx < moea64_pteg_count*8; idx++) { phyp_pft_hcall(H_READ, 0, idx, 0, 0, &old.pte_hi, &old.pte_lo, &old.pte_lo); vsid = (old.pte_hi << (ADDR_API_SHFT64 - ADDR_PIDX_SHFT)) >> 28; if (vsid == VSID_VRMA || vsid == 0 /* Older VRMA */) continue; if (old.pte_hi & LPTE_VALID) phyp_hcall(H_REMOVE, 0, idx, 0); } /* * Scan the large page size property for PAPR compatible machines. * See PAPR D.5 Changes to Section 5.1.4, 'CPU Node Properties' * for the encoding of the property. */ len = OF_getproplen(node, "ibm,segment-page-sizes"); if (len > 0) { /* * We have to use a variable length array on the stack * since we have very limited stack space. */ pcell_t arr[len/sizeof(cell_t)]; res = OF_getencprop(node, "ibm,segment-page-sizes", arr, sizeof(arr)); len /= 4; idx = 0; while (len > 0) { shift = arr[idx]; slb_encoding = arr[idx + 1]; nptlp = arr[idx + 2]; idx += 3; len -= 3; while (len > 0 && nptlp) { lp_size = arr[idx]; lp_encoding = arr[idx+1]; if (slb_encoding == SLBV_L && lp_encoding == 0) break; idx += 2; len -= 2; nptlp--; } if (nptlp && slb_encoding == SLBV_L && lp_encoding == 0) break; } if (len == 0) panic("Standard large pages (SLB[L] = 1, PTE[LP] = 0) " "not supported by this system. Please enable huge " "page backing if running under PowerKVM."); moea64_large_page_shift = shift; moea64_large_page_size = 1ULL << lp_size; } moea64_mid_bootstrap(mmup, kernelstart, kernelend); moea64_late_bootstrap(mmup, kernelstart, kernelend); /* Test for broken versions of KVM that don't conform to the spec */ if (phyp_hcall(H_CLEAR_MOD, 0, 0) == H_FUNCTION) brokenkvm = 1; } static void mphyp_cpu_bootstrap(mmu_t mmup, int ap) { struct slb *slb = PCPU_GET(slb); register_t seg0; int i; /* * Install kernel SLB entries */ __asm __volatile ("slbia"); __asm __volatile ("slbmfee %0,%1; slbie %0;" : "=r"(seg0) : "r"(0)); for (i = 0; i < 64; i++) { if (!(slb[i].slbe & SLBE_VALID)) continue; __asm __volatile ("slbmte %0, %1" :: "r"(slb[i].slbv), "r"(slb[i].slbe)); } } static int64_t mphyp_pte_synch(mmu_t mmu, struct pvo_entry *pvo) { struct lpte pte; uint64_t junk; __asm __volatile("ptesync"); phyp_pft_hcall(H_READ, 0, pvo->pvo_pte.slot, 0, 0, &pte.pte_hi, &pte.pte_lo, &junk); if ((pte.pte_hi & LPTE_AVPN_MASK) != ((pvo->pvo_vpn >> (ADDR_API_SHFT64 - ADDR_PIDX_SHFT)) & LPTE_AVPN_MASK)) return (-1); if (!(pte.pte_hi & LPTE_VALID)) return (-1); return (pte.pte_lo & (LPTE_CHG | LPTE_REF)); } static int64_t mphyp_pte_clear(mmu_t mmu, struct pvo_entry *pvo, uint64_t ptebit) { struct rm_priotracker track; int64_t refchg; uint64_t ptelo, junk; int err; /* * This involves two steps (synch and clear) so we need the entry * not to change in the middle. We are protected against deliberate * unset by virtue of holding the pmap lock. Protection against * incidental unset (page table eviction) comes from holding the * shared eviction lock. */ PMAP_LOCK_ASSERT(pvo->pvo_pmap, MA_OWNED); rm_rlock(&mphyp_eviction_lock, &track); refchg = mphyp_pte_synch(mmu, pvo); if (refchg < 0) { rm_runlock(&mphyp_eviction_lock, &track); return (refchg); } if (brokenkvm) { /* * No way to clear either bit, which is total madness. * Pessimistically claim that, once modified, it stays so * forever and that it is never referenced. */ rm_runlock(&mphyp_eviction_lock, &track); return (refchg & ~LPTE_REF); } if (ptebit & LPTE_CHG) { err = phyp_pft_hcall(H_CLEAR_MOD, 0, pvo->pvo_pte.slot, 0, 0, &ptelo, &junk, &junk); KASSERT(err == H_SUCCESS, ("Error clearing page change bit: %d", err)); refchg |= (ptelo & LPTE_CHG); } if (ptebit & LPTE_REF) { err = phyp_pft_hcall(H_CLEAR_REF, 0, pvo->pvo_pte.slot, 0, 0, &ptelo, &junk, &junk); KASSERT(err == H_SUCCESS, ("Error clearing page reference bit: %d", err)); refchg |= (ptelo & LPTE_REF); } rm_runlock(&mphyp_eviction_lock, &track); return (refchg); } static int64_t mphyp_pte_unset(mmu_t mmu, struct pvo_entry *pvo) { struct lpte pte; uint64_t junk; int err; PMAP_LOCK_ASSERT(pvo->pvo_pmap, MA_OWNED); moea64_pte_from_pvo(pvo, &pte); err = phyp_pft_hcall(H_REMOVE, H_AVPN, pvo->pvo_pte.slot, pte.pte_hi & LPTE_AVPN_MASK, 0, &pte.pte_hi, &pte.pte_lo, &junk); KASSERT(err == H_SUCCESS || err == H_NOT_FOUND, ("Error removing page: %d", err)); if (err == H_NOT_FOUND) { moea64_pte_overflow--; return (-1); } return (pte.pte_lo & (LPTE_REF | LPTE_CHG)); } static uintptr_t mphyp_pte_spillable_ident(uintptr_t ptegbase, struct lpte *to_evict) { uint64_t slot, junk, k; struct lpte pt; int i, j; /* Start at a random slot */ i = mftb() % 8; k = -1; for (j = 0; j < 8; j++) { slot = ptegbase + (i + j) % 8; phyp_pft_hcall(H_READ, 0, slot, 0, 0, &pt.pte_hi, &pt.pte_lo, &junk); if (pt.pte_hi & LPTE_WIRED) continue; /* This is a candidate, so remember it */ k = slot; /* Try to get a page that has not been used lately */ if (!(pt.pte_hi & LPTE_VALID) || !(pt.pte_lo & LPTE_REF)) { memcpy(to_evict, &pt, sizeof(struct lpte)); return (k); } } if (k == -1) return (k); phyp_pft_hcall(H_READ, 0, k, 0, 0, &to_evict->pte_hi, &to_evict->pte_lo, &junk); return (k); } static int mphyp_pte_insert(mmu_t mmu, struct pvo_entry *pvo) { struct rm_priotracker track; int64_t result; struct lpte evicted, pte; uint64_t index, junk, lastptelo; PMAP_LOCK_ASSERT(pvo->pvo_pmap, MA_OWNED); /* Initialize PTE */ moea64_pte_from_pvo(pvo, &pte); evicted.pte_hi = 0; /* Make sure further insertion is locked out during evictions */ rm_rlock(&mphyp_eviction_lock, &track); /* * First try primary hash. */ pvo->pvo_pte.slot &= ~7UL; /* Base slot address */ result = phyp_pft_hcall(H_ENTER, 0, pvo->pvo_pte.slot, pte.pte_hi, pte.pte_lo, &index, &evicted.pte_lo, &junk); if (result == H_SUCCESS) { rm_runlock(&mphyp_eviction_lock, &track); pvo->pvo_pte.slot = index; return (0); } KASSERT(result == H_PTEG_FULL, ("Page insertion error: %ld " "(ptegidx: %#zx/%#x, PTE %#lx/%#lx", result, pvo->pvo_pte.slot, moea64_pteg_count, pte.pte_hi, pte.pte_lo)); /* * Next try secondary hash. */ pvo->pvo_vaddr ^= PVO_HID; pte.pte_hi ^= LPTE_HID; pvo->pvo_pte.slot ^= (moea64_pteg_mask << 3); result = phyp_pft_hcall(H_ENTER, 0, pvo->pvo_pte.slot, pte.pte_hi, pte.pte_lo, &index, &evicted.pte_lo, &junk); if (result == H_SUCCESS) { rm_runlock(&mphyp_eviction_lock, &track); pvo->pvo_pte.slot = index; return (0); } KASSERT(result == H_PTEG_FULL, ("Secondary page insertion error: %ld", result)); /* * Out of luck. Find a PTE to sacrifice. */ /* Lock out all insertions for a bit */ rm_runlock(&mphyp_eviction_lock, &track); rm_wlock(&mphyp_eviction_lock); index = mphyp_pte_spillable_ident(pvo->pvo_pte.slot, &evicted); if (index == -1L) { /* Try other hash table? */ pvo->pvo_vaddr ^= PVO_HID; pte.pte_hi ^= LPTE_HID; pvo->pvo_pte.slot ^= (moea64_pteg_mask << 3); index = mphyp_pte_spillable_ident(pvo->pvo_pte.slot, &evicted); } if (index == -1L) { /* No freeable slots in either PTEG? We're hosed. */ rm_wunlock(&mphyp_eviction_lock); panic("mphyp_pte_insert: overflow"); return (-1); } /* Victim acquired: update page before waving goodbye */ if (evicted.pte_hi & LPTE_VALID) { result = phyp_pft_hcall(H_REMOVE, H_AVPN, index, evicted.pte_hi & LPTE_AVPN_MASK, 0, &junk, &lastptelo, &junk); moea64_pte_overflow++; KASSERT(result == H_SUCCESS, ("Error evicting page: %d", (int)result)); } /* * Set the new PTE. */ result = phyp_pft_hcall(H_ENTER, H_EXACT, index, pte.pte_hi, pte.pte_lo, &index, &evicted.pte_lo, &junk); rm_wunlock(&mphyp_eviction_lock); /* All clear */ pvo->pvo_pte.slot = index; if (result == H_SUCCESS) return (0); panic("Page replacement error: %ld", result); return (result); } Index: head/sys/powerpc/pseries/phyp-hvcall.h =================================================================== --- head/sys/powerpc/pseries/phyp-hvcall.h (revision 326260) +++ head/sys/powerpc/pseries/phyp-hvcall.h (revision 326261) @@ -1,305 +1,307 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2010 Andreas Tobler * 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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _PSERIES_PHYP_HVCALL_H_ #define _PSERIES_PHYP_HVCALL_H_ /* Information taken from: Power.org PAPR, Version 2.4 (December 7, 2009). */ #include /* Return codes. */ #define H_SUCCESS 0 #define H_BUSY 1 /* Hardware Busy -- Retry Later. */ #define H_CLOSED 2 /* Virtual I/O connection is closed. */ #define H_NOT_AVAILABLE 3 #define H_CONSTRAINED 4 /* The request called for resources in excess of the maximum allowed. The resultant allocation was constrained to maximum allowed. */ #define H_PARTIAL 5 /* The request completed only partially successful. Parameters were valid but some specific hcall function condition prevented fully completing the architected function, see the specific hcall definition for possible reasons. */ #define H_IN_PROGRESS 14 #define H_PAGE_REGISTERED 15 #define H_PARTIAL_STORE 16 #define H_PENDING 17 #define H_CONTINUE 18 #define H_LONG_BUSY_ORDER_1_MS 9900 /* This return code is identical to H_BUSY, but with the added bonus of a hint to the partition OS. If the partition OS can delay for 1 millisecond, the hcall will likely succeed on a new hcall with no further busy return codes. If the partition OS cannot handle a delay, they are certainly free to immediately turn around and try again. */ #define H_LONG_BUSY_ORDER_10_MS 9901 /* Similar to H_LONG_BUSY_ORDER_1_MS, but the hint is 10mSec wait this time. */ #define H_LONG_BUSY_ORDER_100_MS 9902 /* Similar to H_LONG_BUSY_ORDER_1_MS, but the hint is 100mSec wait this time. */ #define H_LONG_BUSY_ORDER_1_S 9903 /* Similar to H_LONG_BUSY_ORDER_1_MS, but the hint is 1Sec wait this time. */ #define H_LONG_BUSY_ORDER_10_S 9904 /* Similar to H_LONG_BUSY_ORDER_1_MS, but the hint is 10Sec wait this time. */ #define H_LONG_BUSY_ORDER_100_S 9905 /* Similar to H_LONG_BUSY_ORDER_1_MS, but the hint is 100Sec wait this time. */ #define H_HARDWARE -1 /* Error. */ #define H_FUNCTION -2 /* Not supported. */ #define H_PRIVILEGE -3 /* Caller not in privileged mode. */ #define H_PARAMETER -4 /* Outside valid range for partition or conflicting. */ #define H_BAD_MODE -5 /* Illegal MSR value. */ #define H_PTEG_FULL -6 /* The requested pteg was full. */ #define H_NOT_FOUND -7 /* The requested entitiy was not found. */ #define H_RESERVED_DABR -8 /* The requested address is reserved by the hypervisor on this processor. */ #define H_NOMEM -9 #define H_AUTHORITY -10 /* The caller did not have authority to perform the function. */ #define H_PERMISSION -11 /* The mapping specified by the request does not allow for the requested transfer. */ #define H_DROPPED -12 /* One or more packets could not be delivered to their requested destinations. */ #define H_S_PARM -13 /* The source parameter is illegal. */ #define H_D_PARM -14 /* The destination parameter is illegal. */ #define H_R_PARM -15 /* The remote TCE mapping is illegal. */ #define H_RESOURCE -16 /* One or more required resources are in use. */ #define H_ADAPTER_PARM -17 /* Invalid adapter. */ #define H_RH_PARM -18 /* Resource not valid or logical partition conflicting. */ #define H_RCQ_PARM -19 /* RCQ not valid or logical partition conflicting. */ #define H_SCQ_PARM -20 /* SCQ not valid or logical partition conflicting. */ #define H_EQ_PARM -21 /* EQ not valid or logical partition conflicting. */ #define H_RT_PARM -22 /* Invalid resource type. */ #define H_ST_PARM -23 /* Invalid service type. */ #define H_SIGT_PARM -24 /* Invalid signalling type. */ #define H_TOKEN_PARM -25 /* Invalid token. */ #define H_MLENGTH_PARM -27 /* Invalid memory length. */ #define H_MEM_PARM -28 /* Invalid memory I/O virtual address. */ #define H_MEM_ACCESS_PARM -29 /* Invalid memory access control. */ #define H_ATTR_PARM -30 /* Invalid attribute value. */ #define H_PORT_PARM -31 /* Invalid port number. */ #define H_MCG_PARM -32 /* Invalid multicast group. */ #define H_VL_PARM -33 /* Invalid virtual lane. */ #define H_TSIZE_PARM -34 /* Invalid trace size. */ #define H_TRACE_PARM -35 /* Invalid trace buffer. */ #define H_MASK_PARM -37 /* Invalid mask value. */ #define H_MCG_FULL -38 /* Multicast attachments exceeded. */ #define H_ALIAS_EXIST -39 /* Alias QP already defined. */ #define H_P_COUNTER -40 /* Invalid counter specification. */ #define H_TABLE_FULL -41 /* Resource page table full. */ #define H_ALT_TABLE -42 /* Alternate table already exists / alternate page table not available. */ #define H_MR_CONDITION -43 /* Invalid memory region condition. */ #define H_NOT_ENOUGH_RESOURCES -44 /* Insufficient resources. */ #define H_R_STATE -45 /* Invalid resource state condition or sequencing error. */ #define H_RESCINDED -46 #define H_ABORTED -54 #define H_P2 -55 #define H_P3 -56 #define H_P4 -57 #define H_P5 -58 #define H_P6 -59 #define H_P7 -60 #define H_P8 -61 #define H_P9 -62 #define H_NOOP -63 #define H_TOO_BIG -64 #define H_UNSUPPORTED -67 /* Parameter value outside of the range supported by this implementation. */ /* Flags. */ /* Table 168. Page Frame Table Access flags field definition. */ #define H_EXACT (1UL<<(63-24)) #define H_R_XLATE (1UL<<(63-25)) #define H_READ_4 (1UL<<(63-26)) /* Table 178. CMO Page Usage State flags Definition. */ #define H_PAGE_STATE_CHANGE (1UL<<(63-28)) #define H_PAGE_UNUSED ((1UL<<(63-29)) | (1UL<<(63-30))) #define H_PAGE_SET_UNUSED (H_PAGE_STATE_CHANGE | H_PAGE_UNUSED) #define H_PAGE_SET_LOANED (H_PAGE_SET_UNUSED | (1UL<<(63-31))) #define H_PAGE_SET_ACTIVE H_PAGE_STATE_CHANGE /* Table 168. Page Frame Table Access flags field definition. */ #define H_AVPN (1UL<<(63-32)) #define H_ANDCOND (1UL<<(63-33)) #define H_ICACHE_INVALIDATE (1UL<<(63-40)) #define H_ICACHE_SYNCHRONIZE (1UL<<(63-41)) #define H_ZERO_PAGE (1UL<<(63-48)) #define H_COPY_PAGE (1UL<<(63-49)) #define H_N (1UL<<(63-61)) #define H_PP1 (1UL<<(63-62)) #define H_PP2 (1UL<<(63-63)) /* pSeries hypervisor opcodes. */ #define H_REMOVE 0x04 #define H_ENTER 0x08 #define H_READ 0x0c #define H_CLEAR_MOD 0x10 #define H_CLEAR_REF 0x14 #define H_PROTECT 0x18 #define H_GET_TCE 0x1c #define H_PUT_TCE 0x20 #define H_SET_SPRG0 0x24 #define H_SET_DABR 0x28 #define H_PAGE_INIT 0x2c #define H_SET_ASR 0x30 #define H_ASR_ON 0x34 #define H_ASR_OFF 0x38 #define H_LOGICAL_CI_LOAD 0x3c #define H_LOGICAL_CI_STORE 0x40 #define H_LOGICAL_CACHE_LOAD 0x44 #define H_LOGICAL_CACHE_STORE 0x48 #define H_LOGICAL_ICBI 0x4c #define H_LOGICAL_DCBF 0x50 #define H_GET_TERM_CHAR 0x54 #define H_PUT_TERM_CHAR 0x58 #define H_REAL_TO_LOGICAL 0x5c #define H_HYPERVISOR_DATA 0x60 #define H_EOI 0x64 #define H_CPPR 0x68 #define H_IPI 0x6c #define H_IPOLL 0x70 #define H_XIRR 0x74 #define H_MIGRATE_DMA 0x78 #define H_PERFMON 0x7c #define H_REGISTER_VPA 0xdc #define H_CEDE 0xe0 #define H_CONFER 0xe4 #define H_PROD 0xe8 #define H_GET_PPP 0xec #define H_SET_PPP 0xf0 #define H_PURR 0xf4 #define H_PIC 0xf8 #define H_REG_CRQ 0xfc #define H_FREE_CRQ 0x100 #define H_VIO_SIGNAL 0x104 #define H_SEND_CRQ 0x108 #define H_PUT_RTCE 0x10c #define H_COPY_RDMA 0x110 #define H_REGISTER_LOGICAL_LAN 0x114 #define H_FREE_LOGICAL_LAN 0x118 #define H_ADD_LOGICAL_LAN_BUFFER 0x11c #define H_SEND_LOGICAL_LAN 0x120 #define H_BULK_REMOVE 0x124 #define H_WRITE_RDMA 0x128 #define H_READ_RDMA 0x12c #define H_MULTICAST_CTRL 0x130 #define H_SET_XDABR 0x134 #define H_STUFF_TCE 0x138 #define H_PUT_TCE_INDIRECT 0x13c #define H_PUT_RTCE_INDIRECT 0x140 #define H_CHANGE_LOGICAL_LAN_MAC 0x14c #define H_VTERM_PARTNER_INFO 0x150 #define H_REGISTER_VTERM 0x154 #define H_FREE_VTERM 0x158 /* Reserved .... #define H_RESET_EVENTS 0x15c #define H_ALLOC_RESOURCE 0x160 #define H_FREE_RESOURCE 0x164 #define H_MODIFY_QP 0x168 #define H_QUERY_QP 0x16c #define H_REREGISTER_PMR 0x170 #define H_REGISTER_SMR 0x174 #define H_QUERY_MR 0x178 #define H_QUERY_MW 0x17c #define H_QUERY_HCA 0x180 #define H_QUERY_PORT 0x184 #define H_MODIFY_PORT 0x188 #define H_DEFINE_AQP1 0x18c #define H_GET_TRACE_BUFFER 0x190 #define H_DEFINE_AQP0 0x194 #define H_RESIZE_MR 0x198 #define H_ATTACH_MCQP 0x19c #define H_DETACH_MCQP 0x1a0 #define H_CREATE_RPT 0x1a4 #define H_REMOVE_RPT 0x1a8 #define H_REGISTER_RPAGES 0x1ac #define H_DISABLE_AND_GETC 0x1b0 #define H_ERROR_DATA 0x1b4 #define H_GET_HCA_INFO 0x1b8 #define H_GET_PERF_COUNT 0x1bc #define H_MANAGE_TRACE 0x1c0 .... */ #define H_FREE_LOGICAL_LAN_BUFFER 0x1d4 #define H_POLL_PENDING 0x1d8 /* Reserved .... #define H_QUERY_INT_STATE 0x1e4 .... */ #define H_LIOBN_ATTRIBUTES 0x240 #define H_ILLAN_ATTRIBUTES 0x244 #define H_REMOVE_RTCE 0x24c /* Reserved ... #define H_MODIFY_HEA_QP 0x250 #define H_QUERY_HEA_QP 0x254 #define H_QUERY_HEA 0x258 #define H_QUERY_HEA_PORT 0x25c #define H_MODIFY_HEA_PORT 0x260 #define H_REG_BCMC 0x264 #define H_DEREG_BCMC 0x268 #define H_REGISTER_HEA_RPAGES 0x26c #define H_DISABLE_AND_GET_HEA 0x270 #define H_GET_HEA_INFO 0x274 #define H_ALLOC_HEA_RESOURCE 0x278 #define H_ADD_CONN 0x284 #define H_DEL_CONN 0x288 ... */ #define H_JOIN 0x298 #define H_DONOR_OPERATION 0x29c #define H_VASI_SIGNAL 0x2a0 #define H_VASI_STATE 0x2a4 #define H_VIOCTL 0x2a8 #define H_VRMASD 0x2ac #define H_ENABLE_CRQ 0x2b0 /* Reserved ... #define H_GET_EM_PARMS 0x2b8 ... */ #define H_VPM_STAT 0x2bc #define H_SET_MPP 0x2d0 #define H_GET_MPP 0x2d4 #define MAX_HCALL_OPCODE H_GET_MPP int64_t phyp_hcall(uint64_t opcode, ...); int64_t phyp_pft_hcall(uint64_t opcode, uint64_t flags, uint64_t pteidx, uint64_t pte_hi, uint64_t pte_lo, uint64_t *pteidx_out, uint64_t *ptelo_out, uint64_t *r6); #endif /* _PSERIES_PHYP_HVCALL_H_ */ Index: head/sys/powerpc/pseries/phyp_console.c =================================================================== --- head/sys/powerpc/pseries/phyp_console.c (revision 326260) +++ head/sys/powerpc/pseries/phyp_console.c (revision 326261) @@ -1,433 +1,435 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2011 by Nathan Whitehorn. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "phyp-hvcall.h" #include "uart_if.h" struct uart_phyp_softc { device_t dev; phandle_t node; int vtermid; struct tty *tp; struct resource *irqres; int irqrid; struct callout callout; void *sc_icookie; int polltime; struct mtx sc_mtx; int protocol; union { uint64_t u64[2]; char str[16]; } phyp_inbuf; uint64_t inbuflen; uint8_t outseqno; }; static struct uart_phyp_softc *console_sc = NULL; #if defined(KDB) static int alt_break_state; #endif enum { HVTERM1, HVTERMPROT }; #define VS_DATA_PACKET_HEADER 0xff #define VS_CONTROL_PACKET_HEADER 0xfe #define VSV_SET_MODEM_CTL 0x01 #define VSV_MODEM_CTL_UPDATE 0x02 #define VSV_RENEGOTIATE_CONNECTION 0x03 #define VS_QUERY_PACKET_HEADER 0xfd #define VSV_SEND_VERSION_NUMBER 0x01 #define VSV_SEND_MODEM_CTL_STATUS 0x02 #define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc static int uart_phyp_probe(device_t dev); static int uart_phyp_attach(device_t dev); static void uart_phyp_intr(void *v); static device_method_t uart_phyp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_phyp_probe), DEVMETHOD(device_attach, uart_phyp_attach), DEVMETHOD_END }; static driver_t uart_phyp_driver = { "uart", uart_phyp_methods, sizeof(struct uart_phyp_softc), }; DRIVER_MODULE(uart_phyp, vdevice, uart_phyp_driver, uart_devclass, 0, 0); static cn_probe_t uart_phyp_cnprobe; static cn_init_t uart_phyp_cninit; static cn_term_t uart_phyp_cnterm; static cn_getc_t uart_phyp_cngetc; static cn_putc_t uart_phyp_cnputc; static cn_grab_t uart_phyp_cngrab; static cn_ungrab_t uart_phyp_cnungrab; CONSOLE_DRIVER(uart_phyp); static void uart_phyp_ttyoutwakeup(struct tty *tp); static struct ttydevsw uart_phyp_tty_class = { .tsw_flags = TF_INITLOCK|TF_CALLOUT, .tsw_outwakeup = uart_phyp_ttyoutwakeup, }; static int uart_phyp_probe_node(struct uart_phyp_softc *sc) { phandle_t node = sc->node; uint32_t reg; char buf[64]; sc->inbuflen = 0; sc->outseqno = 0; if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0) return (ENXIO); if (strcmp(buf, "vty") != 0) return (ENXIO); if (OF_getprop(node, "device_type", buf, sizeof(buf)) <= 0) return (ENXIO); if (strcmp(buf, "serial") != 0) return (ENXIO); reg = -1; OF_getencprop(node, "reg", ®, sizeof(reg)); if (reg == -1) return (ENXIO); sc->vtermid = reg; sc->node = node; if (OF_getprop(node, "compatible", buf, sizeof(buf)) <= 0) return (ENXIO); if (strcmp(buf, "hvterm1") == 0) { sc->protocol = HVTERM1; return (0); } else if (strcmp(buf, "hvterm-protocol") == 0) { sc->protocol = HVTERMPROT; return (0); } return (ENXIO); } static int uart_phyp_probe(device_t dev) { const char *name; struct uart_phyp_softc sc; int err; name = ofw_bus_get_name(dev); if (name == NULL || strcmp(name, "vty") != 0) return (ENXIO); sc.node = ofw_bus_get_node(dev); err = uart_phyp_probe_node(&sc); if (err != 0) return (err); device_set_desc(dev, "POWER Hypervisor Virtual Serial Port"); return (err); } static void uart_phyp_cnprobe(struct consdev *cp) { char buf[64]; ihandle_t stdout; phandle_t input, chosen; static struct uart_phyp_softc sc; if ((chosen = OF_finddevice("/chosen")) == -1) goto fail; /* Check if OF has an active stdin/stdout */ input = -1; if (OF_getencprop(chosen, "stdout", &stdout, sizeof(stdout)) == sizeof(stdout) && stdout != 0) input = OF_instance_to_package(stdout); if (input == -1) goto fail; if (OF_getprop(input, "device_type", buf, sizeof(buf)) == -1) goto fail; if (strcmp(buf, "serial") != 0) goto fail; sc.node = input; if (uart_phyp_probe_node(&sc) != 0) goto fail; mtx_init(&sc.sc_mtx, "uart_phyp", NULL, MTX_SPIN | MTX_QUIET | MTX_NOWITNESS); cp->cn_pri = CN_NORMAL; console_sc = ≻ return; fail: cp->cn_pri = CN_DEAD; return; } static int uart_phyp_attach(device_t dev) { struct uart_phyp_softc *sc; int unit; sc = device_get_softc(dev); sc->dev = dev; sc->node = ofw_bus_get_node(dev); uart_phyp_probe_node(sc); unit = device_get_unit(dev); sc->tp = tty_alloc(&uart_phyp_tty_class, sc); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_SPIN | MTX_QUIET | MTX_NOWITNESS); if (console_sc != NULL && console_sc->vtermid == sc->vtermid) { sc->outseqno = console_sc->outseqno; console_sc = sc; sprintf(uart_phyp_consdev.cn_name, "ttyu%r", unit); tty_init_console(sc->tp, 0); } sc->irqrid = 0; sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqrid, RF_ACTIVE | RF_SHAREABLE); if (sc->irqres != NULL) { bus_setup_intr(dev, sc->irqres, INTR_TYPE_TTY | INTR_MPSAFE, NULL, uart_phyp_intr, sc, &sc->sc_icookie); } else { callout_init(&sc->callout, 1); sc->polltime = hz / 20; if (sc->polltime < 1) sc->polltime = 1; callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc); } tty_makedev(sc->tp, NULL, "u%r", unit); return (0); } static void uart_phyp_cninit(struct consdev *cp) { strcpy(cp->cn_name, "phypcons"); } static void uart_phyp_cnterm(struct consdev *cp) { } static int uart_phyp_get(struct uart_phyp_softc *sc, void *buffer, size_t bufsize) { int err; int hdr = 0; uart_lock(&sc->sc_mtx); if (sc->inbuflen == 0) { err = phyp_pft_hcall(H_GET_TERM_CHAR, sc->vtermid, 0, 0, 0, &sc->inbuflen, &sc->phyp_inbuf.u64[0], &sc->phyp_inbuf.u64[1]); if (err != H_SUCCESS) { uart_unlock(&sc->sc_mtx); return (-1); } hdr = 1; } if (sc->inbuflen == 0) { uart_unlock(&sc->sc_mtx); return (0); } if (bufsize > sc->inbuflen) bufsize = sc->inbuflen; if ((sc->protocol == HVTERMPROT) && (hdr == 1)) { sc->inbuflen = sc->inbuflen - 4; /* The VTERM protocol has a 4 byte header, skip it here. */ memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[4], sc->inbuflen); } memcpy(buffer, sc->phyp_inbuf.str, bufsize); sc->inbuflen -= bufsize; if (sc->inbuflen > 0) memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[bufsize], sc->inbuflen); uart_unlock(&sc->sc_mtx); return (bufsize); } static int uart_phyp_put(struct uart_phyp_softc *sc, void *buffer, size_t bufsize) { uint16_t seqno; uint64_t len = 0; int err; union { uint64_t u64[2]; char bytes[16]; } cbuf; uart_lock(&sc->sc_mtx); switch (sc->protocol) { case HVTERM1: if (bufsize > 16) bufsize = 16; memcpy(&cbuf, buffer, bufsize); len = bufsize; break; case HVTERMPROT: if (bufsize > 12) bufsize = 12; seqno = sc->outseqno++; cbuf.bytes[0] = VS_DATA_PACKET_HEADER; cbuf.bytes[1] = 4 + bufsize; /* total length, max 16 bytes */ cbuf.bytes[2] = (seqno >> 8) & 0xff; cbuf.bytes[3] = seqno & 0xff; memcpy(&cbuf.bytes[4], buffer, bufsize); len = 4 + bufsize; break; } do { err = phyp_hcall(H_PUT_TERM_CHAR, sc->vtermid, len, cbuf.u64[0], cbuf.u64[1]); DELAY(100); } while (err == H_BUSY); uart_unlock(&sc->sc_mtx); return (bufsize); } static int uart_phyp_cngetc(struct consdev *cp) { unsigned char c; int retval; retval = uart_phyp_get(console_sc, &c, 1); if (retval != 1) return (-1); #if defined(KDB) kdb_alt_break(c, &alt_break_state); #endif return (c); } static void uart_phyp_cnputc(struct consdev *cp, int c) { unsigned char ch = c; uart_phyp_put(console_sc, &ch, 1); } static void uart_phyp_cngrab(struct consdev *cp) { } static void uart_phyp_cnungrab(struct consdev *cp) { } static void uart_phyp_ttyoutwakeup(struct tty *tp) { struct uart_phyp_softc *sc; char buffer[8]; int len; sc = tty_softc(tp); while ((len = ttydisc_getc(tp, buffer, sizeof(buffer))) != 0) uart_phyp_put(sc, buffer, len); } static void uart_phyp_intr(void *v) { struct uart_phyp_softc *sc = v; struct tty *tp = sc->tp; unsigned char c; int len; tty_lock(tp); while ((len = uart_phyp_get(sc, &c, 1)) > 0) ttydisc_rint(tp, c, 0); ttydisc_rint_done(tp); tty_unlock(tp); if (sc->irqres == NULL) callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc); } Index: head/sys/powerpc/pseries/phyp_llan.c =================================================================== --- head/sys/powerpc/pseries/phyp_llan.c (revision 326260) +++ head/sys/powerpc/pseries/phyp_llan.c (revision 326261) @@ -1,550 +1,552 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 2013 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LLAN_MAX_RX_PACKETS 100 #define LLAN_MAX_TX_PACKETS 100 #define LLAN_RX_BUF_LEN 8*PAGE_SIZE #define LLAN_BUFDESC_VALID (1ULL << 63) #define LLAN_ADD_MULTICAST 0x1 #define LLAN_DEL_MULTICAST 0x2 #define LLAN_CLEAR_MULTICAST 0x3 struct llan_xfer { struct mbuf *rx_mbuf; bus_dmamap_t rx_dmamap; uint64_t rx_bufdesc; }; struct llan_receive_queue_entry { /* PAPR page 539 */ uint8_t control; uint8_t reserved; uint16_t offset; uint32_t length; uint64_t handle; } __packed; struct llan_softc { device_t dev; struct mtx io_lock; cell_t unit; uint8_t mac_address[8]; struct ifmedia media; int irqid; struct resource *irq; void *irq_cookie; bus_dma_tag_t rx_dma_tag; bus_dma_tag_t rxbuf_dma_tag; bus_dma_tag_t tx_dma_tag; bus_dmamap_t tx_dma_map; struct llan_receive_queue_entry *rx_buf; int rx_dma_slot; int rx_valid_val; bus_dmamap_t rx_buf_map; bus_addr_t rx_buf_phys; bus_size_t rx_buf_len; bus_addr_t input_buf_phys; bus_addr_t filter_buf_phys; struct llan_xfer rx_xfer[LLAN_MAX_RX_PACKETS]; struct ifnet *ifp; }; static int llan_probe(device_t); static int llan_attach(device_t); static void llan_intr(void *xsc); static void llan_init(void *xsc); static void llan_start(struct ifnet *ifp); static int llan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); static void llan_media_status(struct ifnet *ifp, struct ifmediareq *ifmr); static int llan_media_change(struct ifnet *ifp); static void llan_rx_load_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int err); static int llan_add_rxbuf(struct llan_softc *sc, struct llan_xfer *rx); static int llan_set_multicast(struct llan_softc *sc); static devclass_t llan_devclass; static device_method_t llan_methods[] = { DEVMETHOD(device_probe, llan_probe), DEVMETHOD(device_attach, llan_attach), DEVMETHOD_END }; static driver_t llan_driver = { "llan", llan_methods, sizeof(struct llan_softc) }; DRIVER_MODULE(llan, vdevice, llan_driver, llan_devclass, 0, 0); static int llan_probe(device_t dev) { if (!ofw_bus_is_compatible(dev,"IBM,l-lan")) return (ENXIO); device_set_desc(dev, "POWER Hypervisor Virtual Ethernet"); return (0); } static int llan_attach(device_t dev) { struct llan_softc *sc; phandle_t node; int error, i; sc = device_get_softc(dev); sc->dev = dev; /* Get firmware properties */ node = ofw_bus_get_node(dev); OF_getprop(node, "local-mac-address", sc->mac_address, sizeof(sc->mac_address)); OF_getencprop(node, "reg", &sc->unit, sizeof(sc->unit)); mtx_init(&sc->io_lock, "llan", NULL, MTX_DEF); /* Setup interrupt */ sc->irqid = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, RF_ACTIVE); if (!sc->irq) { device_printf(dev, "Could not allocate IRQ\n"); mtx_destroy(&sc->io_lock); return (ENXIO); } bus_setup_intr(dev, sc->irq, INTR_TYPE_MISC | INTR_MPSAFE | INTR_ENTROPY, NULL, llan_intr, sc, &sc->irq_cookie); /* Setup DMA */ error = bus_dma_tag_create(bus_get_dma_tag(dev), 16, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, LLAN_RX_BUF_LEN, 1, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &sc->rx_dma_tag); error = bus_dma_tag_create(bus_get_dma_tag(dev), 4, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE, 1, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &sc->rxbuf_dma_tag); error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE, 6, BUS_SPACE_MAXSIZE_32BIT, 0, busdma_lock_mutex, &sc->io_lock, &sc->tx_dma_tag); error = bus_dmamem_alloc(sc->rx_dma_tag, (void **)&sc->rx_buf, BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->rx_buf_map); error = bus_dmamap_load(sc->rx_dma_tag, sc->rx_buf_map, sc->rx_buf, LLAN_RX_BUF_LEN, llan_rx_load_cb, sc, 0); /* TX DMA maps */ bus_dmamap_create(sc->tx_dma_tag, 0, &sc->tx_dma_map); /* RX DMA */ for (i = 0; i < LLAN_MAX_RX_PACKETS; i++) { error = bus_dmamap_create(sc->rxbuf_dma_tag, 0, &sc->rx_xfer[i].rx_dmamap); sc->rx_xfer[i].rx_mbuf = NULL; } /* Attach to network stack */ sc->ifp = if_alloc(IFT_ETHER); sc->ifp->if_softc = sc; if_initname(sc->ifp, device_get_name(dev), device_get_unit(dev)); sc->ifp->if_mtu = ETHERMTU; /* XXX max-frame-size from OF? */ sc->ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; sc->ifp->if_hwassist = 0; /* XXX: ibm,illan-options */ sc->ifp->if_capabilities = 0; sc->ifp->if_capenable = 0; sc->ifp->if_start = llan_start; sc->ifp->if_ioctl = llan_ioctl; sc->ifp->if_init = llan_init; ifmedia_init(&sc->media, IFM_IMASK, llan_media_change, llan_media_status); ifmedia_add(&sc->media, IFM_ETHER | IFM_AUTO, 0, NULL); ifmedia_set(&sc->media, IFM_ETHER | IFM_AUTO); IFQ_SET_MAXLEN(&sc->ifp->if_snd, LLAN_MAX_TX_PACKETS); sc->ifp->if_snd.ifq_drv_maxlen = LLAN_MAX_TX_PACKETS; IFQ_SET_READY(&sc->ifp->if_snd); ether_ifattach(sc->ifp, &sc->mac_address[2]); /* We don't have link state reporting, so make it always up */ if_link_state_change(sc->ifp, LINK_STATE_UP); return (0); } static int llan_media_change(struct ifnet *ifp) { struct llan_softc *sc = ifp->if_softc; if (IFM_TYPE(sc->media.ifm_media) != IFM_ETHER) return (EINVAL); if (IFM_SUBTYPE(sc->media.ifm_media) != IFM_AUTO) return (EINVAL); return (0); } static void llan_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) { ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE | IFM_UNKNOWN | IFM_FDX; ifmr->ifm_active = IFM_ETHER; } static void llan_rx_load_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int err) { struct llan_softc *sc = xsc; sc->rx_buf_phys = segs[0].ds_addr; sc->rx_buf_len = segs[0].ds_len - 2*PAGE_SIZE; sc->input_buf_phys = segs[0].ds_addr + segs[0].ds_len - PAGE_SIZE; sc->filter_buf_phys = segs[0].ds_addr + segs[0].ds_len - 2*PAGE_SIZE; } static void llan_init(void *xsc) { struct llan_softc *sc = xsc; uint64_t rx_buf_desc; uint64_t macaddr; int err, i; mtx_lock(&sc->io_lock); phyp_hcall(H_FREE_LOGICAL_LAN, sc->unit); /* Create buffers (page 539) */ sc->rx_dma_slot = 0; sc->rx_valid_val = 1; rx_buf_desc = LLAN_BUFDESC_VALID; rx_buf_desc |= (sc->rx_buf_len << 32); rx_buf_desc |= sc->rx_buf_phys; memcpy(&macaddr, sc->mac_address, 8); err = phyp_hcall(H_REGISTER_LOGICAL_LAN, sc->unit, sc->input_buf_phys, rx_buf_desc, sc->filter_buf_phys, macaddr); for (i = 0; i < LLAN_MAX_RX_PACKETS; i++) llan_add_rxbuf(sc, &sc->rx_xfer[i]); phyp_hcall(H_VIO_SIGNAL, sc->unit, 1); /* Enable interrupts */ /* Tell stack we're up */ sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; mtx_unlock(&sc->io_lock); /* Check for pending receives scheduled before interrupt enable */ llan_intr(sc); } static int llan_add_rxbuf(struct llan_softc *sc, struct llan_xfer *rx) { struct mbuf *m; bus_dma_segment_t segs[1]; int error, nsegs; mtx_assert(&sc->io_lock, MA_OWNED); m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return (ENOBUFS); m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; if (rx->rx_mbuf != NULL) { bus_dmamap_sync(sc->rxbuf_dma_tag, rx->rx_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rxbuf_dma_tag, rx->rx_dmamap); } /* Save pointer to buffer structure */ m_copyback(m, 0, 8, (void *)&rx); error = bus_dmamap_load_mbuf_sg(sc->rxbuf_dma_tag, rx->rx_dmamap, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->dev, "cannot load RX DMA map %p, error = %d\n", rx, error); m_freem(m); return (error); } /* If nsegs is wrong then the stack is corrupt. */ KASSERT(nsegs == 1, ("%s: too many DMA segments (%d)", __func__, nsegs)); rx->rx_mbuf = m; bus_dmamap_sync(sc->rxbuf_dma_tag, rx->rx_dmamap, BUS_DMASYNC_PREREAD); rx->rx_bufdesc = LLAN_BUFDESC_VALID; rx->rx_bufdesc |= (((uint64_t)segs[0].ds_len) << 32); rx->rx_bufdesc |= segs[0].ds_addr; error = phyp_hcall(H_ADD_LOGICAL_LAN_BUFFER, sc->unit, rx->rx_bufdesc); if (error != 0) { m_freem(m); rx->rx_mbuf = NULL; return (ENOBUFS); } return (0); } static void llan_intr(void *xsc) { struct llan_softc *sc = xsc; struct llan_xfer *rx; struct mbuf *m; mtx_lock(&sc->io_lock); restart: phyp_hcall(H_VIO_SIGNAL, sc->unit, 0); while ((sc->rx_buf[sc->rx_dma_slot].control >> 7) == sc->rx_valid_val) { rx = (struct llan_xfer *)sc->rx_buf[sc->rx_dma_slot].handle; m = rx->rx_mbuf; m_adj(m, sc->rx_buf[sc->rx_dma_slot].offset - 8); m->m_len = sc->rx_buf[sc->rx_dma_slot].length; /* llan_add_rxbuf does DMA sync and unload as well as requeue */ if (llan_add_rxbuf(sc, rx) != 0) { if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1); phyp_hcall(H_ADD_LOGICAL_LAN_BUFFER, sc->unit, rx->rx_bufdesc); continue; } if_inc_counter(sc->ifp, IFCOUNTER_IPACKETS, 1); m_adj(m, sc->rx_buf[sc->rx_dma_slot].offset); m->m_len = sc->rx_buf[sc->rx_dma_slot].length; m->m_pkthdr.rcvif = sc->ifp; m->m_pkthdr.len = m->m_len; sc->rx_dma_slot++; if (sc->rx_dma_slot >= sc->rx_buf_len/sizeof(sc->rx_buf[0])) { sc->rx_dma_slot = 0; sc->rx_valid_val = !sc->rx_valid_val; } mtx_unlock(&sc->io_lock); (*sc->ifp->if_input)(sc->ifp, m); mtx_lock(&sc->io_lock); } phyp_hcall(H_VIO_SIGNAL, sc->unit, 1); /* * H_VIO_SIGNAL enables interrupts for future packets only. * Make sure none were queued between the end of the loop and the * enable interrupts call. */ if ((sc->rx_buf[sc->rx_dma_slot].control >> 7) == sc->rx_valid_val) goto restart; mtx_unlock(&sc->io_lock); } static void llan_send_packet(void *xsc, bus_dma_segment_t *segs, int nsegs, bus_size_t mapsize, int error) { struct llan_softc *sc = xsc; uint64_t bufdescs[6]; int i; bzero(bufdescs, sizeof(bufdescs)); for (i = 0; i < nsegs; i++) { bufdescs[i] = LLAN_BUFDESC_VALID; bufdescs[i] |= (((uint64_t)segs[i].ds_len) << 32); bufdescs[i] |= segs[i].ds_addr; } phyp_hcall(H_SEND_LOGICAL_LAN, sc->unit, bufdescs[0], bufdescs[1], bufdescs[2], bufdescs[3], bufdescs[4], bufdescs[5], 0); /* * The hypercall returning implies completion -- or that the call will * not complete. In principle, we should try a few times if we get back * H_BUSY based on the continuation token in R4. For now, just drop * the packet in such cases. */ } static void llan_start_locked(struct ifnet *ifp) { struct llan_softc *sc = ifp->if_softc; bus_addr_t first; int nsegs; struct mbuf *mb_head, *m; mtx_assert(&sc->io_lock, MA_OWNED); first = 0; if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) return; while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { IFQ_DRV_DEQUEUE(&ifp->if_snd, mb_head); if (mb_head == NULL) break; BPF_MTAP(ifp, mb_head); for (m = mb_head, nsegs = 0; m != NULL; m = m->m_next) nsegs++; if (nsegs > 6) { m = m_collapse(mb_head, M_NOWAIT, 6); if (m == NULL) { m_freem(mb_head); continue; } } bus_dmamap_load_mbuf(sc->tx_dma_tag, sc->tx_dma_map, mb_head, llan_send_packet, sc, 0); bus_dmamap_unload(sc->tx_dma_tag, sc->tx_dma_map); m_freem(mb_head); } } static void llan_start(struct ifnet *ifp) { struct llan_softc *sc = ifp->if_softc; mtx_lock(&sc->io_lock); llan_start_locked(ifp); mtx_unlock(&sc->io_lock); } static int llan_set_multicast(struct llan_softc *sc) { struct ifnet *ifp = sc->ifp; struct ifmultiaddr *inm; uint64_t macaddr; mtx_assert(&sc->io_lock, MA_OWNED); phyp_hcall(H_MULTICAST_CTRL, sc->unit, LLAN_CLEAR_MULTICAST, 0); if_maddr_rlock(ifp); TAILQ_FOREACH(inm, &ifp->if_multiaddrs, ifma_link) { if (inm->ifma_addr->sa_family != AF_LINK) continue; memcpy((uint8_t *)&macaddr + 2, LLADDR((struct sockaddr_dl *)inm->ifma_addr), 6); phyp_hcall(H_MULTICAST_CTRL, sc->unit, LLAN_ADD_MULTICAST, macaddr); } if_maddr_runlock(ifp); return (0); } static int llan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { int err = 0; struct llan_softc *sc = ifp->if_softc; switch (cmd) { case SIOCADDMULTI: case SIOCDELMULTI: mtx_lock(&sc->io_lock); if ((sc->ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) llan_set_multicast(sc); mtx_unlock(&sc->io_lock); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: err = ifmedia_ioctl(ifp, (struct ifreq *)data, &sc->media, cmd); break; case SIOCSIFFLAGS: default: err = ether_ioctl(ifp, cmd, data); break; } return (err); } Index: head/sys/powerpc/pseries/phyp_vscsi.c =================================================================== --- head/sys/powerpc/pseries/phyp_vscsi.c (revision 326260) +++ head/sys/powerpc/pseries/phyp_vscsi.c (revision 326261) @@ -1,993 +1,995 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 2013 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct vscsi_softc; /* VSCSI CRQ format from table 260 of PAPR spec 2.4 (page 760) */ struct vscsi_crq { uint8_t valid; uint8_t format; uint8_t reserved; uint8_t status; uint16_t timeout; uint16_t iu_length; uint64_t iu_data; }; struct vscsi_xfer { TAILQ_ENTRY(vscsi_xfer) queue; struct vscsi_softc *sc; union ccb *ccb; bus_dmamap_t dmamap; uint64_t tag; vmem_addr_t srp_iu_offset; vmem_size_t srp_iu_size; }; TAILQ_HEAD(vscsi_xferq, vscsi_xfer); struct vscsi_softc { device_t dev; struct cam_devq *devq; struct cam_sim *sim; struct cam_path *path; struct mtx io_lock; cell_t unit; int bus_initialized; int bus_logged_in; int max_transactions; int irqid; struct resource *irq; void *irq_cookie; bus_dma_tag_t crq_tag; struct vscsi_crq *crq_queue; int n_crqs, cur_crq; bus_dmamap_t crq_map; bus_addr_t crq_phys; vmem_t *srp_iu_arena; void *srp_iu_queue; bus_addr_t srp_iu_phys; bus_dma_tag_t data_tag; struct vscsi_xfer loginxp; struct vscsi_xfer *xfer; struct vscsi_xferq active_xferq; struct vscsi_xferq free_xferq; }; struct srp_login { uint8_t type; uint8_t reserved[7]; uint64_t tag; uint64_t max_cmd_length; uint32_t reserved2; uint16_t buffer_formats; uint8_t flags; uint8_t reserved3[5]; uint8_t initiator_port_id[16]; uint8_t target_port_id[16]; } __packed; struct srp_login_rsp { uint8_t type; uint8_t reserved[3]; uint32_t request_limit_delta; uint8_t tag; uint32_t max_i_to_t_len; uint32_t max_t_to_i_len; uint16_t buffer_formats; uint8_t flags; /* Some reserved bits follow */ } __packed; struct srp_cmd { uint8_t type; uint8_t flags1; uint8_t reserved[3]; uint8_t formats; uint8_t out_buffer_count; uint8_t in_buffer_count; uint64_t tag; uint32_t reserved2; uint64_t lun; uint8_t reserved3[3]; uint8_t additional_cdb; uint8_t cdb[16]; uint8_t data_payload[0]; } __packed; struct srp_rsp { uint8_t type; uint8_t reserved[3]; uint32_t request_limit_delta; uint64_t tag; uint16_t reserved2; uint8_t flags; uint8_t status; uint32_t data_out_resid; uint32_t data_in_resid; uint32_t sense_data_len; uint32_t response_data_len; uint8_t data_payload[0]; } __packed; struct srp_tsk_mgmt { uint8_t type; uint8_t reserved[7]; uint64_t tag; uint32_t reserved2; uint64_t lun; uint8_t reserved3[2]; uint8_t function; uint8_t reserved4; uint64_t manage_tag; uint64_t reserved5; } __packed; /* Message code type */ #define SRP_LOGIN_REQ 0x00 #define SRP_TSK_MGMT 0x01 #define SRP_CMD 0x02 #define SRP_I_LOGOUT 0x03 #define SRP_LOGIN_RSP 0xC0 #define SRP_RSP 0xC1 #define SRP_LOGIN_REJ 0xC2 #define SRP_T_LOGOUT 0x80 #define SRP_CRED_REQ 0x81 #define SRP_AER_REQ 0x82 #define SRP_CRED_RSP 0x41 #define SRP_AER_RSP 0x41 /* Flags for srp_rsp flags field */ #define SRP_RSPVALID 0x01 #define SRP_SNSVALID 0x02 #define SRP_DOOVER 0x04 #define SRP_DOUNDER 0x08 #define SRP_DIOVER 0x10 #define SRP_DIUNDER 0x20 #define MAD_SUCESS 0x00 #define MAD_NOT_SUPPORTED 0xf1 #define MAD_FAILED 0xf7 #define MAD_EMPTY_IU 0x01 #define MAD_ERROR_LOGGING_REQUEST 0x02 #define MAD_ADAPTER_INFO_REQUEST 0x03 #define MAD_CAPABILITIES_EXCHANGE 0x05 #define MAD_PHYS_ADAP_INFO_REQUEST 0x06 #define MAD_TAPE_PASSTHROUGH_REQUEST 0x07 #define MAD_ENABLE_FAST_FAIL 0x08 static int vscsi_probe(device_t); static int vscsi_attach(device_t); static int vscsi_detach(device_t); static void vscsi_cam_action(struct cam_sim *, union ccb *); static void vscsi_cam_poll(struct cam_sim *); static void vscsi_intr(void *arg); static void vscsi_check_response_queue(struct vscsi_softc *sc); static void vscsi_setup_bus(struct vscsi_softc *sc); static void vscsi_srp_login(struct vscsi_softc *sc); static void vscsi_crq_load_cb(void *, bus_dma_segment_t *, int, int); static void vscsi_scsi_command(void *xxp, bus_dma_segment_t *segs, int nsegs, int err); static void vscsi_task_management(struct vscsi_softc *sc, union ccb *ccb); static void vscsi_srp_response(struct vscsi_xfer *, struct vscsi_crq *); static devclass_t vscsi_devclass; static device_method_t vscsi_methods[] = { DEVMETHOD(device_probe, vscsi_probe), DEVMETHOD(device_attach, vscsi_attach), DEVMETHOD(device_detach, vscsi_detach), DEVMETHOD_END }; static driver_t vscsi_driver = { "vscsi", vscsi_methods, sizeof(struct vscsi_softc) }; DRIVER_MODULE(vscsi, vdevice, vscsi_driver, vscsi_devclass, 0, 0); MALLOC_DEFINE(M_VSCSI, "vscsi", "CAM device queue for VSCSI"); static int vscsi_probe(device_t dev) { if (!ofw_bus_is_compatible(dev, "IBM,v-scsi")) return (ENXIO); device_set_desc(dev, "POWER Hypervisor Virtual SCSI Bus"); return (0); } static int vscsi_attach(device_t dev) { struct vscsi_softc *sc; struct vscsi_xfer *xp; int error, i; sc = device_get_softc(dev); if (sc == NULL) return (EINVAL); sc->dev = dev; mtx_init(&sc->io_lock, "vscsi", NULL, MTX_DEF); /* Get properties */ OF_getencprop(ofw_bus_get_node(dev), "reg", &sc->unit, sizeof(sc->unit)); /* Setup interrupt */ sc->irqid = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, RF_ACTIVE); if (!sc->irq) { device_printf(dev, "Could not allocate IRQ\n"); mtx_destroy(&sc->io_lock); return (ENXIO); } bus_setup_intr(dev, sc->irq, INTR_TYPE_CAM | INTR_MPSAFE | INTR_ENTROPY, NULL, vscsi_intr, sc, &sc->irq_cookie); /* Data DMA */ error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE, 256, BUS_SPACE_MAXSIZE_32BIT, 0, busdma_lock_mutex, &sc->io_lock, &sc->data_tag); TAILQ_INIT(&sc->active_xferq); TAILQ_INIT(&sc->free_xferq); /* First XFER for login data */ sc->loginxp.sc = sc; bus_dmamap_create(sc->data_tag, 0, &sc->loginxp.dmamap); TAILQ_INSERT_TAIL(&sc->free_xferq, &sc->loginxp, queue); /* CRQ area */ error = bus_dma_tag_create(bus_get_dma_tag(dev), PAGE_SIZE, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, 8*PAGE_SIZE, 1, BUS_SPACE_MAXSIZE, 0, NULL, NULL, &sc->crq_tag); error = bus_dmamem_alloc(sc->crq_tag, (void **)&sc->crq_queue, BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->crq_map); sc->crq_phys = 0; sc->n_crqs = 0; error = bus_dmamap_load(sc->crq_tag, sc->crq_map, sc->crq_queue, 8*PAGE_SIZE, vscsi_crq_load_cb, sc, 0); mtx_lock(&sc->io_lock); vscsi_setup_bus(sc); sc->xfer = malloc(sizeof(sc->xfer[0])*sc->max_transactions, M_VSCSI, M_NOWAIT); for (i = 0; i < sc->max_transactions; i++) { xp = &sc->xfer[i]; xp->sc = sc; error = bus_dmamap_create(sc->data_tag, 0, &xp->dmamap); if (error) { device_printf(dev, "Could not create DMA map (%d)\n", error); break; } TAILQ_INSERT_TAIL(&sc->free_xferq, xp, queue); } mtx_unlock(&sc->io_lock); /* Allocate CAM bits */ if ((sc->devq = cam_simq_alloc(sc->max_transactions)) == NULL) return (ENOMEM); sc->sim = cam_sim_alloc(vscsi_cam_action, vscsi_cam_poll, "vscsi", sc, device_get_unit(dev), &sc->io_lock, sc->max_transactions, sc->max_transactions, sc->devq); if (sc->sim == NULL) { cam_simq_free(sc->devq); sc->devq = NULL; device_printf(dev, "CAM SIM attach failed\n"); return (EINVAL); } mtx_lock(&sc->io_lock); if (xpt_bus_register(sc->sim, dev, 0) != 0) { device_printf(dev, "XPT bus registration failed\n"); cam_sim_free(sc->sim, FALSE); sc->sim = NULL; cam_simq_free(sc->devq); sc->devq = NULL; mtx_unlock(&sc->io_lock); return (EINVAL); } mtx_unlock(&sc->io_lock); return (0); } static int vscsi_detach(device_t dev) { struct vscsi_softc *sc; sc = device_get_softc(dev); if (sc == NULL) return (EINVAL); if (sc->sim != NULL) { mtx_lock(&sc->io_lock); xpt_bus_deregister(cam_sim_path(sc->sim)); cam_sim_free(sc->sim, FALSE); sc->sim = NULL; mtx_unlock(&sc->io_lock); } if (sc->devq != NULL) { cam_simq_free(sc->devq); sc->devq = NULL; } mtx_destroy(&sc->io_lock); return (0); } static void vscsi_cam_action(struct cam_sim *sim, union ccb *ccb) { struct vscsi_softc *sc = cam_sim_softc(sim); mtx_assert(&sc->io_lock, MA_OWNED); switch (ccb->ccb_h.func_code) { case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; cpi->hba_inquiry = PI_TAG_ABLE; cpi->hba_misc = PIM_EXTLUNS; cpi->target_sprt = 0; cpi->hba_eng_cnt = 0; cpi->max_target = 0; cpi->max_lun = 0; cpi->initiator_id = ~0; strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strlcpy(cpi->hba_vid, "IBM", HBA_IDLEN); strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 150000; cpi->transport = XPORT_SRP; cpi->transport_version = 0; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_SPC4; cpi->ccb_h.status = CAM_REQ_CMP; break; } case XPT_RESET_BUS: ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_RESET_DEV: ccb->ccb_h.status = CAM_REQ_INPROG; vscsi_task_management(sc, ccb); return; case XPT_GET_TRAN_SETTINGS: ccb->cts.protocol = PROTO_SCSI; ccb->cts.protocol_version = SCSI_REV_SPC4; ccb->cts.transport = XPORT_SRP; ccb->cts.transport_version = 0; ccb->cts.proto_specific.valid = 0; ccb->cts.xport_specific.valid = 0; ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_SET_TRAN_SETTINGS: ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; break; case XPT_SCSI_IO: { struct vscsi_xfer *xp; ccb->ccb_h.status = CAM_REQ_INPROG; xp = TAILQ_FIRST(&sc->free_xferq); if (xp == NULL) panic("SCSI queue flooded"); xp->ccb = ccb; TAILQ_REMOVE(&sc->free_xferq, xp, queue); TAILQ_INSERT_TAIL(&sc->active_xferq, xp, queue); bus_dmamap_load_ccb(sc->data_tag, xp->dmamap, ccb, vscsi_scsi_command, xp, 0); return; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); return; } static void vscsi_srp_login(struct vscsi_softc *sc) { struct vscsi_xfer *xp; struct srp_login *login; struct vscsi_crq crq; int err; mtx_assert(&sc->io_lock, MA_OWNED); xp = TAILQ_FIRST(&sc->free_xferq); if (xp == NULL) panic("SCSI queue flooded"); xp->ccb = NULL; TAILQ_REMOVE(&sc->free_xferq, xp, queue); TAILQ_INSERT_TAIL(&sc->active_xferq, xp, queue); /* Set up command */ xp->srp_iu_size = crq.iu_length = 64; err = vmem_alloc(xp->sc->srp_iu_arena, xp->srp_iu_size, M_BESTFIT | M_NOWAIT, &xp->srp_iu_offset); if (err) panic("Error during VMEM allocation (%d)", err); login = (struct srp_login *)((uint8_t *)xp->sc->srp_iu_queue + (uintptr_t)xp->srp_iu_offset); bzero(login, xp->srp_iu_size); login->type = SRP_LOGIN_REQ; login->tag = (uint64_t)(xp); login->max_cmd_length = htobe64(256); login->buffer_formats = htobe16(0x1 | 0x2); /* Direct and indirect */ login->flags = 0; /* Create CRQ entry */ crq.valid = 0x80; crq.format = 0x01; crq.iu_data = xp->sc->srp_iu_phys + xp->srp_iu_offset; bus_dmamap_sync(sc->crq_tag, sc->crq_map, BUS_DMASYNC_PREWRITE); err = phyp_hcall(H_SEND_CRQ, xp->sc->unit, ((uint64_t *)(&crq))[0], ((uint64_t *)(&crq))[1]); if (err != 0) panic("CRQ send failure (%d)", err); } static void vscsi_task_management(struct vscsi_softc *sc, union ccb *ccb) { struct srp_tsk_mgmt *cmd; struct vscsi_xfer *xp; struct vscsi_crq crq; int err; mtx_assert(&sc->io_lock, MA_OWNED); xp = TAILQ_FIRST(&sc->free_xferq); if (xp == NULL) panic("SCSI queue flooded"); xp->ccb = ccb; TAILQ_REMOVE(&sc->free_xferq, xp, queue); TAILQ_INSERT_TAIL(&sc->active_xferq, xp, queue); xp->srp_iu_size = crq.iu_length = sizeof(*cmd); err = vmem_alloc(xp->sc->srp_iu_arena, xp->srp_iu_size, M_BESTFIT | M_NOWAIT, &xp->srp_iu_offset); if (err) panic("Error during VMEM allocation (%d)", err); cmd = (struct srp_tsk_mgmt *)((uint8_t *)xp->sc->srp_iu_queue + (uintptr_t)xp->srp_iu_offset); bzero(cmd, xp->srp_iu_size); cmd->type = SRP_TSK_MGMT; cmd->tag = (uint64_t)xp; cmd->lun = htobe64(CAM_EXTLUN_BYTE_SWIZZLE(ccb->ccb_h.target_lun)); switch (ccb->ccb_h.func_code) { case XPT_RESET_DEV: cmd->function = 0x08; break; default: panic("Unimplemented code %d", ccb->ccb_h.func_code); break; } bus_dmamap_sync(xp->sc->crq_tag, xp->sc->crq_map, BUS_DMASYNC_PREWRITE); /* Create CRQ entry */ crq.valid = 0x80; crq.format = 0x01; crq.iu_data = xp->sc->srp_iu_phys + xp->srp_iu_offset; err = phyp_hcall(H_SEND_CRQ, xp->sc->unit, ((uint64_t *)(&crq))[0], ((uint64_t *)(&crq))[1]); if (err != 0) panic("CRQ send failure (%d)", err); } static void vscsi_scsi_command(void *xxp, bus_dma_segment_t *segs, int nsegs, int err) { struct vscsi_xfer *xp = xxp; uint8_t *cdb; union ccb *ccb = xp->ccb; struct srp_cmd *cmd; uint64_t chunk_addr; uint32_t chunk_size; int desc_start, i; struct vscsi_crq crq; KASSERT(err == 0, ("DMA error %d\n", err)); mtx_assert(&xp->sc->io_lock, MA_OWNED); cdb = (ccb->ccb_h.flags & CAM_CDB_POINTER) ? ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes; /* Command format from Table 20, page 37 of SRP spec */ crq.iu_length = 48 + ((nsegs > 1) ? 20 : 16) + ((ccb->csio.cdb_len > 16) ? (ccb->csio.cdb_len - 16) : 0); xp->srp_iu_size = crq.iu_length; if (nsegs > 1) xp->srp_iu_size += nsegs*16; xp->srp_iu_size = roundup(xp->srp_iu_size, 16); err = vmem_alloc(xp->sc->srp_iu_arena, xp->srp_iu_size, M_BESTFIT | M_NOWAIT, &xp->srp_iu_offset); if (err) panic("Error during VMEM allocation (%d)", err); cmd = (struct srp_cmd *)((uint8_t *)xp->sc->srp_iu_queue + (uintptr_t)xp->srp_iu_offset); bzero(cmd, xp->srp_iu_size); cmd->type = SRP_CMD; if (ccb->csio.cdb_len > 16) cmd->additional_cdb = (ccb->csio.cdb_len - 16) << 2; memcpy(cmd->cdb, cdb, ccb->csio.cdb_len); cmd->tag = (uint64_t)(xp); /* Let the responder find this again */ cmd->lun = htobe64(CAM_EXTLUN_BYTE_SWIZZLE(ccb->ccb_h.target_lun)); if (nsegs > 1) { /* Use indirect descriptors */ switch (ccb->ccb_h.flags & CAM_DIR_MASK) { case CAM_DIR_OUT: cmd->formats = (2 << 4); break; case CAM_DIR_IN: cmd->formats = 2; break; default: panic("Does not support bidirectional commands (%d)", ccb->ccb_h.flags & CAM_DIR_MASK); break; } desc_start = ((ccb->csio.cdb_len > 16) ? ccb->csio.cdb_len - 16 : 0); chunk_addr = xp->sc->srp_iu_phys + xp->srp_iu_offset + 20 + desc_start + sizeof(*cmd); chunk_size = 16*nsegs; memcpy(&cmd->data_payload[desc_start], &chunk_addr, 8); memcpy(&cmd->data_payload[desc_start+12], &chunk_size, 4); chunk_size = 0; for (i = 0; i < nsegs; i++) chunk_size += segs[i].ds_len; memcpy(&cmd->data_payload[desc_start+16], &chunk_size, 4); desc_start += 20; for (i = 0; i < nsegs; i++) { chunk_addr = segs[i].ds_addr; chunk_size = segs[i].ds_len; memcpy(&cmd->data_payload[desc_start + 16*i], &chunk_addr, 8); /* Set handle tag to 0 */ memcpy(&cmd->data_payload[desc_start + 16*i + 12], &chunk_size, 4); } } else if (nsegs == 1) { switch (ccb->ccb_h.flags & CAM_DIR_MASK) { case CAM_DIR_OUT: cmd->formats = (1 << 4); break; case CAM_DIR_IN: cmd->formats = 1; break; default: panic("Does not support bidirectional commands (%d)", ccb->ccb_h.flags & CAM_DIR_MASK); break; } /* * Memory descriptor: * 8 byte address * 4 byte handle * 4 byte length */ chunk_addr = segs[0].ds_addr; chunk_size = segs[0].ds_len; desc_start = ((ccb->csio.cdb_len > 16) ? ccb->csio.cdb_len - 16 : 0); memcpy(&cmd->data_payload[desc_start], &chunk_addr, 8); /* Set handle tag to 0 */ memcpy(&cmd->data_payload[desc_start+12], &chunk_size, 4); KASSERT(xp->srp_iu_size >= 48 + ((ccb->csio.cdb_len > 16) ? ccb->csio.cdb_len : 16), ("SRP IU command length")); } else { cmd->formats = 0; } bus_dmamap_sync(xp->sc->crq_tag, xp->sc->crq_map, BUS_DMASYNC_PREWRITE); /* Create CRQ entry */ crq.valid = 0x80; crq.format = 0x01; crq.iu_data = xp->sc->srp_iu_phys + xp->srp_iu_offset; err = phyp_hcall(H_SEND_CRQ, xp->sc->unit, ((uint64_t *)(&crq))[0], ((uint64_t *)(&crq))[1]); if (err != 0) panic("CRQ send failure (%d)", err); } static void vscsi_crq_load_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int err) { struct vscsi_softc *sc = xsc; sc->crq_phys = segs[0].ds_addr; sc->n_crqs = PAGE_SIZE/sizeof(struct vscsi_crq); sc->srp_iu_queue = (uint8_t *)(sc->crq_queue); sc->srp_iu_phys = segs[0].ds_addr; sc->srp_iu_arena = vmem_create("VSCSI SRP IU", PAGE_SIZE, segs[0].ds_len - PAGE_SIZE, 16, 0, M_BESTFIT | M_NOWAIT); } static void vscsi_setup_bus(struct vscsi_softc *sc) { struct vscsi_crq crq; struct vscsi_xfer *xp; int error; struct { uint32_t type; uint16_t status; uint16_t length; uint64_t tag; uint64_t buffer; struct { char srp_version[8]; char partition_name[96]; uint32_t partition_number; uint32_t mad_version; uint32_t os_type; uint32_t port_max_txu[8]; } payload; } mad_adapter_info; bzero(&crq, sizeof(crq)); /* Init message */ crq.valid = 0xc0; crq.format = 0x01; do { error = phyp_hcall(H_FREE_CRQ, sc->unit); } while (error == H_BUSY); /* See initialization sequence page 757 */ bzero(sc->crq_queue, sc->n_crqs*sizeof(sc->crq_queue[0])); sc->cur_crq = 0; sc->bus_initialized = 0; sc->bus_logged_in = 0; bus_dmamap_sync(sc->crq_tag, sc->crq_map, BUS_DMASYNC_PREWRITE); error = phyp_hcall(H_REG_CRQ, sc->unit, sc->crq_phys, sc->n_crqs*sizeof(sc->crq_queue[0])); KASSERT(error == 0, ("CRQ registration success")); error = phyp_hcall(H_SEND_CRQ, sc->unit, ((uint64_t *)(&crq))[0], ((uint64_t *)(&crq))[1]); if (error != 0) panic("CRQ setup failure (%d)", error); while (sc->bus_initialized == 0) vscsi_check_response_queue(sc); /* Send MAD adapter info */ mad_adapter_info.type = MAD_ADAPTER_INFO_REQUEST; mad_adapter_info.status = 0; mad_adapter_info.length = sizeof(mad_adapter_info.payload); strcpy(mad_adapter_info.payload.srp_version, "16.a"); strcpy(mad_adapter_info.payload.partition_name, "UNKNOWN"); mad_adapter_info.payload.partition_number = -1; mad_adapter_info.payload.mad_version = 1; mad_adapter_info.payload.os_type = 2; /* Claim we are Linux */ mad_adapter_info.payload.port_max_txu[0] = 0; /* If this fails, we get the defaults above */ OF_getprop(OF_finddevice("/"), "ibm,partition-name", mad_adapter_info.payload.partition_name, sizeof(mad_adapter_info.payload.partition_name)); OF_getprop(OF_finddevice("/"), "ibm,partition-no", &mad_adapter_info.payload.partition_number, sizeof(mad_adapter_info.payload.partition_number)); xp = TAILQ_FIRST(&sc->free_xferq); xp->ccb = NULL; TAILQ_REMOVE(&sc->free_xferq, xp, queue); TAILQ_INSERT_TAIL(&sc->active_xferq, xp, queue); xp->srp_iu_size = crq.iu_length = sizeof(mad_adapter_info); vmem_alloc(xp->sc->srp_iu_arena, xp->srp_iu_size, M_BESTFIT | M_NOWAIT, &xp->srp_iu_offset); mad_adapter_info.buffer = xp->sc->srp_iu_phys + xp->srp_iu_offset + 24; mad_adapter_info.tag = (uint64_t)xp; memcpy((uint8_t *)xp->sc->srp_iu_queue + (uintptr_t)xp->srp_iu_offset, &mad_adapter_info, sizeof(mad_adapter_info)); crq.valid = 0x80; crq.format = 0x02; crq.iu_data = xp->sc->srp_iu_phys + xp->srp_iu_offset; bus_dmamap_sync(sc->crq_tag, sc->crq_map, BUS_DMASYNC_PREWRITE); phyp_hcall(H_SEND_CRQ, xp->sc->unit, ((uint64_t *)(&crq))[0], ((uint64_t *)(&crq))[1]); while (TAILQ_EMPTY(&sc->free_xferq)) vscsi_check_response_queue(sc); /* Send SRP login */ vscsi_srp_login(sc); while (sc->bus_logged_in == 0) vscsi_check_response_queue(sc); error = phyp_hcall(H_VIO_SIGNAL, sc->unit, 1); /* Enable interrupts */ } static void vscsi_intr(void *xsc) { struct vscsi_softc *sc = xsc; mtx_lock(&sc->io_lock); vscsi_check_response_queue(sc); mtx_unlock(&sc->io_lock); } static void vscsi_srp_response(struct vscsi_xfer *xp, struct vscsi_crq *crq) { union ccb *ccb = xp->ccb; struct vscsi_softc *sc = xp->sc; struct srp_rsp *rsp; uint32_t sense_len; /* SRP response packet in original request */ rsp = (struct srp_rsp *)((uint8_t *)sc->srp_iu_queue + (uintptr_t)xp->srp_iu_offset); ccb->csio.scsi_status = rsp->status; if (ccb->csio.scsi_status == SCSI_STATUS_OK) ccb->ccb_h.status = CAM_REQ_CMP; else ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; #ifdef NOTYET /* Collect fast fail codes */ if (crq->status != 0) ccb->ccb_h.status = CAM_REQ_CMP_ERR; #endif if (ccb->ccb_h.status != CAM_REQ_CMP) { ccb->ccb_h.status |= CAM_DEV_QFRZN; xpt_freeze_devq(ccb->ccb_h.path, /*count*/ 1); } if (!(rsp->flags & SRP_RSPVALID)) rsp->response_data_len = 0; if (!(rsp->flags & SRP_SNSVALID)) rsp->sense_data_len = 0; if (!(rsp->flags & (SRP_DOOVER | SRP_DOUNDER))) rsp->data_out_resid = 0; if (!(rsp->flags & (SRP_DIOVER | SRP_DIUNDER))) rsp->data_in_resid = 0; if (rsp->flags & SRP_SNSVALID) { bzero(&ccb->csio.sense_data, sizeof(struct scsi_sense_data)); ccb->ccb_h.status |= CAM_AUTOSNS_VALID; sense_len = min(be32toh(rsp->sense_data_len), ccb->csio.sense_len); memcpy(&ccb->csio.sense_data, &rsp->data_payload[be32toh(rsp->response_data_len)], sense_len); ccb->csio.sense_resid = ccb->csio.sense_len - be32toh(rsp->sense_data_len); } switch (ccb->ccb_h.flags & CAM_DIR_MASK) { case CAM_DIR_OUT: ccb->csio.resid = rsp->data_out_resid; break; case CAM_DIR_IN: ccb->csio.resid = rsp->data_in_resid; break; } bus_dmamap_sync(sc->data_tag, xp->dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->data_tag, xp->dmamap); xpt_done(ccb); xp->ccb = NULL; } static void vscsi_login_response(struct vscsi_xfer *xp, struct vscsi_crq *crq) { struct vscsi_softc *sc = xp->sc; struct srp_login_rsp *rsp; /* SRP response packet in original request */ rsp = (struct srp_login_rsp *)((uint8_t *)sc->srp_iu_queue + (uintptr_t)xp->srp_iu_offset); KASSERT(be16toh(rsp->buffer_formats) & 0x3, ("Both direct and indirect " "buffers supported")); sc->max_transactions = be32toh(rsp->request_limit_delta); device_printf(sc->dev, "Queue depth %d commands\n", sc->max_transactions); sc->bus_logged_in = 1; } static void vscsi_cam_poll(struct cam_sim *sim) { struct vscsi_softc *sc = cam_sim_softc(sim); vscsi_check_response_queue(sc); } static void vscsi_check_response_queue(struct vscsi_softc *sc) { struct vscsi_crq *crq; struct vscsi_xfer *xp; int code; mtx_assert(&sc->io_lock, MA_OWNED); while (sc->crq_queue[sc->cur_crq].valid != 0) { /* The hypercalls at both ends of this are not optimal */ phyp_hcall(H_VIO_SIGNAL, sc->unit, 0); bus_dmamap_sync(sc->crq_tag, sc->crq_map, BUS_DMASYNC_POSTREAD); crq = &sc->crq_queue[sc->cur_crq]; switch (crq->valid) { case 0xc0: if (crq->format == 0x02) sc->bus_initialized = 1; break; case 0x80: /* IU data is set to tag pointer (the XP) */ xp = (struct vscsi_xfer *)crq->iu_data; switch (crq->format) { case 0x01: code = *((uint8_t *)sc->srp_iu_queue + (uintptr_t)xp->srp_iu_offset); switch (code) { case SRP_RSP: vscsi_srp_response(xp, crq); break; case SRP_LOGIN_RSP: vscsi_login_response(xp, crq); break; default: device_printf(sc->dev, "Unknown SRP " "response code %d\n", code); break; } break; case 0x02: /* Ignore management datagrams */ break; default: panic("Unknown CRQ format %d\n", crq->format); break; } vmem_free(sc->srp_iu_arena, xp->srp_iu_offset, xp->srp_iu_size); TAILQ_REMOVE(&sc->active_xferq, xp, queue); TAILQ_INSERT_TAIL(&sc->free_xferq, xp, queue); break; default: device_printf(sc->dev, "Unknown CRQ message type %d\n", crq->valid); break; } crq->valid = 0; sc->cur_crq = (sc->cur_crq + 1) % sc->n_crqs; bus_dmamap_sync(sc->crq_tag, sc->crq_map, BUS_DMASYNC_PREWRITE); phyp_hcall(H_VIO_SIGNAL, sc->unit, 1); } } Index: head/sys/powerpc/pseries/platform_chrp.c =================================================================== --- head/sys/powerpc/pseries/platform_chrp.c (revision 326260) +++ head/sys/powerpc/pseries/platform_chrp.c (revision 326261) @@ -1,528 +1,530 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2008 Marcel Moolenaar * Copyright (c) 2009 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "platform_if.h" #ifdef SMP extern void *ap_pcpu; #endif #ifdef __powerpc64__ static uint8_t splpar_vpa[MAXCPU][640] __aligned(128); /* XXX: dpcpu */ #endif static vm_offset_t realmaxaddr = VM_MAX_ADDRESS; static int chrp_probe(platform_t); static int chrp_attach(platform_t); void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz, struct mem_region *avail, int *availsz); static vm_offset_t chrp_real_maxaddr(platform_t); static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref); static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref); static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref); static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref); static void chrp_smp_ap_init(platform_t); #ifdef SMP static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu); static struct cpu_group *chrp_smp_topo(platform_t plat); #endif static void chrp_reset(platform_t); #ifdef __powerpc64__ #include "phyp-hvcall.h" static void phyp_cpu_idle(sbintime_t sbt); #endif static platform_method_t chrp_methods[] = { PLATFORMMETHOD(platform_probe, chrp_probe), PLATFORMMETHOD(platform_attach, chrp_attach), PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions), PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr), PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq), PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init), PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu), PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu), PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp), #ifdef SMP PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu), PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo), #endif PLATFORMMETHOD(platform_reset, chrp_reset), { 0, 0 } }; static platform_def_t chrp_platform = { "chrp", chrp_methods, 0 }; PLATFORM_DEF(chrp_platform); static int chrp_probe(platform_t plat) { if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1) return (BUS_PROBE_GENERIC); return (ENXIO); } static int chrp_attach(platform_t plat) { #ifdef __powerpc64__ int i; /* XXX: check for /rtas/ibm,hypertas-functions? */ if (!(mfmsr() & PSL_HV)) { struct mem_region *phys, *avail; int nphys, navail; mem_regions(&phys, &nphys, &avail, &navail); realmaxaddr = phys[0].mr_size; pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC); cpu_idle_hook = phyp_cpu_idle; /* Set up important VPA fields */ for (i = 0; i < MAXCPU; i++) { bzero(splpar_vpa[i], sizeof(splpar_vpa)); /* First two: VPA size */ splpar_vpa[i][4] = (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff); splpar_vpa[i][5] = (uint8_t)(sizeof(splpar_vpa[i]) & 0xff); splpar_vpa[i][0xba] = 1; /* Maintain FPRs */ splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */ splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */ splpar_vpa[i][0xfd] = 0xff; splpar_vpa[i][0xff] = 1; /* Maintain Altivec */ } mb(); /* Set up hypervisor CPU stuff */ chrp_smp_ap_init(plat); } #endif /* Some systems (e.g. QEMU) need Open Firmware to stand down */ ofw_quiesce(); return (0); } static int parse_drconf_memory(struct mem_region *ofmem, int *msz, struct mem_region *ofavail, int *asz) { phandle_t phandle; vm_offset_t base; int i, idx, len, lasz, lmsz, res; uint32_t flags, lmb_size[2]; uint32_t *dmem; lmsz = *msz; lasz = *asz; phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory"); if (phandle == -1) /* No drconf node, return. */ return (0); res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size, sizeof(lmb_size)); if (res == -1) return (0); printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20); /* Parse the /ibm,dynamic-memory. The first position gives the # of entries. The next two words reflect the address of the memory block. The next four words are the DRC index, reserved, list index and flags. (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory) #el Addr DRC-idx res list-idx flags ------------------------------------------------- | 4 | 8 | 4 | 4 | 4 | 4 |.... ------------------------------------------------- */ len = OF_getproplen(phandle, "ibm,dynamic-memory"); if (len > 0) { /* We have to use a variable length array on the stack since we have very limited stack space. */ cell_t arr[len/sizeof(cell_t)]; res = OF_getencprop(phandle, "ibm,dynamic-memory", arr, sizeof(arr)); if (res == -1) return (0); /* Number of elements */ idx = arr[0]; /* First address, in arr[1], arr[2]*/ dmem = &arr[1]; for (i = 0; i < idx; i++) { base = ((uint64_t)dmem[0] << 32) + dmem[1]; dmem += 4; flags = dmem[1]; /* Use region only if available and not reserved. */ if ((flags & 0x8) && !(flags & 0x80)) { ofmem[lmsz].mr_start = base; ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1]; ofavail[lasz].mr_start = base; ofavail[lasz].mr_size = (vm_size_t)lmb_size[1]; lmsz++; lasz++; } dmem += 2; } } *msz = lmsz; *asz = lasz; return (1); } void chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz, struct mem_region *avail, int *availsz) { vm_offset_t maxphysaddr; int i; ofw_mem_regions(phys, physsz, avail, availsz); parse_drconf_memory(phys, physsz, avail, availsz); /* * On some firmwares (SLOF), some memory may be marked available that * doesn't actually exist. This manifests as an extension of the last * available segment past the end of physical memory, so truncate that * one. */ maxphysaddr = 0; for (i = 0; i < *physsz; i++) if (phys[i].mr_start + phys[i].mr_size > maxphysaddr) maxphysaddr = phys[i].mr_start + phys[i].mr_size; for (i = 0; i < *availsz; i++) if (avail[i].mr_start + avail[i].mr_size > maxphysaddr) avail[i].mr_size = maxphysaddr - avail[i].mr_start; } static vm_offset_t chrp_real_maxaddr(platform_t plat) { return (realmaxaddr); } static u_long chrp_timebase_freq(platform_t plat, struct cpuref *cpuref) { phandle_t phandle; int32_t ticks = -1; phandle = cpuref->cr_hwref; OF_getencprop(phandle, "timebase-frequency", &ticks, sizeof(ticks)); if (ticks <= 0) panic("Unable to determine timebase frequency!"); return (ticks); } static int chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref) { char buf[8]; phandle_t cpu, dev, root; int res, cpuid; root = OF_peer(0); dev = OF_child(root); while (dev != 0) { res = OF_getprop(dev, "name", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpus") == 0) break; dev = OF_peer(dev); } if (dev == 0) { /* * psim doesn't have a name property on the /cpus node, * but it can be found directly */ dev = OF_finddevice("/cpus"); if (dev == 0) return (ENOENT); } cpu = OF_child(dev); while (cpu != 0) { res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpu") == 0) break; cpu = OF_peer(cpu); } if (cpu == 0) return (ENOENT); cpuref->cr_hwref = cpu; res = OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, sizeof(cpuid)); if (res <= 0) res = OF_getencprop(cpu, "reg", &cpuid, sizeof(cpuid)); if (res <= 0) cpuid = 0; cpuref->cr_cpuid = cpuid; return (0); } static int chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref) { char buf[8]; phandle_t cpu; int i, res, cpuid; /* Check for whether it should be the next thread */ res = OF_getproplen(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s"); if (res > 0) { cell_t interrupt_servers[res/sizeof(cell_t)]; OF_getencprop(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s", interrupt_servers, res); for (i = 0; i < res/sizeof(cell_t) - 1; i++) { if (interrupt_servers[i] == cpuref->cr_cpuid) { cpuref->cr_cpuid = interrupt_servers[i+1]; return (0); } } } /* Next CPU core/package */ cpu = OF_peer(cpuref->cr_hwref); while (cpu != 0) { res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpu") == 0) break; cpu = OF_peer(cpu); } if (cpu == 0) return (ENOENT); cpuref->cr_hwref = cpu; res = OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, sizeof(cpuid)); if (res <= 0) res = OF_getencprop(cpu, "reg", &cpuid, sizeof(cpuid)); if (res <= 0) cpuid = 0; cpuref->cr_cpuid = cpuid; return (0); } static int chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref) { ihandle_t inst; phandle_t bsp, chosen; int res, cpuid; chosen = OF_finddevice("/chosen"); if (chosen == 0) return (ENXIO); res = OF_getencprop(chosen, "cpu", &inst, sizeof(inst)); if (res < 0) return (ENXIO); bsp = OF_instance_to_package(inst); /* Pick the primary thread. Can it be any other? */ cpuref->cr_hwref = bsp; res = OF_getencprop(bsp, "ibm,ppc-interrupt-server#s", &cpuid, sizeof(cpuid)); if (res <= 0) res = OF_getencprop(bsp, "reg", &cpuid, sizeof(cpuid)); if (res <= 0) cpuid = 0; cpuref->cr_cpuid = cpuid; return (0); } #ifdef SMP static int chrp_smp_start_cpu(platform_t plat, struct pcpu *pc) { cell_t start_cpu; int result, err, timeout; if (!rtas_exists()) { printf("RTAS uninitialized: unable to start AP %d\n", pc->pc_cpuid); return (ENXIO); } start_cpu = rtas_token_lookup("start-cpu"); if (start_cpu == -1) { printf("RTAS unknown method: unable to start AP %d\n", pc->pc_cpuid); return (ENXIO); } ap_pcpu = pc; powerpc_sync(); result = rtas_call_method(start_cpu, 3, 1, pc->pc_cpuid, EXC_RST, pc, &err); if (result < 0 || err != 0) { printf("RTAS error (%d/%d): unable to start AP %d\n", result, err, pc->pc_cpuid); return (ENXIO); } timeout = 10000; while (!pc->pc_awake && timeout--) DELAY(100); return ((pc->pc_awake) ? 0 : EBUSY); } static struct cpu_group * chrp_smp_topo(platform_t plat) { struct pcpu *pc, *last_pc; int i, ncores, ncpus; ncores = ncpus = 0; last_pc = NULL; for (i = 0; i <= mp_maxid; i++) { pc = pcpu_find(i); if (pc == NULL) continue; if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref) ncores++; last_pc = pc; ncpus++; } if (ncpus % ncores != 0) { printf("WARNING: Irregular SMP topology. Performance may be " "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores); return (smp_topo_none()); } /* Don't do anything fancier for non-threaded SMP */ if (ncpus == ncores) return (smp_topo_none()); return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT)); } #endif static void chrp_reset(platform_t platform) { OF_reboot(); } #ifdef __powerpc64__ static void phyp_cpu_idle(sbintime_t sbt) { register_t msr; msr = mfmsr(); mtmsr(msr & ~PSL_EE); if (sched_runnable()) { mtmsr(msr); return; } phyp_hcall(H_CEDE); /* Re-enables interrupts internally */ mtmsr(msr); } static void chrp_smp_ap_init(platform_t platform) { if (!(mfmsr() & PSL_HV)) { /* Register VPA */ phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(cpuid), splpar_vpa[PCPU_GET(cpuid)]); /* Set interrupt priority */ phyp_hcall(H_CPPR, 0xff); } } #else static void chrp_smp_ap_init(platform_t platform) { } #endif Index: head/sys/powerpc/pseries/plpar_iommu.c =================================================================== --- head/sys/powerpc/pseries/plpar_iommu.c (revision 326260) +++ head/sys/powerpc/pseries/plpar_iommu.c (revision 326261) @@ -1,245 +1,247 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2013, Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_PHYPIOMMU, "iommu", "IOMMU data for PAPR LPARs"); struct papr_iommu_map { uint32_t iobn; vmem_t *vmem; struct papr_iommu_map *next; }; static SLIST_HEAD(iommu_maps, iommu_map) iommu_map_head = SLIST_HEAD_INITIALIZER(iommu_map_head); static int papr_supports_stuff_tce = -1; struct iommu_map { uint32_t iobn; vmem_t *vmem; SLIST_ENTRY(iommu_map) entries; }; struct dma_window { struct iommu_map *map; bus_addr_t start; bus_addr_t end; }; int phyp_iommu_set_dma_tag(device_t bus, device_t dev, bus_dma_tag_t tag) { device_t p; phandle_t node; cell_t dma_acells, dma_scells, dmawindow[6]; struct iommu_map *i; int cell; for (p = dev; device_get_parent(p) != NULL; p = device_get_parent(p)) { if (ofw_bus_has_prop(p, "ibm,my-dma-window")) break; if (ofw_bus_has_prop(p, "ibm,dma-window")) break; } if (p == NULL) return (ENXIO); node = ofw_bus_get_node(p); if (OF_getencprop(node, "ibm,#dma-size-cells", &dma_scells, sizeof(cell_t)) <= 0) OF_searchencprop(node, "#size-cells", &dma_scells, sizeof(cell_t)); if (OF_getencprop(node, "ibm,#dma-address-cells", &dma_acells, sizeof(cell_t)) <= 0) OF_searchencprop(node, "#address-cells", &dma_acells, sizeof(cell_t)); if (ofw_bus_has_prop(p, "ibm,my-dma-window")) OF_getencprop(node, "ibm,my-dma-window", dmawindow, sizeof(cell_t)*(dma_scells + dma_acells + 1)); else OF_getencprop(node, "ibm,dma-window", dmawindow, sizeof(cell_t)*(dma_scells + dma_acells + 1)); struct dma_window *window = malloc(sizeof(struct dma_window), M_PHYPIOMMU, M_WAITOK); window->start = 0; for (cell = 1; cell < 1 + dma_acells; cell++) { window->start <<= 32; window->start |= dmawindow[cell]; } window->end = 0; for (; cell < 1 + dma_acells + dma_scells; cell++) { window->end <<= 32; window->end |= dmawindow[cell]; } window->end += window->start; if (bootverbose) device_printf(dev, "Mapping IOMMU domain %#x\n", dmawindow[0]); window->map = NULL; SLIST_FOREACH(i, &iommu_map_head, entries) { if (i->iobn == dmawindow[0]) { window->map = i; break; } } if (window->map == NULL) { window->map = malloc(sizeof(struct iommu_map), M_PHYPIOMMU, M_WAITOK); window->map->iobn = dmawindow[0]; /* * Allocate IOMMU range beginning at PAGE_SIZE. Some drivers * (em(4), for example) do not like getting mappings at 0. */ window->map->vmem = vmem_create("IOMMU mappings", PAGE_SIZE, trunc_page(VMEM_ADDR_MAX) - PAGE_SIZE, PAGE_SIZE, 0, M_BESTFIT | M_NOWAIT); SLIST_INSERT_HEAD(&iommu_map_head, window->map, entries); } /* * Check experimentally whether we can use H_STUFF_TCE. It is required * by the spec but some firmware (e.g. QEMU) does not actually support * it */ if (papr_supports_stuff_tce == -1) papr_supports_stuff_tce = !(phyp_hcall(H_STUFF_TCE, window->map->iobn, 0, 0, 0) == H_FUNCTION); bus_dma_tag_set_iommu(tag, bus, window); return (0); } int phyp_iommu_map(device_t dev, bus_dma_segment_t *segs, int *nsegs, bus_addr_t min, bus_addr_t max, bus_size_t alignment, bus_addr_t boundary, void *cookie) { struct dma_window *window = cookie; bus_addr_t minaddr, maxaddr; bus_addr_t alloced; bus_size_t allocsize; int error, i, j; uint64_t tce; minaddr = window->start; maxaddr = window->end; /* XXX: handle exclusion range in a more useful way */ if (min < maxaddr) maxaddr = min; /* XXX: consolidate segs? */ for (i = 0; i < *nsegs; i++) { allocsize = round_page(segs[i].ds_len + (segs[i].ds_addr & PAGE_MASK)); error = vmem_xalloc(window->map->vmem, allocsize, (alignment < PAGE_SIZE) ? PAGE_SIZE : alignment, 0, boundary, minaddr, maxaddr, M_BESTFIT | M_NOWAIT, &alloced); if (error != 0) { panic("VMEM failure: %d\n", error); return (error); } KASSERT(alloced % PAGE_SIZE == 0, ("Alloc not page aligned")); KASSERT((alloced + (segs[i].ds_addr & PAGE_MASK)) % alignment == 0, ("Allocated segment does not match alignment constraint")); tce = trunc_page(segs[i].ds_addr); tce |= 0x3; /* read/write */ for (j = 0; j < allocsize; j += PAGE_SIZE) { error = phyp_hcall(H_PUT_TCE, window->map->iobn, alloced + j, tce + j); if (error < 0) { panic("IOMMU mapping error: %d\n", error); return (ENOMEM); } } segs[i].ds_addr = alloced + (segs[i].ds_addr & PAGE_MASK); KASSERT(segs[i].ds_addr > 0, ("Address needs to be positive")); KASSERT(segs[i].ds_addr + segs[i].ds_len < maxaddr, ("Address not in range")); if (error < 0) { panic("IOMMU mapping error: %d\n", error); return (ENOMEM); } } return (0); } int phyp_iommu_unmap(device_t dev, bus_dma_segment_t *segs, int nsegs, void *cookie) { struct dma_window *window = cookie; bus_addr_t pageround; bus_size_t roundedsize; int i; bus_addr_t j; for (i = 0; i < nsegs; i++) { pageround = trunc_page(segs[i].ds_addr); roundedsize = round_page(segs[i].ds_len + (segs[i].ds_addr & PAGE_MASK)); if (papr_supports_stuff_tce) { phyp_hcall(H_STUFF_TCE, window->map->iobn, pageround, 0, roundedsize/PAGE_SIZE); } else { for (j = 0; j < roundedsize; j += PAGE_SIZE) phyp_hcall(H_PUT_TCE, window->map->iobn, pageround + j, 0); } vmem_xfree(window->map->vmem, pageround, roundedsize); } return (0); } Index: head/sys/powerpc/pseries/plpar_iommu.h =================================================================== --- head/sys/powerpc/pseries/plpar_iommu.h (revision 326260) +++ head/sys/powerpc/pseries/plpar_iommu.h (revision 326261) @@ -1,43 +1,45 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2013, Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _PSERIES_PHYP_IOMMU_H_ #define _PSERIES_PHYP_IOMMU_H_ #include #include int phyp_iommu_set_dma_tag(device_t dev, device_t child, bus_dma_tag_t tag); int phyp_iommu_map(device_t dev, bus_dma_segment_t *segs, int *nsegs, bus_addr_t min, bus_addr_t max, bus_size_t alignment, bus_addr_t boundary, void *cookie); int phyp_iommu_unmap(device_t dev, bus_dma_segment_t *segs, int nsegs, void *cookie); #endif Index: head/sys/powerpc/pseries/plpar_pcibus.c =================================================================== --- head/sys/powerpc/pseries/plpar_pcibus.c (revision 326260) +++ head/sys/powerpc/pseries/plpar_pcibus.c (revision 326261) @@ -1,113 +1,115 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pci_if.h" #include "iommu_if.h" static int plpar_pcibus_probe(device_t); static bus_dma_tag_t plpar_pcibus_get_dma_tag(device_t dev, device_t child); /* * Driver methods. */ static device_method_t plpar_pcibus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, plpar_pcibus_probe), /* IOMMU functions */ DEVMETHOD(bus_get_dma_tag, plpar_pcibus_get_dma_tag), DEVMETHOD(iommu_map, phyp_iommu_map), DEVMETHOD(iommu_unmap, phyp_iommu_unmap), DEVMETHOD_END }; static devclass_t pci_devclass; DEFINE_CLASS_1(pci, plpar_pcibus_driver, plpar_pcibus_methods, sizeof(struct pci_softc), ofw_pcibus_driver); DRIVER_MODULE(plpar_pcibus, pcib, plpar_pcibus_driver, pci_devclass, 0, 0); static int plpar_pcibus_probe(device_t dev) { phandle_t rtas; if (ofw_bus_get_node(dev) == -1 || !rtas_exists()) return (ENXIO); rtas = OF_finddevice("/rtas"); if (!OF_hasprop(rtas, "ibm,hypertas-functions")) return (ENXIO); device_set_desc(dev, "POWER Hypervisor PCI bus"); return (BUS_PROBE_SPECIFIC); } static bus_dma_tag_t plpar_pcibus_get_dma_tag(device_t dev, device_t child) { struct ofw_pcibus_devinfo *dinfo; while (device_get_parent(child) != dev) child = device_get_parent(child); dinfo = device_get_ivars(child); if (dinfo->opd_dma_tag != NULL) return (dinfo->opd_dma_tag); bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE, BUS_SPACE_UNRESTRICTED, BUS_SPACE_MAXSIZE, 0, NULL, NULL, &dinfo->opd_dma_tag); phyp_iommu_set_dma_tag(dev, child, dinfo->opd_dma_tag); return (dinfo->opd_dma_tag); } Index: head/sys/powerpc/pseries/rtas_dev.c =================================================================== --- head/sys/powerpc/pseries/rtas_dev.c (revision 326260) +++ head/sys/powerpc/pseries/rtas_dev.c (revision 326261) @@ -1,173 +1,175 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clock_if.h" static int rtasdev_probe(device_t); static int rtasdev_attach(device_t); /* clock interface */ static int rtas_gettime(device_t dev, struct timespec *ts); static int rtas_settime(device_t dev, struct timespec *ts); static void rtas_shutdown(void *arg, int howto); static device_method_t rtasdev_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rtasdev_probe), DEVMETHOD(device_attach, rtasdev_attach), /* clock interface */ DEVMETHOD(clock_gettime, rtas_gettime), DEVMETHOD(clock_settime, rtas_settime), { 0, 0 }, }; static driver_t rtasdev_driver = { "rtas", rtasdev_methods, 0 }; static devclass_t rtasdev_devclass; DRIVER_MODULE(rtasdev, ofwbus, rtasdev_driver, rtasdev_devclass, 0, 0); static int rtasdev_probe(device_t dev) { const char *name = ofw_bus_get_name(dev); if (strcmp(name, "rtas") != 0) return (ENXIO); if (!rtas_exists()) return (ENXIO); device_set_desc(dev, "Run-Time Abstraction Services"); return (0); } static int rtasdev_attach(device_t dev) { if (rtas_token_lookup("get-time-of-day") != -1) clock_register(dev, 2000); EVENTHANDLER_REGISTER(shutdown_final, rtas_shutdown, NULL, SHUTDOWN_PRI_LAST); return (0); } static int rtas_gettime(device_t dev, struct timespec *ts) { struct clocktime ct; cell_t tod[8]; cell_t token; int error; token = rtas_token_lookup("get-time-of-day"); if (token == -1) return (ENXIO); error = rtas_call_method(token, 0, 8, &tod[0], &tod[1], &tod[2], &tod[3], &tod[4], &tod[5], &tod[6], &tod[7]); if (error < 0) return (ENXIO); if (tod[0] != 0) return ((tod[0] == -1) ? ENXIO : EAGAIN); ct.year = tod[1]; ct.mon = tod[2]; ct.day = tod[3]; ct.hour = tod[4]; ct.min = tod[5]; ct.sec = tod[6]; ct.nsec = tod[7]; return (clock_ct_to_ts(&ct, ts)); } static int rtas_settime(device_t dev, struct timespec *ts) { struct clocktime ct; cell_t token, status; int error; token = rtas_token_lookup("set-time-of-day"); if (token == -1) return (ENXIO); clock_ts_to_ct(ts, &ct); error = rtas_call_method(token, 7, 1, ct.year, ct.mon, ct.day, ct.hour, ct.min, ct.sec, ct.nsec, &status); if (error < 0) return (ENXIO); if (status != 0) return (((int)status < 0) ? ENXIO : EAGAIN); return (0); } static void rtas_shutdown(void *arg, int howto) { cell_t token, status; if (howto & RB_HALT) { token = rtas_token_lookup("power-off"); if (token == -1) return; rtas_call_method(token, 2, 1, 0, 0, &status); } else { token = rtas_token_lookup("system-reboot"); if (token == -1) return; rtas_call_method(token, 0, 1, &status); } } Index: head/sys/powerpc/pseries/rtas_pci.c =================================================================== --- head/sys/powerpc/pseries/rtas_pci.c (revision 326260) +++ head/sys/powerpc/pseries/rtas_pci.c (revision 326261) @@ -1,210 +1,212 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #include "iommu_if.h" /* * Device interface. */ static int rtaspci_probe(device_t); static int rtaspci_attach(device_t); /* * pcib interface. */ static u_int32_t rtaspci_read_config(device_t, u_int, u_int, u_int, u_int, int); static void rtaspci_write_config(device_t, u_int, u_int, u_int, u_int, u_int32_t, int); /* * Driver methods. */ static device_method_t rtaspci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rtaspci_probe), DEVMETHOD(device_attach, rtaspci_attach), /* pcib interface */ DEVMETHOD(pcib_read_config, rtaspci_read_config), DEVMETHOD(pcib_write_config, rtaspci_write_config), DEVMETHOD_END }; struct rtaspci_softc { struct ofw_pci_softc pci_sc; struct ofw_pci_register sc_pcir; cell_t read_pci_config, write_pci_config; cell_t ex_read_pci_config, ex_write_pci_config; int sc_extended_config; }; static devclass_t rtaspci_devclass; DEFINE_CLASS_1(pcib, rtaspci_driver, rtaspci_methods, sizeof(struct rtaspci_softc), ofw_pci_driver); DRIVER_MODULE(rtaspci, ofwbus, rtaspci_driver, rtaspci_devclass, 0, 0); static int rtaspci_probe(device_t dev) { const char *type; if (!rtas_exists()) return (ENXIO); type = ofw_bus_get_type(dev); if (OF_getproplen(ofw_bus_get_node(dev), "used-by-rtas") < 0) return (ENXIO); if (type == NULL || strcmp(type, "pci") != 0) return (ENXIO); device_set_desc(dev, "RTAS Host-PCI bridge"); return (BUS_PROBE_GENERIC); } static int rtaspci_attach(device_t dev) { struct rtaspci_softc *sc; sc = device_get_softc(dev); if (OF_getencprop(ofw_bus_get_node(dev), "reg", (pcell_t *)&sc->sc_pcir, sizeof(sc->sc_pcir)) == -1) return (ENXIO); sc->read_pci_config = rtas_token_lookup("read-pci-config"); sc->write_pci_config = rtas_token_lookup("write-pci-config"); sc->ex_read_pci_config = rtas_token_lookup("ibm,read-pci-config"); sc->ex_write_pci_config = rtas_token_lookup("ibm,write-pci-config"); sc->sc_extended_config = 0; OF_getencprop(ofw_bus_get_node(dev), "ibm,pci-config-space-type", &sc->sc_extended_config, sizeof(sc->sc_extended_config)); return (ofw_pci_attach(dev)); } static uint32_t rtaspci_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int width) { struct rtaspci_softc *sc; uint32_t retval = 0xffffffff; uint32_t config_addr; int error, pcierror; sc = device_get_softc(dev); config_addr = ((bus & 0xff) << 16) | ((slot & 0x1f) << 11) | ((func & 0x7) << 8) | (reg & 0xff); if (sc->sc_extended_config) config_addr |= (reg & 0xf00) << 16; if (sc->ex_read_pci_config != -1) error = rtas_call_method(sc->ex_read_pci_config, 4, 2, config_addr, sc->sc_pcir.phys_hi, sc->sc_pcir.phys_mid, width, &pcierror, &retval); else error = rtas_call_method(sc->read_pci_config, 2, 2, config_addr, width, &pcierror, &retval); /* Sign-extend output */ switch (width) { case 1: retval = (int32_t)(int8_t)(retval); break; case 2: retval = (int32_t)(int16_t)(retval); break; } if (error < 0 || pcierror != 0) retval = 0xffffffff; return (retval); } static void rtaspci_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t val, int width) { struct rtaspci_softc *sc; uint32_t config_addr; int pcierror; sc = device_get_softc(dev); config_addr = ((bus & 0xff) << 16) | ((slot & 0x1f) << 11) | ((func & 0x7) << 8) | (reg & 0xff); if (sc->sc_extended_config) config_addr |= (reg & 0xf00) << 16; if (sc->ex_write_pci_config != -1) rtas_call_method(sc->ex_write_pci_config, 5, 1, config_addr, sc->sc_pcir.phys_hi, sc->sc_pcir.phys_mid, width, val, &pcierror); else rtas_call_method(sc->write_pci_config, 3, 1, config_addr, width, val, &pcierror); } Index: head/sys/powerpc/pseries/vdevice.c =================================================================== --- head/sys/powerpc/pseries/vdevice.c (revision 326260) +++ head/sys/powerpc/pseries/vdevice.c (revision 326261) @@ -1,217 +1,219 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iommu_if.h" static int vdevice_probe(device_t); static int vdevice_attach(device_t); static const struct ofw_bus_devinfo *vdevice_get_devinfo(device_t dev, device_t child); static int vdevice_print_child(device_t dev, device_t child); static struct resource_list *vdevice_get_resource_list(device_t, device_t); static bus_dma_tag_t vdevice_get_dma_tag(device_t dev, device_t child); /* * VDevice devinfo */ struct vdevice_devinfo { struct ofw_bus_devinfo mdi_obdinfo; struct resource_list mdi_resources; bus_dma_tag_t mdi_dma_tag; }; static device_method_t vdevice_methods[] = { /* Device interface */ DEVMETHOD(device_probe, vdevice_probe), DEVMETHOD(device_attach, vdevice_attach), /* Bus interface */ DEVMETHOD(bus_add_child, bus_generic_add_child), DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), DEVMETHOD(bus_print_child, vdevice_print_child), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_get_resource_list, vdevice_get_resource_list), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, vdevice_get_devinfo), 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), /* IOMMU interface */ DEVMETHOD(bus_get_dma_tag, vdevice_get_dma_tag), DEVMETHOD(iommu_map, phyp_iommu_map), DEVMETHOD(iommu_unmap, phyp_iommu_unmap), DEVMETHOD_END }; static driver_t vdevice_driver = { "vdevice", vdevice_methods, 0 }; static devclass_t vdevice_devclass; DRIVER_MODULE(vdevice, ofwbus, vdevice_driver, vdevice_devclass, 0, 0); static int vdevice_probe(device_t dev) { const char *name; name = ofw_bus_get_name(dev); if (name == NULL || strcmp(name, "vdevice") != 0) return (ENXIO); if (!ofw_bus_is_compatible(dev, "IBM,vdevice")) return (ENXIO); device_set_desc(dev, "POWER Hypervisor Virtual Device Root"); return (0); } static int vdevice_attach(device_t dev) { phandle_t root, child; device_t cdev; struct vdevice_devinfo *dinfo; root = ofw_bus_get_node(dev); /* The XICP (root PIC) will handle all our interrupts */ powerpc_register_pic(root_pic, OF_xref_from_node(root), 1 << 24 /* 24-bit XIRR field */, 1 /* Number of IPIs */, FALSE); for (child = OF_child(root); child != 0; child = OF_peer(child)) { dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO); if (ofw_bus_gen_setup_devinfo(&dinfo->mdi_obdinfo, child) != 0) { free(dinfo, M_DEVBUF); continue; } resource_list_init(&dinfo->mdi_resources); ofw_bus_intr_to_rl(dev, child, &dinfo->mdi_resources, NULL); cdev = device_add_child(dev, NULL, -1); if (cdev == NULL) { device_printf(dev, "<%s>: device_add_child failed\n", dinfo->mdi_obdinfo.obd_name); ofw_bus_gen_destroy_devinfo(&dinfo->mdi_obdinfo); free(dinfo, M_DEVBUF); continue; } device_set_ivars(cdev, dinfo); } return (bus_generic_attach(dev)); } static const struct ofw_bus_devinfo * vdevice_get_devinfo(device_t dev, device_t child) { return (device_get_ivars(child)); } static int vdevice_print_child(device_t dev, device_t child) { struct vdevice_devinfo *dinfo; struct resource_list *rl; int retval = 0; dinfo = device_get_ivars(child); rl = &dinfo->mdi_resources; retval += bus_print_child_header(dev, child); retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); retval += bus_print_child_footer(dev, child); return (retval); } static struct resource_list * vdevice_get_resource_list (device_t dev, device_t child) { struct vdevice_devinfo *dinfo; dinfo = device_get_ivars(child); return (&dinfo->mdi_resources); } static bus_dma_tag_t vdevice_get_dma_tag(device_t dev, device_t child) { struct vdevice_devinfo *dinfo; while (child != NULL && device_get_parent(child) != dev) child = device_get_parent(child); dinfo = device_get_ivars(child); if (dinfo->mdi_dma_tag == NULL) { bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE, BUS_SPACE_UNRESTRICTED, BUS_SPACE_MAXSIZE, 0, NULL, NULL, &dinfo->mdi_dma_tag); phyp_iommu_set_dma_tag(dev, child, dinfo->mdi_dma_tag); } return (dinfo->mdi_dma_tag); } Index: head/sys/powerpc/pseries/xics.c =================================================================== --- head/sys/powerpc/pseries/xics.c (revision 326260) +++ head/sys/powerpc/pseries/xics.c (revision 326261) @@ -1,332 +1,334 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 2011 Nathan Whitehorn * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "phyp-hvcall.h" #include "pic_if.h" #define XICP_PRIORITY 5 /* Random non-zero number */ #define XICP_IPI 2 #define MAX_XICP_IRQS (1<<24) /* 24-bit XIRR field */ static int xicp_probe(device_t); static int xicp_attach(device_t); static int xics_probe(device_t); static int xics_attach(device_t); static void xicp_bind(device_t dev, u_int irq, cpuset_t cpumask); static void xicp_dispatch(device_t, struct trapframe *); static void xicp_enable(device_t, u_int, u_int); static void xicp_eoi(device_t, u_int); static void xicp_ipi(device_t, u_int); static void xicp_mask(device_t, u_int); static void xicp_unmask(device_t, u_int); static device_method_t xicp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, xicp_probe), DEVMETHOD(device_attach, xicp_attach), /* PIC interface */ DEVMETHOD(pic_bind, xicp_bind), DEVMETHOD(pic_dispatch, xicp_dispatch), DEVMETHOD(pic_enable, xicp_enable), DEVMETHOD(pic_eoi, xicp_eoi), DEVMETHOD(pic_ipi, xicp_ipi), DEVMETHOD(pic_mask, xicp_mask), DEVMETHOD(pic_unmask, xicp_unmask), { 0, 0 }, }; static device_method_t xics_methods[] = { /* Device interface */ DEVMETHOD(device_probe, xics_probe), DEVMETHOD(device_attach, xics_attach), { 0, 0 }, }; struct xicp_softc { struct mtx sc_mtx; int ibm_int_on; int ibm_int_off; int ibm_get_xive; int ibm_set_xive; /* XXX: inefficient -- hash table? tree? */ struct { int irq; int vector; } intvecs[256]; int nintvecs; }; static driver_t xicp_driver = { "xicp", xicp_methods, sizeof(struct xicp_softc) }; static driver_t xics_driver = { "xics", xics_methods, 0 }; static devclass_t xicp_devclass; static devclass_t xics_devclass; EARLY_DRIVER_MODULE(xicp, ofwbus, xicp_driver, xicp_devclass, 0, 0, BUS_PASS_INTERRUPT-1); EARLY_DRIVER_MODULE(xics, ofwbus, xics_driver, xics_devclass, 0, 0, BUS_PASS_INTERRUPT); static int xicp_probe(device_t dev) { if (ofw_bus_get_name(dev) == NULL || strcmp(ofw_bus_get_name(dev), "interrupt-controller") != 0) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ibm,ppc-xicp")) return (ENXIO); device_set_desc(dev, "PAPR virtual interrupt controller"); return (BUS_PROBE_GENERIC); } static int xics_probe(device_t dev) { if (ofw_bus_get_name(dev) == NULL || strcmp(ofw_bus_get_name(dev), "interrupt-controller") != 0) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ibm,ppc-xics")) return (ENXIO); device_set_desc(dev, "PAPR virtual interrupt source"); return (BUS_PROBE_GENERIC); } static int xicp_attach(device_t dev) { struct xicp_softc *sc = device_get_softc(dev); phandle_t phandle = ofw_bus_get_node(dev); mtx_init(&sc->sc_mtx, "XICP", NULL, MTX_DEF); sc->nintvecs = 0; sc->ibm_int_on = rtas_token_lookup("ibm,int-on"); sc->ibm_int_off = rtas_token_lookup("ibm,int-off"); sc->ibm_set_xive = rtas_token_lookup("ibm,set-xive"); sc->ibm_get_xive = rtas_token_lookup("ibm,get-xive"); powerpc_register_pic(dev, OF_xref_from_node(phandle), MAX_XICP_IRQS, 1 /* Number of IPIs */, FALSE); root_pic = dev; return (0); } static int xics_attach(device_t dev) { phandle_t phandle = ofw_bus_get_node(dev); /* The XICP (root PIC) will handle all our interrupts */ powerpc_register_pic(root_pic, OF_xref_from_node(phandle), MAX_XICP_IRQS, 1 /* Number of IPIs */, FALSE); return (0); } /* * PIC I/F methods. */ static void xicp_bind(device_t dev, u_int irq, cpuset_t cpumask) { struct xicp_softc *sc = device_get_softc(dev); cell_t status, cpu; int ncpus, i, error; /* * This doesn't appear to actually support affinity groups, so pick a * random CPU. */ ncpus = 0; CPU_FOREACH(cpu) if (CPU_ISSET(cpu, &cpumask)) ncpus++; i = mftb() % ncpus; ncpus = 0; CPU_FOREACH(cpu) { if (!CPU_ISSET(cpu, &cpumask)) continue; if (ncpus == i) break; ncpus++; } error = rtas_call_method(sc->ibm_set_xive, 3, 1, irq, cpu, XICP_PRIORITY, &status); if (error < 0) panic("Cannot bind interrupt %d to CPU %d", irq, cpu); } static void xicp_dispatch(device_t dev, struct trapframe *tf) { struct xicp_softc *sc; uint64_t xirr, junk; int i; sc = device_get_softc(dev); for (;;) { /* Return value in R4, use the PFT call */ phyp_pft_hcall(H_XIRR, 0, 0, 0, 0, &xirr, &junk, &junk); xirr &= 0x00ffffff; if (xirr == 0) { /* No more pending interrupts? */ phyp_hcall(H_CPPR, (uint64_t)0xff); break; } if (xirr == XICP_IPI) { /* Magic number for IPIs */ xirr = MAX_XICP_IRQS; /* Map to FreeBSD magic */ phyp_hcall(H_IPI, (uint64_t)(PCPU_GET(cpuid)), 0xff); /* Clear IPI */ } /* XXX: super inefficient */ for (i = 0; i < sc->nintvecs; i++) { if (sc->intvecs[i].irq == xirr) break; } KASSERT(i < sc->nintvecs, ("Unmapped XIRR")); powerpc_dispatch_intr(sc->intvecs[i].vector, tf); } } static void xicp_enable(device_t dev, u_int irq, u_int vector) { struct xicp_softc *sc; cell_t status, cpu; sc = device_get_softc(dev); KASSERT(sc->nintvecs + 1 < nitems(sc->intvecs), ("Too many XICP interrupts")); mtx_lock(&sc->sc_mtx); sc->intvecs[sc->nintvecs].irq = irq; sc->intvecs[sc->nintvecs].vector = vector; mb(); sc->nintvecs++; mtx_unlock(&sc->sc_mtx); /* IPIs are also enabled */ if (irq == MAX_XICP_IRQS) return; /* Bind to this CPU to start: distrib. ID is last entry in gserver# */ cpu = PCPU_GET(cpuid); rtas_call_method(sc->ibm_set_xive, 3, 1, irq, cpu, XICP_PRIORITY, &status); xicp_unmask(dev, irq); } static void xicp_eoi(device_t dev, u_int irq) { uint64_t xirr; if (irq == MAX_XICP_IRQS) /* Remap IPI interrupt to internal value */ irq = XICP_IPI; xirr = irq | (XICP_PRIORITY << 24); phyp_hcall(H_EOI, xirr); } static void xicp_ipi(device_t dev, u_int cpu) { phyp_hcall(H_IPI, (uint64_t)cpu, XICP_PRIORITY); } static void xicp_mask(device_t dev, u_int irq) { struct xicp_softc *sc = device_get_softc(dev); cell_t status; if (irq == MAX_XICP_IRQS) return; rtas_call_method(sc->ibm_int_off, 1, 1, irq, &status); } static void xicp_unmask(device_t dev, u_int irq) { struct xicp_softc *sc = device_get_softc(dev); cell_t status; if (irq == MAX_XICP_IRQS) return; rtas_call_method(sc->ibm_int_on, 1, 1, irq, &status); } Index: head/sys/powerpc/psim/ata_iobus.c =================================================================== --- head/sys/powerpc/psim/ata_iobus.c (revision 326260) +++ head/sys/powerpc/psim/ata_iobus.c (revision 326261) @@ -1,256 +1,258 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2002 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * PSIM local bus ATA controller */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Define the iobus ata bus attachment. This creates a pseudo-bus that * the ATA device can be attached to */ static int ata_iobus_attach(device_t dev); static int ata_iobus_probe(device_t dev); static int ata_iobus_print_child(device_t dev, device_t child); struct resource *ata_iobus_alloc_resource(device_t, device_t, int, int *, rman_res_t, rman_res_t, rman_res_t, u_int); static int ata_iobus_release_resource(device_t, device_t, int, int, struct resource *); static device_method_t ata_iobus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ata_iobus_probe), DEVMETHOD(device_attach, ata_iobus_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus methods */ DEVMETHOD(bus_print_child, ata_iobus_print_child), DEVMETHOD(bus_alloc_resource, ata_iobus_alloc_resource), DEVMETHOD(bus_release_resource, ata_iobus_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD_END }; static driver_t ata_iobus_driver = { "ataiobus", ata_iobus_methods, 0, }; static devclass_t ata_iobus_devclass; DRIVER_MODULE(ataiobus, iobus, ata_iobus_driver, ata_iobus_devclass, NULL, NULL); MODULE_DEPEND(ata, ata, 1, 1, 1); static int ata_iobus_probe(device_t dev) { char *type = iobus_get_name(dev); if (strncmp(type, "ata", 3) != 0) return (ENXIO); device_set_desc(dev, "PSIM ATA Controller"); return (0); } static int ata_iobus_attach(device_t dev) { /* * Add a single child per controller. Should be able * to add two */ device_add_child(dev, "ata", -1); return (bus_generic_attach(dev)); } static int ata_iobus_print_child(device_t dev, device_t child) { int retval = 0; retval += bus_print_child_header(dev, child); /* irq ? */ retval += bus_print_child_footer(dev, child); return (retval); } struct resource * ata_iobus_alloc_resource(device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct resource *res = NULL; int myrid; u_int *ofw_regs; ofw_regs = iobus_get_regs(dev); /* * The reg array for the PSIM ata device has 6 start/size entries: * 0 - unused * 1/2/3 - unused * 4/5/6 - primary command * 7/8/9 - secondary command * 10/11/12 - primary control * 13/14/15 - secondary control * 16/17/18 - primary/secondary dma registers, unimplemented * * The resource values are calculated from these registers */ if (type == SYS_RES_IOPORT) { switch (*rid) { case ATA_IOADDR_RID: myrid = 0; start = ofw_regs[4]; end = start + ATA_IOSIZE - 1; count = ATA_IOSIZE; res = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, SYS_RES_MEMORY, &myrid, start, end, count, flags); break; case ATA_CTLADDR_RID: myrid = 0; start = ofw_regs[10]; end = start + ATA_CTLIOSIZE - 1; count = ATA_CTLIOSIZE; res = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, SYS_RES_MEMORY, &myrid, start, end, count, flags); break; case ATA_BMADDR_RID: /* DMA not properly supported by psim */ break; } return (res); } else if (type == SYS_RES_IRQ && *rid == ATA_IRQ_RID) { /* * Pass this on to the parent */ return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, SYS_RES_IRQ, rid, 0, ~0, 1, flags)); } else { return (NULL); } } static int ata_iobus_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { /* no hotplug... */ return (0); } /* * Define the actual ATA device. This is a sub-bus to the ata-iobus layer * to allow the higher layer bus to massage the resource allocation. */ static int ata_iobus_sub_probe(device_t dev); static int ata_iobus_sub_setmode(device_t dev, int target, int mode); static device_method_t ata_iobus_sub_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ata_iobus_sub_probe), DEVMETHOD(device_attach, ata_attach), DEVMETHOD(device_detach, ata_detach), DEVMETHOD(device_resume, ata_resume), /* ATA interface */ DEVMETHOD(ata_setmode, ata_iobus_sub_setmode), DEVMETHOD_END }; static driver_t ata_iobus_sub_driver = { "ata", ata_iobus_sub_methods, sizeof(struct ata_channel), }; DRIVER_MODULE(ata, ataiobus, ata_iobus_sub_driver, ata_devclass, NULL, NULL); static int ata_iobus_sub_probe(device_t dev) { struct ata_channel *ch = device_get_softc(dev); /* Only a single unit per controller thus far */ ch->unit = 0; ch->flags = (ATA_USE_16BIT|ATA_NO_SLAVE); ata_generic_hw(dev); return ata_probe(dev); } static int ata_iobus_sub_setmode(device_t parent, int target, int mode) { /* Only ever PIO mode here... */ return (ATA_PIO); } Index: head/sys/powerpc/psim/iobus.c =================================================================== --- head/sys/powerpc/psim/iobus.c (revision 326260) +++ head/sys/powerpc/psim/iobus.c (revision 326261) @@ -1,413 +1,415 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2002 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * PSIM 'iobus' local bus. Should be set up in the device tree like: * * /iobus@0x80000000/name psim-iobus * * Code borrowed from various nexus.c and uninorth.c :-) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct iobus_softc { phandle_t sc_node; vm_offset_t sc_addr; vm_offset_t sc_size; struct rman sc_mem_rman; }; static MALLOC_DEFINE(M_IOBUS, "iobus", "iobus device information"); static int iobus_probe(device_t); static int iobus_attach(device_t); static int iobus_print_child(device_t dev, device_t child); static void iobus_probe_nomatch(device_t, device_t); static int iobus_read_ivar(device_t, device_t, int, uintptr_t *); static int iobus_write_ivar(device_t, device_t, int, uintptr_t); static struct resource *iobus_alloc_resource(device_t, device_t, int, int *, rman_res_t, rman_res_t, rman_res_t, u_int); static int iobus_activate_resource(device_t, device_t, int, int, struct resource *); static int iobus_deactivate_resource(device_t, device_t, int, int, struct resource *); static int iobus_release_resource(device_t, device_t, int, int, struct resource *); /* * Bus interface definition */ static device_method_t iobus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, iobus_probe), DEVMETHOD(device_attach, iobus_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, iobus_print_child), DEVMETHOD(bus_probe_nomatch, iobus_probe_nomatch), DEVMETHOD(bus_read_ivar, iobus_read_ivar), DEVMETHOD(bus_write_ivar, iobus_write_ivar), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, iobus_alloc_resource), DEVMETHOD(bus_release_resource, iobus_release_resource), DEVMETHOD(bus_activate_resource, iobus_activate_resource), DEVMETHOD(bus_deactivate_resource, iobus_deactivate_resource), { 0, 0 } }; static driver_t iobus_driver = { "iobus", iobus_methods, sizeof(struct iobus_softc) }; devclass_t iobus_devclass; DRIVER_MODULE(iobus, ofwbus, iobus_driver, iobus_devclass, 0, 0); static int iobus_probe(device_t dev) { const char *type = ofw_bus_get_name(dev); if (strcmp(type, "psim-iobus") != 0) return (ENXIO); device_set_desc(dev, "PSIM local bus"); return (0); } /* * Add interrupt/addr range to the dev's resource list if present */ static void iobus_add_intr(phandle_t devnode, struct iobus_devinfo *dinfo) { u_int intr = -1; if (OF_getprop(devnode, "interrupt", &intr, sizeof(intr)) != -1) { resource_list_add(&dinfo->id_resources, SYS_RES_IRQ, 0, intr, intr, 1); } dinfo->id_interrupt = intr; } static void iobus_add_reg(phandle_t devnode, struct iobus_devinfo *dinfo, vm_offset_t iobus_off) { u_int size; int i; size = OF_getprop(devnode, "reg", dinfo->id_reg,sizeof(dinfo->id_reg)); if (size != -1) { dinfo->id_nregs = size / (sizeof(dinfo->id_reg[0])); for (i = 0; i < dinfo->id_nregs; i+= 3) { /* * Scale the absolute addresses back to iobus * relative offsets. This is to better simulate * macio */ dinfo->id_reg[i+1] -= iobus_off; resource_list_add(&dinfo->id_resources, SYS_RES_MEMORY, 0, dinfo->id_reg[i+1], dinfo->id_reg[i+1] + dinfo->id_reg[i+2], dinfo->id_reg[i+2]); } } } static int iobus_attach(device_t dev) { struct iobus_softc *sc; struct iobus_devinfo *dinfo; phandle_t root; phandle_t child; device_t cdev; char *name; u_int reg[2]; int size; sc = device_get_softc(dev); sc->sc_node = ofw_bus_get_node(dev); /* * Find the base addr/size of the iobus, and initialize the * resource manager */ size = OF_getprop(sc->sc_node, "reg", reg, sizeof(reg)); if (size == sizeof(reg)) { sc->sc_addr = reg[0]; sc->sc_size = reg[1]; } else { return (ENXIO); } sc->sc_mem_rman.rm_type = RMAN_ARRAY; sc->sc_mem_rman.rm_descr = "IOBus Device Memory"; if (rman_init(&sc->sc_mem_rman) != 0) { device_printf(dev, "failed to init mem range resources\n"); return (ENXIO); } rman_manage_region(&sc->sc_mem_rman, 0, sc->sc_size); /* * Iterate through the sub-devices */ root = sc->sc_node; for (child = OF_child(root); child != 0; child = OF_peer(child)) { OF_getprop_alloc(child, "name", 1, (void **)&name); cdev = device_add_child(dev, NULL, -1); if (cdev != NULL) { dinfo = malloc(sizeof(*dinfo), M_IOBUS, M_WAITOK); memset(dinfo, 0, sizeof(*dinfo)); resource_list_init(&dinfo->id_resources); dinfo->id_node = child; dinfo->id_name = name; iobus_add_intr(child, dinfo); iobus_add_reg(child, dinfo, sc->sc_addr); device_set_ivars(cdev, dinfo); } else { OF_prop_free(name); } } return (bus_generic_attach(dev)); } static int iobus_print_child(device_t dev, device_t child) { struct iobus_devinfo *dinfo; struct resource_list *rl; int retval = 0; dinfo = device_get_ivars(child); rl = &dinfo->id_resources; retval += bus_print_child_header(dev, child); retval += printf(" offset 0x%x", dinfo->id_reg[1]); retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); retval += bus_print_child_footer(dev, child); return (retval); } static void iobus_probe_nomatch(device_t dev, device_t child) { } static int iobus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct iobus_devinfo *dinfo; if ((dinfo = device_get_ivars(child)) == NULL) return (ENOENT); switch (which) { case IOBUS_IVAR_NODE: *result = dinfo->id_node; break; case IOBUS_IVAR_NAME: *result = (uintptr_t)dinfo->id_name; break; case IOBUS_IVAR_NREGS: *result = dinfo->id_nregs; break; case IOBUS_IVAR_REGS: *result = (uintptr_t)dinfo->id_reg; break; default: return (ENOENT); } return (0); } static int iobus_write_ivar(device_t dev, device_t child, int which, uintptr_t value) { return (EINVAL); } static struct resource * iobus_alloc_resource(device_t bus, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct iobus_softc *sc; int needactivate; struct resource *rv; struct rman *rm; sc = device_get_softc(bus); needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; switch (type) { case SYS_RES_MEMORY: case SYS_RES_IOPORT: rm = &sc->sc_mem_rman; break; case SYS_RES_IRQ: return (bus_alloc_resource(bus, type, rid, start, end, count, flags)); default: device_printf(bus, "unknown resource request from %s\n", device_get_nameunit(child)); return (NULL); } rv = rman_reserve_resource(rm, start, end, count, flags, child); if (rv == NULL) { device_printf(bus, "failed to reserve resource for %s\n", device_get_nameunit(child)); return (NULL); } rman_set_rid(rv, *rid); if (needactivate) { if (bus_activate_resource(child, type, *rid, rv) != 0) { device_printf(bus, "failed to activate resource for %s\n", device_get_nameunit(child)); rman_release_resource(rv); return (NULL); } } return (rv); } static int iobus_release_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { if (rman_get_flags(res) & RF_ACTIVE) { int error = bus_deactivate_resource(child, type, rid, res); if (error) return error; } return (rman_release_resource(res)); } static int iobus_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { struct iobus_softc *sc; void *p; sc = device_get_softc(bus); if (type == SYS_RES_IRQ) return (bus_activate_resource(bus, type, rid, res)); if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { p = pmap_mapdev((vm_offset_t)rman_get_start(res) + sc->sc_addr, (vm_size_t)rman_get_size(res)); if (p == NULL) return (ENOMEM); rman_set_virtual(res, p); rman_set_bustag(res, &bs_le_tag); rman_set_bushandle(res, (u_long)p); } return (rman_activate_resource(res)); } static int iobus_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { /* * If this is a memory resource, unmap it. */ if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { u_int32_t psize; psize = rman_get_size(res); pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize); } return (rman_deactivate_resource(res)); } Index: head/sys/powerpc/psim/iobusvar.h =================================================================== --- head/sys/powerpc/psim/iobusvar.h (revision 326260) +++ head/sys/powerpc/psim/iobusvar.h (revision 326261) @@ -1,66 +1,68 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2002 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _PSIM_IOBUSVAR_H_ #define _PSIM_IOBUSVAR_H_ /* * Accessors for iobus devices */ enum iobus_ivars { IOBUS_IVAR_NODE, IOBUS_IVAR_NAME, IOBUS_IVAR_NREGS, IOBUS_IVAR_REGS, }; #define IOBUS_ACCESSOR(var, ivar, type) \ __BUS_ACCESSOR(iobus, var, IOBUS, ivar, type) IOBUS_ACCESSOR(node, NODE, phandle_t) IOBUS_ACCESSOR(name, NAME, char *) IOBUS_ACCESSOR(nregs, NREGS, u_int) IOBUS_ACCESSOR(regs, REGS, u_int *) #undef IOBUS_ACCESSOR /* * Per-device structure. */ struct iobus_devinfo { phandle_t id_node; char *id_name; int id_interrupt; u_int id_nregs; u_int id_reg[24]; struct resource_list id_resources; }; #endif /* _PSIM_IOBUSVAR_H_ */ Index: head/sys/powerpc/psim/openpic_iobus.c =================================================================== --- head/sys/powerpc/psim/openpic_iobus.c (revision 326260) +++ head/sys/powerpc/psim/openpic_iobus.c (revision 326261) @@ -1,120 +1,122 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2003 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * The psim iobus attachment for the OpenPIC interrupt controller. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" /* * PSIM IOBus interface */ static int openpic_iobus_probe(device_t); static int openpic_iobus_attach(device_t); static device_method_t openpic_iobus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, openpic_iobus_probe), DEVMETHOD(device_attach, openpic_iobus_attach), /* PIC interface */ DEVMETHOD(pic_config, openpic_config), DEVMETHOD(pic_dispatch, openpic_dispatch), DEVMETHOD(pic_enable, openpic_enable), DEVMETHOD(pic_eoi, openpic_eoi), DEVMETHOD(pic_ipi, openpic_ipi), DEVMETHOD(pic_mask, openpic_mask), DEVMETHOD(pic_unmask, openpic_unmask), { 0, 0 } }; static driver_t openpic_iobus_driver = { "openpic", openpic_iobus_methods, sizeof(struct openpic_softc) }; DRIVER_MODULE(openpic, iobus, openpic_iobus_driver, openpic_devclass, 0, 0); static int openpic_iobus_probe(device_t dev) { struct openpic_softc *sc; char *name; name = iobus_get_name(dev); if (strcmp(name, "interrupt-controller") != 0) return (ENXIO); /* * The description was already printed out in the nexus * probe, so don't do it again here */ device_set_desc(dev, OPENPIC_DEVSTR); sc = device_get_softc(dev); sc->sc_psim = 1; return (0); } static int openpic_iobus_attach(device_t dev) { return (openpic_common_attach(dev, 0)); } Index: head/sys/powerpc/psim/uart_iobus.c =================================================================== --- head/sys/powerpc/psim/uart_iobus.c (revision 326260) +++ head/sys/powerpc/psim/uart_iobus.c (revision 326261) @@ -1,87 +1,89 @@ /*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 2002 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * PSIM local bus 16550 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int uart_iobus_probe(device_t dev); static device_method_t uart_iobus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_iobus_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), { 0, 0 } }; static driver_t uart_iobus_driver = { uart_driver_name, uart_iobus_methods, sizeof(struct uart_softc), }; static int uart_iobus_probe(device_t dev) { struct uart_softc *sc; char *type; type = iobus_get_name(dev); if (strncmp(type, "com", 3) != 0) return (ENXIO); sc = device_get_softc(dev); sc->sc_class = &uart_ns8250_class; device_set_desc(dev, "PSIM serial port"); return (uart_bus_probe(dev, 0, 0, 0, 0, 0)); } DRIVER_MODULE(uart, iobus, uart_iobus_driver, uart_devclass, 0, 0);