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