Commit | Line | Data |
---|---|---|
247e9cff SA |
1 | /* |
2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | |
3 | * | |
4 | * This software is licensed under the terms of the GNU General Public | |
5 | * License version 2, as published by the Free Software Foundation, and | |
6 | * may be copied, distributed, and modified under those terms. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | */ | |
13 | ||
14 | #include <linux/module.h> | |
247e9cff | 15 | #include <linux/kernel.h> |
247e9cff SA |
16 | #include <linux/mm.h> |
17 | #include <linux/uaccess.h> | |
18 | #include <linux/fs.h> | |
19 | #include <linux/sched.h> | |
20 | #include <linux/slab.h> | |
21 | ||
22 | #include <linux/mmc/core.h> | |
23 | #include <linux/mmc/card.h> | |
24 | #include <linux/mmc/sdio_func.h> | |
25 | ||
9e412a0a ML |
26 | #include <linux/firmware.h> |
27 | ||
247e9cff | 28 | #include "gdm_sdio.h" |
a6000538 | 29 | #include "sdio_boot.h" |
247e9cff SA |
30 | |
31 | #define TYPE_A_HEADER_SIZE 4 | |
32 | #define TYPE_A_LOOKAHEAD_SIZE 16 | |
9e412a0a | 33 | #define YMEM0_SIZE 0x8000 /* 32kbytes */ |
247e9cff SA |
34 | #define DOWNLOAD_SIZE (YMEM0_SIZE - TYPE_A_HEADER_SIZE) |
35 | ||
9e412a0a ML |
36 | #define FW_DIR "gdm72xx/" |
37 | #define FW_KRN "gdmskrn.bin" | |
38 | #define FW_RFS "gdmsrfs.bin" | |
247e9cff SA |
39 | |
40 | static u8 *tx_buf; | |
41 | ||
42 | static int ack_ready(struct sdio_func *func) | |
43 | { | |
917ab47f | 44 | unsigned long wait = jiffies + HZ; |
247e9cff SA |
45 | u8 val; |
46 | int ret; | |
47 | ||
917ab47f | 48 | while (time_before(jiffies, wait)) { |
247e9cff SA |
49 | val = sdio_readb(func, 0x13, &ret); |
50 | if (val & 0x01) | |
51 | return 1; | |
52 | schedule(); | |
53 | } | |
54 | ||
55 | return 0; | |
56 | } | |
57 | ||
9e412a0a | 58 | static int download_image(struct sdio_func *func, const char *img_name) |
247e9cff | 59 | { |
9e412a0a | 60 | int ret = 0, len, pno; |
247e9cff SA |
61 | u8 *buf = tx_buf; |
62 | loff_t pos = 0; | |
9e412a0a ML |
63 | int img_len; |
64 | const struct firmware *firm; | |
65 | ||
66 | ret = request_firmware(&firm, img_name, &func->dev); | |
67 | if (ret < 0) { | |
626e557f YT |
68 | dev_err(&func->dev, |
69 | "requesting firmware %s failed with error %d\n", | |
9e412a0a ML |
70 | img_name, ret); |
71 | return ret; | |
247e9cff SA |
72 | } |
73 | ||
9e412a0a | 74 | buf = kmalloc(DOWNLOAD_SIZE + TYPE_A_HEADER_SIZE, GFP_KERNEL); |
78110bb8 | 75 | if (buf == NULL) |
9e412a0a | 76 | return -ENOMEM; |
247e9cff | 77 | |
9e412a0a ML |
78 | img_len = firm->size; |
79 | ||
80 | if (img_len <= 0) { | |
81 | ret = -1; | |
247e9cff SA |
82 | goto out; |
83 | } | |
84 | ||
85 | pno = 0; | |
9e412a0a ML |
86 | while (img_len > 0) { |
87 | if (img_len > DOWNLOAD_SIZE) { | |
88 | len = DOWNLOAD_SIZE; | |
89 | buf[3] = 0; | |
90 | } else { | |
91 | len = img_len; /* the last packet */ | |
92 | buf[3] = 2; | |
247e9cff SA |
93 | } |
94 | ||
95 | buf[0] = len & 0xff; | |
96 | buf[1] = (len >> 8) & 0xff; | |
97 | buf[2] = (len >> 16) & 0xff; | |
98 | ||
9e412a0a | 99 | memcpy(buf+TYPE_A_HEADER_SIZE, firm->data + pos, len); |
247e9cff SA |
100 | ret = sdio_memcpy_toio(func, 0, buf, len + TYPE_A_HEADER_SIZE); |
101 | if (ret < 0) { | |
626e557f YT |
102 | dev_err(&func->dev, |
103 | "send image error: packet number = %d ret = %d\n", | |
104 | pno, ret); | |
247e9cff SA |
105 | goto out; |
106 | } | |
9e412a0a | 107 | |
247e9cff SA |
108 | if (buf[3] == 2) /* The last packet */ |
109 | break; | |
110 | if (!ack_ready(func)) { | |
111 | ret = -EIO; | |
626e557f | 112 | dev_err(&func->dev, "Ack is not ready.\n"); |
247e9cff SA |
113 | goto out; |
114 | } | |
115 | ret = sdio_memcpy_fromio(func, buf, 0, TYPE_A_LOOKAHEAD_SIZE); | |
116 | if (ret < 0) { | |
626e557f YT |
117 | dev_err(&func->dev, |
118 | "receive ack error: packet number = %d ret = %d\n", | |
119 | pno, ret); | |
247e9cff SA |
120 | goto out; |
121 | } | |
122 | sdio_writeb(func, 0x01, 0x13, &ret); | |
123 | sdio_writeb(func, 0x00, 0x10, &ret); /* PCRRT */ | |
124 | ||
9e412a0a ML |
125 | img_len -= DOWNLOAD_SIZE; |
126 | pos += DOWNLOAD_SIZE; | |
247e9cff SA |
127 | pno++; |
128 | } | |
9e412a0a | 129 | |
247e9cff | 130 | out: |
9e412a0a | 131 | kfree(buf); |
247e9cff SA |
132 | return ret; |
133 | } | |
134 | ||
135 | int sdio_boot(struct sdio_func *func) | |
136 | { | |
247e9cff | 137 | int ret; |
9e412a0a ML |
138 | const char *krn_name = FW_DIR FW_KRN; |
139 | const char *rfs_name = FW_DIR FW_RFS; | |
247e9cff SA |
140 | |
141 | tx_buf = kmalloc(YMEM0_SIZE, GFP_KERNEL); | |
78110bb8 | 142 | if (tx_buf == NULL) |
247e9cff | 143 | return -ENOMEM; |
247e9cff | 144 | |
9e412a0a | 145 | ret = download_image(func, krn_name); |
247e9cff SA |
146 | if (ret) |
147 | goto restore_fs; | |
626e557f | 148 | dev_info(&func->dev, "GCT: Kernel download success.\n"); |
247e9cff | 149 | |
9e412a0a | 150 | ret = download_image(func, rfs_name); |
247e9cff SA |
151 | if (ret) |
152 | goto restore_fs; | |
626e557f | 153 | dev_info(&func->dev, "GCT: Filesystem download success.\n"); |
247e9cff SA |
154 | |
155 | restore_fs: | |
247e9cff SA |
156 | kfree(tx_buf); |
157 | return ret; | |
158 | } |