6041202a9d95967028f390df7cd3209b66e118f6
[kernel/u-boot.git] / recovery / board / samsung / universal / universal.c
1 /*
2  * Copyright (C) 2009-2010 Samsung Electronics
3  */
4
5 #include <common.h>
6 #include <linux/mtd/mtd.h>
7 #include <linux/mtd/onenand.h>
8 #include <asm/io.h>
9 #include <asm/arch/gpio.h>
10 #include <asm/arch/keypad.h>
11 #include "recovery.h"
12 #include "onenand.h"
13
14 #ifdef RECOVERY_DEBUG
15 #define PUTS(s) serial_puts(DEBUG_MARK""s)
16 #else
17 #define PUTS(s)
18 #endif
19
20 typedef int (init_fnc_t) (void);
21
22 DECLARE_GLOBAL_DATA_PTR;
23
24 static struct s5pc110_gpio *gpio_base =
25         (struct s5pc110_gpio *) S5PC110_GPIO_BASE;
26
27 static void sdelay(unsigned long usec)
28 {
29         ulong kv;
30
31         do {
32                 kv = 0x100000;
33                 while (kv)
34                         kv--;
35                 usec--;
36         } while (usec);
37 }
38
39 static int check_keypad(void)
40 {
41         unsigned int condition = 0;
42         unsigned int reg, value;
43         unsigned int col_num, row_num;
44         unsigned int col_mask;
45         unsigned int col_mask_shift;
46         unsigned int row_state[4] = {0, 0, 0, 0};
47         unsigned int i;
48
49         /* board is limo universal */
50
51         row_num = 2;
52         col_num = 3;
53
54         for (i = 0; i < sizeof(row_state)/sizeof(int); i++)
55                 row_state[i] = 0;
56
57         for (i = 0; i < row_num; i++) {
58                 /* Set GPH3[3:0] to KP_ROW[3:0] */
59                 gpio_cfg_pin(&gpio_base->gpio_h3, i, 0x3);
60                 gpio_set_pull(&gpio_base->gpio_h3, i, GPIO_PULL_UP);
61         }
62
63         for (i = 0; i < col_num; i++)
64                 /* Set GPH2[3:0] to KP_COL[3:0] */
65                 gpio_cfg_pin(&gpio_base->gpio_h2, i, 0x3);
66
67         reg = S5PC110_KEYPAD_BASE;
68         col_mask = S5PC110_KEYIFCOLEN_MASK;
69         col_mask_shift = 8;
70
71         /* KEYIFCOL reg clear */
72         writel(0, reg + S5PC1XX_KEYIFCOL_OFFSET);
73
74         /* key_scan */
75         for (i = 0; i < col_num; i++) {
76                 value = col_mask;
77                 value &= ~(1 << i) << col_mask_shift;
78
79                 writel(value, reg + S5PC1XX_KEYIFCOL_OFFSET);
80                 sdelay(1000);
81
82                 value = readl(reg + S5PC1XX_KEYIFROW_OFFSET);
83                 row_state[i] = ~value & ((1 << row_num) - 1);
84         }
85
86         /* KEYIFCOL reg clear */
87         writel(0, reg + S5PC1XX_KEYIFCOL_OFFSET);
88
89         /* check volume up/down */
90         if ((row_state[1] & 0x3) == 0x3)
91                 condition = 1;
92
93         return condition;
94 }
95
96 static int check_block(void)
97 {
98         struct onenand_op *onenand_ops = onenand_get_interface();
99         struct mtd_info *mtd = onenand_ops->mtd;
100         struct onenand_chip *this = mtd->priv;
101         int i;
102         int page_to_check = 4;
103         int ret, retlen = 0;
104         ulong blocksize = 1 << this->erase_shift;
105         ulong pagesize = 1 << this->page_shift;
106         u_char *down_ram_addr;
107         ulong uboot_addr;
108         u_char verify_buf[0x10];
109
110         down_ram_addr = (unsigned char *)CONFIG_SYS_DOWN_ADDR;
111         uboot_addr = CONFIG_RECOVERY_UBOOT_BLOCK << this->erase_shift;
112
113         onenand_ops->read(uboot_addr, blocksize, &retlen, down_ram_addr, 0);
114         if (retlen != blocksize)
115                 return 1;
116
117         memset(verify_buf, 0xFF, sizeof(verify_buf));
118
119         for (i = 0; i < page_to_check; i++) {
120                 ret = memcmp(down_ram_addr + pagesize*i, verify_buf,
121                                 sizeof(verify_buf));
122                 if (ret)
123                         break;
124         }
125
126         if (i == page_to_check)
127                 return 1;
128
129         return 0;
130 }
131
132 int board_check_condition(void)
133 {
134         if (check_keypad()) {
135                 PUTS("manual mode\n");
136                 return 1;
137         }
138
139         if (check_block()) {
140                 PUTS("bootloader image broken\n");
141                 return 1;
142         }
143
144         return 0;
145 }
146
147 int board_load_uboot(unsigned char *buf)
148 {
149         struct mtd_info *mtd = &onenand_mtd;
150         struct onenand_chip *this = mtd->priv;
151         int offset;
152         size_t size;
153         size_t ret;
154
155         offset = CONFIG_RECOVERY_UBOOT_BLOCK << this->erase_shift;
156         size = CONFIG_SYS_MONITOR_LEN;
157
158         mtd->read(mtd, offset, size, &ret, buf);
159
160         if (size != ret)
161                 return -1;
162
163         return 0;
164 }
165
166 void board_recovery_init(void)
167 {
168         /* set GPIO to enable UART2 */
169         gpio_cfg_pin(&gpio_base->gpio_a1, 0, 0x2);
170         gpio_cfg_pin(&gpio_base->gpio_a1, 1, 0x2);
171
172         /* UART_SEL MP0_5[7] at S5PC110 */
173         gpio_direction_output(&gpio_base->gpio_mp0_5, 7, 0x1);
174         gpio_set_pull(&gpio_base->gpio_mp0_5, 7, GPIO_PULL_DOWN);
175 }