[char-misc-next] misc: microchip: pci1xxxx: Add OTP/EEPROM driver for the pci1xxxx switch
Commit Message
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
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
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
..
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
@@ -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>
@@ -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
new file mode 100644
@@ -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");