From nobody Fri Sep 12 08:27:15 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 87657C636D3 for ; Sun, 12 Feb 2023 03:57:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229645AbjBLD5t (ORCPT ); Sat, 11 Feb 2023 22:57:49 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54396 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229628AbjBLD5r (ORCPT ); Sat, 11 Feb 2023 22:57:47 -0500 Received: from esa.microchip.iphmx.com (esa.microchip.iphmx.com [68.232.154.123]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F1DBE14494; Sat, 11 Feb 2023 19:57:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1676174263; x=1707710263; h=from:to:cc:subject:date:message-id:mime-version: content-transfer-encoding; bh=FzmV3bWI0LrZfG5UsqRp+K75zcwqIID6m5G+pS5TjIg=; b=DpobrpRzXS0m4ZQDTMBPo4nIeaSus5hrzwOnGsLrbfaxuYFJA61monS6 R8fhGuVgLcwkK7YNz3bASmfDy6hcRQWcVSPoWGMmc8Oque2s+sYWtOQgh yjNwe5+PR1EbQz8div7bAb19Et0KSxvdLGkD1WZzcK0mN8Hm/xmxZE1cz rnvsIkslsCSUw0OVcliB/+uiscdZzS3ixFRlS8Sbo8WE3qNNv35fDPEQ4 +LPmZhrMmPWzuva+l9WrA5XzxLWRm7mYWkGiEjRLF78t1dSpO/WQ+aGUw h1bFW/ZGC5fpVa2iTkATcoNuVjBS8jXVOISv8pHDaJ2vw66A/Ry5exDIU w==; X-IronPort-AV: E=Sophos;i="5.97,291,1669100400"; d="scan'208";a="196492909" Received: from unknown (HELO email.microchip.com) ([170.129.1.10]) by esa4.microchip.iphmx.com with ESMTP/TLS/AES256-SHA256; 11 Feb 2023 20:57:42 -0700 Received: from chn-vm-ex02.mchp-main.com (10.10.85.144) by chn-vm-ex04.mchp-main.com (10.10.85.152) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.16; Sat, 11 Feb 2023 20:57:41 -0700 Received: from CHE-LT-UNGSOFTWARE.microchip.com (10.10.115.15) by chn-vm-ex02.mchp-main.com (10.10.85.144) with Microsoft SMTP Server id 15.1.2507.16 via Frontend Transport; Sat, 11 Feb 2023 20:57:39 -0700 From: Tharun Kumar P To: CC: , , , Subject: [PATCH v5 char-misc-next] misc: microchip: pci1xxxx: Add OTP/EEPROM driver for the pci1xxxx switch Date: Sun, 12 Feb 2023 09:27:43 +0530 Message-ID: <20230212035743.231353-1-tharunkumar.pasumarthi@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" From: Kumaravel Thiagarajan 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. Co-developed-by: Tharun Kumar P Signed-off-by: Tharun Kumar P Signed-off-by: Kumaravel Thiagarajan --- v4 -> v5: - Used proper errno - Removed un-necessary prints v3 -> v4: - Remove extra space, tab, un-necessary casting, paranthesis, do while(false) loops - Used read_poll_timeout for polling BUSY_BIT v2 -> v3: - Modified commit description to include build issues reported by Kernel test robot which are fixed in this patch v1 -> v2: - 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 | 649 ++++++++++++++++++ 4 files changed, 652 insertions(+), 1 deletion(-) create mode 100644 drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c diff --git a/MAINTAINERS b/MAINTAINERS index 69d1e8ad52c5..8a57683927fd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13803,6 +13803,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..0d097afc84aa --- /dev/null +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c @@ -0,0 +1,649 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2022-2023 Microchip Technology Inc. +// PCI1xxxx OTP/EEPROM driver + +#include +#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 OTP_STATUS_READ_DELAY_US (4000) +#define OTP_STATUS_READ_TIMEOUT_US (20000) + +#define MMAP_EEPROM_OFFSET(x) (EEPROM_REG_ADDR_BASE + (x)) + +#define EEPROM_CMD_REG (0x00) +#define EEPROM_DATA_REG (0x04) +#define EEPROM_CFG_REG (0x08) + +#define EEPROM_CMD_EPC_BUSY_BIT BIT(31) +#define EEPROM_CMD_EPC_TIMEOUT_BIT BIT(17) +#define EEPROM_CMD_EPC_WRITE (BIT(29) | BIT(28)) + +#define EEPROM_CFG_BAUD_RATE_100KHZ BIT(9) +#define EEPROM_CFG_SIZE_SEL BIT(12) +#define EEPROM_CFG_PULSE_WIDTH_100KHZ (BIT(17) | BIT(16)) +#define OTP_EEPROM_SECTOR_SIZE (512) +#define OTP_SIZE_IN_BYTES (8 * 1024) +#define EEPROM_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 has_eeprom; + 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_EEPROM_SECTOR_SIZE; +static int e2p_sector_count =3D EEPROM_SIZE_IN_BYTES / OTP_EEPROM_SECTOR_S= IZE; +static int otp_device_count, e2p_device_count; +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 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); + + retval =3D set_sys_lock(priv); + if (retval) + goto exit; + + if (!disk_priv->has_eeprom) { + 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)); + } + +exit: + 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 disk->private_data; + priv =3D dev_get_drvdata(&disk_priv->pdev->dev); + + mutex_lock(&disk_priv->lock); + + if (!disk_priv->has_eeprom) { + 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 EEPROM_DATA_REG register */ + writel(value, priv->reg_base + MMAP_EEPROM_OFFSET(EEPROM_DATA_REG)); + data =3D EEPROM_CMD_EPC_TIMEOUT_BIT | EEPROM_CMD_EPC_WRITE | byte_offset; + + /* Write the data into EEPROM_CMD_REG register */ + writel(data, priv->reg_base + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG)); + + /* Set the EPC_BUSY bit of EEPROM_CMD_REG register */ + writel(EEPROM_CMD_EPC_BUSY_BIT | data, priv->reg_base + + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG)); + + /* Wait for the EPC_BUSY bit to get cleared */ + do { + data =3D readl(priv->reg_base + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG)); + } while (data & EEPROM_CMD_EPC_BUSY_BIT); + + if (data & EEPROM_CMD_EPC_TIMEOUT_BIT) + return -EBUSY; + + 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 EEPROM_CMD_REG + * register + */ + writel(byte_offset, priv->reg_base + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG)); + + /* Set the EPC_BUSY bit of EEPROM_CMD_REG register */ + writel(EEPROM_CMD_EPC_BUSY_BIT | byte_offset, priv->reg_base + + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG)); + + /* Wait for the EPC_BUSY bit to get cleared */ + do { + regval =3D readl(priv->reg_base + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG)); + } while (regval & EEPROM_CMD_EPC_BUSY_BIT); + + if (regval & EEPROM_CMD_EPC_TIMEOUT_BIT) + return -EBUSY; + + /* Read the contents from the EEPROM_DATA_REG */ + *data =3D readl(priv->reg_base + MMAP_EEPROM_OFFSET(EEPROM_DATA_REG)); + return 0; +} + +static bool is_e2p_responsive(struct pci1xxxx_otp_e2p_device *priv) +{ + u32 data; + + if (set_sys_lock(priv)) + return false; + + writel((EEPROM_CFG_PULSE_WIDTH_100KHZ | EEPROM_CFG_SIZE_SEL | + EEPROM_CFG_BAUD_RATE_100KHZ), priv->reg_base + + MMAP_EEPROM_OFFSET(EEPROM_CFG_REG)); + + /* + * Write the byte offset into the EPC_ADDRESS field of EEPROM_CMD_REG + * register + */ + writel(EEPROM_CMD_EPC_TIMEOUT_BIT, priv->reg_base + + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG)); + + /* Set the EPC_BUSY bit of EEPROM_CMD_REG register */ + writel(EEPROM_CMD_EPC_BUSY_BIT, priv->reg_base + + MMAP_EEPROM_OFFSET(EEPROM_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(EEPROM_CMD_REG)); + } while (data & EEPROM_CMD_EPC_BUSY_BIT); + + /* If EPC_TIMEOUT is set, then the EEPROM is not responsive */ + release_sys_lock(priv); + + if (data & EEPROM_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) +{ + u8 data; + int ret; + + 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)); + + ret =3D read_poll_timeout(readl, data, !(data & OTP_STATUS_BUSY_BIT), + OTP_STATUS_READ_DELAY_US, OTP_STATUS_READ_TIMEOUT_US, + true, priv->reg_base + MMAP_OTP_OFFSET(OTP_STATUS_OFFSET)); + if (ret < 0) + return -EBUSY; + + /* 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, "OTP write read mismatch 0x%x\n", data); + return -EIO; +} + +static int otp_device_read_byte(struct pci1xxxx_otp_e2p_device *priv, + unsigned long byte_offset, u8 *data) +{ + int ret; + + 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)); + + ret =3D read_poll_timeout(readl, *data, !(*data & OTP_STATUS_BUSY_BIT), + OTP_STATUS_READ_DELAY_US, OTP_STATUS_READ_TIMEOUT_US, + true, priv->reg_base + MMAP_OTP_OFFSET(OTP_STATUS_OFFSET)); + if (ret < 0) + return -EBUSY; + + /* 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 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_EEPROM_SECTOR_SIZE; + for (j =3D 0; j < OTP_EEPROM_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_EEPROM_SECTOR_SIZE; + for (j =3D 0; j < OTP_EEPROM_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 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; + if (is_e2p_responsive(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) + return -ENOMEM; + + 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"); + return retval; + } + + 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_EEPROM_SECTOR_SIZE); + blk_queue_physical_block_size(priv->otp_e2p_device[i].queue, + OTP_EEPROM_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].has_eeprom =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; + } + } + + 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); + + dev_err(&aux_dev->dev, + "otp/eeprom device enumeration failed with errno =3D %d\n", + retval); + 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) +{ + return otp_e2p_device_create_block_device(aux_dev); +} + +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; + + otp_block_driver_major =3D register_blkdev(otp_block_driver_major, + "OTPBlockDevice"); + if (otp_block_driver_major < 0) + return otp_block_driver_major; + + retval =3D auxiliary_driver_register(&pci1xxxx_otp_e2p_driver); + if (retval) + unregister_blkdev(otp_block_driver_major, "OTPBlockDevice"); + + return retval; +} + +static void __exit pci1xxxx_otp_e2p_driver_exit(void) +{ + unregister_blkdev(otp_block_driver_major, "OTPBlockDevice"); + auxiliary_driver_unregister(&pci1xxxx_otp_e2p_driver); +} + +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