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 SA |
15 | #include <linux/kernel.h> |
16 | #include <linux/init.h> | |
17 | #include <linux/mm.h> | |
18 | #include <linux/uaccess.h> | |
19 | #include <linux/fs.h> | |
20 | #include <linux/sched.h> | |
21 | #include <linux/slab.h> | |
22 | ||
23 | #include <linux/mmc/core.h> | |
24 | #include <linux/mmc/card.h> | |
25 | #include <linux/mmc/sdio_func.h> | |
26 | ||
9e412a0a ML |
27 | #include <linux/firmware.h> |
28 | ||
247e9cff SA |
29 | #include "gdm_sdio.h" |
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 | { | |
44 | unsigned long start = jiffies; | |
45 | u8 val; | |
46 | int ret; | |
47 | ||
48 | while ((jiffies - start) < HZ) { | |
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) { | |
68 | printk(KERN_ERR | |
69 | "requesting firmware %s failed with error %d\n", | |
70 | img_name, ret); | |
71 | return ret; | |
247e9cff SA |
72 | } |
73 | ||
9e412a0a ML |
74 | buf = kmalloc(DOWNLOAD_SIZE + TYPE_A_HEADER_SIZE, GFP_KERNEL); |
75 | if (buf == NULL) { | |
76 | printk(KERN_ERR "Error: kmalloc\n"); | |
77 | return -ENOMEM; | |
247e9cff SA |
78 | } |
79 | ||
9e412a0a ML |
80 | img_len = firm->size; |
81 | ||
82 | if (img_len <= 0) { | |
83 | ret = -1; | |
247e9cff SA |
84 | goto out; |
85 | } | |
86 | ||
87 | pno = 0; | |
9e412a0a ML |
88 | while (img_len > 0) { |
89 | if (img_len > DOWNLOAD_SIZE) { | |
90 | len = DOWNLOAD_SIZE; | |
91 | buf[3] = 0; | |
92 | } else { | |
93 | len = img_len; /* the last packet */ | |
94 | buf[3] = 2; | |
247e9cff SA |
95 | } |
96 | ||
97 | buf[0] = len & 0xff; | |
98 | buf[1] = (len >> 8) & 0xff; | |
99 | buf[2] = (len >> 16) & 0xff; | |
100 | ||
9e412a0a | 101 | memcpy(buf+TYPE_A_HEADER_SIZE, firm->data + pos, len); |
247e9cff SA |
102 | ret = sdio_memcpy_toio(func, 0, buf, len + TYPE_A_HEADER_SIZE); |
103 | if (ret < 0) { | |
104 | printk(KERN_ERR "gdmwm: send image error: " | |
105 | "packet number = %d ret = %d\n", pno, ret); | |
106 | goto out; | |
107 | } | |
9e412a0a | 108 | |
247e9cff SA |
109 | if (buf[3] == 2) /* The last packet */ |
110 | break; | |
111 | if (!ack_ready(func)) { | |
112 | ret = -EIO; | |
113 | printk(KERN_ERR "gdmwm: Ack is not ready.\n"); | |
114 | goto out; | |
115 | } | |
116 | ret = sdio_memcpy_fromio(func, buf, 0, TYPE_A_LOOKAHEAD_SIZE); | |
117 | if (ret < 0) { | |
118 | printk(KERN_ERR "gdmwm: receive ack error: " | |
119 | "packet number = %d ret = %d\n", pno, ret); | |
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); | |
142 | if (tx_buf == NULL) { | |
143 | printk(KERN_ERR "Error: kmalloc: %s %d\n", __func__, __LINE__); | |
144 | return -ENOMEM; | |
145 | } | |
146 | ||
9e412a0a | 147 | ret = download_image(func, krn_name); |
247e9cff SA |
148 | if (ret) |
149 | goto restore_fs; | |
150 | printk(KERN_INFO "GCT: Kernel download success.\n"); | |
151 | ||
9e412a0a | 152 | ret = download_image(func, rfs_name); |
247e9cff SA |
153 | if (ret) |
154 | goto restore_fs; | |
155 | printk(KERN_INFO "GCT: Filesystem download success.\n"); | |
156 | ||
157 | restore_fs: | |
247e9cff SA |
158 | kfree(tx_buf); |
159 | return ret; | |
160 | } |