1c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross/*
2c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * Copyright (C) 2012 Google, Inc.
3c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross *
4c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * This software is licensed under the terms of the GNU General Public
5c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * License version 2, as published by the Free Software Foundation, and
6c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * may be copied, distributed, and modified under those terms.
7c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross *
8c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * This program is distributed in the hope that it will be useful,
9c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * but WITHOUT ANY WARRANTY; without even the implied warranty of
10c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * GNU General Public License for more details.
12c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross *
13c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross */
14c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
15404a6043385de17273624b076599669db5ad891fColin Cross#include <linux/device.h>
16404a6043385de17273624b076599669db5ad891fColin Cross#include <linux/err.h>
17c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross#include <linux/errno.h>
18c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross#include <linux/kernel.h>
19c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross#include <linux/init.h>
20c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross#include <linux/io.h>
21404a6043385de17273624b076599669db5ad891fColin Cross#include <linux/list.h>
22404a6043385de17273624b076599669db5ad891fColin Cross#include <linux/memblock.h>
23a3a0f312f78c4321a33314edb18b565f97ba489fColin Cross#include <linux/persistent_ram.h>
249cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross#include <linux/rslib.h>
25c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross#include <linux/slab.h>
26404a6043385de17273624b076599669db5ad891fColin Cross#include <linux/vmalloc.h>
27c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
28c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Crossstruct persistent_ram_buffer {
29c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	uint32_t    sig;
30808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	atomic_t    start;
31808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	atomic_t    size;
32c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	uint8_t     data[0];
33c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross};
34c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
35c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross#define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */
36c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
374a2212f2e6a4938180bfced93efb3adea4c6418dColin Crossstatic __devinitdata LIST_HEAD(persistent_ram_list);
38c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
39808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Crossstatic inline size_t buffer_size(struct persistent_ram_zone *prz)
40808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross{
41808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	return atomic_read(&prz->buffer->size);
42808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross}
43808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross
44808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Crossstatic inline size_t buffer_start(struct persistent_ram_zone *prz)
45808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross{
46808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	return atomic_read(&prz->buffer->start);
47808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross}
48808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross
49808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross/* increase and wrap the start pointer, returning the old value */
50808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Crossstatic inline size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a)
51808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross{
52808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	int old;
53808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	int new;
54808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross
55808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	do {
56808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross		old = atomic_read(&prz->buffer->start);
57808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross		new = old + a;
58808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross		while (unlikely(new > prz->buffer_size))
59808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross			new -= prz->buffer_size;
60808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	} while (atomic_cmpxchg(&prz->buffer->start, old, new) != old);
61808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross
62808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	return old;
63808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross}
64808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross
65808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross/* increase the size counter until it hits the max size */
66808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Crossstatic inline void buffer_size_add(struct persistent_ram_zone *prz, size_t a)
67808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross{
68808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	size_t old;
69808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	size_t new;
70808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross
71808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	if (atomic_read(&prz->buffer->size) == prz->buffer_size)
72808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross		return;
73808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross
74808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	do {
75808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross		old = atomic_read(&prz->buffer->size);
76808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross		new = old + a;
77808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross		if (new > prz->buffer_size)
78808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross			new = prz->buffer_size;
79808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	} while (atomic_cmpxchg(&prz->buffer->size, old, new) != old);
80808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross}
81808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross
82a15d0b365e9bbf04dacb44fbe69d15f6594460e1Colin Crossstatic void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz,
83c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	uint8_t *data, size_t len, uint8_t *ecc)
84c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{
85c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	int i;
869cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	uint16_t par[prz->ecc_size];
879cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
88c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	/* Initialize the parity buffer */
89c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	memset(par, 0, sizeof(par));
90c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	encode_rs8(prz->rs_decoder, data, len, par, 0);
919cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	for (i = 0; i < prz->ecc_size; i++)
92c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		ecc[i] = par[i];
93c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross}
94c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
95c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Crossstatic int persistent_ram_decode_rs8(struct persistent_ram_zone *prz,
96c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	void *data, size_t len, uint8_t *ecc)
97c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{
98c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	int i;
999cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	uint16_t par[prz->ecc_size];
1009cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
1019cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	for (i = 0; i < prz->ecc_size; i++)
102c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		par[i] = ecc[i];
103c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	return decode_rs8(prz->rs_decoder, data, par, len,
104c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross				NULL, 0, NULL, 0, NULL);
105c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross}
106c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
107a15d0b365e9bbf04dacb44fbe69d15f6594460e1Colin Crossstatic void notrace persistent_ram_update_ecc(struct persistent_ram_zone *prz,
108808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	unsigned int start, unsigned int count)
109c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{
110c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	struct persistent_ram_buffer *buffer = prz->buffer;
111c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	uint8_t *buffer_end = buffer->data + prz->buffer_size;
112c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	uint8_t *block;
113c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	uint8_t *par;
1149cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	int ecc_block_size = prz->ecc_block_size;
1159cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	int ecc_size = prz->ecc_size;
1169cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	int size = prz->ecc_block_size;
1179cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
1189cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	if (!prz->ecc)
1199cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		return;
1209cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
121808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	block = buffer->data + (start & ~(ecc_block_size - 1));
122808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	par = prz->par_buffer + (start / ecc_block_size) * prz->ecc_size;
123808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross
124c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	do {
1259cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		if (block + ecc_block_size > buffer_end)
126c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross			size = buffer_end - block;
127c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		persistent_ram_encode_rs8(prz, block, size, par);
1289cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		block += ecc_block_size;
1299cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		par += ecc_size;
130808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	} while (block < buffer->data + start + count);
131c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross}
132c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
1339cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Crossstatic void persistent_ram_update_header_ecc(struct persistent_ram_zone *prz)
134c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{
135c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	struct persistent_ram_buffer *buffer = prz->buffer;
136c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
1379cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	if (!prz->ecc)
1389cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		return;
1399cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
140c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	persistent_ram_encode_rs8(prz, (uint8_t *)buffer, sizeof(*buffer),
141c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross				  prz->par_header);
142c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross}
143c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
1449cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Crossstatic void persistent_ram_ecc_old(struct persistent_ram_zone *prz)
145c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{
146c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	struct persistent_ram_buffer *buffer = prz->buffer;
147c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	uint8_t *block;
148c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	uint8_t *par;
149c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
1509cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	if (!prz->ecc)
1519cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		return;
1529cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
153c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	block = buffer->data;
154c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	par = prz->par_buffer;
155808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	while (block < buffer->data + buffer_size(prz)) {
156c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		int numerr;
1579cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		int size = prz->ecc_block_size;
158c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		if (block + size > buffer->data + prz->buffer_size)
159c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross			size = buffer->data + prz->buffer_size - block;
160c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		numerr = persistent_ram_decode_rs8(prz, block, size, par);
161c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		if (numerr > 0) {
1629cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross			pr_devel("persistent_ram: error in block %p, %d\n",
163c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross			       block, numerr);
164c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross			prz->corrected_bytes += numerr;
165c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		} else if (numerr < 0) {
1669cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross			pr_devel("persistent_ram: uncorrectable error in block %p\n",
167c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross				block);
168c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross			prz->bad_blocks++;
169c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		}
1709cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		block += prz->ecc_block_size;
1719cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		par += prz->ecc_size;
1729cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	}
1739cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross}
1749cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
1759cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Crossstatic int persistent_ram_init_ecc(struct persistent_ram_zone *prz,
1766411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg	size_t buffer_size, struct persistent_ram *ram)
1779cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross{
1789cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	int numerr;
1799cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	struct persistent_ram_buffer *buffer = prz->buffer;
1809cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	int ecc_blocks;
1819cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
1829cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	if (!prz->ecc)
1839cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		return 0;
1849cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
1856411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg	prz->ecc_block_size = ram->ecc_block_size ?: 128;
1866411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg	prz->ecc_size = ram->ecc_size ?: 16;
1876411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg	prz->ecc_symsize = ram->ecc_symsize ?: 8;
1886411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg	prz->ecc_poly = ram->ecc_poly ?: 0x11d;
1899cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
1906ce7d7aa4b53b6171c835a6964a24201ac89ee64Arve Hjønnevåg	ecc_blocks = DIV_ROUND_UP(prz->buffer_size - prz->ecc_size,
1916ce7d7aa4b53b6171c835a6964a24201ac89ee64Arve Hjønnevåg				  prz->ecc_block_size + prz->ecc_size);
1929cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	prz->buffer_size -= (ecc_blocks + 1) * prz->ecc_size;
1939cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
1949cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	if (prz->buffer_size > buffer_size) {
1959cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		pr_err("persistent_ram: invalid size %zu, non-ecc datasize %zu\n",
1969cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		       buffer_size, prz->buffer_size);
1979cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		return -EINVAL;
1989cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	}
1999cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
2009cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	prz->par_buffer = buffer->data + prz->buffer_size;
2019cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	prz->par_header = prz->par_buffer + ecc_blocks * prz->ecc_size;
2029cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
2039cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	/*
2049cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	 * first consecutive root is 0
2059cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	 * primitive element to generate roots = 1
2069cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	 */
2079cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	prz->rs_decoder = init_rs(prz->ecc_symsize, prz->ecc_poly, 0, 1,
2089cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross				  prz->ecc_size);
2099cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	if (prz->rs_decoder == NULL) {
2109cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		pr_info("persistent_ram: init_rs failed\n");
2119cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		return -EINVAL;
212c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	}
2139cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
2149cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	prz->corrected_bytes = 0;
2159cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	prz->bad_blocks = 0;
2169cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
2179cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	numerr = persistent_ram_decode_rs8(prz, buffer, sizeof(*buffer),
2189cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross					   prz->par_header);
2199cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	if (numerr > 0) {
2209cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		pr_info("persistent_ram: error in header, %d\n", numerr);
2219cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		prz->corrected_bytes += numerr;
2229cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	} else if (numerr < 0) {
2239cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		pr_info("persistent_ram: uncorrectable error in header\n");
2249cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		prz->bad_blocks++;
2259cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	}
2269cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
2279cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	return 0;
2289cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross}
2299cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
2309cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Crossssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
2319cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	char *str, size_t len)
2329cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross{
2339cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	ssize_t ret;
2349cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
2359cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	if (prz->corrected_bytes || prz->bad_blocks)
2369cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		ret = snprintf(str, len, ""
2379cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross			"\n%d Corrected bytes, %d unrecoverable blocks\n",
2389cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross			prz->corrected_bytes, prz->bad_blocks);
2399cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	else
2409cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross		ret = snprintf(str, len, "\nNo errors detected\n");
2419cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
2429cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	return ret;
2439cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross}
2449cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
245a15d0b365e9bbf04dacb44fbe69d15f6594460e1Colin Crossstatic void notrace persistent_ram_update(struct persistent_ram_zone *prz,
246808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	const void *s, unsigned int start, unsigned int count)
2479cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross{
2489cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	struct persistent_ram_buffer *buffer = prz->buffer;
249808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	memcpy(buffer->data + start, s, count);
250808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	persistent_ram_update_ecc(prz, start, count);
2519cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross}
2529cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
2534a2212f2e6a4938180bfced93efb3adea4c6418dColin Crossstatic void __devinit
2549cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Crosspersistent_ram_save_old(struct persistent_ram_zone *prz)
2559cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross{
2569cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	struct persistent_ram_buffer *buffer = prz->buffer;
257808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	size_t size = buffer_size(prz);
258808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	size_t start = buffer_start(prz);
2599cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	char *dest;
2609cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross
2619cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	persistent_ram_ecc_old(prz);
262c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
263808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	dest = kmalloc(size, GFP_KERNEL);
264c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	if (dest == NULL) {
265c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		pr_err("persistent_ram: failed to allocate buffer\n");
266c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		return;
267c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	}
268c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
269c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	prz->old_log = dest;
270808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	prz->old_log_size = size;
271808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	memcpy(prz->old_log, &buffer->data[start], size - start);
272808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	memcpy(prz->old_log + size - start, &buffer->data[0], start);
273c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross}
274c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
275a15d0b365e9bbf04dacb44fbe69d15f6594460e1Colin Crossint notrace persistent_ram_write(struct persistent_ram_zone *prz,
276c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	const void *s, unsigned int count)
277c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{
278c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	int rem;
279c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	int c = count;
280808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	size_t start;
281c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
282808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	if (unlikely(c > prz->buffer_size)) {
283c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		s += c - prz->buffer_size;
284c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		c = prz->buffer_size;
285c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	}
286808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross
287bbe784b683f6e49bc345577fc5398344bc7c289eAnton Vorontsov	buffer_size_add(prz, c);
288808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross
289808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	start = buffer_start_add(prz, c);
290808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross
291808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	rem = prz->buffer_size - start;
292808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	if (unlikely(rem < c)) {
293808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross		persistent_ram_update(prz, s, start, rem);
294c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		s += rem;
295c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		c -= rem;
296808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross		start = 0;
297c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	}
298808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	persistent_ram_update(prz, s, start, c);
299c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
3009cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	persistent_ram_update_header_ecc(prz);
301c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
302c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	return count;
303c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross}
304c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
305c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Crosssize_t persistent_ram_old_size(struct persistent_ram_zone *prz)
306c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{
307c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	return prz->old_log_size;
308c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross}
309c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
310c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Crossvoid *persistent_ram_old(struct persistent_ram_zone *prz)
311c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{
312c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	return prz->old_log;
313c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross}
314c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
315c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Crossvoid persistent_ram_free_old(struct persistent_ram_zone *prz)
316c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{
317c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	kfree(prz->old_log);
318c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	prz->old_log = NULL;
319c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	prz->old_log_size = 0;
320c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross}
321c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
322404a6043385de17273624b076599669db5ad891fColin Crossstatic int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size,
323404a6043385de17273624b076599669db5ad891fColin Cross		struct persistent_ram_zone *prz)
324c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{
325404a6043385de17273624b076599669db5ad891fColin Cross	struct page **pages;
326404a6043385de17273624b076599669db5ad891fColin Cross	phys_addr_t page_start;
327404a6043385de17273624b076599669db5ad891fColin Cross	unsigned int page_count;
328404a6043385de17273624b076599669db5ad891fColin Cross	pgprot_t prot;
329404a6043385de17273624b076599669db5ad891fColin Cross	unsigned int i;
330404a6043385de17273624b076599669db5ad891fColin Cross
331404a6043385de17273624b076599669db5ad891fColin Cross	page_start = start - offset_in_page(start);
332404a6043385de17273624b076599669db5ad891fColin Cross	page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE);
333404a6043385de17273624b076599669db5ad891fColin Cross
334404a6043385de17273624b076599669db5ad891fColin Cross	prot = pgprot_noncached(PAGE_KERNEL);
335404a6043385de17273624b076599669db5ad891fColin Cross
336404a6043385de17273624b076599669db5ad891fColin Cross	pages = kmalloc(sizeof(struct page *) * page_count, GFP_KERNEL);
337404a6043385de17273624b076599669db5ad891fColin Cross	if (!pages) {
338404a6043385de17273624b076599669db5ad891fColin Cross		pr_err("%s: Failed to allocate array for %u pages\n", __func__,
339404a6043385de17273624b076599669db5ad891fColin Cross			page_count);
340404a6043385de17273624b076599669db5ad891fColin Cross		return -ENOMEM;
341404a6043385de17273624b076599669db5ad891fColin Cross	}
342404a6043385de17273624b076599669db5ad891fColin Cross
343404a6043385de17273624b076599669db5ad891fColin Cross	for (i = 0; i < page_count; i++) {
344404a6043385de17273624b076599669db5ad891fColin Cross		phys_addr_t addr = page_start + i * PAGE_SIZE;
345404a6043385de17273624b076599669db5ad891fColin Cross		pages[i] = pfn_to_page(addr >> PAGE_SHIFT);
346404a6043385de17273624b076599669db5ad891fColin Cross	}
347404a6043385de17273624b076599669db5ad891fColin Cross	prz->vaddr = vmap(pages, page_count, VM_MAP, prot);
348404a6043385de17273624b076599669db5ad891fColin Cross	kfree(pages);
349404a6043385de17273624b076599669db5ad891fColin Cross	if (!prz->vaddr) {
350404a6043385de17273624b076599669db5ad891fColin Cross		pr_err("%s: Failed to map %u pages\n", __func__, page_count);
351404a6043385de17273624b076599669db5ad891fColin Cross		return -ENOMEM;
352404a6043385de17273624b076599669db5ad891fColin Cross	}
353404a6043385de17273624b076599669db5ad891fColin Cross
354404a6043385de17273624b076599669db5ad891fColin Cross	prz->buffer = prz->vaddr + offset_in_page(start);
355404a6043385de17273624b076599669db5ad891fColin Cross	prz->buffer_size = size - sizeof(struct persistent_ram_buffer);
356404a6043385de17273624b076599669db5ad891fColin Cross
357404a6043385de17273624b076599669db5ad891fColin Cross	return 0;
358404a6043385de17273624b076599669db5ad891fColin Cross}
359404a6043385de17273624b076599669db5ad891fColin Cross
3604a2212f2e6a4938180bfced93efb3adea4c6418dColin Crossstatic int __devinit persistent_ram_buffer_init(const char *name,
3616411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg		struct persistent_ram_zone *prz, struct persistent_ram **ramp)
362404a6043385de17273624b076599669db5ad891fColin Cross{
363404a6043385de17273624b076599669db5ad891fColin Cross	int i;
364404a6043385de17273624b076599669db5ad891fColin Cross	struct persistent_ram *ram;
365404a6043385de17273624b076599669db5ad891fColin Cross	struct persistent_ram_descriptor *desc;
366404a6043385de17273624b076599669db5ad891fColin Cross	phys_addr_t start;
367404a6043385de17273624b076599669db5ad891fColin Cross
368404a6043385de17273624b076599669db5ad891fColin Cross	list_for_each_entry(ram, &persistent_ram_list, node) {
369404a6043385de17273624b076599669db5ad891fColin Cross		start = ram->start;
370404a6043385de17273624b076599669db5ad891fColin Cross		for (i = 0; i < ram->num_descs; i++) {
371404a6043385de17273624b076599669db5ad891fColin Cross			desc = &ram->descs[i];
3726411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg			if (!strcmp(desc->name, name)) {
3736411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg				*ramp = ram;
374404a6043385de17273624b076599669db5ad891fColin Cross				return persistent_ram_buffer_map(start,
375404a6043385de17273624b076599669db5ad891fColin Cross						desc->size, prz);
3766411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg			}
377404a6043385de17273624b076599669db5ad891fColin Cross			start += desc->size;
378404a6043385de17273624b076599669db5ad891fColin Cross		}
379404a6043385de17273624b076599669db5ad891fColin Cross	}
380404a6043385de17273624b076599669db5ad891fColin Cross
381404a6043385de17273624b076599669db5ad891fColin Cross	return -EINVAL;
382404a6043385de17273624b076599669db5ad891fColin Cross}
383404a6043385de17273624b076599669db5ad891fColin Cross
3844a2212f2e6a4938180bfced93efb3adea4c6418dColin Crossstatic  __devinit
385404a6043385de17273624b076599669db5ad891fColin Crossstruct persistent_ram_zone *__persistent_ram_init(struct device *dev, bool ecc)
386404a6043385de17273624b076599669db5ad891fColin Cross{
3876411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg	struct persistent_ram *ram;
388404a6043385de17273624b076599669db5ad891fColin Cross	struct persistent_ram_zone *prz;
389474a89885f77953b12bce9f23660c31ef5c2630eJesper Juhl	int ret = -ENOMEM;
390c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
391404a6043385de17273624b076599669db5ad891fColin Cross	prz = kzalloc(sizeof(struct persistent_ram_zone), GFP_KERNEL);
392404a6043385de17273624b076599669db5ad891fColin Cross	if (!prz) {
393404a6043385de17273624b076599669db5ad891fColin Cross		pr_err("persistent_ram: failed to allocate persistent ram zone\n");
394474a89885f77953b12bce9f23660c31ef5c2630eJesper Juhl		goto err;
395404a6043385de17273624b076599669db5ad891fColin Cross	}
396c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
397404a6043385de17273624b076599669db5ad891fColin Cross	INIT_LIST_HEAD(&prz->node);
398c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
3996411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg	ret = persistent_ram_buffer_init(dev_name(dev), prz, &ram);
400404a6043385de17273624b076599669db5ad891fColin Cross	if (ret) {
401404a6043385de17273624b076599669db5ad891fColin Cross		pr_err("persistent_ram: failed to initialize buffer\n");
402474a89885f77953b12bce9f23660c31ef5c2630eJesper Juhl		goto err;
403c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	}
404c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
4059cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	prz->ecc = ecc;
4066411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg	ret = persistent_ram_init_ecc(prz, prz->buffer_size, ram);
4079cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross	if (ret)
408474a89885f77953b12bce9f23660c31ef5c2630eJesper Juhl		goto err;
409c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
410404a6043385de17273624b076599669db5ad891fColin Cross	if (prz->buffer->sig == PERSISTENT_RAM_SIG) {
411808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross		if (buffer_size(prz) > prz->buffer_size ||
412808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross		    buffer_start(prz) > buffer_size(prz))
413808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross			pr_info("persistent_ram: found existing invalid buffer,"
4148543065b05c5a2aadbaf476a3af3099753fd710eColin Cross				" size %zu, start %zu\n",
415808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross			       buffer_size(prz), buffer_start(prz));
416c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		else {
417808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross			pr_info("persistent_ram: found existing buffer,"
4188543065b05c5a2aadbaf476a3af3099753fd710eColin Cross				" size %zu, start %zu\n",
419808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross			       buffer_size(prz), buffer_start(prz));
420c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross			persistent_ram_save_old(prz);
421c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross		}
422c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	} else {
423808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross		pr_info("persistent_ram: no valid data in buffer"
424808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross			" (sig = 0x%08x)\n", prz->buffer->sig);
425c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross	}
426c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
427404a6043385de17273624b076599669db5ad891fColin Cross	prz->buffer->sig = PERSISTENT_RAM_SIG;
428808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	atomic_set(&prz->buffer->start, 0);
429808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross	atomic_set(&prz->buffer->size, 0);
430c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
431404a6043385de17273624b076599669db5ad891fColin Cross	return prz;
432474a89885f77953b12bce9f23660c31ef5c2630eJesper Juhlerr:
433474a89885f77953b12bce9f23660c31ef5c2630eJesper Juhl	kfree(prz);
434474a89885f77953b12bce9f23660c31ef5c2630eJesper Juhl	return ERR_PTR(ret);
435404a6043385de17273624b076599669db5ad891fColin Cross}
436c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
4374a2212f2e6a4938180bfced93efb3adea4c6418dColin Crossstruct persistent_ram_zone * __devinit
438404a6043385de17273624b076599669db5ad891fColin Crosspersistent_ram_init_ringbuffer(struct device *dev, bool ecc)
439404a6043385de17273624b076599669db5ad891fColin Cross{
440404a6043385de17273624b076599669db5ad891fColin Cross	return __persistent_ram_init(dev, ecc);
441c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross}
442c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross
443404a6043385de17273624b076599669db5ad891fColin Crossint __init persistent_ram_early_init(struct persistent_ram *ram)
444c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{
445404a6043385de17273624b076599669db5ad891fColin Cross	int ret;
446404a6043385de17273624b076599669db5ad891fColin Cross
447404a6043385de17273624b076599669db5ad891fColin Cross	ret = memblock_reserve(ram->start, ram->size);
448404a6043385de17273624b076599669db5ad891fColin Cross	if (ret) {
449404a6043385de17273624b076599669db5ad891fColin Cross		pr_err("Failed to reserve persistent memory from %08lx-%08lx\n",
450404a6043385de17273624b076599669db5ad891fColin Cross			(long)ram->start, (long)(ram->start + ram->size - 1));
451404a6043385de17273624b076599669db5ad891fColin Cross		return ret;
452404a6043385de17273624b076599669db5ad891fColin Cross	}
453404a6043385de17273624b076599669db5ad891fColin Cross
454404a6043385de17273624b076599669db5ad891fColin Cross	list_add_tail(&ram->node, &persistent_ram_list);
455404a6043385de17273624b076599669db5ad891fColin Cross
456404a6043385de17273624b076599669db5ad891fColin Cross	pr_info("Initialized persistent memory from %08lx-%08lx\n",
457404a6043385de17273624b076599669db5ad891fColin Cross		(long)ram->start, (long)(ram->start + ram->size - 1));
458404a6043385de17273624b076599669db5ad891fColin Cross
459404a6043385de17273624b076599669db5ad891fColin Cross	return 0;
460c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross}
461