Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Probing flash chips with QINFO records.
0004  * (C) 2008 Korolev Alexey <akorolev@infradead.org>
0005  * (C) 2008 Vasiliy Leonenko <vasiliy.leonenko@gmail.com>
0006  */
0007 #include <linux/module.h>
0008 #include <linux/types.h>
0009 #include <linux/kernel.h>
0010 #include <linux/init.h>
0011 #include <linux/errno.h>
0012 #include <linux/slab.h>
0013 #include <linux/interrupt.h>
0014 
0015 #include <linux/mtd/xip.h>
0016 #include <linux/mtd/map.h>
0017 #include <linux/mtd/pfow.h>
0018 #include <linux/mtd/qinfo.h>
0019 
0020 static int lpddr_chip_setup(struct map_info *map, struct lpddr_private *lpddr);
0021 struct mtd_info *lpddr_probe(struct map_info *map);
0022 static struct lpddr_private *lpddr_probe_chip(struct map_info *map);
0023 static int lpddr_pfow_present(struct map_info *map,
0024             struct lpddr_private *lpddr);
0025 
0026 static struct qinfo_query_info qinfo_array[] = {
0027     /* General device info */
0028     {0, 0, "DevSizeShift", "Device size 2^n bytes"},
0029     {0, 3, "BufSizeShift", "Program buffer size 2^n bytes"},
0030     /* Erase block information */
0031     {1, 1, "TotalBlocksNum", "Total number of blocks"},
0032     {1, 2, "UniformBlockSizeShift", "Uniform block size 2^n bytes"},
0033     /* Partition information */
0034     {2, 1, "HWPartsNum", "Number of hardware partitions"},
0035     /* Optional features */
0036     {5, 1, "SuspEraseSupp", "Suspend erase supported"},
0037     /* Operation typical time */
0038     {10, 0, "SingleWordProgTime", "Single word program 2^n u-sec"},
0039     {10, 1, "ProgBufferTime", "Program buffer write 2^n u-sec"},
0040     {10, 2, "BlockEraseTime", "Block erase 2^n m-sec"},
0041     {10, 3, "FullChipEraseTime", "Full chip erase 2^n m-sec"},
0042 };
0043 
0044 static long lpddr_get_qinforec_pos(struct map_info *map, char *id_str)
0045 {
0046     int qinfo_lines = ARRAY_SIZE(qinfo_array);
0047     int i;
0048     int bankwidth = map_bankwidth(map) * 8;
0049     int major, minor;
0050 
0051     for (i = 0; i < qinfo_lines; i++) {
0052         if (strcmp(id_str, qinfo_array[i].id_str) == 0) {
0053             major = qinfo_array[i].major & ((1 << bankwidth) - 1);
0054             minor = qinfo_array[i].minor & ((1 << bankwidth) - 1);
0055             return minor | (major << bankwidth);
0056         }
0057     }
0058     printk(KERN_ERR"%s qinfo id string is wrong! \n", map->name);
0059     BUG();
0060     return -1;
0061 }
0062 
0063 static uint16_t lpddr_info_query(struct map_info *map, char *id_str)
0064 {
0065     unsigned int dsr, val;
0066     int bits_per_chip = map_bankwidth(map) * 8;
0067     unsigned long adr = lpddr_get_qinforec_pos(map, id_str);
0068     int attempts = 20;
0069 
0070     /* Write a request for the PFOW record */
0071     map_write(map, CMD(LPDDR_INFO_QUERY),
0072             map->pfow_base + PFOW_COMMAND_CODE);
0073     map_write(map, CMD(adr & ((1 << bits_per_chip) - 1)),
0074             map->pfow_base + PFOW_COMMAND_ADDRESS_L);
0075     map_write(map, CMD(adr >> bits_per_chip),
0076             map->pfow_base + PFOW_COMMAND_ADDRESS_H);
0077     map_write(map, CMD(LPDDR_START_EXECUTION),
0078             map->pfow_base + PFOW_COMMAND_EXECUTE);
0079 
0080     while ((attempts--) > 0) {
0081         dsr = CMDVAL(map_read(map, map->pfow_base + PFOW_DSR));
0082         if (dsr & DSR_READY_STATUS)
0083             break;
0084         udelay(10);
0085     }
0086 
0087     val = CMDVAL(map_read(map, map->pfow_base + PFOW_COMMAND_DATA));
0088     return val;
0089 }
0090 
0091 static int lpddr_pfow_present(struct map_info *map, struct lpddr_private *lpddr)
0092 {
0093     map_word pfow_val[4];
0094 
0095     /* Check identification string */
0096     pfow_val[0] = map_read(map, map->pfow_base + PFOW_QUERY_STRING_P);
0097     pfow_val[1] = map_read(map, map->pfow_base + PFOW_QUERY_STRING_F);
0098     pfow_val[2] = map_read(map, map->pfow_base + PFOW_QUERY_STRING_O);
0099     pfow_val[3] = map_read(map, map->pfow_base + PFOW_QUERY_STRING_W);
0100 
0101     if (!map_word_equal(map, CMD('P'), pfow_val[0]))
0102         goto out;
0103 
0104     if (!map_word_equal(map, CMD('F'), pfow_val[1]))
0105         goto out;
0106 
0107     if (!map_word_equal(map, CMD('O'), pfow_val[2]))
0108         goto out;
0109 
0110     if (!map_word_equal(map, CMD('W'), pfow_val[3]))
0111         goto out;
0112 
0113     return 1;   /* "PFOW" is found */
0114 out:
0115     printk(KERN_WARNING"%s: PFOW string at 0x%lx is not found \n",
0116                     map->name, map->pfow_base);
0117     return 0;
0118 }
0119 
0120 static int lpddr_chip_setup(struct map_info *map, struct lpddr_private *lpddr)
0121 {
0122 
0123     lpddr->qinfo = kzalloc(sizeof(struct qinfo_chip), GFP_KERNEL);
0124     if (!lpddr->qinfo)
0125         return 0;
0126 
0127     /* Get the ManuID */
0128     lpddr->ManufactId = CMDVAL(map_read(map, map->pfow_base + PFOW_MANUFACTURER_ID));
0129     /* Get the DeviceID */
0130     lpddr->DevId = CMDVAL(map_read(map, map->pfow_base + PFOW_DEVICE_ID));
0131     /* read parameters from chip qinfo table */
0132     lpddr->qinfo->DevSizeShift = lpddr_info_query(map, "DevSizeShift");
0133     lpddr->qinfo->TotalBlocksNum = lpddr_info_query(map, "TotalBlocksNum");
0134     lpddr->qinfo->BufSizeShift = lpddr_info_query(map, "BufSizeShift");
0135     lpddr->qinfo->HWPartsNum = lpddr_info_query(map, "HWPartsNum");
0136     lpddr->qinfo->UniformBlockSizeShift =
0137                 lpddr_info_query(map, "UniformBlockSizeShift");
0138     lpddr->qinfo->SuspEraseSupp = lpddr_info_query(map, "SuspEraseSupp");
0139     lpddr->qinfo->SingleWordProgTime =
0140                 lpddr_info_query(map, "SingleWordProgTime");
0141     lpddr->qinfo->ProgBufferTime = lpddr_info_query(map, "ProgBufferTime");
0142     lpddr->qinfo->BlockEraseTime = lpddr_info_query(map, "BlockEraseTime");
0143     return 1;
0144 }
0145 static struct lpddr_private *lpddr_probe_chip(struct map_info *map)
0146 {
0147     struct lpddr_private lpddr;
0148     struct lpddr_private *retlpddr;
0149     int numvirtchips;
0150 
0151 
0152     if ((map->pfow_base + 0x1000) >= map->size) {
0153         printk(KERN_NOTICE"%s Probe at base (0x%08lx) past the end of"
0154                 "the map(0x%08lx)\n", map->name,
0155                 (unsigned long)map->pfow_base, map->size - 1);
0156         return NULL;
0157     }
0158     memset(&lpddr, 0, sizeof(struct lpddr_private));
0159     if (!lpddr_pfow_present(map, &lpddr))
0160         return NULL;
0161 
0162     if (!lpddr_chip_setup(map, &lpddr))
0163         return NULL;
0164 
0165     /* Ok so we found a chip */
0166     lpddr.chipshift = lpddr.qinfo->DevSizeShift;
0167     lpddr.numchips = 1;
0168 
0169     numvirtchips = lpddr.numchips * lpddr.qinfo->HWPartsNum;
0170     retlpddr = kzalloc(struct_size(retlpddr, chips, numvirtchips),
0171                GFP_KERNEL);
0172     if (!retlpddr)
0173         return NULL;
0174 
0175     memcpy(retlpddr, &lpddr, sizeof(struct lpddr_private));
0176 
0177     retlpddr->numchips = numvirtchips;
0178     retlpddr->chipshift = retlpddr->qinfo->DevSizeShift -
0179                 __ffs(retlpddr->qinfo->HWPartsNum);
0180 
0181     return retlpddr;
0182 }
0183 
0184 struct mtd_info *lpddr_probe(struct map_info *map)
0185 {
0186     struct mtd_info *mtd = NULL;
0187     struct lpddr_private *lpddr;
0188 
0189     /* First probe the map to see if we havecan open PFOW here */
0190     lpddr = lpddr_probe_chip(map);
0191     if (!lpddr)
0192         return NULL;
0193 
0194     map->fldrv_priv = lpddr;
0195     mtd = lpddr_cmdset(map);
0196     if (mtd) {
0197         if (mtd->size > map->size) {
0198             printk(KERN_WARNING "Reducing visibility of %ldKiB chip"
0199                 "to %ldKiB\n", (unsigned long)mtd->size >> 10,
0200                 (unsigned long)map->size >> 10);
0201             mtd->size = map->size;
0202         }
0203         return mtd;
0204     }
0205 
0206     kfree(lpddr->qinfo);
0207     kfree(lpddr);
0208     map->fldrv_priv = NULL;
0209     return NULL;
0210 }
0211 
0212 static struct mtd_chip_driver lpddr_chipdrv = {
0213     .probe      = lpddr_probe,
0214     .name       = "qinfo_probe",
0215     .module     = THIS_MODULE
0216 };
0217 
0218 static int __init lpddr_probe_init(void)
0219 {
0220     register_mtd_chip_driver(&lpddr_chipdrv);
0221     return 0;
0222 }
0223 
0224 static void __exit lpddr_probe_exit(void)
0225 {
0226     unregister_mtd_chip_driver(&lpddr_chipdrv);
0227 }
0228 
0229 module_init(lpddr_probe_init);
0230 module_exit(lpddr_probe_exit);
0231 
0232 MODULE_LICENSE("GPL");
0233 MODULE_AUTHOR("Vasiliy Leonenko <vasiliy.leonenko@gmail.com>");
0234 MODULE_DESCRIPTION("Driver to probe qinfo flash chips");
0235