6e657ccf143854b749164055291e7b493a4e2d74
[kernel/u-boot.git] / recovery / usbd.c
1 /*
2  * USB Downloader for SAMSUNG Platform
3  *
4  * Copyright (C) 2007-2010 Samsung Electronics
5  * Minkyu Kang <mk7.kang@samsung.com>
6  *
7  */
8
9 #include <common.h>
10 #include "recovery.h"
11 #include "usbd.h"
12 #include "onenand.h"
13
14 #ifdef RECOVERY_DEBUG
15 #define PUTS(s) serial_puts(DEBUG_MARK"usb: "s)
16 #else
17 #define PUTS(s)
18 #endif
19
20 static struct usbd_ops usbd_ops;
21 static unsigned long down_ram_addr;
22
23 int update_boot_image(void)
24 {
25         struct onenand_op *onenand_ops = onenand_get_interface();
26         ulong len, offset;
27
28 #if 1
29         /* case: IPL, Recovery, u-boot are one file */
30         offset = 0;
31         len = CONFIG_RECOVERY_SIZE + CONFIG_RECOVERY_ADDR;
32 #else
33         /* case: IPL, Recover are one file and u-boot is another */
34         offset = CONFIG_RECOVERY_ADDR;
35         len = CONFIG_RECOVERY_SIZE;
36 #endif
37         /* Erase */
38         onenand_ops->erase(offset, len, 0);
39         /* Write */
40         onenand_ops->write(offset, len, NULL, (u_char *)down_ram_addr);
41
42         return 0;
43 }
44
45 /* Parsing received data packet and Process data */
46 static int process_data(struct usbd_ops *usbd)
47 {
48         ulong cmd = 0, arg = 0, len = 0, flag = 0;
49         int recvlen = 0;
50         int ret = 0;
51         int img_type;
52
53         /* Parse command */
54         cmd  = *((ulong *) usbd->rx_data + 0);
55         arg  = *((ulong *) usbd->rx_data + 1);
56         len  = *((ulong *) usbd->rx_data + 2);
57         flag = *((ulong *) usbd->rx_data + 3);
58
59         /* Reset tx buffer */
60         *((ulong *) usbd->tx_data) = 0;
61
62         switch (cmd) {
63         case COMMAND_DOWNLOAD_IMAGE:
64                 PUTS("COMMAND_DOWNLOAD_IMAGE\n");
65                 usbd->recv_setup((char *)down_ram_addr, (int)len);
66
67                 /* response */
68                 usbd->send_data(usbd->tx_data, usbd->tx_len);
69
70                 /* Receive image */
71                 recvlen = usbd->recv_data();
72
73                 /* Retry this commad */
74                 if (recvlen < len) {
75                         PUTS("Error: wrong image size\n");
76                         *((ulong *) usbd->tx_data) = STATUS_RETRY;
77                 } else
78                         *((ulong *) usbd->tx_data) = STATUS_DONE;
79
80                 usbd->send_data(usbd->tx_data, usbd->tx_len);
81                 return 1;
82
83         case COMMAND_PARTITION_SYNC:
84                 *((ulong *) usbd->tx_data) = CONFIG_RECOVERY_BOOT_BLOCKS - 1;
85                 usbd->send_data(usbd->tx_data, usbd->tx_len);
86                 return 1;
87
88         case COMMAND_WRITE_PART_1:
89                 PUTS("COMMAND_WRITE_PART_BOOT\n");
90                 img_type = IMG_BOOT;
91                 break;
92
93         /* Download complete -> reset */
94         case COMMAND_RESET_PDA:
95                 PUTS("Download finished and Auto reset!\n");
96                 PUTS("Wait........\n");
97                 /* Stop USB */
98                 usbd->usb_stop();
99
100                 if (usbd->cpu_reset)
101                         usbd->cpu_reset();
102
103                 return 0;
104
105         /* Error */
106         case COMMAND_RESET_USB:
107                 PUTS("Error is occured!(maybe previous step)->\
108                                 Turn off and restart!\n");
109                 /* Stop USB */
110                 usbd->usb_stop();
111                 return 0;
112
113         default:
114                 return 1;
115         }
116
117         /* Erase and Write to NAND */
118         switch (img_type) {
119         case IMG_BOOT:
120                 update_boot_image();
121                 break;
122
123         default:
124                 /* Retry? */
125                 break;
126         }
127
128         if (ret) {
129                 /* Retry this commad */
130                 *((ulong *) usbd->tx_data) = STATUS_RETRY;
131                 usbd->send_data(usbd->tx_data, usbd->tx_len);
132                 return 1;
133         } else
134                 *((ulong *) usbd->tx_data) = STATUS_DONE;
135
136         /* Write image success -> Report status */
137         usbd->send_data(usbd->tx_data, usbd->tx_len);
138
139         return 1;
140 }
141
142 int do_usbd_down(void)
143 {
144         struct usbd_ops *usbd;
145
146         PUTS("USB Downloader\n");
147         /* interface setting */
148         usbd = usbd_set_interface(&usbd_ops);
149         down_ram_addr = usbd->ram_addr;
150
151         /* init the usb controller */
152         usbd->usb_init();
153
154         /* receive setting */
155         usbd->recv_setup(usbd->rx_data, usbd->rx_len);
156
157         /* detect the download request from Host PC */
158         if (usbd->recv_data())
159                 usbd->send_data(usbd->tx_data, usbd->tx_len);
160         else
161                 return 0;
162
163         PUTS("Receive the packet\n");
164
165         /* receive the data from Host PC */
166         while (1) {
167                 usbd->recv_setup(usbd->rx_data, usbd->rx_len);
168
169                 if (usbd->recv_data()) {
170                         if (process_data(usbd) == 0)
171                                 return 0;
172                 }
173         }
174
175         return 0;
176 }