aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2016-01-26 23:50:07 -0500
committerMark Brown <broonie@kernel.org>2016-01-27 15:04:50 -0500
commit922a9f936e40001f9b921379aab90047d5990923 (patch)
tree50502ff73929e4327f990f894bd332d1129be70a
parent25d6463e48ea41149c88dc40648a888699e77158 (diff)
regmap: mmio: Convert to regmap_bus and fix accessor usage
Currently regmap-mmio uses the __raw accessors to read and write from memory. This is not safe as these interact poorly with spinlocks and are not guaranteed to generate emulated instructions on at least ARM where regmap is commonly used. The APIs that are provided all provide some byte swapping so this is difficult to do with the current regmap-mmio implementation which attempts to use the regmap core byte swapping. We can fix this by modernising the MMIO implementation to use reg_read() and reg_write() operations which were added after the API was implemented and pass simple unsigned integers through to the bus, making use of the formatting provided by the I/O accessors using a similar pattern to that used by the core. This will be less efficient for block I/O operations since we now enable and disable any required clocks per register but it is not clear that any users of regmap-mmio actually use block I/O and there is room to optimise later. This removes support for big endian I/O on 64 bit registers since no I/O accessors are provided, no current users were found and support can be added easily once they are available. In addition make the default endianness little endian. This was the behaviour prior to 29bb45f25ff305 (regmap-mmio: Use native endianness for read/write) and is the behaviour desired by most existing users, the users have been audited and those that need native endianness converted to request it explicitly. Previously native was documented as the default but due to the byte swapping in the accessors this was not correctly implemented. Fixes: 29bb45f25ff305 (regmap-mmio: Use native endianness for read/write) Reported-by: Johannes Berg <johannes@sipsolutions.net> Tested-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--drivers/base/regmap/regmap-mmio.c259
1 files changed, 139 insertions, 120 deletions
diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c
index 8812bfb9e3b8..7526906ca080 100644
--- a/drivers/base/regmap/regmap-mmio.c
+++ b/drivers/base/regmap/regmap-mmio.c
@@ -25,26 +25,14 @@
25 25
26struct regmap_mmio_context { 26struct regmap_mmio_context {
27 void __iomem *regs; 27 void __iomem *regs;
28 unsigned reg_bytes;
29 unsigned val_bytes; 28 unsigned val_bytes;
30 unsigned pad_bytes;
31 struct clk *clk; 29 struct clk *clk;
32};
33 30
34static inline void regmap_mmio_regsize_check(size_t reg_size) 31 void (*reg_write)(struct regmap_mmio_context *ctx,
35{ 32 unsigned int reg, unsigned int val);
36 switch (reg_size) { 33 unsigned int (*reg_read)(struct regmap_mmio_context *ctx,
37 case 1: 34 unsigned int reg);
38 case 2: 35};
39 case 4:
40#ifdef CONFIG_64BIT
41 case 8:
42#endif
43 break;
44 default:
45 BUG();
46 }
47}
48 36
49static int regmap_mmio_regbits_check(size_t reg_bits) 37static int regmap_mmio_regbits_check(size_t reg_bits)
50{ 38{
@@ -88,72 +76,62 @@ static int regmap_mmio_get_min_stride(size_t val_bits)
88 return min_stride; 76 return min_stride;
89} 77}
90 78
91static inline void regmap_mmio_count_check(size_t count, u32 offset) 79static void regmap_mmio_write8(struct regmap_mmio_context *ctx,
80 unsigned int reg,
81 unsigned int val)
82{
83 writeb(val, ctx->regs + reg);
84}
85
86static void regmap_mmio_write16le(struct regmap_mmio_context *ctx,
87 unsigned int reg,
88 unsigned int val)
92{ 89{
93 BUG_ON(count <= offset); 90 writew(val, ctx->regs + reg);
94} 91}
95 92
96static inline unsigned int 93static void regmap_mmio_write16be(struct regmap_mmio_context *ctx,
97regmap_mmio_get_offset(const void *reg, size_t reg_size) 94 unsigned int reg,
95 unsigned int val)
98{ 96{
99 switch (reg_size) { 97 iowrite16be(val, ctx->regs + reg);
100 case 1: 98}
101 return *(u8 *)reg; 99
102 case 2: 100static void regmap_mmio_write32le(struct regmap_mmio_context *ctx,
103 return *(u16 *)reg; 101 unsigned int reg,
104 case 4: 102 unsigned int val)
105 return *(u32 *)reg; 103{
104 writel(val, ctx->regs + reg);
105}
106
107static void regmap_mmio_write32be(struct regmap_mmio_context *ctx,
108 unsigned int reg,
109 unsigned int val)
110{
111 iowrite32be(val, ctx->regs + reg);
112}
113
106#ifdef CONFIG_64BIT 114#ifdef CONFIG_64BIT
107 case 8: 115static void regmap_mmio_write64le(struct regmap_mmio_context *ctx,
108 return *(u64 *)reg; 116 unsigned int reg,
109#endif 117 unsigned int val)
110 default: 118{
111 BUG(); 119 writeq(val, ctx->regs + reg);
112 }
113} 120}
121#endif
114 122
115static int regmap_mmio_gather_write(void *context, 123static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val)
116 const void *reg, size_t reg_size,
117 const void *val, size_t val_size)
118{ 124{
119 struct regmap_mmio_context *ctx = context; 125 struct regmap_mmio_context *ctx = context;
120 unsigned int offset;
121 int ret; 126 int ret;
122 127
123 regmap_mmio_regsize_check(reg_size);
124
125 if (!IS_ERR(ctx->clk)) { 128 if (!IS_ERR(ctx->clk)) {
126 ret = clk_enable(ctx->clk); 129 ret = clk_enable(ctx->clk);
127 if (ret < 0) 130 if (ret < 0)
128 return ret; 131 return ret;
129 } 132 }
130 133
131 offset = regmap_mmio_get_offset(reg, reg_size); 134 ctx->reg_write(ctx, reg, val);
132
133 while (val_size) {
134 switch (ctx->val_bytes) {
135 case 1:
136 __raw_writeb(*(u8 *)val, ctx->regs + offset);
137 break;
138 case 2:
139 __raw_writew(*(u16 *)val, ctx->regs + offset);
140 break;
141 case 4:
142 __raw_writel(*(u32 *)val, ctx->regs + offset);
143 break;
144#ifdef CONFIG_64BIT
145 case 8:
146 __raw_writeq(*(u64 *)val, ctx->regs + offset);
147 break;
148#endif
149 default:
150 /* Should be caught by regmap_mmio_check_config */
151 BUG();
152 }
153 val_size -= ctx->val_bytes;
154 val += ctx->val_bytes;
155 offset += ctx->val_bytes;
156 }
157 135
158 if (!IS_ERR(ctx->clk)) 136 if (!IS_ERR(ctx->clk))
159 clk_disable(ctx->clk); 137 clk_disable(ctx->clk);
@@ -161,59 +139,56 @@ static int regmap_mmio_gather_write(void *context,
161 return 0; 139 return 0;
162} 140}
163 141
164static int regmap_mmio_write(void *context, const void *data, size_t count) 142static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx,
143 unsigned int reg)
165{ 144{
166 struct regmap_mmio_context *ctx = context; 145 return readb(ctx->regs + reg);
167 unsigned int offset = ctx->reg_bytes + ctx->pad_bytes; 146}
147
148static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx,
149 unsigned int reg)
150{
151 return readw(ctx->regs + reg);
152}
153
154static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx,
155 unsigned int reg)
156{
157 return ioread16be(ctx->regs + reg);
158}
159
160static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx,
161 unsigned int reg)
162{
163 return readl(ctx->regs + reg);
164}
168 165
169 regmap_mmio_count_check(count, offset); 166static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx,
167 unsigned int reg)
168{
169 return ioread32be(ctx->regs + reg);
170}
170 171
171 return regmap_mmio_gather_write(context, data, ctx->reg_bytes, 172#ifdef CONFIG_64BIT
172 data + offset, count - offset); 173static unsigned int regmap_mmio_read64le(struct regmap_mmio_context *ctx,
174 unsigned int reg)
175{
176 return readq(ctx->regs + reg);
173} 177}
178#endif
174 179
175static int regmap_mmio_read(void *context, 180static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val)
176 const void *reg, size_t reg_size,
177 void *val, size_t val_size)
178{ 181{
179 struct regmap_mmio_context *ctx = context; 182 struct regmap_mmio_context *ctx = context;
180 unsigned int offset;
181 int ret; 183 int ret;
182 184
183 regmap_mmio_regsize_check(reg_size);
184
185 if (!IS_ERR(ctx->clk)) { 185 if (!IS_ERR(ctx->clk)) {
186 ret = clk_enable(ctx->clk); 186 ret = clk_enable(ctx->clk);
187 if (ret < 0) 187 if (ret < 0)
188 return ret; 188 return ret;
189 } 189 }
190 190
191 offset = regmap_mmio_get_offset(reg, reg_size); 191 *val = ctx->reg_read(ctx, reg);
192
193 while (val_size) {
194 switch (ctx->val_bytes) {
195 case 1:
196 *(u8 *)val = __raw_readb(ctx->regs + offset);
197 break;
198 case 2:
199 *(u16 *)val = __raw_readw(ctx->regs + offset);
200 break;
201 case 4:
202 *(u32 *)val = __raw_readl(ctx->regs + offset);
203 break;
204#ifdef CONFIG_64BIT
205 case 8:
206 *(u64 *)val = __raw_readq(ctx->regs + offset);
207 break;
208#endif
209 default:
210 /* Should be caught by regmap_mmio_check_config */
211 BUG();
212 }
213 val_size -= ctx->val_bytes;
214 val += ctx->val_bytes;
215 offset += ctx->val_bytes;
216 }
217 192
218 if (!IS_ERR(ctx->clk)) 193 if (!IS_ERR(ctx->clk))
219 clk_disable(ctx->clk); 194 clk_disable(ctx->clk);
@@ -232,14 +207,11 @@ static void regmap_mmio_free_context(void *context)
232 kfree(context); 207 kfree(context);
233} 208}
234 209
235static struct regmap_bus regmap_mmio = { 210static const struct regmap_bus regmap_mmio = {
236 .fast_io = true, 211 .fast_io = true,
237 .write = regmap_mmio_write, 212 .reg_write = regmap_mmio_write,
238 .gather_write = regmap_mmio_gather_write, 213 .reg_read = regmap_mmio_read,
239 .read = regmap_mmio_read,
240 .free_context = regmap_mmio_free_context, 214 .free_context = regmap_mmio_free_context,
241 .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
242 .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
243}; 215};
244 216
245static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, 217static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
@@ -265,24 +237,71 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
265 if (config->reg_stride < min_stride) 237 if (config->reg_stride < min_stride)
266 return ERR_PTR(-EINVAL); 238 return ERR_PTR(-EINVAL);
267 239
268 switch (config->reg_format_endian) {
269 case REGMAP_ENDIAN_DEFAULT:
270 case REGMAP_ENDIAN_NATIVE:
271 break;
272 default:
273 return ERR_PTR(-EINVAL);
274 }
275
276 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 240 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
277 if (!ctx) 241 if (!ctx)
278 return ERR_PTR(-ENOMEM); 242 return ERR_PTR(-ENOMEM);
279 243
280 ctx->regs = regs; 244 ctx->regs = regs;
281 ctx->val_bytes = config->val_bits / 8; 245 ctx->val_bytes = config->val_bits / 8;
282 ctx->reg_bytes = config->reg_bits / 8;
283 ctx->pad_bytes = config->pad_bits / 8;
284 ctx->clk = ERR_PTR(-ENODEV); 246 ctx->clk = ERR_PTR(-ENODEV);
285 247
248 switch (config->reg_format_endian) {
249 case REGMAP_ENDIAN_DEFAULT:
250 case REGMAP_ENDIAN_LITTLE:
251#ifdef __LITTLE_ENDIAN
252 case REGMAP_ENDIAN_NATIVE:
253#endif
254 switch (config->val_bits) {
255 case 8:
256 ctx->reg_read = regmap_mmio_read8;
257 ctx->reg_write = regmap_mmio_write8;
258 break;
259 case 16:
260 ctx->reg_read = regmap_mmio_read16le;
261 ctx->reg_write = regmap_mmio_write16le;
262 break;
263 case 32:
264 ctx->reg_read = regmap_mmio_read32le;
265 ctx->reg_write = regmap_mmio_write32le;
266 break;
267#ifdef CONFIG_64BIT
268 case 64:
269 ctx->reg_read = regmap_mmio_read64le;
270 ctx->reg_write = regmap_mmio_write64le;
271 break;
272#endif
273 default:
274 ret = -EINVAL;
275 goto err_free;
276 }
277 break;
278 case REGMAP_ENDIAN_BIG:
279#ifdef __BIG_ENDIAN
280 case REGMAP_ENDIAN_NATIVE:
281#endif
282 switch (config->val_bits) {
283 case 8:
284 ctx->reg_read = regmap_mmio_read8;
285 ctx->reg_write = regmap_mmio_write8;
286 break;
287 case 16:
288 ctx->reg_read = regmap_mmio_read16be;
289 ctx->reg_write = regmap_mmio_write16be;
290 break;
291 case 32:
292 ctx->reg_read = regmap_mmio_read32be;
293 ctx->reg_write = regmap_mmio_write32be;
294 break;
295 default:
296 ret = -EINVAL;
297 goto err_free;
298 }
299 break;
300 default:
301 ret = -EINVAL;
302 goto err_free;
303 }
304
286 if (clk_id == NULL) 305 if (clk_id == NULL)
287 return ctx; 306 return ctx;
288 307