[char-misc-next] misc: microchip: pci1xxxx: Add OTP/EEPROM driver for the pci1xxxx switch

Message ID 20221222054048.3080265-1-kumaravel.thiagarajan@microchip.com
State New
Headers
Series [char-misc-next] misc: microchip: pci1xxxx: Add OTP/EEPROM driver for the pci1xxxx switch |

Commit Message

Kumaravel Thiagarajan Dec. 22, 2022, 5:40 a.m. UTC
  Microchip's pci1xxxx is an unmanaged PCIe3.1a switch for consumer, industrial,
and automotive applications. This switch integrates OTP and EEPROM to enable
customization of the part in the field. This patch provides the OTP/EEPROM
driver to support the same.

Co-developed-by: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>
Signed-off-by: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>
Signed-off-by: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>
---
 MAINTAINERS                                   |   1 +
 drivers/misc/mchp_pci1xxxx/Makefile           |   2 +-
 .../misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c | 693 ++++++++++++++++++
 3 files changed, 695 insertions(+), 1 deletion(-)
 create mode 100644 drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c
  

Comments

kernel test robot Dec. 22, 2022, 2:17 a.m. UTC | #1
Hi Kumaravel,

I love your patch! Yet something to improve:

[auto build test ERROR on char-misc/char-misc-next]

url:    https://github.com/intel-lab-lkp/linux/commits/Kumaravel-Thiagarajan/misc-microchip-pci1xxxx-Add-OTP-EEPROM-driver-for-the-pci1xxxx-switch/20221222-010842
patch link:    https://lore.kernel.org/r/20221222054048.3080265-1-kumaravel.thiagarajan%40microchip.com
patch subject: [PATCH char-misc-next] misc: microchip: pci1xxxx: Add OTP/EEPROM driver for the pci1xxxx switch
config: x86_64-randconfig-a001-20221219
compiler: gcc-11 (Debian 11.3.0-8) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/1459549ab87dcfc060354d5bd591ee819de77287
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Kumaravel-Thiagarajan/misc-microchip-pci1xxxx-Add-OTP-EEPROM-driver-for-the-pci1xxxx-switch/20221222-010842
        git checkout 1459549ab87dcfc060354d5bd591ee819de77287
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=x86_64 olddefconfig
        make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   ld: vmlinux.o: in function `pci1xxxx_otp_e2p_remove':
>> drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:616: undefined reference to `blk_mq_destroy_queue'
>> ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:619: undefined reference to `del_gendisk'
>> ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:620: undefined reference to `put_disk'
>> ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:621: undefined reference to `blk_mq_free_tag_set'
   ld: vmlinux.o: in function `otp_e2p_device_create_block_device':
>> drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:523: undefined reference to `blk_mq_init_queue'
>> ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:533: undefined reference to `blk_queue_logical_block_size'
>> ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:535: undefined reference to `blk_queue_physical_block_size'
>> ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:539: undefined reference to `__blk_mq_alloc_disk'
>> ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:561: undefined reference to `set_capacity'
   ld: vmlinux.o: in function `add_disk':
>> include/linux/blkdev.h:751: undefined reference to `device_add_disk'
   ld: vmlinux.o: in function `otp_e2p_device_create_block_device':
>> drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:516: undefined reference to `blk_mq_alloc_tag_set'
   ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:569: undefined reference to `set_capacity'
   ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:591: undefined reference to `del_gendisk'
   ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:592: undefined reference to `put_disk'
>> ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:594: undefined reference to `blk_mq_destroy_queue'
   ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:596: undefined reference to `blk_mq_free_tag_set'
   ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:598: undefined reference to `del_gendisk'
   ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:599: undefined reference to `put_disk'
   ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:601: undefined reference to `blk_mq_destroy_queue'
   ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:603: undefined reference to `blk_mq_free_tag_set'
   ld: vmlinux.o: in function `OTPE2P_queue_rq':
>> drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:435: undefined reference to `blk_mq_start_request'
>> ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:437: undefined reference to `blk_mq_end_request'
   ld: vmlinux.o: in function `pci1xxxx_otp_e2p_driver_init':
>> drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:662: undefined reference to `__register_blkdev'
   ld: vmlinux.o: in function `pci1xxxx_otp_e2p_driver_exit':
>> drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:684: undefined reference to `unregister_blkdev'


vim +616 drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c

   429	
   430	static blk_status_t OTPE2P_queue_rq(struct blk_mq_hw_ctx *hctx,
   431					    const struct blk_mq_queue_data *bd)
   432	{
   433		struct request *req = bd->rq;
   434	
 > 435		blk_mq_start_request(req);
   436		if (!otp_e2P_device_transfer(req)) {
 > 437			blk_mq_end_request(req, BLK_STS_OK);
   438			return BLK_STS_OK;
   439		}
   440	
   441		return BLK_STS_IOERR;
   442	}
   443	
   444	static const struct blk_mq_ops OTPE2P_mq_ops = {
   445		.queue_rq	= OTPE2P_queue_rq,
   446	};
   447	
   448	static const struct block_device_operations otp_e2p_device_ops = {
   449		.owner = THIS_MODULE,
   450		.open = otp_e2p_device_open,
   451		.release = otp_e2p_device_release,
   452	};
   453	
   454	static int otp_e2p_device_create_block_device(struct auxiliary_device *aux_dev)
   455	{
   456		struct auxiliary_device_wrapper *aux_dev_wrapper;
   457		struct pci1xxxx_otp_e2p_device *priv;
   458		struct gp_aux_data_type *pdata;
   459		int retval = 0, i;
   460	
   461		aux_dev_wrapper = (struct auxiliary_device_wrapper *) container_of(aux_dev,
   462				  struct auxiliary_device_wrapper, aux_dev);
   463		pdata = &(aux_dev_wrapper->gp_aux_data);
   464		if (!pdata) {
   465			dev_err(&aux_dev->dev, "Invalid data in aux_dev_wrapper->gp_aux_data\n");
   466			return -EINVAL;
   467		}
   468	
   469		priv = devm_kzalloc(&aux_dev->dev, sizeof(struct pci1xxxx_otp_e2p_device),
   470				    GFP_KERNEL);
   471		if (!priv)
   472			return -ENOMEM;
   473	
   474		priv->pdev = aux_dev;
   475	
   476		dev_set_drvdata(&aux_dev->dev, priv);
   477	
   478		if (!devm_request_mem_region(&aux_dev->dev, pdata->region_start +
   479					     PERI_PF3_SYSTEM_REG_ADDR_BASE,
   480			PERI_PF3_SYSTEM_REG_LENGTH, aux_dev->name)) {
   481			dev_err(&aux_dev->dev, "can't request otpe2p region\n");
   482			return -ENOMEM;
   483		}
   484	
   485		priv->reg_base = devm_ioremap(&aux_dev->dev, pdata->region_start +
   486					      PERI_PF3_SYSTEM_REG_ADDR_BASE,
   487					      PERI_PF3_SYSTEM_REG_LENGTH);
   488		if (!priv->reg_base) {
   489			dev_err(&aux_dev->dev, "ioremap failed\n");
   490			return -ENOMEM;
   491		}
   492	
   493		priv->block_device_count = 0;
   494		do {
   495			if (check_e2p_response(priv))
   496				priv->block_device_count = 2;
   497			else
   498				priv->block_device_count = 1;
   499	
   500			priv->otp_e2p_device = devm_kzalloc(&priv->pdev->dev,
   501							    priv->block_device_count *
   502							    sizeof(struct pci1xxxx_otp_e2p_disk),
   503							    GFP_KERNEL);
   504			if (!priv->otp_e2p_device) {
   505				retval = -ENOMEM;
   506				break;
   507			}
   508	
   509			for (i = 0; i < priv->block_device_count; i++) {
   510				mutex_init(&priv->otp_e2p_device[i].lock);
   511				priv->otp_e2p_device[i].tag_set.ops = &OTPE2P_mq_ops;
   512				priv->otp_e2p_device[i].tag_set.nr_hw_queues = 1;
   513				priv->otp_e2p_device[i].tag_set.queue_depth = 16;
   514				priv->otp_e2p_device[i].tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
   515	
 > 516				retval = blk_mq_alloc_tag_set(&priv->otp_e2p_device[i].tag_set);
   517				if (retval) {
   518					dev_err(&aux_dev->dev, "blk_mq_alloc_tag_set failed\n");
   519					break;
   520				}
   521	
   522				priv->otp_e2p_device[i].queue =
 > 523					blk_mq_init_queue(&priv->otp_e2p_device[i].tag_set);
   524				if (IS_ERR(priv->otp_e2p_device[i].queue)) {
   525					retval = PTR_ERR(priv->otp_e2p_device[i].queue);
   526					priv->otp_e2p_device[i].queue = NULL;
   527					if (i)
   528						goto e2p_free_tag_set;
   529					else
   530						goto otp_free_tag_set;
   531				}
   532	
 > 533				blk_queue_logical_block_size(priv->otp_e2p_device[i].queue,
   534							     OTP_E2P_SECTOR_SIZE);
 > 535				blk_queue_physical_block_size(priv->otp_e2p_device[i].queue,
   536							      OTP_E2P_SECTOR_SIZE);
   537				priv->otp_e2p_device[i].queue->queuedata = priv;
   538				priv->otp_e2p_device[i].gd =
 > 539					blk_mq_alloc_disk(&priv->otp_e2p_device[i].tag_set,
   540					NULL);
   541				if (IS_ERR(priv->otp_e2p_device[i].gd)) {
   542					retval = PTR_ERR(priv->otp_e2p_device[i].gd);
   543					if (i)
   544						goto e2p_destroy_queue;
   545					else
   546						goto otp_destroy_queue;
   547				}
   548	
   549				priv->otp_e2p_device[i].pdev = aux_dev;
   550				priv->otp_e2p_device[i].gd->major = OTP_block_driver_major;
   551				priv->otp_e2p_device[i].gd->minors = 1;
   552				priv->otp_e2p_device[i].gd->first_minor =
   553					otp_device_count + e2p_device_count;
   554				priv->otp_e2p_device[i].gd->fops = &otp_e2p_device_ops;
   555				priv->otp_e2p_device[i].gd->private_data =
   556					&priv->otp_e2p_device[i];
   557	
   558				if (i == 0) {
   559					snprintf(priv->otp_e2p_device[i].gd->disk_name,
   560						 32, "PCI1xxxxOTP%x", otp_device_count);
 > 561					set_capacity(priv->otp_e2p_device[i].gd,
   562						     OTP_sector_count);
   563					priv->otp_e2p_device[i].disk_read_byte = otp_device_read_byte;
   564					priv->otp_e2p_device[i].disk_write_byte = otp_device_write_byte;
   565					otp_device_count++;
   566				} else {
   567					snprintf(priv->otp_e2p_device[i].gd->disk_name,
   568						 32, "PCI1xxxxE2P%x", otp_device_count - 1);
   569					set_capacity(priv->otp_e2p_device[i].gd,
   570						     E2P_sector_count);
   571					priv->otp_e2p_device[i].E2P = true;
   572					priv->otp_e2p_device[i].disk_read_byte = e2p_device_read_byte;
   573					priv->otp_e2p_device[i].disk_write_byte = e2p_device_write_byte;
   574					e2p_device_count++;
   575				}
   576	
   577				retval = add_disk(priv->otp_e2p_device[i].gd);
   578				if (retval) {
   579					if (i)
   580						goto e2p_free_disk;
   581					else
   582						goto otp_free_disk;
   583				}
   584			}
   585	
   586		} while (false);
   587	
   588		return retval;
   589	
   590	e2p_free_disk:
 > 591		del_gendisk(priv->otp_e2p_device[1].gd);
 > 592		put_disk(priv->otp_e2p_device[1].gd);
   593	e2p_destroy_queue:
 > 594		blk_mq_destroy_queue(priv->otp_e2p_device[1].queue);
   595	e2p_free_tag_set:
 > 596		blk_mq_free_tag_set(&priv->otp_e2p_device[1].tag_set);
   597	otp_free_disk:
   598		del_gendisk(priv->otp_e2p_device[0].gd);
 > 599		put_disk(priv->otp_e2p_device[0].gd);
   600	otp_destroy_queue:
   601		blk_mq_destroy_queue(priv->otp_e2p_device[0].queue);
   602	otp_free_tag_set:
   603		blk_mq_free_tag_set(&priv->otp_e2p_device[0].tag_set);
   604	
   605		return retval;
   606	}
   607	
   608	static void pci1xxxx_otp_e2p_remove(struct auxiliary_device *aux_dev)
   609	{
   610		struct pci1xxxx_otp_e2p_device *priv = dev_get_drvdata(&aux_dev->dev);
   611		int i;
   612	
   613		for (i = 0; i < priv->block_device_count; i++) {
   614	
   615			if (priv->otp_e2p_device[i].queue)
 > 616				blk_mq_destroy_queue(priv->otp_e2p_device[i].queue);
   617	
   618			if (priv->otp_e2p_device[i].gd) {
 > 619				del_gendisk(priv->otp_e2p_device[i].gd);
 > 620				put_disk(priv->otp_e2p_device[i].gd);
 > 621				blk_mq_free_tag_set(&priv->otp_e2p_device[i].tag_set);
   622			}
   623		}
   624	}
   625	
   626	static int pci1xxxx_otp_e2p_probe(struct auxiliary_device *aux_dev,
   627					  const struct auxiliary_device_id *id)
   628	{
   629		int retval;
   630	
   631		retval = otp_e2p_device_create_block_device(aux_dev);
   632		if (retval) {
   633			dev_err(&aux_dev->dev,
   634				"otp/eeprom device enumeration failed with errno = %d\n",
   635				retval);
   636			return retval;
   637		}
   638	
   639		return 0;
   640	}
   641	
   642	static const struct auxiliary_device_id pci1xxxx_otp_e2p_auxiliary_id_table[] = {
   643		{.name = "mchp_pci1xxxx_gp.gp_otp_e2p"},
   644		{},
   645	};
   646	MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_otp_e2p_auxiliary_id_table);
   647	
   648	static struct auxiliary_driver pci1xxxx_otp_e2p_driver = {
   649		.driver = {
   650			.name = "PCI1xxxxOTPE2P",
   651		},
   652		.probe = pci1xxxx_otp_e2p_probe,
   653		.remove = pci1xxxx_otp_e2p_remove,
   654		.id_table = pci1xxxx_otp_e2p_auxiliary_id_table
   655	};
   656	
   657	static int __init pci1xxxx_otp_e2p_driver_init(void)
   658	{
   659		int retval;
   660	
   661		do {
 > 662			OTP_block_driver_major = register_blkdev(OTP_block_driver_major,
   663				"OTPBlockDevice");
   664			if (OTP_block_driver_major < 0) {
   665				retval = OTP_block_driver_major;
   666				break;
   667			}
   668	
   669			block_driver_registered = 1;
   670	
   671			retval = auxiliary_driver_register(&pci1xxxx_otp_e2p_driver);
   672			if (retval)
   673				break;
   674	
   675		} while (false);
   676	
   677		return retval;
   678	}
   679	
   680	static void __exit pci1xxxx_otp_e2p_driver_exit(void)
   681	{
   682		auxiliary_driver_unregister(&pci1xxxx_otp_e2p_driver);
   683		if (block_driver_registered)
 > 684			unregister_blkdev(OTP_block_driver_major, "OTPBlockDevice");
   685	}
   686
  
kernel test robot Dec. 22, 2022, 4:59 a.m. UTC | #2
Hi Kumaravel,

I love your patch! Yet something to improve:

[auto build test ERROR on char-misc/char-misc-next]

url:    https://github.com/intel-lab-lkp/linux/commits/Kumaravel-Thiagarajan/misc-microchip-pci1xxxx-Add-OTP-EEPROM-driver-for-the-pci1xxxx-switch/20221222-010842
patch link:    https://lore.kernel.org/r/20221222054048.3080265-1-kumaravel.thiagarajan%40microchip.com
patch subject: [PATCH char-misc-next] misc: microchip: pci1xxxx: Add OTP/EEPROM driver for the pci1xxxx switch
config: i386-randconfig-a012-20221219
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/1459549ab87dcfc060354d5bd591ee819de77287
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Kumaravel-Thiagarajan/misc-microchip-pci1xxxx-Add-OTP-EEPROM-driver-for-the-pci1xxxx-switch/20221222-010842
        git checkout 1459549ab87dcfc060354d5bd591ee819de77287
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

>> ld.lld: error: undefined symbol: blk_mq_alloc_tag_set
   >>> referenced by mchp_pci1xxxx_otpe2p.c:516 (drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:516)
   >>>               drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o:(pci1xxxx_otp_e2p_probe) in archive vmlinux.a
--
>> ld.lld: error: undefined symbol: blk_mq_init_queue
   >>> referenced by mchp_pci1xxxx_otpe2p.c:523 (drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:523)
   >>>               drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o:(pci1xxxx_otp_e2p_probe) in archive vmlinux.a
--
>> ld.lld: error: undefined symbol: blk_mq_free_tag_set
   >>> referenced by mchp_pci1xxxx_otpe2p.c:596 (drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:596)
   >>>               drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o:(pci1xxxx_otp_e2p_probe) in archive vmlinux.a
   >>> referenced by mchp_pci1xxxx_otpe2p.c:603 (drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:603)
   >>>               drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o:(pci1xxxx_otp_e2p_probe) in archive vmlinux.a
   >>> referenced by mchp_pci1xxxx_otpe2p.c:621 (drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:621)
   >>>               drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o:(pci1xxxx_otp_e2p_remove) in archive vmlinux.a
--
>> ld.lld: error: undefined symbol: blk_mq_start_request
   >>> referenced by mchp_pci1xxxx_otpe2p.c:435 (drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:435)
   >>>               drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o:(OTPE2P_queue_rq) in archive vmlinux.a
--
>> ld.lld: error: undefined symbol: blk_mq_end_request
   >>> referenced by mchp_pci1xxxx_otpe2p.c:437 (drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:437)
   >>>               drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o:(OTPE2P_queue_rq) in archive vmlinux.a
--
>> ld.lld: error: undefined symbol: unregister_blkdev
   >>> referenced by mchp_pci1xxxx_otpe2p.c:684 (drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:684)
   >>>               drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o:(pci1xxxx_otp_e2p_driver_exit) in archive vmlinux.a
--
>> ld.lld: error: undefined symbol: __register_blkdev
   >>> referenced by mchp_pci1xxxx_otpe2p.c:662 (drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:662)
   >>>               drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o:(pci1xxxx_otp_e2p_driver_init) in archive vmlinux.a
--
>> ld.lld: error: undefined symbol: blk_queue_logical_block_size
   >>> referenced by mchp_pci1xxxx_otpe2p.c:533 (drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:533)
   >>>               drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o:(pci1xxxx_otp_e2p_probe) in archive vmlinux.a
--
>> ld.lld: error: undefined symbol: blk_queue_physical_block_size
   >>> referenced by mchp_pci1xxxx_otpe2p.c:535 (drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:535)
   >>>               drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o:(pci1xxxx_otp_e2p_probe) in archive vmlinux.a
--
>> ld.lld: error: undefined symbol: __blk_mq_alloc_disk
   >>> referenced by mchp_pci1xxxx_otpe2p.c:539 (drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:539)
   >>>               drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o:(pci1xxxx_otp_e2p_probe) in archive vmlinux.a
--
>> ld.lld: error: undefined symbol: set_capacity
   >>> referenced by mchp_pci1xxxx_otpe2p.c:561 (drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:561)
   >>>               drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o:(pci1xxxx_otp_e2p_probe) in archive vmlinux.a
   >>> referenced by mchp_pci1xxxx_otpe2p.c:569 (drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c:569)
   >>>               drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o:(pci1xxxx_otp_e2p_probe) in archive vmlinux.a
..
  
kernel test robot Dec. 22, 2022, 6:51 a.m. UTC | #3
Hi Kumaravel,

I love your patch! Yet something to improve:

[auto build test ERROR on char-misc/char-misc-next]

url:    https://github.com/intel-lab-lkp/linux/commits/Kumaravel-Thiagarajan/misc-microchip-pci1xxxx-Add-OTP-EEPROM-driver-for-the-pci1xxxx-switch/20221222-010842
patch link:    https://lore.kernel.org/r/20221222054048.3080265-1-kumaravel.thiagarajan%40microchip.com
patch subject: [PATCH char-misc-next] misc: microchip: pci1xxxx: Add OTP/EEPROM driver for the pci1xxxx switch
config: s390-randconfig-r044-20221219
compiler: clang version 16.0.0 (https://github.com/llvm/llvm-project 98b13979fb05f3ed288a900deb843e7b27589e58)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install s390 cross compiling tool for clang build
        # apt-get install binutils-s390x-linux-gnu
        # https://github.com/intel-lab-lkp/linux/commit/1459549ab87dcfc060354d5bd591ee819de77287
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Kumaravel-Thiagarajan/misc-microchip-pci1xxxx-Add-OTP-EEPROM-driver-for-the-pci1xxxx-switch/20221222-010842
        git checkout 1459549ab87dcfc060354d5bd591ee819de77287
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=s390 olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=s390 SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   s390x-linux-ld: DWARF error: invalid or unhandled FORM value: 0x23
   drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o: in function `pci1xxxx_otp_e2p_probe':
>> mchp_pci1xxxx_otpe2p.c:(.text+0x38c): undefined reference to `blk_mq_alloc_tag_set'
>> s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0x3b6): undefined reference to `blk_mq_init_queue'
>> s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0x458): undefined reference to `blk_queue_logical_block_size'
>> s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0x482): undefined reference to `blk_queue_physical_block_size'
>> s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0x4d6): undefined reference to `__blk_mq_alloc_disk'
>> s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0x706): undefined reference to `set_capacity'
   s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0x77c): undefined reference to `set_capacity'
>> s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0x818): undefined reference to `device_add_disk'
>> s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0x8ba): undefined reference to `del_gendisk'
>> s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0x8e0): undefined reference to `put_disk'
>> s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0x90a): undefined reference to `blk_mq_destroy_queue'
>> s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0x924): undefined reference to `blk_mq_free_tag_set'
   s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0x94a): undefined reference to `del_gendisk'
   s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0x970): undefined reference to `put_disk'
   s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0x996): undefined reference to `blk_mq_destroy_queue'
   s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0x9ac): undefined reference to `blk_mq_free_tag_set'
   s390x-linux-ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o: in function `pci1xxxx_otp_e2p_remove':
>> mchp_pci1xxxx_otpe2p.c:(.text+0xab6): undefined reference to `blk_mq_destroy_queue'
   s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0xae0): undefined reference to `del_gendisk'
   s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0xb06): undefined reference to `put_disk'
   s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0xb22): undefined reference to `blk_mq_free_tag_set'
   s390x-linux-ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o: in function `OTPE2P_queue_rq':
>> mchp_pci1xxxx_otpe2p.c:(.text+0x154e): undefined reference to `blk_mq_start_request'
>> s390x-linux-ld: mchp_pci1xxxx_otpe2p.c:(.text+0x1840): undefined reference to `blk_mq_end_request'
   s390x-linux-ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o: in function `pci1xxxx_otp_e2p_driver_exit':
>> mchp_pci1xxxx_otpe2p.c:(.exit.text+0x58): undefined reference to `unregister_blkdev'
   s390x-linux-ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.o: in function `pci1xxxx_otp_e2p_driver_init':
>> mchp_pci1xxxx_otpe2p.c:(.init.text+0x38): undefined reference to `__register_blkdev'
   pahole: .tmp_vmlinux.btf: No such file or directory
   .btf.vmlinux.bin.o: file not recognized: file format not recognized
  

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 9886aa1a4403..195af3ac451d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13614,6 +13614,7 @@  S:	Supported
 F:	drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c
 F:	drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.h
 F:	drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c
+F:	drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c
 
 MICROCHIP OTPC DRIVER
 M:	Claudiu Beznea <claudiu.beznea@microchip.com>
diff --git a/drivers/misc/mchp_pci1xxxx/Makefile b/drivers/misc/mchp_pci1xxxx/Makefile
index fc4615cfe28b..ae31251dab37 100644
--- a/drivers/misc/mchp_pci1xxxx/Makefile
+++ b/drivers/misc/mchp_pci1xxxx/Makefile
@@ -1 +1 @@ 
-obj-$(CONFIG_GP_PCI1XXXX) := mchp_pci1xxxx_gp.o mchp_pci1xxxx_gpio.o
+obj-$(CONFIG_GP_PCI1XXXX) := mchp_pci1xxxx_gp.o mchp_pci1xxxx_gpio.o mchp_pci1xxxx_otpe2p.o
diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c
new file mode 100644
index 000000000000..03a537d45873
--- /dev/null
+++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c
@@ -0,0 +1,693 @@ 
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2022 Microchip Technology Inc.
+// PCI1xxxx OTP/EEPROM driver
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bio.h>
+#include <linux/blkdev.h>
+#include <linux/blk-mq.h>
+#include <linux/delay.h>
+#include <linux/gpio/driver.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+#include "mchp_pci1xxxx_gp.h"
+
+#define PERI_PF3_SYSTEM_REG_ADDR_BASE	(0x2000)
+#define PERI_PF3_SYSTEM_REG_LENGTH	(0x4000)
+
+#define CONFIG_REG_ADDR_BASE		(0)
+#define EEPROM_REG_ADDR_BASE		(0x0E00)
+#define OTP_REG_ADDR_BASE		(0x1000)
+
+#define MMAP_CFG_OFFSET(x)		(CONFIG_REG_ADDR_BASE + (x))
+
+#define CFG_SYS_LOCK_OFFSET		(0xA0)
+#define CFG_SYS_LOCK_PF3		BIT(5)
+
+#define MMAP_OTP_OFFSET(x)		(OTP_REG_ADDR_BASE + (x))
+
+#define OTP_PWR_DN_OFFSET		(0x00)
+#define OTP_ADDR_HIGH_OFFSET		(0x04)
+#define OTP_ADDR_LOW_OFFSET		(0x08)
+#define	OTP_ADDR_BITS_OFFSET		(0x0C)
+#define OTP_PRGM_DATA_OFFSET		(0x10)
+#define OTP_PRGM_MODE_OFFSET		(0x14)
+#define OTP_RD_DATA_OFFSET		(0x18)
+#define OTP_FUNC_CMD_OFFSET		(0x20)
+#define OTP_TEST_CMD_OFFSET		(0x24)
+#define OTP_CMD_GO_OFFSET		(0x28)
+#define OTP_PASS_FAIL_OFFSET		(0x2C)
+#define OTP_STATUS_OFFSET		(0x30)
+#define OTP_MAX_PRG_OFFSET		(0x34)
+#define OTP_RSTB_PW_OFFSET		(0x50)
+#define OTP_PGM_PW_OFFSET		(0x60)
+#define OTP_READ_PW_OFFSET		(0x70)
+
+#define OTP_PWR_DN_BIT			BIT(0)
+#define OTP_CMD_GO_BIT			BIT(0)
+#define OTP_PGM_MODE_BYTE_BIT		BIT(0)
+#define OTP_STATUS_BUSY_BIT		BIT(0)
+#define OTP_FUNC_PGM_BIT		BIT(1)
+#define OTP_FUNC_RD_BIT			BIT(0)
+
+#define OTP_RW_TIMEOUT_MILLISECONDS	(5)
+
+#define MMAP_EEPROM_OFFSET(x)		(EEPROM_REG_ADDR_BASE + (x))
+
+#define E2P_CMD_REG			(0x00)
+#define E2P_DATA_REG			(0x04)
+#define E2P_CFG_REG			(0x08)
+#define E2P_PAD_CTL_REG			(0x0C)
+
+#define E2P_CMD_EPC_BUSY_BIT		BIT(31)
+#define E2P_CMD_EPC_TIMEOUT_BIT		BIT(17)
+#define E2P_CMD_EPC_WRITE		(BIT(29) | BIT(28))
+
+#define E2P_CFG_BAUD_RATE_100KHZ	BIT(9)
+#define E2P_CFG_SIZE_SEL		BIT(12)
+#define E2P_CFG_PULSE_WIDTH_100KHZ	(BIT(17) | BIT(16))
+#define OTP_E2P_SECTOR_SIZE		(512)
+#define OTP_SIZE_IN_BYTES		(8 * 1024)
+#define E2P_SIZE_IN_BYTES		(8 * 1024)
+
+struct pci1xxxx_otp_e2p_device {
+	struct pci1xxxx_otp_e2p_disk *otp_e2p_device;
+	struct auxiliary_device *pdev;
+	void __iomem *reg_base;
+	int block_device_count;
+};
+
+struct pci1xxxx_otp_e2p_disk {
+	struct blk_mq_tag_set tag_set;
+	struct auxiliary_device *pdev;
+	struct request_queue *queue;
+	struct mutex  lock;
+	struct gendisk *gd;
+	bool E2P;
+	int (*disk_write_byte)(struct pci1xxxx_otp_e2p_device *priv,
+		unsigned long byte_offset, u8 value);
+	int (*disk_read_byte)(struct pci1xxxx_otp_e2p_device *priv,
+		unsigned long byte_offset, u8 *data);
+};
+
+static int OTP_sector_count = OTP_SIZE_IN_BYTES / OTP_E2P_SECTOR_SIZE;
+static int E2P_sector_count = E2P_SIZE_IN_BYTES / OTP_E2P_SECTOR_SIZE;
+static int otp_device_count, e2p_device_count;
+static int block_driver_registered;
+static int OTP_block_driver_major;
+
+static void otp_device_set_address(struct pci1xxxx_otp_e2p_device *priv, u16 address)
+{
+	u32 lo, hi;
+
+	lo = address & 0xFF;
+	hi = (address & 0x1f00) >> 8;
+	writel(lo, priv->reg_base + MMAP_OTP_OFFSET(OTP_ADDR_LOW_OFFSET));
+	writel(hi, priv->reg_base + MMAP_OTP_OFFSET(OTP_ADDR_HIGH_OFFSET));
+}
+
+static int set_sys_lock(struct pci1xxxx_otp_e2p_device *priv)
+{
+	void __iomem *p = priv->reg_base + MMAP_CFG_OFFSET(CFG_SYS_LOCK_OFFSET);
+	u8 data;
+
+	writel(CFG_SYS_LOCK_PF3, p);
+	data = readl(p);
+	if (data != CFG_SYS_LOCK_PF3)
+		return -EPERM;
+
+	return 0;
+}
+
+static int release_sys_lock(struct pci1xxxx_otp_e2p_device *priv)
+{
+	void __iomem *p = priv->reg_base + MMAP_CFG_OFFSET(CFG_SYS_LOCK_OFFSET);
+	u8 data;
+
+	data = readl(p);
+	if (data != CFG_SYS_LOCK_PF3)
+		return 0;
+
+	writel(0, p);
+
+	data = readl(p);
+	if (data & CFG_SYS_LOCK_PF3)
+		return -EPERM;
+
+	return 0;
+}
+
+static int otp_e2p_device_open(struct block_device *bdev, fmode_t mode)
+{
+	struct pci1xxxx_otp_e2p_disk *disk_priv;
+	struct pci1xxxx_otp_e2p_device *priv;
+	struct auxiliary_device *pdev;
+	int retval = 0;
+	u8 data;
+
+	disk_priv = (struct pci1xxxx_otp_e2p_disk *)bdev->bd_disk->private_data;
+	pdev = (struct auxiliary_device *)disk_priv->pdev;
+	priv = dev_get_drvdata(&pdev->dev);
+
+	mutex_lock(&disk_priv->lock);
+
+	do {
+		retval = set_sys_lock(priv);
+		if (retval)
+			break;
+
+		if (!disk_priv->E2P) {
+			data = readl(priv->reg_base +
+				     MMAP_OTP_OFFSET(OTP_PWR_DN_OFFSET));
+			writel((data & ~OTP_PWR_DN_BIT), priv->reg_base +
+				MMAP_OTP_OFFSET(OTP_PWR_DN_OFFSET));
+		}
+	} while (false);
+
+	mutex_unlock(&disk_priv->lock);
+
+	return retval;
+}
+
+static void otp_e2p_device_release(struct gendisk *disk, fmode_t mode)
+{
+	struct pci1xxxx_otp_e2p_disk *disk_priv;
+	struct pci1xxxx_otp_e2p_device *priv;
+	u8 data;
+
+	disk_priv = (struct pci1xxxx_otp_e2p_disk *)disk->private_data;
+	priv = dev_get_drvdata(&disk_priv->pdev->dev);
+
+	mutex_lock(&disk_priv->lock);
+
+	if (!disk_priv->E2P) {
+		data = readl(priv->reg_base + MMAP_OTP_OFFSET(OTP_PWR_DN_OFFSET));
+		writel((data | OTP_PWR_DN_BIT), priv->reg_base +
+			MMAP_OTP_OFFSET(OTP_PWR_DN_OFFSET));
+	}
+	release_sys_lock(priv);
+
+	mutex_unlock(&disk_priv->lock);
+}
+
+static int e2p_device_write_byte(struct pci1xxxx_otp_e2p_device *priv,
+				 unsigned long byte_offset, u8 value)
+{
+	u32 data;
+
+	/* Write the value into E2P_DATA_REG register */
+	writel(value, priv->reg_base + MMAP_EEPROM_OFFSET(E2P_DATA_REG));
+	data = E2P_CMD_EPC_TIMEOUT_BIT | E2P_CMD_EPC_WRITE | byte_offset;
+
+	/* Write the data into E2P_CMD_REG register */
+	writel(data, priv->reg_base + MMAP_EEPROM_OFFSET(E2P_CMD_REG));
+
+	/* Set the EPC_BUSY bit of E2P_CMD_REG register */
+	writel(E2P_CMD_EPC_BUSY_BIT | data, priv->reg_base +
+	       MMAP_EEPROM_OFFSET(E2P_CMD_REG));
+
+	/* Wait for the EPC_BUSY bit to get cleared */
+	do {
+		data = readl(priv->reg_base + MMAP_EEPROM_OFFSET(E2P_CMD_REG));
+	} while (data & E2P_CMD_EPC_BUSY_BIT);
+
+	if (data & E2P_CMD_EPC_TIMEOUT_BIT) {
+		dev_err(&priv->pdev->dev, "%s timed out\n", __func__);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int e2p_device_read_byte(struct pci1xxxx_otp_e2p_device *priv,
+				unsigned long byte_offset, u8 *data)
+{
+	u32 regval;
+
+	/*
+	 * Write the byte offset into the EPC_ADDRESS field of E2P_CMD_REG
+	 * register
+	 */
+	writel(byte_offset, priv->reg_base + MMAP_EEPROM_OFFSET(E2P_CMD_REG));
+
+	/* Set the EPC_BUSY bit of E2P_CMD_REG register */
+	writel(E2P_CMD_EPC_BUSY_BIT | byte_offset, priv->reg_base +
+	       MMAP_EEPROM_OFFSET(E2P_CMD_REG));
+
+	/* Wait for the EPC_BUSY bit to get cleared */
+	do {
+		regval = readl(priv->reg_base + MMAP_EEPROM_OFFSET(E2P_CMD_REG));
+	} while (regval & E2P_CMD_EPC_BUSY_BIT);
+
+	if (regval & E2P_CMD_EPC_TIMEOUT_BIT) {
+		dev_err(&priv->pdev->dev, "%s timed out\n", __func__);
+		return -EFAULT;
+	}
+
+	/* Read the contents from the E2P_DATA_REG */
+	*data = readl(priv->reg_base + MMAP_EEPROM_OFFSET(E2P_DATA_REG));
+	return 0;
+}
+
+static bool check_e2p_response(struct pci1xxxx_otp_e2p_device *priv)
+{
+	u32 data;
+
+	if (set_sys_lock(priv))
+		return false;
+
+	writel((E2P_CFG_PULSE_WIDTH_100KHZ | E2P_CFG_SIZE_SEL |
+		E2P_CFG_BAUD_RATE_100KHZ), priv->reg_base +
+		MMAP_EEPROM_OFFSET(E2P_CFG_REG));
+
+	/*
+	 * Write the byte offset into the EPC_ADDRESS field of E2P_CMD_REG
+	 * register
+	 */
+	writel(E2P_CMD_EPC_TIMEOUT_BIT, priv->reg_base +
+	       MMAP_EEPROM_OFFSET(E2P_CMD_REG));
+
+	/* Set the EPC_BUSY bit of E2P_CMD_REG register */
+	writel(E2P_CMD_EPC_BUSY_BIT, priv->reg_base +
+	       MMAP_EEPROM_OFFSET(E2P_CMD_REG));
+
+	/* Wait for the EPC_BUSY bit to get cleared or timeout bit to get set*/
+	do {
+		data = readl(priv->reg_base + MMAP_EEPROM_OFFSET(E2P_CMD_REG));
+	} while (data & E2P_CMD_EPC_BUSY_BIT);
+
+	/* If EPC_TIMEOUT is set, then the EEPROM is not responsive */
+	release_sys_lock(priv);
+
+	if (data & E2P_CMD_EPC_TIMEOUT_BIT) {
+		dev_err(&priv->pdev->dev,
+			"EPC_Timeout, EEPROM is unresponsive: %x\n", data);
+		return false;
+	}
+
+	return true;
+}
+
+static int otp_device_write_byte(struct pci1xxxx_otp_e2p_device *priv,
+				 unsigned long byte_offset, u8 value)
+{
+	unsigned long j0, j1, delay;
+	u8 data;
+
+	if (!value)
+		return 0;
+
+	otp_device_set_address(priv, (u16)byte_offset);
+
+	/* Set OTP_PGM_MODE_BYTE command bit in OTP_PRGM_MODE register */
+	data = readl(priv->reg_base + MMAP_OTP_OFFSET(OTP_PRGM_MODE_OFFSET));
+	writel((data | OTP_PGM_MODE_BYTE_BIT), priv->reg_base +
+		MMAP_OTP_OFFSET(OTP_PRGM_MODE_OFFSET));
+
+	/* Write the value to program into OTP_PRGM_DATA register */
+	writel(value, priv->reg_base + MMAP_OTP_OFFSET(OTP_PRGM_DATA_OFFSET));
+
+	/* Set OTP_PROGRAM command bit in OTP_FUNC_CMD register */
+	data = readl(priv->reg_base + MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));
+	writel((data | OTP_FUNC_PGM_BIT), priv->reg_base +
+		MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));
+
+	/* Set OTP_GO command bit in OTP_CMD_GO register */
+	data = readl(priv->reg_base + MMAP_OTP_OFFSET(OTP_CMD_GO_OFFSET));
+	writel((data | OTP_CMD_GO_BIT), priv->reg_base +
+		MMAP_OTP_OFFSET(OTP_CMD_GO_OFFSET));
+	delay = msecs_to_jiffies(OTP_RW_TIMEOUT_MILLISECONDS);
+	j0 = jiffies;
+	j1 = j0 + delay;
+
+	/* Wait for the OTP_BUSY bit to get cleared in OTP_STATUS register */
+	do {
+		data = readl(priv->reg_base + MMAP_OTP_OFFSET(OTP_STATUS_OFFSET));
+	} while ((data & OTP_STATUS_BUSY_BIT) && (time_before(jiffies, j1)));
+
+	if (data & OTP_STATUS_BUSY_BIT) {
+		dev_err(&priv->pdev->dev, "%s timed out\n", __func__);
+		return -EFAULT;
+	}
+
+	/* Read the result from OTP_RD_DATA register */
+	data = readl(priv->reg_base + MMAP_OTP_OFFSET(OTP_PASS_FAIL_OFFSET));
+	if (data & 0x02)
+		return 0;
+
+	dev_err(&priv->pdev->dev, "%s write read mismatch 0x%x\n", __func__, data);
+	return -EFAULT;
+}
+
+static int otp_device_read_byte(struct pci1xxxx_otp_e2p_device *priv,
+				unsigned long byte_offset, u8 *data)
+{
+	unsigned long j0, j1, delay;
+
+	otp_device_set_address(priv, (u16)byte_offset);
+
+	/* Set OTP_READ command bit in OTP_FUNC_CMD register */
+	*data = readl(priv->reg_base + MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));
+	writel((*data | OTP_FUNC_RD_BIT), priv->reg_base +
+		MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));
+
+	/* Set OTP_GO command bit in OTP_CMD_GO register */
+	*data = readl(priv->reg_base + MMAP_OTP_OFFSET(OTP_CMD_GO_OFFSET));
+	writel((*data | OTP_CMD_GO_BIT), priv->reg_base +
+		MMAP_OTP_OFFSET(OTP_CMD_GO_OFFSET));
+	delay = msecs_to_jiffies(OTP_RW_TIMEOUT_MILLISECONDS);
+	j0 = jiffies;
+	j1 = j0 + delay;
+
+	/* Wait for OTP_BUSY bit to get cleared in OTP_STATUS */
+	do {
+		*data = readl(priv->reg_base + MMAP_OTP_OFFSET(OTP_STATUS_OFFSET));
+	} while ((*data & OTP_STATUS_BUSY_BIT) && (time_before(jiffies, j1)));
+
+	if (*data & OTP_STATUS_BUSY_BIT) {
+		dev_err(&priv->pdev->dev, "%s timed out\n", __func__);
+		return -EFAULT;
+	}
+
+	/* Read the result from OTP_RD_DATA register */
+	*data = readl(priv->reg_base + MMAP_OTP_OFFSET(OTP_RD_DATA_OFFSET));
+	return 0;
+}
+
+static int otp_e2P_device_transfer(struct request *req)
+{
+	struct pci1xxxx_otp_e2p_disk *disk_priv;
+	struct pci1xxxx_otp_e2p_device *priv;
+	unsigned long sector;
+	unsigned long nsect;
+	long byte_offset;
+	int retval;
+	u8 *buffer;
+	int write;
+	int i, j;
+
+	sector = blk_rq_pos(req);
+	nsect = blk_rq_cur_sectors(req);
+	buffer = bio_data(req->bio);
+	write = rq_data_dir(req);
+	disk_priv = (struct pci1xxxx_otp_e2p_disk *)req->q->disk->private_data;
+	priv = dev_get_drvdata(&disk_priv->pdev->dev);
+
+	if (write) {
+		for (i = 0; i < nsect; i++) {
+			byte_offset = (sector + i) * OTP_E2P_SECTOR_SIZE;
+			for (j = 0; j < OTP_E2P_SECTOR_SIZE; j++) {
+				retval = disk_priv->disk_write_byte(priv,
+								    byte_offset + j,
+								    *buffer);
+				if (retval)
+					return retval;
+
+				buffer++;
+			}
+		}
+	} else {
+		for (i = 0; i < nsect; i++) {
+			byte_offset = (sector + i) * OTP_E2P_SECTOR_SIZE;
+			for (j = 0; j < OTP_E2P_SECTOR_SIZE; j++) {
+				retval = disk_priv->disk_read_byte(priv,
+								   byte_offset + j,
+								   buffer);
+				if (retval)
+					return retval;
+
+				buffer++;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static blk_status_t OTPE2P_queue_rq(struct blk_mq_hw_ctx *hctx,
+				    const struct blk_mq_queue_data *bd)
+{
+	struct request *req = bd->rq;
+
+	blk_mq_start_request(req);
+	if (!otp_e2P_device_transfer(req)) {
+		blk_mq_end_request(req, BLK_STS_OK);
+		return BLK_STS_OK;
+	}
+
+	return BLK_STS_IOERR;
+}
+
+static const struct blk_mq_ops OTPE2P_mq_ops = {
+	.queue_rq	= OTPE2P_queue_rq,
+};
+
+static const struct block_device_operations otp_e2p_device_ops = {
+	.owner = THIS_MODULE,
+	.open = otp_e2p_device_open,
+	.release = otp_e2p_device_release,
+};
+
+static int otp_e2p_device_create_block_device(struct auxiliary_device *aux_dev)
+{
+	struct auxiliary_device_wrapper *aux_dev_wrapper;
+	struct pci1xxxx_otp_e2p_device *priv;
+	struct gp_aux_data_type *pdata;
+	int retval = 0, i;
+
+	aux_dev_wrapper = (struct auxiliary_device_wrapper *) container_of(aux_dev,
+			  struct auxiliary_device_wrapper, aux_dev);
+	pdata = &(aux_dev_wrapper->gp_aux_data);
+	if (!pdata) {
+		dev_err(&aux_dev->dev, "Invalid data in aux_dev_wrapper->gp_aux_data\n");
+		return -EINVAL;
+	}
+
+	priv = devm_kzalloc(&aux_dev->dev, sizeof(struct pci1xxxx_otp_e2p_device),
+			    GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->pdev = aux_dev;
+
+	dev_set_drvdata(&aux_dev->dev, priv);
+
+	if (!devm_request_mem_region(&aux_dev->dev, pdata->region_start +
+				     PERI_PF3_SYSTEM_REG_ADDR_BASE,
+		PERI_PF3_SYSTEM_REG_LENGTH, aux_dev->name)) {
+		dev_err(&aux_dev->dev, "can't request otpe2p region\n");
+		return -ENOMEM;
+	}
+
+	priv->reg_base = devm_ioremap(&aux_dev->dev, pdata->region_start +
+				      PERI_PF3_SYSTEM_REG_ADDR_BASE,
+				      PERI_PF3_SYSTEM_REG_LENGTH);
+	if (!priv->reg_base) {
+		dev_err(&aux_dev->dev, "ioremap failed\n");
+		return -ENOMEM;
+	}
+
+	priv->block_device_count = 0;
+	do {
+		if (check_e2p_response(priv))
+			priv->block_device_count = 2;
+		else
+			priv->block_device_count = 1;
+
+		priv->otp_e2p_device = devm_kzalloc(&priv->pdev->dev,
+						    priv->block_device_count *
+						    sizeof(struct pci1xxxx_otp_e2p_disk),
+						    GFP_KERNEL);
+		if (!priv->otp_e2p_device) {
+			retval = -ENOMEM;
+			break;
+		}
+
+		for (i = 0; i < priv->block_device_count; i++) {
+			mutex_init(&priv->otp_e2p_device[i].lock);
+			priv->otp_e2p_device[i].tag_set.ops = &OTPE2P_mq_ops;
+			priv->otp_e2p_device[i].tag_set.nr_hw_queues = 1;
+			priv->otp_e2p_device[i].tag_set.queue_depth = 16;
+			priv->otp_e2p_device[i].tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
+
+			retval = blk_mq_alloc_tag_set(&priv->otp_e2p_device[i].tag_set);
+			if (retval) {
+				dev_err(&aux_dev->dev, "blk_mq_alloc_tag_set failed\n");
+				break;
+			}
+
+			priv->otp_e2p_device[i].queue =
+				blk_mq_init_queue(&priv->otp_e2p_device[i].tag_set);
+			if (IS_ERR(priv->otp_e2p_device[i].queue)) {
+				retval = PTR_ERR(priv->otp_e2p_device[i].queue);
+				priv->otp_e2p_device[i].queue = NULL;
+				if (i)
+					goto e2p_free_tag_set;
+				else
+					goto otp_free_tag_set;
+			}
+
+			blk_queue_logical_block_size(priv->otp_e2p_device[i].queue,
+						     OTP_E2P_SECTOR_SIZE);
+			blk_queue_physical_block_size(priv->otp_e2p_device[i].queue,
+						      OTP_E2P_SECTOR_SIZE);
+			priv->otp_e2p_device[i].queue->queuedata = priv;
+			priv->otp_e2p_device[i].gd =
+				blk_mq_alloc_disk(&priv->otp_e2p_device[i].tag_set,
+				NULL);
+			if (IS_ERR(priv->otp_e2p_device[i].gd)) {
+				retval = PTR_ERR(priv->otp_e2p_device[i].gd);
+				if (i)
+					goto e2p_destroy_queue;
+				else
+					goto otp_destroy_queue;
+			}
+
+			priv->otp_e2p_device[i].pdev = aux_dev;
+			priv->otp_e2p_device[i].gd->major = OTP_block_driver_major;
+			priv->otp_e2p_device[i].gd->minors = 1;
+			priv->otp_e2p_device[i].gd->first_minor =
+				otp_device_count + e2p_device_count;
+			priv->otp_e2p_device[i].gd->fops = &otp_e2p_device_ops;
+			priv->otp_e2p_device[i].gd->private_data =
+				&priv->otp_e2p_device[i];
+
+			if (i == 0) {
+				snprintf(priv->otp_e2p_device[i].gd->disk_name,
+					 32, "PCI1xxxxOTP%x", otp_device_count);
+				set_capacity(priv->otp_e2p_device[i].gd,
+					     OTP_sector_count);
+				priv->otp_e2p_device[i].disk_read_byte = otp_device_read_byte;
+				priv->otp_e2p_device[i].disk_write_byte = otp_device_write_byte;
+				otp_device_count++;
+			} else {
+				snprintf(priv->otp_e2p_device[i].gd->disk_name,
+					 32, "PCI1xxxxE2P%x", otp_device_count - 1);
+				set_capacity(priv->otp_e2p_device[i].gd,
+					     E2P_sector_count);
+				priv->otp_e2p_device[i].E2P = true;
+				priv->otp_e2p_device[i].disk_read_byte = e2p_device_read_byte;
+				priv->otp_e2p_device[i].disk_write_byte = e2p_device_write_byte;
+				e2p_device_count++;
+			}
+
+			retval = add_disk(priv->otp_e2p_device[i].gd);
+			if (retval) {
+				if (i)
+					goto e2p_free_disk;
+				else
+					goto otp_free_disk;
+			}
+		}
+
+	} while (false);
+
+	return retval;
+
+e2p_free_disk:
+	del_gendisk(priv->otp_e2p_device[1].gd);
+	put_disk(priv->otp_e2p_device[1].gd);
+e2p_destroy_queue:
+	blk_mq_destroy_queue(priv->otp_e2p_device[1].queue);
+e2p_free_tag_set:
+	blk_mq_free_tag_set(&priv->otp_e2p_device[1].tag_set);
+otp_free_disk:
+	del_gendisk(priv->otp_e2p_device[0].gd);
+	put_disk(priv->otp_e2p_device[0].gd);
+otp_destroy_queue:
+	blk_mq_destroy_queue(priv->otp_e2p_device[0].queue);
+otp_free_tag_set:
+	blk_mq_free_tag_set(&priv->otp_e2p_device[0].tag_set);
+
+	return retval;
+}
+
+static void pci1xxxx_otp_e2p_remove(struct auxiliary_device *aux_dev)
+{
+	struct pci1xxxx_otp_e2p_device *priv = dev_get_drvdata(&aux_dev->dev);
+	int i;
+
+	for (i = 0; i < priv->block_device_count; i++) {
+
+		if (priv->otp_e2p_device[i].queue)
+			blk_mq_destroy_queue(priv->otp_e2p_device[i].queue);
+
+		if (priv->otp_e2p_device[i].gd) {
+			del_gendisk(priv->otp_e2p_device[i].gd);
+			put_disk(priv->otp_e2p_device[i].gd);
+			blk_mq_free_tag_set(&priv->otp_e2p_device[i].tag_set);
+		}
+	}
+}
+
+static int pci1xxxx_otp_e2p_probe(struct auxiliary_device *aux_dev,
+				  const struct auxiliary_device_id *id)
+{
+	int retval;
+
+	retval = otp_e2p_device_create_block_device(aux_dev);
+	if (retval) {
+		dev_err(&aux_dev->dev,
+			"otp/eeprom device enumeration failed with errno = %d\n",
+			retval);
+		return retval;
+	}
+
+	return 0;
+}
+
+static const struct auxiliary_device_id pci1xxxx_otp_e2p_auxiliary_id_table[] = {
+	{.name = "mchp_pci1xxxx_gp.gp_otp_e2p"},
+	{},
+};
+MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_otp_e2p_auxiliary_id_table);
+
+static struct auxiliary_driver pci1xxxx_otp_e2p_driver = {
+	.driver = {
+		.name = "PCI1xxxxOTPE2P",
+	},
+	.probe = pci1xxxx_otp_e2p_probe,
+	.remove = pci1xxxx_otp_e2p_remove,
+	.id_table = pci1xxxx_otp_e2p_auxiliary_id_table
+};
+
+static int __init pci1xxxx_otp_e2p_driver_init(void)
+{
+	int retval;
+
+	do {
+		OTP_block_driver_major = register_blkdev(OTP_block_driver_major,
+			"OTPBlockDevice");
+		if (OTP_block_driver_major < 0) {
+			retval = OTP_block_driver_major;
+			break;
+		}
+
+		block_driver_registered = 1;
+
+		retval = auxiliary_driver_register(&pci1xxxx_otp_e2p_driver);
+		if (retval)
+			break;
+
+	} while (false);
+
+	return retval;
+}
+
+static void __exit pci1xxxx_otp_e2p_driver_exit(void)
+{
+	auxiliary_driver_unregister(&pci1xxxx_otp_e2p_driver);
+	if (block_driver_registered)
+		unregister_blkdev(OTP_block_driver_major, "OTPBlockDevice");
+}
+
+module_init(pci1xxxx_otp_e2p_driver_init);
+module_exit(pci1xxxx_otp_e2p_driver_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>");
+MODULE_AUTHOR("Tharun Kumar P<tharunkumar.pasumarthi@microchip.com>");
+MODULE_DESCRIPTION("Microchip Technology Inc. PCI1xxxx OTP EEPROM programmer");