xref: /linux/drivers/block/ps3vram.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f507cd22SGeert Uytterhoeven /*
3de667203SGeert Uytterhoeven  * ps3vram - Use extra PS3 video ram as block device.
4f507cd22SGeert Uytterhoeven  *
5f507cd22SGeert Uytterhoeven  * Copyright 2009 Sony Corporation
6f507cd22SGeert Uytterhoeven  *
7f507cd22SGeert Uytterhoeven  * Based on the MTD ps3vram driver, which is
8f507cd22SGeert Uytterhoeven  * Copyright (c) 2007-2008 Jim Paris <jim@jtan.com>
9f507cd22SGeert Uytterhoeven  * Added support RSX DMA Vivien Chappelier <vivien.chappelier@free.fr>
10f507cd22SGeert Uytterhoeven  */
11f507cd22SGeert Uytterhoeven 
12f507cd22SGeert Uytterhoeven #include <linux/blkdev.h>
13f507cd22SGeert Uytterhoeven #include <linux/delay.h>
140c8d44f2SPaul Gortmaker #include <linux/module.h>
15f507cd22SGeert Uytterhoeven #include <linux/proc_fs.h>
16f507cd22SGeert Uytterhoeven #include <linux/seq_file.h>
175a0e3ad6STejun Heo #include <linux/slab.h>
18f507cd22SGeert Uytterhoeven 
199413c883SGeert Uytterhoeven #include <asm/cell-regs.h>
20f507cd22SGeert Uytterhoeven #include <asm/firmware.h>
21f507cd22SGeert Uytterhoeven #include <asm/lv1call.h>
22f507cd22SGeert Uytterhoeven #include <asm/ps3.h>
23d3352c9fSGeert Uytterhoeven #include <asm/ps3gpu.h>
24f507cd22SGeert Uytterhoeven 
25f507cd22SGeert Uytterhoeven 
26f507cd22SGeert Uytterhoeven #define DEVICE_NAME		"ps3vram"
27f507cd22SGeert Uytterhoeven 
28f507cd22SGeert Uytterhoeven 
29f507cd22SGeert Uytterhoeven #define XDR_BUF_SIZE (2 * 1024 * 1024) /* XDR buffer (must be 1MiB aligned) */
30f507cd22SGeert Uytterhoeven #define XDR_IOIF 0x0c000000
31f507cd22SGeert Uytterhoeven 
32f507cd22SGeert Uytterhoeven #define FIFO_BASE XDR_IOIF
33f507cd22SGeert Uytterhoeven #define FIFO_SIZE (64 * 1024)
34f507cd22SGeert Uytterhoeven 
35f507cd22SGeert Uytterhoeven #define DMA_PAGE_SIZE (4 * 1024)
36f507cd22SGeert Uytterhoeven 
37f507cd22SGeert Uytterhoeven #define CACHE_PAGE_SIZE (256 * 1024)
38f507cd22SGeert Uytterhoeven #define CACHE_PAGE_COUNT ((XDR_BUF_SIZE - FIFO_SIZE) / CACHE_PAGE_SIZE)
39f507cd22SGeert Uytterhoeven 
40f507cd22SGeert Uytterhoeven #define CACHE_OFFSET CACHE_PAGE_SIZE
41f507cd22SGeert Uytterhoeven #define FIFO_OFFSET 0
42f507cd22SGeert Uytterhoeven 
43f507cd22SGeert Uytterhoeven #define CTRL_PUT 0x10
44f507cd22SGeert Uytterhoeven #define CTRL_GET 0x11
45f507cd22SGeert Uytterhoeven #define CTRL_TOP 0x15
46f507cd22SGeert Uytterhoeven 
47f507cd22SGeert Uytterhoeven #define UPLOAD_SUBCH	1
48f507cd22SGeert Uytterhoeven #define DOWNLOAD_SUBCH	2
49f507cd22SGeert Uytterhoeven 
50f507cd22SGeert Uytterhoeven #define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN	0x0000030c
51f507cd22SGeert Uytterhoeven #define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY	0x00000104
52f507cd22SGeert Uytterhoeven 
53f507cd22SGeert Uytterhoeven #define CACHE_PAGE_PRESENT 1
54f507cd22SGeert Uytterhoeven #define CACHE_PAGE_DIRTY   2
55f507cd22SGeert Uytterhoeven 
56f507cd22SGeert Uytterhoeven struct ps3vram_tag {
57f507cd22SGeert Uytterhoeven 	unsigned int address;
58f507cd22SGeert Uytterhoeven 	unsigned int flags;
59f507cd22SGeert Uytterhoeven };
60f507cd22SGeert Uytterhoeven 
61f507cd22SGeert Uytterhoeven struct ps3vram_cache {
62f507cd22SGeert Uytterhoeven 	unsigned int page_count;
63f507cd22SGeert Uytterhoeven 	unsigned int page_size;
64f507cd22SGeert Uytterhoeven 	struct ps3vram_tag *tags;
65f507cd22SGeert Uytterhoeven 	unsigned int hit;
66f507cd22SGeert Uytterhoeven 	unsigned int miss;
67f507cd22SGeert Uytterhoeven };
68f507cd22SGeert Uytterhoeven 
69f507cd22SGeert Uytterhoeven struct ps3vram_priv {
70f507cd22SGeert Uytterhoeven 	struct gendisk *gendisk;
71f507cd22SGeert Uytterhoeven 
72f507cd22SGeert Uytterhoeven 	u64 size;
73f507cd22SGeert Uytterhoeven 
74f507cd22SGeert Uytterhoeven 	u64 memory_handle;
75f507cd22SGeert Uytterhoeven 	u64 context_handle;
76e7bdd17bSGeoff Levand 	u32 __iomem *ctrl;
77e7bdd17bSGeoff Levand 	void __iomem *reports;
78f507cd22SGeert Uytterhoeven 	u8 *xdr_buf;
79f507cd22SGeert Uytterhoeven 
80f507cd22SGeert Uytterhoeven 	u32 *fifo_base;
81f507cd22SGeert Uytterhoeven 	u32 *fifo_ptr;
82f507cd22SGeert Uytterhoeven 
83f507cd22SGeert Uytterhoeven 	struct ps3vram_cache cache;
84f507cd22SGeert Uytterhoeven 
85fb89e89dSGeert Uytterhoeven 	spinlock_t lock;	/* protecting list of bios */
86fb89e89dSGeert Uytterhoeven 	struct bio_list list;
87f507cd22SGeert Uytterhoeven };
88f507cd22SGeert Uytterhoeven 
89f507cd22SGeert Uytterhoeven 
90f507cd22SGeert Uytterhoeven static int ps3vram_major;
91f507cd22SGeert Uytterhoeven 
92f507cd22SGeert Uytterhoeven #define DMA_NOTIFIER_HANDLE_BASE 0x66604200 /* first DMA notifier handle */
93f507cd22SGeert Uytterhoeven #define DMA_NOTIFIER_OFFSET_BASE 0x1000     /* first DMA notifier offset */
94f507cd22SGeert Uytterhoeven #define DMA_NOTIFIER_SIZE        0x40
95f507cd22SGeert Uytterhoeven #define NOTIFIER 7	/* notifier used for completion report */
96f507cd22SGeert Uytterhoeven 
97f507cd22SGeert Uytterhoeven static char *size = "256M";
98f507cd22SGeert Uytterhoeven module_param(size, charp, 0);
99f507cd22SGeert Uytterhoeven MODULE_PARM_DESC(size, "memory size");
100f507cd22SGeert Uytterhoeven 
ps3vram_get_notifier(void __iomem * reports,int notifier)101e7bdd17bSGeoff Levand static u32 __iomem *ps3vram_get_notifier(void __iomem *reports, int notifier)
102f507cd22SGeert Uytterhoeven {
1031bd9784fSGeert Uytterhoeven 	return reports + DMA_NOTIFIER_OFFSET_BASE +
104f507cd22SGeert Uytterhoeven 	       DMA_NOTIFIER_SIZE * notifier;
105f507cd22SGeert Uytterhoeven }
106f507cd22SGeert Uytterhoeven 
ps3vram_notifier_reset(struct ps3_system_bus_device * dev)107f507cd22SGeert Uytterhoeven static void ps3vram_notifier_reset(struct ps3_system_bus_device *dev)
108f507cd22SGeert Uytterhoeven {
10903fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
110e7bdd17bSGeoff Levand 	u32 __iomem *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
111f507cd22SGeert Uytterhoeven 	int i;
112f507cd22SGeert Uytterhoeven 
113f507cd22SGeert Uytterhoeven 	for (i = 0; i < 4; i++)
114e7bdd17bSGeoff Levand 		iowrite32be(0xffffffff, notify + i);
115f507cd22SGeert Uytterhoeven }
116f507cd22SGeert Uytterhoeven 
ps3vram_notifier_wait(struct ps3_system_bus_device * dev,unsigned int timeout_ms)117f507cd22SGeert Uytterhoeven static int ps3vram_notifier_wait(struct ps3_system_bus_device *dev,
118f507cd22SGeert Uytterhoeven 				 unsigned int timeout_ms)
119f507cd22SGeert Uytterhoeven {
12003fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
121e7bdd17bSGeoff Levand 	u32 __iomem *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
122f21121cdSHideyuki Sasaki 	unsigned long timeout;
123f21121cdSHideyuki Sasaki 
124f21121cdSHideyuki Sasaki 	for (timeout = 20; timeout; timeout--) {
125e7bdd17bSGeoff Levand 		if (!ioread32be(notify + 3))
126f21121cdSHideyuki Sasaki 			return 0;
127f21121cdSHideyuki Sasaki 		udelay(10);
128f21121cdSHideyuki Sasaki 	}
129f21121cdSHideyuki Sasaki 
130f21121cdSHideyuki Sasaki 	timeout = jiffies + msecs_to_jiffies(timeout_ms);
131f507cd22SGeert Uytterhoeven 
132f507cd22SGeert Uytterhoeven 	do {
133e7bdd17bSGeoff Levand 		if (!ioread32be(notify + 3))
134f507cd22SGeert Uytterhoeven 			return 0;
135f507cd22SGeert Uytterhoeven 		msleep(1);
136f507cd22SGeert Uytterhoeven 	} while (time_before(jiffies, timeout));
137f507cd22SGeert Uytterhoeven 
138f507cd22SGeert Uytterhoeven 	return -ETIMEDOUT;
139f507cd22SGeert Uytterhoeven }
140f507cd22SGeert Uytterhoeven 
ps3vram_init_ring(struct ps3_system_bus_device * dev)141f507cd22SGeert Uytterhoeven static void ps3vram_init_ring(struct ps3_system_bus_device *dev)
142f507cd22SGeert Uytterhoeven {
14303fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
144f507cd22SGeert Uytterhoeven 
145e7bdd17bSGeoff Levand 	iowrite32be(FIFO_BASE + FIFO_OFFSET, priv->ctrl + CTRL_PUT);
146e7bdd17bSGeoff Levand 	iowrite32be(FIFO_BASE + FIFO_OFFSET, priv->ctrl + CTRL_GET);
147f507cd22SGeert Uytterhoeven }
148f507cd22SGeert Uytterhoeven 
ps3vram_wait_ring(struct ps3_system_bus_device * dev,unsigned int timeout_ms)149f507cd22SGeert Uytterhoeven static int ps3vram_wait_ring(struct ps3_system_bus_device *dev,
150f507cd22SGeert Uytterhoeven 			     unsigned int timeout_ms)
151f507cd22SGeert Uytterhoeven {
15203fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
153f507cd22SGeert Uytterhoeven 	unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
154f507cd22SGeert Uytterhoeven 
155f507cd22SGeert Uytterhoeven 	do {
156e7bdd17bSGeoff Levand 		if (ioread32be(priv->ctrl + CTRL_PUT) == ioread32be(priv->ctrl + CTRL_GET))
157f507cd22SGeert Uytterhoeven 			return 0;
158f507cd22SGeert Uytterhoeven 		msleep(1);
159f507cd22SGeert Uytterhoeven 	} while (time_before(jiffies, timeout));
160f507cd22SGeert Uytterhoeven 
161f507cd22SGeert Uytterhoeven 	dev_warn(&dev->core, "FIFO timeout (%08x/%08x/%08x)\n",
162e7bdd17bSGeoff Levand 		 ioread32be(priv->ctrl + CTRL_PUT), ioread32be(priv->ctrl + CTRL_GET),
163e7bdd17bSGeoff Levand 		 ioread32be(priv->ctrl + CTRL_TOP));
164f507cd22SGeert Uytterhoeven 
165f507cd22SGeert Uytterhoeven 	return -ETIMEDOUT;
166f507cd22SGeert Uytterhoeven }
167f507cd22SGeert Uytterhoeven 
ps3vram_out_ring(struct ps3vram_priv * priv,u32 data)168f507cd22SGeert Uytterhoeven static void ps3vram_out_ring(struct ps3vram_priv *priv, u32 data)
169f507cd22SGeert Uytterhoeven {
170f507cd22SGeert Uytterhoeven 	*(priv->fifo_ptr)++ = data;
171f507cd22SGeert Uytterhoeven }
172f507cd22SGeert Uytterhoeven 
ps3vram_begin_ring(struct ps3vram_priv * priv,u32 chan,u32 tag,u32 size)173f507cd22SGeert Uytterhoeven static void ps3vram_begin_ring(struct ps3vram_priv *priv, u32 chan, u32 tag,
174f507cd22SGeert Uytterhoeven 			       u32 size)
175f507cd22SGeert Uytterhoeven {
176f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, (size << 18) | (chan << 13) | tag);
177f507cd22SGeert Uytterhoeven }
178f507cd22SGeert Uytterhoeven 
ps3vram_rewind_ring(struct ps3_system_bus_device * dev)179f507cd22SGeert Uytterhoeven static void ps3vram_rewind_ring(struct ps3_system_bus_device *dev)
180f507cd22SGeert Uytterhoeven {
18103fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
182f507cd22SGeert Uytterhoeven 	int status;
183f507cd22SGeert Uytterhoeven 
184f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, 0x20000000 | (FIFO_BASE + FIFO_OFFSET));
185f507cd22SGeert Uytterhoeven 
186e7bdd17bSGeoff Levand 	iowrite32be(FIFO_BASE + FIFO_OFFSET, priv->ctrl + CTRL_PUT);
187f507cd22SGeert Uytterhoeven 
188f507cd22SGeert Uytterhoeven 	/* asking the HV for a blit will kick the FIFO */
189d3352c9fSGeert Uytterhoeven 	status = lv1_gpu_fb_blit(priv->context_handle, 0, 0, 0, 0);
190f507cd22SGeert Uytterhoeven 	if (status)
191d3352c9fSGeert Uytterhoeven 		dev_err(&dev->core, "%s: lv1_gpu_fb_blit failed %d\n",
192d3352c9fSGeert Uytterhoeven 			__func__, status);
193f507cd22SGeert Uytterhoeven 
194f507cd22SGeert Uytterhoeven 	priv->fifo_ptr = priv->fifo_base;
195f507cd22SGeert Uytterhoeven }
196f507cd22SGeert Uytterhoeven 
ps3vram_fire_ring(struct ps3_system_bus_device * dev)197f507cd22SGeert Uytterhoeven static void ps3vram_fire_ring(struct ps3_system_bus_device *dev)
198f507cd22SGeert Uytterhoeven {
19903fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
200f507cd22SGeert Uytterhoeven 	int status;
201f507cd22SGeert Uytterhoeven 
202f507cd22SGeert Uytterhoeven 	mutex_lock(&ps3_gpu_mutex);
203f507cd22SGeert Uytterhoeven 
204e7bdd17bSGeoff Levand 	iowrite32be(FIFO_BASE + FIFO_OFFSET + (priv->fifo_ptr - priv->fifo_base)
205e7bdd17bSGeoff Levand 		* sizeof(u32), priv->ctrl + CTRL_PUT);
206f507cd22SGeert Uytterhoeven 
207f507cd22SGeert Uytterhoeven 	/* asking the HV for a blit will kick the FIFO */
208d3352c9fSGeert Uytterhoeven 	status = lv1_gpu_fb_blit(priv->context_handle, 0, 0, 0, 0);
209f507cd22SGeert Uytterhoeven 	if (status)
210d3352c9fSGeert Uytterhoeven 		dev_err(&dev->core, "%s: lv1_gpu_fb_blit failed %d\n",
211d3352c9fSGeert Uytterhoeven 			__func__, status);
212f507cd22SGeert Uytterhoeven 
213f507cd22SGeert Uytterhoeven 	if ((priv->fifo_ptr - priv->fifo_base) * sizeof(u32) >
214f507cd22SGeert Uytterhoeven 	    FIFO_SIZE - 1024) {
215f507cd22SGeert Uytterhoeven 		dev_dbg(&dev->core, "FIFO full, rewinding\n");
216f507cd22SGeert Uytterhoeven 		ps3vram_wait_ring(dev, 200);
217f507cd22SGeert Uytterhoeven 		ps3vram_rewind_ring(dev);
218f507cd22SGeert Uytterhoeven 	}
219f507cd22SGeert Uytterhoeven 
220f507cd22SGeert Uytterhoeven 	mutex_unlock(&ps3_gpu_mutex);
221f507cd22SGeert Uytterhoeven }
222f507cd22SGeert Uytterhoeven 
ps3vram_bind(struct ps3_system_bus_device * dev)223f507cd22SGeert Uytterhoeven static void ps3vram_bind(struct ps3_system_bus_device *dev)
224f507cd22SGeert Uytterhoeven {
22503fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
226f507cd22SGeert Uytterhoeven 
227f507cd22SGeert Uytterhoeven 	ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0, 1);
228f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, 0x31337303);
229f507cd22SGeert Uytterhoeven 	ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x180, 3);
230f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER);
231f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, 0xfeed0001);	/* DMA system RAM instance */
232f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, 0xfeed0000);     /* DMA video RAM instance */
233f507cd22SGeert Uytterhoeven 
234f507cd22SGeert Uytterhoeven 	ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0, 1);
235f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, 0x3137c0de);
236f507cd22SGeert Uytterhoeven 	ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x180, 3);
237f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER);
238f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, 0xfeed0000);	/* DMA video RAM instance */
239f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, 0xfeed0001);	/* DMA system RAM instance */
240f507cd22SGeert Uytterhoeven 
241f507cd22SGeert Uytterhoeven 	ps3vram_fire_ring(dev);
242f507cd22SGeert Uytterhoeven }
243f507cd22SGeert Uytterhoeven 
ps3vram_upload(struct ps3_system_bus_device * dev,unsigned int src_offset,unsigned int dst_offset,int len,int count)244f507cd22SGeert Uytterhoeven static int ps3vram_upload(struct ps3_system_bus_device *dev,
245f507cd22SGeert Uytterhoeven 			  unsigned int src_offset, unsigned int dst_offset,
246f507cd22SGeert Uytterhoeven 			  int len, int count)
247f507cd22SGeert Uytterhoeven {
24803fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
249f507cd22SGeert Uytterhoeven 
250f507cd22SGeert Uytterhoeven 	ps3vram_begin_ring(priv, UPLOAD_SUBCH,
251f507cd22SGeert Uytterhoeven 			   NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
252f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, XDR_IOIF + src_offset);
253f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, dst_offset);
254f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, len);
255f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, len);
256f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, len);
257f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, count);
258f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, (1 << 8) | 1);
259f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, 0);
260f507cd22SGeert Uytterhoeven 
261f507cd22SGeert Uytterhoeven 	ps3vram_notifier_reset(dev);
262f507cd22SGeert Uytterhoeven 	ps3vram_begin_ring(priv, UPLOAD_SUBCH,
263f507cd22SGeert Uytterhoeven 			   NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1);
264f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, 0);
265f507cd22SGeert Uytterhoeven 	ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x100, 1);
266f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, 0);
267f507cd22SGeert Uytterhoeven 	ps3vram_fire_ring(dev);
268f507cd22SGeert Uytterhoeven 	if (ps3vram_notifier_wait(dev, 200) < 0) {
269f507cd22SGeert Uytterhoeven 		dev_warn(&dev->core, "%s: Notifier timeout\n", __func__);
270f507cd22SGeert Uytterhoeven 		return -1;
271f507cd22SGeert Uytterhoeven 	}
272f507cd22SGeert Uytterhoeven 
273f507cd22SGeert Uytterhoeven 	return 0;
274f507cd22SGeert Uytterhoeven }
275f507cd22SGeert Uytterhoeven 
ps3vram_download(struct ps3_system_bus_device * dev,unsigned int src_offset,unsigned int dst_offset,int len,int count)276f507cd22SGeert Uytterhoeven static int ps3vram_download(struct ps3_system_bus_device *dev,
277f507cd22SGeert Uytterhoeven 			    unsigned int src_offset, unsigned int dst_offset,
278f507cd22SGeert Uytterhoeven 			    int len, int count)
279f507cd22SGeert Uytterhoeven {
28003fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
281f507cd22SGeert Uytterhoeven 
282f507cd22SGeert Uytterhoeven 	ps3vram_begin_ring(priv, DOWNLOAD_SUBCH,
283f507cd22SGeert Uytterhoeven 			   NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
284f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, src_offset);
285f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, XDR_IOIF + dst_offset);
286f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, len);
287f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, len);
288f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, len);
289f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, count);
290f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, (1 << 8) | 1);
291f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, 0);
292f507cd22SGeert Uytterhoeven 
293f507cd22SGeert Uytterhoeven 	ps3vram_notifier_reset(dev);
294f507cd22SGeert Uytterhoeven 	ps3vram_begin_ring(priv, DOWNLOAD_SUBCH,
295f507cd22SGeert Uytterhoeven 			   NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1);
296f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, 0);
297f507cd22SGeert Uytterhoeven 	ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x100, 1);
298f507cd22SGeert Uytterhoeven 	ps3vram_out_ring(priv, 0);
299f507cd22SGeert Uytterhoeven 	ps3vram_fire_ring(dev);
300f507cd22SGeert Uytterhoeven 	if (ps3vram_notifier_wait(dev, 200) < 0) {
301f507cd22SGeert Uytterhoeven 		dev_warn(&dev->core, "%s: Notifier timeout\n", __func__);
302f507cd22SGeert Uytterhoeven 		return -1;
303f507cd22SGeert Uytterhoeven 	}
304f507cd22SGeert Uytterhoeven 
305f507cd22SGeert Uytterhoeven 	return 0;
306f507cd22SGeert Uytterhoeven }
307f507cd22SGeert Uytterhoeven 
ps3vram_cache_evict(struct ps3_system_bus_device * dev,int entry)308f507cd22SGeert Uytterhoeven static void ps3vram_cache_evict(struct ps3_system_bus_device *dev, int entry)
309f507cd22SGeert Uytterhoeven {
31003fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
311f507cd22SGeert Uytterhoeven 	struct ps3vram_cache *cache = &priv->cache;
312f507cd22SGeert Uytterhoeven 
313f507cd22SGeert Uytterhoeven 	if (!(cache->tags[entry].flags & CACHE_PAGE_DIRTY))
314f507cd22SGeert Uytterhoeven 		return;
315f507cd22SGeert Uytterhoeven 
316f507cd22SGeert Uytterhoeven 	dev_dbg(&dev->core, "Flushing %d: 0x%08x\n", entry,
317f507cd22SGeert Uytterhoeven 		cache->tags[entry].address);
318f507cd22SGeert Uytterhoeven 	if (ps3vram_upload(dev, CACHE_OFFSET + entry * cache->page_size,
319f507cd22SGeert Uytterhoeven 			   cache->tags[entry].address, DMA_PAGE_SIZE,
320f507cd22SGeert Uytterhoeven 			   cache->page_size / DMA_PAGE_SIZE) < 0) {
321f507cd22SGeert Uytterhoeven 		dev_err(&dev->core,
322f507cd22SGeert Uytterhoeven 			"Failed to upload from 0x%x to " "0x%x size 0x%x\n",
323f507cd22SGeert Uytterhoeven 			entry * cache->page_size, cache->tags[entry].address,
324f507cd22SGeert Uytterhoeven 			cache->page_size);
325f507cd22SGeert Uytterhoeven 	}
326f507cd22SGeert Uytterhoeven 	cache->tags[entry].flags &= ~CACHE_PAGE_DIRTY;
327f507cd22SGeert Uytterhoeven }
328f507cd22SGeert Uytterhoeven 
ps3vram_cache_load(struct ps3_system_bus_device * dev,int entry,unsigned int address)329f507cd22SGeert Uytterhoeven static void ps3vram_cache_load(struct ps3_system_bus_device *dev, int entry,
330f507cd22SGeert Uytterhoeven 			       unsigned int address)
331f507cd22SGeert Uytterhoeven {
33203fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
333f507cd22SGeert Uytterhoeven 	struct ps3vram_cache *cache = &priv->cache;
334f507cd22SGeert Uytterhoeven 
335f507cd22SGeert Uytterhoeven 	dev_dbg(&dev->core, "Fetching %d: 0x%08x\n", entry, address);
336f507cd22SGeert Uytterhoeven 	if (ps3vram_download(dev, address,
337f507cd22SGeert Uytterhoeven 			     CACHE_OFFSET + entry * cache->page_size,
338f507cd22SGeert Uytterhoeven 			     DMA_PAGE_SIZE,
339f507cd22SGeert Uytterhoeven 			     cache->page_size / DMA_PAGE_SIZE) < 0) {
340f507cd22SGeert Uytterhoeven 		dev_err(&dev->core,
341f507cd22SGeert Uytterhoeven 			"Failed to download from 0x%x to 0x%x size 0x%x\n",
342f507cd22SGeert Uytterhoeven 			address, entry * cache->page_size, cache->page_size);
343f507cd22SGeert Uytterhoeven 	}
344f507cd22SGeert Uytterhoeven 
345f507cd22SGeert Uytterhoeven 	cache->tags[entry].address = address;
346f507cd22SGeert Uytterhoeven 	cache->tags[entry].flags |= CACHE_PAGE_PRESENT;
347f507cd22SGeert Uytterhoeven }
348f507cd22SGeert Uytterhoeven 
349f507cd22SGeert Uytterhoeven 
ps3vram_cache_flush(struct ps3_system_bus_device * dev)350f507cd22SGeert Uytterhoeven static void ps3vram_cache_flush(struct ps3_system_bus_device *dev)
351f507cd22SGeert Uytterhoeven {
35203fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
353f507cd22SGeert Uytterhoeven 	struct ps3vram_cache *cache = &priv->cache;
354f507cd22SGeert Uytterhoeven 	int i;
355f507cd22SGeert Uytterhoeven 
356f507cd22SGeert Uytterhoeven 	dev_dbg(&dev->core, "FLUSH\n");
357f507cd22SGeert Uytterhoeven 	for (i = 0; i < cache->page_count; i++) {
358f507cd22SGeert Uytterhoeven 		ps3vram_cache_evict(dev, i);
359f507cd22SGeert Uytterhoeven 		cache->tags[i].flags = 0;
360f507cd22SGeert Uytterhoeven 	}
361f507cd22SGeert Uytterhoeven }
362f507cd22SGeert Uytterhoeven 
ps3vram_cache_match(struct ps3_system_bus_device * dev,loff_t address)363f507cd22SGeert Uytterhoeven static unsigned int ps3vram_cache_match(struct ps3_system_bus_device *dev,
364f507cd22SGeert Uytterhoeven 					loff_t address)
365f507cd22SGeert Uytterhoeven {
36603fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
367f507cd22SGeert Uytterhoeven 	struct ps3vram_cache *cache = &priv->cache;
368f507cd22SGeert Uytterhoeven 	unsigned int base;
369f507cd22SGeert Uytterhoeven 	unsigned int offset;
370f507cd22SGeert Uytterhoeven 	int i;
371f507cd22SGeert Uytterhoeven 	static int counter;
372f507cd22SGeert Uytterhoeven 
373f507cd22SGeert Uytterhoeven 	offset = (unsigned int) (address & (cache->page_size - 1));
374f507cd22SGeert Uytterhoeven 	base = (unsigned int) (address - offset);
375f507cd22SGeert Uytterhoeven 
376f507cd22SGeert Uytterhoeven 	/* fully associative check */
377f507cd22SGeert Uytterhoeven 	for (i = 0; i < cache->page_count; i++) {
378f507cd22SGeert Uytterhoeven 		if ((cache->tags[i].flags & CACHE_PAGE_PRESENT) &&
379f507cd22SGeert Uytterhoeven 		    cache->tags[i].address == base) {
380f507cd22SGeert Uytterhoeven 			cache->hit++;
381f507cd22SGeert Uytterhoeven 			dev_dbg(&dev->core, "Found entry %d: 0x%08x\n", i,
382f507cd22SGeert Uytterhoeven 				cache->tags[i].address);
383f507cd22SGeert Uytterhoeven 			return i;
384f507cd22SGeert Uytterhoeven 		}
385f507cd22SGeert Uytterhoeven 	}
386f507cd22SGeert Uytterhoeven 
387f507cd22SGeert Uytterhoeven 	/* choose a random entry */
388f507cd22SGeert Uytterhoeven 	i = (jiffies + (counter++)) % cache->page_count;
389f507cd22SGeert Uytterhoeven 	dev_dbg(&dev->core, "Using entry %d\n", i);
390f507cd22SGeert Uytterhoeven 
391f507cd22SGeert Uytterhoeven 	ps3vram_cache_evict(dev, i);
392f507cd22SGeert Uytterhoeven 	ps3vram_cache_load(dev, i, base);
393f507cd22SGeert Uytterhoeven 
394f507cd22SGeert Uytterhoeven 	cache->miss++;
395f507cd22SGeert Uytterhoeven 	return i;
396f507cd22SGeert Uytterhoeven }
397f507cd22SGeert Uytterhoeven 
ps3vram_cache_init(struct ps3_system_bus_device * dev)398f507cd22SGeert Uytterhoeven static int ps3vram_cache_init(struct ps3_system_bus_device *dev)
399f507cd22SGeert Uytterhoeven {
40003fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
401f507cd22SGeert Uytterhoeven 
402f507cd22SGeert Uytterhoeven 	priv->cache.page_count = CACHE_PAGE_COUNT;
403f507cd22SGeert Uytterhoeven 	priv->cache.page_size = CACHE_PAGE_SIZE;
4046396bb22SKees Cook 	priv->cache.tags = kcalloc(CACHE_PAGE_COUNT,
4056396bb22SKees Cook 				   sizeof(struct ps3vram_tag),
4066396bb22SKees Cook 				   GFP_KERNEL);
407fd1335e0SMarkus Elfring 	if (!priv->cache.tags)
408f507cd22SGeert Uytterhoeven 		return -ENOMEM;
409f507cd22SGeert Uytterhoeven 
410f507cd22SGeert Uytterhoeven 	dev_info(&dev->core, "Created ram cache: %d entries, %d KiB each\n",
411f507cd22SGeert Uytterhoeven 		CACHE_PAGE_COUNT, CACHE_PAGE_SIZE / 1024);
412f507cd22SGeert Uytterhoeven 
413f507cd22SGeert Uytterhoeven 	return 0;
414f507cd22SGeert Uytterhoeven }
415f507cd22SGeert Uytterhoeven 
ps3vram_cache_cleanup(struct ps3_system_bus_device * dev)416f507cd22SGeert Uytterhoeven static void ps3vram_cache_cleanup(struct ps3_system_bus_device *dev)
417f507cd22SGeert Uytterhoeven {
41803fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
419f507cd22SGeert Uytterhoeven 
420f507cd22SGeert Uytterhoeven 	ps3vram_cache_flush(dev);
421f507cd22SGeert Uytterhoeven 	kfree(priv->cache.tags);
422f507cd22SGeert Uytterhoeven }
423f507cd22SGeert Uytterhoeven 
ps3vram_read(struct ps3_system_bus_device * dev,loff_t from,size_t len,size_t * retlen,u_char * buf)4244e4cbee9SChristoph Hellwig static blk_status_t ps3vram_read(struct ps3_system_bus_device *dev, loff_t from,
425f507cd22SGeert Uytterhoeven 			size_t len, size_t *retlen, u_char *buf)
426f507cd22SGeert Uytterhoeven {
42703fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
428f507cd22SGeert Uytterhoeven 	unsigned int cached, count;
429f507cd22SGeert Uytterhoeven 
430f507cd22SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s: from=0x%08x len=0x%zx\n", __func__,
431f507cd22SGeert Uytterhoeven 		(unsigned int)from, len);
432f507cd22SGeert Uytterhoeven 
433f507cd22SGeert Uytterhoeven 	if (from >= priv->size)
4344e4cbee9SChristoph Hellwig 		return BLK_STS_IOERR;
435f507cd22SGeert Uytterhoeven 
436f507cd22SGeert Uytterhoeven 	if (len > priv->size - from)
437f507cd22SGeert Uytterhoeven 		len = priv->size - from;
438f507cd22SGeert Uytterhoeven 
439f507cd22SGeert Uytterhoeven 	/* Copy from vram to buf */
440f507cd22SGeert Uytterhoeven 	count = len;
441f507cd22SGeert Uytterhoeven 	while (count) {
442f507cd22SGeert Uytterhoeven 		unsigned int offset, avail;
443f507cd22SGeert Uytterhoeven 		unsigned int entry;
444f507cd22SGeert Uytterhoeven 
445f507cd22SGeert Uytterhoeven 		offset = (unsigned int) (from & (priv->cache.page_size - 1));
446f507cd22SGeert Uytterhoeven 		avail  = priv->cache.page_size - offset;
447f507cd22SGeert Uytterhoeven 
448f507cd22SGeert Uytterhoeven 		entry = ps3vram_cache_match(dev, from);
449f507cd22SGeert Uytterhoeven 		cached = CACHE_OFFSET + entry * priv->cache.page_size + offset;
450f507cd22SGeert Uytterhoeven 
451f507cd22SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s: from=%08x cached=%08x offset=%08x "
452f507cd22SGeert Uytterhoeven 			"avail=%08x count=%08x\n", __func__,
453f507cd22SGeert Uytterhoeven 			(unsigned int)from, cached, offset, avail, count);
454f507cd22SGeert Uytterhoeven 
455f507cd22SGeert Uytterhoeven 		if (avail > count)
456f507cd22SGeert Uytterhoeven 			avail = count;
457f507cd22SGeert Uytterhoeven 		memcpy(buf, priv->xdr_buf + cached, avail);
458f507cd22SGeert Uytterhoeven 
459f507cd22SGeert Uytterhoeven 		buf += avail;
460f507cd22SGeert Uytterhoeven 		count -= avail;
461f507cd22SGeert Uytterhoeven 		from += avail;
462f507cd22SGeert Uytterhoeven 	}
463f507cd22SGeert Uytterhoeven 
464f507cd22SGeert Uytterhoeven 	*retlen = len;
465f507cd22SGeert Uytterhoeven 	return 0;
466f507cd22SGeert Uytterhoeven }
467f507cd22SGeert Uytterhoeven 
ps3vram_write(struct ps3_system_bus_device * dev,loff_t to,size_t len,size_t * retlen,const u_char * buf)4684e4cbee9SChristoph Hellwig static blk_status_t ps3vram_write(struct ps3_system_bus_device *dev, loff_t to,
469f507cd22SGeert Uytterhoeven 			 size_t len, size_t *retlen, const u_char *buf)
470f507cd22SGeert Uytterhoeven {
47103fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
472f507cd22SGeert Uytterhoeven 	unsigned int cached, count;
473f507cd22SGeert Uytterhoeven 
474f507cd22SGeert Uytterhoeven 	if (to >= priv->size)
4754e4cbee9SChristoph Hellwig 		return BLK_STS_IOERR;
476f507cd22SGeert Uytterhoeven 
477f507cd22SGeert Uytterhoeven 	if (len > priv->size - to)
478f507cd22SGeert Uytterhoeven 		len = priv->size - to;
479f507cd22SGeert Uytterhoeven 
480f507cd22SGeert Uytterhoeven 	/* Copy from buf to vram */
481f507cd22SGeert Uytterhoeven 	count = len;
482f507cd22SGeert Uytterhoeven 	while (count) {
483f507cd22SGeert Uytterhoeven 		unsigned int offset, avail;
484f507cd22SGeert Uytterhoeven 		unsigned int entry;
485f507cd22SGeert Uytterhoeven 
486f507cd22SGeert Uytterhoeven 		offset = (unsigned int) (to & (priv->cache.page_size - 1));
487f507cd22SGeert Uytterhoeven 		avail  = priv->cache.page_size - offset;
488f507cd22SGeert Uytterhoeven 
489f507cd22SGeert Uytterhoeven 		entry = ps3vram_cache_match(dev, to);
490f507cd22SGeert Uytterhoeven 		cached = CACHE_OFFSET + entry * priv->cache.page_size + offset;
491f507cd22SGeert Uytterhoeven 
492f507cd22SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s: to=%08x cached=%08x offset=%08x "
493f507cd22SGeert Uytterhoeven 			"avail=%08x count=%08x\n", __func__, (unsigned int)to,
494f507cd22SGeert Uytterhoeven 			cached, offset, avail, count);
495f507cd22SGeert Uytterhoeven 
496f507cd22SGeert Uytterhoeven 		if (avail > count)
497f507cd22SGeert Uytterhoeven 			avail = count;
498f507cd22SGeert Uytterhoeven 		memcpy(priv->xdr_buf + cached, buf, avail);
499f507cd22SGeert Uytterhoeven 
500f507cd22SGeert Uytterhoeven 		priv->cache.tags[entry].flags |= CACHE_PAGE_DIRTY;
501f507cd22SGeert Uytterhoeven 
502f507cd22SGeert Uytterhoeven 		buf += avail;
503f507cd22SGeert Uytterhoeven 		count -= avail;
504f507cd22SGeert Uytterhoeven 		to += avail;
505f507cd22SGeert Uytterhoeven 	}
506f507cd22SGeert Uytterhoeven 
507f507cd22SGeert Uytterhoeven 	*retlen = len;
508f507cd22SGeert Uytterhoeven 	return 0;
509f507cd22SGeert Uytterhoeven }
510f507cd22SGeert Uytterhoeven 
ps3vram_proc_show(struct seq_file * m,void * v)511f507cd22SGeert Uytterhoeven static int ps3vram_proc_show(struct seq_file *m, void *v)
512f507cd22SGeert Uytterhoeven {
513f507cd22SGeert Uytterhoeven 	struct ps3vram_priv *priv = m->private;
514f507cd22SGeert Uytterhoeven 
515f507cd22SGeert Uytterhoeven 	seq_printf(m, "hit:%u\nmiss:%u\n", priv->cache.hit, priv->cache.miss);
516f507cd22SGeert Uytterhoeven 	return 0;
517f507cd22SGeert Uytterhoeven }
518f507cd22SGeert Uytterhoeven 
ps3vram_proc_init(struct ps3_system_bus_device * dev)5198d85fce7SGreg Kroah-Hartman static void ps3vram_proc_init(struct ps3_system_bus_device *dev)
520f507cd22SGeert Uytterhoeven {
52103fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
522f507cd22SGeert Uytterhoeven 	struct proc_dir_entry *pde;
523f507cd22SGeert Uytterhoeven 
5243f3942acSChristoph Hellwig 	pde = proc_create_single_data(DEVICE_NAME, 0444, NULL,
5253f3942acSChristoph Hellwig 			ps3vram_proc_show, priv);
5263c20e2f2SGeert Uytterhoeven 	if (!pde)
527f507cd22SGeert Uytterhoeven 		dev_warn(&dev->core, "failed to create /proc entry\n");
528f507cd22SGeert Uytterhoeven }
529f507cd22SGeert Uytterhoeven 
ps3vram_do_bio(struct ps3_system_bus_device * dev,struct bio * bio)530fb89e89dSGeert Uytterhoeven static struct bio *ps3vram_do_bio(struct ps3_system_bus_device *dev,
531fb89e89dSGeert Uytterhoeven 				  struct bio *bio)
532f507cd22SGeert Uytterhoeven {
53303fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
534f507cd22SGeert Uytterhoeven 	int write = bio_data_dir(bio) == WRITE;
535f507cd22SGeert Uytterhoeven 	const char *op = write ? "write" : "read";
5364f024f37SKent Overstreet 	loff_t offset = bio->bi_iter.bi_sector << 9;
5374e4cbee9SChristoph Hellwig 	blk_status_t error = 0;
5387988613bSKent Overstreet 	struct bio_vec bvec;
5397988613bSKent Overstreet 	struct bvec_iter iter;
540fb89e89dSGeert Uytterhoeven 	struct bio *next;
541f507cd22SGeert Uytterhoeven 
5427988613bSKent Overstreet 	bio_for_each_segment(bvec, bio, iter) {
543f507cd22SGeert Uytterhoeven 		/* PS3 is ppc64, so we don't handle highmem */
5446da525b3SChristoph Hellwig 		char *ptr = bvec_virt(&bvec);
5457988613bSKent Overstreet 		size_t len = bvec.bv_len, retlen;
546f507cd22SGeert Uytterhoeven 
547f507cd22SGeert Uytterhoeven 		dev_dbg(&dev->core, "    %s %zu bytes at offset %llu\n", op,
548f507cd22SGeert Uytterhoeven 			len, offset);
549f507cd22SGeert Uytterhoeven 		if (write)
550f507cd22SGeert Uytterhoeven 			error = ps3vram_write(dev, offset, len, &retlen, ptr);
551f507cd22SGeert Uytterhoeven 		else
552f507cd22SGeert Uytterhoeven 			error = ps3vram_read(dev, offset, len, &retlen, ptr);
553f507cd22SGeert Uytterhoeven 
554f507cd22SGeert Uytterhoeven 		if (error) {
555f507cd22SGeert Uytterhoeven 			dev_err(&dev->core, "%s failed\n", op);
556f507cd22SGeert Uytterhoeven 			goto out;
557f507cd22SGeert Uytterhoeven 		}
558f507cd22SGeert Uytterhoeven 
559f507cd22SGeert Uytterhoeven 		if (retlen != len) {
560f507cd22SGeert Uytterhoeven 			dev_err(&dev->core, "Short %s\n", op);
5614e4cbee9SChristoph Hellwig 			error = BLK_STS_IOERR;
562f507cd22SGeert Uytterhoeven 			goto out;
563f507cd22SGeert Uytterhoeven 		}
564f507cd22SGeert Uytterhoeven 
565f507cd22SGeert Uytterhoeven 		offset += len;
566f507cd22SGeert Uytterhoeven 	}
567f507cd22SGeert Uytterhoeven 
568f507cd22SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s completed\n", op);
569f507cd22SGeert Uytterhoeven 
570f507cd22SGeert Uytterhoeven out:
571fb89e89dSGeert Uytterhoeven 	spin_lock_irq(&priv->lock);
572fb89e89dSGeert Uytterhoeven 	bio_list_pop(&priv->list);
573fb89e89dSGeert Uytterhoeven 	next = bio_list_peek(&priv->list);
574fb89e89dSGeert Uytterhoeven 	spin_unlock_irq(&priv->lock);
575fb89e89dSGeert Uytterhoeven 
5764e4cbee9SChristoph Hellwig 	bio->bi_status = error;
5774246a0b6SChristoph Hellwig 	bio_endio(bio);
578fb89e89dSGeert Uytterhoeven 	return next;
579fb89e89dSGeert Uytterhoeven }
580fb89e89dSGeert Uytterhoeven 
ps3vram_submit_bio(struct bio * bio)5813e08773cSChristoph Hellwig static void ps3vram_submit_bio(struct bio *bio)
582fb89e89dSGeert Uytterhoeven {
583309dca30SChristoph Hellwig 	struct ps3_system_bus_device *dev = bio->bi_bdev->bd_disk->private_data;
58403fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
585fb89e89dSGeert Uytterhoeven 	int busy;
586fb89e89dSGeert Uytterhoeven 
587fb89e89dSGeert Uytterhoeven 	dev_dbg(&dev->core, "%s\n", __func__);
588fb89e89dSGeert Uytterhoeven 
589fb89e89dSGeert Uytterhoeven 	spin_lock_irq(&priv->lock);
590fb89e89dSGeert Uytterhoeven 	busy = !bio_list_empty(&priv->list);
591fb89e89dSGeert Uytterhoeven 	bio_list_add(&priv->list, bio);
592fb89e89dSGeert Uytterhoeven 	spin_unlock_irq(&priv->lock);
593fb89e89dSGeert Uytterhoeven 
594fb89e89dSGeert Uytterhoeven 	if (busy)
5953e08773cSChristoph Hellwig 		return;
596fb89e89dSGeert Uytterhoeven 
597fb89e89dSGeert Uytterhoeven 	do {
598fb89e89dSGeert Uytterhoeven 		bio = ps3vram_do_bio(dev, bio);
599fb89e89dSGeert Uytterhoeven 	} while (bio);
600f507cd22SGeert Uytterhoeven }
601f507cd22SGeert Uytterhoeven 
602c62b37d9SChristoph Hellwig static const struct block_device_operations ps3vram_fops = {
603c62b37d9SChristoph Hellwig 	.owner		= THIS_MODULE,
604c62b37d9SChristoph Hellwig 	.submit_bio	= ps3vram_submit_bio,
605c62b37d9SChristoph Hellwig };
606c62b37d9SChristoph Hellwig 
ps3vram_probe(struct ps3_system_bus_device * dev)6078d85fce7SGreg Kroah-Hartman static int ps3vram_probe(struct ps3_system_bus_device *dev)
608f507cd22SGeert Uytterhoeven {
609f507cd22SGeert Uytterhoeven 	struct ps3vram_priv *priv;
610f507cd22SGeert Uytterhoeven 	int error, status;
611f507cd22SGeert Uytterhoeven 	struct gendisk *gendisk;
61256ac72dbSGeert Uytterhoeven 	u64 ddr_size, ddr_lpar, ctrl_lpar, info_lpar, reports_lpar,
61356ac72dbSGeert Uytterhoeven 	    reports_size, xdr_lpar;
614f507cd22SGeert Uytterhoeven 	char *rest;
615f507cd22SGeert Uytterhoeven 
616f507cd22SGeert Uytterhoeven 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
617f507cd22SGeert Uytterhoeven 	if (!priv) {
618f507cd22SGeert Uytterhoeven 		error = -ENOMEM;
619f507cd22SGeert Uytterhoeven 		goto fail;
620f507cd22SGeert Uytterhoeven 	}
621f507cd22SGeert Uytterhoeven 
622fb89e89dSGeert Uytterhoeven 	spin_lock_init(&priv->lock);
623fb89e89dSGeert Uytterhoeven 	bio_list_init(&priv->list);
62403fa68c2SGeert Uytterhoeven 	ps3_system_bus_set_drvdata(dev, priv);
625f507cd22SGeert Uytterhoeven 
626f507cd22SGeert Uytterhoeven 	/* Allocate XDR buffer (1MiB aligned) */
627f507cd22SGeert Uytterhoeven 	priv->xdr_buf = (void *)__get_free_pages(GFP_KERNEL,
628f507cd22SGeert Uytterhoeven 		get_order(XDR_BUF_SIZE));
629f507cd22SGeert Uytterhoeven 	if (priv->xdr_buf == NULL) {
630f507cd22SGeert Uytterhoeven 		dev_err(&dev->core, "Could not allocate XDR buffer\n");
631f507cd22SGeert Uytterhoeven 		error = -ENOMEM;
632f507cd22SGeert Uytterhoeven 		goto fail_free_priv;
633f507cd22SGeert Uytterhoeven 	}
634f507cd22SGeert Uytterhoeven 
635f507cd22SGeert Uytterhoeven 	/* Put FIFO at begginning of XDR buffer */
636f507cd22SGeert Uytterhoeven 	priv->fifo_base = (u32 *) (priv->xdr_buf + FIFO_OFFSET);
637f507cd22SGeert Uytterhoeven 	priv->fifo_ptr = priv->fifo_base;
638f507cd22SGeert Uytterhoeven 
639f507cd22SGeert Uytterhoeven 	/* XXX: Need to open GPU, in case ps3fb or snd_ps3 aren't loaded */
640f507cd22SGeert Uytterhoeven 	if (ps3_open_hv_device(dev)) {
641f507cd22SGeert Uytterhoeven 		dev_err(&dev->core, "ps3_open_hv_device failed\n");
642f507cd22SGeert Uytterhoeven 		error = -EAGAIN;
6433273d877SJim Paris 		goto out_free_xdr_buf;
644f507cd22SGeert Uytterhoeven 	}
645f507cd22SGeert Uytterhoeven 
646f507cd22SGeert Uytterhoeven 	/* Request memory */
647f507cd22SGeert Uytterhoeven 	status = -1;
648f507cd22SGeert Uytterhoeven 	ddr_size = ALIGN(memparse(size, &rest), 1024*1024);
649f507cd22SGeert Uytterhoeven 	if (!ddr_size) {
650f507cd22SGeert Uytterhoeven 		dev_err(&dev->core, "Specified size is too small\n");
651f507cd22SGeert Uytterhoeven 		error = -EINVAL;
652f507cd22SGeert Uytterhoeven 		goto out_close_gpu;
653f507cd22SGeert Uytterhoeven 	}
654f507cd22SGeert Uytterhoeven 
655f507cd22SGeert Uytterhoeven 	while (ddr_size > 0) {
656f507cd22SGeert Uytterhoeven 		status = lv1_gpu_memory_allocate(ddr_size, 0, 0, 0, 0,
657f507cd22SGeert Uytterhoeven 						 &priv->memory_handle,
658f507cd22SGeert Uytterhoeven 						 &ddr_lpar);
659f507cd22SGeert Uytterhoeven 		if (!status)
660f507cd22SGeert Uytterhoeven 			break;
661f507cd22SGeert Uytterhoeven 		ddr_size -= 1024*1024;
662f507cd22SGeert Uytterhoeven 	}
663f507cd22SGeert Uytterhoeven 	if (status) {
664f507cd22SGeert Uytterhoeven 		dev_err(&dev->core, "lv1_gpu_memory_allocate failed %d\n",
665f507cd22SGeert Uytterhoeven 			status);
666f507cd22SGeert Uytterhoeven 		error = -ENOMEM;
6673273d877SJim Paris 		goto out_close_gpu;
668f507cd22SGeert Uytterhoeven 	}
669f507cd22SGeert Uytterhoeven 
670f507cd22SGeert Uytterhoeven 	/* Request context */
671f507cd22SGeert Uytterhoeven 	status = lv1_gpu_context_allocate(priv->memory_handle, 0,
672f507cd22SGeert Uytterhoeven 					  &priv->context_handle, &ctrl_lpar,
673f507cd22SGeert Uytterhoeven 					  &info_lpar, &reports_lpar,
674f507cd22SGeert Uytterhoeven 					  &reports_size);
675f507cd22SGeert Uytterhoeven 	if (status) {
676f507cd22SGeert Uytterhoeven 		dev_err(&dev->core, "lv1_gpu_context_allocate failed %d\n",
677f507cd22SGeert Uytterhoeven 			status);
678f507cd22SGeert Uytterhoeven 		error = -ENOMEM;
679f507cd22SGeert Uytterhoeven 		goto out_free_memory;
680f507cd22SGeert Uytterhoeven 	}
681f507cd22SGeert Uytterhoeven 
682f507cd22SGeert Uytterhoeven 	/* Map XDR buffer to RSX */
68356ac72dbSGeert Uytterhoeven 	xdr_lpar = ps3_mm_phys_to_lpar(__pa(priv->xdr_buf));
684f507cd22SGeert Uytterhoeven 	status = lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF,
68556ac72dbSGeert Uytterhoeven 				       xdr_lpar, XDR_BUF_SIZE,
68656ac72dbSGeert Uytterhoeven 				       CBE_IOPTE_PP_W | CBE_IOPTE_PP_R |
68756ac72dbSGeert Uytterhoeven 				       CBE_IOPTE_M);
688f507cd22SGeert Uytterhoeven 	if (status) {
689f507cd22SGeert Uytterhoeven 		dev_err(&dev->core, "lv1_gpu_context_iomap failed %d\n",
690f507cd22SGeert Uytterhoeven 			status);
691f507cd22SGeert Uytterhoeven 		error = -ENOMEM;
692f507cd22SGeert Uytterhoeven 		goto out_free_context;
693f507cd22SGeert Uytterhoeven 	}
694f507cd22SGeert Uytterhoeven 
695f507cd22SGeert Uytterhoeven 	priv->ctrl = ioremap(ctrl_lpar, 64 * 1024);
696f507cd22SGeert Uytterhoeven 	if (!priv->ctrl) {
697f507cd22SGeert Uytterhoeven 		dev_err(&dev->core, "ioremap CTRL failed\n");
698f507cd22SGeert Uytterhoeven 		error = -ENOMEM;
699c3b94fd8SGeert Uytterhoeven 		goto out_unmap_context;
700f507cd22SGeert Uytterhoeven 	}
701f507cd22SGeert Uytterhoeven 
702f507cd22SGeert Uytterhoeven 	priv->reports = ioremap(reports_lpar, reports_size);
703f507cd22SGeert Uytterhoeven 	if (!priv->reports) {
704f507cd22SGeert Uytterhoeven 		dev_err(&dev->core, "ioremap REPORTS failed\n");
705f507cd22SGeert Uytterhoeven 		error = -ENOMEM;
706f507cd22SGeert Uytterhoeven 		goto out_unmap_ctrl;
707f507cd22SGeert Uytterhoeven 	}
708f507cd22SGeert Uytterhoeven 
709f507cd22SGeert Uytterhoeven 	mutex_lock(&ps3_gpu_mutex);
710f507cd22SGeert Uytterhoeven 	ps3vram_init_ring(dev);
711f507cd22SGeert Uytterhoeven 	mutex_unlock(&ps3_gpu_mutex);
712f507cd22SGeert Uytterhoeven 
713f507cd22SGeert Uytterhoeven 	priv->size = ddr_size;
714f507cd22SGeert Uytterhoeven 
715f507cd22SGeert Uytterhoeven 	ps3vram_bind(dev);
716f507cd22SGeert Uytterhoeven 
717f507cd22SGeert Uytterhoeven 	mutex_lock(&ps3_gpu_mutex);
718f507cd22SGeert Uytterhoeven 	error = ps3vram_wait_ring(dev, 100);
719f507cd22SGeert Uytterhoeven 	mutex_unlock(&ps3_gpu_mutex);
720f507cd22SGeert Uytterhoeven 	if (error < 0) {
721f507cd22SGeert Uytterhoeven 		dev_err(&dev->core, "Failed to initialize channels\n");
722f507cd22SGeert Uytterhoeven 		error = -ETIMEDOUT;
723f507cd22SGeert Uytterhoeven 		goto out_unmap_reports;
724f507cd22SGeert Uytterhoeven 	}
725f507cd22SGeert Uytterhoeven 
72600e7c259SGeoff Levand 	error = ps3vram_cache_init(dev);
72700e7c259SGeoff Levand 	if (error < 0) {
72800e7c259SGeoff Levand 		goto out_unmap_reports;
72900e7c259SGeoff Levand 	}
73000e7c259SGeoff Levand 
731f507cd22SGeert Uytterhoeven 	ps3vram_proc_init(dev);
732f507cd22SGeert Uytterhoeven 
733*74fa8f9cSChristoph Hellwig 	gendisk = blk_alloc_disk(NULL, NUMA_NO_NODE);
734*74fa8f9cSChristoph Hellwig 	if (IS_ERR(gendisk)) {
735684bf9cdSChristoph Hellwig 		dev_err(&dev->core, "blk_alloc_disk failed\n");
736*74fa8f9cSChristoph Hellwig 		error = PTR_ERR(gendisk);
737f507cd22SGeert Uytterhoeven 		goto out_cache_cleanup;
738f507cd22SGeert Uytterhoeven 	}
739f507cd22SGeert Uytterhoeven 
740f507cd22SGeert Uytterhoeven 	priv->gendisk = gendisk;
741f507cd22SGeert Uytterhoeven 	gendisk->major = ps3vram_major;
742684bf9cdSChristoph Hellwig 	gendisk->minors = 1;
7431ebe2e5fSChristoph Hellwig 	gendisk->flags |= GENHD_FL_NO_PART;
744f507cd22SGeert Uytterhoeven 	gendisk->fops = &ps3vram_fops;
745f507cd22SGeert Uytterhoeven 	gendisk->private_data = dev;
746e55e1b48SWolfram Sang 	strscpy(gendisk->disk_name, DEVICE_NAME, sizeof(gendisk->disk_name));
747f507cd22SGeert Uytterhoeven 	set_capacity(gendisk, priv->size >> 9);
748f507cd22SGeert Uytterhoeven 
7491d0c0651SGeert Uytterhoeven 	dev_info(&dev->core, "%s: Using %llu MiB of GPU memory\n",
750f507cd22SGeert Uytterhoeven 		 gendisk->disk_name, get_capacity(gendisk) >> 11);
751f507cd22SGeert Uytterhoeven 
7523c30883aSLuis Chamberlain 	error = device_add_disk(&dev->core, gendisk, NULL);
7533c30883aSLuis Chamberlain 	if (error)
7543c30883aSLuis Chamberlain 		goto out_cleanup_disk;
7553c30883aSLuis Chamberlain 
756f507cd22SGeert Uytterhoeven 	return 0;
757f507cd22SGeert Uytterhoeven 
7583c30883aSLuis Chamberlain out_cleanup_disk:
7598b9ab626SChristoph Hellwig 	put_disk(gendisk);
760f507cd22SGeert Uytterhoeven out_cache_cleanup:
761f507cd22SGeert Uytterhoeven 	remove_proc_entry(DEVICE_NAME, NULL);
762f507cd22SGeert Uytterhoeven 	ps3vram_cache_cleanup(dev);
763f507cd22SGeert Uytterhoeven out_unmap_reports:
764f507cd22SGeert Uytterhoeven 	iounmap(priv->reports);
765f507cd22SGeert Uytterhoeven out_unmap_ctrl:
766f507cd22SGeert Uytterhoeven 	iounmap(priv->ctrl);
76756ac72dbSGeert Uytterhoeven out_unmap_context:
76856ac72dbSGeert Uytterhoeven 	lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF, xdr_lpar,
76956ac72dbSGeert Uytterhoeven 			      XDR_BUF_SIZE, CBE_IOPTE_M);
770f507cd22SGeert Uytterhoeven out_free_context:
771f507cd22SGeert Uytterhoeven 	lv1_gpu_context_free(priv->context_handle);
772f507cd22SGeert Uytterhoeven out_free_memory:
773f507cd22SGeert Uytterhoeven 	lv1_gpu_memory_free(priv->memory_handle);
774f507cd22SGeert Uytterhoeven out_close_gpu:
775f507cd22SGeert Uytterhoeven 	ps3_close_hv_device(dev);
776f507cd22SGeert Uytterhoeven out_free_xdr_buf:
777f507cd22SGeert Uytterhoeven 	free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE));
778f507cd22SGeert Uytterhoeven fail_free_priv:
779f507cd22SGeert Uytterhoeven 	kfree(priv);
78003fa68c2SGeert Uytterhoeven 	ps3_system_bus_set_drvdata(dev, NULL);
781f507cd22SGeert Uytterhoeven fail:
782f507cd22SGeert Uytterhoeven 	return error;
783f507cd22SGeert Uytterhoeven }
784f507cd22SGeert Uytterhoeven 
ps3vram_remove(struct ps3_system_bus_device * dev)7856d247e4dSUwe Kleine-König static void ps3vram_remove(struct ps3_system_bus_device *dev)
786f507cd22SGeert Uytterhoeven {
78703fa68c2SGeert Uytterhoeven 	struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
788f507cd22SGeert Uytterhoeven 
789f507cd22SGeert Uytterhoeven 	del_gendisk(priv->gendisk);
7908b9ab626SChristoph Hellwig 	put_disk(priv->gendisk);
791f507cd22SGeert Uytterhoeven 	remove_proc_entry(DEVICE_NAME, NULL);
792f507cd22SGeert Uytterhoeven 	ps3vram_cache_cleanup(dev);
793f507cd22SGeert Uytterhoeven 	iounmap(priv->reports);
794f507cd22SGeert Uytterhoeven 	iounmap(priv->ctrl);
79556ac72dbSGeert Uytterhoeven 	lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF,
79656ac72dbSGeert Uytterhoeven 			      ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)),
79756ac72dbSGeert Uytterhoeven 			      XDR_BUF_SIZE, CBE_IOPTE_M);
798f507cd22SGeert Uytterhoeven 	lv1_gpu_context_free(priv->context_handle);
799f507cd22SGeert Uytterhoeven 	lv1_gpu_memory_free(priv->memory_handle);
800f507cd22SGeert Uytterhoeven 	ps3_close_hv_device(dev);
801f507cd22SGeert Uytterhoeven 	free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE));
802f507cd22SGeert Uytterhoeven 	kfree(priv);
80303fa68c2SGeert Uytterhoeven 	ps3_system_bus_set_drvdata(dev, NULL);
804f507cd22SGeert Uytterhoeven }
805f507cd22SGeert Uytterhoeven 
806f507cd22SGeert Uytterhoeven static struct ps3_system_bus_driver ps3vram = {
807f507cd22SGeert Uytterhoeven 	.match_id	= PS3_MATCH_ID_GPU,
808f507cd22SGeert Uytterhoeven 	.match_sub_id	= PS3_MATCH_SUB_ID_GPU_RAMDISK,
809f507cd22SGeert Uytterhoeven 	.core.name	= DEVICE_NAME,
810f507cd22SGeert Uytterhoeven 	.core.owner	= THIS_MODULE,
811f507cd22SGeert Uytterhoeven 	.probe		= ps3vram_probe,
812f507cd22SGeert Uytterhoeven 	.remove		= ps3vram_remove,
813f507cd22SGeert Uytterhoeven 	.shutdown	= ps3vram_remove,
814f507cd22SGeert Uytterhoeven };
815f507cd22SGeert Uytterhoeven 
816f507cd22SGeert Uytterhoeven 
ps3vram_init(void)817f507cd22SGeert Uytterhoeven static int __init ps3vram_init(void)
818f507cd22SGeert Uytterhoeven {
819f507cd22SGeert Uytterhoeven 	int error;
820f507cd22SGeert Uytterhoeven 
821f507cd22SGeert Uytterhoeven 	if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
822f507cd22SGeert Uytterhoeven 		return -ENODEV;
823f507cd22SGeert Uytterhoeven 
824f507cd22SGeert Uytterhoeven 	error = register_blkdev(0, DEVICE_NAME);
825f507cd22SGeert Uytterhoeven 	if (error <= 0) {
826f507cd22SGeert Uytterhoeven 		pr_err("%s: register_blkdev failed %d\n", DEVICE_NAME, error);
827f507cd22SGeert Uytterhoeven 		return error;
828f507cd22SGeert Uytterhoeven 	}
829f507cd22SGeert Uytterhoeven 	ps3vram_major = error;
830f507cd22SGeert Uytterhoeven 
831f507cd22SGeert Uytterhoeven 	pr_info("%s: registered block device major %d\n", DEVICE_NAME,
832f507cd22SGeert Uytterhoeven 		ps3vram_major);
833f507cd22SGeert Uytterhoeven 
834f507cd22SGeert Uytterhoeven 	error = ps3_system_bus_driver_register(&ps3vram);
835f507cd22SGeert Uytterhoeven 	if (error)
836f507cd22SGeert Uytterhoeven 		unregister_blkdev(ps3vram_major, DEVICE_NAME);
837f507cd22SGeert Uytterhoeven 
838f507cd22SGeert Uytterhoeven 	return error;
839f507cd22SGeert Uytterhoeven }
840f507cd22SGeert Uytterhoeven 
ps3vram_exit(void)841f507cd22SGeert Uytterhoeven static void __exit ps3vram_exit(void)
842f507cd22SGeert Uytterhoeven {
843f507cd22SGeert Uytterhoeven 	ps3_system_bus_driver_unregister(&ps3vram);
844f507cd22SGeert Uytterhoeven 	unregister_blkdev(ps3vram_major, DEVICE_NAME);
845f507cd22SGeert Uytterhoeven }
846f507cd22SGeert Uytterhoeven 
847f507cd22SGeert Uytterhoeven module_init(ps3vram_init);
848f507cd22SGeert Uytterhoeven module_exit(ps3vram_exit);
849f507cd22SGeert Uytterhoeven 
850f507cd22SGeert Uytterhoeven MODULE_LICENSE("GPL");
851f507cd22SGeert Uytterhoeven MODULE_DESCRIPTION("PS3 Video RAM Storage Driver");
852f507cd22SGeert Uytterhoeven MODULE_AUTHOR("Sony Corporation");
853f507cd22SGeert Uytterhoeven MODULE_ALIAS(PS3_MODULE_ALIAS_GPU_RAMDISK);
854