1f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/*
2f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *  PS3 address space management.
3f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
4f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *  Copyright (C) 2006 Sony Computer Entertainment Inc.
5f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *  Copyright 2006 Sony Corp.
6f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
7f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *  This program is free software; you can redistribute it and/or modify
8f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *  it under the terms of the GNU General Public License as published by
9f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *  the Free Software Foundation; version 2 of the License.
10f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
11f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *  This program is distributed in the hope that it will be useful,
12f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *  GNU General Public License for more details.
15f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
16f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *  You should have received a copy of the GNU General Public License
17f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *  along with this program; if not, write to the Free Software
18f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
20f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
21f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand#include <linux/kernel.h>
224b16f8e2d6d64249f0ed3ca7fe2a319d0dde2719Paul Gortmaker#include <linux/export.h>
2395f72d1ed41a66f1c1c29c24d479de81a0bea36fYinghai Lu#include <linux/memblock.h>
245a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
25f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
269413c8836a16e9d034928a7f9d3ad81bebd71ce9Geert Uytterhoeven#include <asm/cell-regs.h>
27e22ba7e38144c1cccac5024cfd6ec88bb64d3e1fArnd Bergmann#include <asm/firmware.h>
28d9b2b2a277219d4812311d995054ce4f95067725David S. Miller#include <asm/prom.h>
29f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand#include <asm/udbg.h>
30f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand#include <asm/lv1call.h>
31ae3a197e3d0bfe3f4bf1693723e82dc018c096f3David Howells#include <asm/setup.h>
32f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
33f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand#include "platform.h"
34f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
35f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand#if defined(DEBUG)
3683bb643d0714b0006ab99dbd195ec51b55a97f4eGeert Uytterhoeven#define DBG udbg_printf
37f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand#else
387424639af480a05cac428ec7e7e38a11d6ff5734Michael Ellerman#define DBG pr_devel
39f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand#endif
40f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
41f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandenum {
42f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand#if defined(CONFIG_PS3_DYNAMIC_DMA)
43f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	USE_DYNAMIC_DMA = 1,
44f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand#else
45f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	USE_DYNAMIC_DMA = 0,
46f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand#endif
47f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand};
48f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
49f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandenum {
50f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	PAGE_SHIFT_4K = 12U,
51f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	PAGE_SHIFT_64K = 16U,
52f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	PAGE_SHIFT_16M = 24U,
53f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand};
54f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
55f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandstatic unsigned long make_page_sizes(unsigned long a, unsigned long b)
56f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
57f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	return (a << 56) | (b << 48);
58f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
59f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
60f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandenum {
61f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	ALLOCATE_MEMORY_TRY_ALT_UNIT = 0X04,
62f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	ALLOCATE_MEMORY_ADDR_ZERO = 0X08,
63f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand};
64f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
65f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/* valid htab sizes are {18,19,20} = 256K, 512K, 1M */
66f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
67f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandenum {
68f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	HTAB_SIZE_MAX = 20U, /* HV limit of 1MB */
69f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	HTAB_SIZE_MIN = 18U, /* CPU limit of 256KB */
70f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand};
71f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
72f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/*============================================================================*/
73f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/* virtual address space routines                                             */
74f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/*============================================================================*/
75f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
76f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
77f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * struct mem_region - memory region structure
78f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @base: base address
79f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @size: size in bytes
80f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @offset: difference between base and rm.size
811e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider * @destroy: flag if region should be destroyed upon shutdown
82f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
83f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
84f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandstruct mem_region {
85b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	u64 base;
865418b9c671a99727667cd499a2acbebe66e172ccStephen Rothwell	u64 size;
87f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	unsigned long offset;
881e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	int destroy;
89f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand};
90f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
91f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
92f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * struct map - address space state variables holder
93f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @total: total memory available as reported by HV
94f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @vas_id - HV virtual address space id
95f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @htab_size: htab size in bytes
96f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
97f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * The HV virtual address space (vas) allows for hotplug memory regions.
98f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * Memory regions can be created and destroyed in the vas at runtime.
99f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @rm: real mode (bootmem) region
1008ac5fd118cd137db5ca8c786778ba600f87111a0Hector Martin * @r1: highmem region(s)
101f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
102f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * ps3 addresses
103f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * virt_addr: a cpu 'translated' effective address
104f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * phys_addr: an address in what Linux thinks is the physical address space
105f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * lpar_addr: an address in the HV virtual address space
106f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * bus_addr: an io controller 'translated' address on a device bus
107f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
108f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
109f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandstruct map {
1105418b9c671a99727667cd499a2acbebe66e172ccStephen Rothwell	u64 total;
111b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	u64 vas_id;
112b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	u64 htab_size;
113f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	struct mem_region rm;
114f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	struct mem_region r1;
115f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand};
116f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
117f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand#define debug_dump_map(x) _debug_dump_map(x, __func__, __LINE__)
1186bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic void __maybe_unused _debug_dump_map(const struct map *m,
1196bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	const char *func, int line)
120f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
1215418b9c671a99727667cd499a2acbebe66e172ccStephen Rothwell	DBG("%s:%d: map.total     = %llxh\n", func, line, m->total);
1225418b9c671a99727667cd499a2acbebe66e172ccStephen Rothwell	DBG("%s:%d: map.rm.size   = %llxh\n", func, line, m->rm.size);
123b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	DBG("%s:%d: map.vas_id    = %llu\n", func, line, m->vas_id);
124b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	DBG("%s:%d: map.htab_size = %llxh\n", func, line, m->htab_size);
125b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	DBG("%s:%d: map.r1.base   = %llxh\n", func, line, m->r1.base);
126f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	DBG("%s:%d: map.r1.offset = %lxh\n", func, line, m->r1.offset);
1275418b9c671a99727667cd499a2acbebe66e172ccStephen Rothwell	DBG("%s:%d: map.r1.size   = %llxh\n", func, line, m->r1.size);
128f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
129f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
130f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandstatic struct map map;
131f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
132f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
133f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * ps3_mm_phys_to_lpar - translate a linux physical address to lpar address
134f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @phys_addr: linux physical address
135f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
136f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
137f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandunsigned long ps3_mm_phys_to_lpar(unsigned long phys_addr)
138f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
139f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	BUG_ON(is_kernel_addr(phys_addr));
140a628df1e9dd0d3de540bf0b7d8c6ea4896539bbbGeoff Levand	return (phys_addr < map.rm.size || phys_addr >= map.total)
141a628df1e9dd0d3de540bf0b7d8c6ea4896539bbbGeoff Levand		? phys_addr : phys_addr + map.r1.offset;
142f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
143f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
144f58a9d171a346afb1b09190427e6c28c6118703eGeoff LevandEXPORT_SYMBOL(ps3_mm_phys_to_lpar);
145f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
146f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
147f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * ps3_mm_vas_create - create the virtual address space
148f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
149f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
150f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandvoid __init ps3_mm_vas_create(unsigned long* htab_size)
151f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
152f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	int result;
153b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	u64 start_address;
154b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	u64 size;
155b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	u64 access_right;
156b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	u64 max_page_size;
157b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	u64 flags;
158f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
159f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	result = lv1_query_logical_partition_address_region_info(0,
160f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		&start_address, &size, &access_right, &max_page_size,
161f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		&flags);
162f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
163f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (result) {
164f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		DBG("%s:%d: lv1_query_logical_partition_address_region_info "
165f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			"failed: %s\n", __func__, __LINE__,
166f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			ps3_result(result));
167f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		goto fail;
168f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
169f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
170f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (max_page_size < PAGE_SHIFT_16M) {
171b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell		DBG("%s:%d: bad max_page_size %llxh\n", __func__, __LINE__,
172f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			max_page_size);
173f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		goto fail;
174f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
175f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
176f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	BUILD_BUG_ON(CONFIG_PS3_HTAB_SIZE > HTAB_SIZE_MAX);
177f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	BUILD_BUG_ON(CONFIG_PS3_HTAB_SIZE < HTAB_SIZE_MIN);
178f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
179f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	result = lv1_construct_virtual_address_space(CONFIG_PS3_HTAB_SIZE,
180f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			2, make_page_sizes(PAGE_SHIFT_16M, PAGE_SHIFT_64K),
181f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			&map.vas_id, &map.htab_size);
182f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
183f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (result) {
184f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		DBG("%s:%d: lv1_construct_virtual_address_space failed: %s\n",
185f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			__func__, __LINE__, ps3_result(result));
186f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		goto fail;
187f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
188f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
189f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	result = lv1_select_virtual_address_space(map.vas_id);
190f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
191f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (result) {
192f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		DBG("%s:%d: lv1_select_virtual_address_space failed: %s\n",
193f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			__func__, __LINE__, ps3_result(result));
194f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		goto fail;
195f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
196f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
197f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	*htab_size = map.htab_size;
198f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
199f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	debug_dump_map(&map);
200f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
201f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	return;
202f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
203f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandfail:
204f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	panic("ps3_mm_vas_create failed");
205f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
206f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
207f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
208f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * ps3_mm_vas_destroy -
209f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
210f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
211f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandvoid ps3_mm_vas_destroy(void)
212f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
2136bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	int result;
2146bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
215b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	DBG("%s:%d: map.vas_id    = %llu\n", __func__, __LINE__, map.vas_id);
2166bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
217f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (map.vas_id) {
2186bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		result = lv1_select_virtual_address_space(0);
2196bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		BUG_ON(result);
2206bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		result = lv1_destruct_virtual_address_space(map.vas_id);
2216bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		BUG_ON(result);
222f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		map.vas_id = 0;
223f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
224f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
225f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
226f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
227f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * ps3_mm_region_create - create a memory region in the vas
228f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @r: pointer to a struct mem_region to accept initialized values
229f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @size: requested region size
230f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
231f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * This implementation creates the region with the vas large page size.
232f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @size is rounded down to a multiple of the vas large page size.
233f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
234f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
23532f44a12e0674499c4db09b08da0dfa576a91d84Geert Uytterhoevenstatic int ps3_mm_region_create(struct mem_region *r, unsigned long size)
236f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
237f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	int result;
238b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	u64 muid;
239f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
240f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	r->size = _ALIGN_DOWN(size, 1 << PAGE_SHIFT_16M);
241f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
242f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	DBG("%s:%d requested  %lxh\n", __func__, __LINE__, size);
2435418b9c671a99727667cd499a2acbebe66e172ccStephen Rothwell	DBG("%s:%d actual     %llxh\n", __func__, __LINE__, r->size);
2445418b9c671a99727667cd499a2acbebe66e172ccStephen Rothwell	DBG("%s:%d difference %llxh (%lluMB)\n", __func__, __LINE__,
2455418b9c671a99727667cd499a2acbebe66e172ccStephen Rothwell		size - r->size, (size - r->size) / 1024 / 1024);
246f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
247f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (r->size == 0) {
248f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		DBG("%s:%d: size == 0\n", __func__, __LINE__);
249f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		result = -1;
250f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		goto zero_region;
251f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
252f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
253f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	result = lv1_allocate_memory(r->size, PAGE_SHIFT_16M, 0,
254f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		ALLOCATE_MEMORY_TRY_ALT_UNIT, &r->base, &muid);
255f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
256f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (result || r->base < map.rm.size) {
257f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		DBG("%s:%d: lv1_allocate_memory failed: %s\n",
258f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			__func__, __LINE__, ps3_result(result));
259f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		goto zero_region;
260f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
261f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
2621e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	r->destroy = 1;
263f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	r->offset = r->base - map.rm.size;
264f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	return result;
265f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
266f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandzero_region:
267f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	r->size = r->base = r->offset = 0;
268f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	return result;
269f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
270f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
271f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
272f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * ps3_mm_region_destroy - destroy a memory region
273f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @r: pointer to struct mem_region
274f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
275f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
27632f44a12e0674499c4db09b08da0dfa576a91d84Geert Uytterhoevenstatic void ps3_mm_region_destroy(struct mem_region *r)
277f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
2786bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	int result;
2796bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
2801e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	if (!r->destroy) {
2811e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider		pr_info("%s:%d: Not destroying high region: %llxh %llxh\n",
2821e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider			__func__, __LINE__, r->base, r->size);
2831e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider		return;
2841e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	}
2851e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider
286b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	DBG("%s:%d: r->base = %llxh\n", __func__, __LINE__, r->base);
2871e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider
288f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (r->base) {
2896bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		result = lv1_release_memory(r->base);
2906bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		BUG_ON(result);
291f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		r->size = r->base = r->offset = 0;
292f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		map.total = map.rm.size;
293f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
294f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
295f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
2961e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heiderstatic int ps3_mm_get_repository_highmem(struct mem_region *r)
2971e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider{
2981e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	int result;
2991e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider
3001e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	/* Assume a single highmem region. */
3011e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider
3021e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	result = ps3_repository_read_highmem_info(0, &r->base, &r->size);
3031e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider
3041e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	if (result)
3051e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider		goto zero_region;
3061e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider
3071e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	if (!r->base || !r->size) {
3081e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider		result = -1;
3091e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider		goto zero_region;
3101e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	}
3111e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider
3121e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	r->offset = r->base - map.rm.size;
3131e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider
3141e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	DBG("%s:%d: Found high region in repository: %llxh %llxh\n",
3151e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	    __func__, __LINE__, r->base, r->size);
3161e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider
3171e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	return 0;
3181e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider
3191e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heiderzero_region:
3201e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	DBG("%s:%d: No high region in repository.\n", __func__, __LINE__);
3211e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider
3221e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	r->size = r->base = r->offset = 0;
3231e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	return result;
3241e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider}
3251e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider
326f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/*============================================================================*/
327f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/* dma routines                                                               */
328f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/*============================================================================*/
329f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
330f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
3316bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand * dma_sb_lpar_to_bus - Translate an lpar address to ioc mapped bus address.
332f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @r: pointer to dma region structure
333f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @lpar_addr: HV lpar address
334f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
335f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
3366bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic unsigned long dma_sb_lpar_to_bus(struct ps3_dma_region *r,
337f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	unsigned long lpar_addr)
338f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
3396bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (lpar_addr >= map.rm.size)
3406bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		lpar_addr -= map.r1.offset;
3416bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	BUG_ON(lpar_addr < r->offset);
3426bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	BUG_ON(lpar_addr >= r->offset + r->len);
3436bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	return r->bus_addr + lpar_addr - r->offset;
344f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
345f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
346f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand#define dma_dump_region(_a) _dma_dump_region(_a, __func__, __LINE__)
3476bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic void  __maybe_unused _dma_dump_region(const struct ps3_dma_region *r,
3486bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	const char *func, int line)
349f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
3505c949070c7a591d1001a5d8444731dfa4223b094Stephen Rothwell	DBG("%s:%d: dev        %llu:%llu\n", func, line, r->dev->bus_id,
3516bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		r->dev->dev_id);
352f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	DBG("%s:%d: page_size  %u\n", func, line, r->page_size);
353f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	DBG("%s:%d: bus_addr   %lxh\n", func, line, r->bus_addr);
354f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	DBG("%s:%d: len        %lxh\n", func, line, r->len);
3556bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	DBG("%s:%d: offset     %lxh\n", func, line, r->offset);
356f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
357f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
3586bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand  /**
359f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * dma_chunk - A chunk of dma pages mapped by the io controller.
360f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @region - The dma region that owns this chunk.
361f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @lpar_addr: Starting lpar address of the area to map.
362f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @bus_addr: Starting ioc bus address of the area to map.
363f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @len: Length in bytes of the area to map.
364f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @link: A struct list_head used with struct ps3_dma_region.chunk_list, the
365f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * list of all chuncks owned by the region.
366f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
367f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * This implementation uses a very simple dma page manager
368f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * based on the dma_chunk structure.  This scheme assumes
369f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * that all drivers use very well behaved dma ops.
370f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
371f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
372f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandstruct dma_chunk {
373f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	struct ps3_dma_region *region;
374f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	unsigned long lpar_addr;
375f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	unsigned long bus_addr;
376f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	unsigned long len;
377f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	struct list_head link;
378f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	unsigned int usage_count;
379f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand};
380f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
381f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand#define dma_dump_chunk(_a) _dma_dump_chunk(_a, __func__, __LINE__)
382f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandstatic void _dma_dump_chunk (const struct dma_chunk* c, const char* func,
383f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	int line)
384f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
3855c949070c7a591d1001a5d8444731dfa4223b094Stephen Rothwell	DBG("%s:%d: r.dev        %llu:%llu\n", func, line,
3866bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		c->region->dev->bus_id, c->region->dev->dev_id);
387f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	DBG("%s:%d: r.bus_addr   %lxh\n", func, line, c->region->bus_addr);
388f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	DBG("%s:%d: r.page_size  %u\n", func, line, c->region->page_size);
389f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	DBG("%s:%d: r.len        %lxh\n", func, line, c->region->len);
3906bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	DBG("%s:%d: r.offset     %lxh\n", func, line, c->region->offset);
391f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	DBG("%s:%d: c.lpar_addr  %lxh\n", func, line, c->lpar_addr);
392f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	DBG("%s:%d: c.bus_addr   %lxh\n", func, line, c->bus_addr);
393f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	DBG("%s:%d: c.len        %lxh\n", func, line, c->len);
394f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
395f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
396f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandstatic struct dma_chunk * dma_find_chunk(struct ps3_dma_region *r,
397f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	unsigned long bus_addr, unsigned long len)
398f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
399f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	struct dma_chunk *c;
400f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	unsigned long aligned_bus = _ALIGN_DOWN(bus_addr, 1 << r->page_size);
4016bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	unsigned long aligned_len = _ALIGN_UP(len+bus_addr-aligned_bus,
4026bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand					      1 << r->page_size);
403f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
404f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	list_for_each_entry(c, &r->chunk_list.head, link) {
405f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		/* intersection */
4066bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		if (aligned_bus >= c->bus_addr &&
4076bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		    aligned_bus + aligned_len <= c->bus_addr + c->len)
408f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			return c;
4096bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
410f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		/* below */
4116bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		if (aligned_bus + aligned_len <= c->bus_addr)
412f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			continue;
4136bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
414f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		/* above */
4156bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		if (aligned_bus >= c->bus_addr + c->len)
416f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			continue;
417f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
418f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		/* we don't handle the multi-chunk case for now */
419f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		dma_dump_chunk(c);
420f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		BUG();
421f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
422f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	return NULL;
423f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
424f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
4256bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic struct dma_chunk *dma_find_chunk_lpar(struct ps3_dma_region *r,
4266bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	unsigned long lpar_addr, unsigned long len)
4276bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand{
4286bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	struct dma_chunk *c;
4296bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	unsigned long aligned_lpar = _ALIGN_DOWN(lpar_addr, 1 << r->page_size);
4306bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	unsigned long aligned_len = _ALIGN_UP(len + lpar_addr - aligned_lpar,
4316bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand					      1 << r->page_size);
4326bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
4336bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	list_for_each_entry(c, &r->chunk_list.head, link) {
4346bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		/* intersection */
4356bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		if (c->lpar_addr <= aligned_lpar &&
4366bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		    aligned_lpar < c->lpar_addr + c->len) {
4376bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			if (aligned_lpar + aligned_len <= c->lpar_addr + c->len)
4386bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand				return c;
4396bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			else {
4406bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand				dma_dump_chunk(c);
4416bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand				BUG();
4426bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			}
4436bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		}
4446bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		/* below */
4456bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		if (aligned_lpar + aligned_len <= c->lpar_addr) {
4466bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			continue;
4476bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		}
4486bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		/* above */
4496bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		if (c->lpar_addr + c->len <= aligned_lpar) {
4506bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			continue;
4516bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		}
4526bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
4536bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	return NULL;
4546bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand}
4556bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
4566bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic int dma_sb_free_chunk(struct dma_chunk *c)
457f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
458f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	int result = 0;
459f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
460f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (c->bus_addr) {
4616bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		result = lv1_unmap_device_dma_region(c->region->dev->bus_id,
4626bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			c->region->dev->dev_id, c->bus_addr, c->len);
463f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		BUG_ON(result);
464f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
465f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
466f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	kfree(c);
467f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	return result;
468f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
469f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
4706bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic int dma_ioc0_free_chunk(struct dma_chunk *c)
4716bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand{
4726bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	int result = 0;
4736bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	int iopage;
4746bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	unsigned long offset;
4756bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	struct ps3_dma_region *r = c->region;
4766bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
4776bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	DBG("%s:start\n", __func__);
4786bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	for (iopage = 0; iopage < (c->len >> r->page_size); iopage++) {
4796bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		offset = (1 << r->page_size) * iopage;
4806bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		/* put INVALID entry */
4816bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		result = lv1_put_iopte(0,
4826bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand				       c->bus_addr + offset,
4836bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand				       c->lpar_addr + offset,
4846bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand				       r->ioid,
4856bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand				       0);
4866bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		DBG("%s: bus=%#lx, lpar=%#lx, ioid=%d\n", __func__,
4876bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		    c->bus_addr + offset,
4886bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		    c->lpar_addr + offset,
4896bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		    r->ioid);
4906bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
4916bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		if (result) {
4926bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			DBG("%s:%d: lv1_put_iopte failed: %s\n", __func__,
4936bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			    __LINE__, ps3_result(result));
4946bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		}
4956bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
4966bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	kfree(c);
4976bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	DBG("%s:end\n", __func__);
4986bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	return result;
4996bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand}
5006bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
501f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
5026bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand * dma_sb_map_pages - Maps dma pages into the io controller bus address space.
503f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @r: Pointer to a struct ps3_dma_region.
504f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @phys_addr: Starting physical address of the area to map.
505f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @len: Length in bytes of the area to map.
506f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * c_out: A pointer to receive an allocated struct dma_chunk for this area.
507f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
508f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * This is the lowest level dma mapping routine, and is the one that will
509f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * make the HV call to add the pages into the io controller address space.
510f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
511f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
5126bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic int dma_sb_map_pages(struct ps3_dma_region *r, unsigned long phys_addr,
5136bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	    unsigned long len, struct dma_chunk **c_out, u64 iopte_flag)
514f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
515f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	int result;
516f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	struct dma_chunk *c;
517f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
518f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	c = kzalloc(sizeof(struct dma_chunk), GFP_ATOMIC);
519f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
520f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (!c) {
521f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		result = -ENOMEM;
522f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		goto fail_alloc;
523f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
524f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
525f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	c->region = r;
526f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	c->lpar_addr = ps3_mm_phys_to_lpar(phys_addr);
5276bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	c->bus_addr = dma_sb_lpar_to_bus(r, c->lpar_addr);
528f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	c->len = len;
529f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
5306bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	BUG_ON(iopte_flag != 0xf800000000000000UL);
5316bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	result = lv1_map_device_dma_region(c->region->dev->bus_id,
5326bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand					   c->region->dev->dev_id, c->lpar_addr,
5336bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand					   c->bus_addr, c->len, iopte_flag);
534f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (result) {
535f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		DBG("%s:%d: lv1_map_device_dma_region failed: %s\n",
536f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			__func__, __LINE__, ps3_result(result));
537f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		goto fail_map;
538f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
539f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
540f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	list_add(&c->link, &r->chunk_list.head);
541f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
542f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	*c_out = c;
543f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	return 0;
544f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
545f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandfail_map:
546f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	kfree(c);
547f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandfail_alloc:
548f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	*c_out = NULL;
549f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	DBG(" <- %s:%d\n", __func__, __LINE__);
550f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	return result;
551f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
552f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
5536bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic int dma_ioc0_map_pages(struct ps3_dma_region *r, unsigned long phys_addr,
5546bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			      unsigned long len, struct dma_chunk **c_out,
5556bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			      u64 iopte_flag)
5566bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand{
5576bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	int result;
5586bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	struct dma_chunk *c, *last;
5596bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	int iopage, pages;
5606bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	unsigned long offset;
5616bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
5626bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	DBG(KERN_ERR "%s: phy=%#lx, lpar%#lx, len=%#lx\n", __func__,
5636bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	    phys_addr, ps3_mm_phys_to_lpar(phys_addr), len);
5646bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	c = kzalloc(sizeof(struct dma_chunk), GFP_ATOMIC);
5656bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
5666bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (!c) {
5676bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		result = -ENOMEM;
5686bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		goto fail_alloc;
5696bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
5706bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
5716bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	c->region = r;
5726bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	c->len = len;
5736bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	c->lpar_addr = ps3_mm_phys_to_lpar(phys_addr);
5746bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	/* allocate IO address */
5756bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (list_empty(&r->chunk_list.head)) {
5766bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		/* first one */
5776bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		c->bus_addr = r->bus_addr;
5786bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	} else {
5796bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		/* derive from last bus addr*/
5806bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		last  = list_entry(r->chunk_list.head.next,
5816bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand				   struct dma_chunk, link);
5826bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		c->bus_addr = last->bus_addr + last->len;
5836bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		DBG("%s: last bus=%#lx, len=%#lx\n", __func__,
5846bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		    last->bus_addr, last->len);
5856bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
5866bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
5876bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	/* FIXME: check whether length exceeds region size */
5886bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
5896bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	/* build ioptes for the area */
5906bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	pages = len >> r->page_size;
5915c949070c7a591d1001a5d8444731dfa4223b094Stephen Rothwell	DBG("%s: pgsize=%#x len=%#lx pages=%#x iopteflag=%#llx\n", __func__,
5926bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	    r->page_size, r->len, pages, iopte_flag);
5936bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	for (iopage = 0; iopage < pages; iopage++) {
5946bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		offset = (1 << r->page_size) * iopage;
5956bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		result = lv1_put_iopte(0,
5966bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand				       c->bus_addr + offset,
5976bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand				       c->lpar_addr + offset,
5986bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand				       r->ioid,
5996bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand				       iopte_flag);
6006bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		if (result) {
6017e28060a5e04966d20c04c232b2f438f96e0a29eGeert Uytterhoeven			pr_warning("%s:%d: lv1_put_iopte failed: %s\n",
6027e28060a5e04966d20c04c232b2f438f96e0a29eGeert Uytterhoeven				   __func__, __LINE__, ps3_result(result));
6036bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			goto fail_map;
6046bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		}
6056bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		DBG("%s: pg=%d bus=%#lx, lpar=%#lx, ioid=%#x\n", __func__,
6066bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		    iopage, c->bus_addr + offset, c->lpar_addr + offset,
6076bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		    r->ioid);
6086bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
6096bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
6106bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	/* be sure that last allocated one is inserted at head */
6116bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	list_add(&c->link, &r->chunk_list.head);
6126bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
6136bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	*c_out = c;
6146bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	DBG("%s: end\n", __func__);
6156bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	return 0;
6166bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
6176bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandfail_map:
6186bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	for (iopage--; 0 <= iopage; iopage--) {
6196bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		lv1_put_iopte(0,
6206bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			      c->bus_addr + offset,
6216bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			      c->lpar_addr + offset,
6226bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			      r->ioid,
6236bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			      0);
6246bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
6256bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	kfree(c);
6266bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandfail_alloc:
6276bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	*c_out = NULL;
6286bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	return result;
6296bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand}
6306bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
631f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
6326bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand * dma_sb_region_create - Create a device dma region.
633f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @r: Pointer to a struct ps3_dma_region.
634f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
635f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * This is the lowest level dma region create routine, and is the one that
636f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * will make the HV call to create the region.
637f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
638f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
6396bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic int dma_sb_region_create(struct ps3_dma_region *r)
640f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
641f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	int result;
642b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	u64 bus_addr;
643f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
64462d80749addc969803a173573889f9b01d2459e1Geoff Levand	DBG(" -> %s:%d:\n", __func__, __LINE__);
6456bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
6466bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	BUG_ON(!r);
6476bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
6486bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (!r->dev->bus_id) {
6495c949070c7a591d1001a5d8444731dfa4223b094Stephen Rothwell		pr_info("%s:%d: %llu:%llu no dma\n", __func__, __LINE__,
6506bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			r->dev->bus_id, r->dev->dev_id);
6516bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		return 0;
6526bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
6536bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
6546bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	DBG("%s:%u: len = 0x%lx, page_size = %u, offset = 0x%lx\n", __func__,
6556bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	    __LINE__, r->len, r->page_size, r->offset);
6566bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
6576bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	BUG_ON(!r->len);
6586bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	BUG_ON(!r->page_size);
6596bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	BUG_ON(!r->region_ops);
6606bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
661f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	INIT_LIST_HEAD(&r->chunk_list.head);
662f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	spin_lock_init(&r->chunk_list.lock);
663f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
6646bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	result = lv1_allocate_device_dma_region(r->dev->bus_id, r->dev->dev_id,
6656bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		roundup_pow_of_two(r->len), r->page_size, r->region_type,
666b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell		&bus_addr);
667b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	r->bus_addr = bus_addr;
668f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
669f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (result) {
670f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		DBG("%s:%d: lv1_allocate_device_dma_region failed: %s\n",
671f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			__func__, __LINE__, ps3_result(result));
672f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		r->len = r->bus_addr = 0;
673f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
674f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
675f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	return result;
676f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
677f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
6786bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic int dma_ioc0_region_create(struct ps3_dma_region *r)
6796bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand{
6806bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	int result;
681b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	u64 bus_addr;
6826bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
6836bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	INIT_LIST_HEAD(&r->chunk_list.head);
6846bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	spin_lock_init(&r->chunk_list.lock);
6856bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
6866bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	result = lv1_allocate_io_segment(0,
6876bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand					 r->len,
6886bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand					 r->page_size,
689b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell					 &bus_addr);
690b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	r->bus_addr = bus_addr;
6916bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (result) {
6926bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		DBG("%s:%d: lv1_allocate_io_segment failed: %s\n",
6936bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			__func__, __LINE__, ps3_result(result));
6946bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		r->len = r->bus_addr = 0;
6956bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
6966bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	DBG("%s: len=%#lx, pg=%d, bus=%#lx\n", __func__,
6976bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	    r->len, r->page_size, r->bus_addr);
6986bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	return result;
6996bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand}
7006bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
701f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
702f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * dma_region_free - Free a device dma region.
703f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @r: Pointer to a struct ps3_dma_region.
704f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
705f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * This is the lowest level dma region free routine, and is the one that
706f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * will make the HV call to free the region.
707f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
708f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
7096bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic int dma_sb_region_free(struct ps3_dma_region *r)
710f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
711f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	int result;
712f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	struct dma_chunk *c;
713f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	struct dma_chunk *tmp;
714f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
7156bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	BUG_ON(!r);
7166bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
7176bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (!r->dev->bus_id) {
7185c949070c7a591d1001a5d8444731dfa4223b094Stephen Rothwell		pr_info("%s:%d: %llu:%llu no dma\n", __func__, __LINE__,
7196bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			r->dev->bus_id, r->dev->dev_id);
7206bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		return 0;
7216bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
7226bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
723f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	list_for_each_entry_safe(c, tmp, &r->chunk_list.head, link) {
724f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		list_del(&c->link);
7256bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		dma_sb_free_chunk(c);
726f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
727f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
7286bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	result = lv1_free_device_dma_region(r->dev->bus_id, r->dev->dev_id,
729f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		r->bus_addr);
730f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
731f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (result)
732f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		DBG("%s:%d: lv1_free_device_dma_region failed: %s\n",
733f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			__func__, __LINE__, ps3_result(result));
734f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
7356bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	r->bus_addr = 0;
7366bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
7376bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	return result;
7386bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand}
7396bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
7406bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic int dma_ioc0_region_free(struct ps3_dma_region *r)
7416bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand{
7426bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	int result;
7436bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	struct dma_chunk *c, *n;
7446bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
7456bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	DBG("%s: start\n", __func__);
7466bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	list_for_each_entry_safe(c, n, &r->chunk_list.head, link) {
7476bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		list_del(&c->link);
7486bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		dma_ioc0_free_chunk(c);
7496bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
7506bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
7516bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	result = lv1_release_io_segment(0, r->bus_addr);
7526bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
7536bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (result)
7546bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		DBG("%s:%d: lv1_free_device_dma_region failed: %s\n",
7556bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			__func__, __LINE__, ps3_result(result));
7566bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
7576bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	r->bus_addr = 0;
7586bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	DBG("%s: end\n", __func__);
759f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
760f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	return result;
761f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
762f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
763f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
7646bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand * dma_sb_map_area - Map an area of memory into a device dma region.
765f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @r: Pointer to a struct ps3_dma_region.
766f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @virt_addr: Starting virtual address of the area to map.
767f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @len: Length in bytes of the area to map.
768f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @bus_addr: A pointer to return the starting ioc bus address of the area to
769f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * map.
770f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
771f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * This is the common dma mapping routine.
772f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
773f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
7746bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic int dma_sb_map_area(struct ps3_dma_region *r, unsigned long virt_addr,
775494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwell	   unsigned long len, dma_addr_t *bus_addr,
7766bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	   u64 iopte_flag)
777f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
778f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	int result;
779f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	unsigned long flags;
780f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	struct dma_chunk *c;
781f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr)
782f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		: virt_addr;
7836bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	unsigned long aligned_phys = _ALIGN_DOWN(phys_addr, 1 << r->page_size);
7846bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	unsigned long aligned_len = _ALIGN_UP(len + phys_addr - aligned_phys,
7856bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand					      1 << r->page_size);
7866bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	*bus_addr = dma_sb_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr));
787f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
788f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (!USE_DYNAMIC_DMA) {
789f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		unsigned long lpar_addr = ps3_mm_phys_to_lpar(phys_addr);
790f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		DBG(" -> %s:%d\n", __func__, __LINE__);
791f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		DBG("%s:%d virt_addr %lxh\n", __func__, __LINE__,
792f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			virt_addr);
793f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		DBG("%s:%d phys_addr %lxh\n", __func__, __LINE__,
794f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			phys_addr);
795f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		DBG("%s:%d lpar_addr %lxh\n", __func__, __LINE__,
796f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			lpar_addr);
797f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		DBG("%s:%d len       %lxh\n", __func__, __LINE__, len);
798494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwell		DBG("%s:%d bus_addr  %llxh (%lxh)\n", __func__, __LINE__,
799f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		*bus_addr, len);
800f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
801f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
802f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	spin_lock_irqsave(&r->chunk_list.lock, flags);
803f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	c = dma_find_chunk(r, *bus_addr, len);
804f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
805f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (c) {
8066bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		DBG("%s:%d: reusing mapped chunk", __func__, __LINE__);
8076bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		dma_dump_chunk(c);
808f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		c->usage_count++;
809f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		spin_unlock_irqrestore(&r->chunk_list.lock, flags);
810f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		return 0;
811f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
812f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
8136bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	result = dma_sb_map_pages(r, aligned_phys, aligned_len, &c, iopte_flag);
814f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
815f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (result) {
816f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		*bus_addr = 0;
8176bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		DBG("%s:%d: dma_sb_map_pages failed (%d)\n",
818f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			__func__, __LINE__, result);
819f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		spin_unlock_irqrestore(&r->chunk_list.lock, flags);
820f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		return result;
821f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
822f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
823f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	c->usage_count = 1;
824f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
825f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	spin_unlock_irqrestore(&r->chunk_list.lock, flags);
826f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	return result;
827f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
828f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
8296bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic int dma_ioc0_map_area(struct ps3_dma_region *r, unsigned long virt_addr,
830494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwell	     unsigned long len, dma_addr_t *bus_addr,
8316bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	     u64 iopte_flag)
8326bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand{
8336bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	int result;
8346bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	unsigned long flags;
8356bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	struct dma_chunk *c;
8366bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr)
8376bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		: virt_addr;
8386bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	unsigned long aligned_phys = _ALIGN_DOWN(phys_addr, 1 << r->page_size);
8396bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	unsigned long aligned_len = _ALIGN_UP(len + phys_addr - aligned_phys,
8406bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand					      1 << r->page_size);
8416bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
8426bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	DBG(KERN_ERR "%s: vaddr=%#lx, len=%#lx\n", __func__,
8436bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	    virt_addr, len);
8446bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	DBG(KERN_ERR "%s: ph=%#lx a_ph=%#lx a_l=%#lx\n", __func__,
8456bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	    phys_addr, aligned_phys, aligned_len);
8466bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
8476bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	spin_lock_irqsave(&r->chunk_list.lock, flags);
8486bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	c = dma_find_chunk_lpar(r, ps3_mm_phys_to_lpar(phys_addr), len);
8496bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
8506bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (c) {
8516bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		/* FIXME */
8526bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		BUG();
8536bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		*bus_addr = c->bus_addr + phys_addr - aligned_phys;
8546bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		c->usage_count++;
8556bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		spin_unlock_irqrestore(&r->chunk_list.lock, flags);
8566bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		return 0;
8576bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
8586bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
8596bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	result = dma_ioc0_map_pages(r, aligned_phys, aligned_len, &c,
8606bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand				    iopte_flag);
8616bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
8626bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (result) {
8636bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		*bus_addr = 0;
8646bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		DBG("%s:%d: dma_ioc0_map_pages failed (%d)\n",
8656bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			__func__, __LINE__, result);
8666bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		spin_unlock_irqrestore(&r->chunk_list.lock, flags);
8676bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		return result;
8686bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
8696bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	*bus_addr = c->bus_addr + phys_addr - aligned_phys;
870494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwell	DBG("%s: va=%#lx pa=%#lx a_pa=%#lx bus=%#llx\n", __func__,
8716bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	    virt_addr, phys_addr, aligned_phys, *bus_addr);
8726bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	c->usage_count = 1;
8736bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
8746bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	spin_unlock_irqrestore(&r->chunk_list.lock, flags);
8756bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	return result;
8766bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand}
8776bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
878f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
8796bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand * dma_sb_unmap_area - Unmap an area of memory from a device dma region.
880f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @r: Pointer to a struct ps3_dma_region.
881f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @bus_addr: The starting ioc bus address of the area to unmap.
882f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @len: Length in bytes of the area to unmap.
883f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
884f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * This is the common dma unmap routine.
885f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
886f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
887494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwellstatic int dma_sb_unmap_area(struct ps3_dma_region *r, dma_addr_t bus_addr,
888f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	unsigned long len)
889f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
890f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	unsigned long flags;
891f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	struct dma_chunk *c;
892f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
893f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	spin_lock_irqsave(&r->chunk_list.lock, flags);
894f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	c = dma_find_chunk(r, bus_addr, len);
895f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
896f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (!c) {
897f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		unsigned long aligned_bus = _ALIGN_DOWN(bus_addr,
898f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			1 << r->page_size);
8996bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		unsigned long aligned_len = _ALIGN_UP(len + bus_addr
9006bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			- aligned_bus, 1 << r->page_size);
901494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwell		DBG("%s:%d: not found: bus_addr %llxh\n",
902f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			__func__, __LINE__, bus_addr);
903f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		DBG("%s:%d: not found: len %lxh\n",
904f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			__func__, __LINE__, len);
905f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		DBG("%s:%d: not found: aligned_bus %lxh\n",
906f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			__func__, __LINE__, aligned_bus);
907f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		DBG("%s:%d: not found: aligned_len %lxh\n",
908f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand			__func__, __LINE__, aligned_len);
909f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		BUG();
910f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
911f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
912f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	c->usage_count--;
913f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
914f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (!c->usage_count) {
915f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		list_del(&c->link);
9166bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		dma_sb_free_chunk(c);
917f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
918f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
919f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	spin_unlock_irqrestore(&r->chunk_list.lock, flags);
920f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	return 0;
921f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
922f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
92332f44a12e0674499c4db09b08da0dfa576a91d84Geert Uytterhoevenstatic int dma_ioc0_unmap_area(struct ps3_dma_region *r,
924494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwell			dma_addr_t bus_addr, unsigned long len)
9256bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand{
9266bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	unsigned long flags;
9276bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	struct dma_chunk *c;
9286bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
929494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwell	DBG("%s: start a=%#llx l=%#lx\n", __func__, bus_addr, len);
9306bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	spin_lock_irqsave(&r->chunk_list.lock, flags);
9316bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	c = dma_find_chunk(r, bus_addr, len);
9326bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
9336bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (!c) {
9346bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		unsigned long aligned_bus = _ALIGN_DOWN(bus_addr,
9356bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand							1 << r->page_size);
9366bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		unsigned long aligned_len = _ALIGN_UP(len + bus_addr
9376bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand						      - aligned_bus,
9386bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand						      1 << r->page_size);
939494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwell		DBG("%s:%d: not found: bus_addr %llxh\n",
9406bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		    __func__, __LINE__, bus_addr);
9416bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		DBG("%s:%d: not found: len %lxh\n",
9426bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		    __func__, __LINE__, len);
9436bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		DBG("%s:%d: not found: aligned_bus %lxh\n",
9446bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		    __func__, __LINE__, aligned_bus);
9456bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		DBG("%s:%d: not found: aligned_len %lxh\n",
9466bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		    __func__, __LINE__, aligned_len);
9476bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		BUG();
9486bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
9496bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
9506bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	c->usage_count--;
9516bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
9526bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (!c->usage_count) {
9536bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		list_del(&c->link);
9546bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		dma_ioc0_free_chunk(c);
9556bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
9566bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
9576bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	spin_unlock_irqrestore(&r->chunk_list.lock, flags);
9586bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	DBG("%s: end\n", __func__);
9596bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	return 0;
9606bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand}
9616bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
962f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
9636bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand * dma_sb_region_create_linear - Setup a linear dma mapping for a device.
964f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @r: Pointer to a struct ps3_dma_region.
965f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
966f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * This routine creates an HV dma region for the device and maps all available
967f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * ram into the io controller bus address space.
968f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
969f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
9706bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic int dma_sb_region_create_linear(struct ps3_dma_region *r)
971f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
972f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	int result;
973494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwell	unsigned long virt_addr, len;
974494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwell	dma_addr_t tmp;
9756bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
9766bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (r->len > 16*1024*1024) {	/* FIXME: need proper fix */
9776bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		/* force 16M dma pages for linear mapping */
9786bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		if (r->page_size != PS3_DMA_16M) {
9796bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			pr_info("%s:%d: forcing 16M pages for linear map\n",
9806bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand				__func__, __LINE__);
9816bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			r->page_size = PS3_DMA_16M;
9826bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			r->len = _ALIGN_UP(r->len, 1 << r->page_size);
9836bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		}
984f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	}
985f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
9866bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	result = dma_sb_region_create(r);
987f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	BUG_ON(result);
988f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
9896bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (r->offset < map.rm.size) {
9906bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		/* Map (part of) 1st RAM chunk */
9916bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		virt_addr = map.rm.base + r->offset;
9926bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		len = map.rm.size - r->offset;
9936bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		if (len > r->len)
9946bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			len = r->len;
9956bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		result = dma_sb_map_area(r, virt_addr, len, &tmp,
9965c6fc8db768fb9990ee67ab052896fd46fbe2651Geert Uytterhoeven			CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_SO_RW |
9975c6fc8db768fb9990ee67ab052896fd46fbe2651Geert Uytterhoeven			CBE_IOPTE_M);
9986bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		BUG_ON(result);
9996bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
1000f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
10016bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (r->offset + r->len > map.rm.size) {
10026bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		/* Map (part of) 2nd RAM chunk */
1003a628df1e9dd0d3de540bf0b7d8c6ea4896539bbbGeoff Levand		virt_addr = map.rm.size;
10046bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		len = r->len;
10056bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		if (r->offset >= map.rm.size)
10066bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			virt_addr += r->offset - map.rm.size;
10076bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		else
10086bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			len -= map.rm.size - r->offset;
10096bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		result = dma_sb_map_area(r, virt_addr, len, &tmp,
10105c6fc8db768fb9990ee67ab052896fd46fbe2651Geert Uytterhoeven			CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_SO_RW |
10115c6fc8db768fb9990ee67ab052896fd46fbe2651Geert Uytterhoeven			CBE_IOPTE_M);
10126bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		BUG_ON(result);
10136bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
1014f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1015f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	return result;
1016f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
1017f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1018f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
10196bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand * dma_sb_region_free_linear - Free a linear dma mapping for a device.
1020f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @r: Pointer to a struct ps3_dma_region.
1021f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
1022f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * This routine will unmap all mapped areas and free the HV dma region.
1023f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
1024f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
10256bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic int dma_sb_region_free_linear(struct ps3_dma_region *r)
1026f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
1027f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	int result;
1028494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwell	dma_addr_t bus_addr;
1029494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwell	unsigned long len, lpar_addr;
10306bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
10316bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (r->offset < map.rm.size) {
10326bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		/* Unmap (part of) 1st RAM chunk */
10336bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		lpar_addr = map.rm.base + r->offset;
10346bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		len = map.rm.size - r->offset;
10356bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		if (len > r->len)
10366bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			len = r->len;
10376bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		bus_addr = dma_sb_lpar_to_bus(r, lpar_addr);
10386bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		result = dma_sb_unmap_area(r, bus_addr, len);
10396bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		BUG_ON(result);
10406bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
1041f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
10426bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (r->offset + r->len > map.rm.size) {
10436bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		/* Unmap (part of) 2nd RAM chunk */
10446bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		lpar_addr = map.r1.base;
10456bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		len = r->len;
10466bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		if (r->offset >= map.rm.size)
10476bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			lpar_addr += r->offset - map.rm.size;
10486bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		else
10496bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			len -= map.rm.size - r->offset;
10506bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		bus_addr = dma_sb_lpar_to_bus(r, lpar_addr);
10516bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		result = dma_sb_unmap_area(r, bus_addr, len);
10526bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		BUG_ON(result);
10536bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
1054f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
10556bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	result = dma_sb_region_free(r);
1056f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	BUG_ON(result);
1057f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1058f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	return result;
1059f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
1060f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1061f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
10626bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand * dma_sb_map_area_linear - Map an area of memory into a device dma region.
1063f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @r: Pointer to a struct ps3_dma_region.
1064f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @virt_addr: Starting virtual address of the area to map.
1065f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @len: Length in bytes of the area to map.
1066f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @bus_addr: A pointer to return the starting ioc bus address of the area to
1067f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * map.
1068f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
10696bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand * This routine just returns the corresponding bus address.  Actual mapping
1070f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * occurs in dma_region_create_linear().
1071f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
1072f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
10736bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic int dma_sb_map_area_linear(struct ps3_dma_region *r,
1074494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwell	unsigned long virt_addr, unsigned long len, dma_addr_t *bus_addr,
10756bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	u64 iopte_flag)
1076f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
1077f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr)
1078f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		: virt_addr;
10796bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	*bus_addr = dma_sb_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr));
1080f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	return 0;
1081f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
1082f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1083f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
1084f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * dma_unmap_area_linear - Unmap an area of memory from a device dma region.
1085f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @r: Pointer to a struct ps3_dma_region.
1086f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @bus_addr: The starting ioc bus address of the area to unmap.
1087f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * @len: Length in bytes of the area to unmap.
1088f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand *
10896bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand * This routine does nothing.  Unmapping occurs in dma_sb_region_free_linear().
1090f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
1091f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
10926bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic int dma_sb_unmap_area_linear(struct ps3_dma_region *r,
1093494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwell	dma_addr_t bus_addr, unsigned long len)
1094f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
1095f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	return 0;
10966bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand};
10976bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
10986bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic const struct ps3_dma_region_ops ps3_dma_sb_region_ops =  {
10996bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	.create = dma_sb_region_create,
11006bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	.free = dma_sb_region_free,
11016bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	.map = dma_sb_map_area,
11026bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	.unmap = dma_sb_unmap_area
11036bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand};
11046bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
11056bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic const struct ps3_dma_region_ops ps3_dma_sb_region_linear_ops = {
11066bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	.create = dma_sb_region_create_linear,
11076bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	.free = dma_sb_region_free_linear,
11086bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	.map = dma_sb_map_area_linear,
11096bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	.unmap = dma_sb_unmap_area_linear
11106bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand};
11116bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
11126bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandstatic const struct ps3_dma_region_ops ps3_dma_ioc0_region_ops = {
11136bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	.create = dma_ioc0_region_create,
11146bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	.free = dma_ioc0_region_free,
11156bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	.map = dma_ioc0_map_area,
11166bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	.unmap = dma_ioc0_unmap_area
11176bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand};
11186bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
11196bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levandint ps3_dma_region_init(struct ps3_system_bus_device *dev,
11206bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	struct ps3_dma_region *r, enum ps3_dma_page_size page_size,
11216bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	enum ps3_dma_region_type region_type, void *addr, unsigned long len)
11226bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand{
11236bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	unsigned long lpar_addr;
11246bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
11256bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	lpar_addr = addr ? ps3_mm_phys_to_lpar(__pa(addr)) : 0;
11266bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
11276bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	r->dev = dev;
11286bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	r->page_size = page_size;
11296bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	r->region_type = region_type;
11306bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	r->offset = lpar_addr;
11316bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	if (r->offset >= map.rm.size)
11326bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		r->offset -= map.r1.offset;
11336bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	r->len = len ? len : _ALIGN_UP(map.total, 1 << r->page_size);
11346bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
11356bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	switch (dev->dev_type) {
11366bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	case PS3_DEVICE_TYPE_SB:
11376bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		r->region_ops =  (USE_DYNAMIC_DMA)
11386bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			? &ps3_dma_sb_region_ops
11396bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand			: &ps3_dma_sb_region_linear_ops;
11406bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		break;
11416bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	case PS3_DEVICE_TYPE_IOC0:
11426bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		r->region_ops = &ps3_dma_ioc0_region_ops;
11436bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		break;
11446bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	default:
11456bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		BUG();
11466bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand		return -EINVAL;
11476bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	}
11486bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	return 0;
1149f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
11506bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff LevandEXPORT_SYMBOL(ps3_dma_region_init);
1151f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1152f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandint ps3_dma_region_create(struct ps3_dma_region *r)
1153f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
11546bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	BUG_ON(!r);
11556bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	BUG_ON(!r->region_ops);
11566bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	BUG_ON(!r->region_ops->create);
11576bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	return r->region_ops->create(r);
1158f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
11596bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff LevandEXPORT_SYMBOL(ps3_dma_region_create);
1160f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1161f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandint ps3_dma_region_free(struct ps3_dma_region *r)
1162f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
11636bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	BUG_ON(!r);
11646bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	BUG_ON(!r->region_ops);
11656bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	BUG_ON(!r->region_ops->free);
11666bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	return r->region_ops->free(r);
1167f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
11686bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff LevandEXPORT_SYMBOL(ps3_dma_region_free);
1169f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1170f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandint ps3_dma_map(struct ps3_dma_region *r, unsigned long virt_addr,
1171494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwell	unsigned long len, dma_addr_t *bus_addr,
11726bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	u64 iopte_flag)
1173f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
11746bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	return r->region_ops->map(r, virt_addr, len, bus_addr, iopte_flag);
1175f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
1176f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1177494fd07a88ea561e1bea73516d7e92c4c2d1f223Stephen Rothwellint ps3_dma_unmap(struct ps3_dma_region *r, dma_addr_t bus_addr,
1178f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	unsigned long len)
1179f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
11806bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	return r->region_ops->unmap(r, bus_addr, len);
1181f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
1182f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1183f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/*============================================================================*/
1184f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/* system startup routines                                                    */
1185f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/*============================================================================*/
1186f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1187f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
1188f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * ps3_mm_init - initialize the address space state variables
1189f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
1190f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1191f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandvoid __init ps3_mm_init(void)
1192f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
1193f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	int result;
1194f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1195f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	DBG(" -> %s:%d\n", __func__, __LINE__);
1196f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1197f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	result = ps3_repository_read_mm_info(&map.rm.base, &map.rm.size,
1198f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		&map.total);
1199f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1200f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	if (result)
1201f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand		panic("ps3_repository_read_mm_info() failed");
1202f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1203f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	map.rm.offset = map.rm.base;
1204f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	map.vas_id = map.htab_size = 0;
1205f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1206f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	/* this implementation assumes map.rm.base is zero */
1207f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1208f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	BUG_ON(map.rm.base);
1209f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	BUG_ON(!map.rm.size);
1210f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
12111e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	/* Check if we got the highmem region from an earlier boot step */
1212f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
12131e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider	if (ps3_mm_get_repository_highmem(&map.r1))
12141e755c09926de2d9b0e239ba0ce10fdb6835551aAndre Heider		ps3_mm_region_create(&map.r1, map.total - map.rm.size);
1215f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
12166bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	/* correct map.total for the real total amount of memory we use */
12176bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand	map.total = map.rm.size + map.r1.size;
12186bb5cf1025414fe00b20f3bef56135849e4ed3b8Geoff Levand
12198ac5fd118cd137db5ca8c786778ba600f87111a0Hector Martin	if (!map.r1.size) {
12208ac5fd118cd137db5ca8c786778ba600f87111a0Hector Martin		DBG("%s:%d: No highmem region found\n", __func__, __LINE__);
12218ac5fd118cd137db5ca8c786778ba600f87111a0Hector Martin	} else {
12228ac5fd118cd137db5ca8c786778ba600f87111a0Hector Martin		DBG("%s:%d: Adding highmem region: %llxh %llxh\n",
12238ac5fd118cd137db5ca8c786778ba600f87111a0Hector Martin			__func__, __LINE__, map.rm.size,
12248ac5fd118cd137db5ca8c786778ba600f87111a0Hector Martin			map.total - map.rm.size);
12258ac5fd118cd137db5ca8c786778ba600f87111a0Hector Martin		memblock_add(map.rm.size, map.total - map.rm.size);
12268ac5fd118cd137db5ca8c786778ba600f87111a0Hector Martin	}
12278ac5fd118cd137db5ca8c786778ba600f87111a0Hector Martin
1228f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	DBG(" <- %s:%d\n", __func__, __LINE__);
1229f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
1230f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1231f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand/**
1232f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand * ps3_mm_shutdown - final cleanup of address space
1233f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand */
1234f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand
1235f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levandvoid ps3_mm_shutdown(void)
1236f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand{
1237f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand	ps3_mm_region_destroy(&map.r1);
1238f58a9d171a346afb1b09190427e6c28c6118703eGeoff Levand}
1239