From patchwork Fri Jun 11 05:26:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiri Prchal X-Patchwork-Id: 458699 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id AABF2C48BE0 for ; Fri, 11 Jun 2021 05:27:01 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 889FF61182 for ; Fri, 11 Jun 2021 05:27:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230358AbhFKF25 (ORCPT ); Fri, 11 Jun 2021 01:28:57 -0400 Received: from router.aksignal.cz ([62.44.4.214]:41472 "EHLO router.aksignal.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230160AbhFKF24 (ORCPT ); Fri, 11 Jun 2021 01:28:56 -0400 Received: from localhost (localhost [127.0.0.1]) by router.aksignal.cz (Postfix) with ESMTP id 05D7844438; Fri, 11 Jun 2021 07:26:57 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at router.aksignal.cz Received: from router.aksignal.cz ([127.0.0.1]) by localhost (router.aksignal.cz [127.0.0.1]) (amavisd-new, port 10026) with LMTP id 6vhq6CfdXyqs; Fri, 11 Jun 2021 07:26:56 +0200 (CEST) Received: from pc-gameroom.prchals.tk (unknown [83.240.30.185]) (Authenticated sender: jiri.prchal@aksignal.cz) by router.aksignal.cz (Postfix) with ESMTPSA id 2BCAE44420; Fri, 11 Jun 2021 07:26:56 +0200 (CEST) From: Jiri Prchal To: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Rob Herring , Christian Eggers , Arnd Bergmann , Greg Kroah-Hartman , Jiri Prchal Subject: [PATCH v9 1/5] nvmem: prepare basics for FRAM support Date: Fri, 11 Jun 2021 07:26:48 +0200 Message-Id: <20210611052652.7894-2-jiri.prchal@aksignal.cz> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210611052652.7894-1-jiri.prchal@aksignal.cz> References: <20210611052652.7894-1-jiri.prchal@aksignal.cz> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org Added enum and string for FRAM to expose it as "fram". Signed-off-by: Jiri Prchal --- v2: no change here v3: resend and added more recipients v4: resend v5: no change here v6: changed commit subject v7: no change here v8: changed subject v9: no change here --- drivers/nvmem/core.c | 4 ++++ include/linux/nvmem-provider.h | 1 + 2 files changed, 5 insertions(+) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 177f5bf27c6d..01ef9a934b0a 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -180,6 +180,7 @@ static const char * const nvmem_type_str[] = { [NVMEM_TYPE_EEPROM] = "EEPROM", [NVMEM_TYPE_OTP] = "OTP", [NVMEM_TYPE_BATTERY_BACKED] = "Battery backed", + [NVMEM_TYPE_FRAM] = "FRAM", }; #ifdef CONFIG_DEBUG_LOCK_ALLOC @@ -359,6 +360,9 @@ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, if (!config->base_dev) return -EINVAL; + if (config->type == NVMEM_TYPE_FRAM) + bin_attr_nvmem_eeprom_compat.attr.name = "fram"; + nvmem->eeprom = bin_attr_nvmem_eeprom_compat; nvmem->eeprom.attr.mode = nvmem_bin_attr_get_umode(nvmem); nvmem->eeprom.size = nvmem->size; diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index e162b757b6d5..890003565761 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -25,6 +25,7 @@ enum nvmem_type { NVMEM_TYPE_EEPROM, NVMEM_TYPE_OTP, NVMEM_TYPE_BATTERY_BACKED, + NVMEM_TYPE_FRAM, }; #define NVMEM_DEVID_NONE (-1) From patchwork Fri Jun 11 05:26:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiri Prchal X-Patchwork-Id: 458698 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 00E53C48BE5 for ; Fri, 11 Jun 2021 05:27:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DD462610A2 for ; Fri, 11 Jun 2021 05:27:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230468AbhFKF26 (ORCPT ); Fri, 11 Jun 2021 01:28:58 -0400 Received: from router.aksignal.cz ([62.44.4.214]:41496 "EHLO router.aksignal.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230248AbhFKF25 (ORCPT ); Fri, 11 Jun 2021 01:28:57 -0400 Received: from localhost (localhost [127.0.0.1]) by router.aksignal.cz (Postfix) with ESMTP id 765564441F; Fri, 11 Jun 2021 07:26:58 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at router.aksignal.cz Received: from router.aksignal.cz ([127.0.0.1]) by localhost (router.aksignal.cz [127.0.0.1]) (amavisd-new, port 10026) with LMTP id mHQowfbQgVto; Fri, 11 Jun 2021 07:26:56 +0200 (CEST) Received: from pc-gameroom.prchals.tk (unknown [83.240.30.185]) (Authenticated sender: jiri.prchal@aksignal.cz) by router.aksignal.cz (Postfix) with ESMTPSA id 56BD144421; Fri, 11 Jun 2021 07:26:56 +0200 (CEST) From: Jiri Prchal To: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Rob Herring , Christian Eggers , Arnd Bergmann , Greg Kroah-Hartman , Jiri Prchal , kernel test robot Subject: [PATCH v9 2/5] nvmem: eeprom: at25: add support for FRAM Date: Fri, 11 Jun 2021 07:26:49 +0200 Message-Id: <20210611052652.7894-3-jiri.prchal@aksignal.cz> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210611052652.7894-1-jiri.prchal@aksignal.cz> References: <20210611052652.7894-1-jiri.prchal@aksignal.cz> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org Added support for Cypress FRAMs. These frams have ID and some of them have serial number too. Size of them is recognized by ID. They don't have pages, it could be read or written at once, but it's limited in this driver to io limit 4096. Signed-off-by: Jiri Prchal --- v2: fixed warning at %zd at int Reported-by: kernel test robot v3: resend and added more recipients v4: resend v5: used in-kernel function int_pow v6: no change here v7: moved definition of sernum size to patch 4 v8: changed buffer type to u8 v9: removed needless has_sernum --- drivers/misc/eeprom/Kconfig | 5 +- drivers/misc/eeprom/at25.c | 134 ++++++++++++++++++++++++++++-------- 2 files changed, 107 insertions(+), 32 deletions(-) diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 0f791bfdc1f5..f0a7531f354c 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -32,12 +32,13 @@ config EEPROM_AT24 will be called at24. config EEPROM_AT25 - tristate "SPI EEPROMs from most vendors" + tristate "SPI EEPROMs (FRAMs) from most vendors" depends on SPI && SYSFS select NVMEM select NVMEM_SYSFS help - Enable this driver to get read/write support to most SPI EEPROMs, + Enable this driver to get read/write support to most SPI EEPROMs + and Cypress FRAMs, after you configure the board init code to know about each eeprom on your target board. diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index b76e4901b4a4..e59b6852816d 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * at25.c -- support most SPI EEPROMs, such as Atmel AT25 models + * and Cypress FRAMs FM25 models * * Copyright (C) 2006 David Brownell */ @@ -16,6 +17,9 @@ #include #include #include +#include +#include +#include /* * NOTE: this is an *EEPROM* driver. The vagaries of product naming @@ -42,6 +46,9 @@ struct at25_data { #define AT25_WRSR 0x01 /* write status register */ #define AT25_READ 0x03 /* read byte(s) */ #define AT25_WRITE 0x02 /* write byte(s)/sector */ +#define FM25_SLEEP 0xb9 /* enter sleep mode */ +#define FM25_RDID 0x9f /* read device ID */ +#define FM25_RDSN 0xc3 /* read S/N */ #define AT25_SR_nRDY 0x01 /* nRDY = write-in-progress */ #define AT25_SR_WEN 0x02 /* write enable (latched) */ @@ -51,6 +58,8 @@ struct at25_data { #define AT25_INSTR_BIT3 0x08 /* Additional address bit in instr */ +#define FM25_ID_LEN 9 /* ID length */ + #define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */ /* Specs often allow 5 msec for a page write, sometimes 20 msec; @@ -58,6 +67,9 @@ struct at25_data { */ #define EE_TIMEOUT 25 +#define IS_EEPROM 0 +#define IS_FRAM 1 + /*-------------------------------------------------------------------------*/ #define io_limit PAGE_SIZE /* bytes */ @@ -129,6 +141,36 @@ static int at25_ee_read(void *priv, unsigned int offset, return status; } +/* + * read extra registers as ID or serial number + */ +static int fm25_aux_read(struct at25_data *at25, u8 *buf, uint8_t command, + int len) +{ + int status; + struct spi_transfer t[2]; + struct spi_message m; + + spi_message_init(&m); + memset(t, 0, sizeof(t)); + + t[0].tx_buf = &command; + t[0].len = 1; + spi_message_add_tail(&t[0], &m); + + t[1].rx_buf = buf; + t[1].len = len; + spi_message_add_tail(&t[1], &m); + + mutex_lock(&at25->lock); + + status = spi_sync(at25->spi, &m); + dev_dbg(&at25->spi->dev, "read %d aux bytes --> %d\n", len, status); + + mutex_unlock(&at25->lock); + return status; +} + static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) { struct at25_data *at25 = priv; @@ -303,34 +345,37 @@ static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip) return 0; } +static const struct of_device_id at25_of_match[] = { + { .compatible = "atmel,at25", .data = (const void *)IS_EEPROM }, + { .compatible = "cypress,fm25", .data = (const void *)IS_FRAM }, + { } +}; +MODULE_DEVICE_TABLE(of, at25_of_match); + static int at25_probe(struct spi_device *spi) { struct at25_data *at25 = NULL; struct spi_eeprom chip; int err; int sr; - int addrlen; + u8 id[FM25_ID_LEN]; + const struct of_device_id *match; + int is_fram = 0; + + match = of_match_device(of_match_ptr(at25_of_match), &spi->dev); + if (match) + is_fram = (int)(uintptr_t)match->data; /* Chip description */ if (!spi->dev.platform_data) { - err = at25_fw_to_chip(&spi->dev, &chip); - if (err) - return err; + if (!is_fram) { + err = at25_fw_to_chip(&spi->dev, &chip); + if (err) + return err; + } } else chip = *(struct spi_eeprom *)spi->dev.platform_data; - /* For now we only support 8/16/24 bit addressing */ - if (chip.flags & EE_ADDR1) - addrlen = 1; - else if (chip.flags & EE_ADDR2) - addrlen = 2; - else if (chip.flags & EE_ADDR3) - addrlen = 3; - else { - dev_dbg(&spi->dev, "unsupported address type\n"); - return -EINVAL; - } - /* Ping the chip ... the status register is pretty portable, * unlike probing manufacturer IDs. We do expect that system * firmware didn't write it in the past few milliseconds! @@ -349,9 +394,44 @@ static int at25_probe(struct spi_device *spi) at25->chip = chip; at25->spi = spi; spi_set_drvdata(spi, at25); - at25->addrlen = addrlen; - at25->nvmem_config.type = NVMEM_TYPE_EEPROM; + if (is_fram) { + /* Get ID of chip */ + fm25_aux_read(at25, id, FM25_RDID, FM25_ID_LEN); + if (id[6] != 0xc2) { + dev_err(&spi->dev, + "Error: no Cypress FRAM (id %02x)\n", id[6]); + return -ENODEV; + } + /* set size found in ID */ + if (id[7] < 0x21 || id[7] > 0x26) { + dev_err(&spi->dev, "Error: unsupported size (id %02x)\n", id[7]); + return -ENODEV; + } + chip.byte_len = int_pow(2, id[7] - 0x21 + 4) * 1024; + + if (at25->chip.byte_len > 64 * 1024) + at25->chip.flags |= EE_ADDR3; + else + at25->chip.flags |= EE_ADDR2; + + at25->chip.page_size = PAGE_SIZE; + strncpy(at25->chip.name, "fm25", sizeof(at25->chip.name)); + } + + /* For now we only support 8/16/24 bit addressing */ + if (at25->chip.flags & EE_ADDR1) + at25->addrlen = 1; + else if (at25->chip.flags & EE_ADDR2) + at25->addrlen = 2; + else if (at25->chip.flags & EE_ADDR3) + at25->addrlen = 3; + else { + dev_dbg(&spi->dev, "unsupported address type\n"); + return -EINVAL; + } + + at25->nvmem_config.type = is_fram ? NVMEM_TYPE_FRAM : NVMEM_TYPE_EEPROM; at25->nvmem_config.name = dev_name(&spi->dev); at25->nvmem_config.dev = &spi->dev; at25->nvmem_config.read_only = chip.flags & EE_READONLY; @@ -370,23 +450,17 @@ static int at25_probe(struct spi_device *spi) if (IS_ERR(at25->nvmem)) return PTR_ERR(at25->nvmem); - dev_info(&spi->dev, "%d %s %s eeprom%s, pagesize %u\n", - (chip.byte_len < 1024) ? chip.byte_len : (chip.byte_len / 1024), - (chip.byte_len < 1024) ? "Byte" : "KByte", - at25->chip.name, - (chip.flags & EE_READONLY) ? " (readonly)" : "", - at25->chip.page_size); + dev_info(&spi->dev, "%d %s %s %s%s, pagesize %u\n", + (chip.byte_len < 1024) ? chip.byte_len : (chip.byte_len / 1024), + (chip.byte_len < 1024) ? "Byte" : "KByte", + at25->chip.name, is_fram ? "fram" : "eeprom", + (chip.flags & EE_READONLY) ? " (readonly)" : "", + at25->chip.page_size); return 0; } /*-------------------------------------------------------------------------*/ -static const struct of_device_id at25_of_match[] = { - { .compatible = "atmel,at25", }, - { } -}; -MODULE_DEVICE_TABLE(of, at25_of_match); - static struct spi_driver at25_driver = { .driver = { .name = "at25", From patchwork Fri Jun 11 05:26:51 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiri Prchal X-Patchwork-Id: 458697 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BCCB8C4743D for ; Fri, 11 Jun 2021 05:27:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 94709610A2 for ; Fri, 11 Jun 2021 05:27:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230482AbhFKF3B (ORCPT ); Fri, 11 Jun 2021 01:29:01 -0400 Received: from router.aksignal.cz ([62.44.4.214]:41506 "EHLO router.aksignal.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230416AbhFKF27 (ORCPT ); Fri, 11 Jun 2021 01:28:59 -0400 Received: from localhost (localhost [127.0.0.1]) by router.aksignal.cz (Postfix) with ESMTP id 0912D44421; Fri, 11 Jun 2021 07:27:00 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at router.aksignal.cz Received: from router.aksignal.cz ([127.0.0.1]) by localhost (router.aksignal.cz [127.0.0.1]) (amavisd-new, port 10026) with LMTP id QzMiNkoiH-65; Fri, 11 Jun 2021 07:26:59 +0200 (CEST) Received: from pc-gameroom.prchals.tk (unknown [83.240.30.185]) (Authenticated sender: jiri.prchal@aksignal.cz) by router.aksignal.cz (Postfix) with ESMTPSA id B805144437; Fri, 11 Jun 2021 07:26:56 +0200 (CEST) From: Jiri Prchal To: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Rob Herring , Christian Eggers , Arnd Bergmann , Greg Kroah-Hartman , Jiri Prchal Subject: [PATCH v9 4/5] nvmem: eeprom: at25: export FRAM serial num Date: Fri, 11 Jun 2021 07:26:51 +0200 Message-Id: <20210611052652.7894-5-jiri.prchal@aksignal.cz> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210611052652.7894-1-jiri.prchal@aksignal.cz> References: <20210611052652.7894-1-jiri.prchal@aksignal.cz> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org This exports serial number of FRAM in sysfs file named "sernum". Formatted in hex, each byte separated by space. Example: $ cat /sys/class/spi_master/spi0/spi0.0/sernum ef cd ab 89 67 45 23 01 Signed-off-by: Jiri Prchal --- v2: no change here v3: resend and added more recipients v4: resend v5: reworked up on Greg comments: no spaces in string, sysfs done correctly v6: no change here v7: moved FM25_SN_LEN, static array, used sysfs_emit, DEVICE_ATTR_RO v8: clarify sysfs_emit format v9: sizeof parentheses, export with spaces MSB first --- drivers/misc/eeprom/at25.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index e59b6852816d..19280e9f118d 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -31,6 +31,7 @@ * AT25M02, AT25128B */ +#define FM25_SN_LEN 8 /* serial number length */ struct at25_data { struct spi_device *spi; struct mutex lock; @@ -38,6 +39,7 @@ struct at25_data { unsigned addrlen; struct nvmem_config nvmem_config; struct nvmem_device *nvmem; + u8 sernum[FM25_SN_LEN]; }; #define AT25_WREN 0x06 /* latch the write enable */ @@ -171,6 +173,21 @@ static int fm25_aux_read(struct at25_data *at25, u8 *buf, uint8_t command, return status; } +static ssize_t sernum_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct at25_data *at25; + + at25 = dev_get_drvdata(dev); + return sysfs_emit(buf, "%*ph\n", sizeof(at25->sernum), at25->sernum); +} +static DEVICE_ATTR_RO(sernum); + +static struct attribute *sernum_attrs[] = { + &dev_attr_sernum.attr, + NULL, +}; +ATTRIBUTE_GROUPS(sernum); + static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) { struct at25_data *at25 = priv; @@ -359,6 +376,8 @@ static int at25_probe(struct spi_device *spi) int err; int sr; u8 id[FM25_ID_LEN]; + u8 sernum[FM25_SN_LEN]; + int i; const struct of_device_id *match; int is_fram = 0; @@ -415,6 +434,13 @@ static int at25_probe(struct spi_device *spi) else at25->chip.flags |= EE_ADDR2; + if (id[8]) { + fm25_aux_read(at25, sernum, FM25_RDSN, FM25_SN_LEN); + /* swap byte order */ + for (i = 0; i < FM25_SN_LEN; i++) + at25->sernum[i] = sernum[FM25_SN_LEN - 1 - i]; + } + at25->chip.page_size = PAGE_SIZE; strncpy(at25->chip.name, "fm25", sizeof(at25->chip.name)); } @@ -465,6 +491,7 @@ static struct spi_driver at25_driver = { .driver = { .name = "at25", .of_match_table = at25_of_match, + .dev_groups = sernum_groups, }, .probe = at25_probe, };