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 | ||
27 | #include "gdm_sdio.h" | |
28 | ||
29 | #define TYPE_A_HEADER_SIZE 4 | |
30 | #define TYPE_A_LOOKAHEAD_SIZE 16 | |
31 | #define YMEM0_SIZE 0x8000 /* 32kbytes */ | |
32 | #define DOWNLOAD_SIZE (YMEM0_SIZE - TYPE_A_HEADER_SIZE) | |
33 | ||
34 | #define KRN_PATH "/lib/firmware/gdm72xx/gdmskrn.bin" | |
35 | #define RFS_PATH "/lib/firmware/gdm72xx/gdmsrfs.bin" | |
36 | ||
37 | static u8 *tx_buf; | |
38 | ||
39 | static int ack_ready(struct sdio_func *func) | |
40 | { | |
41 | unsigned long start = jiffies; | |
42 | u8 val; | |
43 | int ret; | |
44 | ||
45 | while ((jiffies - start) < HZ) { | |
46 | val = sdio_readb(func, 0x13, &ret); | |
47 | if (val & 0x01) | |
48 | return 1; | |
49 | schedule(); | |
50 | } | |
51 | ||
52 | return 0; | |
53 | } | |
54 | ||
55 | static int download_image(struct sdio_func *func, char *img_name) | |
56 | { | |
57 | int ret = 0, len, size, pno; | |
58 | struct file *filp = NULL; | |
59 | struct inode *inode = NULL; | |
60 | u8 *buf = tx_buf; | |
61 | loff_t pos = 0; | |
62 | ||
63 | filp = filp_open(img_name, O_RDONLY | O_LARGEFILE, 0); | |
64 | if (IS_ERR(filp)) { | |
65 | printk(KERN_ERR "Can't find %s.\n", img_name); | |
66 | return -ENOENT; | |
67 | } | |
68 | ||
09fada5b AV |
69 | inode = filp->f_dentry->d_inode; |
70 | if (!S_ISREG(inode->i_mode)) { | |
247e9cff SA |
71 | printk(KERN_ERR "Invalid file type: %s\n", img_name); |
72 | ret = -EINVAL; | |
73 | goto out; | |
74 | } | |
75 | ||
76 | size = i_size_read(inode->i_mapping->host); | |
77 | if (size <= 0) { | |
78 | printk(KERN_ERR "Unable to find file size: %s\n", img_name); | |
79 | ret = size; | |
80 | goto out; | |
81 | } | |
82 | ||
83 | pno = 0; | |
84 | while ((len = filp->f_op->read(filp, buf + TYPE_A_HEADER_SIZE, | |
85 | DOWNLOAD_SIZE, &pos))) { | |
86 | if (len < 0) { | |
87 | ret = -1; | |
88 | goto out; | |
89 | } | |
90 | ||
91 | buf[0] = len & 0xff; | |
92 | buf[1] = (len >> 8) & 0xff; | |
93 | buf[2] = (len >> 16) & 0xff; | |
94 | ||
95 | if (pos >= size) /* The last packet */ | |
96 | buf[3] = 2; | |
97 | else | |
98 | buf[3] = 0; | |
99 | ||
100 | ret = sdio_memcpy_toio(func, 0, buf, len + TYPE_A_HEADER_SIZE); | |
101 | if (ret < 0) { | |
102 | printk(KERN_ERR "gdmwm: send image error: " | |
103 | "packet number = %d ret = %d\n", pno, ret); | |
104 | goto out; | |
105 | } | |
106 | if (buf[3] == 2) /* The last packet */ | |
107 | break; | |
108 | if (!ack_ready(func)) { | |
109 | ret = -EIO; | |
110 | printk(KERN_ERR "gdmwm: Ack is not ready.\n"); | |
111 | goto out; | |
112 | } | |
113 | ret = sdio_memcpy_fromio(func, buf, 0, TYPE_A_LOOKAHEAD_SIZE); | |
114 | if (ret < 0) { | |
115 | printk(KERN_ERR "gdmwm: receive ack error: " | |
116 | "packet number = %d ret = %d\n", pno, ret); | |
117 | goto out; | |
118 | } | |
119 | sdio_writeb(func, 0x01, 0x13, &ret); | |
120 | sdio_writeb(func, 0x00, 0x10, &ret); /* PCRRT */ | |
121 | ||
122 | pno++; | |
123 | } | |
124 | out: | |
09fada5b | 125 | filp_close(filp, NULL); |
247e9cff SA |
126 | return ret; |
127 | } | |
128 | ||
129 | int sdio_boot(struct sdio_func *func) | |
130 | { | |
131 | static mm_segment_t fs; | |
132 | int ret; | |
133 | ||
134 | tx_buf = kmalloc(YMEM0_SIZE, GFP_KERNEL); | |
135 | if (tx_buf == NULL) { | |
136 | printk(KERN_ERR "Error: kmalloc: %s %d\n", __func__, __LINE__); | |
137 | return -ENOMEM; | |
138 | } | |
139 | ||
140 | fs = get_fs(); | |
141 | set_fs(get_ds()); | |
142 | ||
143 | ret = download_image(func, KRN_PATH); | |
144 | if (ret) | |
145 | goto restore_fs; | |
146 | printk(KERN_INFO "GCT: Kernel download success.\n"); | |
147 | ||
148 | ret = download_image(func, RFS_PATH); | |
149 | if (ret) | |
150 | goto restore_fs; | |
151 | printk(KERN_INFO "GCT: Filesystem download success.\n"); | |
152 | ||
153 | restore_fs: | |
154 | set_fs(fs); | |
155 | kfree(tx_buf); | |
156 | return ret; | |
157 | } |