From nobody Mon Sep 15 09:27:28 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 48528C54EBC for ; Thu, 12 Jan 2023 07:31:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236659AbjALHbT (ORCPT ); Thu, 12 Jan 2023 02:31:19 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54908 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234409AbjALHbP (ORCPT ); Thu, 12 Jan 2023 02:31:15 -0500 Received: from esa.microchip.iphmx.com (esa.microchip.iphmx.com [68.232.153.233]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A7212373A6; Wed, 11 Jan 2023 23:31:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1673508672; x=1705044672; h=from:to:cc:subject:date:message-id:mime-version: content-transfer-encoding; bh=Bqzn6ImxaAXnwgSAwrV4k39iNA6Qp8LULG1FMm/NeE4=; b=0BX8G8x1CRvZ8i1iZYXzZ+n6GKK26SiblQLGUaeSdNyQ959qN0rHf3wl A746mo/DvVGjri92JPJf4dO/LwQ35oPSNViVlv160FL3x9MOKNafMaRW7 44v2uA9jx6GJiuylG1Dj6zM2zS9j4FO2vx2zi3uGmd4xkjzMVuLZrhS3+ ysuaQxzVlRZ3C91cGURYdLK2sCeRQSvXnetXZAqdLTspiOAMO8FUHA7WJ hpAGEXjZlQI5pYyr+pIgKLDR4n1ypMnoVzIx/KBM/DdB845GFKa+EgLVI Rz7tQnXonhzj5D4Zn7bj6MGceAcPDLnege7AVmr9jjqEcFNrqBWxMmGnF w==; X-IronPort-AV: E=Sophos;i="5.96,319,1665471600"; d="scan'208";a="196323884" Received: from unknown (HELO email.microchip.com) ([170.129.1.10]) by esa3.microchip.iphmx.com with ESMTP/TLS/AES256-SHA256; 12 Jan 2023 00:31:11 -0700 Received: from chn-vm-ex04.mchp-main.com (10.10.85.152) by chn-vm-ex01.mchp-main.com (10.10.85.143) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.16; Thu, 12 Jan 2023 00:31:11 -0700 Received: from CHE-LT-UNGSOFTWARE.microchip.com (10.10.115.15) by chn-vm-ex04.mchp-main.com (10.10.85.152) with Microsoft SMTP Server id 15.1.2507.16 via Frontend Transport; Thu, 12 Jan 2023 00:31:08 -0700 From: Kumaravel Thiagarajan To: CC: , , , , "kernel test robot" , Tharun Kumar P Subject: [PATCH v2 char-misc-next] misc: microchip: pci1xxxx: Add OTP/EEPROM driver for the pci1xxxx switch Date: Fri, 13 Jan 2023 01:36:19 +0530 Message-ID: <20230112200619.2336505-1-kumaravel.thiagarajan@microchip.com> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Microchip's pci1xxxx is an unmanaged PCIe3.1a switch for consumer, industri= al, 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. Reported-by: kernel test robot Co-developed-by: Tharun Kumar P Signed-off-by: Tharun Kumar P Signed-off-by: Kumaravel Thiagarajan --- V1 -> V2: 1. Resolve build issue reported by kernel test robot --- MAINTAINERS | 1 + drivers/misc/mchp_pci1xxxx/Kconfig | 1 + drivers/misc/mchp_pci1xxxx/Makefile | 2 +- .../misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c | 693 ++++++++++++++++++ 4 files changed, 696 insertions(+), 1 deletion(-) create mode 100644 drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c 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 =20 MICROCHIP OTPC DRIVER M: Claudiu Beznea diff --git a/drivers/misc/mchp_pci1xxxx/Kconfig b/drivers/misc/mchp_pci1xxx= x/Kconfig index 4abb47de7219..67fa3299cfb6 100644 --- a/drivers/misc/mchp_pci1xxxx/Kconfig +++ b/drivers/misc/mchp_pci1xxxx/Kconfig @@ -2,6 +2,7 @@ config GP_PCI1XXXX tristate "Microchip PCI1XXXX PCIe to GPIO Expander + OTP/EEPROM man= ager" depends on PCI depends on GPIOLIB + depends on BLOCK select GPIOLIB_IRQCHIP select AUXILIARY_BUS help diff --git a/drivers/misc/mchp_pci1xxxx/Makefile b/drivers/misc/mchp_pci1xx= xx/Makefile index fc4615cfe28b..ae31251dab37 100644 --- a/drivers/misc/mchp_pci1xxxx/Makefile +++ b/drivers/misc/mchp_pci1xxxx/Makefile @@ -1 +1 @@ -obj-$(CONFIG_GP_PCI1XXXX) :=3D mchp_pci1xxxx_gp.o mchp_pci1xxxx_gpio.o +obj-$(CONFIG_GP_PCI1XXXX) :=3D mchp_pci1xxxx_gp.o mchp_pci1xxxx_gpio.o mch= p_pci1xxxx_otpe2p.o diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c b/drivers/mi= sc/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) 2023 Microchip Technology Inc. +// PCI1xxxx OTP/EEPROM driver + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 =3D OTP_SIZE_IN_BYTES / OTP_E2P_SECTOR_SIZE; +static int E2P_sector_count =3D 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, u= 16 address) +{ + u32 lo, hi; + + lo =3D address & 0xFF; + hi =3D (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 =3D priv->reg_base + MMAP_CFG_OFFSET(CFG_SYS_LOCK_OFFSET); + u8 data; + + writel(CFG_SYS_LOCK_PF3, p); + data =3D readl(p); + if (data !=3D CFG_SYS_LOCK_PF3) + return -EPERM; + + return 0; +} + +static int release_sys_lock(struct pci1xxxx_otp_e2p_device *priv) +{ + void __iomem *p =3D priv->reg_base + MMAP_CFG_OFFSET(CFG_SYS_LOCK_OFFSET); + u8 data; + + data =3D readl(p); + if (data !=3D CFG_SYS_LOCK_PF3) + return 0; + + writel(0, p); + + data =3D 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 =3D 0; + u8 data; + + disk_priv =3D (struct pci1xxxx_otp_e2p_disk *)bdev->bd_disk->private_data; + pdev =3D (struct auxiliary_device *)disk_priv->pdev; + priv =3D dev_get_drvdata(&pdev->dev); + + mutex_lock(&disk_priv->lock); + + do { + retval =3D set_sys_lock(priv); + if (retval) + break; + + if (!disk_priv->E2P) { + data =3D 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 =3D (struct pci1xxxx_otp_e2p_disk *)disk->private_data; + priv =3D dev_get_drvdata(&disk_priv->pdev->dev); + + mutex_lock(&disk_priv->lock); + + if (!disk_priv->E2P) { + data =3D 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 =3D 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 =3D 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 =3D 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 =3D 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 =3D 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 =3D 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 =3D 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 =3D 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 =3D msecs_to_jiffies(OTP_RW_TIMEOUT_MILLISECONDS); + j0 =3D jiffies; + j1 =3D j0 + delay; + + /* Wait for the OTP_BUSY bit to get cleared in OTP_STATUS register */ + do { + data =3D 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 =3D 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 =3D 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 =3D 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 =3D msecs_to_jiffies(OTP_RW_TIMEOUT_MILLISECONDS); + j0 =3D jiffies; + j1 =3D j0 + delay; + + /* Wait for OTP_BUSY bit to get cleared in OTP_STATUS */ + do { + *data =3D 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 =3D 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 =3D blk_rq_pos(req); + nsect =3D blk_rq_cur_sectors(req); + buffer =3D bio_data(req->bio); + write =3D rq_data_dir(req); + disk_priv =3D (struct pci1xxxx_otp_e2p_disk *)req->q->disk->private_data; + priv =3D dev_get_drvdata(&disk_priv->pdev->dev); + + if (write) { + for (i =3D 0; i < nsect; i++) { + byte_offset =3D (sector + i) * OTP_E2P_SECTOR_SIZE; + for (j =3D 0; j < OTP_E2P_SECTOR_SIZE; j++) { + retval =3D disk_priv->disk_write_byte(priv, + byte_offset + j, + *buffer); + if (retval) + return retval; + + buffer++; + } + } + } else { + for (i =3D 0; i < nsect; i++) { + byte_offset =3D (sector + i) * OTP_E2P_SECTOR_SIZE; + for (j =3D 0; j < OTP_E2P_SECTOR_SIZE; j++) { + retval =3D 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 =3D 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 =3D { + .queue_rq =3D OTPE2P_queue_rq, +}; + +static const struct block_device_operations otp_e2p_device_ops =3D { + .owner =3D THIS_MODULE, + .open =3D otp_e2p_device_open, + .release =3D 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 =3D 0, i; + + aux_dev_wrapper =3D (struct auxiliary_device_wrapper *) container_of(aux_= dev, + struct auxiliary_device_wrapper, aux_dev); + pdata =3D &(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 =3D devm_kzalloc(&aux_dev->dev, sizeof(struct pci1xxxx_otp_e2p_devic= e), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->pdev =3D 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 =3D 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 =3D 0; + do { + if (check_e2p_response(priv)) + priv->block_device_count =3D 2; + else + priv->block_device_count =3D 1; + + priv->otp_e2p_device =3D devm_kzalloc(&priv->pdev->dev, + priv->block_device_count * + sizeof(struct pci1xxxx_otp_e2p_disk), + GFP_KERNEL); + if (!priv->otp_e2p_device) { + retval =3D -ENOMEM; + break; + } + + for (i =3D 0; i < priv->block_device_count; i++) { + mutex_init(&priv->otp_e2p_device[i].lock); + priv->otp_e2p_device[i].tag_set.ops =3D &OTPE2P_mq_ops; + priv->otp_e2p_device[i].tag_set.nr_hw_queues =3D 1; + priv->otp_e2p_device[i].tag_set.queue_depth =3D 16; + priv->otp_e2p_device[i].tag_set.flags =3D BLK_MQ_F_SHOULD_MERGE; + + retval =3D 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 =3D + blk_mq_init_queue(&priv->otp_e2p_device[i].tag_set); + if (IS_ERR(priv->otp_e2p_device[i].queue)) { + retval =3D PTR_ERR(priv->otp_e2p_device[i].queue); + priv->otp_e2p_device[i].queue =3D 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 =3D priv; + priv->otp_e2p_device[i].gd =3D + blk_mq_alloc_disk(&priv->otp_e2p_device[i].tag_set, + NULL); + if (IS_ERR(priv->otp_e2p_device[i].gd)) { + retval =3D 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 =3D aux_dev; + priv->otp_e2p_device[i].gd->major =3D OTP_block_driver_major; + priv->otp_e2p_device[i].gd->minors =3D 1; + priv->otp_e2p_device[i].gd->first_minor =3D + otp_device_count + e2p_device_count; + priv->otp_e2p_device[i].gd->fops =3D &otp_e2p_device_ops; + priv->otp_e2p_device[i].gd->private_data =3D + &priv->otp_e2p_device[i]; + + if (i =3D=3D 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 =3D otp_device_read_byte; + priv->otp_e2p_device[i].disk_write_byte =3D 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 =3D true; + priv->otp_e2p_device[i].disk_read_byte =3D e2p_device_read_byte; + priv->otp_e2p_device[i].disk_write_byte =3D e2p_device_write_byte; + e2p_device_count++; + } + + retval =3D 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 =3D dev_get_drvdata(&aux_dev->dev); + int i; + + for (i =3D 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 =3D otp_e2p_device_create_block_device(aux_dev); + if (retval) { + dev_err(&aux_dev->dev, + "otp/eeprom device enumeration failed with errno =3D %d\n", + retval); + return retval; + } + + return 0; +} + +static const struct auxiliary_device_id pci1xxxx_otp_e2p_auxiliary_id_tabl= e[] =3D { + {.name =3D "mchp_pci1xxxx_gp.gp_otp_e2p"}, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_otp_e2p_auxiliary_id_table); + +static struct auxiliary_driver pci1xxxx_otp_e2p_driver =3D { + .driver =3D { + .name =3D "PCI1xxxxOTPE2P", + }, + .probe =3D pci1xxxx_otp_e2p_probe, + .remove =3D pci1xxxx_otp_e2p_remove, + .id_table =3D pci1xxxx_otp_e2p_auxiliary_id_table +}; + +static int __init pci1xxxx_otp_e2p_driver_init(void) +{ + int retval; + + do { + OTP_block_driver_major =3D register_blkdev(OTP_block_driver_major, + "OTPBlockDevice"); + if (OTP_block_driver_major < 0) { + retval =3D OTP_block_driver_major; + break; + } + + block_driver_registered =3D 1; + + retval =3D 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 = "); +MODULE_AUTHOR("Tharun Kumar P"); +MODULE_DESCRIPTION("Microchip Technology Inc. PCI1xxxx OTP EEPROM programm= er"); --=20 2.25.1