From patchwork Thu Mar 5 09:45:55 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 45446 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-lb0-f197.google.com (mail-lb0-f197.google.com [209.85.217.197]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 0E0FA204BC for ; Thu, 5 Mar 2015 09:46:23 +0000 (UTC) Received: by lbdu14 with SMTP id u14sf7774538lbd.3 for ; Thu, 05 Mar 2015 01:46:22 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:sender:precedence:list-id :x-original-sender:x-original-authentication-results:mailing-list :list-post:list-help:list-archive:list-unsubscribe; bh=clgj4haUp25TjqWmS3thMozNg7IOKhs00Mhx/lerJRs=; b=WO+I8yKfz1b74c8WxllXEQSTZ2IoWFIYAvq1YG0Fh2wKn2X0CiX6fVU1weePkd344H Auln70JXE4ZVPjDp7N0Vl6oRCKnmrZCyLAxT0F/MTQSrYU5cpgls1E1AQiqM1/QBVMlU DomaZ/5wVWN5bSt8yaqY2wY+v63Grdb0DZYyxSIUwahe4pTDfIUhyfb+DhvO1LJFDKym MYYFB2Q7aNF6NOwpjBHFwHBIiNQBTQOpl1Ji6MQ3KpP2ZpON+nAkNfOaXqeRqp/qGfMK GQMbSi2Maqr0M1XMEC2WsQJL5gxKfflmAko6nHl/2lFMOyMDHvBtGVGZeeDqOdywSqen bUPQ== X-Gm-Message-State: ALoCoQl3SK9hEBoei/1zHGODI+QW5QpBLSGjytTKjNRvQcrF49XF33fRTEvDfxGxlGPwwURsKXOk X-Received: by 10.180.75.232 with SMTP id f8mr4519248wiw.0.1425548781988; Thu, 05 Mar 2015 01:46:21 -0800 (PST) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.152.206.104 with SMTP id ln8ls279297lac.94.gmail; Thu, 05 Mar 2015 01:46:21 -0800 (PST) X-Received: by 10.112.144.41 with SMTP id sj9mr7061270lbb.3.1425548781652; Thu, 05 Mar 2015 01:46:21 -0800 (PST) Received: from mail-lb0-f175.google.com (mail-lb0-f175.google.com. [209.85.217.175]) by mx.google.com with ESMTPS id u10si4110404lau.117.2015.03.05.01.46.21 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 05 Mar 2015 01:46:21 -0800 (PST) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.175 as permitted sender) client-ip=209.85.217.175; Received: by lbdu10 with SMTP id u10so3249881lbd.4 for ; Thu, 05 Mar 2015 01:46:21 -0800 (PST) X-Received: by 10.152.178.197 with SMTP id da5mr7313757lac.56.1425548781505; Thu, 05 Mar 2015 01:46:21 -0800 (PST) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.112.35.133 with SMTP id h5csp372480lbj; Thu, 5 Mar 2015 01:46:20 -0800 (PST) X-Received: by 10.70.88.134 with SMTP id bg6mr1982122pdb.7.1425548775059; Thu, 05 Mar 2015 01:46:15 -0800 (PST) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 3si8719721pdl.95.2015.03.05.01.46.13; Thu, 05 Mar 2015 01:46:15 -0800 (PST) Received-SPF: none (google.com: linux-kernel-owner@vger.kernel.org does not designate permitted sender hosts) client-ip=209.132.180.67; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932507AbbCEJqH (ORCPT + 28 others); Thu, 5 Mar 2015 04:46:07 -0500 Received: from mail-wg0-f50.google.com ([74.125.82.50]:36031 "EHLO mail-wg0-f50.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932479AbbCEJqB (ORCPT ); Thu, 5 Mar 2015 04:46:01 -0500 Received: by wghk14 with SMTP id k14so1463044wgh.3 for ; Thu, 05 Mar 2015 01:46:00 -0800 (PST) X-Received: by 10.194.109.9 with SMTP id ho9mr16358956wjb.29.1425548760203; Thu, 05 Mar 2015 01:46:00 -0800 (PST) Received: from srini-ThinkPad-X1-Carbon-2nd.dlink.com (host-89-242-228-249.as13285.net. [89.242.228.249]) by mx.google.com with ESMTPSA id nb18sm10844120wic.3.2015.03.05.01.45.58 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 05 Mar 2015 01:45:59 -0800 (PST) From: Srinivas Kandagatla To: linux-arm-kernel@lists.infradead.org Cc: Maxime Ripard , Rob Herring , Pawel Moll , Kumar Gala , linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, Stephen Boyd , andrew@lunn.ch, Arnd Bergmann , broonie@kernel.org, Greg Kroah-Hartman , Srinivas Kandagatla Subject: [PATCH v1 2/6] eeprom: Add a simple EEPROM framework for eeprom consumers Date: Thu, 5 Mar 2015 09:45:55 +0000 Message-Id: <1425548755-12976-1-git-send-email-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1425548685-12887-1-git-send-email-srinivas.kandagatla@linaro.org> References: <1425548685-12887-1-git-send-email-srinivas.kandagatla@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: srinivas.kandagatla@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.175 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , This patch adds just consumers part of the framework just to enable easy review. Up until now, EEPROM drivers were stored in drivers/misc, where they all had to duplicate pretty much the same code to register a sysfs file, allow in-kernel users to access the content of the devices they were driving, etc. This was also a problem as far as other in-kernel users were involved, since the solutions used were pretty much different from on driver to another, there was a rather big abstraction leak. This introduction of this framework aims at solving this. It also introduces DT representation for consumer devices to go get the data they require (MAC Addresses, SoC/Revision ID, part numbers, and so on) from the EEPROMs. Having regmap interface to this framework would give much better abstraction for eeproms on different buses. Signed-off-by: Maxime Ripard [Maxime Ripard: intial version of the framework] Signed-off-by: Srinivas Kandagatla --- drivers/eeprom/core.c | 298 ++++++++++++++++++++++++++++++++++++++++ include/linux/eeprom-consumer.h | 67 +++++++++ 2 files changed, 365 insertions(+) create mode 100644 include/linux/eeprom-consumer.h diff --git a/drivers/eeprom/core.c b/drivers/eeprom/core.c index 243e466..75ae39c 100644 --- a/drivers/eeprom/core.c +++ b/drivers/eeprom/core.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -31,6 +32,13 @@ struct eeprom_device { atomic_t users; }; +struct eeprom_cell { + struct eeprom_device *eeprom; + int nblocks; + int size; + struct eeprom_block blocks[0]; +}; + static DEFINE_MUTEX(eeprom_mutex); static DEFINE_IDA(eeprom_ida); @@ -116,6 +124,37 @@ static struct class eeprom_class = { .dev_release = eeprom_release, }; +static int of_eeprom_match(struct device *dev, const void *eeprom_np) +{ + return dev->of_node == eeprom_np; +} + +static struct eeprom_device *of_eeprom_find(struct device_node *eeprom_np) +{ + struct device *d; + + if (!eeprom_np) + return NULL; + + d = class_find_device(&eeprom_class, NULL, eeprom_np, of_eeprom_match); + + return d ? to_eeprom(d) : NULL; +} + +static int eeprom_match(struct device *dev, const void *data) +{ + return !strcmp(dev_name(dev), (const char *)data); +} + +static struct eeprom_device *eeprom_find(const char *name) +{ + struct device *d; + + d = class_find_device(&eeprom_class, NULL, (void *)name, eeprom_match); + + return d ? to_eeprom(d) : NULL; +} + /** * eeprom_register(): Register a eeprom device for given eeprom. * Also creates an binary entry in /sys/class/eeprom/name-id/eeprom @@ -189,6 +228,265 @@ int eeprom_unregister(struct eeprom_device *eeprom) } EXPORT_SYMBOL(eeprom_unregister); +static struct eeprom_cell *eeprom_cell_sanity_check(struct eeprom_cell *cell) +{ + struct eeprom_device *eeprom = cell->eeprom; + int i; + + /* byte aligned, no need to check for stride sanity */ + if (eeprom->stride == 1) + return cell; + + for (i = 0; i < cell->nblocks; i++) { + if (!IS_ALIGNED(cell->blocks[i].offset, eeprom->stride) || + !IS_ALIGNED(cell->blocks[i].count, eeprom->stride)) { + dev_err(&eeprom->dev, + "cell unaligned to eeprom stride %d\n", + eeprom->stride); + atomic_dec(&eeprom->users); + module_put(eeprom->owner); + kfree(cell); + return ERR_PTR(-EINVAL); + } + } + + return cell; +} + +/** + * eeprom_cell_get(): Get eeprom cell of device form a given eeprom name + * and blocks. + * + * @ename: eeprom device name that needs to be looked-up. + * @blocks: eeprom blocks containing offset and length information. + * @nblocks: number of eeprom blocks. + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct eeprom_cell. The eeprom_cell will be freed by the + * eeprom_cell_put(). + */ +struct eeprom_cell *eeprom_cell_get(const char *ename, + struct eeprom_block *blocks, int nblocks) +{ + struct eeprom_cell *cell; + struct eeprom_device *eeprom = NULL; + int i; + + mutex_lock(&eeprom_mutex); + eeprom = eeprom_find(ename); + if (!eeprom) { + mutex_unlock(&eeprom_mutex); + return ERR_PTR(-EPROBE_DEFER); + } + atomic_inc(&eeprom->users); + + if (!try_module_get(eeprom->owner)) { + dev_err(&eeprom->dev, + "could not increase module refcount for cell %s\n", + ename); + mutex_unlock(&eeprom_mutex); + return ERR_PTR(-EINVAL); + } + mutex_unlock(&eeprom_mutex); + + cell = kzalloc(sizeof(*cell) + nblocks * sizeof(*blocks), GFP_KERNEL); + if (!cell) + return ERR_PTR(-ENOMEM); + + memcpy(cell->blocks, blocks, nblocks * sizeof(*blocks)); + cell->nblocks = nblocks; + cell->eeprom = eeprom; + cell->size = 0; + + for (i = 0; i < nblocks; i++) + cell->size += blocks[i].count; + + return eeprom_cell_sanity_check(cell); +} +EXPORT_SYMBOL(eeprom_cell_get); + +/** + * of_eeprom_cell_get(): Get eeprom cell of device form a given index + * + * @dev: Device that will be interacted with + * @index: eeprom index in eeproms property. + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct eeprom_cell. The eeprom_cell will be freed by the + * eeprom_cell_put(). + */ +struct eeprom_cell *of_eeprom_cell_get(struct device *dev, int index) +{ + struct device_node *cell_np; + struct eeprom_cell *cell; + struct eeprom_device *eeprom = NULL; + int i, nblocks; + struct property *prop; + const __be32 *vp; + u32 pv; + + if (!dev || !dev->of_node) + return ERR_PTR(-EINVAL); + + cell_np = of_parse_phandle(dev->of_node, "eeproms", index); + if (!cell_np) + return ERR_PTR(-EPROBE_DEFER); + + mutex_lock(&eeprom_mutex); + eeprom = of_eeprom_find(cell_np->parent); + if (!eeprom) { + mutex_unlock(&eeprom_mutex); + return ERR_PTR(-EPROBE_DEFER); + } + + atomic_inc(&eeprom->users); + + if (!try_module_get(eeprom->owner)) { + dev_err(&eeprom->dev, + "could not increase module refcount for cell %d\n", + index); + mutex_unlock(&eeprom_mutex); + return ERR_PTR(-EINVAL); + } + mutex_unlock(&eeprom_mutex); + + nblocks = of_property_count_u32_elems(cell_np, "reg") / 2; + + cell = kzalloc(sizeof(*cell) + nblocks * sizeof(struct eeprom_block), + GFP_KERNEL); + cell->nblocks = nblocks; + cell->eeprom = eeprom; + cell->size = 0; + i = 0; + + of_property_for_each_u32(cell_np, "reg", prop, vp, pv) { + cell->blocks[i].offset = pv; + vp = of_prop_next_u32(prop, vp, &pv); + cell->blocks[i].count = pv; + cell->size += pv; + i++; + } + + return eeprom_cell_sanity_check(cell); +} +EXPORT_SYMBOL(of_eeprom_cell_get); + +/** + * of_eeprom_cell_get_byname(): Get eeprom cell of device form a given name + * + * @dev: Device that will be interacted with + * @name: eeprom name in eeprom-names property. + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct eeprom_cell. The eeprom_cell will be freed by the + * eeprom_cell_put(). + */ +struct eeprom_cell *of_eeprom_cell_get_byname(struct device *dev, + const char *id) +{ + int index = 0; + + if (!dev || !dev->of_node) + return ERR_PTR(-EINVAL); + + if (id) + index = of_property_match_string(dev->of_node, + "eeprom-names", + id); + return of_eeprom_cell_get(dev, index); + +} +EXPORT_SYMBOL(of_eeprom_cell_get_byname); + +/** + * eeprom_cell_put(): Release previously allocated eeprom cell. + * + * @cell: Previously allocated eeprom cell by eeprom_cell_get() + * or of_eeprom_cell_get() or of_eeprom_cell_get_byname(). + */ +void eeprom_cell_put(struct eeprom_cell *cell) +{ + struct eeprom_device *eeprom = cell->eeprom; + + atomic_dec(&eeprom->users); + module_put(eeprom->owner); + kfree(cell); +} +EXPORT_SYMBOL(eeprom_cell_put); + +/** + * eeprom_cell_read(): Read a given eeprom cell + * + * @cell: eeprom cell to be read. + * @len: pointer to length of cell which will be populated on successful read. + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a char * bufffer. The buffer should be freed by the consumer with a + * kfree(). + */ +char *eeprom_cell_read(struct eeprom_cell *cell, ssize_t *len) +{ + struct eeprom_device *eeprom = cell->eeprom; + char *buf; + int rc, i, offset = 0; + + if (!eeprom || !eeprom->regmap) + return ERR_PTR(-EINVAL); + + buf = kzalloc(cell->size, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < cell->nblocks; i++) { + rc = regmap_bulk_read(eeprom->regmap, cell->blocks[i].offset, + buf + offset, + cell->blocks[i].count); + + if (IS_ERR_VALUE(rc)) { + kfree(buf); + return ERR_PTR(rc); + } + offset += cell->blocks[i].count; + } + + *len = cell->size; + + return buf; +} +EXPORT_SYMBOL(eeprom_cell_read); + +/** + * eeprom_cell_write(): Write to a given eeprom cell + * + * @cell: eeprom cell to be written. + * @buf: Buffer to be written. + * @len: length of buffer to be written to eeprom cell. + * + * The return value will be an non zero on error or a zero on successful write. + */ +int eeprom_cell_write(struct eeprom_cell *cell, const char *buf, ssize_t len) +{ + struct eeprom_device *eeprom = cell->eeprom; + int i, rc, offset = 0; + + if (!eeprom || !eeprom->regmap || len != cell->size) + return -EINVAL; + + for (i = 0; i < cell->nblocks; i++) { + rc = regmap_bulk_write(eeprom->regmap, cell->blocks[i].offset, + buf + offset, + cell->blocks[i].count); + + if (IS_ERR_VALUE(rc)) + return rc; + + offset += cell->blocks[i].count; + } + + return len; +} +EXPORT_SYMBOL(eeprom_cell_write); + static int eeprom_init(void) { return class_register(&eeprom_class); diff --git a/include/linux/eeprom-consumer.h b/include/linux/eeprom-consumer.h new file mode 100644 index 0000000..e06f5e5 --- /dev/null +++ b/include/linux/eeprom-consumer.h @@ -0,0 +1,67 @@ +/* + * EEPROM framework consumer. + * + * Copyright (C) 2015 Srinivas Kandagatla + * Copyright (C) 2013 Maxime Ripard + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef _LINUX_EEPROM_CONSUMER_H +#define _LINUX_EEPROM_CONSUMER_H + +struct eeprom_cell; + +struct eeprom_block { + loff_t offset; + size_t count; +}; +#ifdef CONFIG_EEPROM +struct eeprom_cell *eeprom_cell_get(const char *ename, + struct eeprom_block *blocks, int nblocks); +void eeprom_cell_put(struct eeprom_cell *cell); +char *eeprom_cell_read(struct eeprom_cell *cell, ssize_t *len); +int eeprom_cell_write(struct eeprom_cell *cell, const char *buf, ssize_t len); +#else + +static inline struct eeprom_cell *eeprom_cell_get(const char *ename, + struct eeprom_block *blocks, int nblocks) +{ + return NULL; +} + +static inline void eeprom_cell_put(struct eeprom_cell *cell) +{ +} + +static inline char *eeprom_cell_read(struct eeprom_cell *cell, ssize_t *len) +{ + return NULL; +} + +static inline int eeprom_cell_write(struct eeprom_cell *cell, + const char *buf, ssize_t len) +{ + return -ENOSYS; +} +#endif /* CONFIG_EEPROM */ + +#if defined(CONFIG_EEPROM) && defined(CONFIG_OF) +struct eeprom_cell *of_eeprom_cell_get(struct device *dev, int index); +struct eeprom_cell *of_eeprom_cell_get_byname(struct device *dev, + const char *name); +#else +static inline struct eeprom_cell *of_eeprom_cell_get( + struct device *dev, int index) +{ + return NULL; +} +static inline struct eeprom_cell *of_eeprom_cell_get_byname(struct device *dev, + const char *name) +{ + return NULL; +} +#endif +#endif /* ifndef _LINUX_EEPROM_CONSUMER_H */