162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PS3 address space management. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2006 Sony Computer Entertainment Inc. 662306a36Sopenharmony_ci * Copyright 2006 Sony Corp. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/export.h> 1262306a36Sopenharmony_ci#include <linux/memblock.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <asm/cell-regs.h> 1662306a36Sopenharmony_ci#include <asm/firmware.h> 1762306a36Sopenharmony_ci#include <asm/udbg.h> 1862306a36Sopenharmony_ci#include <asm/lv1call.h> 1962306a36Sopenharmony_ci#include <asm/setup.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "platform.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#if defined(DEBUG) 2462306a36Sopenharmony_ci#define DBG udbg_printf 2562306a36Sopenharmony_ci#else 2662306a36Sopenharmony_ci#define DBG pr_devel 2762306a36Sopenharmony_ci#endif 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cienum { 3062306a36Sopenharmony_ci#if defined(CONFIG_PS3_DYNAMIC_DMA) 3162306a36Sopenharmony_ci USE_DYNAMIC_DMA = 1, 3262306a36Sopenharmony_ci#else 3362306a36Sopenharmony_ci USE_DYNAMIC_DMA = 0, 3462306a36Sopenharmony_ci#endif 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cienum { 3862306a36Sopenharmony_ci PAGE_SHIFT_4K = 12U, 3962306a36Sopenharmony_ci PAGE_SHIFT_64K = 16U, 4062306a36Sopenharmony_ci PAGE_SHIFT_16M = 24U, 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic unsigned long __init make_page_sizes(unsigned long a, unsigned long b) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci return (a << 56) | (b << 48); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cienum { 4962306a36Sopenharmony_ci ALLOCATE_MEMORY_TRY_ALT_UNIT = 0X04, 5062306a36Sopenharmony_ci ALLOCATE_MEMORY_ADDR_ZERO = 0X08, 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* valid htab sizes are {18,19,20} = 256K, 512K, 1M */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cienum { 5662306a36Sopenharmony_ci HTAB_SIZE_MAX = 20U, /* HV limit of 1MB */ 5762306a36Sopenharmony_ci HTAB_SIZE_MIN = 18U, /* CPU limit of 256KB */ 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/*============================================================================*/ 6162306a36Sopenharmony_ci/* virtual address space routines */ 6262306a36Sopenharmony_ci/*============================================================================*/ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/** 6562306a36Sopenharmony_ci * struct mem_region - memory region structure 6662306a36Sopenharmony_ci * @base: base address 6762306a36Sopenharmony_ci * @size: size in bytes 6862306a36Sopenharmony_ci * @offset: difference between base and rm.size 6962306a36Sopenharmony_ci * @destroy: flag if region should be destroyed upon shutdown 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct mem_region { 7362306a36Sopenharmony_ci u64 base; 7462306a36Sopenharmony_ci u64 size; 7562306a36Sopenharmony_ci unsigned long offset; 7662306a36Sopenharmony_ci int destroy; 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/** 8062306a36Sopenharmony_ci * struct map - address space state variables holder 8162306a36Sopenharmony_ci * @total: total memory available as reported by HV 8262306a36Sopenharmony_ci * @vas_id - HV virtual address space id 8362306a36Sopenharmony_ci * @htab_size: htab size in bytes 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * The HV virtual address space (vas) allows for hotplug memory regions. 8662306a36Sopenharmony_ci * Memory regions can be created and destroyed in the vas at runtime. 8762306a36Sopenharmony_ci * @rm: real mode (bootmem) region 8862306a36Sopenharmony_ci * @r1: highmem region(s) 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * ps3 addresses 9162306a36Sopenharmony_ci * virt_addr: a cpu 'translated' effective address 9262306a36Sopenharmony_ci * phys_addr: an address in what Linux thinks is the physical address space 9362306a36Sopenharmony_ci * lpar_addr: an address in the HV virtual address space 9462306a36Sopenharmony_ci * bus_addr: an io controller 'translated' address on a device bus 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistruct map { 9862306a36Sopenharmony_ci u64 total; 9962306a36Sopenharmony_ci u64 vas_id; 10062306a36Sopenharmony_ci u64 htab_size; 10162306a36Sopenharmony_ci struct mem_region rm; 10262306a36Sopenharmony_ci struct mem_region r1; 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#define debug_dump_map(x) _debug_dump_map(x, __func__, __LINE__) 10662306a36Sopenharmony_cistatic void __maybe_unused _debug_dump_map(const struct map *m, 10762306a36Sopenharmony_ci const char *func, int line) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci DBG("%s:%d: map.total = %llxh\n", func, line, m->total); 11062306a36Sopenharmony_ci DBG("%s:%d: map.rm.size = %llxh\n", func, line, m->rm.size); 11162306a36Sopenharmony_ci DBG("%s:%d: map.vas_id = %llu\n", func, line, m->vas_id); 11262306a36Sopenharmony_ci DBG("%s:%d: map.htab_size = %llxh\n", func, line, m->htab_size); 11362306a36Sopenharmony_ci DBG("%s:%d: map.r1.base = %llxh\n", func, line, m->r1.base); 11462306a36Sopenharmony_ci DBG("%s:%d: map.r1.offset = %lxh\n", func, line, m->r1.offset); 11562306a36Sopenharmony_ci DBG("%s:%d: map.r1.size = %llxh\n", func, line, m->r1.size); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic struct map map; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/** 12162306a36Sopenharmony_ci * ps3_mm_phys_to_lpar - translate a linux physical address to lpar address 12262306a36Sopenharmony_ci * @phys_addr: linux physical address 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ciunsigned long ps3_mm_phys_to_lpar(unsigned long phys_addr) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci BUG_ON(is_kernel_addr(phys_addr)); 12862306a36Sopenharmony_ci return (phys_addr < map.rm.size || phys_addr >= map.total) 12962306a36Sopenharmony_ci ? phys_addr : phys_addr + map.r1.offset; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ciEXPORT_SYMBOL(ps3_mm_phys_to_lpar); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/** 13562306a36Sopenharmony_ci * ps3_mm_vas_create - create the virtual address space 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_civoid __init ps3_mm_vas_create(unsigned long* htab_size) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci int result; 14162306a36Sopenharmony_ci u64 start_address; 14262306a36Sopenharmony_ci u64 size; 14362306a36Sopenharmony_ci u64 access_right; 14462306a36Sopenharmony_ci u64 max_page_size; 14562306a36Sopenharmony_ci u64 flags; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci result = lv1_query_logical_partition_address_region_info(0, 14862306a36Sopenharmony_ci &start_address, &size, &access_right, &max_page_size, 14962306a36Sopenharmony_ci &flags); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (result) { 15262306a36Sopenharmony_ci DBG("%s:%d: lv1_query_logical_partition_address_region_info " 15362306a36Sopenharmony_ci "failed: %s\n", __func__, __LINE__, 15462306a36Sopenharmony_ci ps3_result(result)); 15562306a36Sopenharmony_ci goto fail; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (max_page_size < PAGE_SHIFT_16M) { 15962306a36Sopenharmony_ci DBG("%s:%d: bad max_page_size %llxh\n", __func__, __LINE__, 16062306a36Sopenharmony_ci max_page_size); 16162306a36Sopenharmony_ci goto fail; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci BUILD_BUG_ON(CONFIG_PS3_HTAB_SIZE > HTAB_SIZE_MAX); 16562306a36Sopenharmony_ci BUILD_BUG_ON(CONFIG_PS3_HTAB_SIZE < HTAB_SIZE_MIN); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci result = lv1_construct_virtual_address_space(CONFIG_PS3_HTAB_SIZE, 16862306a36Sopenharmony_ci 2, make_page_sizes(PAGE_SHIFT_16M, PAGE_SHIFT_64K), 16962306a36Sopenharmony_ci &map.vas_id, &map.htab_size); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (result) { 17262306a36Sopenharmony_ci DBG("%s:%d: lv1_construct_virtual_address_space failed: %s\n", 17362306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 17462306a36Sopenharmony_ci goto fail; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci result = lv1_select_virtual_address_space(map.vas_id); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (result) { 18062306a36Sopenharmony_ci DBG("%s:%d: lv1_select_virtual_address_space failed: %s\n", 18162306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 18262306a36Sopenharmony_ci goto fail; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci *htab_size = map.htab_size; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci debug_dump_map(&map); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cifail: 19262306a36Sopenharmony_ci panic("ps3_mm_vas_create failed"); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/** 19662306a36Sopenharmony_ci * ps3_mm_vas_destroy - 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * called during kexec sequence with MMU off. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cinotrace void ps3_mm_vas_destroy(void) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci int result; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (map.vas_id) { 20662306a36Sopenharmony_ci result = lv1_select_virtual_address_space(0); 20762306a36Sopenharmony_ci result += lv1_destruct_virtual_address_space(map.vas_id); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (result) { 21062306a36Sopenharmony_ci lv1_panic(0); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci map.vas_id = 0; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic int __init ps3_mm_get_repository_highmem(struct mem_region *r) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci int result; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Assume a single highmem region. */ 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci result = ps3_repository_read_highmem_info(0, &r->base, &r->size); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (result) 22662306a36Sopenharmony_ci goto zero_region; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (!r->base || !r->size) { 22962306a36Sopenharmony_ci result = -1; 23062306a36Sopenharmony_ci goto zero_region; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci r->offset = r->base - map.rm.size; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci DBG("%s:%d: Found high region in repository: %llxh %llxh\n", 23662306a36Sopenharmony_ci __func__, __LINE__, r->base, r->size); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cizero_region: 24162306a36Sopenharmony_ci DBG("%s:%d: No high region in repository.\n", __func__, __LINE__); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci r->size = r->base = r->offset = 0; 24462306a36Sopenharmony_ci return result; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int ps3_mm_set_repository_highmem(const struct mem_region *r) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci /* Assume a single highmem region. */ 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return r ? ps3_repository_write_highmem_info(0, r->base, r->size) : 25262306a36Sopenharmony_ci ps3_repository_write_highmem_info(0, 0, 0); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/** 25662306a36Sopenharmony_ci * ps3_mm_region_create - create a memory region in the vas 25762306a36Sopenharmony_ci * @r: pointer to a struct mem_region to accept initialized values 25862306a36Sopenharmony_ci * @size: requested region size 25962306a36Sopenharmony_ci * 26062306a36Sopenharmony_ci * This implementation creates the region with the vas large page size. 26162306a36Sopenharmony_ci * @size is rounded down to a multiple of the vas large page size. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int ps3_mm_region_create(struct mem_region *r, unsigned long size) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci int result; 26762306a36Sopenharmony_ci u64 muid; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci r->size = ALIGN_DOWN(size, 1 << PAGE_SHIFT_16M); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci DBG("%s:%d requested %lxh\n", __func__, __LINE__, size); 27262306a36Sopenharmony_ci DBG("%s:%d actual %llxh\n", __func__, __LINE__, r->size); 27362306a36Sopenharmony_ci DBG("%s:%d difference %llxh (%lluMB)\n", __func__, __LINE__, 27462306a36Sopenharmony_ci size - r->size, (size - r->size) / 1024 / 1024); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (r->size == 0) { 27762306a36Sopenharmony_ci DBG("%s:%d: size == 0\n", __func__, __LINE__); 27862306a36Sopenharmony_ci result = -1; 27962306a36Sopenharmony_ci goto zero_region; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci result = lv1_allocate_memory(r->size, PAGE_SHIFT_16M, 0, 28362306a36Sopenharmony_ci ALLOCATE_MEMORY_TRY_ALT_UNIT, &r->base, &muid); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (result || r->base < map.rm.size) { 28662306a36Sopenharmony_ci DBG("%s:%d: lv1_allocate_memory failed: %s\n", 28762306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 28862306a36Sopenharmony_ci goto zero_region; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci r->destroy = 1; 29262306a36Sopenharmony_ci r->offset = r->base - map.rm.size; 29362306a36Sopenharmony_ci return result; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cizero_region: 29662306a36Sopenharmony_ci r->size = r->base = r->offset = 0; 29762306a36Sopenharmony_ci return result; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci/** 30162306a36Sopenharmony_ci * ps3_mm_region_destroy - destroy a memory region 30262306a36Sopenharmony_ci * @r: pointer to struct mem_region 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic void ps3_mm_region_destroy(struct mem_region *r) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci int result; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (!r->destroy) { 31062306a36Sopenharmony_ci return; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (r->base) { 31462306a36Sopenharmony_ci result = lv1_release_memory(r->base); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (result) { 31762306a36Sopenharmony_ci lv1_panic(0); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci r->size = r->base = r->offset = 0; 32162306a36Sopenharmony_ci map.total = map.rm.size; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci ps3_mm_set_repository_highmem(NULL); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/*============================================================================*/ 32862306a36Sopenharmony_ci/* dma routines */ 32962306a36Sopenharmony_ci/*============================================================================*/ 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci/** 33262306a36Sopenharmony_ci * dma_sb_lpar_to_bus - Translate an lpar address to ioc mapped bus address. 33362306a36Sopenharmony_ci * @r: pointer to dma region structure 33462306a36Sopenharmony_ci * @lpar_addr: HV lpar address 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic unsigned long dma_sb_lpar_to_bus(struct ps3_dma_region *r, 33862306a36Sopenharmony_ci unsigned long lpar_addr) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci if (lpar_addr >= map.rm.size) 34162306a36Sopenharmony_ci lpar_addr -= map.r1.offset; 34262306a36Sopenharmony_ci BUG_ON(lpar_addr < r->offset); 34362306a36Sopenharmony_ci BUG_ON(lpar_addr >= r->offset + r->len); 34462306a36Sopenharmony_ci return r->bus_addr + lpar_addr - r->offset; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci#define dma_dump_region(_a) _dma_dump_region(_a, __func__, __LINE__) 34862306a36Sopenharmony_cistatic void __maybe_unused _dma_dump_region(const struct ps3_dma_region *r, 34962306a36Sopenharmony_ci const char *func, int line) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci DBG("%s:%d: dev %llu:%llu\n", func, line, r->dev->bus_id, 35262306a36Sopenharmony_ci r->dev->dev_id); 35362306a36Sopenharmony_ci DBG("%s:%d: page_size %u\n", func, line, r->page_size); 35462306a36Sopenharmony_ci DBG("%s:%d: bus_addr %lxh\n", func, line, r->bus_addr); 35562306a36Sopenharmony_ci DBG("%s:%d: len %lxh\n", func, line, r->len); 35662306a36Sopenharmony_ci DBG("%s:%d: offset %lxh\n", func, line, r->offset); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /** 36062306a36Sopenharmony_ci * dma_chunk - A chunk of dma pages mapped by the io controller. 36162306a36Sopenharmony_ci * @region - The dma region that owns this chunk. 36262306a36Sopenharmony_ci * @lpar_addr: Starting lpar address of the area to map. 36362306a36Sopenharmony_ci * @bus_addr: Starting ioc bus address of the area to map. 36462306a36Sopenharmony_ci * @len: Length in bytes of the area to map. 36562306a36Sopenharmony_ci * @link: A struct list_head used with struct ps3_dma_region.chunk_list, the 36662306a36Sopenharmony_ci * list of all chunks owned by the region. 36762306a36Sopenharmony_ci * 36862306a36Sopenharmony_ci * This implementation uses a very simple dma page manager 36962306a36Sopenharmony_ci * based on the dma_chunk structure. This scheme assumes 37062306a36Sopenharmony_ci * that all drivers use very well behaved dma ops. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistruct dma_chunk { 37462306a36Sopenharmony_ci struct ps3_dma_region *region; 37562306a36Sopenharmony_ci unsigned long lpar_addr; 37662306a36Sopenharmony_ci unsigned long bus_addr; 37762306a36Sopenharmony_ci unsigned long len; 37862306a36Sopenharmony_ci struct list_head link; 37962306a36Sopenharmony_ci unsigned int usage_count; 38062306a36Sopenharmony_ci}; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci#define dma_dump_chunk(_a) _dma_dump_chunk(_a, __func__, __LINE__) 38362306a36Sopenharmony_cistatic void _dma_dump_chunk (const struct dma_chunk* c, const char* func, 38462306a36Sopenharmony_ci int line) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci DBG("%s:%d: r.dev %llu:%llu\n", func, line, 38762306a36Sopenharmony_ci c->region->dev->bus_id, c->region->dev->dev_id); 38862306a36Sopenharmony_ci DBG("%s:%d: r.bus_addr %lxh\n", func, line, c->region->bus_addr); 38962306a36Sopenharmony_ci DBG("%s:%d: r.page_size %u\n", func, line, c->region->page_size); 39062306a36Sopenharmony_ci DBG("%s:%d: r.len %lxh\n", func, line, c->region->len); 39162306a36Sopenharmony_ci DBG("%s:%d: r.offset %lxh\n", func, line, c->region->offset); 39262306a36Sopenharmony_ci DBG("%s:%d: c.lpar_addr %lxh\n", func, line, c->lpar_addr); 39362306a36Sopenharmony_ci DBG("%s:%d: c.bus_addr %lxh\n", func, line, c->bus_addr); 39462306a36Sopenharmony_ci DBG("%s:%d: c.len %lxh\n", func, line, c->len); 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic struct dma_chunk * dma_find_chunk(struct ps3_dma_region *r, 39862306a36Sopenharmony_ci unsigned long bus_addr, unsigned long len) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct dma_chunk *c; 40162306a36Sopenharmony_ci unsigned long aligned_bus = ALIGN_DOWN(bus_addr, 1 << r->page_size); 40262306a36Sopenharmony_ci unsigned long aligned_len = ALIGN(len+bus_addr-aligned_bus, 40362306a36Sopenharmony_ci 1 << r->page_size); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci list_for_each_entry(c, &r->chunk_list.head, link) { 40662306a36Sopenharmony_ci /* intersection */ 40762306a36Sopenharmony_ci if (aligned_bus >= c->bus_addr && 40862306a36Sopenharmony_ci aligned_bus + aligned_len <= c->bus_addr + c->len) 40962306a36Sopenharmony_ci return c; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* below */ 41262306a36Sopenharmony_ci if (aligned_bus + aligned_len <= c->bus_addr) 41362306a36Sopenharmony_ci continue; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* above */ 41662306a36Sopenharmony_ci if (aligned_bus >= c->bus_addr + c->len) 41762306a36Sopenharmony_ci continue; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* we don't handle the multi-chunk case for now */ 42062306a36Sopenharmony_ci dma_dump_chunk(c); 42162306a36Sopenharmony_ci BUG(); 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci return NULL; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic struct dma_chunk *dma_find_chunk_lpar(struct ps3_dma_region *r, 42762306a36Sopenharmony_ci unsigned long lpar_addr, unsigned long len) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct dma_chunk *c; 43062306a36Sopenharmony_ci unsigned long aligned_lpar = ALIGN_DOWN(lpar_addr, 1 << r->page_size); 43162306a36Sopenharmony_ci unsigned long aligned_len = ALIGN(len + lpar_addr - aligned_lpar, 43262306a36Sopenharmony_ci 1 << r->page_size); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci list_for_each_entry(c, &r->chunk_list.head, link) { 43562306a36Sopenharmony_ci /* intersection */ 43662306a36Sopenharmony_ci if (c->lpar_addr <= aligned_lpar && 43762306a36Sopenharmony_ci aligned_lpar < c->lpar_addr + c->len) { 43862306a36Sopenharmony_ci if (aligned_lpar + aligned_len <= c->lpar_addr + c->len) 43962306a36Sopenharmony_ci return c; 44062306a36Sopenharmony_ci else { 44162306a36Sopenharmony_ci dma_dump_chunk(c); 44262306a36Sopenharmony_ci BUG(); 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci /* below */ 44662306a36Sopenharmony_ci if (aligned_lpar + aligned_len <= c->lpar_addr) { 44762306a36Sopenharmony_ci continue; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci /* above */ 45062306a36Sopenharmony_ci if (c->lpar_addr + c->len <= aligned_lpar) { 45162306a36Sopenharmony_ci continue; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci return NULL; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic int dma_sb_free_chunk(struct dma_chunk *c) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci int result = 0; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (c->bus_addr) { 46262306a36Sopenharmony_ci result = lv1_unmap_device_dma_region(c->region->dev->bus_id, 46362306a36Sopenharmony_ci c->region->dev->dev_id, c->bus_addr, c->len); 46462306a36Sopenharmony_ci BUG_ON(result); 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci kfree(c); 46862306a36Sopenharmony_ci return result; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic int dma_ioc0_free_chunk(struct dma_chunk *c) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci int result = 0; 47462306a36Sopenharmony_ci int iopage; 47562306a36Sopenharmony_ci unsigned long offset; 47662306a36Sopenharmony_ci struct ps3_dma_region *r = c->region; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci DBG("%s:start\n", __func__); 47962306a36Sopenharmony_ci for (iopage = 0; iopage < (c->len >> r->page_size); iopage++) { 48062306a36Sopenharmony_ci offset = (1 << r->page_size) * iopage; 48162306a36Sopenharmony_ci /* put INVALID entry */ 48262306a36Sopenharmony_ci result = lv1_put_iopte(0, 48362306a36Sopenharmony_ci c->bus_addr + offset, 48462306a36Sopenharmony_ci c->lpar_addr + offset, 48562306a36Sopenharmony_ci r->ioid, 48662306a36Sopenharmony_ci 0); 48762306a36Sopenharmony_ci DBG("%s: bus=%#lx, lpar=%#lx, ioid=%d\n", __func__, 48862306a36Sopenharmony_ci c->bus_addr + offset, 48962306a36Sopenharmony_ci c->lpar_addr + offset, 49062306a36Sopenharmony_ci r->ioid); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (result) { 49362306a36Sopenharmony_ci DBG("%s:%d: lv1_put_iopte failed: %s\n", __func__, 49462306a36Sopenharmony_ci __LINE__, ps3_result(result)); 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci kfree(c); 49862306a36Sopenharmony_ci DBG("%s:end\n", __func__); 49962306a36Sopenharmony_ci return result; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci/** 50362306a36Sopenharmony_ci * dma_sb_map_pages - Maps dma pages into the io controller bus address space. 50462306a36Sopenharmony_ci * @r: Pointer to a struct ps3_dma_region. 50562306a36Sopenharmony_ci * @phys_addr: Starting physical address of the area to map. 50662306a36Sopenharmony_ci * @len: Length in bytes of the area to map. 50762306a36Sopenharmony_ci * c_out: A pointer to receive an allocated struct dma_chunk for this area. 50862306a36Sopenharmony_ci * 50962306a36Sopenharmony_ci * This is the lowest level dma mapping routine, and is the one that will 51062306a36Sopenharmony_ci * make the HV call to add the pages into the io controller address space. 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic int dma_sb_map_pages(struct ps3_dma_region *r, unsigned long phys_addr, 51462306a36Sopenharmony_ci unsigned long len, struct dma_chunk **c_out, u64 iopte_flag) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci int result; 51762306a36Sopenharmony_ci struct dma_chunk *c; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci c = kzalloc(sizeof(*c), GFP_ATOMIC); 52062306a36Sopenharmony_ci if (!c) { 52162306a36Sopenharmony_ci result = -ENOMEM; 52262306a36Sopenharmony_ci goto fail_alloc; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci c->region = r; 52662306a36Sopenharmony_ci c->lpar_addr = ps3_mm_phys_to_lpar(phys_addr); 52762306a36Sopenharmony_ci c->bus_addr = dma_sb_lpar_to_bus(r, c->lpar_addr); 52862306a36Sopenharmony_ci c->len = len; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci BUG_ON(iopte_flag != 0xf800000000000000UL); 53162306a36Sopenharmony_ci result = lv1_map_device_dma_region(c->region->dev->bus_id, 53262306a36Sopenharmony_ci c->region->dev->dev_id, c->lpar_addr, 53362306a36Sopenharmony_ci c->bus_addr, c->len, iopte_flag); 53462306a36Sopenharmony_ci if (result) { 53562306a36Sopenharmony_ci DBG("%s:%d: lv1_map_device_dma_region failed: %s\n", 53662306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 53762306a36Sopenharmony_ci goto fail_map; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci list_add(&c->link, &r->chunk_list.head); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci *c_out = c; 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cifail_map: 54662306a36Sopenharmony_ci kfree(c); 54762306a36Sopenharmony_cifail_alloc: 54862306a36Sopenharmony_ci *c_out = NULL; 54962306a36Sopenharmony_ci DBG(" <- %s:%d\n", __func__, __LINE__); 55062306a36Sopenharmony_ci return result; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic int dma_ioc0_map_pages(struct ps3_dma_region *r, unsigned long phys_addr, 55462306a36Sopenharmony_ci unsigned long len, struct dma_chunk **c_out, 55562306a36Sopenharmony_ci u64 iopte_flag) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci int result; 55862306a36Sopenharmony_ci struct dma_chunk *c, *last; 55962306a36Sopenharmony_ci int iopage, pages; 56062306a36Sopenharmony_ci unsigned long offset; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci DBG(KERN_ERR "%s: phy=%#lx, lpar%#lx, len=%#lx\n", __func__, 56362306a36Sopenharmony_ci phys_addr, ps3_mm_phys_to_lpar(phys_addr), len); 56462306a36Sopenharmony_ci c = kzalloc(sizeof(*c), GFP_ATOMIC); 56562306a36Sopenharmony_ci if (!c) { 56662306a36Sopenharmony_ci result = -ENOMEM; 56762306a36Sopenharmony_ci goto fail_alloc; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci c->region = r; 57162306a36Sopenharmony_ci c->len = len; 57262306a36Sopenharmony_ci c->lpar_addr = ps3_mm_phys_to_lpar(phys_addr); 57362306a36Sopenharmony_ci /* allocate IO address */ 57462306a36Sopenharmony_ci if (list_empty(&r->chunk_list.head)) { 57562306a36Sopenharmony_ci /* first one */ 57662306a36Sopenharmony_ci c->bus_addr = r->bus_addr; 57762306a36Sopenharmony_ci } else { 57862306a36Sopenharmony_ci /* derive from last bus addr*/ 57962306a36Sopenharmony_ci last = list_entry(r->chunk_list.head.next, 58062306a36Sopenharmony_ci struct dma_chunk, link); 58162306a36Sopenharmony_ci c->bus_addr = last->bus_addr + last->len; 58262306a36Sopenharmony_ci DBG("%s: last bus=%#lx, len=%#lx\n", __func__, 58362306a36Sopenharmony_ci last->bus_addr, last->len); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* FIXME: check whether length exceeds region size */ 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* build ioptes for the area */ 58962306a36Sopenharmony_ci pages = len >> r->page_size; 59062306a36Sopenharmony_ci DBG("%s: pgsize=%#x len=%#lx pages=%#x iopteflag=%#llx\n", __func__, 59162306a36Sopenharmony_ci r->page_size, r->len, pages, iopte_flag); 59262306a36Sopenharmony_ci for (iopage = 0; iopage < pages; iopage++) { 59362306a36Sopenharmony_ci offset = (1 << r->page_size) * iopage; 59462306a36Sopenharmony_ci result = lv1_put_iopte(0, 59562306a36Sopenharmony_ci c->bus_addr + offset, 59662306a36Sopenharmony_ci c->lpar_addr + offset, 59762306a36Sopenharmony_ci r->ioid, 59862306a36Sopenharmony_ci iopte_flag); 59962306a36Sopenharmony_ci if (result) { 60062306a36Sopenharmony_ci pr_warn("%s:%d: lv1_put_iopte failed: %s\n", 60162306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 60262306a36Sopenharmony_ci goto fail_map; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci DBG("%s: pg=%d bus=%#lx, lpar=%#lx, ioid=%#x\n", __func__, 60562306a36Sopenharmony_ci iopage, c->bus_addr + offset, c->lpar_addr + offset, 60662306a36Sopenharmony_ci r->ioid); 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* be sure that last allocated one is inserted at head */ 61062306a36Sopenharmony_ci list_add(&c->link, &r->chunk_list.head); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci *c_out = c; 61362306a36Sopenharmony_ci DBG("%s: end\n", __func__); 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cifail_map: 61762306a36Sopenharmony_ci for (iopage--; 0 <= iopage; iopage--) { 61862306a36Sopenharmony_ci lv1_put_iopte(0, 61962306a36Sopenharmony_ci c->bus_addr + offset, 62062306a36Sopenharmony_ci c->lpar_addr + offset, 62162306a36Sopenharmony_ci r->ioid, 62262306a36Sopenharmony_ci 0); 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci kfree(c); 62562306a36Sopenharmony_cifail_alloc: 62662306a36Sopenharmony_ci *c_out = NULL; 62762306a36Sopenharmony_ci return result; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci/** 63162306a36Sopenharmony_ci * dma_sb_region_create - Create a device dma region. 63262306a36Sopenharmony_ci * @r: Pointer to a struct ps3_dma_region. 63362306a36Sopenharmony_ci * 63462306a36Sopenharmony_ci * This is the lowest level dma region create routine, and is the one that 63562306a36Sopenharmony_ci * will make the HV call to create the region. 63662306a36Sopenharmony_ci */ 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic int dma_sb_region_create(struct ps3_dma_region *r) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci int result; 64162306a36Sopenharmony_ci u64 bus_addr; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci DBG(" -> %s:%d:\n", __func__, __LINE__); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci BUG_ON(!r); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (!r->dev->bus_id) { 64862306a36Sopenharmony_ci pr_info("%s:%d: %llu:%llu no dma\n", __func__, __LINE__, 64962306a36Sopenharmony_ci r->dev->bus_id, r->dev->dev_id); 65062306a36Sopenharmony_ci return 0; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci DBG("%s:%u: len = 0x%lx, page_size = %u, offset = 0x%lx\n", __func__, 65462306a36Sopenharmony_ci __LINE__, r->len, r->page_size, r->offset); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci BUG_ON(!r->len); 65762306a36Sopenharmony_ci BUG_ON(!r->page_size); 65862306a36Sopenharmony_ci BUG_ON(!r->region_ops); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci INIT_LIST_HEAD(&r->chunk_list.head); 66162306a36Sopenharmony_ci spin_lock_init(&r->chunk_list.lock); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci result = lv1_allocate_device_dma_region(r->dev->bus_id, r->dev->dev_id, 66462306a36Sopenharmony_ci roundup_pow_of_two(r->len), r->page_size, r->region_type, 66562306a36Sopenharmony_ci &bus_addr); 66662306a36Sopenharmony_ci r->bus_addr = bus_addr; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (result) { 66962306a36Sopenharmony_ci DBG("%s:%d: lv1_allocate_device_dma_region failed: %s\n", 67062306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 67162306a36Sopenharmony_ci r->len = r->bus_addr = 0; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return result; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic int dma_ioc0_region_create(struct ps3_dma_region *r) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci int result; 68062306a36Sopenharmony_ci u64 bus_addr; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci INIT_LIST_HEAD(&r->chunk_list.head); 68362306a36Sopenharmony_ci spin_lock_init(&r->chunk_list.lock); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci result = lv1_allocate_io_segment(0, 68662306a36Sopenharmony_ci r->len, 68762306a36Sopenharmony_ci r->page_size, 68862306a36Sopenharmony_ci &bus_addr); 68962306a36Sopenharmony_ci r->bus_addr = bus_addr; 69062306a36Sopenharmony_ci if (result) { 69162306a36Sopenharmony_ci DBG("%s:%d: lv1_allocate_io_segment failed: %s\n", 69262306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 69362306a36Sopenharmony_ci r->len = r->bus_addr = 0; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci DBG("%s: len=%#lx, pg=%d, bus=%#lx\n", __func__, 69662306a36Sopenharmony_ci r->len, r->page_size, r->bus_addr); 69762306a36Sopenharmony_ci return result; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci/** 70162306a36Sopenharmony_ci * dma_region_free - Free a device dma region. 70262306a36Sopenharmony_ci * @r: Pointer to a struct ps3_dma_region. 70362306a36Sopenharmony_ci * 70462306a36Sopenharmony_ci * This is the lowest level dma region free routine, and is the one that 70562306a36Sopenharmony_ci * will make the HV call to free the region. 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic int dma_sb_region_free(struct ps3_dma_region *r) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci int result; 71162306a36Sopenharmony_ci struct dma_chunk *c; 71262306a36Sopenharmony_ci struct dma_chunk *tmp; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci BUG_ON(!r); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci if (!r->dev->bus_id) { 71762306a36Sopenharmony_ci pr_info("%s:%d: %llu:%llu no dma\n", __func__, __LINE__, 71862306a36Sopenharmony_ci r->dev->bus_id, r->dev->dev_id); 71962306a36Sopenharmony_ci return 0; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci list_for_each_entry_safe(c, tmp, &r->chunk_list.head, link) { 72362306a36Sopenharmony_ci list_del(&c->link); 72462306a36Sopenharmony_ci dma_sb_free_chunk(c); 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci result = lv1_free_device_dma_region(r->dev->bus_id, r->dev->dev_id, 72862306a36Sopenharmony_ci r->bus_addr); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (result) 73162306a36Sopenharmony_ci DBG("%s:%d: lv1_free_device_dma_region failed: %s\n", 73262306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci r->bus_addr = 0; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci return result; 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic int dma_ioc0_region_free(struct ps3_dma_region *r) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci int result; 74262306a36Sopenharmony_ci struct dma_chunk *c, *n; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci DBG("%s: start\n", __func__); 74562306a36Sopenharmony_ci list_for_each_entry_safe(c, n, &r->chunk_list.head, link) { 74662306a36Sopenharmony_ci list_del(&c->link); 74762306a36Sopenharmony_ci dma_ioc0_free_chunk(c); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci result = lv1_release_io_segment(0, r->bus_addr); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci if (result) 75362306a36Sopenharmony_ci DBG("%s:%d: lv1_free_device_dma_region failed: %s\n", 75462306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci r->bus_addr = 0; 75762306a36Sopenharmony_ci DBG("%s: end\n", __func__); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci return result; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci/** 76362306a36Sopenharmony_ci * dma_sb_map_area - Map an area of memory into a device dma region. 76462306a36Sopenharmony_ci * @r: Pointer to a struct ps3_dma_region. 76562306a36Sopenharmony_ci * @virt_addr: Starting virtual address of the area to map. 76662306a36Sopenharmony_ci * @len: Length in bytes of the area to map. 76762306a36Sopenharmony_ci * @bus_addr: A pointer to return the starting ioc bus address of the area to 76862306a36Sopenharmony_ci * map. 76962306a36Sopenharmony_ci * 77062306a36Sopenharmony_ci * This is the common dma mapping routine. 77162306a36Sopenharmony_ci */ 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic int dma_sb_map_area(struct ps3_dma_region *r, unsigned long virt_addr, 77462306a36Sopenharmony_ci unsigned long len, dma_addr_t *bus_addr, 77562306a36Sopenharmony_ci u64 iopte_flag) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci int result; 77862306a36Sopenharmony_ci unsigned long flags; 77962306a36Sopenharmony_ci struct dma_chunk *c; 78062306a36Sopenharmony_ci unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr) 78162306a36Sopenharmony_ci : virt_addr; 78262306a36Sopenharmony_ci unsigned long aligned_phys = ALIGN_DOWN(phys_addr, 1 << r->page_size); 78362306a36Sopenharmony_ci unsigned long aligned_len = ALIGN(len + phys_addr - aligned_phys, 78462306a36Sopenharmony_ci 1 << r->page_size); 78562306a36Sopenharmony_ci *bus_addr = dma_sb_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr)); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci if (!USE_DYNAMIC_DMA) { 78862306a36Sopenharmony_ci unsigned long lpar_addr = ps3_mm_phys_to_lpar(phys_addr); 78962306a36Sopenharmony_ci DBG(" -> %s:%d\n", __func__, __LINE__); 79062306a36Sopenharmony_ci DBG("%s:%d virt_addr %lxh\n", __func__, __LINE__, 79162306a36Sopenharmony_ci virt_addr); 79262306a36Sopenharmony_ci DBG("%s:%d phys_addr %lxh\n", __func__, __LINE__, 79362306a36Sopenharmony_ci phys_addr); 79462306a36Sopenharmony_ci DBG("%s:%d lpar_addr %lxh\n", __func__, __LINE__, 79562306a36Sopenharmony_ci lpar_addr); 79662306a36Sopenharmony_ci DBG("%s:%d len %lxh\n", __func__, __LINE__, len); 79762306a36Sopenharmony_ci DBG("%s:%d bus_addr %llxh (%lxh)\n", __func__, __LINE__, 79862306a36Sopenharmony_ci *bus_addr, len); 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci spin_lock_irqsave(&r->chunk_list.lock, flags); 80262306a36Sopenharmony_ci c = dma_find_chunk(r, *bus_addr, len); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci if (c) { 80562306a36Sopenharmony_ci DBG("%s:%d: reusing mapped chunk", __func__, __LINE__); 80662306a36Sopenharmony_ci dma_dump_chunk(c); 80762306a36Sopenharmony_ci c->usage_count++; 80862306a36Sopenharmony_ci spin_unlock_irqrestore(&r->chunk_list.lock, flags); 80962306a36Sopenharmony_ci return 0; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci result = dma_sb_map_pages(r, aligned_phys, aligned_len, &c, iopte_flag); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (result) { 81562306a36Sopenharmony_ci *bus_addr = 0; 81662306a36Sopenharmony_ci DBG("%s:%d: dma_sb_map_pages failed (%d)\n", 81762306a36Sopenharmony_ci __func__, __LINE__, result); 81862306a36Sopenharmony_ci spin_unlock_irqrestore(&r->chunk_list.lock, flags); 81962306a36Sopenharmony_ci return result; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci c->usage_count = 1; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci spin_unlock_irqrestore(&r->chunk_list.lock, flags); 82562306a36Sopenharmony_ci return result; 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic int dma_ioc0_map_area(struct ps3_dma_region *r, unsigned long virt_addr, 82962306a36Sopenharmony_ci unsigned long len, dma_addr_t *bus_addr, 83062306a36Sopenharmony_ci u64 iopte_flag) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci int result; 83362306a36Sopenharmony_ci unsigned long flags; 83462306a36Sopenharmony_ci struct dma_chunk *c; 83562306a36Sopenharmony_ci unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr) 83662306a36Sopenharmony_ci : virt_addr; 83762306a36Sopenharmony_ci unsigned long aligned_phys = ALIGN_DOWN(phys_addr, 1 << r->page_size); 83862306a36Sopenharmony_ci unsigned long aligned_len = ALIGN(len + phys_addr - aligned_phys, 83962306a36Sopenharmony_ci 1 << r->page_size); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci DBG(KERN_ERR "%s: vaddr=%#lx, len=%#lx\n", __func__, 84262306a36Sopenharmony_ci virt_addr, len); 84362306a36Sopenharmony_ci DBG(KERN_ERR "%s: ph=%#lx a_ph=%#lx a_l=%#lx\n", __func__, 84462306a36Sopenharmony_ci phys_addr, aligned_phys, aligned_len); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci spin_lock_irqsave(&r->chunk_list.lock, flags); 84762306a36Sopenharmony_ci c = dma_find_chunk_lpar(r, ps3_mm_phys_to_lpar(phys_addr), len); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (c) { 85062306a36Sopenharmony_ci /* FIXME */ 85162306a36Sopenharmony_ci BUG(); 85262306a36Sopenharmony_ci *bus_addr = c->bus_addr + phys_addr - aligned_phys; 85362306a36Sopenharmony_ci c->usage_count++; 85462306a36Sopenharmony_ci spin_unlock_irqrestore(&r->chunk_list.lock, flags); 85562306a36Sopenharmony_ci return 0; 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci result = dma_ioc0_map_pages(r, aligned_phys, aligned_len, &c, 85962306a36Sopenharmony_ci iopte_flag); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (result) { 86262306a36Sopenharmony_ci *bus_addr = 0; 86362306a36Sopenharmony_ci DBG("%s:%d: dma_ioc0_map_pages failed (%d)\n", 86462306a36Sopenharmony_ci __func__, __LINE__, result); 86562306a36Sopenharmony_ci spin_unlock_irqrestore(&r->chunk_list.lock, flags); 86662306a36Sopenharmony_ci return result; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci *bus_addr = c->bus_addr + phys_addr - aligned_phys; 86962306a36Sopenharmony_ci DBG("%s: va=%#lx pa=%#lx a_pa=%#lx bus=%#llx\n", __func__, 87062306a36Sopenharmony_ci virt_addr, phys_addr, aligned_phys, *bus_addr); 87162306a36Sopenharmony_ci c->usage_count = 1; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci spin_unlock_irqrestore(&r->chunk_list.lock, flags); 87462306a36Sopenharmony_ci return result; 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci/** 87862306a36Sopenharmony_ci * dma_sb_unmap_area - Unmap an area of memory from a device dma region. 87962306a36Sopenharmony_ci * @r: Pointer to a struct ps3_dma_region. 88062306a36Sopenharmony_ci * @bus_addr: The starting ioc bus address of the area to unmap. 88162306a36Sopenharmony_ci * @len: Length in bytes of the area to unmap. 88262306a36Sopenharmony_ci * 88362306a36Sopenharmony_ci * This is the common dma unmap routine. 88462306a36Sopenharmony_ci */ 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_cistatic int dma_sb_unmap_area(struct ps3_dma_region *r, dma_addr_t bus_addr, 88762306a36Sopenharmony_ci unsigned long len) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci unsigned long flags; 89062306a36Sopenharmony_ci struct dma_chunk *c; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci spin_lock_irqsave(&r->chunk_list.lock, flags); 89362306a36Sopenharmony_ci c = dma_find_chunk(r, bus_addr, len); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci if (!c) { 89662306a36Sopenharmony_ci unsigned long aligned_bus = ALIGN_DOWN(bus_addr, 89762306a36Sopenharmony_ci 1 << r->page_size); 89862306a36Sopenharmony_ci unsigned long aligned_len = ALIGN(len + bus_addr 89962306a36Sopenharmony_ci - aligned_bus, 1 << r->page_size); 90062306a36Sopenharmony_ci DBG("%s:%d: not found: bus_addr %llxh\n", 90162306a36Sopenharmony_ci __func__, __LINE__, bus_addr); 90262306a36Sopenharmony_ci DBG("%s:%d: not found: len %lxh\n", 90362306a36Sopenharmony_ci __func__, __LINE__, len); 90462306a36Sopenharmony_ci DBG("%s:%d: not found: aligned_bus %lxh\n", 90562306a36Sopenharmony_ci __func__, __LINE__, aligned_bus); 90662306a36Sopenharmony_ci DBG("%s:%d: not found: aligned_len %lxh\n", 90762306a36Sopenharmony_ci __func__, __LINE__, aligned_len); 90862306a36Sopenharmony_ci BUG(); 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci c->usage_count--; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci if (!c->usage_count) { 91462306a36Sopenharmony_ci list_del(&c->link); 91562306a36Sopenharmony_ci dma_sb_free_chunk(c); 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci spin_unlock_irqrestore(&r->chunk_list.lock, flags); 91962306a36Sopenharmony_ci return 0; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic int dma_ioc0_unmap_area(struct ps3_dma_region *r, 92362306a36Sopenharmony_ci dma_addr_t bus_addr, unsigned long len) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci unsigned long flags; 92662306a36Sopenharmony_ci struct dma_chunk *c; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci DBG("%s: start a=%#llx l=%#lx\n", __func__, bus_addr, len); 92962306a36Sopenharmony_ci spin_lock_irqsave(&r->chunk_list.lock, flags); 93062306a36Sopenharmony_ci c = dma_find_chunk(r, bus_addr, len); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (!c) { 93362306a36Sopenharmony_ci unsigned long aligned_bus = ALIGN_DOWN(bus_addr, 93462306a36Sopenharmony_ci 1 << r->page_size); 93562306a36Sopenharmony_ci unsigned long aligned_len = ALIGN(len + bus_addr 93662306a36Sopenharmony_ci - aligned_bus, 93762306a36Sopenharmony_ci 1 << r->page_size); 93862306a36Sopenharmony_ci DBG("%s:%d: not found: bus_addr %llxh\n", 93962306a36Sopenharmony_ci __func__, __LINE__, bus_addr); 94062306a36Sopenharmony_ci DBG("%s:%d: not found: len %lxh\n", 94162306a36Sopenharmony_ci __func__, __LINE__, len); 94262306a36Sopenharmony_ci DBG("%s:%d: not found: aligned_bus %lxh\n", 94362306a36Sopenharmony_ci __func__, __LINE__, aligned_bus); 94462306a36Sopenharmony_ci DBG("%s:%d: not found: aligned_len %lxh\n", 94562306a36Sopenharmony_ci __func__, __LINE__, aligned_len); 94662306a36Sopenharmony_ci BUG(); 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci c->usage_count--; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci if (!c->usage_count) { 95262306a36Sopenharmony_ci list_del(&c->link); 95362306a36Sopenharmony_ci dma_ioc0_free_chunk(c); 95462306a36Sopenharmony_ci } 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci spin_unlock_irqrestore(&r->chunk_list.lock, flags); 95762306a36Sopenharmony_ci DBG("%s: end\n", __func__); 95862306a36Sopenharmony_ci return 0; 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci/** 96262306a36Sopenharmony_ci * dma_sb_region_create_linear - Setup a linear dma mapping for a device. 96362306a36Sopenharmony_ci * @r: Pointer to a struct ps3_dma_region. 96462306a36Sopenharmony_ci * 96562306a36Sopenharmony_ci * This routine creates an HV dma region for the device and maps all available 96662306a36Sopenharmony_ci * ram into the io controller bus address space. 96762306a36Sopenharmony_ci */ 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic int dma_sb_region_create_linear(struct ps3_dma_region *r) 97062306a36Sopenharmony_ci{ 97162306a36Sopenharmony_ci int result; 97262306a36Sopenharmony_ci unsigned long virt_addr, len; 97362306a36Sopenharmony_ci dma_addr_t tmp; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci if (r->len > 16*1024*1024) { /* FIXME: need proper fix */ 97662306a36Sopenharmony_ci /* force 16M dma pages for linear mapping */ 97762306a36Sopenharmony_ci if (r->page_size != PS3_DMA_16M) { 97862306a36Sopenharmony_ci pr_info("%s:%d: forcing 16M pages for linear map\n", 97962306a36Sopenharmony_ci __func__, __LINE__); 98062306a36Sopenharmony_ci r->page_size = PS3_DMA_16M; 98162306a36Sopenharmony_ci r->len = ALIGN(r->len, 1 << r->page_size); 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci result = dma_sb_region_create(r); 98662306a36Sopenharmony_ci BUG_ON(result); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci if (r->offset < map.rm.size) { 98962306a36Sopenharmony_ci /* Map (part of) 1st RAM chunk */ 99062306a36Sopenharmony_ci virt_addr = map.rm.base + r->offset; 99162306a36Sopenharmony_ci len = map.rm.size - r->offset; 99262306a36Sopenharmony_ci if (len > r->len) 99362306a36Sopenharmony_ci len = r->len; 99462306a36Sopenharmony_ci result = dma_sb_map_area(r, virt_addr, len, &tmp, 99562306a36Sopenharmony_ci CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_SO_RW | 99662306a36Sopenharmony_ci CBE_IOPTE_M); 99762306a36Sopenharmony_ci BUG_ON(result); 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci if (r->offset + r->len > map.rm.size) { 100162306a36Sopenharmony_ci /* Map (part of) 2nd RAM chunk */ 100262306a36Sopenharmony_ci virt_addr = map.rm.size; 100362306a36Sopenharmony_ci len = r->len; 100462306a36Sopenharmony_ci if (r->offset >= map.rm.size) 100562306a36Sopenharmony_ci virt_addr += r->offset - map.rm.size; 100662306a36Sopenharmony_ci else 100762306a36Sopenharmony_ci len -= map.rm.size - r->offset; 100862306a36Sopenharmony_ci result = dma_sb_map_area(r, virt_addr, len, &tmp, 100962306a36Sopenharmony_ci CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_SO_RW | 101062306a36Sopenharmony_ci CBE_IOPTE_M); 101162306a36Sopenharmony_ci BUG_ON(result); 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci return result; 101562306a36Sopenharmony_ci} 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci/** 101862306a36Sopenharmony_ci * dma_sb_region_free_linear - Free a linear dma mapping for a device. 101962306a36Sopenharmony_ci * @r: Pointer to a struct ps3_dma_region. 102062306a36Sopenharmony_ci * 102162306a36Sopenharmony_ci * This routine will unmap all mapped areas and free the HV dma region. 102262306a36Sopenharmony_ci */ 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_cistatic int dma_sb_region_free_linear(struct ps3_dma_region *r) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci int result; 102762306a36Sopenharmony_ci dma_addr_t bus_addr; 102862306a36Sopenharmony_ci unsigned long len, lpar_addr; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (r->offset < map.rm.size) { 103162306a36Sopenharmony_ci /* Unmap (part of) 1st RAM chunk */ 103262306a36Sopenharmony_ci lpar_addr = map.rm.base + r->offset; 103362306a36Sopenharmony_ci len = map.rm.size - r->offset; 103462306a36Sopenharmony_ci if (len > r->len) 103562306a36Sopenharmony_ci len = r->len; 103662306a36Sopenharmony_ci bus_addr = dma_sb_lpar_to_bus(r, lpar_addr); 103762306a36Sopenharmony_ci result = dma_sb_unmap_area(r, bus_addr, len); 103862306a36Sopenharmony_ci BUG_ON(result); 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci if (r->offset + r->len > map.rm.size) { 104262306a36Sopenharmony_ci /* Unmap (part of) 2nd RAM chunk */ 104362306a36Sopenharmony_ci lpar_addr = map.r1.base; 104462306a36Sopenharmony_ci len = r->len; 104562306a36Sopenharmony_ci if (r->offset >= map.rm.size) 104662306a36Sopenharmony_ci lpar_addr += r->offset - map.rm.size; 104762306a36Sopenharmony_ci else 104862306a36Sopenharmony_ci len -= map.rm.size - r->offset; 104962306a36Sopenharmony_ci bus_addr = dma_sb_lpar_to_bus(r, lpar_addr); 105062306a36Sopenharmony_ci result = dma_sb_unmap_area(r, bus_addr, len); 105162306a36Sopenharmony_ci BUG_ON(result); 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci result = dma_sb_region_free(r); 105562306a36Sopenharmony_ci BUG_ON(result); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci return result; 105862306a36Sopenharmony_ci} 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci/** 106162306a36Sopenharmony_ci * dma_sb_map_area_linear - Map an area of memory into a device dma region. 106262306a36Sopenharmony_ci * @r: Pointer to a struct ps3_dma_region. 106362306a36Sopenharmony_ci * @virt_addr: Starting virtual address of the area to map. 106462306a36Sopenharmony_ci * @len: Length in bytes of the area to map. 106562306a36Sopenharmony_ci * @bus_addr: A pointer to return the starting ioc bus address of the area to 106662306a36Sopenharmony_ci * map. 106762306a36Sopenharmony_ci * 106862306a36Sopenharmony_ci * This routine just returns the corresponding bus address. Actual mapping 106962306a36Sopenharmony_ci * occurs in dma_region_create_linear(). 107062306a36Sopenharmony_ci */ 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_cistatic int dma_sb_map_area_linear(struct ps3_dma_region *r, 107362306a36Sopenharmony_ci unsigned long virt_addr, unsigned long len, dma_addr_t *bus_addr, 107462306a36Sopenharmony_ci u64 iopte_flag) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr) 107762306a36Sopenharmony_ci : virt_addr; 107862306a36Sopenharmony_ci *bus_addr = dma_sb_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr)); 107962306a36Sopenharmony_ci return 0; 108062306a36Sopenharmony_ci} 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci/** 108362306a36Sopenharmony_ci * dma_unmap_area_linear - Unmap an area of memory from a device dma region. 108462306a36Sopenharmony_ci * @r: Pointer to a struct ps3_dma_region. 108562306a36Sopenharmony_ci * @bus_addr: The starting ioc bus address of the area to unmap. 108662306a36Sopenharmony_ci * @len: Length in bytes of the area to unmap. 108762306a36Sopenharmony_ci * 108862306a36Sopenharmony_ci * This routine does nothing. Unmapping occurs in dma_sb_region_free_linear(). 108962306a36Sopenharmony_ci */ 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_cistatic int dma_sb_unmap_area_linear(struct ps3_dma_region *r, 109262306a36Sopenharmony_ci dma_addr_t bus_addr, unsigned long len) 109362306a36Sopenharmony_ci{ 109462306a36Sopenharmony_ci return 0; 109562306a36Sopenharmony_ci}; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_cistatic const struct ps3_dma_region_ops ps3_dma_sb_region_ops = { 109862306a36Sopenharmony_ci .create = dma_sb_region_create, 109962306a36Sopenharmony_ci .free = dma_sb_region_free, 110062306a36Sopenharmony_ci .map = dma_sb_map_area, 110162306a36Sopenharmony_ci .unmap = dma_sb_unmap_area 110262306a36Sopenharmony_ci}; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_cistatic const struct ps3_dma_region_ops ps3_dma_sb_region_linear_ops = { 110562306a36Sopenharmony_ci .create = dma_sb_region_create_linear, 110662306a36Sopenharmony_ci .free = dma_sb_region_free_linear, 110762306a36Sopenharmony_ci .map = dma_sb_map_area_linear, 110862306a36Sopenharmony_ci .unmap = dma_sb_unmap_area_linear 110962306a36Sopenharmony_ci}; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_cistatic const struct ps3_dma_region_ops ps3_dma_ioc0_region_ops = { 111262306a36Sopenharmony_ci .create = dma_ioc0_region_create, 111362306a36Sopenharmony_ci .free = dma_ioc0_region_free, 111462306a36Sopenharmony_ci .map = dma_ioc0_map_area, 111562306a36Sopenharmony_ci .unmap = dma_ioc0_unmap_area 111662306a36Sopenharmony_ci}; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ciint ps3_dma_region_init(struct ps3_system_bus_device *dev, 111962306a36Sopenharmony_ci struct ps3_dma_region *r, enum ps3_dma_page_size page_size, 112062306a36Sopenharmony_ci enum ps3_dma_region_type region_type, void *addr, unsigned long len) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci unsigned long lpar_addr; 112362306a36Sopenharmony_ci int result; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci lpar_addr = addr ? ps3_mm_phys_to_lpar(__pa(addr)) : 0; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci r->dev = dev; 112862306a36Sopenharmony_ci r->page_size = page_size; 112962306a36Sopenharmony_ci r->region_type = region_type; 113062306a36Sopenharmony_ci r->offset = lpar_addr; 113162306a36Sopenharmony_ci if (r->offset >= map.rm.size) 113262306a36Sopenharmony_ci r->offset -= map.r1.offset; 113362306a36Sopenharmony_ci r->len = len ? len : ALIGN(map.total, 1 << r->page_size); 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci dev->core.dma_mask = &r->dma_mask; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci result = dma_set_mask_and_coherent(&dev->core, DMA_BIT_MASK(32)); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci if (result < 0) { 114062306a36Sopenharmony_ci dev_err(&dev->core, "%s:%d: dma_set_mask_and_coherent failed: %d\n", 114162306a36Sopenharmony_ci __func__, __LINE__, result); 114262306a36Sopenharmony_ci return result; 114362306a36Sopenharmony_ci } 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci switch (dev->dev_type) { 114662306a36Sopenharmony_ci case PS3_DEVICE_TYPE_SB: 114762306a36Sopenharmony_ci r->region_ops = (USE_DYNAMIC_DMA) 114862306a36Sopenharmony_ci ? &ps3_dma_sb_region_ops 114962306a36Sopenharmony_ci : &ps3_dma_sb_region_linear_ops; 115062306a36Sopenharmony_ci break; 115162306a36Sopenharmony_ci case PS3_DEVICE_TYPE_IOC0: 115262306a36Sopenharmony_ci r->region_ops = &ps3_dma_ioc0_region_ops; 115362306a36Sopenharmony_ci break; 115462306a36Sopenharmony_ci default: 115562306a36Sopenharmony_ci BUG(); 115662306a36Sopenharmony_ci return -EINVAL; 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci return 0; 115962306a36Sopenharmony_ci} 116062306a36Sopenharmony_ciEXPORT_SYMBOL(ps3_dma_region_init); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ciint ps3_dma_region_create(struct ps3_dma_region *r) 116362306a36Sopenharmony_ci{ 116462306a36Sopenharmony_ci BUG_ON(!r); 116562306a36Sopenharmony_ci BUG_ON(!r->region_ops); 116662306a36Sopenharmony_ci BUG_ON(!r->region_ops->create); 116762306a36Sopenharmony_ci return r->region_ops->create(r); 116862306a36Sopenharmony_ci} 116962306a36Sopenharmony_ciEXPORT_SYMBOL(ps3_dma_region_create); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ciint ps3_dma_region_free(struct ps3_dma_region *r) 117262306a36Sopenharmony_ci{ 117362306a36Sopenharmony_ci BUG_ON(!r); 117462306a36Sopenharmony_ci BUG_ON(!r->region_ops); 117562306a36Sopenharmony_ci BUG_ON(!r->region_ops->free); 117662306a36Sopenharmony_ci return r->region_ops->free(r); 117762306a36Sopenharmony_ci} 117862306a36Sopenharmony_ciEXPORT_SYMBOL(ps3_dma_region_free); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ciint ps3_dma_map(struct ps3_dma_region *r, unsigned long virt_addr, 118162306a36Sopenharmony_ci unsigned long len, dma_addr_t *bus_addr, 118262306a36Sopenharmony_ci u64 iopte_flag) 118362306a36Sopenharmony_ci{ 118462306a36Sopenharmony_ci return r->region_ops->map(r, virt_addr, len, bus_addr, iopte_flag); 118562306a36Sopenharmony_ci} 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ciint ps3_dma_unmap(struct ps3_dma_region *r, dma_addr_t bus_addr, 118862306a36Sopenharmony_ci unsigned long len) 118962306a36Sopenharmony_ci{ 119062306a36Sopenharmony_ci return r->region_ops->unmap(r, bus_addr, len); 119162306a36Sopenharmony_ci} 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci/*============================================================================*/ 119462306a36Sopenharmony_ci/* system startup routines */ 119562306a36Sopenharmony_ci/*============================================================================*/ 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci/** 119862306a36Sopenharmony_ci * ps3_mm_init - initialize the address space state variables 119962306a36Sopenharmony_ci */ 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_civoid __init ps3_mm_init(void) 120262306a36Sopenharmony_ci{ 120362306a36Sopenharmony_ci int result; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci DBG(" -> %s:%d\n", __func__, __LINE__); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci result = ps3_repository_read_mm_info(&map.rm.base, &map.rm.size, 120862306a36Sopenharmony_ci &map.total); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci if (result) 121162306a36Sopenharmony_ci panic("ps3_repository_read_mm_info() failed"); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci map.rm.offset = map.rm.base; 121462306a36Sopenharmony_ci map.vas_id = map.htab_size = 0; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci /* this implementation assumes map.rm.base is zero */ 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci BUG_ON(map.rm.base); 121962306a36Sopenharmony_ci BUG_ON(!map.rm.size); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci /* Check if we got the highmem region from an earlier boot step */ 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci if (ps3_mm_get_repository_highmem(&map.r1)) { 122462306a36Sopenharmony_ci result = ps3_mm_region_create(&map.r1, map.total - map.rm.size); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci if (!result) 122762306a36Sopenharmony_ci ps3_mm_set_repository_highmem(&map.r1); 122862306a36Sopenharmony_ci } 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci /* correct map.total for the real total amount of memory we use */ 123162306a36Sopenharmony_ci map.total = map.rm.size + map.r1.size; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci if (!map.r1.size) { 123462306a36Sopenharmony_ci DBG("%s:%d: No highmem region found\n", __func__, __LINE__); 123562306a36Sopenharmony_ci } else { 123662306a36Sopenharmony_ci DBG("%s:%d: Adding highmem region: %llxh %llxh\n", 123762306a36Sopenharmony_ci __func__, __LINE__, map.rm.size, 123862306a36Sopenharmony_ci map.total - map.rm.size); 123962306a36Sopenharmony_ci memblock_add(map.rm.size, map.total - map.rm.size); 124062306a36Sopenharmony_ci } 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci DBG(" <- %s:%d\n", __func__, __LINE__); 124362306a36Sopenharmony_ci} 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci/** 124662306a36Sopenharmony_ci * ps3_mm_shutdown - final cleanup of address space 124762306a36Sopenharmony_ci * 124862306a36Sopenharmony_ci * called during kexec sequence with MMU off. 124962306a36Sopenharmony_ci */ 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_cinotrace void ps3_mm_shutdown(void) 125262306a36Sopenharmony_ci{ 125362306a36Sopenharmony_ci ps3_mm_region_destroy(&map.r1); 125462306a36Sopenharmony_ci} 1255