[ARM] orion5x: ts78xx make more bulletproof the RTC load/unload code
[linux-2.6] / arch / arm / mach-orion5x / ts78xx-setup.c
1 /*
2  * arch/arm/mach-orion5x/ts78xx-setup.c
3  *
4  * Maintainer: Alexander Clouter <alex@digriz.org.uk>
5  *
6  * This file is licensed under the terms of the GNU General Public
7  * License version 2.  This program is licensed "as is" without any
8  * warranty of any kind, whether express or implied.
9  */
10
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/sysfs.h>
14 #include <linux/platform_device.h>
15 #include <linux/mv643xx_eth.h>
16 #include <linux/ata_platform.h>
17 #include <linux/m48t86.h>
18 #include <asm/mach-types.h>
19 #include <asm/mach/arch.h>
20 #include <asm/mach/map.h>
21 #include <mach/orion5x.h>
22 #include "common.h"
23 #include "mpp.h"
24 #include "ts78xx-fpga.h"
25
26 /*****************************************************************************
27  * TS-78xx Info
28  ****************************************************************************/
29
30 /*
31  * FPGA - lives where the PCI bus would be at ORION5X_PCI_MEM_PHYS_BASE
32  */
33 #define TS78XX_FPGA_REGS_PHYS_BASE      0xe8000000
34 #define TS78XX_FPGA_REGS_VIRT_BASE      0xff900000
35 #define TS78XX_FPGA_REGS_SIZE           SZ_1M
36
37 static struct ts78xx_fpga_data ts78xx_fpga = {
38         .id             = 0,
39         .state          = 1,
40 /*      .supports       = ... - populated by ts78xx_fpga_supports() */
41 };
42
43 /*****************************************************************************
44  * I/O Address Mapping
45  ****************************************************************************/
46 static struct map_desc ts78xx_io_desc[] __initdata = {
47         {
48                 .virtual        = TS78XX_FPGA_REGS_VIRT_BASE,
49                 .pfn            = __phys_to_pfn(TS78XX_FPGA_REGS_PHYS_BASE),
50                 .length         = TS78XX_FPGA_REGS_SIZE,
51                 .type           = MT_DEVICE,
52         },
53 };
54
55 void __init ts78xx_map_io(void)
56 {
57         orion5x_map_io();
58         iotable_init(ts78xx_io_desc, ARRAY_SIZE(ts78xx_io_desc));
59 }
60
61 /*****************************************************************************
62  * Ethernet
63  ****************************************************************************/
64 static struct mv643xx_eth_platform_data ts78xx_eth_data = {
65         .phy_addr       = MV643XX_ETH_PHY_ADDR(0),
66 };
67
68 /*****************************************************************************
69  * SATA
70  ****************************************************************************/
71 static struct mv_sata_platform_data ts78xx_sata_data = {
72         .n_ports        = 2,
73 };
74
75 /*****************************************************************************
76  * RTC M48T86 - nicked^Wborrowed from arch/arm/mach-ep93xx/ts72xx.c
77  ****************************************************************************/
78 #ifdef CONFIG_RTC_DRV_M48T86
79 #define TS_RTC_CTRL     (TS78XX_FPGA_REGS_VIRT_BASE | 0x808)
80 #define TS_RTC_DATA     (TS78XX_FPGA_REGS_VIRT_BASE | 0x80c)
81
82 static unsigned char ts78xx_ts_rtc_readbyte(unsigned long addr)
83 {
84         writeb(addr, TS_RTC_CTRL);
85         return readb(TS_RTC_DATA);
86 }
87
88 static void ts78xx_ts_rtc_writebyte(unsigned char value, unsigned long addr)
89 {
90         writeb(addr, TS_RTC_CTRL);
91         writeb(value, TS_RTC_DATA);
92 }
93
94 static struct m48t86_ops ts78xx_ts_rtc_ops = {
95         .readbyte       = ts78xx_ts_rtc_readbyte,
96         .writebyte      = ts78xx_ts_rtc_writebyte,
97 };
98
99 static struct platform_device ts78xx_ts_rtc_device = {
100         .name           = "rtc-m48t86",
101         .id             = -1,
102         .dev            = {
103                 .platform_data  = &ts78xx_ts_rtc_ops,
104         },
105         .num_resources  = 0,
106 };
107
108 /*
109  * TS uses some of the user storage space on the RTC chip so see if it is
110  * present; as it's an optional feature at purchase time and not all boards
111  * will have it present
112  *
113  * I've used the method TS use in their rtc7800.c example for the detection
114  *
115  * TODO: track down a guinea pig without an RTC to see if we can work out a
116  *              better RTC detection routine
117  */
118 static int ts78xx_ts_rtc_load(void)
119 {
120         int rc;
121         unsigned char tmp_rtc0, tmp_rtc1;
122
123         tmp_rtc0 = ts78xx_ts_rtc_readbyte(126);
124         tmp_rtc1 = ts78xx_ts_rtc_readbyte(127);
125
126         ts78xx_ts_rtc_writebyte(0x00, 126);
127         ts78xx_ts_rtc_writebyte(0x55, 127);
128         if (ts78xx_ts_rtc_readbyte(127) == 0x55) {
129                 ts78xx_ts_rtc_writebyte(0xaa, 127);
130                 if (ts78xx_ts_rtc_readbyte(127) == 0xaa
131                                 && ts78xx_ts_rtc_readbyte(126) == 0x00) {
132                         ts78xx_ts_rtc_writebyte(tmp_rtc0, 126);
133                         ts78xx_ts_rtc_writebyte(tmp_rtc1, 127);
134
135                         if (ts78xx_fpga.supports.ts_rtc.init == 0) {
136                                 rc = platform_device_register(&ts78xx_ts_rtc_device);
137                                 if (!rc)
138                                         ts78xx_fpga.supports.ts_rtc.init = 1;
139                         } else
140                                 rc = platform_device_add(&ts78xx_ts_rtc_device);
141
142                         return rc;
143                 }
144         }
145
146         return -ENODEV;
147 };
148
149 static void ts78xx_ts_rtc_unload(void)
150 {
151         platform_device_del(&ts78xx_ts_rtc_device);
152 }
153 #else
154 static int ts78xx_ts_rtc_load(void)
155 {
156         return -ENODEV;
157 }
158
159 static void ts78xx_ts_rtc_unload(void)
160 {
161 }
162 #endif
163
164 /*****************************************************************************
165  * FPGA 'hotplug' support code
166  ****************************************************************************/
167 static void ts78xx_fpga_devices_zero_init(void)
168 {
169         ts78xx_fpga.supports.ts_rtc.init = 0;
170 }
171
172 static void ts78xx_fpga_supports(void)
173 {
174         /* TODO: put this 'table' into ts78xx-fpga.h */
175         switch (ts78xx_fpga.id) {
176         case TS7800_REV_B:
177                 ts78xx_fpga.supports.ts_rtc.present = 1;
178                 break;
179         default:
180                 ts78xx_fpga.supports.ts_rtc.present = 0;
181         }
182 }
183
184 static int ts78xx_fpga_load_devices(void)
185 {
186         int tmp, ret = 0;
187
188         if (ts78xx_fpga.supports.ts_rtc.present == 1) {
189                 tmp = ts78xx_ts_rtc_load();
190                 if (tmp) {
191                         printk(KERN_INFO "TS-78xx RTC"
192                                         " not detected or enabled\n");
193                         ts78xx_fpga.supports.ts_rtc.present = 0;
194                 }
195                 ret |= tmp;
196         }
197
198         return ret;
199 }
200
201 static int ts78xx_fpga_unload_devices(void)
202 {
203         int ret = 0;
204
205         if (ts78xx_fpga.supports.ts_rtc.present == 1)
206                 ts78xx_ts_rtc_unload();
207
208         return ret;
209 }
210
211 static int ts78xx_fpga_load(void)
212 {
213         ts78xx_fpga.id = readl(TS78XX_FPGA_REGS_VIRT_BASE);
214
215         printk(KERN_INFO "TS-78xx FPGA: magic=0x%.6x, rev=0x%.2x\n",
216                         (ts78xx_fpga.id >> 8) & 0xffffff,
217                         ts78xx_fpga.id & 0xff);
218
219         ts78xx_fpga_supports();
220
221         if (ts78xx_fpga_load_devices()) {
222                 ts78xx_fpga.state = -1;
223                 return -EBUSY;
224         }
225
226         return 0;
227 };
228
229 static int ts78xx_fpga_unload(void)
230 {
231         unsigned int fpga_id;
232
233         fpga_id = readl(TS78XX_FPGA_REGS_VIRT_BASE);
234
235         /*
236          * There does not seem to be a feasible way to block access to the GPIO
237          * pins from userspace (/dev/mem).  This if clause should hopefully warn
238          * those foolish enough not to follow 'policy' :)
239          *
240          * UrJTAG SVN since r1381 can be used to reprogram the FPGA
241          */
242         if (ts78xx_fpga.id != fpga_id) {
243                 printk(KERN_ERR "TS-78xx FPGA: magic/rev mismatch\n"
244                         "TS-78xx FPGA: was 0x%.6x/%.2x but now 0x%.6x/%.2x\n",
245                         (ts78xx_fpga.id >> 8) & 0xffffff, ts78xx_fpga.id & 0xff,
246                         (fpga_id >> 8) & 0xffffff, fpga_id & 0xff);
247                 ts78xx_fpga.state = -1;
248                 return -EBUSY;
249         }
250
251         if (ts78xx_fpga_unload_devices()) {
252                 ts78xx_fpga.state = -1;
253                 return -EBUSY;
254         }
255
256         return 0;
257 };
258
259 static ssize_t ts78xx_fpga_show(struct kobject *kobj,
260                         struct kobj_attribute *attr, char *buf)
261 {
262         if (ts78xx_fpga.state < 0)
263                 return sprintf(buf, "borked\n");
264
265         return sprintf(buf, "%s\n", (ts78xx_fpga.state) ? "online" : "offline");
266 }
267
268 static ssize_t ts78xx_fpga_store(struct kobject *kobj,
269                         struct kobj_attribute *attr, const char *buf, size_t n)
270 {
271         int value, ret;
272
273         if (ts78xx_fpga.state < 0) {
274                 printk(KERN_ERR "TS-78xx FPGA: borked, you must powercycle asap\n");
275                 return -EBUSY;
276         }
277
278         if (strncmp(buf, "online", sizeof("online") - 1) == 0)
279                 value = 1;
280         else if (strncmp(buf, "offline", sizeof("offline") - 1) == 0)
281                 value = 0;
282         else {
283                 printk(KERN_ERR "ts78xx_fpga_store: Invalid value\n");
284                 return -EINVAL;
285         }
286
287         if (ts78xx_fpga.state == value)
288                 return n;
289
290         ret = (ts78xx_fpga.state == 0)
291                 ? ts78xx_fpga_load()
292                 : ts78xx_fpga_unload();
293
294         if (!(ret < 0))
295                 ts78xx_fpga.state = value;
296
297         return n;
298 }
299
300 static struct kobj_attribute ts78xx_fpga_attr =
301         __ATTR(ts78xx_fpga, 0644, ts78xx_fpga_show, ts78xx_fpga_store);
302
303 /*****************************************************************************
304  * General Setup
305  ****************************************************************************/
306 static struct orion5x_mpp_mode ts78xx_mpp_modes[] __initdata = {
307         {  0, MPP_UNUSED },
308         {  1, MPP_GPIO },               /* JTAG Clock */
309         {  2, MPP_GPIO },               /* JTAG Data In */
310         {  3, MPP_GPIO },               /* Lat ECP2 256 FPGA - PB2B */
311         {  4, MPP_GPIO },               /* JTAG Data Out */
312         {  5, MPP_GPIO },               /* JTAG TMS */
313         {  6, MPP_GPIO },               /* Lat ECP2 256 FPGA - PB31A_CLK4+ */
314         {  7, MPP_GPIO },               /* Lat ECP2 256 FPGA - PB22B */
315         {  8, MPP_UNUSED },
316         {  9, MPP_UNUSED },
317         { 10, MPP_UNUSED },
318         { 11, MPP_UNUSED },
319         { 12, MPP_UNUSED },
320         { 13, MPP_UNUSED },
321         { 14, MPP_UNUSED },
322         { 15, MPP_UNUSED },
323         { 16, MPP_UART },
324         { 17, MPP_UART },
325         { 18, MPP_UART },
326         { 19, MPP_UART },
327         /*
328          * MPP[20] PCI Clock Out 1
329          * MPP[21] PCI Clock Out 0
330          * MPP[22] Unused
331          * MPP[23] Unused
332          * MPP[24] Unused
333          * MPP[25] Unused
334          */
335         { -1 },
336 };
337
338 static void __init ts78xx_init(void)
339 {
340         int ret;
341
342         /*
343          * Setup basic Orion functions. Need to be called early.
344          */
345         orion5x_init();
346
347         orion5x_mpp_conf(ts78xx_mpp_modes);
348
349         /*
350          * Configure peripherals.
351          */
352         orion5x_ehci0_init();
353         orion5x_ehci1_init();
354         orion5x_eth_init(&ts78xx_eth_data);
355         orion5x_sata_init(&ts78xx_sata_data);
356         orion5x_uart0_init();
357         orion5x_uart1_init();
358         orion5x_xor_init();
359
360         /* FPGA init */
361         ts78xx_fpga_devices_zero_init();
362         ret = ts78xx_fpga_load();
363         ret = sysfs_create_file(power_kobj, &ts78xx_fpga_attr.attr);
364         if (ret)
365                 printk(KERN_ERR "sysfs_create_file failed: %d\n", ret);
366 }
367
368 MACHINE_START(TS78XX, "Technologic Systems TS-78xx SBC")
369         /* Maintainer: Alexander Clouter <alex@digriz.org.uk> */
370         .phys_io        = ORION5X_REGS_PHYS_BASE,
371         .io_pg_offst    = ((ORION5X_REGS_VIRT_BASE) >> 18) & 0xFFFC,
372         .boot_params    = 0x00000100,
373         .init_machine   = ts78xx_init,
374         .map_io         = ts78xx_map_io,
375         .init_irq       = orion5x_init_irq,
376         .timer          = &orion5x_timer,
377 MACHINE_END