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
733684bf9cdSChristoph Hellwig gendisk = blk_alloc_disk(NUMA_NO_NODE);
734684bf9cdSChristoph Hellwig if (!gendisk) {
735684bf9cdSChristoph Hellwig dev_err(&dev->core, "blk_alloc_disk failed\n");
736f507cd22SGeert Uytterhoeven error = -ENOMEM;
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;
746*e55e1b48SWolfram 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