1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Probing flash chips with QINFO records.
4 * (C) 2008 Korolev Alexey <akorolev@infradead.org>
5 * (C) 2008 Vasiliy Leonenko <vasiliy.leonenko@gmail.com>
7 #include <linux/module.h>
8 #include <linux/types.h>
9 #include <linux/kernel.h>
10 #include <linux/init.h>
11 #include <linux/errno.h>
12 #include <linux/slab.h>
13 #include <linux/interrupt.h>
15 #include <linux/mtd/xip.h>
16 #include <linux/mtd/map.h>
17 #include <linux/mtd/pfow.h>
18 #include <linux/mtd/qinfo.h>
20 static int lpddr_chip_setup(struct map_info
*map
, struct lpddr_private
*lpddr
);
21 struct mtd_info
*lpddr_probe(struct map_info
*map
);
22 static struct lpddr_private
*lpddr_probe_chip(struct map_info
*map
);
23 static int lpddr_pfow_present(struct map_info
*map
,
24 struct lpddr_private
*lpddr
);
26 static struct qinfo_query_info qinfo_array
[] = {
27 /* General device info */
28 {0, 0, "DevSizeShift", "Device size 2^n bytes"},
29 {0, 3, "BufSizeShift", "Program buffer size 2^n bytes"},
30 /* Erase block information */
31 {1, 1, "TotalBlocksNum", "Total number of blocks"},
32 {1, 2, "UniformBlockSizeShift", "Uniform block size 2^n bytes"},
33 /* Partition information */
34 {2, 1, "HWPartsNum", "Number of hardware partitions"},
35 /* Optional features */
36 {5, 1, "SuspEraseSupp", "Suspend erase supported"},
37 /* Operation typical time */
38 {10, 0, "SingleWordProgTime", "Single word program 2^n u-sec"},
39 {10, 1, "ProgBufferTime", "Program buffer write 2^n u-sec"},
40 {10, 2, "BlockEraseTime", "Block erase 2^n m-sec"},
41 {10, 3, "FullChipEraseTime", "Full chip erase 2^n m-sec"},
44 static long lpddr_get_qinforec_pos(struct map_info
*map
, char *id_str
)
46 int qinfo_lines
= ARRAY_SIZE(qinfo_array
);
48 int bankwidth
= map_bankwidth(map
) * 8;
51 for (i
= 0; i
< qinfo_lines
; i
++) {
52 if (strcmp(id_str
, qinfo_array
[i
].id_str
) == 0) {
53 major
= qinfo_array
[i
].major
& ((1 << bankwidth
) - 1);
54 minor
= qinfo_array
[i
].minor
& ((1 << bankwidth
) - 1);
55 return minor
| (major
<< bankwidth
);
58 printk(KERN_ERR
"%s qinfo id string is wrong! \n", map
->name
);
63 static uint16_t lpddr_info_query(struct map_info
*map
, char *id_str
)
65 unsigned int dsr
, val
;
66 int bits_per_chip
= map_bankwidth(map
) * 8;
67 unsigned long adr
= lpddr_get_qinforec_pos(map
, id_str
);
70 /* Write a request for the PFOW record */
71 map_write(map
, CMD(LPDDR_INFO_QUERY
),
72 map
->pfow_base
+ PFOW_COMMAND_CODE
);
73 map_write(map
, CMD(adr
& ((1 << bits_per_chip
) - 1)),
74 map
->pfow_base
+ PFOW_COMMAND_ADDRESS_L
);
75 map_write(map
, CMD(adr
>> bits_per_chip
),
76 map
->pfow_base
+ PFOW_COMMAND_ADDRESS_H
);
77 map_write(map
, CMD(LPDDR_START_EXECUTION
),
78 map
->pfow_base
+ PFOW_COMMAND_EXECUTE
);
80 while ((attempts
--) > 0) {
81 dsr
= CMDVAL(map_read(map
, map
->pfow_base
+ PFOW_DSR
));
82 if (dsr
& DSR_READY_STATUS
)
87 val
= CMDVAL(map_read(map
, map
->pfow_base
+ PFOW_COMMAND_DATA
));
91 static int lpddr_pfow_present(struct map_info
*map
, struct lpddr_private
*lpddr
)
95 /* Check identification string */
96 pfow_val
[0] = map_read(map
, map
->pfow_base
+ PFOW_QUERY_STRING_P
);
97 pfow_val
[1] = map_read(map
, map
->pfow_base
+ PFOW_QUERY_STRING_F
);
98 pfow_val
[2] = map_read(map
, map
->pfow_base
+ PFOW_QUERY_STRING_O
);
99 pfow_val
[3] = map_read(map
, map
->pfow_base
+ PFOW_QUERY_STRING_W
);
101 if (!map_word_equal(map
, CMD('P'), pfow_val
[0]))
104 if (!map_word_equal(map
, CMD('F'), pfow_val
[1]))
107 if (!map_word_equal(map
, CMD('O'), pfow_val
[2]))
110 if (!map_word_equal(map
, CMD('W'), pfow_val
[3]))
113 return 1; /* "PFOW" is found */
115 printk(KERN_WARNING
"%s: PFOW string at 0x%lx is not found \n",
116 map
->name
, map
->pfow_base
);
120 static int lpddr_chip_setup(struct map_info
*map
, struct lpddr_private
*lpddr
)
123 lpddr
->qinfo
= kzalloc(sizeof(struct qinfo_chip
), GFP_KERNEL
);
128 lpddr
->ManufactId
= CMDVAL(map_read(map
, map
->pfow_base
+ PFOW_MANUFACTURER_ID
));
129 /* Get the DeviceID */
130 lpddr
->DevId
= CMDVAL(map_read(map
, map
->pfow_base
+ PFOW_DEVICE_ID
));
131 /* read parameters from chip qinfo table */
132 lpddr
->qinfo
->DevSizeShift
= lpddr_info_query(map
, "DevSizeShift");
133 lpddr
->qinfo
->TotalBlocksNum
= lpddr_info_query(map
, "TotalBlocksNum");
134 lpddr
->qinfo
->BufSizeShift
= lpddr_info_query(map
, "BufSizeShift");
135 lpddr
->qinfo
->HWPartsNum
= lpddr_info_query(map
, "HWPartsNum");
136 lpddr
->qinfo
->UniformBlockSizeShift
=
137 lpddr_info_query(map
, "UniformBlockSizeShift");
138 lpddr
->qinfo
->SuspEraseSupp
= lpddr_info_query(map
, "SuspEraseSupp");
139 lpddr
->qinfo
->SingleWordProgTime
=
140 lpddr_info_query(map
, "SingleWordProgTime");
141 lpddr
->qinfo
->ProgBufferTime
= lpddr_info_query(map
, "ProgBufferTime");
142 lpddr
->qinfo
->BlockEraseTime
= lpddr_info_query(map
, "BlockEraseTime");
145 static struct lpddr_private
*lpddr_probe_chip(struct map_info
*map
)
147 struct lpddr_private lpddr
;
148 struct lpddr_private
*retlpddr
;
152 if ((map
->pfow_base
+ 0x1000) >= map
->size
) {
153 printk(KERN_NOTICE
"%s Probe at base (0x%08lx) past the end of"
154 "the map(0x%08lx)\n", map
->name
,
155 (unsigned long)map
->pfow_base
, map
->size
- 1);
158 memset(&lpddr
, 0, sizeof(struct lpddr_private
));
159 if (!lpddr_pfow_present(map
, &lpddr
))
162 if (!lpddr_chip_setup(map
, &lpddr
))
165 /* Ok so we found a chip */
166 lpddr
.chipshift
= lpddr
.qinfo
->DevSizeShift
;
169 numvirtchips
= lpddr
.numchips
* lpddr
.qinfo
->HWPartsNum
;
170 retlpddr
= kzalloc(struct_size(retlpddr
, chips
, numvirtchips
),
175 memcpy(retlpddr
, &lpddr
, sizeof(struct lpddr_private
));
177 retlpddr
->numchips
= numvirtchips
;
178 retlpddr
->chipshift
= retlpddr
->qinfo
->DevSizeShift
-
179 __ffs(retlpddr
->qinfo
->HWPartsNum
);
184 struct mtd_info
*lpddr_probe(struct map_info
*map
)
186 struct mtd_info
*mtd
= NULL
;
187 struct lpddr_private
*lpddr
;
189 /* First probe the map to see if we havecan open PFOW here */
190 lpddr
= lpddr_probe_chip(map
);
194 map
->fldrv_priv
= lpddr
;
195 mtd
= lpddr_cmdset(map
);
197 if (mtd
->size
> map
->size
) {
198 printk(KERN_WARNING
"Reducing visibility of %ldKiB chip"
199 "to %ldKiB\n", (unsigned long)mtd
->size
>> 10,
200 (unsigned long)map
->size
>> 10);
201 mtd
->size
= map
->size
;
208 map
->fldrv_priv
= NULL
;
212 static struct mtd_chip_driver lpddr_chipdrv
= {
213 .probe
= lpddr_probe
,
214 .name
= "qinfo_probe",
215 .module
= THIS_MODULE
218 static int __init
lpddr_probe_init(void)
220 register_mtd_chip_driver(&lpddr_chipdrv
);
224 static void __exit
lpddr_probe_exit(void)
226 unregister_mtd_chip_driver(&lpddr_chipdrv
);
229 module_init(lpddr_probe_init
);
230 module_exit(lpddr_probe_exit
);
232 MODULE_LICENSE("GPL");
233 MODULE_AUTHOR("Vasiliy Leonenko <vasiliy.leonenko@gmail.com>");
234 MODULE_DESCRIPTION("Driver to probe qinfo flash chips");