Hi Greg,
This patchset adds a new simple NVMEM framework to kernel, and it is tested
with various drivers like "QCOM thermal sensors", "QCOM cpr driver",
"begal bone cape manager" and few more on the way.
Thankyou all for providing inputs and comments on previous versions of this
patchset. Here is the v7 of the patchset addressing all the issues raised as
part of previous versions review.
Up until now, NVMEM drivers like eeprom 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.
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 NVMEMs.
After learning few things about QCOM qfprom and other eeprom/efuses, which
has packed fields at bit level. Which makes it important to add support to
such memories. This version adds support to this type of non volatile
memories by adding support to bit level nvmem-cells.
Having regmap interface to this framework would give much better
abstraction for nvmems on different buses.
patch 1-4 Introduces the NVMEM framework.
Patch 5-6 Adds Qualcomm specific qfprom driver.
Patch 7 migrates an existing driver to nvmem framework.
Patch 8 adds entry in MAINTAINERS.
Its also possible to migrate other nvmem drivers to this framework, and I think
some of them already posted patches based on this framework.
Providers APIs:
nvmem_register/unregister();
Consumers APIs:
Cell based apis for both DT/Non-DT:
nvmem_cell_get()/nvmem_cell_put();
devm_nvmem_cell_get()/devm_nvmem_cell_put();
of_nvmem_cell_get()
nvmem_cell_read()/nvmem_cell_write();
Raw byte access apis for both DT/non-DT.
nvmem_device_get()/nvmem_device_put()
devm_nvmem_device_get()/nvmem_device_put()
of_nvmem_device_get()
nvmem_device_read()/nvmem_device_write();
nvmem_device_cell_read()/nvmem_device_cell_write();
Device Tree:
/* Provider */
qfprom: qfprom@00700000 {
...
/* Data cells */
tsens_calibration: calib@404 {
reg = <0x404 0x10>;
};
tsens_calibration_bckp: calib_bckp@504 {
reg = <0x504 0x11>;
bit-offset = 6;
nbits = 128;
};
pvs_version: pvs-version@6 {
reg = <0x6 0x2>
bit-offset = 7;
nbits = 2;
};
speed_bin: speed-bin@c{
reg = <0xc 0x1>;
bit-offset = 2;
nbits = 3;
};
...
};
/* Consumer */
tsens {
...
nvmem-cells = <&tsens_calibration>;
nvmem-cell-names = "calibration";
};
userspace interface: binary file in /sys/bus/nvmem/devices/*/nvmem
ex:
hexdump /sys/bus/nvmem/devices/qfprom0/nvmem
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
00000a0 db10 2240 0000 e000 0c00 0c00 0000 0c00
0000000 0000 0000 0000 0000 0000 0000 0000 0000
...
*
0001000
Changes since v6 (https://lkml.org/lkml/2015/6/24/325)
* Replaced class usage with bus, suggested by Stephen Boyd.
* various cosmetic and header cleanups suggested by Stefan Wahren
* changed nvmem-cell property to nvmem-cells spotted by Rajendra Nayak
Changes since v5(https://lkml.org/lkml/2015/5/21/643)
* skipped regmap patches which are already merged by Mark.
* Fixed lot of style related comments by Stephen.
* Fixed sunxi driver.
* added devm_* variants requested by Stephen.
* added of_* variants requested by Pantelis Antoniou
* added read_only options for non-dt drivers.
* added basic how-to doc.
Changes since v4(https://lkml.org/lkml/2015/3/30/725)
* rename eeprom to nvmem suggested by Matt Porter
* added bit level support to nvmem cells, if not framework is
not usable for qcom platforms.
* removed ternary operator shortcut suggested by Mark B.
* unified consumer/provider apis for both DT and non-DT.
* added name support for cell.
* added bit level bindings.
* added read-only support suggested by Matt Porter and others.
* added new nvmem_device based consumer apis.
Changes since v3(https://lkml.org/lkml/2015/3/24/1066)
* simplified logic in bin_attr_eeprom_read/write spotted by Mark Brown.
* moved from using regmap_bulk_read/write to regmap_raw_read/write
spotted by Mark Brown
* Fixed return error codes for the dummy wrappers spotted by Sascha Hauer
* Fixed various error code checks in core spotted by Sascha Hauer.
* Simplified consumer bindings suggested by Sascha Hauer.
* Added eeprom-mmio helper functions.
Changes since v2(https://lkml.org/lkml/2015/3/13/168)
* Fixed error handling in eeprom_register spotted by Mark Brown
* Added new regmap_get_max_register() and regmap_get_reg_stride().
* Fixed module build errors reported by kbuild robot.
* recycle the ids when eeprom provider is released.
Changes since v1(https://lkml.org/lkml/2015/3/5/153)
* Fix various Licencing issues spotted by Paul Bolle and Mark Brown
* Allow eeprom core to build as module spotted by Paul Bolle.
* Fix various kconfig issues spotted by Paul Bolle.
* remove unessary atomic varible spotted by Mark Brown.
* Few cleanups and common up some of the code in core.
* Add qfprom bindings.
Changes since RFC(https://lkml.org/lkml/2015/2/19/307)
* Fix documentation and error checks in read/write spotted by Andrew Lunn
* Kconfig fix suggested by Stephen Boyd.
* Add module owner suggested by Stephen Boyd and others.
* Fix unsafe handling of eeprom in unregister spotted by Russell and Mark Brown.
* seperate bindings patch as suggested by Rob.
* Add MAINTAINERS as suggested by Rob.
* Added support to allow reading eeprom for things like serial number which
* canbe scatters across.
* Added eeprom data using reg property suggested by Sascha and Stephen.
* Added non-DT support.
* Move kerneldoc to the src files spotted by Mark Brown.
* Remove local list and do eeprom lookup by using class_find_device()
Thanks,
srini
Maxime Ripard (1):
nvmem: sunxi: Move the SID driver to the nvmem framework
Srinivas Kandagatla (8):
nvmem: Add a simple NVMEM framework for nvmem providers
nvmem: Add a simple NVMEM framework for consumers
nvmem: Add nvmem_device based consumer apis.
nvmem: Add bindings for simple nvmem framework
Documentation: nvmem: add nvmem api level and how-to doc
nvmem: qfprom: Add Qualcomm QFPROM support.
nvmem: qfprom: Add bindings for qfprom
nvmem: Add to MAINTAINERS for nvmem framework
Documentation/ABI/testing/sysfs-driver-sunxi-sid | 22 -
.../bindings/misc/allwinner,sunxi-sid.txt | 17 -
.../bindings/nvmem/allwinner,sunxi-sid.txt | 21 +
Documentation/devicetree/bindings/nvmem/nvmem.txt | 85 ++
Documentation/devicetree/bindings/nvmem/qfprom.txt | 35 +
Documentation/nvmem/nvmem.txt | 152 +++
MAINTAINERS | 9 +
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/misc/eeprom/Kconfig | 13 -
drivers/misc/eeprom/Makefile | 1 -
drivers/misc/eeprom/sunxi_sid.c | 156 ---
drivers/nvmem/Kconfig | 39 +
drivers/nvmem/Makefile | 12 +
drivers/nvmem/core.c | 1059 ++++++++++++++++++++
drivers/nvmem/qfprom.c | 87 ++
drivers/nvmem/sunxi_sid.c | 160 +++
include/linux/nvmem-consumer.h | 155 +++
include/linux/nvmem-provider.h | 48 +
19 files changed, 1865 insertions(+), 209 deletions(-)
delete mode 100644 Documentation/ABI/testing/sysfs-driver-sunxi-sid
delete mode 100644 Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt
create mode 100644 Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt
create mode 100644 Documentation/devicetree/bindings/nvmem/nvmem.txt
create mode 100644 Documentation/devicetree/bindings/nvmem/qfprom.txt
create mode 100644 Documentation/nvmem/nvmem.txt
delete mode 100644 drivers/misc/eeprom/sunxi_sid.c
create mode 100644 drivers/nvmem/Kconfig
create mode 100644 drivers/nvmem/Makefile
create mode 100644 drivers/nvmem/core.c
create mode 100644 drivers/nvmem/qfprom.c
create mode 100644 drivers/nvmem/sunxi_sid.c
create mode 100644 include/linux/nvmem-consumer.h
create mode 100644 include/linux/nvmem-provider.h
--
1.9.1
This patch adds just providers part of the framework just to enable easy
review.
Up until now, NVMEM drivers like eeprom 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 nvmems.
Having regmap interface to this framework would give much better
abstraction for nvmems on different buses.
Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
[Maxime Ripard: intial version of eeprom framework]
Signed-off-by: Srinivas Kandagatla <redacted>
---
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/nvmem/Kconfig | 13 ++
drivers/nvmem/Makefile | 6 +
drivers/nvmem/core.c | 380 +++++++++++++++++++++++++++++++++++++++++
include/linux/nvmem-provider.h | 54 ++++++
6 files changed, 456 insertions(+)
create mode 100644 drivers/nvmem/Kconfig
create mode 100644 drivers/nvmem/Makefile
create mode 100644 drivers/nvmem/core.c
create mode 100644 include/linux/nvmem-provider.h
This patch adds just consumers part of the framework just to enable easy
review.
Up until now, nvmem 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 nvmems.
Having regmap interface to this framework would give much better
abstraction for nvmems on different buses.
Signed-off-by: Maxime Ripard <redacted>
[Maxime Ripard: intial version of the framework]
Signed-off-by: Srinivas Kandagatla <redacted>
---
drivers/nvmem/core.c | 424 ++++++++++++++++++++++++++++++++++++++++-
include/linux/nvmem-consumer.h | 76 ++++++++
2 files changed, 498 insertions(+), 2 deletions(-)
create mode 100644 include/linux/nvmem-consumer.h
@@ -361,6 +363,424 @@ int nvmem_unregister(struct nvmem_device *nvmem)}EXPORT_SYMBOL_GPL(nvmem_unregister);+staticstructnvmem_device*__nvmem_device_get(structdevice_node*np,+structnvmem_cell**cellp,+constchar*cell_id)+{+structnvmem_device*nvmem=NULL;++mutex_lock(&nvmem_mutex);++if(np){+nvmem=of_nvmem_find(np);+if(!nvmem){+mutex_unlock(&nvmem_mutex);+returnERR_PTR(-EPROBE_DEFER);+}+}else{+structnvmem_cell*cell=nvmem_find_cell(cell_id);++if(cell){+nvmem=cell->nvmem;+*cellp=cell;+}++if(!nvmem){+mutex_unlock(&nvmem_mutex);+returnERR_PTR(-ENOENT);+}+}++nvmem->users++;+mutex_unlock(&nvmem_mutex);++if(!try_module_get(nvmem->owner)){+dev_err(&nvmem->dev,+"could not increase module refcount for cell %s\n",+nvmem->name);++mutex_lock(&nvmem_mutex);+nvmem->users--;+mutex_unlock(&nvmem_mutex);++returnERR_PTR(-EINVAL);+}++returnnvmem;+}++staticvoid__nvmem_device_put(structnvmem_device*nvmem)+{+module_put(nvmem->owner);+mutex_lock(&nvmem_mutex);+nvmem->users--;+mutex_unlock(&nvmem_mutex);+}++staticstructnvmem_cell*nvmem_cell_get_from_list(constchar*cell_id)+{+structnvmem_cell*cell=NULL;+structnvmem_device*nvmem;++nvmem=__nvmem_device_get(NULL,&cell,cell_id);+if(IS_ERR(nvmem))+returnERR_CAST(nvmem);+++returncell;++}++#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF)+/**+*of_nvmem_cell_get()-Getanvmemcellfromgivendevicenodeandcellid+*+*@devnode:Devicetreenodethatusesthenvmemcell+*@id:nvmemcellnamefromnvmem-cell-namesproperty.+*+*ThereturnvaluewillbeanERR_PTR()onerrororavalidpointer+*toastructnvmem_cell.Thenvmem_cellwillbefreedbythe+*nvmem_cell_put().+*/+structnvmem_cell*of_nvmem_cell_get(structdevice_node*np,+constchar*name)+{+structdevice_node*cell_np,*nvmem_np;+structnvmem_cell*cell;+structnvmem_device*nvmem;+const__be32*addr;+intrval,len,index;++index=of_property_match_string(np,"nvmem-cell-names",name);++cell_np=of_parse_phandle(np,"nvmem-cells",index);+if(!cell_np)+returnERR_PTR(-EINVAL);++nvmem_np=of_get_next_parent(cell_np);+if(!nvmem_np)+returnERR_PTR(-EINVAL);++nvmem=__nvmem_device_get(nvmem_np,NULL,NULL);+if(IS_ERR(nvmem))+returnERR_CAST(nvmem);++addr=of_get_property(cell_np,"reg",&len);+if(!addr||(len<2*sizeof(int))){+dev_err(&nvmem->dev,"nvmem: invalid reg on %s\n",+cell_np->full_name);+rval=-EINVAL;+gotoerr_mem;+}++cell=kzalloc(sizeof(*cell),GFP_KERNEL);+if(!cell){+rval=-ENOMEM;+gotoerr_mem;+}++cell->nvmem=nvmem;+cell->offset=be32_to_cpup(addr++);+cell->bytes=be32_to_cpup(addr);+cell->name=cell_np->name;++of_property_read_u32(cell_np,"bit-offset",&cell->bit_offset);+of_property_read_u32(cell_np,"nbits",&cell->nbits);++if(cell->nbits)+cell->bytes=DIV_ROUND_UP(cell->nbits+cell->bit_offset,+BITS_PER_BYTE);++if(!IS_ALIGNED(cell->offset,nvmem->stride)){+dev_err(&nvmem->dev,+"cell %s unaligned to nvmem stride %d\n",+cell->name,nvmem->stride);+rval=-EINVAL;+gotoerr_sanity;+}++nvmem_cell_add(cell);++returncell;++err_sanity:+kfree(cell);++err_mem:+__nvmem_device_put(nvmem);++returnERR_PTR(rval);++}+EXPORT_SYMBOL_GPL(of_nvmem_cell_get);+#endif++/**+*nvmem_cell_get()-Getnvmemcellofdeviceformagivencellname+*+*@devnode:Devicetreenodethatusesthenvmemcell+*@id:nvmemcellnametoget.+*+*ThereturnvaluewillbeanERR_PTR()onerrororavalidpointer+*toastructnvmem_cell.Thenvmem_cellwillbefreedbythe+*nvmem_cell_put().+*/+structnvmem_cell*nvmem_cell_get(structdevice*dev,constchar*cell_id)+{+structnvmem_cell*cell;++if(dev->of_node){/* try dt first */+cell=of_nvmem_cell_get(dev->of_node,cell_id);+if(!IS_ERR(cell)||PTR_ERR(cell)==-EPROBE_DEFER)+returncell;+}++returnnvmem_cell_get_from_list(cell_id);++}+EXPORT_SYMBOL_GPL(nvmem_cell_get);++staticvoiddevm_nvmem_cell_release(structdevice*dev,void*res)+{+nvmem_cell_put(*(structnvmem_cell**)res);+}++/**+*devm_nvmem_cell_get()-Getnvmemcellofdeviceformagivenid+*+*@devnode:Devicetreenodethatusesthenvmemcell+*@id:nvmemidinnvmem-namesproperty.+*+*ThereturnvaluewillbeanERR_PTR()onerrororavalidpointer+*toastructnvmem_cell.Thenvmem_cellwillbefreedbythe+*automaticallyoncethedeviceisfreed.+*/+structnvmem_cell*devm_nvmem_cell_get(structdevice*dev,constchar*id)+{+structnvmem_cell**ptr,*cell;++ptr=devres_alloc(devm_nvmem_cell_release,sizeof(*ptr),GFP_KERNEL);+if(!ptr)+returnERR_PTR(-ENOMEM);++cell=nvmem_cell_get(dev,id);+if(!IS_ERR(cell)){+*ptr=cell;+devres_add(dev,ptr);+}else{+devres_free(ptr);+}++returncell;+}+EXPORT_SYMBOL_GPL(devm_nvmem_cell_get);++staticintdevm_nvmem_cell_match(structdevice*dev,void*res,void*data)+{+structnvmem_cell**c=res;++if(!c||!*c){+WARN_ON(!c||!*c);+return0;+}+return*c==data;+}++/**+*devm_nvmem_cell_put()-Releasepreviouslyallocatednvmemcell+*fromdevm_nvmem_cell_get.+*+*@cell:Previouslyallocatednvmemcellbydevm_nvmem_cell_get()+*/+voiddevm_nvmem_cell_put(structdevice*dev,structnvmem_cell*cell)+{+intret;++ret=devres_release(dev,devm_nvmem_cell_release,+devm_nvmem_cell_match,cell);++WARN_ON(ret);+}+EXPORT_SYMBOL(devm_nvmem_cell_put);++/**+*nvmem_cell_put()-Releasepreviouslyallocatednvmemcell.+*+*@cell:Previouslyallocatednvmemcellbynvmem_cell_get()+*/+voidnvmem_cell_put(structnvmem_cell*cell)+{+structnvmem_device*nvmem=cell->nvmem;++__nvmem_device_put(nvmem);+nvmem_cell_drop(cell);+}+EXPORT_SYMBOL_GPL(nvmem_cell_put);++staticinlinevoidnvmem_shift_read_buffer_in_place(structnvmem_cell*cell,+void*buf)+{+u8*p,*b;+inti,bit_offset=cell->bit_offset;++p=b=buf;+if(bit_offset){+/* First shift */+*b++>>=bit_offset;++/* setup rest of the bytes if any */+for(i=1;i<cell->bytes;i++){+/* Get bits from next byte and shift them towards msb */+*p|=*b<<(BITS_PER_BYTE-bit_offset);++p=b;+*b++>>=bit_offset;+}++/* result fits in less bytes */+if(cell->bytes!=DIV_ROUND_UP(cell->nbits,BITS_PER_BYTE))+*p--=0;+}+/* clear msb bits if any leftover in the last byte */+*p&=GENMASK((cell->nbits%BITS_PER_BYTE)-1,0);+}++staticint__nvmem_cell_read(structnvmem_device*nvmem,+structnvmem_cell*cell,+void*buf,size_t*len)+{+intrc;++rc=regmap_raw_read(nvmem->regmap,cell->offset,buf,cell->bytes);++if(IS_ERR_VALUE(rc))+returnrc;++/* shift bits in-place */+if(cell->bit_offset||cell->bit_offset)+nvmem_shift_read_buffer_in_place(cell,buf);++*len=cell->bytes;++return0;+}+/**+*nvmem_cell_read()-Readagivennvmemcell+*+*@cell:nvmemcelltoberead.+*@len:pointertolengthofcellwhichwillbepopulatedonsuccessfulread.+*+*ThereturnvaluewillbeanERR_PTR()onerrororavalidpointer+*toachar*bufffer.Thebuffershouldbefreedbytheconsumerwitha+*kfree().+*/+void*nvmem_cell_read(structnvmem_cell*cell,size_t*len)+{+structnvmem_device*nvmem=cell->nvmem;+u8*buf;+intrc;++if(!nvmem||!nvmem->regmap)+returnERR_PTR(-EINVAL);++buf=kzalloc(cell->bytes,GFP_KERNEL);+if(!buf)+returnERR_PTR(-ENOMEM);++rc=__nvmem_cell_read(nvmem,cell,buf,len);+if(IS_ERR_VALUE(rc)){+kfree(buf);+returnERR_PTR(rc);+}++returnbuf;+}+EXPORT_SYMBOL_GPL(nvmem_cell_read);++staticinlinevoid*nvmem_cell_prepare_write_buffer(structnvmem_cell*cell,+u8*_buf,intlen)+{+structnvmem_device*nvmem=cell->nvmem;+inti,rc,nbits,bit_offset=cell->bit_offset;+u8v,*p,*buf,*b,pbyte,pbits;++nbits=cell->nbits;+buf=kzalloc(cell->bytes,GFP_KERNEL);+if(!buf)+returnERR_PTR(-ENOMEM);++memcpy(buf,_buf,len);+p=b=buf;++if(bit_offset){+pbyte=*b;+*b<<=bit_offset;++/* setup the first byte with lsb bits from nvmem */+rc=regmap_raw_read(nvmem->regmap,cell->offset,&v,1);+*b++|=GENMASK(bit_offset-1,0)&v;++/* setup rest of the byte if any */+for(i=1;i<cell->bytes;i++){+/* Get last byte bits and shift them towards lsb */+pbits=pbyte>>(BITS_PER_BYTE-1-bit_offset);+pbyte=*b;+p=b;+*b<<=bit_offset;+*b++|=pbits;+}+}++/* if it's not end on byte boundary */+if((nbits+bit_offset)%BITS_PER_BYTE){+/* setup the last byte with msb bits from nvmem */+rc=regmap_raw_read(nvmem->regmap,+cell->offset+cell->bytes-1,&v,1);+*p|=GENMASK(7,(nbits+bit_offset)%BITS_PER_BYTE)&v;++}++returnbuf;+}++/**+*nvmem_cell_write()-Writetoagivennvmemcell+*+*@cell:nvmemcelltobewritten.+*@buf:Buffertobewritten.+*@len:lengthofbuffertobewrittentonvmemcell.+*+*Thereturnvaluewillbeanlengthofbyteswrittenornonzeroonfailure.+*/+intnvmem_cell_write(structnvmem_cell*cell,void*buf,size_tlen)+{+structnvmem_device*nvmem=cell->nvmem;+intrc;+void*wbuf=buf;++if(!nvmem||!nvmem->regmap||nvmem->read_only||+(cell->bit_offset==0&&len!=cell->bytes))+return-EINVAL;++if(cell->bit_offset||cell->nbits){+wbuf=nvmem_cell_prepare_write_buffer(cell,buf,len);+if(IS_ERR(wbuf))+returnPTR_ERR(wbuf);+}++rc=regmap_raw_write(nvmem->regmap,cell->offset,wbuf,cell->bytes);++/* free the tmp buffer */+if(cell->bit_offset)+kfree(wbuf);++if(IS_ERR_VALUE(rc))+returnrc;++returnlen;+}+EXPORT_SYMBOL_GPL(nvmem_cell_write);+staticint__initnvmem_init(void){returnbus_register(&nvmem_bus_type);
This patch adds bindings for simple nvmem framework which allows nvmem
consumers to talk to nvmem providers to get access to nvmem cell data.
Signed-off-by: Maxime Ripard <redacted>
[Maxime Ripard: intial version of eeprom framework]
Signed-off-by: Srinivas Kandagatla <redacted>
---
Documentation/devicetree/bindings/nvmem/nvmem.txt | 85 +++++++++++++++++++++++
1 file changed, 85 insertions(+)
create mode 100644 Documentation/devicetree/bindings/nvmem/nvmem.txt
@@ -0,0 +1,85 @@+= NVMEM(Non Volatile Memory) Data Device Tree Bindings =++This binding is intended to represent the location of hardware+configuration data stored in NVMEMs like eeprom, efuses and so on.++On a significant proportion of boards, the manufacturer has stored+some data on NVMEM, for the OS to be able to retrieve these information+and act upon it. Obviously, the OS has to know about where to retrieve+these data from, and where they are stored on the storage device.++This document is here to document this.++= Data providers =+Contains bindings specific to provider drivers and data cells as children+of this node.++Optional properties:+ read-only: Mark the provider as read only.++= Data cells =+These are the child nodes of the provider which contain data cell+information like offset and size in nvmem provider.++Required properties:+reg: specifies the offset in byte within that storage device, start bit+ in the byte and the length in bytes of the data we care about.+ There could be more than one offset-length pairs in this property.++Optional properties:++bit-offset: specifies the offset in bit within the address range specified+ by reg property. Can take values from 0-7.+nbits: specifies number of bits this cell occupies starting from bit-offset.++For example:++ /* Provider */+ qfprom: qfprom@00700000 {+ ...++ /* Data cells */+ tsens_calibration: calib@404 {+ reg = <0x404 0x10>;+ };++ tsens_calibration_bckp: calib_bckp@504 {+ reg = <0x504 0x11>;+ bit-offset = 6;+ nbits = 128;+ };++ pvs_version: pvs-version@6 {+ reg = <0x6 0x2>+ bit-offset = 7;+ nbits = 2;+ };++ speed_bin: speed-bin@c{+ reg = <0xc 0x1>;+ bit-offset = 2;+ nbits = 3;++ };+ ...+ };++= Data consumers =+Are device nodes which consume nvmem data cells/providers.++Required-properties:+nvmem-cells: list of phandle to the nvmem data cells.+nvmem-cell-names: names for the each nvmem-cells specified. Required if+ nvmem-cells is used.++Optional-properties:+nvmem : list of phandles to nvmem providers.+nvmem-names: names for the each nvmem provider. required if nvmem is used.++For example:++ tsens {+ ...+ nvmem-cells = <&tsens_calibration>;+ nvmem-cell-names = "calibration";+ };
@@ -0,0 +1,152 @@+ NVMEM SUBSYSTEM+ Srinivas Kandagatla <srinivas.kandagatla@linaro.org>++This document explains the Simple NVMEM Framework along with the APIs provided,+and how-to-use.++1. Introduction+===============+*NVMEM* is the abbreviation for Non Volatile Memory layer. It is used to+retrieve configuration or SOC or Device specific data from a non volatile memories+like eeprom, efuses and so on.++Up until now, NVMEM drivers like eeprom 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.++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 NVMEMs.+This framework is based on regmap, so that most of the abstraction+available in regmap can be reused, across multiple types of buses.++NVMEM Providers++++++++++++++++++NVMEM provider refers to an entity that implements methods to initialize, read+and write the non-volatile memory.++2. Registering/Unregistering the NVMEM provider+===============================================++A NVMEM provider can register with NVMEM core by suppling relevant+nvmem configuration to nvmem_register(), on success core would return a valid+nvmem_device pointer.++nvmem_unregister(nvmem) is used to unregister the already registered provider.++For example for simple qfprom case:++static struct nvmem_config econfig = {+ .name = "qfprom",+ .owner = THIS_MODULE,+};++static int qfprom_probe(struct platform_device *pdev)+{+ ...+ econfig.dev = &pdev->dev;+ nvmem = nvmem_register(&econfig);+ ...+}++It is mandatory that the NVMEM provider has a regmap associated with its+struct device.++NVMEM Consumers++++++++++++++++++NVMEM consumers are the entities which make use of the NVMEM provider to+read/write into NVMEM.++3. NVMEM cell based consumer APIs.+=================================++NVMEM cells are the data entries/fields in the NVMEM.+The NVMEM framework provides 3 APIs to read/write NVMEM cells.++struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *name);+struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, const char *name);++void nvmem_cell_put(struct nvmem_cell *cell);+void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell);++void *nvmem_cell_read(struct nvmem_cell *cell, ssize_t *len);+int nvmem_cell_write(struct nvmem_cell *cell, void *buf, ssize_t len);++*nvmem_cell_get() apis will get a reference to nvmem cell for a given id,+and nvmem_cell_read/write() can then directly read or write to the cell.+Once the usage of the cell is finished the consumer should call *nvmem_cell_put()+to free all the allocation memory for the cell.++4. Direct NVMEM device based consumer APIs.+==========================================++In some instances it is necessary to directly read/write the NVMEM.+To facilitate such consumers NVMEM framework provides below apis.++struct nvmem_device *nvmem_device_get(struct device *dev, const char *name);+struct nvmem_device *devm_nvmem_device_get(struct device *dev,+ const char *name);+void nvmem_device_put(struct nvmem_device *nvmem);+int nvmem_device_read(struct nvmem_device *nvmem, unsigned int offset,+ size_t bytes, void *buf);+int nvmem_device_write(struct nvmem_device *nvmem, unsigned int offset,+ size_t bytes, void *buf);+int nvmem_device_cell_read(struct nvmem_device *nvmem,+ struct nvmem_cell_info *info, void *buf);+int nvmem_device_cell_write(struct nvmem_device *nvmem,+ struct nvmem_cell_info *info, void *buf);++Before the consumers can read/write NVMEM directly, it should get hold+of nvmem_controller from one of the *nvmem_device_get() api.++Difference between these apis and cell based apis is that these apis+always take nvmem_device as parameter.++5. Releasing a reference to the NVMEM+=====================================++When the consumers no longer needs the NVMEM, it has to release the reference+to the NVMEM it has obtained using the APIs mentioned in the above section.+NVMEM framework provides 2 APIs to release a reference to the NVMEM.++void nvmem_cell_put(struct nvmem_cell *cell);+void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell);+void nvmem_device_put(struct nvmem_device *nvmem);+void devm_nvmem_device_put(struct device *dev, struct nvmem_device *nvmem);++Both these APIs are used to release a reference to the NVMEM and+devm_nvmem_cell_put and devm_nvmem_device_put destroys the devres associated+with this NVMEM.++Userspace++++++++++++6. Userspace binary interface.+==============================++Userspace can read/write the raw NVMEM file located at+/sys/bus/nvmem/devices/*/nvmem++ex:++hexdump /sys/bus/nvmem/devices/qfprom0/nvmem++0000000 0000 0000 0000 0000 0000 0000 0000 0000+*+00000a0 db10 2240 0000 e000 0c00 0c00 0000 0c00+0000000 0000 0000 0000 0000 0000 0000 0000 0000+...+*+0001000++7. DeviceTree Binding+=====================++The documentation for NVMEM dt binding can be found @+Documentation/devicetree/bindings/nvmem/nvmem.txt
This patch adds QFPROM support driver which is used by other drivers
like thermal sensor and cpufreq.
On MSM parts there are some efuses (called qfprom) these fuses store
things like calibration data, speed bins.. etc. Drivers like cpufreq,
thermal sensors would read out this data for configuring the driver.
Signed-off-by: Srinivas Kandagatla <redacted>
---
drivers/nvmem/Kconfig | 15 +++++++++
drivers/nvmem/Makefile | 4 +++
drivers/nvmem/qfprom.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 106 insertions(+)
create mode 100644 drivers/nvmem/qfprom.c
@@ -1,22 +0,0 @@-What: /sys/devices/*/<our-device>/eeprom-Date: August 2013-Contact: Oliver Schinagl <oliver@schinagl.nl>-Description: read-only access to the SID (Security-ID) on current- A-series SoC's from Allwinner. Currently supports A10, A10s, A13- and A20 CPU's. The earlier A1x series of SoCs exports 16 bytes,- whereas the newer A20 SoC exposes 512 bytes split into sections.- Besides the 16 bytes of SID, there's also an SJTAG area,- HDMI-HDCP key and some custom keys. Below a quick overview, for- details see the user manual:- 0x000 128 bit root-key (sun[457]i)- 0x010 128 bit boot-key (sun7i)- 0x020 64 bit security-jtag-key (sun7i)- 0x028 16 bit key configuration (sun7i)- 0x02b 16 bit custom-vendor-key (sun7i)- 0x02c 320 bit low general key (sun7i)- 0x040 32 bit read-control access (sun7i)- 0x064 224 bit low general key (sun7i)- 0x080 2304 bit HDCP-key (sun7i)- 0x1a0 768 bit high general key (sun7i)-Users: any user space application which wants to read the SID on- Allwinner's A-series of CPU's.
@@ -0,0 +1,21 @@+Allwinner sunxi-sid++Required properties:+- compatible: "allwinner,sun4i-a10-sid" or "allwinner,sun7i-a20-sid"+- reg: Should contain registers location and length++= Data cells =+Are child nodes of qfprom, bindings of which as described in+bindings/nvmem/nvmem.txt++Example for sun4i:+ sid@01c23800 {+ compatible = "allwinner,sun4i-a10-sid";+ reg = <0x01c23800 0x10>+ };++Example for sun7i:+ sid@01c23800 {+ compatible = "allwinner,sun7i-a20-sid";+ reg = <0x01c23800 0x200>+ };
@@ -1,156 +0,0 @@-/*- * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl>- * http://www.linux-sunxi.org- *- * This program is free software; you can redistribute it and/or modify- * it under the terms of the GNU General Public License as published by- * the Free Software Foundation; either version 2 of the License, or- * (at your option) any later version.- *- * This program is distributed in the hope that it will be useful,- * but WITHOUT ANY WARRANTY; without even the implied warranty of- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- * GNU General Public License for more details.- *- * This driver exposes the Allwinner security ID, efuses exported in byte-- * sized chunks.- */--#include <linux/compiler.h>-#include <linux/device.h>-#include <linux/err.h>-#include <linux/export.h>-#include <linux/fs.h>-#include <linux/io.h>-#include <linux/kernel.h>-#include <linux/kobject.h>-#include <linux/module.h>-#include <linux/of_device.h>-#include <linux/platform_device.h>-#include <linux/random.h>-#include <linux/slab.h>-#include <linux/stat.h>-#include <linux/sysfs.h>-#include <linux/types.h>--#define DRV_NAME "sunxi-sid"--struct sunxi_sid_data {- void __iomem *reg_base;- unsigned int keysize;-};--/* We read the entire key, due to a 32 bit read alignment requirement. Since we- * want to return the requested byte, this results in somewhat slower code and- * uses 4 times more reads as needed but keeps code simpler. Since the SID is- * only very rarely probed, this is not really an issue.- */-static u8 sunxi_sid_read_byte(const struct sunxi_sid_data *sid_data,- const unsigned int offset)-{- u32 sid_key;-- if (offset >= sid_data->keysize)- return 0;-- sid_key = ioread32be(sid_data->reg_base + round_down(offset, 4));- sid_key >>= (offset % 4) * 8;-- return sid_key; /* Only return the last byte */-}--static ssize_t sid_read(struct file *fd, struct kobject *kobj,- struct bin_attribute *attr, char *buf,- loff_t pos, size_t size)-{- struct platform_device *pdev;- struct sunxi_sid_data *sid_data;- int i;-- pdev = to_platform_device(kobj_to_dev(kobj));- sid_data = platform_get_drvdata(pdev);-- if (pos < 0 || pos >= sid_data->keysize)- return 0;- if (size > sid_data->keysize - pos)- size = sid_data->keysize - pos;-- for (i = 0; i < size; i++)- buf[i] = sunxi_sid_read_byte(sid_data, pos + i);-- return i;-}--static struct bin_attribute sid_bin_attr = {- .attr = { .name = "eeprom", .mode = S_IRUGO, },- .read = sid_read,-};--static int sunxi_sid_remove(struct platform_device *pdev)-{- device_remove_bin_file(&pdev->dev, &sid_bin_attr);- dev_dbg(&pdev->dev, "driver unloaded\n");-- return 0;-}--static const struct of_device_id sunxi_sid_of_match[] = {- { .compatible = "allwinner,sun4i-a10-sid", .data = (void *)16},- { .compatible = "allwinner,sun7i-a20-sid", .data = (void *)512},- {/* sentinel */},-};-MODULE_DEVICE_TABLE(of, sunxi_sid_of_match);--static int sunxi_sid_probe(struct platform_device *pdev)-{- struct sunxi_sid_data *sid_data;- struct resource *res;- const struct of_device_id *of_dev_id;- u8 *entropy;- unsigned int i;-- sid_data = devm_kzalloc(&pdev->dev, sizeof(struct sunxi_sid_data),- GFP_KERNEL);- if (!sid_data)- return -ENOMEM;-- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);- sid_data->reg_base = devm_ioremap_resource(&pdev->dev, res);- if (IS_ERR(sid_data->reg_base))- return PTR_ERR(sid_data->reg_base);-- of_dev_id = of_match_device(sunxi_sid_of_match, &pdev->dev);- if (!of_dev_id)- return -ENODEV;- sid_data->keysize = (int)of_dev_id->data;-- platform_set_drvdata(pdev, sid_data);-- sid_bin_attr.size = sid_data->keysize;- if (device_create_bin_file(&pdev->dev, &sid_bin_attr))- return -ENODEV;-- entropy = kzalloc(sizeof(u8) * sid_data->keysize, GFP_KERNEL);- for (i = 0; i < sid_data->keysize; i++)- entropy[i] = sunxi_sid_read_byte(sid_data, i);- add_device_randomness(entropy, sid_data->keysize);- kfree(entropy);-- dev_dbg(&pdev->dev, "loaded\n");-- return 0;-}--static struct platform_driver sunxi_sid_driver = {- .probe = sunxi_sid_probe,- .remove = sunxi_sid_remove,- .driver = {- .name = DRV_NAME,- .of_match_table = sunxi_sid_of_match,- },-};-module_platform_driver(sunxi_sid_driver);--MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>");-MODULE_DESCRIPTION("Allwinner sunxi security id driver");-MODULE_LICENSE("GPL");
@@ -0,0 +1,160 @@+/*+*AllwinnersunXiSoCsSecurityIDsupport.+*+*Copyright(c)2013OliverSchinagl<oliver@schinagl.nl>+*Copyright(C)2014MaximeRipard<maxime.ripard@free-electrons.com>+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;eitherversion2oftheLicense,or+*(atyouroption)anylaterversion.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.Seethe+*GNUGeneralPublicLicenseformoredetails.+*+*/+++#include<linux/platform_device.h>+#include<linux/nvmem-provider.h>+#include<linux/slab.h>+#include<linux/regmap.h>+#include<linux/device.h>+#include<linux/io.h>+#include<linux/module.h>+#include<linux/delay.h>+#include<linux/of.h>+++staticstructnvmem_configeconfig={+.name="sunxi-sid",+.read_only=true,+.owner=THIS_MODULE,+};++structsunxi_sid{+void__iomem*base;+};++/* We read the entire key, due to a 32 bit read alignment requirement. Since we+*wanttoreturntherequestedbyte,thisresultsinsomewhatslowercodeand+*uses4timesmorereadsasneededbutkeepscodesimpler.SincetheSIDis+*onlyveryrarelyprobed,thisisnotreallyanissue.+*/+staticu8sunxi_sid_read_byte(conststructsunxi_sid*sid,+constunsignedintoffset)+{+u32sid_key;++sid_key=ioread32be(sid->base+round_down(offset,4));+sid_key>>=(offset%4)*8;++returnsid_key;/* Only return the last byte */+}++staticintsunxi_sid_read(void*context,+constvoid*reg,size_treg_size,+void*val,size_tval_size)+{+structsunxi_sid*sid=context;+unsignedintoffset=*(u32*)reg;+u8*buf=val;++while(val_size){+*buf++=sunxi_sid_read_byte(sid,offset);+val_size--;+offset++;+}++return0;+}++staticintsunxi_sid_write(void*context,constvoid*data,size_tcount)+{+/* Unimplemented, dummy to keep regmap core happy */+return0;+}++staticstructregmap_bussunxi_sid_bus={+.read=sunxi_sid_read,+.write=sunxi_sid_write,+.reg_format_endian_default=REGMAP_ENDIAN_NATIVE,+.val_format_endian_default=REGMAP_ENDIAN_NATIVE,+};++staticboolsunxi_sid_writeable_reg(structdevice*dev,unsignedintreg)+{+returnfalse;+}++staticstructregmap_configsunxi_sid_regmap_config={+.reg_bits=32,+.val_bits=8,+.reg_stride=1,+.writeable_reg=sunxi_sid_writeable_reg,+};++staticintsunxi_sid_probe(structplatform_device*pdev)+{+structdevice*dev=&pdev->dev;+structresource*res;+structnvmem_device*nvmem;+structregmap*regmap;+structsunxi_sid*sid;++sid=devm_kzalloc(dev,sizeof(*sid),GFP_KERNEL);+if(!sid)+return-ENOMEM;++res=platform_get_resource(pdev,IORESOURCE_MEM,0);+sid->base=devm_ioremap_resource(dev,res);+if(IS_ERR(sid->base))+returnPTR_ERR(sid->base);++sunxi_sid_regmap_config.max_register=resource_size(res)-1;++regmap=devm_regmap_init(dev,&sunxi_sid_bus,sid,+&sunxi_sid_regmap_config);+if(IS_ERR(regmap)){+dev_err(dev,"regmap init failed\n");+returnPTR_ERR(regmap);+}+econfig.dev=dev;+nvmem=nvmem_register(&econfig);+if(IS_ERR(nvmem))+returnPTR_ERR(nvmem);++platform_set_drvdata(pdev,nvmem);++return0;+}++staticintsunxi_sid_remove(structplatform_device*pdev)+{+structnvmem_device*nvmem=platform_get_drvdata(pdev);++returnnvmem_unregister(nvmem);+}++staticconststructof_device_idsunxi_sid_of_match[]={+{.compatible="allwinner,sun4i-a10-sid"},+{.compatible="allwinner,sun7i-a20-sid"},+{/* sentinel */},+};+MODULE_DEVICE_TABLE(of,sunxi_sid_of_match);++staticstructplatform_driversunxi_sid_driver={+.probe=sunxi_sid_probe,+.remove=sunxi_sid_remove,+.driver={+.name="eeprom-sunxi-sid",+.of_match_table=sunxi_sid_of_match,+},+};+module_platform_driver(sunxi_sid_driver);++MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>");+MODULE_DESCRIPTION("Allwinner sunxi security id driver");+MODULE_LICENSE("GPL");
This patch adds bindings for qfprom found in QCOM SOCs. QFPROM driver
is based on simple nvmem framework.
Reviewed-by: Stephen Boyd <redacted>
Signed-off-by: Srinivas Kandagatla <redacted>
---
Documentation/devicetree/bindings/nvmem/qfprom.txt | 35 ++++++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 Documentation/devicetree/bindings/nvmem/qfprom.txt
@@ -0,0 +1,35 @@+= Qualcomm QFPROM device tree bindings =++This binding is intended to represent QFPROM which is found in most QCOM SOCs.++Required properties:+- compatible: should be "qcom,qfprom"+- reg: Should contain registers location and length++= Data cells =+Are child nodes of qfprom, bindings of which as described in+bindings/nvmem/nvmem.txt++Example:++ qfprom: qfprom@00700000 {+ compatible = "qcom,qfprom";+ reg = <0x00700000 0x8000>;+ ...+ /* Data cells */+ tsens_calibration: calib@404 {+ reg = <0x4404 0x10>;+ };+ };+++= Data consumers =+Are device nodes which consume nvmem data cells.++For example:++ tsens {+ ...+ nvmem-cells = <&tsens_calibration>;+ nvmem-cell-names = "calibration";+ };
This patch adds read/write apis which are based on nvmem_device. It is
common that the drivers like omap cape manager or qcom cpr driver to
access bytes directly at particular offset in the eeprom and not from
nvmem cell info in DT. These driver would need to get access to the nvmem
directly, which is what these new APIS provide.
These wrapper apis would help such users to avoid code duplication in
there drivers and also avoid them reading a big eeprom blob and parsing
it internally in there driver.
Signed-off-by: Srinivas Kandagatla <redacted>
---
drivers/nvmem/core.c | 259 +++++++++++++++++++++++++++++++++++++++++
include/linux/nvmem-consumer.h | 79 +++++++++++++
include/linux/nvmem-provider.h | 10 +-
3 files changed, 340 insertions(+), 8 deletions(-)
+/**
+ * nvmem_cell_read() - Read a given nvmem cell
+ *
+ * @cell: nvmem 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
One too many f's in buffer, it's returning a void *
+/**
+ * nvmem_cell_write() - Write to a given nvmem cell
+ *
+ * @cell: nvmem cell to be written.
+ * @buf: Buffer to be written.
+ * @len: length of buffer to be written to nvmem cell.
+ *
+ * The return value will be an length of bytes written or non zero on failure.
From: Rob Herring <hidden> Date: 2015-07-10 19:04:52
On Fri, Jul 10, 2015 at 4:45 AM, Srinivas Kandagatla
[off-list ref] wrote:
quoted hunk
This patch adds bindings for simple nvmem framework which allows nvmem
consumers to talk to nvmem providers to get access to nvmem cell data.
Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
[Maxime Ripard: intial version of eeprom framework]
Signed-off-by: Srinivas Kandagatla <redacted>
---
Documentation/devicetree/bindings/nvmem/nvmem.txt | 85 +++++++++++++++++++++++
1 file changed, 85 insertions(+)
create mode 100644 Documentation/devicetree/bindings/nvmem/nvmem.txt
@@ -0,0 +1,85 @@+= NVMEM(Non Volatile Memory) Data Device Tree Bindings =++This binding is intended to represent the location of hardware+configuration data stored in NVMEMs like eeprom, efuses and so on.++On a significant proportion of boards, the manufacturer has stored+some data on NVMEM, for the OS to be able to retrieve these information+and act upon it. Obviously, the OS has to know about where to retrieve+these data from, and where they are stored on the storage device.++This document is here to document this.++= Data providers =+Contains bindings specific to provider drivers and data cells as children+of this node.
#address-cells and #size-cells are required here.
+
+Optional properties:
+ read-only: Mark the provider as read only.
Couldn't this be per field rather than global?
+
+= Data cells =
+These are the child nodes of the provider which contain data cell
+information like offset and size in nvmem provider.
+
+Required properties:
+reg: specifies the offset in byte within that storage device, start bit
+ in the byte and the length in bytes of the data we care about.
+ There could be more than one offset-length pairs in this property.
+
+Optional properties:
+
+bit-offset: specifies the offset in bit within the address range specified
+ by reg property. Can take values from 0-7.
+nbits: specifies number of bits this cell occupies starting from bit-offset.
How about just: "bits = <<offset> <size>>"
Then the bit specification is more aligned with the byte location
(i.e. reg property).
You could also do this all in the reg property with 2 address cells
for byte and bit position and then size can be in bits. reg doesn't
have to match a memory mapped bus addressing meanings. If you wanted
to handle ranges and address translation, then you would need custom
functions like PCI does. I'm not sure you would need that.
+
+For example:
+
+ /* Provider */
+ qfprom: qfprom@00700000 {
+ ...
+
+ /* Data cells */
+ tsens_calibration: calib@404 {
+ reg = <0x404 0x10>;
+ };
+
+ tsens_calibration_bckp: calib_bckp@504 {
+ reg = <0x504 0x11>;
+ bit-offset = 6;
+ nbits = 128;
+ };
+
+ pvs_version: pvs-version@6 {
+ reg = <0x6 0x2>
+ bit-offset = 7;
+ nbits = 2;
+ };
+
+ speed_bin: speed-bin@c{
+ reg = <0xc 0x1>;
+ bit-offset = 2;
+ nbits = 3;
+
+ };
+ ...
+ };
+
+= Data consumers =
+Are device nodes which consume nvmem data cells/providers.
+
+Required-properties:
+nvmem-cells: list of phandle to the nvmem data cells.
+nvmem-cell-names: names for the each nvmem-cells specified. Required if
+ nvmem-cells is used.
+
+Optional-properties:
+nvmem : list of phandles to nvmem providers.
+nvmem-names: names for the each nvmem provider. required if nvmem is used.
+
+For example:
+
+ tsens {
+ ...
+ nvmem-cells = <&tsens_calibration>;
+ nvmem-cell-names = "calibration";
+ };
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Thanks Rob for quick review,
On 10/07/15 20:04, Rob Herring wrote:
On Fri, Jul 10, 2015 at 4:45 AM, Srinivas Kandagatla
[off-list ref] wrote:
quoted
This patch adds bindings for simple nvmem framework which allows nvmem
consumers to talk to nvmem providers to get access to nvmem cell data.
Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
[Maxime Ripard: intial version of eeprom framework]
Signed-off-by: Srinivas Kandagatla <redacted>
---
Documentation/devicetree/bindings/nvmem/nvmem.txt | 85 +++++++++++++++++++++++
1 file changed, 85 insertions(+)
create mode 100644 Documentation/devicetree/bindings/nvmem/nvmem.txt
@@ -0,0 +1,85 @@+= NVMEM(Non Volatile Memory) Data Device Tree Bindings =++This binding is intended to represent the location of hardware+configuration data stored in NVMEMs like eeprom, efuses and so on.++On a significant proportion of boards, the manufacturer has stored+some data on NVMEM, for the OS to be able to retrieve these information+and act upon it. Obviously, the OS has to know about where to retrieve+these data from, and where they are stored on the storage device.++This document is here to document this.++= Data providers =+Contains bindings specific to provider drivers and data cells as children+of this node.
#address-cells and #size-cells are required here.
quoted
+
+Optional properties:
+ read-only: Mark the provider as read only.
Couldn't this be per field rather than global?
Not ATM, The reason for making this property global is to mark the
complete nvmem to be readonly/read-write. Which most of the use-cases
will have. Currently this property is used for setting permissions on
the sysfs binary file, also it would be impossible to apply per field
read-only property to such file.
Am also planning to send few patches on top of these to expose fields in
sysfs which would then allow us to use per field read-only property.
Again not sure how the direct access to nvmem would fit in with such
requirements. Need to evaluate this option in more detail though. :-)
quoted
+
+= Data cells =
+These are the child nodes of the provider which contain data cell
+information like offset and size in nvmem provider.
+
+Required properties:
+reg: specifies the offset in byte within that storage device, start bit
+ in the byte and the length in bytes of the data we care about.
+ There could be more than one offset-length pairs in this property.
+
+Optional properties:
+
+bit-offset: specifies the offset in bit within the address range specified
+ by reg property. Can take values from 0-7.
+nbits: specifies number of bits this cell occupies starting from bit-offset.
How about just: "bits = <<offset> <size>>"
Thats another possible way to specify the same info, Only reason I came
up with bit-offset and nbits is due to the fact that similar properties
were seen in other device DT bindings.
I will try your suggestion and see how it looks before I send new version.
Then the bit specification is more aligned with the byte location
(i.e. reg property).
You could also do this all in the reg property with 2 address cells
for byte and bit position and then size can be in bits. reg doesn't
have to match a memory mapped bus addressing meanings. If you wanted
to handle ranges and address translation, then you would need custom
functions like PCI does. I'm not sure you would need that.
I wanted to keep things simple for this first version.
From: Philipp Zabel <p.zabel@pengutronix.de> Date: 2015-07-13 16:50:46
Hi Srinivas,
Am Freitag, den 10.07.2015, 10:44 +0100 schrieb Srinivas Kandagatla:
This patch adds just providers part of the framework just to enable easy
review.
[...]
+/**
+ * nvmem_register() - Register a nvmem device for given nvmem_config.
+ * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
+ *
+ * @config: nvmem device configuration with which nvmem device is created.
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to nvmem_device.
+ */
+
+struct nvmem_device *nvmem_register(struct nvmem_config *config)
+{
I think read_only should be set if any of the device node property or
nvmem_config->read_only request it. That way, even if the nvmem is
theoretically writeable (no read-only property in DT), the driver still
can make it read-only if writing isn't implemented:
+ nvmem->read_only = nvmem->dev.of_node ?
+ of_property_read_bool(nvmem->dev.of_node,
+ "read-only") : 0;
+ nvmem->read_only |= config->read_only;
[...]
From: Stefan Wahren <hidden> Date: 2015-07-13 18:55:18
Hi Srinivas,
[add Ezequiel Garcia to CC]
Srinivas Kandagatla [off-list ref] hat am 10. Juli 2015 um
11:43 geschrieben:
Hi Greg,
This patchset adds a new simple NVMEM framework to kernel, and it is tested
with various drivers like "QCOM thermal sensors", "QCOM cpr driver",
"begal bone cape manager" and few more on the way.
Thankyou all for providing inputs and comments on previous versions of this
patchset. Here is the v7 of the patchset addressing all the issues raised as
part of previous versions review.
Up until now, NVMEM drivers like eeprom 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.
i only want to mention that there are more places of NVMEM drivers like:
drivers/mfd/ab3100-otp.c
drivers/soc/tegra/fuse/*.c
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 NVMEMs.
After learning few things about QCOM qfprom and other eeprom/efuses, which
has packed fields at bit level. Which makes it important to add support to
such memories. This version adds support to this type of non volatile
memories by adding support to bit level nvmem-cells.
Having regmap interface to this framework would give much better
abstraction for nvmems on different buses.
patch 1-4 Introduces the NVMEM framework.
Patch 5-6 Adds Qualcomm specific qfprom driver.
Patch 7 migrates an existing driver to nvmem framework.
Patch 8 adds entry in MAINTAINERS.
Its also possible to migrate other nvmem drivers to this framework, and I
think
some of them already posted patches based on this framework.
Providers APIs:
nvmem_register/unregister();
How do i get the cell info from the devicetree into the nvmem_config?
I expected a function something like of_nvmem_cell_info_get() in the Providers
API.
Since we're entering userspace the behavior should be clear.
How do we treat register gaps? Fill them with zero?
Best regards
Stefan
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Srinivas Kandagatla [off-list ref] hat am 10. Juli 2015 um
11:43 geschrieben:
Hi Greg,
This patchset adds a new simple NVMEM framework to kernel, and it is tested
with various drivers like "QCOM thermal sensors", "QCOM cpr driver",
"begal bone cape manager" and few more on the way.
Thankyou all for providing inputs and comments on previous versions of this
patchset. Here is the v7 of the patchset addressing all the issues raised as
part of previous versions review.
Up until now, NVMEM drivers like eeprom 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.
i only want to mention that there are more places of NVMEM drivers like:
drivers/mfd/ab3100-otp.c
drivers/soc/tegra/fuse/*.c
Thanks for mentioning.
quoted
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 NVMEMs.
After learning few things about QCOM qfprom and other eeprom/efuses, which
has packed fields at bit level. Which makes it important to add support to
such memories. This version adds support to this type of non volatile
memories by adding support to bit level nvmem-cells.
Having regmap interface to this framework would give much better
abstraction for nvmems on different buses.
patch 1-4 Introduces the NVMEM framework.
Patch 5-6 Adds Qualcomm specific qfprom driver.
Patch 7 migrates an existing driver to nvmem framework.
Patch 8 adds entry in MAINTAINERS.
Its also possible to migrate other nvmem drivers to this framework, and I
think
some of them already posted patches based on this framework.
Providers APIs:
nvmem_register/unregister();
How do i get the cell info from the devicetree into the nvmem_config?
Not sure what is the real use-case here, But this is how it is supposed
to work.
cellinfo in nvmem_config is used to pass cell information in non-dt
style to the core. The core would parse it and convert into nvmem-cells.
Am not sure why would you want to do other way round. Could you explain
the real use case here?
I expected a function something like of_nvmem_cell_info_get() in the Providers
API.
How do i get the cell info from the devicetree into the nvmem_config?
Not sure what is the real use-case here, But this is how it is supposed
to work.
cellinfo in nvmem_config is used to pass cell information in non-dt
style to the core. The core would parse it and convert into nvmem-cells.
Am not sure why would you want to do other way round. Could you explain
the real use case here?
my question comes from porting mxs_ocotp to NVMEM framework.
Here is the devicetree part:
ocotp: ocotp@8002c000 {
compatible = "fsl,imx28-ocotp", "fsl,ocotp";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x8002c000 0x2000>;
clocks = <&clks 25>;
read-only;
/* Data cells */
ocotp_customer: costumer@20 {
reg = <0x20 0x10>;
};
ocotp_rom0: rom0@1a0 {
reg = <0x1a0 0x4>;
};
};
After calling nvmem_register() in the provider driver [1] no data cell is
registered. So
i looked at the core code and i thought that retrieving the cell info and put it
into the nvmem_config
is job of the provider driver.
Did i missed something?
[1] -
https://github.com/lategoodbye/fsl_ocotp/commit/7c98e19755b69f761885b0e1ceb2c258a7c47ade
How do i get the cell info from the devicetree into the nvmem_config?
Not sure what is the real use-case here, But this is how it is supposed
to work.
cellinfo in nvmem_config is used to pass cell information in non-dt
style to the core. The core would parse it and convert into nvmem-cells.
Am not sure why would you want to do other way round. Could you explain
the real use case here?
my question comes from porting mxs_ocotp to NVMEM framework.
Here is the devicetree part:
ocotp: ocotp@8002c000 {
compatible = "fsl,imx28-ocotp", "fsl,ocotp";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x8002c000 0x2000>;
clocks = <&clks 25>;
read-only;
/* Data cells */
ocotp_customer: costumer@20 {
reg = <0x20 0x10>;
};
ocotp_rom0: rom0@1a0 {
reg = <0x1a0 0x4>;
};
};
After calling nvmem_register() in the provider driver [1] no data cell is
registered. So
i looked at the core code and i thought that retrieving the cell info and put it
into the nvmem_config
is job of the provider driver.
Did i missed something?
Ok,
There are 2 possible ways to specify nvmem cells
1> via the DT cell entries.
2> via provider driver directly using cell_info struct.
In the ocotp case its done via dt, so you dont need pass the cell info
in nvmem_config.
You should also note that cell structure in DT is only created when an
attempt to access is made as the provider lookup is straight forward in
this case. In non-DT case the cell structure is created immediately and
added to the global cell list.
--srini
From: Stephen Boyd <hidden> Date: 2015-07-14 21:18:35
On 07/10, Srinivas Kandagatla wrote:
This patch adds QFPROM support driver which is used by other drivers
like thermal sensor and cpufreq.
On MSM parts there are some efuses (called qfprom) these fuses store
things like calibration data, speed bins.. etc. Drivers like cpufreq,
thermal sensors would read out this data for configuring the driver.
Signed-off-by: Srinivas Kandagatla <redacted>
---
I didn't see any reply on v6, but at least delay.h was removed.
Same comments from v6 below.
@@ -0,0 +1,152 @@+ NVMEM SUBSYSTEM+ Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>++This document explains the Simple NVMEM Framework along with the APIs provided,
Why is simple and framework capitalized? Is it the "Simple NVMEM
Framework" or just the "NVMEM" framework?
+and how-to-use.
how to use it?
+
+1. Introduction
+===============
+*NVMEM* is the abbreviation for Non Volatile Memory layer. It is used to
+retrieve configuration or SOC or Device specific data from a non volatile memories
^ ^
of remove a?
+like eeprom, efuses and so on.
+
+Up until now, NVMEM drivers like eeprom were stored in drivers/misc, where they
Up until now will soon be out of date, perhaps say "before this
framework existed"?
+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
^
one
+was a rather big abstraction leak.
+
+Introduction of this framework aims at solving this. It also introduces DT
This framework aims to solve these problems.
+representation for consumer devices to go get the data they require (MAC
+Addresses, SoC/Revision ID, part numbers, and so on) from the NVMEMs.
+This framework is based on regmap, so that most of the abstraction
+available in regmap can be reused, across multiple types of buses.
+
+NVMEM Providers
++++++++++++++++
+
+NVMEM provider refers to an entity that implements methods to initialize, read
+and write the non-volatile memory.
+
+2. Registering/Unregistering the NVMEM provider
+===============================================
+
+A NVMEM provider can register with NVMEM core by suppling relevant
^
supplying
+nvmem configuration to nvmem_register(), on success core would return a valid
+nvmem_device pointer.
+
+nvmem_unregister(nvmem) is used to unregister the already registered provider.
unregister a previously registered provider?
+
+For example for simple qfprom case:
For example, a simple qfprom case:
+
+static struct nvmem_config econfig = {
+ .name = "qfprom",
+ .owner = THIS_MODULE,
+};
+
+static int qfprom_probe(struct platform_device *pdev)
+{
+ ...
+ econfig.dev = &pdev->dev;
+ nvmem = nvmem_register(&econfig);
+ ...
+}
+
+It is mandatory that the NVMEM provider has a regmap associated with its
+struct device.
How do I ensure that?
+
+NVMEM Consumers
++++++++++++++++
+
+NVMEM consumers are the entities which make use of the NVMEM provider to
+read/write into NVMEM.
read from and write to NVMEM?
+
+3. NVMEM cell based consumer APIs.
+=================================
+
+NVMEM cells are the data entries/fields in the NVMEM.
+The NVMEM framework provides 3 APIs to read/write NVMEM cells.
+
+struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *name);
+struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, const char *name);
+
+void nvmem_cell_put(struct nvmem_cell *cell);
+void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell);
+
+void *nvmem_cell_read(struct nvmem_cell *cell, ssize_t *len);
+int nvmem_cell_write(struct nvmem_cell *cell, void *buf, ssize_t len);
+
+*nvmem_cell_get() apis will get a reference to nvmem cell for a given id,
+and nvmem_cell_read/write() can then directly read or write to the cell.
Drop "directly"?
+Once the usage of the cell is finished the consumer should call *nvmem_cell_put()
+to free all the allocation memory for the cell.
+
+4. Direct NVMEM device based consumer APIs.
^
Drop the full stop?
+==========================================
+
+In some instances it is necessary to directly read/write the NVMEM.
+To facilitate such consumers NVMEM framework provides below apis.
+
+struct nvmem_device *nvmem_device_get(struct device *dev, const char *name);
+struct nvmem_device *devm_nvmem_device_get(struct device *dev,
+ const char *name);
+void nvmem_device_put(struct nvmem_device *nvmem);
+int nvmem_device_read(struct nvmem_device *nvmem, unsigned int offset,
+ size_t bytes, void *buf);
+int nvmem_device_write(struct nvmem_device *nvmem, unsigned int offset,
+ size_t bytes, void *buf);
+int nvmem_device_cell_read(struct nvmem_device *nvmem,
+ struct nvmem_cell_info *info, void *buf);
+int nvmem_device_cell_write(struct nvmem_device *nvmem,
+ struct nvmem_cell_info *info, void *buf);
+
+Before the consumers can read/write NVMEM directly, it should get hold
^
a
+of nvmem_controller from one of the *nvmem_device_get() api.
+
+Difference between these apis and cell based apis is that these apis
^
The
+always take nvmem_device as parameter.
+
+5. Releasing a reference to the NVMEM
+=====================================
+
+When the consumers no longer needs the NVMEM, it has to release the reference
When a consumer no longer needs?
+to the NVMEM it has obtained using the APIs mentioned in the above section.
+NVMEM framework provides 2 APIs to release a reference to the NVMEM.
^
The
+
+void nvmem_cell_put(struct nvmem_cell *cell);
+void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell);
+void nvmem_device_put(struct nvmem_device *nvmem);
+void devm_nvmem_device_put(struct device *dev, struct nvmem_device *nvmem);
+
+Both these APIs are used to release a reference to the NVMEM and
+devm_nvmem_cell_put and devm_nvmem_device_put destroys the devres associated
+with this NVMEM.
+==============================
+
+Userspace can read/write the raw NVMEM file located at
+/sys/bus/nvmem/devices/*/nvmem
+
+ex:
+
+hexdump /sys/bus/nvmem/devices/qfprom0/nvmem
+
+0000000 0000 0000 0000 0000 0000 0000 0000 0000
+*
+00000a0 db10 2240 0000 e000 0c00 0c00 0000 0c00
+0000000 0000 0000 0000 0000 0000 0000 0000 0000
+...
+*
+0001000
+
+7. DeviceTree Binding
+=====================
+
+The documentation for NVMEM dt binding can be found @
+Documentation/devicetree/bindings/nvmem/nvmem.txt
How about?
See Documentation/devicetree/bindings/nvmem/nvmem.txt
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
@@ -0,0 +1,152 @@+ NVMEM SUBSYSTEM+ Srinivas Kandagatla <srinivas.kandagatla@linaro.org>++This document explains the Simple NVMEM Framework along with the APIs provided,
Why is simple and framework capitalized? Is it the "Simple NVMEM
Framework" or just the "NVMEM" framework?
quoted
+and how-to-use.
how to use it?
yep,
Thanks Stephen,
I will fix all the comments you raised in next version.
quoted
+
+1. Introduction
+===============
+*NVMEM* is the abbreviation for Non Volatile Memory layer. It is used to
+retrieve configuration or SOC or Device specific data from a non volatile memories
^ ^
of remove a?
quoted
+like eeprom, efuses and so on.
+
+Up until now, NVMEM drivers like eeprom were stored in drivers/misc, where they
Up until now will soon be out of date, perhaps say "before this
framework existed"?
quoted
+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
^
one
yep, will fix it.
quoted
+was a rather big abstraction leak.
+
+Introduction of this framework aims at solving this. It also introduces DT
This framework aims to solve these problems.
quoted
+representation for consumer devices to go get the data they require (MAC
+Addresses, SoC/Revision ID, part numbers, and so on) from the NVMEMs.
+This framework is based on regmap, so that most of the abstraction
+available in regmap can be reused, across multiple types of buses.
+
+NVMEM Providers
++++++++++++++++
+
+NVMEM provider refers to an entity that implements methods to initialize, read
+and write the non-volatile memory.
+
+2. Registering/Unregistering the NVMEM provider
+===============================================
+
+A NVMEM provider can register with NVMEM core by suppling relevant
^
supplying
quoted
+nvmem configuration to nvmem_register(), on success core would return a valid
+nvmem_device pointer.
+
+nvmem_unregister(nvmem) is used to unregister the already registered provider.
unregister a previously registered provider?
quoted
+
+For example for simple qfprom case:
For example, a simple qfprom case:
oops.. I will fix it.
quoted
+
+static struct nvmem_config econfig = {
+ .name = "qfprom",
+ .owner = THIS_MODULE,
+};
+
+static int qfprom_probe(struct platform_device *pdev)
+{
+ ...
+ econfig.dev = &pdev->dev;
+ nvmem = nvmem_register(&econfig);
+ ...
+}
+
+It is mandatory that the NVMEM provider has a regmap associated with its
+struct device.
How do I ensure that?
yes, I think I need to add few lines on the errors which would make it
more explicit.
quoted
+
+NVMEM Consumers
++++++++++++++++
+
+NVMEM consumers are the entities which make use of the NVMEM provider to
+read/write into NVMEM.
read from and write to NVMEM?
Yep.
quoted
+
+3. NVMEM cell based consumer APIs.
+=================================
+
+NVMEM cells are the data entries/fields in the NVMEM.
+The NVMEM framework provides 3 APIs to read/write NVMEM cells.
+
+struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *name);
+struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, const char *name);
+
+void nvmem_cell_put(struct nvmem_cell *cell);
+void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell);
+
+void *nvmem_cell_read(struct nvmem_cell *cell, ssize_t *len);
+int nvmem_cell_write(struct nvmem_cell *cell, void *buf, ssize_t len);
+
+*nvmem_cell_get() apis will get a reference to nvmem cell for a given id,
+and nvmem_cell_read/write() can then directly read or write to the cell.
Drop "directly"?
ok.
quoted
+Once the usage of the cell is finished the consumer should call *nvmem_cell_put()
+to free all the allocation memory for the cell.
+
+4. Direct NVMEM device based consumer APIs.
^
Drop the full stop?
quoted
+==========================================
+
+In some instances it is necessary to directly read/write the NVMEM.
+To facilitate such consumers NVMEM framework provides below apis.
+
+struct nvmem_device *nvmem_device_get(struct device *dev, const char *name);
+struct nvmem_device *devm_nvmem_device_get(struct device *dev,
+ const char *name);
+void nvmem_device_put(struct nvmem_device *nvmem);
+int nvmem_device_read(struct nvmem_device *nvmem, unsigned int offset,
+ size_t bytes, void *buf);
+int nvmem_device_write(struct nvmem_device *nvmem, unsigned int offset,
+ size_t bytes, void *buf);
+int nvmem_device_cell_read(struct nvmem_device *nvmem,
+ struct nvmem_cell_info *info, void *buf);
+int nvmem_device_cell_write(struct nvmem_device *nvmem,
+ struct nvmem_cell_info *info, void *buf);
+
+Before the consumers can read/write NVMEM directly, it should get hold
^
a
quoted
+of nvmem_controller from one of the *nvmem_device_get() api.
+
+Difference between these apis and cell based apis is that these apis
^
The
yes, will fix it.
quoted
+always take nvmem_device as parameter.
+
+5. Releasing a reference to the NVMEM
+=====================================
+
+When the consumers no longer needs the NVMEM, it has to release the reference
When a consumer no longer needs?
yep I will fix it.
quoted
+to the NVMEM it has obtained using the APIs mentioned in the above section.
+NVMEM framework provides 2 APIs to release a reference to the NVMEM.
^
The
quoted
+
+void nvmem_cell_put(struct nvmem_cell *cell);
+void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell);
+void nvmem_device_put(struct nvmem_device *nvmem);
+void devm_nvmem_device_put(struct device *dev, struct nvmem_device *nvmem);
+
+Both these APIs are used to release a reference to the NVMEM and
+devm_nvmem_cell_put and devm_nvmem_device_put destroys the devres associated
+with this NVMEM.
Thanks Stephen for the comments.
On 14/07/15 22:18, Stephen Boyd wrote:
On 07/10, Srinivas Kandagatla wrote:
quoted
This patch adds QFPROM support driver which is used by other drivers
like thermal sensor and cpufreq.
On MSM parts there are some efuses (called qfprom) these fuses store
things like calibration data, speed bins.. etc. Drivers like cpufreq,
thermal sensors would read out this data for configuring the driver.
Signed-off-by: Srinivas Kandagatla <redacted>
---
I didn't see any reply on v6, but at least delay.h was removed.
Same comments from v6 below.
+ return 0;
+ }
+ return *nvmem == data;
+}
+
[..]
+
+/**
+ * nvmem_device_write() - Write cell to a given nvmem device
+ *
+ * @nvmem: nvmem device to be written to.
+ * @offset: offset in nvmem device.
+ * @bytes: number of bytes to write.
+ * @buf: buffer to be written.
+ *
+ * The return value will be an length of bytes written or non zero on failure.
Should say negative value instead of non-zero? Length is
non-zero already.
General nitpick comment: Kernel-doc allows for a standard return
syntax.
Return: length of bytes written or negative value on failure.
+ return 0;
+ }
+ return *nvmem == data;
+}
+
[..]
+
+/**
+ * nvmem_device_write() - Write cell to a given nvmem device
+ *
+ * @nvmem: nvmem device to be written to.
+ * @offset: offset in nvmem device.
+ * @bytes: number of bytes to write.
+ * @buf: buffer to be written.
+ *
+ * The return value will be an length of bytes written or non zero on failure.
Should say negative value instead of non-zero? Length is
non-zero already.
General nitpick comment: Kernel-doc allows for a standard return
syntax.
Ok, I will fix such instances.
Return: length of bytes written or negative value on failure.
Why does this move from provider to consumer? Can't we do put
this struct in the right place from the beginning?
I will take care of it in next version.
--srini
quoted
+struct nvmem_device;
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html