2 * Probing flash chips with QINFO records.
3 * (C) 2008 Korolev Alexey <akorolev@infradead.org>
4 * (C) 2008 Vasiliy Leonenko <vasiliy.leonenko@gmail.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 #include <linux/module.h>
22 #include <linux/types.h>
23 #include <linux/kernel.h>
24 #include <linux/init.h>
25 #include <linux/errno.h>
26 #include <linux/slab.h>
27 #include <linux/interrupt.h>
29 #include <linux/mtd/xip.h>
30 #include <linux/mtd/map.h>
31 #include <linux/mtd/pfow.h>
32 #include <linux/mtd/qinfo.h>
34 static int lpddr_chip_setup(struct map_info
*map
, struct lpddr_private
*lpddr
);
35 struct mtd_info
*lpddr_probe(struct map_info
*map
);
36 static struct lpddr_private
*lpddr_probe_chip(struct map_info
*map
);
37 static int lpddr_pfow_present(struct map_info
*map
,
38 struct lpddr_private
*lpddr
);
40 static struct qinfo_query_info qinfo_array
[] = {
41 /* General device info */
42 {0, 0, "DevSizeShift", "Device size 2^n bytes"},
43 {0, 3, "BufSizeShift", "Program buffer size 2^n bytes"},
44 /* Erase block information */
45 {1, 1, "TotalBlocksNum", "Total number of blocks"},
46 {1, 2, "UniformBlockSizeShift", "Uniform block size 2^n bytes"},
47 /* Partition information */
48 {2, 1, "HWPartsNum", "Number of hardware partitions"},
49 /* Optional features */
50 {5, 1, "SuspEraseSupp", "Suspend erase supported"},
51 /* Operation typical time */
52 {10, 0, "SingleWordProgTime", "Single word program 2^n u-sec"},
53 {10, 1, "ProgBufferTime", "Program buffer write 2^n u-sec"},
54 {10, 2, "BlockEraseTime", "Block erase 2^n m-sec"},
55 {10, 3, "FullChipEraseTime", "Full chip erase 2^n m-sec"},
58 static long lpddr_get_qinforec_pos(struct map_info
*map
, char *id_str
)
60 int qinfo_lines
= ARRAY_SIZE(qinfo_array
);
62 int bankwidth
= map_bankwidth(map
) * 8;
65 for (i
= 0; i
< qinfo_lines
; i
++) {
66 if (strcmp(id_str
, qinfo_array
[i
].id_str
) == 0) {
67 major
= qinfo_array
[i
].major
& ((1 << bankwidth
) - 1);
68 minor
= qinfo_array
[i
].minor
& ((1 << bankwidth
) - 1);
69 return minor
| (major
<< bankwidth
);
72 printk(KERN_ERR
"%s qinfo id string is wrong! \n", map
->name
);
77 static uint16_t lpddr_info_query(struct map_info
*map
, char *id_str
)
79 unsigned int dsr
, val
;
80 int bits_per_chip
= map_bankwidth(map
) * 8;
81 unsigned long adr
= lpddr_get_qinforec_pos(map
, id_str
);
84 /* Write a request for the PFOW record */
85 map_write(map
, CMD(LPDDR_INFO_QUERY
),
86 map
->pfow_base
+ PFOW_COMMAND_CODE
);
87 map_write(map
, CMD(adr
& ((1 << bits_per_chip
) - 1)),
88 map
->pfow_base
+ PFOW_COMMAND_ADDRESS_L
);
89 map_write(map
, CMD(adr
>> bits_per_chip
),
90 map
->pfow_base
+ PFOW_COMMAND_ADDRESS_H
);
91 map_write(map
, CMD(LPDDR_START_EXECUTION
),
92 map
->pfow_base
+ PFOW_COMMAND_EXECUTE
);
94 while ((attempts
--) > 0) {
95 dsr
= CMDVAL(map_read(map
, map
->pfow_base
+ PFOW_DSR
));
96 if (dsr
& DSR_READY_STATUS
)
101 val
= CMDVAL(map_read(map
, map
->pfow_base
+ PFOW_COMMAND_DATA
));
105 static int lpddr_pfow_present(struct map_info
*map
, struct lpddr_private
*lpddr
)
107 map_word pfow_val
[4];
109 /* Check identification string */
110 pfow_val
[0] = map_read(map
, map
->pfow_base
+ PFOW_QUERY_STRING_P
);
111 pfow_val
[1] = map_read(map
, map
->pfow_base
+ PFOW_QUERY_STRING_F
);
112 pfow_val
[2] = map_read(map
, map
->pfow_base
+ PFOW_QUERY_STRING_O
);
113 pfow_val
[3] = map_read(map
, map
->pfow_base
+ PFOW_QUERY_STRING_W
);
115 if (!map_word_equal(map
, CMD('P'), pfow_val
[0]))
118 if (!map_word_equal(map
, CMD('F'), pfow_val
[1]))
121 if (!map_word_equal(map
, CMD('O'), pfow_val
[2]))
124 if (!map_word_equal(map
, CMD('W'), pfow_val
[3]))
127 return 1; /* "PFOW" is found */
129 printk(KERN_WARNING
"%s: PFOW string at 0x%lx is not found \n",
130 map
->name
, map
->pfow_base
);
134 static int lpddr_chip_setup(struct map_info
*map
, struct lpddr_private
*lpddr
)
137 lpddr
->qinfo
= kzalloc(sizeof(struct qinfo_chip
), GFP_KERNEL
);
142 lpddr
->ManufactId
= CMDVAL(map_read(map
, map
->pfow_base
+ PFOW_MANUFACTURER_ID
));
143 /* Get the DeviceID */
144 lpddr
->DevId
= CMDVAL(map_read(map
, map
->pfow_base
+ PFOW_DEVICE_ID
));
145 /* read parameters from chip qinfo table */
146 lpddr
->qinfo
->DevSizeShift
= lpddr_info_query(map
, "DevSizeShift");
147 lpddr
->qinfo
->TotalBlocksNum
= lpddr_info_query(map
, "TotalBlocksNum");
148 lpddr
->qinfo
->BufSizeShift
= lpddr_info_query(map
, "BufSizeShift");
149 lpddr
->qinfo
->HWPartsNum
= lpddr_info_query(map
, "HWPartsNum");
150 lpddr
->qinfo
->UniformBlockSizeShift
=
151 lpddr_info_query(map
, "UniformBlockSizeShift");
152 lpddr
->qinfo
->SuspEraseSupp
= lpddr_info_query(map
, "SuspEraseSupp");
153 lpddr
->qinfo
->SingleWordProgTime
=
154 lpddr_info_query(map
, "SingleWordProgTime");
155 lpddr
->qinfo
->ProgBufferTime
= lpddr_info_query(map
, "ProgBufferTime");
156 lpddr
->qinfo
->BlockEraseTime
= lpddr_info_query(map
, "BlockEraseTime");
159 static struct lpddr_private
*lpddr_probe_chip(struct map_info
*map
)
161 struct lpddr_private lpddr
;
162 struct lpddr_private
*retlpddr
;
166 if ((map
->pfow_base
+ 0x1000) >= map
->size
) {
167 printk(KERN_NOTICE
"%s Probe at base (0x%08lx) past the end of"
168 "the map(0x%08lx)\n", map
->name
,
169 (unsigned long)map
->pfow_base
, map
->size
- 1);
172 memset(&lpddr
, 0, sizeof(struct lpddr_private
));
173 if (!lpddr_pfow_present(map
, &lpddr
))
176 if (!lpddr_chip_setup(map
, &lpddr
))
179 /* Ok so we found a chip */
180 lpddr
.chipshift
= lpddr
.qinfo
->DevSizeShift
;
183 numvirtchips
= lpddr
.numchips
* lpddr
.qinfo
->HWPartsNum
;
184 retlpddr
= kzalloc(sizeof(struct lpddr_private
) +
185 numvirtchips
* sizeof(struct flchip
), GFP_KERNEL
);
189 memcpy(retlpddr
, &lpddr
, sizeof(struct lpddr_private
));
191 retlpddr
->numchips
= numvirtchips
;
192 retlpddr
->chipshift
= retlpddr
->qinfo
->DevSizeShift
-
193 __ffs(retlpddr
->qinfo
->HWPartsNum
);
198 struct mtd_info
*lpddr_probe(struct map_info
*map
)
200 struct mtd_info
*mtd
= NULL
;
201 struct lpddr_private
*lpddr
;
203 /* First probe the map to see if we havecan open PFOW here */
204 lpddr
= lpddr_probe_chip(map
);
208 map
->fldrv_priv
= lpddr
;
209 mtd
= lpddr_cmdset(map
);
211 if (mtd
->size
> map
->size
) {
212 printk(KERN_WARNING
"Reducing visibility of %ldKiB chip"
213 "to %ldKiB\n", (unsigned long)mtd
->size
>> 10,
214 (unsigned long)map
->size
>> 10);
215 mtd
->size
= map
->size
;
222 map
->fldrv_priv
= NULL
;
226 static struct mtd_chip_driver lpddr_chipdrv
= {
227 .probe
= lpddr_probe
,
228 .name
= "qinfo_probe",
229 .module
= THIS_MODULE
232 static int __init
lpddr_probe_init(void)
234 register_mtd_chip_driver(&lpddr_chipdrv
);
238 static void __exit
lpddr_probe_exit(void)
240 unregister_mtd_chip_driver(&lpddr_chipdrv
);
243 module_init(lpddr_probe_init
);
244 module_exit(lpddr_probe_exit
);
246 MODULE_LICENSE("GPL");
247 MODULE_AUTHOR("Vasiliy Leonenko <vasiliy.leonenko@gmail.com>");
248 MODULE_DESCRIPTION("Driver to probe qinfo flash chips");