summaryrefslogtreecommitdiff
path: root/common/hw_recovery.c
blob: 01184c87b8f0d9c501956a3531a4d15bbdbdf645 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
/*++
Copyright (c) 2010 WonderMedia Technologies, Inc.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.  See the GNU General Public License for more details. You
should have received a copy of the GNU General Public License along with this
program. If not, see http://www.gnu.org/licenses/>.

WonderMedia Technologies, Inc.
cheney chen mce Shenzhen china

Modifed by howayhuo to use gpio lib
--*/

#include <common.h>
#include <linux/mtd/nand.h>
#include <mmc.h>
#include <part.h>

#include "../board/wmt/include/wmt_pmc.h"
#include "../board/wmt/include/wmt_iomux.h"
#include "../board/wmt/include/common_def.h"

/*
* Format:
*     setenv wmt.hardware.recovery enable:gpiono:active:delay
* Example:
*     Press PowerKey and GPIO8(volume+) key in the same time to force enter fastboot
*     The GPIO8 is low level when pressing the volume+ key
*     Long press power key for 1 second to conform enter recovery
*
* setenv wmt.recovery.key 1:8:0:10
*/
#define ENV_RECOVERY_KEY "wmt.recovery.key"

#define ENV_RECOVERY_NAME "wmt.recovery.enable"

#define TIMEOUT_SECONDS4 40    //100ms * 40
#define TIMEOUT_SECONDS10 100    //100ms * 100
//#define HW_RECOVERY_DEBUG

#define ENV_MISC_PART "misc-TF_part"
#define ENV_MISC_OFFSET "misc-NAND_ofs"
#define WMT_BOOT_DEV "wmt.boot.dev"

struct recovery_key_env_t {
    int enable;   // 1: enable the function of pressing key to do recovery. 0: don't enable
    int gpiono;   // the gpio no of gpiokey
    int active;   // 1: the gpio level is 1 when the gpiokey is pressed. 0: the gpio level is 0 when the gpiokey is pressed
    int delay;    // how long time to press power key to confirm enter recovery: (delay x 100) ms
};

static struct recovery_key_env_t default_recovery_key = {
    //power and volume+ in default
    .enable   = 1,
    .gpiono   = 8,
    .active = 0,
    .delay    = 10,   // long press power key for 1 second to conform
};

struct bootloader_message {
    char command[32];
    char status[32];
    char recovery[1024];
};

extern int WMTSaveImageToNAND(struct nand_chip *nand, unsigned long long naddr, unsigned int dwImageStart,
        unsigned int dwImageLength, int oob_offs, unsigned int need_erase);
struct nand_chip* get_current_nand_chip(void);


static const int NAND_MISC_COMMAND_PAGE = 1;  // bootloader command is this page

static int nand_set_bootloader_message(const struct bootloader_message *in){
    char * ofsStr;
    unsigned long miscOff;
    struct nand_chip * nand = NULL;
    unsigned long nandOff;

    if(in == NULL){
        printf("invalid argument\n");
        return -1;
    }

    if((ofsStr = getenv(ENV_MISC_OFFSET)) == NULL){
        printf("%s is undefined.\n", ENV_MISC_OFFSET);
        return -1;
    }

    miscOff = simple_strtoul(ofsStr, NULL, 16);

    if(miscOff == 0){
        printf("Invalid misc offset %x(%s)\n", miscOff, ofsStr);
        return -1;
    }

    nand = get_current_nand_chip();
    if(nand == NULL){
        printf("Current nand chip is NULL\n");
        return -1;
    }

    nandOff = miscOff + NAND_MISC_COMMAND_PAGE * nand->dwPageSize;

    printf("set bootloader message , nand off = 0x%x(%x, %x), memory off = %p, size = 0x%x\n",
            nandOff, miscOff, nand->dwPageSize, in, sizeof(struct bootloader_message));

    return WMTSaveImageToNAND(nand, nandOff, (unsigned int)in, sizeof(struct bootloader_message), 0, 1);
}

extern block_dev_desc_t *get_dev (char*, int);

static  unsigned long mmc_part_offset =0;

int mmc_register_device_recovery(block_dev_desc_t *dev_desc, int part_no)
{
    unsigned char buffer[0x200];
    disk_partition_t info;

    if (!dev_desc->block_read)
        return -1;

    /* check if we have a MBR (on floppies we have only a PBR) */
    if (dev_desc->block_read (dev_desc->dev, 0, 1, (ulong *) buffer) != 1) {
        printf ("** Can't read from device %d **\n", dev_desc->dev);
        return -1;
    }

    if(!get_partition_info(dev_desc, part_no, &info)) {
            mmc_part_offset = info.start;
            printf("part_offset : %x, cur_part : %x\n", mmc_part_offset, part_no);
    } else {
#if 1
        printf ("** Partition %d not valid on device %d **\n",part_no,dev_desc->dev);
        return -1;
#else

        /* FIXME we need to determine the start block of the
         * partition where the boot.img partition resides. This can be done
         * by using the get_partition_info routine. For this
         * purpose the libpart must be included.
         */
        part_offset=32;
        cur_part = 1;
#endif
    }
    return 0;
}

static int mmc_set_bootloader_message(const struct bootloader_message *in){
    char *partStr;
    unsigned long partNo=0;
    unsigned long deviceId;
    unsigned long writeSize, writeCnt;
    block_dev_desc_t *devDesc=NULL;
    char *ep;
    int ret;

    if((partStr = getenv(ENV_MISC_PART)) == NULL){
        printf("%s is undefined.\n", ENV_MISC_PART);
        return -1;
    }

    deviceId = simple_strtoul(partStr, &ep, 16);

    if (deviceId < 0 || deviceId > 3){
        printf("Invalid device Id %d\n", deviceId);
        return -1;
    }

    /*get mmc dev descriptor*/
    devDesc=get_dev("mmc", deviceId);
    if (devDesc == NULL) {
        printf("\n** Invalid boot device **\n");
        return 1;
    }

    if (*ep) {
        if (*ep != ':') {
            puts ("\n** Invalid boot device, use `dev[:part]' **\n");
            return -1;
        }
        partNo = (int)simple_strtoul(++ep, NULL, 16);
    }

    /* init mmc controller */
    if (mmc_init(1, deviceId)) {
        printf("mmc init failed?\n");
        return -1;
    }
    if (mmc_register_device_recovery(devDesc, partNo) != 0) {
        printf ("\n** Unable to use mmc %d:%d for fatload **\n", deviceId, partNo);
        return -1;
    }

    writeSize = sizeof(struct bootloader_message);
    writeCnt = writeSize/512 + (writeSize % 512)?1:0;

    ret = mmc_bwrite(deviceId, mmc_part_offset, writeCnt, (ulong*)in);
    printf("set bootloader message, deviceId = %d, partId = %d, part_off=0x%x, write block cnt=%d, memory off = %p, ret = %d\n",
           deviceId, partNo, mmc_part_offset, writeCnt, in, ret);

    return ret;
}


static int set_bootloader_message(const struct bootloader_message *in){
    char *bootDev;
    if((bootDev = getenv(WMT_BOOT_DEV)) == NULL){
         printf("%s is undefined.\n", WMT_BOOT_DEV);
         return -1;
    }

    if(strcmp(bootDev, "NAND") == 0){
        return nand_set_bootloader_message(in);
    }else if(strcmp(bootDev, "TF") == 0){
       return mmc_set_bootloader_message(in);
    }

    printf("boot %s is not supported.\n", bootDev);
    return -1;
}


static void boot_into_recovery(void){
    struct bootloader_message msg;

    memset(&msg, 0xff, sizeof(struct bootloader_message));
    strcpy(msg.command, "boot-recovery");
    strcpy(msg.recovery, "recovery\n--wipe_data\n--locale=en_US\n--execute_script=/system/.restore/restore.sh");


    if(set_bootloader_message(&msg) >=0 ){
        run_command("textout 10 65 \"Boot into system recovery mode... \" 0xff0000;", 0);
        run_command("run boot-nand-ota-recovery", 0);
        run_command("run boot-kernel", 0);
    }
}

static int parse_recovery_key_env(char *name, struct recovery_key_env_t *p_env)
{
    enum
    {
        idx_enable,
        idx_gpiono,
        idx_active,
        idx_delay,
        idx_max
    };

    char *p;
    long ps[idx_max] = {0};
    char *endp;
    int i = 0;

    p = getenv(name);
    if (!p)
        return -1;

    while (i < idx_max) {
        ps[i++] = simple_strtoul(p, &endp, 0);

        if (*endp == '\0')
            break;
            p = endp + 1;

            if (*p == '\0')
            break;
    }

    p_env->enable = ps[0];
    p_env->gpiono = ps[1];
    p_env->active = ps[2];
    p_env->delay = ps[3];

    if(i != 4) {
        printf("parse_recovery_key_env: wrong env num in %s\n", name);
        return -1;
    }

    if(p_env->enable == 0)
    return 0;

    if(gpio_is_valid(p_env->gpiono))
        return 0;
    else {
        printf("parse_recovery_key_env: wrong gpio no in %s\n", name);
        return -1;
    }
}

int hw_recovery(void)
{
    int i;
    char *s;
    int timeout = 0;
    int power_btn_press = 0;
    int gpio_btn_press = 0;
    int times = 0;
    int ret = 0;
    struct recovery_key_env_t recovery_key;

    //decrease the delay time for system boot
    if((s = getenv(ENV_RECOVERY_NAME)) != NULL && !strcmp(s, "0"))
    {
        printf("No recovery.\n");
        return ret;
    }

    ret = parse_recovery_key_env(ENV_RECOVERY_KEY, &recovery_key);
    if(ret)
        memcpy(&recovery_key, &default_recovery_key, sizeof(struct recovery_key_env_t));

    if(recovery_key.enable == 0)
        return -1;

    if(!(PMPB_VAL & BIT24))
            return -1;

    if(recovery_key.active != 0)
        recovery_key.active = 1;

    if(recovery_key.delay > 30) {
        recovery_key.delay = 30;
        printf("limit recovery_key.delay to 30\n");
    }

    gpio_direction_input(recovery_key.gpiono);
    gpio_setpull(recovery_key.gpiono, recovery_key.active? GPIO_PULL_DOWN : GPIO_PULL_UP);

    //commbined key, power button and vol+ in default
    power_btn_press = PMPB_VAL & BIT24;
    gpio_btn_press = (gpio_get_value(recovery_key.gpiono) == recovery_key.active);

    if(!power_btn_press || !gpio_btn_press)
        return -1;

    //check press commbined key for 1 seconds
    for(i = 0; i < 10; i++) {
        if(i != 0)
            udelay(100000);

        power_btn_press = PMPB_VAL & BIT24;
        if(!power_btn_press)
            return -1;

        gpio_btn_press = (gpio_get_value(recovery_key.gpiono) == recovery_key.active);
        if(!gpio_btn_press)
            return -1;
        }

    timeout = 0;
    times = 0;

    run_command("textout 10 5 \"Are you sure system recovery?\" 0xff0000", 0);
    run_command("textout 10 25 \"Long press power button again to enter system recovery.\" 0xff0000", 0);

    while(timeout < TIMEOUT_SECONDS10) {
        power_btn_press = PMPB_VAL & BIT24;
        gpio_btn_press = (gpio_get_value(recovery_key.gpiono) == recovery_key.active);

        timeout++;

#ifdef HW_RECOVERY_DEBUG
        printf("times = %d, timeout = %d\n", times, timeout);
#endif
        //for user's a mistake
        if(timeout >= TIMEOUT_SECONDS10) {
            //clean the text
            run_command("textout 10 5 \" \" 0xff0000", 0);
            run_command("textout 10 25 \" \" 0xff0000", 0);
        }

        if(gpio_btn_press) {
            udelay(100000);
            continue;
        }

        if(power_btn_press)
            times++;

        if(times >= recovery_key.delay) { // 100ms * recovery_key.delay = xx seconds
            run_command("textout 10 45 \"Recovery confirmed.\" 0xffff00;", 0);
            boot_into_recovery();
            return 0;
        }
        udelay(100000);
    }

    return 0;
}