@@ -34,6 +34,8 @@ struct nvmem_device {
3434 struct bin_attribute eeprom ;
3535 struct device * base_dev ;
3636 struct list_head cells ;
37+ const struct nvmem_keepout * keepout ;
38+ unsigned int nkeepout ;
3739 nvmem_reg_read_t reg_read ;
3840 nvmem_reg_write_t reg_write ;
3941 struct gpio_desc * wp_gpio ;
@@ -66,17 +68,17 @@ static LIST_HEAD(nvmem_lookup_list);
6668
6769static BLOCKING_NOTIFIER_HEAD (nvmem_notifier );
6870
69- static int nvmem_reg_read (struct nvmem_device * nvmem , unsigned int offset ,
70- void * val , size_t bytes )
71+ static int __nvmem_reg_read (struct nvmem_device * nvmem , unsigned int offset ,
72+ void * val , size_t bytes )
7173{
7274 if (nvmem -> reg_read )
7375 return nvmem -> reg_read (nvmem -> priv , offset , val , bytes );
7476
7577 return - EINVAL ;
7678}
7779
78- static int nvmem_reg_write (struct nvmem_device * nvmem , unsigned int offset ,
79- void * val , size_t bytes )
80+ static int __nvmem_reg_write (struct nvmem_device * nvmem , unsigned int offset ,
81+ void * val , size_t bytes )
8082{
8183 int ret ;
8284
@@ -90,6 +92,88 @@ static int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
9092 return - EINVAL ;
9193}
9294
95+ static int nvmem_access_with_keepouts (struct nvmem_device * nvmem ,
96+ unsigned int offset , void * val ,
97+ size_t bytes , int write )
98+ {
99+
100+ unsigned int end = offset + bytes ;
101+ unsigned int kend , ksize ;
102+ const struct nvmem_keepout * keepout = nvmem -> keepout ;
103+ const struct nvmem_keepout * keepoutend = keepout + nvmem -> nkeepout ;
104+ int rc ;
105+
106+ /*
107+ * Skip all keepouts before the range being accessed.
108+ * Keepouts are sorted.
109+ */
110+ while ((keepout < keepoutend ) && (keepout -> end <= offset ))
111+ keepout ++ ;
112+
113+ while ((offset < end ) && (keepout < keepoutend )) {
114+ /* Access the valid portion before the keepout. */
115+ if (offset < keepout -> start ) {
116+ kend = min (end , keepout -> start );
117+ ksize = kend - offset ;
118+ if (write )
119+ rc = __nvmem_reg_write (nvmem , offset , val , ksize );
120+ else
121+ rc = __nvmem_reg_read (nvmem , offset , val , ksize );
122+
123+ if (rc )
124+ return rc ;
125+
126+ offset += ksize ;
127+ val += ksize ;
128+ }
129+
130+ /*
131+ * Now we're aligned to the start of this keepout zone. Go
132+ * through it.
133+ */
134+ kend = min (end , keepout -> end );
135+ ksize = kend - offset ;
136+ if (!write )
137+ memset (val , keepout -> value , ksize );
138+
139+ val += ksize ;
140+ offset += ksize ;
141+ keepout ++ ;
142+ }
143+
144+ /*
145+ * If we ran out of keepouts but there's still stuff to do, send it
146+ * down directly
147+ */
148+ if (offset < end ) {
149+ ksize = end - offset ;
150+ if (write )
151+ return __nvmem_reg_write (nvmem , offset , val , ksize );
152+ else
153+ return __nvmem_reg_read (nvmem , offset , val , ksize );
154+ }
155+
156+ return 0 ;
157+ }
158+
159+ static int nvmem_reg_read (struct nvmem_device * nvmem , unsigned int offset ,
160+ void * val , size_t bytes )
161+ {
162+ if (!nvmem -> nkeepout )
163+ return __nvmem_reg_read (nvmem , offset , val , bytes );
164+
165+ return nvmem_access_with_keepouts (nvmem , offset , val , bytes , false);
166+ }
167+
168+ static int nvmem_reg_write (struct nvmem_device * nvmem , unsigned int offset ,
169+ void * val , size_t bytes )
170+ {
171+ if (!nvmem -> nkeepout )
172+ return __nvmem_reg_write (nvmem , offset , val , bytes );
173+
174+ return nvmem_access_with_keepouts (nvmem , offset , val , bytes , true);
175+ }
176+
93177#ifdef CONFIG_NVMEM_SYSFS
94178static const char * const nvmem_type_str [] = {
95179 [NVMEM_TYPE_UNKNOWN ] = "Unknown" ,
@@ -533,6 +617,59 @@ nvmem_find_cell_by_name(struct nvmem_device *nvmem, const char *cell_id)
533617 return cell ;
534618}
535619
620+ static int nvmem_validate_keepouts (struct nvmem_device * nvmem )
621+ {
622+ unsigned int cur = 0 ;
623+ const struct nvmem_keepout * keepout = nvmem -> keepout ;
624+ const struct nvmem_keepout * keepoutend = keepout + nvmem -> nkeepout ;
625+
626+ while (keepout < keepoutend ) {
627+ /* Ensure keepouts are sorted and don't overlap. */
628+ if (keepout -> start < cur ) {
629+ dev_err (& nvmem -> dev ,
630+ "Keepout regions aren't sorted or overlap.\n" );
631+
632+ return - ERANGE ;
633+ }
634+
635+ if (keepout -> end < keepout -> start ) {
636+ dev_err (& nvmem -> dev ,
637+ "Invalid keepout region.\n" );
638+
639+ return - EINVAL ;
640+ }
641+
642+ /*
643+ * Validate keepouts (and holes between) don't violate
644+ * word_size constraints.
645+ */
646+ if ((keepout -> end - keepout -> start < nvmem -> word_size ) ||
647+ ((keepout -> start != cur ) &&
648+ (keepout -> start - cur < nvmem -> word_size ))) {
649+
650+ dev_err (& nvmem -> dev ,
651+ "Keepout regions violate word_size constraints.\n" );
652+
653+ return - ERANGE ;
654+ }
655+
656+ /* Validate keepouts don't violate stride (alignment). */
657+ if (!IS_ALIGNED (keepout -> start , nvmem -> stride ) ||
658+ !IS_ALIGNED (keepout -> end , nvmem -> stride )) {
659+
660+ dev_err (& nvmem -> dev ,
661+ "Keepout regions violate stride.\n" );
662+
663+ return - EINVAL ;
664+ }
665+
666+ cur = keepout -> end ;
667+ keepout ++ ;
668+ }
669+
670+ return 0 ;
671+ }
672+
536673static int nvmem_add_cells_from_of (struct nvmem_device * nvmem )
537674{
538675 struct device_node * parent , * child ;
@@ -647,6 +784,8 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
647784 nvmem -> type = config -> type ;
648785 nvmem -> reg_read = config -> reg_read ;
649786 nvmem -> reg_write = config -> reg_write ;
787+ nvmem -> keepout = config -> keepout ;
788+ nvmem -> nkeepout = config -> nkeepout ;
650789 if (!config -> no_of_node )
651790 nvmem -> dev .of_node = config -> dev -> of_node ;
652791
@@ -671,6 +810,12 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
671810 nvmem -> dev .groups = nvmem_dev_groups ;
672811#endif
673812
813+ if (nvmem -> nkeepout ) {
814+ rval = nvmem_validate_keepouts (nvmem );
815+ if (rval )
816+ goto err_put_device ;
817+ }
818+
674819 dev_dbg (& nvmem -> dev , "Registering nvmem device %s\n" , config -> name );
675820
676821 rval = device_register (& nvmem -> dev );
0 commit comments