Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * c 2001 PPC 64 Team, IBM Corp | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public License | |
6 | * as published by the Free Software Foundation; either version | |
7 | * 2 of the License, or (at your option) any later version. | |
8 | * | |
9 | * /dev/nvram driver for PPC64 | |
10 | * | |
11 | * This perhaps should live in drivers/char | |
12 | */ | |
13 | ||
14 | ||
15 | #include <linux/types.h> | |
16 | #include <linux/errno.h> | |
17 | #include <linux/init.h> | |
18 | #include <linux/slab.h> | |
19 | #include <linux/spinlock.h> | |
20 | #include <asm/uaccess.h> | |
21 | #include <asm/nvram.h> | |
22 | #include <asm/rtas.h> | |
23 | #include <asm/prom.h> | |
24 | #include <asm/machdep.h> | |
25 | ||
26 | static unsigned int nvram_size; | |
27 | static int nvram_fetch, nvram_store; | |
28 | static char nvram_buf[NVRW_CNT]; /* assume this is in the first 4GB */ | |
29 | static DEFINE_SPINLOCK(nvram_lock); | |
30 | ||
31 | ||
32 | static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) | |
33 | { | |
34 | unsigned int i; | |
35 | unsigned long len; | |
36 | int done; | |
37 | unsigned long flags; | |
38 | char *p = buf; | |
39 | ||
40 | ||
41 | if (nvram_size == 0 || nvram_fetch == RTAS_UNKNOWN_SERVICE) | |
42 | return -ENODEV; | |
43 | ||
44 | if (*index >= nvram_size) | |
45 | return 0; | |
46 | ||
47 | i = *index; | |
48 | if (i + count > nvram_size) | |
49 | count = nvram_size - i; | |
50 | ||
51 | spin_lock_irqsave(&nvram_lock, flags); | |
52 | ||
53 | for (; count != 0; count -= len) { | |
54 | len = count; | |
55 | if (len > NVRW_CNT) | |
56 | len = NVRW_CNT; | |
57 | ||
58 | if ((rtas_call(nvram_fetch, 3, 2, &done, i, __pa(nvram_buf), | |
59 | len) != 0) || len != done) { | |
60 | spin_unlock_irqrestore(&nvram_lock, flags); | |
61 | return -EIO; | |
62 | } | |
63 | ||
64 | memcpy(p, nvram_buf, len); | |
65 | ||
66 | p += len; | |
67 | i += len; | |
68 | } | |
69 | ||
70 | spin_unlock_irqrestore(&nvram_lock, flags); | |
71 | ||
72 | *index = i; | |
73 | return p - buf; | |
74 | } | |
75 | ||
76 | static ssize_t pSeries_nvram_write(char *buf, size_t count, loff_t *index) | |
77 | { | |
78 | unsigned int i; | |
79 | unsigned long len; | |
80 | int done; | |
81 | unsigned long flags; | |
82 | const char *p = buf; | |
83 | ||
84 | if (nvram_size == 0 || nvram_store == RTAS_UNKNOWN_SERVICE) | |
85 | return -ENODEV; | |
86 | ||
87 | if (*index >= nvram_size) | |
88 | return 0; | |
89 | ||
90 | i = *index; | |
91 | if (i + count > nvram_size) | |
92 | count = nvram_size - i; | |
93 | ||
94 | spin_lock_irqsave(&nvram_lock, flags); | |
95 | ||
96 | for (; count != 0; count -= len) { | |
97 | len = count; | |
98 | if (len > NVRW_CNT) | |
99 | len = NVRW_CNT; | |
100 | ||
101 | memcpy(nvram_buf, p, len); | |
102 | ||
103 | if ((rtas_call(nvram_store, 3, 2, &done, i, __pa(nvram_buf), | |
104 | len) != 0) || len != done) { | |
105 | spin_unlock_irqrestore(&nvram_lock, flags); | |
106 | return -EIO; | |
107 | } | |
108 | ||
109 | p += len; | |
110 | i += len; | |
111 | } | |
112 | spin_unlock_irqrestore(&nvram_lock, flags); | |
113 | ||
114 | *index = i; | |
115 | return p - buf; | |
116 | } | |
117 | ||
118 | static ssize_t pSeries_nvram_get_size(void) | |
119 | { | |
120 | return nvram_size ? nvram_size : -ENODEV; | |
121 | } | |
122 | ||
123 | int __init pSeries_nvram_init(void) | |
124 | { | |
125 | struct device_node *nvram; | |
126 | unsigned int *nbytes_p, proplen; | |
127 | ||
128 | nvram = of_find_node_by_type(NULL, "nvram"); | |
129 | if (nvram == NULL) | |
130 | return -ENODEV; | |
131 | ||
132 | nbytes_p = (unsigned int *)get_property(nvram, "#bytes", &proplen); | |
133 | if (nbytes_p == NULL || proplen != sizeof(unsigned int)) | |
134 | return -EIO; | |
135 | ||
136 | nvram_size = *nbytes_p; | |
137 | ||
138 | nvram_fetch = rtas_token("nvram-fetch"); | |
139 | nvram_store = rtas_token("nvram-store"); | |
140 | printk(KERN_INFO "PPC64 nvram contains %d bytes\n", nvram_size); | |
141 | of_node_put(nvram); | |
142 | ||
143 | ppc_md.nvram_read = pSeries_nvram_read; | |
144 | ppc_md.nvram_write = pSeries_nvram_write; | |
145 | ppc_md.nvram_size = pSeries_nvram_get_size; | |
146 | ||
147 | return 0; | |
148 | } |