• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  linux/drivers/mmchost/rkemmc_ops.c
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or (at
7  * your option) any later version.
8  */
9 
10 #include <linux/mmc/core.h>
11 #include <linux/mmc/card.h>
12 #include <linux/mmc/host.h>
13 #include <linux/mmc/mmc.h>
14 #include <linux/slab.h>
15 
16 #include <linux/scatterlist.h>
17 #include <linux/swap.h> /* For nr_free_buffer_pages() */
18 #include <linux/list.h>
19 
20 #include <linux/debugfs.h>
21 #include <linux/uaccess.h>
22 #include <linux/seq_file.h>
23 #include <linux/mutex.h>
24 #include <linux/miscdevice.h>
25 #include "core/block.h"
26 #include "core/card.h"
27 #include "core/core.h"
28 #include "core/mmc_ops.h"
29 #include "rk_sdmmc_ops.h"
30 
31 #define BLKSZ 512
32 
33 enum emmc_area_type {
34     MMC_DATA_AREA_MAIN,
35     MMC_DATA_AREA_BOOT1,
36     MMC_DATA_AREA_BOOT2,
37     MMC_DATA_AREA_RPMB,
38 };
39 
rk_emmc_set_areatype(enum emmc_area_type areatype)40 static int rk_emmc_set_areatype(enum emmc_area_type areatype)
41 {
42     int err;
43     u8 part_config;
44 
45     part_config = this_card->ext_csd.part_config;
46     part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
47     part_config |= (u8)areatype;
48     err = mmc_switch(this_card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONFIG, part_config, this_card->ext_csd.part_time);
49 
50     return err;
51 }
52 
53 /*
54  * Fill in the mmc_request structure given a set of transfer parameters.
55  */
rk_emmc_prepare_mrq(struct mmc_request * mrq,struct scatterlist * sg,unsigned sg_len,unsigned dev_addr,unsigned blocks,unsigned blksz,int write)56 static void rk_emmc_prepare_mrq(struct mmc_request *mrq, struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
57                                 unsigned blocks, unsigned blksz, int write)
58 {
59     BUG_ON(!mrq || !mrq->cmd || !mrq->data || !mrq->stop);
60 
61     if (blocks > 1) {
62         mrq->cmd->opcode = write ? MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
63     } else {
64         mrq->cmd->opcode = write ? MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
65     }
66 
67     mrq->cmd->arg = dev_addr;
68     if (!mmc_card_blockaddr(this_card)) {
69         mrq->cmd->arg <<= 0x09;
70     }
71 
72     mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC;
73 
74     if (blocks == 1) {
75         mrq->stop = NULL;
76     } else {
77         mrq->stop->opcode = MMC_STOP_TRANSMISSION;
78         mrq->stop->arg = 0;
79         mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
80     }
81 
82     mrq->data->blksz = blksz;
83     mrq->data->blocks = blocks;
84     mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
85     mrq->data->sg = sg;
86     mrq->data->sg_len = sg_len;
87     mmc_set_data_timeout(mrq->data, this_card);
88 }
89 
rk_emmc_busy(struct mmc_command * cmd)90 static int rk_emmc_busy(struct mmc_command *cmd)
91 {
92     return !(cmd->resp[0] & R1_READY_FOR_DATA) || (R1_CURRENT_STATE(cmd->resp[0]) == 0x07);
93 }
94 
95 /*
96  * Wait for the card to finish the busy state
97  */
rk_emmc_wait_busy(void)98 static int rk_emmc_wait_busy(void)
99 {
100     int ret, busy;
101     struct mmc_command cmd = {0};
102 
103     busy = 0;
104     do {
105         memset(&cmd, 0, sizeof(struct mmc_command));
106 
107         cmd.opcode = MMC_SEND_STATUS;
108         cmd.arg = this_card->rca << 0x10;
109         cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
110 
111         ret = mmc_wait_for_cmd(this_card->host, &cmd, 0);
112         if (ret) {
113             break;
114         }
115 
116         if (!busy && rk_emmc_busy(&cmd)) {
117             busy = 1;
118             if (this_card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) {
119                 pr_info("%s: Warning: Host did not "
120                         "wait for busy state to end.\n",
121                         mmc_hostname(this_card->host));
122             }
123         }
124     } while (rk_emmc_busy(&cmd));
125 
126     return ret;
127 }
128 
129 /*
130  * Transfer a single sector of kernel addressable data
131  */
rk_emmc_transfer(u8 * buffer,unsigned addr,unsigned blksz,int write)132 int rk_emmc_transfer(u8 *buffer, unsigned addr, unsigned blksz, int write)
133 {
134     int ret = 0;
135     enum emmc_area_type areatype;
136 
137     struct mmc_request mrq = {0};
138     struct mmc_command cmd = {0};
139     struct mmc_command stop = {0};
140     struct mmc_data data = {0};
141 
142     struct scatterlist sg;
143 
144     if (!this_card) {
145         return -EIO;
146     }
147 
148     mrq.cmd = &cmd;
149     mrq.data = &data;
150     mrq.stop = &stop;
151 
152     sg_init_one(&sg, buffer, blksz);
153 
154     rk_emmc_prepare_mrq(&mrq, &sg, 1, addr, 1, blksz, write);
155 
156     mmc_claim_host(this_card->host);
157 
158     areatype = (enum emmc_area_type)this_card->ext_csd.part_config & EXT_CSD_PART_CONFIG_ACC_MASK;
159     if (areatype != MMC_DATA_AREA_MAIN) {
160         ret = rk_emmc_set_areatype(MMC_DATA_AREA_MAIN);
161         if (ret) {
162             pr_err("rk_emmc_set_areatype error!.\n");
163             goto exit;
164         }
165     }
166 
167     mmc_wait_for_req(this_card->host, &mrq);
168 
169     if (cmd.error) {
170         ret = cmd.error;
171         goto exit;
172     }
173     if (data.error) {
174         ret = data.error;
175         goto exit;
176     }
177 
178     ret = rk_emmc_wait_busy();
179 
180     if (areatype != MMC_DATA_AREA_MAIN) {
181         ret = rk_emmc_set_areatype(areatype);
182         if (ret) {
183             pr_err("rk_emmc_set_areatype error!.\n");
184         }
185     }
186 
187 exit:
188     mmc_release_host(this_card->host);
189     return ret;
190 }
191 EXPORT_SYMBOL(rk_emmc_transfer);
192 
193 MODULE_LICENSE("GPL");
194