1*f58a9d17SGeoff Levand /* 2*f58a9d17SGeoff Levand * PS3 address space management. 3*f58a9d17SGeoff Levand * 4*f58a9d17SGeoff Levand * Copyright (C) 2006 Sony Computer Entertainment Inc. 5*f58a9d17SGeoff Levand * Copyright 2006 Sony Corp. 6*f58a9d17SGeoff Levand * 7*f58a9d17SGeoff Levand * This program is free software; you can redistribute it and/or modify 8*f58a9d17SGeoff Levand * it under the terms of the GNU General Public License as published by 9*f58a9d17SGeoff Levand * the Free Software Foundation; version 2 of the License. 10*f58a9d17SGeoff Levand * 11*f58a9d17SGeoff Levand * This program is distributed in the hope that it will be useful, 12*f58a9d17SGeoff Levand * but WITHOUT ANY WARRANTY; without even the implied warranty of 13*f58a9d17SGeoff Levand * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14*f58a9d17SGeoff Levand * GNU General Public License for more details. 15*f58a9d17SGeoff Levand * 16*f58a9d17SGeoff Levand * You should have received a copy of the GNU General Public License 17*f58a9d17SGeoff Levand * along with this program; if not, write to the Free Software 18*f58a9d17SGeoff Levand * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19*f58a9d17SGeoff Levand */ 20*f58a9d17SGeoff Levand 21*f58a9d17SGeoff Levand #include <linux/kernel.h> 22*f58a9d17SGeoff Levand #include <linux/module.h> 23*f58a9d17SGeoff Levand #include <linux/memory_hotplug.h> 24*f58a9d17SGeoff Levand 25*f58a9d17SGeoff Levand #include <asm/lmb.h> 26*f58a9d17SGeoff Levand #include <asm/udbg.h> 27*f58a9d17SGeoff Levand #include <asm/ps3.h> 28*f58a9d17SGeoff Levand #include <asm/lv1call.h> 29*f58a9d17SGeoff Levand 30*f58a9d17SGeoff Levand #include "platform.h" 31*f58a9d17SGeoff Levand 32*f58a9d17SGeoff Levand #if defined(DEBUG) 33*f58a9d17SGeoff Levand #define DBG(fmt...) udbg_printf(fmt) 34*f58a9d17SGeoff Levand #else 35*f58a9d17SGeoff Levand #define DBG(fmt...) do{if(0)printk(fmt);}while(0) 36*f58a9d17SGeoff Levand #endif 37*f58a9d17SGeoff Levand 38*f58a9d17SGeoff Levand enum { 39*f58a9d17SGeoff Levand #if defined(CONFIG_PS3_USE_LPAR_ADDR) 40*f58a9d17SGeoff Levand USE_LPAR_ADDR = 1, 41*f58a9d17SGeoff Levand #else 42*f58a9d17SGeoff Levand USE_LPAR_ADDR = 0, 43*f58a9d17SGeoff Levand #endif 44*f58a9d17SGeoff Levand #if defined(CONFIG_PS3_DYNAMIC_DMA) 45*f58a9d17SGeoff Levand USE_DYNAMIC_DMA = 1, 46*f58a9d17SGeoff Levand #else 47*f58a9d17SGeoff Levand USE_DYNAMIC_DMA = 0, 48*f58a9d17SGeoff Levand #endif 49*f58a9d17SGeoff Levand }; 50*f58a9d17SGeoff Levand 51*f58a9d17SGeoff Levand enum { 52*f58a9d17SGeoff Levand PAGE_SHIFT_4K = 12U, 53*f58a9d17SGeoff Levand PAGE_SHIFT_64K = 16U, 54*f58a9d17SGeoff Levand PAGE_SHIFT_16M = 24U, 55*f58a9d17SGeoff Levand }; 56*f58a9d17SGeoff Levand 57*f58a9d17SGeoff Levand static unsigned long make_page_sizes(unsigned long a, unsigned long b) 58*f58a9d17SGeoff Levand { 59*f58a9d17SGeoff Levand return (a << 56) | (b << 48); 60*f58a9d17SGeoff Levand } 61*f58a9d17SGeoff Levand 62*f58a9d17SGeoff Levand enum { 63*f58a9d17SGeoff Levand ALLOCATE_MEMORY_TRY_ALT_UNIT = 0X04, 64*f58a9d17SGeoff Levand ALLOCATE_MEMORY_ADDR_ZERO = 0X08, 65*f58a9d17SGeoff Levand }; 66*f58a9d17SGeoff Levand 67*f58a9d17SGeoff Levand /* valid htab sizes are {18,19,20} = 256K, 512K, 1M */ 68*f58a9d17SGeoff Levand 69*f58a9d17SGeoff Levand enum { 70*f58a9d17SGeoff Levand HTAB_SIZE_MAX = 20U, /* HV limit of 1MB */ 71*f58a9d17SGeoff Levand HTAB_SIZE_MIN = 18U, /* CPU limit of 256KB */ 72*f58a9d17SGeoff Levand }; 73*f58a9d17SGeoff Levand 74*f58a9d17SGeoff Levand /*============================================================================*/ 75*f58a9d17SGeoff Levand /* virtual address space routines */ 76*f58a9d17SGeoff Levand /*============================================================================*/ 77*f58a9d17SGeoff Levand 78*f58a9d17SGeoff Levand /** 79*f58a9d17SGeoff Levand * struct mem_region - memory region structure 80*f58a9d17SGeoff Levand * @base: base address 81*f58a9d17SGeoff Levand * @size: size in bytes 82*f58a9d17SGeoff Levand * @offset: difference between base and rm.size 83*f58a9d17SGeoff Levand */ 84*f58a9d17SGeoff Levand 85*f58a9d17SGeoff Levand struct mem_region { 86*f58a9d17SGeoff Levand unsigned long base; 87*f58a9d17SGeoff Levand unsigned long size; 88*f58a9d17SGeoff Levand unsigned long offset; 89*f58a9d17SGeoff Levand }; 90*f58a9d17SGeoff Levand 91*f58a9d17SGeoff Levand /** 92*f58a9d17SGeoff Levand * struct map - address space state variables holder 93*f58a9d17SGeoff Levand * @total: total memory available as reported by HV 94*f58a9d17SGeoff Levand * @vas_id - HV virtual address space id 95*f58a9d17SGeoff Levand * @htab_size: htab size in bytes 96*f58a9d17SGeoff Levand * 97*f58a9d17SGeoff Levand * The HV virtual address space (vas) allows for hotplug memory regions. 98*f58a9d17SGeoff Levand * Memory regions can be created and destroyed in the vas at runtime. 99*f58a9d17SGeoff Levand * @rm: real mode (bootmem) region 100*f58a9d17SGeoff Levand * @r1: hotplug memory region(s) 101*f58a9d17SGeoff Levand * 102*f58a9d17SGeoff Levand * ps3 addresses 103*f58a9d17SGeoff Levand * virt_addr: a cpu 'translated' effective address 104*f58a9d17SGeoff Levand * phys_addr: an address in what Linux thinks is the physical address space 105*f58a9d17SGeoff Levand * lpar_addr: an address in the HV virtual address space 106*f58a9d17SGeoff Levand * bus_addr: an io controller 'translated' address on a device bus 107*f58a9d17SGeoff Levand */ 108*f58a9d17SGeoff Levand 109*f58a9d17SGeoff Levand struct map { 110*f58a9d17SGeoff Levand unsigned long total; 111*f58a9d17SGeoff Levand unsigned long vas_id; 112*f58a9d17SGeoff Levand unsigned long htab_size; 113*f58a9d17SGeoff Levand struct mem_region rm; 114*f58a9d17SGeoff Levand struct mem_region r1; 115*f58a9d17SGeoff Levand }; 116*f58a9d17SGeoff Levand 117*f58a9d17SGeoff Levand #define debug_dump_map(x) _debug_dump_map(x, __func__, __LINE__) 118*f58a9d17SGeoff Levand static void _debug_dump_map(const struct map* m, const char* func, int line) 119*f58a9d17SGeoff Levand { 120*f58a9d17SGeoff Levand DBG("%s:%d: map.total = %lxh\n", func, line, m->total); 121*f58a9d17SGeoff Levand DBG("%s:%d: map.rm.size = %lxh\n", func, line, m->rm.size); 122*f58a9d17SGeoff Levand DBG("%s:%d: map.vas_id = %lu\n", func, line, m->vas_id); 123*f58a9d17SGeoff Levand DBG("%s:%d: map.htab_size = %lxh\n", func, line, m->htab_size); 124*f58a9d17SGeoff Levand DBG("%s:%d: map.r1.base = %lxh\n", func, line, m->r1.base); 125*f58a9d17SGeoff Levand DBG("%s:%d: map.r1.offset = %lxh\n", func, line, m->r1.offset); 126*f58a9d17SGeoff Levand DBG("%s:%d: map.r1.size = %lxh\n", func, line, m->r1.size); 127*f58a9d17SGeoff Levand } 128*f58a9d17SGeoff Levand 129*f58a9d17SGeoff Levand static struct map map; 130*f58a9d17SGeoff Levand 131*f58a9d17SGeoff Levand /** 132*f58a9d17SGeoff Levand * ps3_mm_phys_to_lpar - translate a linux physical address to lpar address 133*f58a9d17SGeoff Levand * @phys_addr: linux physical address 134*f58a9d17SGeoff Levand */ 135*f58a9d17SGeoff Levand 136*f58a9d17SGeoff Levand unsigned long ps3_mm_phys_to_lpar(unsigned long phys_addr) 137*f58a9d17SGeoff Levand { 138*f58a9d17SGeoff Levand BUG_ON(is_kernel_addr(phys_addr)); 139*f58a9d17SGeoff Levand if (USE_LPAR_ADDR) 140*f58a9d17SGeoff Levand return phys_addr; 141*f58a9d17SGeoff Levand else 142*f58a9d17SGeoff Levand return (phys_addr < map.rm.size || phys_addr >= map.total) 143*f58a9d17SGeoff Levand ? phys_addr : phys_addr + map.r1.offset; 144*f58a9d17SGeoff Levand } 145*f58a9d17SGeoff Levand 146*f58a9d17SGeoff Levand EXPORT_SYMBOL(ps3_mm_phys_to_lpar); 147*f58a9d17SGeoff Levand 148*f58a9d17SGeoff Levand /** 149*f58a9d17SGeoff Levand * ps3_mm_vas_create - create the virtual address space 150*f58a9d17SGeoff Levand */ 151*f58a9d17SGeoff Levand 152*f58a9d17SGeoff Levand void __init ps3_mm_vas_create(unsigned long* htab_size) 153*f58a9d17SGeoff Levand { 154*f58a9d17SGeoff Levand int result; 155*f58a9d17SGeoff Levand unsigned long start_address; 156*f58a9d17SGeoff Levand unsigned long size; 157*f58a9d17SGeoff Levand unsigned long access_right; 158*f58a9d17SGeoff Levand unsigned long max_page_size; 159*f58a9d17SGeoff Levand unsigned long flags; 160*f58a9d17SGeoff Levand 161*f58a9d17SGeoff Levand result = lv1_query_logical_partition_address_region_info(0, 162*f58a9d17SGeoff Levand &start_address, &size, &access_right, &max_page_size, 163*f58a9d17SGeoff Levand &flags); 164*f58a9d17SGeoff Levand 165*f58a9d17SGeoff Levand if (result) { 166*f58a9d17SGeoff Levand DBG("%s:%d: lv1_query_logical_partition_address_region_info " 167*f58a9d17SGeoff Levand "failed: %s\n", __func__, __LINE__, 168*f58a9d17SGeoff Levand ps3_result(result)); 169*f58a9d17SGeoff Levand goto fail; 170*f58a9d17SGeoff Levand } 171*f58a9d17SGeoff Levand 172*f58a9d17SGeoff Levand if (max_page_size < PAGE_SHIFT_16M) { 173*f58a9d17SGeoff Levand DBG("%s:%d: bad max_page_size %lxh\n", __func__, __LINE__, 174*f58a9d17SGeoff Levand max_page_size); 175*f58a9d17SGeoff Levand goto fail; 176*f58a9d17SGeoff Levand } 177*f58a9d17SGeoff Levand 178*f58a9d17SGeoff Levand BUILD_BUG_ON(CONFIG_PS3_HTAB_SIZE > HTAB_SIZE_MAX); 179*f58a9d17SGeoff Levand BUILD_BUG_ON(CONFIG_PS3_HTAB_SIZE < HTAB_SIZE_MIN); 180*f58a9d17SGeoff Levand 181*f58a9d17SGeoff Levand result = lv1_construct_virtual_address_space(CONFIG_PS3_HTAB_SIZE, 182*f58a9d17SGeoff Levand 2, make_page_sizes(PAGE_SHIFT_16M, PAGE_SHIFT_64K), 183*f58a9d17SGeoff Levand &map.vas_id, &map.htab_size); 184*f58a9d17SGeoff Levand 185*f58a9d17SGeoff Levand if (result) { 186*f58a9d17SGeoff Levand DBG("%s:%d: lv1_construct_virtual_address_space failed: %s\n", 187*f58a9d17SGeoff Levand __func__, __LINE__, ps3_result(result)); 188*f58a9d17SGeoff Levand goto fail; 189*f58a9d17SGeoff Levand } 190*f58a9d17SGeoff Levand 191*f58a9d17SGeoff Levand result = lv1_select_virtual_address_space(map.vas_id); 192*f58a9d17SGeoff Levand 193*f58a9d17SGeoff Levand if (result) { 194*f58a9d17SGeoff Levand DBG("%s:%d: lv1_select_virtual_address_space failed: %s\n", 195*f58a9d17SGeoff Levand __func__, __LINE__, ps3_result(result)); 196*f58a9d17SGeoff Levand goto fail; 197*f58a9d17SGeoff Levand } 198*f58a9d17SGeoff Levand 199*f58a9d17SGeoff Levand *htab_size = map.htab_size; 200*f58a9d17SGeoff Levand 201*f58a9d17SGeoff Levand debug_dump_map(&map); 202*f58a9d17SGeoff Levand 203*f58a9d17SGeoff Levand return; 204*f58a9d17SGeoff Levand 205*f58a9d17SGeoff Levand fail: 206*f58a9d17SGeoff Levand panic("ps3_mm_vas_create failed"); 207*f58a9d17SGeoff Levand } 208*f58a9d17SGeoff Levand 209*f58a9d17SGeoff Levand /** 210*f58a9d17SGeoff Levand * ps3_mm_vas_destroy - 211*f58a9d17SGeoff Levand */ 212*f58a9d17SGeoff Levand 213*f58a9d17SGeoff Levand void ps3_mm_vas_destroy(void) 214*f58a9d17SGeoff Levand { 215*f58a9d17SGeoff Levand if (map.vas_id) { 216*f58a9d17SGeoff Levand lv1_select_virtual_address_space(0); 217*f58a9d17SGeoff Levand lv1_destruct_virtual_address_space(map.vas_id); 218*f58a9d17SGeoff Levand map.vas_id = 0; 219*f58a9d17SGeoff Levand } 220*f58a9d17SGeoff Levand } 221*f58a9d17SGeoff Levand 222*f58a9d17SGeoff Levand /*============================================================================*/ 223*f58a9d17SGeoff Levand /* memory hotplug routines */ 224*f58a9d17SGeoff Levand /*============================================================================*/ 225*f58a9d17SGeoff Levand 226*f58a9d17SGeoff Levand /** 227*f58a9d17SGeoff Levand * ps3_mm_region_create - create a memory region in the vas 228*f58a9d17SGeoff Levand * @r: pointer to a struct mem_region to accept initialized values 229*f58a9d17SGeoff Levand * @size: requested region size 230*f58a9d17SGeoff Levand * 231*f58a9d17SGeoff Levand * This implementation creates the region with the vas large page size. 232*f58a9d17SGeoff Levand * @size is rounded down to a multiple of the vas large page size. 233*f58a9d17SGeoff Levand */ 234*f58a9d17SGeoff Levand 235*f58a9d17SGeoff Levand int ps3_mm_region_create(struct mem_region *r, unsigned long size) 236*f58a9d17SGeoff Levand { 237*f58a9d17SGeoff Levand int result; 238*f58a9d17SGeoff Levand unsigned long muid; 239*f58a9d17SGeoff Levand 240*f58a9d17SGeoff Levand r->size = _ALIGN_DOWN(size, 1 << PAGE_SHIFT_16M); 241*f58a9d17SGeoff Levand 242*f58a9d17SGeoff Levand DBG("%s:%d requested %lxh\n", __func__, __LINE__, size); 243*f58a9d17SGeoff Levand DBG("%s:%d actual %lxh\n", __func__, __LINE__, r->size); 244*f58a9d17SGeoff Levand DBG("%s:%d difference %lxh (%luMB)\n", __func__, __LINE__, 245*f58a9d17SGeoff Levand (unsigned long)(size - r->size), 246*f58a9d17SGeoff Levand (size - r->size) / 1024 / 1024); 247*f58a9d17SGeoff Levand 248*f58a9d17SGeoff Levand if (r->size == 0) { 249*f58a9d17SGeoff Levand DBG("%s:%d: size == 0\n", __func__, __LINE__); 250*f58a9d17SGeoff Levand result = -1; 251*f58a9d17SGeoff Levand goto zero_region; 252*f58a9d17SGeoff Levand } 253*f58a9d17SGeoff Levand 254*f58a9d17SGeoff Levand result = lv1_allocate_memory(r->size, PAGE_SHIFT_16M, 0, 255*f58a9d17SGeoff Levand ALLOCATE_MEMORY_TRY_ALT_UNIT, &r->base, &muid); 256*f58a9d17SGeoff Levand 257*f58a9d17SGeoff Levand if (result || r->base < map.rm.size) { 258*f58a9d17SGeoff Levand DBG("%s:%d: lv1_allocate_memory failed: %s\n", 259*f58a9d17SGeoff Levand __func__, __LINE__, ps3_result(result)); 260*f58a9d17SGeoff Levand goto zero_region; 261*f58a9d17SGeoff Levand } 262*f58a9d17SGeoff Levand 263*f58a9d17SGeoff Levand r->offset = r->base - map.rm.size; 264*f58a9d17SGeoff Levand return result; 265*f58a9d17SGeoff Levand 266*f58a9d17SGeoff Levand zero_region: 267*f58a9d17SGeoff Levand r->size = r->base = r->offset = 0; 268*f58a9d17SGeoff Levand return result; 269*f58a9d17SGeoff Levand } 270*f58a9d17SGeoff Levand 271*f58a9d17SGeoff Levand /** 272*f58a9d17SGeoff Levand * ps3_mm_region_destroy - destroy a memory region 273*f58a9d17SGeoff Levand * @r: pointer to struct mem_region 274*f58a9d17SGeoff Levand */ 275*f58a9d17SGeoff Levand 276*f58a9d17SGeoff Levand void ps3_mm_region_destroy(struct mem_region *r) 277*f58a9d17SGeoff Levand { 278*f58a9d17SGeoff Levand if (r->base) { 279*f58a9d17SGeoff Levand lv1_release_memory(r->base); 280*f58a9d17SGeoff Levand r->size = r->base = r->offset = 0; 281*f58a9d17SGeoff Levand map.total = map.rm.size; 282*f58a9d17SGeoff Levand } 283*f58a9d17SGeoff Levand } 284*f58a9d17SGeoff Levand 285*f58a9d17SGeoff Levand /** 286*f58a9d17SGeoff Levand * ps3_mm_add_memory - hot add memory 287*f58a9d17SGeoff Levand */ 288*f58a9d17SGeoff Levand 289*f58a9d17SGeoff Levand static int __init ps3_mm_add_memory(void) 290*f58a9d17SGeoff Levand { 291*f58a9d17SGeoff Levand int result; 292*f58a9d17SGeoff Levand unsigned long start_addr; 293*f58a9d17SGeoff Levand unsigned long start_pfn; 294*f58a9d17SGeoff Levand unsigned long nr_pages; 295*f58a9d17SGeoff Levand 296*f58a9d17SGeoff Levand BUG_ON(!mem_init_done); 297*f58a9d17SGeoff Levand 298*f58a9d17SGeoff Levand start_addr = USE_LPAR_ADDR ? map.r1.base : map.rm.size; 299*f58a9d17SGeoff Levand start_pfn = start_addr >> PAGE_SHIFT; 300*f58a9d17SGeoff Levand nr_pages = (map.r1.size + PAGE_SIZE - 1) >> PAGE_SHIFT; 301*f58a9d17SGeoff Levand 302*f58a9d17SGeoff Levand DBG("%s:%d: start_addr %lxh, start_pfn %lxh, nr_pages %lxh\n", 303*f58a9d17SGeoff Levand __func__, __LINE__, start_addr, start_pfn, nr_pages); 304*f58a9d17SGeoff Levand 305*f58a9d17SGeoff Levand result = add_memory(0, start_addr, map.r1.size); 306*f58a9d17SGeoff Levand 307*f58a9d17SGeoff Levand if (result) { 308*f58a9d17SGeoff Levand DBG("%s:%d: add_memory failed: (%d)\n", 309*f58a9d17SGeoff Levand __func__, __LINE__, result); 310*f58a9d17SGeoff Levand return result; 311*f58a9d17SGeoff Levand } 312*f58a9d17SGeoff Levand 313*f58a9d17SGeoff Levand result = online_pages(start_pfn, nr_pages); 314*f58a9d17SGeoff Levand 315*f58a9d17SGeoff Levand if (result) 316*f58a9d17SGeoff Levand DBG("%s:%d: online_pages failed: (%d)\n", 317*f58a9d17SGeoff Levand __func__, __LINE__, result); 318*f58a9d17SGeoff Levand 319*f58a9d17SGeoff Levand return result; 320*f58a9d17SGeoff Levand } 321*f58a9d17SGeoff Levand 322*f58a9d17SGeoff Levand core_initcall(ps3_mm_add_memory); 323*f58a9d17SGeoff Levand 324*f58a9d17SGeoff Levand /*============================================================================*/ 325*f58a9d17SGeoff Levand /* dma routines */ 326*f58a9d17SGeoff Levand /*============================================================================*/ 327*f58a9d17SGeoff Levand 328*f58a9d17SGeoff Levand /** 329*f58a9d17SGeoff Levand * dma_lpar_to_bus - Translate an lpar address to ioc mapped bus address. 330*f58a9d17SGeoff Levand * @r: pointer to dma region structure 331*f58a9d17SGeoff Levand * @lpar_addr: HV lpar address 332*f58a9d17SGeoff Levand */ 333*f58a9d17SGeoff Levand 334*f58a9d17SGeoff Levand static unsigned long dma_lpar_to_bus(struct ps3_dma_region *r, 335*f58a9d17SGeoff Levand unsigned long lpar_addr) 336*f58a9d17SGeoff Levand { 337*f58a9d17SGeoff Levand BUG_ON(lpar_addr >= map.r1.base + map.r1.size); 338*f58a9d17SGeoff Levand return r->bus_addr + (lpar_addr <= map.rm.size ? lpar_addr 339*f58a9d17SGeoff Levand : lpar_addr - map.r1.offset); 340*f58a9d17SGeoff Levand } 341*f58a9d17SGeoff Levand 342*f58a9d17SGeoff Levand #define dma_dump_region(_a) _dma_dump_region(_a, __func__, __LINE__) 343*f58a9d17SGeoff Levand static void _dma_dump_region(const struct ps3_dma_region *r, const char* func, 344*f58a9d17SGeoff Levand int line) 345*f58a9d17SGeoff Levand { 346*f58a9d17SGeoff Levand DBG("%s:%d: dev %u:%u\n", func, line, r->did.bus_id, 347*f58a9d17SGeoff Levand r->did.dev_id); 348*f58a9d17SGeoff Levand DBG("%s:%d: page_size %u\n", func, line, r->page_size); 349*f58a9d17SGeoff Levand DBG("%s:%d: bus_addr %lxh\n", func, line, r->bus_addr); 350*f58a9d17SGeoff Levand DBG("%s:%d: len %lxh\n", func, line, r->len); 351*f58a9d17SGeoff Levand } 352*f58a9d17SGeoff Levand 353*f58a9d17SGeoff Levand /** 354*f58a9d17SGeoff Levand * dma_chunk - A chunk of dma pages mapped by the io controller. 355*f58a9d17SGeoff Levand * @region - The dma region that owns this chunk. 356*f58a9d17SGeoff Levand * @lpar_addr: Starting lpar address of the area to map. 357*f58a9d17SGeoff Levand * @bus_addr: Starting ioc bus address of the area to map. 358*f58a9d17SGeoff Levand * @len: Length in bytes of the area to map. 359*f58a9d17SGeoff Levand * @link: A struct list_head used with struct ps3_dma_region.chunk_list, the 360*f58a9d17SGeoff Levand * list of all chuncks owned by the region. 361*f58a9d17SGeoff Levand * 362*f58a9d17SGeoff Levand * This implementation uses a very simple dma page manager 363*f58a9d17SGeoff Levand * based on the dma_chunk structure. This scheme assumes 364*f58a9d17SGeoff Levand * that all drivers use very well behaved dma ops. 365*f58a9d17SGeoff Levand */ 366*f58a9d17SGeoff Levand 367*f58a9d17SGeoff Levand struct dma_chunk { 368*f58a9d17SGeoff Levand struct ps3_dma_region *region; 369*f58a9d17SGeoff Levand unsigned long lpar_addr; 370*f58a9d17SGeoff Levand unsigned long bus_addr; 371*f58a9d17SGeoff Levand unsigned long len; 372*f58a9d17SGeoff Levand struct list_head link; 373*f58a9d17SGeoff Levand unsigned int usage_count; 374*f58a9d17SGeoff Levand }; 375*f58a9d17SGeoff Levand 376*f58a9d17SGeoff Levand #define dma_dump_chunk(_a) _dma_dump_chunk(_a, __func__, __LINE__) 377*f58a9d17SGeoff Levand static void _dma_dump_chunk (const struct dma_chunk* c, const char* func, 378*f58a9d17SGeoff Levand int line) 379*f58a9d17SGeoff Levand { 380*f58a9d17SGeoff Levand DBG("%s:%d: r.dev %u:%u\n", func, line, 381*f58a9d17SGeoff Levand c->region->did.bus_id, c->region->did.dev_id); 382*f58a9d17SGeoff Levand DBG("%s:%d: r.bus_addr %lxh\n", func, line, c->region->bus_addr); 383*f58a9d17SGeoff Levand DBG("%s:%d: r.page_size %u\n", func, line, c->region->page_size); 384*f58a9d17SGeoff Levand DBG("%s:%d: r.len %lxh\n", func, line, c->region->len); 385*f58a9d17SGeoff Levand DBG("%s:%d: c.lpar_addr %lxh\n", func, line, c->lpar_addr); 386*f58a9d17SGeoff Levand DBG("%s:%d: c.bus_addr %lxh\n", func, line, c->bus_addr); 387*f58a9d17SGeoff Levand DBG("%s:%d: c.len %lxh\n", func, line, c->len); 388*f58a9d17SGeoff Levand } 389*f58a9d17SGeoff Levand 390*f58a9d17SGeoff Levand static struct dma_chunk * dma_find_chunk(struct ps3_dma_region *r, 391*f58a9d17SGeoff Levand unsigned long bus_addr, unsigned long len) 392*f58a9d17SGeoff Levand { 393*f58a9d17SGeoff Levand struct dma_chunk *c; 394*f58a9d17SGeoff Levand unsigned long aligned_bus = _ALIGN_DOWN(bus_addr, 1 << r->page_size); 395*f58a9d17SGeoff Levand unsigned long aligned_len = _ALIGN_UP(len, 1 << r->page_size); 396*f58a9d17SGeoff Levand 397*f58a9d17SGeoff Levand list_for_each_entry(c, &r->chunk_list.head, link) { 398*f58a9d17SGeoff Levand /* intersection */ 399*f58a9d17SGeoff Levand if (aligned_bus >= c->bus_addr 400*f58a9d17SGeoff Levand && aligned_bus < c->bus_addr + c->len 401*f58a9d17SGeoff Levand && aligned_bus + aligned_len <= c->bus_addr + c->len) { 402*f58a9d17SGeoff Levand return c; 403*f58a9d17SGeoff Levand } 404*f58a9d17SGeoff Levand /* below */ 405*f58a9d17SGeoff Levand if (aligned_bus + aligned_len <= c->bus_addr) { 406*f58a9d17SGeoff Levand continue; 407*f58a9d17SGeoff Levand } 408*f58a9d17SGeoff Levand /* above */ 409*f58a9d17SGeoff Levand if (aligned_bus >= c->bus_addr + c->len) { 410*f58a9d17SGeoff Levand continue; 411*f58a9d17SGeoff Levand } 412*f58a9d17SGeoff Levand 413*f58a9d17SGeoff Levand /* we don't handle the multi-chunk case for now */ 414*f58a9d17SGeoff Levand 415*f58a9d17SGeoff Levand dma_dump_chunk(c); 416*f58a9d17SGeoff Levand BUG(); 417*f58a9d17SGeoff Levand } 418*f58a9d17SGeoff Levand return NULL; 419*f58a9d17SGeoff Levand } 420*f58a9d17SGeoff Levand 421*f58a9d17SGeoff Levand static int dma_free_chunk(struct dma_chunk *c) 422*f58a9d17SGeoff Levand { 423*f58a9d17SGeoff Levand int result = 0; 424*f58a9d17SGeoff Levand 425*f58a9d17SGeoff Levand if (c->bus_addr) { 426*f58a9d17SGeoff Levand result = lv1_unmap_device_dma_region(c->region->did.bus_id, 427*f58a9d17SGeoff Levand c->region->did.dev_id, c->bus_addr, c->len); 428*f58a9d17SGeoff Levand BUG_ON(result); 429*f58a9d17SGeoff Levand } 430*f58a9d17SGeoff Levand 431*f58a9d17SGeoff Levand kfree(c); 432*f58a9d17SGeoff Levand return result; 433*f58a9d17SGeoff Levand } 434*f58a9d17SGeoff Levand 435*f58a9d17SGeoff Levand /** 436*f58a9d17SGeoff Levand * dma_map_pages - Maps dma pages into the io controller bus address space. 437*f58a9d17SGeoff Levand * @r: Pointer to a struct ps3_dma_region. 438*f58a9d17SGeoff Levand * @phys_addr: Starting physical address of the area to map. 439*f58a9d17SGeoff Levand * @len: Length in bytes of the area to map. 440*f58a9d17SGeoff Levand * c_out: A pointer to receive an allocated struct dma_chunk for this area. 441*f58a9d17SGeoff Levand * 442*f58a9d17SGeoff Levand * This is the lowest level dma mapping routine, and is the one that will 443*f58a9d17SGeoff Levand * make the HV call to add the pages into the io controller address space. 444*f58a9d17SGeoff Levand */ 445*f58a9d17SGeoff Levand 446*f58a9d17SGeoff Levand static int dma_map_pages(struct ps3_dma_region *r, unsigned long phys_addr, 447*f58a9d17SGeoff Levand unsigned long len, struct dma_chunk **c_out) 448*f58a9d17SGeoff Levand { 449*f58a9d17SGeoff Levand int result; 450*f58a9d17SGeoff Levand struct dma_chunk *c; 451*f58a9d17SGeoff Levand 452*f58a9d17SGeoff Levand c = kzalloc(sizeof(struct dma_chunk), GFP_ATOMIC); 453*f58a9d17SGeoff Levand 454*f58a9d17SGeoff Levand if (!c) { 455*f58a9d17SGeoff Levand result = -ENOMEM; 456*f58a9d17SGeoff Levand goto fail_alloc; 457*f58a9d17SGeoff Levand } 458*f58a9d17SGeoff Levand 459*f58a9d17SGeoff Levand c->region = r; 460*f58a9d17SGeoff Levand c->lpar_addr = ps3_mm_phys_to_lpar(phys_addr); 461*f58a9d17SGeoff Levand c->bus_addr = dma_lpar_to_bus(r, c->lpar_addr); 462*f58a9d17SGeoff Levand c->len = len; 463*f58a9d17SGeoff Levand 464*f58a9d17SGeoff Levand result = lv1_map_device_dma_region(c->region->did.bus_id, 465*f58a9d17SGeoff Levand c->region->did.dev_id, c->lpar_addr, c->bus_addr, c->len, 466*f58a9d17SGeoff Levand 0xf800000000000000UL); 467*f58a9d17SGeoff Levand 468*f58a9d17SGeoff Levand if (result) { 469*f58a9d17SGeoff Levand DBG("%s:%d: lv1_map_device_dma_region failed: %s\n", 470*f58a9d17SGeoff Levand __func__, __LINE__, ps3_result(result)); 471*f58a9d17SGeoff Levand goto fail_map; 472*f58a9d17SGeoff Levand } 473*f58a9d17SGeoff Levand 474*f58a9d17SGeoff Levand list_add(&c->link, &r->chunk_list.head); 475*f58a9d17SGeoff Levand 476*f58a9d17SGeoff Levand *c_out = c; 477*f58a9d17SGeoff Levand return 0; 478*f58a9d17SGeoff Levand 479*f58a9d17SGeoff Levand fail_map: 480*f58a9d17SGeoff Levand kfree(c); 481*f58a9d17SGeoff Levand fail_alloc: 482*f58a9d17SGeoff Levand *c_out = NULL; 483*f58a9d17SGeoff Levand DBG(" <- %s:%d\n", __func__, __LINE__); 484*f58a9d17SGeoff Levand return result; 485*f58a9d17SGeoff Levand } 486*f58a9d17SGeoff Levand 487*f58a9d17SGeoff Levand /** 488*f58a9d17SGeoff Levand * dma_region_create - Create a device dma region. 489*f58a9d17SGeoff Levand * @r: Pointer to a struct ps3_dma_region. 490*f58a9d17SGeoff Levand * 491*f58a9d17SGeoff Levand * This is the lowest level dma region create routine, and is the one that 492*f58a9d17SGeoff Levand * will make the HV call to create the region. 493*f58a9d17SGeoff Levand */ 494*f58a9d17SGeoff Levand 495*f58a9d17SGeoff Levand static int dma_region_create(struct ps3_dma_region* r) 496*f58a9d17SGeoff Levand { 497*f58a9d17SGeoff Levand int result; 498*f58a9d17SGeoff Levand 499*f58a9d17SGeoff Levand r->len = _ALIGN_UP(map.total, 1 << r->page_size); 500*f58a9d17SGeoff Levand INIT_LIST_HEAD(&r->chunk_list.head); 501*f58a9d17SGeoff Levand spin_lock_init(&r->chunk_list.lock); 502*f58a9d17SGeoff Levand 503*f58a9d17SGeoff Levand result = lv1_allocate_device_dma_region(r->did.bus_id, r->did.dev_id, 504*f58a9d17SGeoff Levand r->len, r->page_size, r->region_type, &r->bus_addr); 505*f58a9d17SGeoff Levand 506*f58a9d17SGeoff Levand dma_dump_region(r); 507*f58a9d17SGeoff Levand 508*f58a9d17SGeoff Levand if (result) { 509*f58a9d17SGeoff Levand DBG("%s:%d: lv1_allocate_device_dma_region failed: %s\n", 510*f58a9d17SGeoff Levand __func__, __LINE__, ps3_result(result)); 511*f58a9d17SGeoff Levand r->len = r->bus_addr = 0; 512*f58a9d17SGeoff Levand } 513*f58a9d17SGeoff Levand 514*f58a9d17SGeoff Levand return result; 515*f58a9d17SGeoff Levand } 516*f58a9d17SGeoff Levand 517*f58a9d17SGeoff Levand /** 518*f58a9d17SGeoff Levand * dma_region_free - Free a device dma region. 519*f58a9d17SGeoff Levand * @r: Pointer to a struct ps3_dma_region. 520*f58a9d17SGeoff Levand * 521*f58a9d17SGeoff Levand * This is the lowest level dma region free routine, and is the one that 522*f58a9d17SGeoff Levand * will make the HV call to free the region. 523*f58a9d17SGeoff Levand */ 524*f58a9d17SGeoff Levand 525*f58a9d17SGeoff Levand static int dma_region_free(struct ps3_dma_region* r) 526*f58a9d17SGeoff Levand { 527*f58a9d17SGeoff Levand int result; 528*f58a9d17SGeoff Levand struct dma_chunk *c; 529*f58a9d17SGeoff Levand struct dma_chunk *tmp; 530*f58a9d17SGeoff Levand 531*f58a9d17SGeoff Levand list_for_each_entry_safe(c, tmp, &r->chunk_list.head, link) { 532*f58a9d17SGeoff Levand list_del(&c->link); 533*f58a9d17SGeoff Levand dma_free_chunk(c); 534*f58a9d17SGeoff Levand } 535*f58a9d17SGeoff Levand 536*f58a9d17SGeoff Levand result = lv1_free_device_dma_region(r->did.bus_id, r->did.dev_id, 537*f58a9d17SGeoff Levand r->bus_addr); 538*f58a9d17SGeoff Levand 539*f58a9d17SGeoff Levand if (result) 540*f58a9d17SGeoff Levand DBG("%s:%d: lv1_free_device_dma_region failed: %s\n", 541*f58a9d17SGeoff Levand __func__, __LINE__, ps3_result(result)); 542*f58a9d17SGeoff Levand 543*f58a9d17SGeoff Levand r->len = r->bus_addr = 0; 544*f58a9d17SGeoff Levand 545*f58a9d17SGeoff Levand return result; 546*f58a9d17SGeoff Levand } 547*f58a9d17SGeoff Levand 548*f58a9d17SGeoff Levand /** 549*f58a9d17SGeoff Levand * dma_map_area - Map an area of memory into a device dma region. 550*f58a9d17SGeoff Levand * @r: Pointer to a struct ps3_dma_region. 551*f58a9d17SGeoff Levand * @virt_addr: Starting virtual address of the area to map. 552*f58a9d17SGeoff Levand * @len: Length in bytes of the area to map. 553*f58a9d17SGeoff Levand * @bus_addr: A pointer to return the starting ioc bus address of the area to 554*f58a9d17SGeoff Levand * map. 555*f58a9d17SGeoff Levand * 556*f58a9d17SGeoff Levand * This is the common dma mapping routine. 557*f58a9d17SGeoff Levand */ 558*f58a9d17SGeoff Levand 559*f58a9d17SGeoff Levand static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr, 560*f58a9d17SGeoff Levand unsigned long len, unsigned long *bus_addr) 561*f58a9d17SGeoff Levand { 562*f58a9d17SGeoff Levand int result; 563*f58a9d17SGeoff Levand unsigned long flags; 564*f58a9d17SGeoff Levand struct dma_chunk *c; 565*f58a9d17SGeoff Levand unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr) 566*f58a9d17SGeoff Levand : virt_addr; 567*f58a9d17SGeoff Levand 568*f58a9d17SGeoff Levand *bus_addr = dma_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr)); 569*f58a9d17SGeoff Levand 570*f58a9d17SGeoff Levand if (!USE_DYNAMIC_DMA) { 571*f58a9d17SGeoff Levand unsigned long lpar_addr = ps3_mm_phys_to_lpar(phys_addr); 572*f58a9d17SGeoff Levand DBG(" -> %s:%d\n", __func__, __LINE__); 573*f58a9d17SGeoff Levand DBG("%s:%d virt_addr %lxh\n", __func__, __LINE__, 574*f58a9d17SGeoff Levand virt_addr); 575*f58a9d17SGeoff Levand DBG("%s:%d phys_addr %lxh\n", __func__, __LINE__, 576*f58a9d17SGeoff Levand phys_addr); 577*f58a9d17SGeoff Levand DBG("%s:%d lpar_addr %lxh\n", __func__, __LINE__, 578*f58a9d17SGeoff Levand lpar_addr); 579*f58a9d17SGeoff Levand DBG("%s:%d len %lxh\n", __func__, __LINE__, len); 580*f58a9d17SGeoff Levand DBG("%s:%d bus_addr %lxh (%lxh)\n", __func__, __LINE__, 581*f58a9d17SGeoff Levand *bus_addr, len); 582*f58a9d17SGeoff Levand } 583*f58a9d17SGeoff Levand 584*f58a9d17SGeoff Levand spin_lock_irqsave(&r->chunk_list.lock, flags); 585*f58a9d17SGeoff Levand c = dma_find_chunk(r, *bus_addr, len); 586*f58a9d17SGeoff Levand 587*f58a9d17SGeoff Levand if (c) { 588*f58a9d17SGeoff Levand c->usage_count++; 589*f58a9d17SGeoff Levand spin_unlock_irqrestore(&r->chunk_list.lock, flags); 590*f58a9d17SGeoff Levand return 0; 591*f58a9d17SGeoff Levand } 592*f58a9d17SGeoff Levand 593*f58a9d17SGeoff Levand result = dma_map_pages(r, _ALIGN_DOWN(phys_addr, 1 << r->page_size), 594*f58a9d17SGeoff Levand _ALIGN_UP(len, 1 << r->page_size), &c); 595*f58a9d17SGeoff Levand 596*f58a9d17SGeoff Levand if (result) { 597*f58a9d17SGeoff Levand *bus_addr = 0; 598*f58a9d17SGeoff Levand DBG("%s:%d: dma_map_pages failed (%d)\n", 599*f58a9d17SGeoff Levand __func__, __LINE__, result); 600*f58a9d17SGeoff Levand spin_unlock_irqrestore(&r->chunk_list.lock, flags); 601*f58a9d17SGeoff Levand return result; 602*f58a9d17SGeoff Levand } 603*f58a9d17SGeoff Levand 604*f58a9d17SGeoff Levand c->usage_count = 1; 605*f58a9d17SGeoff Levand 606*f58a9d17SGeoff Levand spin_unlock_irqrestore(&r->chunk_list.lock, flags); 607*f58a9d17SGeoff Levand return result; 608*f58a9d17SGeoff Levand } 609*f58a9d17SGeoff Levand 610*f58a9d17SGeoff Levand /** 611*f58a9d17SGeoff Levand * dma_unmap_area - Unmap an area of memory from a device dma region. 612*f58a9d17SGeoff Levand * @r: Pointer to a struct ps3_dma_region. 613*f58a9d17SGeoff Levand * @bus_addr: The starting ioc bus address of the area to unmap. 614*f58a9d17SGeoff Levand * @len: Length in bytes of the area to unmap. 615*f58a9d17SGeoff Levand * 616*f58a9d17SGeoff Levand * This is the common dma unmap routine. 617*f58a9d17SGeoff Levand */ 618*f58a9d17SGeoff Levand 619*f58a9d17SGeoff Levand int dma_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr, 620*f58a9d17SGeoff Levand unsigned long len) 621*f58a9d17SGeoff Levand { 622*f58a9d17SGeoff Levand unsigned long flags; 623*f58a9d17SGeoff Levand struct dma_chunk *c; 624*f58a9d17SGeoff Levand 625*f58a9d17SGeoff Levand spin_lock_irqsave(&r->chunk_list.lock, flags); 626*f58a9d17SGeoff Levand c = dma_find_chunk(r, bus_addr, len); 627*f58a9d17SGeoff Levand 628*f58a9d17SGeoff Levand if (!c) { 629*f58a9d17SGeoff Levand unsigned long aligned_bus = _ALIGN_DOWN(bus_addr, 630*f58a9d17SGeoff Levand 1 << r->page_size); 631*f58a9d17SGeoff Levand unsigned long aligned_len = _ALIGN_UP(len, 1 << r->page_size); 632*f58a9d17SGeoff Levand DBG("%s:%d: not found: bus_addr %lxh\n", 633*f58a9d17SGeoff Levand __func__, __LINE__, bus_addr); 634*f58a9d17SGeoff Levand DBG("%s:%d: not found: len %lxh\n", 635*f58a9d17SGeoff Levand __func__, __LINE__, len); 636*f58a9d17SGeoff Levand DBG("%s:%d: not found: aligned_bus %lxh\n", 637*f58a9d17SGeoff Levand __func__, __LINE__, aligned_bus); 638*f58a9d17SGeoff Levand DBG("%s:%d: not found: aligned_len %lxh\n", 639*f58a9d17SGeoff Levand __func__, __LINE__, aligned_len); 640*f58a9d17SGeoff Levand BUG(); 641*f58a9d17SGeoff Levand } 642*f58a9d17SGeoff Levand 643*f58a9d17SGeoff Levand c->usage_count--; 644*f58a9d17SGeoff Levand 645*f58a9d17SGeoff Levand if (!c->usage_count) { 646*f58a9d17SGeoff Levand list_del(&c->link); 647*f58a9d17SGeoff Levand dma_free_chunk(c); 648*f58a9d17SGeoff Levand } 649*f58a9d17SGeoff Levand 650*f58a9d17SGeoff Levand spin_unlock_irqrestore(&r->chunk_list.lock, flags); 651*f58a9d17SGeoff Levand return 0; 652*f58a9d17SGeoff Levand } 653*f58a9d17SGeoff Levand 654*f58a9d17SGeoff Levand /** 655*f58a9d17SGeoff Levand * dma_region_create_linear - Setup a linear dma maping for a device. 656*f58a9d17SGeoff Levand * @r: Pointer to a struct ps3_dma_region. 657*f58a9d17SGeoff Levand * 658*f58a9d17SGeoff Levand * This routine creates an HV dma region for the device and maps all available 659*f58a9d17SGeoff Levand * ram into the io controller bus address space. 660*f58a9d17SGeoff Levand */ 661*f58a9d17SGeoff Levand 662*f58a9d17SGeoff Levand static int dma_region_create_linear(struct ps3_dma_region *r) 663*f58a9d17SGeoff Levand { 664*f58a9d17SGeoff Levand int result; 665*f58a9d17SGeoff Levand unsigned long tmp; 666*f58a9d17SGeoff Levand 667*f58a9d17SGeoff Levand /* force 16M dma pages for linear mapping */ 668*f58a9d17SGeoff Levand 669*f58a9d17SGeoff Levand if (r->page_size != PS3_DMA_16M) { 670*f58a9d17SGeoff Levand pr_info("%s:%d: forcing 16M pages for linear map\n", 671*f58a9d17SGeoff Levand __func__, __LINE__); 672*f58a9d17SGeoff Levand r->page_size = PS3_DMA_16M; 673*f58a9d17SGeoff Levand } 674*f58a9d17SGeoff Levand 675*f58a9d17SGeoff Levand result = dma_region_create(r); 676*f58a9d17SGeoff Levand BUG_ON(result); 677*f58a9d17SGeoff Levand 678*f58a9d17SGeoff Levand result = dma_map_area(r, map.rm.base, map.rm.size, &tmp); 679*f58a9d17SGeoff Levand BUG_ON(result); 680*f58a9d17SGeoff Levand 681*f58a9d17SGeoff Levand if (USE_LPAR_ADDR) 682*f58a9d17SGeoff Levand result = dma_map_area(r, map.r1.base, map.r1.size, 683*f58a9d17SGeoff Levand &tmp); 684*f58a9d17SGeoff Levand else 685*f58a9d17SGeoff Levand result = dma_map_area(r, map.rm.size, map.r1.size, 686*f58a9d17SGeoff Levand &tmp); 687*f58a9d17SGeoff Levand 688*f58a9d17SGeoff Levand BUG_ON(result); 689*f58a9d17SGeoff Levand 690*f58a9d17SGeoff Levand return result; 691*f58a9d17SGeoff Levand } 692*f58a9d17SGeoff Levand 693*f58a9d17SGeoff Levand /** 694*f58a9d17SGeoff Levand * dma_region_free_linear - Free a linear dma mapping for a device. 695*f58a9d17SGeoff Levand * @r: Pointer to a struct ps3_dma_region. 696*f58a9d17SGeoff Levand * 697*f58a9d17SGeoff Levand * This routine will unmap all mapped areas and free the HV dma region. 698*f58a9d17SGeoff Levand */ 699*f58a9d17SGeoff Levand 700*f58a9d17SGeoff Levand static int dma_region_free_linear(struct ps3_dma_region *r) 701*f58a9d17SGeoff Levand { 702*f58a9d17SGeoff Levand int result; 703*f58a9d17SGeoff Levand 704*f58a9d17SGeoff Levand result = dma_unmap_area(r, dma_lpar_to_bus(r, 0), map.rm.size); 705*f58a9d17SGeoff Levand BUG_ON(result); 706*f58a9d17SGeoff Levand 707*f58a9d17SGeoff Levand result = dma_unmap_area(r, dma_lpar_to_bus(r, map.r1.base), 708*f58a9d17SGeoff Levand map.r1.size); 709*f58a9d17SGeoff Levand BUG_ON(result); 710*f58a9d17SGeoff Levand 711*f58a9d17SGeoff Levand result = dma_region_free(r); 712*f58a9d17SGeoff Levand BUG_ON(result); 713*f58a9d17SGeoff Levand 714*f58a9d17SGeoff Levand return result; 715*f58a9d17SGeoff Levand } 716*f58a9d17SGeoff Levand 717*f58a9d17SGeoff Levand /** 718*f58a9d17SGeoff Levand * dma_map_area_linear - Map an area of memory into a device dma region. 719*f58a9d17SGeoff Levand * @r: Pointer to a struct ps3_dma_region. 720*f58a9d17SGeoff Levand * @virt_addr: Starting virtual address of the area to map. 721*f58a9d17SGeoff Levand * @len: Length in bytes of the area to map. 722*f58a9d17SGeoff Levand * @bus_addr: A pointer to return the starting ioc bus address of the area to 723*f58a9d17SGeoff Levand * map. 724*f58a9d17SGeoff Levand * 725*f58a9d17SGeoff Levand * This routine just returns the coresponding bus address. Actual mapping 726*f58a9d17SGeoff Levand * occurs in dma_region_create_linear(). 727*f58a9d17SGeoff Levand */ 728*f58a9d17SGeoff Levand 729*f58a9d17SGeoff Levand static int dma_map_area_linear(struct ps3_dma_region *r, 730*f58a9d17SGeoff Levand unsigned long virt_addr, unsigned long len, unsigned long *bus_addr) 731*f58a9d17SGeoff Levand { 732*f58a9d17SGeoff Levand unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr) 733*f58a9d17SGeoff Levand : virt_addr; 734*f58a9d17SGeoff Levand *bus_addr = dma_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr)); 735*f58a9d17SGeoff Levand return 0; 736*f58a9d17SGeoff Levand } 737*f58a9d17SGeoff Levand 738*f58a9d17SGeoff Levand /** 739*f58a9d17SGeoff Levand * dma_unmap_area_linear - Unmap an area of memory from a device dma region. 740*f58a9d17SGeoff Levand * @r: Pointer to a struct ps3_dma_region. 741*f58a9d17SGeoff Levand * @bus_addr: The starting ioc bus address of the area to unmap. 742*f58a9d17SGeoff Levand * @len: Length in bytes of the area to unmap. 743*f58a9d17SGeoff Levand * 744*f58a9d17SGeoff Levand * This routine does nothing. Unmapping occurs in dma_region_free_linear(). 745*f58a9d17SGeoff Levand */ 746*f58a9d17SGeoff Levand 747*f58a9d17SGeoff Levand static int dma_unmap_area_linear(struct ps3_dma_region *r, 748*f58a9d17SGeoff Levand unsigned long bus_addr, unsigned long len) 749*f58a9d17SGeoff Levand { 750*f58a9d17SGeoff Levand return 0; 751*f58a9d17SGeoff Levand } 752*f58a9d17SGeoff Levand 753*f58a9d17SGeoff Levand int ps3_dma_region_create(struct ps3_dma_region *r) 754*f58a9d17SGeoff Levand { 755*f58a9d17SGeoff Levand return (USE_DYNAMIC_DMA) 756*f58a9d17SGeoff Levand ? dma_region_create(r) 757*f58a9d17SGeoff Levand : dma_region_create_linear(r); 758*f58a9d17SGeoff Levand } 759*f58a9d17SGeoff Levand 760*f58a9d17SGeoff Levand int ps3_dma_region_free(struct ps3_dma_region *r) 761*f58a9d17SGeoff Levand { 762*f58a9d17SGeoff Levand return (USE_DYNAMIC_DMA) 763*f58a9d17SGeoff Levand ? dma_region_free(r) 764*f58a9d17SGeoff Levand : dma_region_free_linear(r); 765*f58a9d17SGeoff Levand } 766*f58a9d17SGeoff Levand 767*f58a9d17SGeoff Levand int ps3_dma_map(struct ps3_dma_region *r, unsigned long virt_addr, 768*f58a9d17SGeoff Levand unsigned long len, unsigned long *bus_addr) 769*f58a9d17SGeoff Levand { 770*f58a9d17SGeoff Levand return (USE_DYNAMIC_DMA) 771*f58a9d17SGeoff Levand ? dma_map_area(r, virt_addr, len, bus_addr) 772*f58a9d17SGeoff Levand : dma_map_area_linear(r, virt_addr, len, bus_addr); 773*f58a9d17SGeoff Levand } 774*f58a9d17SGeoff Levand 775*f58a9d17SGeoff Levand int ps3_dma_unmap(struct ps3_dma_region *r, unsigned long bus_addr, 776*f58a9d17SGeoff Levand unsigned long len) 777*f58a9d17SGeoff Levand { 778*f58a9d17SGeoff Levand return (USE_DYNAMIC_DMA) ? dma_unmap_area(r, bus_addr, len) 779*f58a9d17SGeoff Levand : dma_unmap_area_linear(r, bus_addr, len); 780*f58a9d17SGeoff Levand } 781*f58a9d17SGeoff Levand 782*f58a9d17SGeoff Levand /*============================================================================*/ 783*f58a9d17SGeoff Levand /* system startup routines */ 784*f58a9d17SGeoff Levand /*============================================================================*/ 785*f58a9d17SGeoff Levand 786*f58a9d17SGeoff Levand /** 787*f58a9d17SGeoff Levand * ps3_mm_init - initialize the address space state variables 788*f58a9d17SGeoff Levand */ 789*f58a9d17SGeoff Levand 790*f58a9d17SGeoff Levand void __init ps3_mm_init(void) 791*f58a9d17SGeoff Levand { 792*f58a9d17SGeoff Levand int result; 793*f58a9d17SGeoff Levand 794*f58a9d17SGeoff Levand DBG(" -> %s:%d\n", __func__, __LINE__); 795*f58a9d17SGeoff Levand 796*f58a9d17SGeoff Levand result = ps3_repository_read_mm_info(&map.rm.base, &map.rm.size, 797*f58a9d17SGeoff Levand &map.total); 798*f58a9d17SGeoff Levand 799*f58a9d17SGeoff Levand if (result) 800*f58a9d17SGeoff Levand panic("ps3_repository_read_mm_info() failed"); 801*f58a9d17SGeoff Levand 802*f58a9d17SGeoff Levand map.rm.offset = map.rm.base; 803*f58a9d17SGeoff Levand map.vas_id = map.htab_size = 0; 804*f58a9d17SGeoff Levand 805*f58a9d17SGeoff Levand /* this implementation assumes map.rm.base is zero */ 806*f58a9d17SGeoff Levand 807*f58a9d17SGeoff Levand BUG_ON(map.rm.base); 808*f58a9d17SGeoff Levand BUG_ON(!map.rm.size); 809*f58a9d17SGeoff Levand 810*f58a9d17SGeoff Levand lmb_add(map.rm.base, map.rm.size); 811*f58a9d17SGeoff Levand lmb_analyze(); 812*f58a9d17SGeoff Levand 813*f58a9d17SGeoff Levand /* arrange to do this in ps3_mm_add_memory */ 814*f58a9d17SGeoff Levand ps3_mm_region_create(&map.r1, map.total - map.rm.size); 815*f58a9d17SGeoff Levand 816*f58a9d17SGeoff Levand DBG(" <- %s:%d\n", __func__, __LINE__); 817*f58a9d17SGeoff Levand } 818*f58a9d17SGeoff Levand 819*f58a9d17SGeoff Levand /** 820*f58a9d17SGeoff Levand * ps3_mm_shutdown - final cleanup of address space 821*f58a9d17SGeoff Levand */ 822*f58a9d17SGeoff Levand 823*f58a9d17SGeoff Levand void ps3_mm_shutdown(void) 824*f58a9d17SGeoff Levand { 825*f58a9d17SGeoff Levand ps3_mm_region_destroy(&map.r1); 826*f58a9d17SGeoff Levand map.total = map.rm.size; 827*f58a9d17SGeoff Levand } 828