137613fa5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
237613fa5SGreg Kroah-Hartman //
337613fa5SGreg Kroah-Hartman // Register map access API - MMIO support
437613fa5SGreg Kroah-Hartman //
537613fa5SGreg Kroah-Hartman // Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
645f5ff81SStephen Warren 
7878ec67bSPhilipp Zabel #include <linux/clk.h>
845f5ff81SStephen Warren #include <linux/err.h>
945f5ff81SStephen Warren #include <linux/io.h>
1045f5ff81SStephen Warren #include <linux/module.h>
1145f5ff81SStephen Warren #include <linux/regmap.h>
1245f5ff81SStephen Warren #include <linux/slab.h>
13400dceb6SAndy Shevchenko #include <linux/swab.h>
1445f5ff81SStephen Warren 
150dbdb76cSMark Brown #include "internal.h"
160dbdb76cSMark Brown 
1745f5ff81SStephen Warren struct regmap_mmio_context {
1845f5ff81SStephen Warren 	void __iomem *regs;
19d63aa09fSJinchao Wang 	unsigned int val_bytes;
2081c0386cSLinus Walleij 	bool big_endian;
2131895662SMaxime Ripard 
2231895662SMaxime Ripard 	bool attached_clk;
23878ec67bSPhilipp Zabel 	struct clk *clk;
2445f5ff81SStephen Warren 
25922a9f93SMark Brown 	void (*reg_write)(struct regmap_mmio_context *ctx,
26922a9f93SMark Brown 			  unsigned int reg, unsigned int val);
27922a9f93SMark Brown 	unsigned int (*reg_read)(struct regmap_mmio_context *ctx,
28922a9f93SMark Brown 			         unsigned int reg);
29922a9f93SMark Brown };
3041b0c2c9SXiubo Li 
regmap_mmio_regbits_check(size_t reg_bits)31451485baSXiubo Li static int regmap_mmio_regbits_check(size_t reg_bits)
32451485baSXiubo Li {
33451485baSXiubo Li 	switch (reg_bits) {
34451485baSXiubo Li 	case 8:
35451485baSXiubo Li 	case 16:
36451485baSXiubo Li 	case 32:
37451485baSXiubo Li 		return 0;
38451485baSXiubo Li 	default:
39451485baSXiubo Li 		return -EINVAL;
40451485baSXiubo Li 	}
41451485baSXiubo Li }
42451485baSXiubo Li 
regmap_mmio_get_min_stride(size_t val_bits)4375fb0aaeSXiubo Li static int regmap_mmio_get_min_stride(size_t val_bits)
4475fb0aaeSXiubo Li {
4575fb0aaeSXiubo Li 	int min_stride;
4675fb0aaeSXiubo Li 
4775fb0aaeSXiubo Li 	switch (val_bits) {
4875fb0aaeSXiubo Li 	case 8:
4975fb0aaeSXiubo Li 		/* The core treats 0 as 1 */
5075fb0aaeSXiubo Li 		min_stride = 0;
5101ed2307SColin Ian King 		break;
5275fb0aaeSXiubo Li 	case 16:
5375fb0aaeSXiubo Li 		min_stride = 2;
5475fb0aaeSXiubo Li 		break;
5575fb0aaeSXiubo Li 	case 32:
5675fb0aaeSXiubo Li 		min_stride = 4;
5775fb0aaeSXiubo Li 		break;
5875fb0aaeSXiubo Li 	default:
5975fb0aaeSXiubo Li 		return -EINVAL;
6075fb0aaeSXiubo Li 	}
6175fb0aaeSXiubo Li 
6275fb0aaeSXiubo Li 	return min_stride;
6375fb0aaeSXiubo Li }
6475fb0aaeSXiubo Li 
regmap_mmio_write8(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)65922a9f93SMark Brown static void regmap_mmio_write8(struct regmap_mmio_context *ctx,
66922a9f93SMark Brown 				unsigned int reg,
67922a9f93SMark Brown 				unsigned int val)
6841b0c2c9SXiubo Li {
69922a9f93SMark Brown 	writeb(val, ctx->regs + reg);
7041b0c2c9SXiubo Li }
7141b0c2c9SXiubo Li 
regmap_mmio_write8_relaxed(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)726e1e90ecSAdrian Ratiu static void regmap_mmio_write8_relaxed(struct regmap_mmio_context *ctx,
736e1e90ecSAdrian Ratiu 				unsigned int reg,
746e1e90ecSAdrian Ratiu 				unsigned int val)
756e1e90ecSAdrian Ratiu {
766e1e90ecSAdrian Ratiu 	writeb_relaxed(val, ctx->regs + reg);
776e1e90ecSAdrian Ratiu }
786e1e90ecSAdrian Ratiu 
regmap_mmio_iowrite8(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)7993ce5576SAndy Shevchenko static void regmap_mmio_iowrite8(struct regmap_mmio_context *ctx,
8093ce5576SAndy Shevchenko 				 unsigned int reg, unsigned int val)
8193ce5576SAndy Shevchenko {
8293ce5576SAndy Shevchenko 	iowrite8(val, ctx->regs + reg);
8393ce5576SAndy Shevchenko }
8493ce5576SAndy Shevchenko 
regmap_mmio_write16le(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)85922a9f93SMark Brown static void regmap_mmio_write16le(struct regmap_mmio_context *ctx,
86922a9f93SMark Brown 				  unsigned int reg,
87922a9f93SMark Brown 				  unsigned int val)
8888cb32c6SXiubo Li {
89922a9f93SMark Brown 	writew(val, ctx->regs + reg);
90922a9f93SMark Brown }
91922a9f93SMark Brown 
regmap_mmio_write16le_relaxed(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)926e1e90ecSAdrian Ratiu static void regmap_mmio_write16le_relaxed(struct regmap_mmio_context *ctx,
936e1e90ecSAdrian Ratiu 				  unsigned int reg,
946e1e90ecSAdrian Ratiu 				  unsigned int val)
956e1e90ecSAdrian Ratiu {
966e1e90ecSAdrian Ratiu 	writew_relaxed(val, ctx->regs + reg);
976e1e90ecSAdrian Ratiu }
986e1e90ecSAdrian Ratiu 
regmap_mmio_iowrite16le(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)9993ce5576SAndy Shevchenko static void regmap_mmio_iowrite16le(struct regmap_mmio_context *ctx,
10093ce5576SAndy Shevchenko 				    unsigned int reg, unsigned int val)
10193ce5576SAndy Shevchenko {
10293ce5576SAndy Shevchenko 	iowrite16(val, ctx->regs + reg);
10393ce5576SAndy Shevchenko }
10493ce5576SAndy Shevchenko 
regmap_mmio_write16be(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)105922a9f93SMark Brown static void regmap_mmio_write16be(struct regmap_mmio_context *ctx,
106922a9f93SMark Brown 				  unsigned int reg,
107922a9f93SMark Brown 				  unsigned int val)
108922a9f93SMark Brown {
1097e7ba58cSAndy Shevchenko 	writew(swab16(val), ctx->regs + reg);
110922a9f93SMark Brown }
111922a9f93SMark Brown 
regmap_mmio_iowrite16be(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)11293ce5576SAndy Shevchenko static void regmap_mmio_iowrite16be(struct regmap_mmio_context *ctx,
11393ce5576SAndy Shevchenko 				    unsigned int reg, unsigned int val)
11493ce5576SAndy Shevchenko {
11593ce5576SAndy Shevchenko 	iowrite16be(val, ctx->regs + reg);
11693ce5576SAndy Shevchenko }
11793ce5576SAndy Shevchenko 
regmap_mmio_write32le(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)118922a9f93SMark Brown static void regmap_mmio_write32le(struct regmap_mmio_context *ctx,
119922a9f93SMark Brown 				  unsigned int reg,
120922a9f93SMark Brown 				  unsigned int val)
121922a9f93SMark Brown {
122922a9f93SMark Brown 	writel(val, ctx->regs + reg);
123922a9f93SMark Brown }
124922a9f93SMark Brown 
regmap_mmio_write32le_relaxed(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)1256e1e90ecSAdrian Ratiu static void regmap_mmio_write32le_relaxed(struct regmap_mmio_context *ctx,
1266e1e90ecSAdrian Ratiu 				  unsigned int reg,
1276e1e90ecSAdrian Ratiu 				  unsigned int val)
1286e1e90ecSAdrian Ratiu {
1296e1e90ecSAdrian Ratiu 	writel_relaxed(val, ctx->regs + reg);
1306e1e90ecSAdrian Ratiu }
1316e1e90ecSAdrian Ratiu 
regmap_mmio_iowrite32le(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)13293ce5576SAndy Shevchenko static void regmap_mmio_iowrite32le(struct regmap_mmio_context *ctx,
13393ce5576SAndy Shevchenko 				    unsigned int reg, unsigned int val)
13493ce5576SAndy Shevchenko {
13593ce5576SAndy Shevchenko 	iowrite32(val, ctx->regs + reg);
13693ce5576SAndy Shevchenko }
13793ce5576SAndy Shevchenko 
regmap_mmio_write32be(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)138922a9f93SMark Brown static void regmap_mmio_write32be(struct regmap_mmio_context *ctx,
139922a9f93SMark Brown 				  unsigned int reg,
140922a9f93SMark Brown 				  unsigned int val)
141922a9f93SMark Brown {
1427e7ba58cSAndy Shevchenko 	writel(swab32(val), ctx->regs + reg);
143922a9f93SMark Brown }
144922a9f93SMark Brown 
regmap_mmio_iowrite32be(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)14593ce5576SAndy Shevchenko static void regmap_mmio_iowrite32be(struct regmap_mmio_context *ctx,
14693ce5576SAndy Shevchenko 				    unsigned int reg, unsigned int val)
14793ce5576SAndy Shevchenko {
14893ce5576SAndy Shevchenko 	iowrite32be(val, ctx->regs + reg);
14993ce5576SAndy Shevchenko }
15093ce5576SAndy Shevchenko 
regmap_mmio_write(void * context,unsigned int reg,unsigned int val)151922a9f93SMark Brown static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val)
15245f5ff81SStephen Warren {
15345f5ff81SStephen Warren 	struct regmap_mmio_context *ctx = context;
154878ec67bSPhilipp Zabel 	int ret;
15545f5ff81SStephen Warren 
1566b8e090eSStephen Warren 	if (!IS_ERR(ctx->clk)) {
157878ec67bSPhilipp Zabel 		ret = clk_enable(ctx->clk);
158878ec67bSPhilipp Zabel 		if (ret < 0)
159878ec67bSPhilipp Zabel 			return ret;
160878ec67bSPhilipp Zabel 	}
161878ec67bSPhilipp Zabel 
162922a9f93SMark Brown 	ctx->reg_write(ctx, reg, val);
16345f5ff81SStephen Warren 
1646b8e090eSStephen Warren 	if (!IS_ERR(ctx->clk))
165878ec67bSPhilipp Zabel 		clk_disable(ctx->clk);
166878ec67bSPhilipp Zabel 
16745f5ff81SStephen Warren 	return 0;
16845f5ff81SStephen Warren }
16945f5ff81SStephen Warren 
regmap_mmio_noinc_write(void * context,unsigned int reg,const void * val,size_t val_count)17081c0386cSLinus Walleij static int regmap_mmio_noinc_write(void *context, unsigned int reg,
17181c0386cSLinus Walleij 				   const void *val, size_t val_count)
17281c0386cSLinus Walleij {
17381c0386cSLinus Walleij 	struct regmap_mmio_context *ctx = context;
17481c0386cSLinus Walleij 	int ret = 0;
17581c0386cSLinus Walleij 	int i;
17681c0386cSLinus Walleij 
17781c0386cSLinus Walleij 	if (!IS_ERR(ctx->clk)) {
17881c0386cSLinus Walleij 		ret = clk_enable(ctx->clk);
17981c0386cSLinus Walleij 		if (ret < 0)
18081c0386cSLinus Walleij 			return ret;
18181c0386cSLinus Walleij 	}
18281c0386cSLinus Walleij 
18381c0386cSLinus Walleij 	/*
18481c0386cSLinus Walleij 	 * There are no native, assembly-optimized write single register
18581c0386cSLinus Walleij 	 * operations for big endian, so fall back to emulation if this
18681c0386cSLinus Walleij 	 * is needed. (Single bytes are fine, they are not affected by
18781c0386cSLinus Walleij 	 * endianness.)
18881c0386cSLinus Walleij 	 */
18981c0386cSLinus Walleij 	if (ctx->big_endian && (ctx->val_bytes > 1)) {
19081c0386cSLinus Walleij 		switch (ctx->val_bytes) {
19181c0386cSLinus Walleij 		case 2:
19281c0386cSLinus Walleij 		{
19381c0386cSLinus Walleij 			const u16 *valp = (const u16 *)val;
19481c0386cSLinus Walleij 			for (i = 0; i < val_count; i++)
19581c0386cSLinus Walleij 				writew(swab16(valp[i]), ctx->regs + reg);
19681c0386cSLinus Walleij 			goto out_clk;
19781c0386cSLinus Walleij 		}
19881c0386cSLinus Walleij 		case 4:
19981c0386cSLinus Walleij 		{
20081c0386cSLinus Walleij 			const u32 *valp = (const u32 *)val;
20181c0386cSLinus Walleij 			for (i = 0; i < val_count; i++)
20281c0386cSLinus Walleij 				writel(swab32(valp[i]), ctx->regs + reg);
20381c0386cSLinus Walleij 			goto out_clk;
20481c0386cSLinus Walleij 		}
20581c0386cSLinus Walleij 		default:
20681c0386cSLinus Walleij 			ret = -EINVAL;
20781c0386cSLinus Walleij 			goto out_clk;
20881c0386cSLinus Walleij 		}
20981c0386cSLinus Walleij 	}
21081c0386cSLinus Walleij 
21181c0386cSLinus Walleij 	switch (ctx->val_bytes) {
21281c0386cSLinus Walleij 	case 1:
21381c0386cSLinus Walleij 		writesb(ctx->regs + reg, (const u8 *)val, val_count);
21481c0386cSLinus Walleij 		break;
21581c0386cSLinus Walleij 	case 2:
21681c0386cSLinus Walleij 		writesw(ctx->regs + reg, (const u16 *)val, val_count);
21781c0386cSLinus Walleij 		break;
21881c0386cSLinus Walleij 	case 4:
21981c0386cSLinus Walleij 		writesl(ctx->regs + reg, (const u32 *)val, val_count);
22081c0386cSLinus Walleij 		break;
22181c0386cSLinus Walleij 	default:
22281c0386cSLinus Walleij 		ret = -EINVAL;
22381c0386cSLinus Walleij 		break;
22481c0386cSLinus Walleij 	}
22581c0386cSLinus Walleij 
22681c0386cSLinus Walleij out_clk:
22781c0386cSLinus Walleij 	if (!IS_ERR(ctx->clk))
22881c0386cSLinus Walleij 		clk_disable(ctx->clk);
22981c0386cSLinus Walleij 
23081c0386cSLinus Walleij 	return ret;
23181c0386cSLinus Walleij }
23281c0386cSLinus Walleij 
regmap_mmio_read8(struct regmap_mmio_context * ctx,unsigned int reg)233922a9f93SMark Brown static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx,
234922a9f93SMark Brown 				      unsigned int reg)
23545f5ff81SStephen Warren {
236922a9f93SMark Brown 	return readb(ctx->regs + reg);
23745f5ff81SStephen Warren }
23845f5ff81SStephen Warren 
regmap_mmio_read8_relaxed(struct regmap_mmio_context * ctx,unsigned int reg)2396e1e90ecSAdrian Ratiu static unsigned int regmap_mmio_read8_relaxed(struct regmap_mmio_context *ctx,
2406e1e90ecSAdrian Ratiu 				      unsigned int reg)
2416e1e90ecSAdrian Ratiu {
2426e1e90ecSAdrian Ratiu 	return readb_relaxed(ctx->regs + reg);
2436e1e90ecSAdrian Ratiu }
2446e1e90ecSAdrian Ratiu 
regmap_mmio_ioread8(struct regmap_mmio_context * ctx,unsigned int reg)24593ce5576SAndy Shevchenko static unsigned int regmap_mmio_ioread8(struct regmap_mmio_context *ctx,
24693ce5576SAndy Shevchenko 					unsigned int reg)
24793ce5576SAndy Shevchenko {
24893ce5576SAndy Shevchenko 	return ioread8(ctx->regs + reg);
24993ce5576SAndy Shevchenko }
25093ce5576SAndy Shevchenko 
regmap_mmio_read16le(struct regmap_mmio_context * ctx,unsigned int reg)251922a9f93SMark Brown static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx,
252922a9f93SMark Brown 				         unsigned int reg)
253922a9f93SMark Brown {
254922a9f93SMark Brown 	return readw(ctx->regs + reg);
255922a9f93SMark Brown }
256922a9f93SMark Brown 
regmap_mmio_read16le_relaxed(struct regmap_mmio_context * ctx,unsigned int reg)2576e1e90ecSAdrian Ratiu static unsigned int regmap_mmio_read16le_relaxed(struct regmap_mmio_context *ctx,
2586e1e90ecSAdrian Ratiu 						 unsigned int reg)
2596e1e90ecSAdrian Ratiu {
2606e1e90ecSAdrian Ratiu 	return readw_relaxed(ctx->regs + reg);
2616e1e90ecSAdrian Ratiu }
2626e1e90ecSAdrian Ratiu 
regmap_mmio_ioread16le(struct regmap_mmio_context * ctx,unsigned int reg)26393ce5576SAndy Shevchenko static unsigned int regmap_mmio_ioread16le(struct regmap_mmio_context *ctx,
26493ce5576SAndy Shevchenko 					   unsigned int reg)
26593ce5576SAndy Shevchenko {
26693ce5576SAndy Shevchenko 	return ioread16(ctx->regs + reg);
26793ce5576SAndy Shevchenko }
26893ce5576SAndy Shevchenko 
regmap_mmio_read16be(struct regmap_mmio_context * ctx,unsigned int reg)269922a9f93SMark Brown static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx,
270922a9f93SMark Brown 				         unsigned int reg)
271922a9f93SMark Brown {
2727e7ba58cSAndy Shevchenko 	return swab16(readw(ctx->regs + reg));
273922a9f93SMark Brown }
274922a9f93SMark Brown 
regmap_mmio_ioread16be(struct regmap_mmio_context * ctx,unsigned int reg)27593ce5576SAndy Shevchenko static unsigned int regmap_mmio_ioread16be(struct regmap_mmio_context *ctx,
27693ce5576SAndy Shevchenko 					   unsigned int reg)
27793ce5576SAndy Shevchenko {
27893ce5576SAndy Shevchenko 	return ioread16be(ctx->regs + reg);
27993ce5576SAndy Shevchenko }
28093ce5576SAndy Shevchenko 
regmap_mmio_read32le(struct regmap_mmio_context * ctx,unsigned int reg)281922a9f93SMark Brown static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx,
282922a9f93SMark Brown 				         unsigned int reg)
283922a9f93SMark Brown {
284922a9f93SMark Brown 	return readl(ctx->regs + reg);
285922a9f93SMark Brown }
286922a9f93SMark Brown 
regmap_mmio_read32le_relaxed(struct regmap_mmio_context * ctx,unsigned int reg)2876e1e90ecSAdrian Ratiu static unsigned int regmap_mmio_read32le_relaxed(struct regmap_mmio_context *ctx,
2886e1e90ecSAdrian Ratiu 						 unsigned int reg)
2896e1e90ecSAdrian Ratiu {
2906e1e90ecSAdrian Ratiu 	return readl_relaxed(ctx->regs + reg);
2916e1e90ecSAdrian Ratiu }
2926e1e90ecSAdrian Ratiu 
regmap_mmio_ioread32le(struct regmap_mmio_context * ctx,unsigned int reg)29393ce5576SAndy Shevchenko static unsigned int regmap_mmio_ioread32le(struct regmap_mmio_context *ctx,
29493ce5576SAndy Shevchenko 					   unsigned int reg)
29593ce5576SAndy Shevchenko {
29693ce5576SAndy Shevchenko 	return ioread32(ctx->regs + reg);
29793ce5576SAndy Shevchenko }
29893ce5576SAndy Shevchenko 
regmap_mmio_read32be(struct regmap_mmio_context * ctx,unsigned int reg)299922a9f93SMark Brown static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx,
300922a9f93SMark Brown 				         unsigned int reg)
301922a9f93SMark Brown {
3027e7ba58cSAndy Shevchenko 	return swab32(readl(ctx->regs + reg));
303922a9f93SMark Brown }
304922a9f93SMark Brown 
regmap_mmio_ioread32be(struct regmap_mmio_context * ctx,unsigned int reg)30593ce5576SAndy Shevchenko static unsigned int regmap_mmio_ioread32be(struct regmap_mmio_context *ctx,
30693ce5576SAndy Shevchenko 					   unsigned int reg)
30793ce5576SAndy Shevchenko {
30893ce5576SAndy Shevchenko 	return ioread32be(ctx->regs + reg);
30993ce5576SAndy Shevchenko }
31093ce5576SAndy Shevchenko 
regmap_mmio_read(void * context,unsigned int reg,unsigned int * val)311922a9f93SMark Brown static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val)
31245f5ff81SStephen Warren {
31345f5ff81SStephen Warren 	struct regmap_mmio_context *ctx = context;
314878ec67bSPhilipp Zabel 	int ret;
31545f5ff81SStephen Warren 
3166b8e090eSStephen Warren 	if (!IS_ERR(ctx->clk)) {
317878ec67bSPhilipp Zabel 		ret = clk_enable(ctx->clk);
318878ec67bSPhilipp Zabel 		if (ret < 0)
319878ec67bSPhilipp Zabel 			return ret;
320878ec67bSPhilipp Zabel 	}
321878ec67bSPhilipp Zabel 
322922a9f93SMark Brown 	*val = ctx->reg_read(ctx, reg);
32345f5ff81SStephen Warren 
3246b8e090eSStephen Warren 	if (!IS_ERR(ctx->clk))
325878ec67bSPhilipp Zabel 		clk_disable(ctx->clk);
326878ec67bSPhilipp Zabel 
32745f5ff81SStephen Warren 	return 0;
32845f5ff81SStephen Warren }
32945f5ff81SStephen Warren 
regmap_mmio_noinc_read(void * context,unsigned int reg,void * val,size_t val_count)33081c0386cSLinus Walleij static int regmap_mmio_noinc_read(void *context, unsigned int reg,
33181c0386cSLinus Walleij 				  void *val, size_t val_count)
33281c0386cSLinus Walleij {
33381c0386cSLinus Walleij 	struct regmap_mmio_context *ctx = context;
33481c0386cSLinus Walleij 	int ret = 0;
33581c0386cSLinus Walleij 
33681c0386cSLinus Walleij 	if (!IS_ERR(ctx->clk)) {
33781c0386cSLinus Walleij 		ret = clk_enable(ctx->clk);
33881c0386cSLinus Walleij 		if (ret < 0)
33981c0386cSLinus Walleij 			return ret;
34081c0386cSLinus Walleij 	}
34181c0386cSLinus Walleij 
34281c0386cSLinus Walleij 	switch (ctx->val_bytes) {
34381c0386cSLinus Walleij 	case 1:
34481c0386cSLinus Walleij 		readsb(ctx->regs + reg, (u8 *)val, val_count);
34581c0386cSLinus Walleij 		break;
34681c0386cSLinus Walleij 	case 2:
34781c0386cSLinus Walleij 		readsw(ctx->regs + reg, (u16 *)val, val_count);
34881c0386cSLinus Walleij 		break;
34981c0386cSLinus Walleij 	case 4:
35081c0386cSLinus Walleij 		readsl(ctx->regs + reg, (u32 *)val, val_count);
35181c0386cSLinus Walleij 		break;
35281c0386cSLinus Walleij 	default:
35381c0386cSLinus Walleij 		ret = -EINVAL;
35481c0386cSLinus Walleij 		goto out_clk;
35581c0386cSLinus Walleij 	}
35681c0386cSLinus Walleij 
35781c0386cSLinus Walleij 	/*
35881c0386cSLinus Walleij 	 * There are no native, assembly-optimized write single register
35981c0386cSLinus Walleij 	 * operations for big endian, so fall back to emulation if this
36081c0386cSLinus Walleij 	 * is needed. (Single bytes are fine, they are not affected by
36181c0386cSLinus Walleij 	 * endianness.)
36281c0386cSLinus Walleij 	 */
36381c0386cSLinus Walleij 	if (ctx->big_endian && (ctx->val_bytes > 1)) {
36481c0386cSLinus Walleij 		switch (ctx->val_bytes) {
36581c0386cSLinus Walleij 		case 2:
366400dceb6SAndy Shevchenko 			swab16_array(val, val_count);
36781c0386cSLinus Walleij 			break;
36881c0386cSLinus Walleij 		case 4:
369400dceb6SAndy Shevchenko 			swab32_array(val, val_count);
37081c0386cSLinus Walleij 			break;
37181c0386cSLinus Walleij 		default:
37281c0386cSLinus Walleij 			ret = -EINVAL;
37381c0386cSLinus Walleij 			break;
37481c0386cSLinus Walleij 		}
37581c0386cSLinus Walleij 	}
37681c0386cSLinus Walleij 
37781c0386cSLinus Walleij out_clk:
37881c0386cSLinus Walleij 	if (!IS_ERR(ctx->clk))
37981c0386cSLinus Walleij 		clk_disable(ctx->clk);
38081c0386cSLinus Walleij 
38181c0386cSLinus Walleij 	return ret;
38281c0386cSLinus Walleij }
38381c0386cSLinus Walleij 
38481c0386cSLinus Walleij 
regmap_mmio_free_context(void * context)38545f5ff81SStephen Warren static void regmap_mmio_free_context(void *context)
38645f5ff81SStephen Warren {
387878ec67bSPhilipp Zabel 	struct regmap_mmio_context *ctx = context;
388878ec67bSPhilipp Zabel 
3896b8e090eSStephen Warren 	if (!IS_ERR(ctx->clk)) {
390878ec67bSPhilipp Zabel 		clk_unprepare(ctx->clk);
391eb4a219dSJames Kelly 		if (!ctx->attached_clk)
392878ec67bSPhilipp Zabel 			clk_put(ctx->clk);
393878ec67bSPhilipp Zabel 	}
39445f5ff81SStephen Warren 	kfree(context);
39545f5ff81SStephen Warren }
39645f5ff81SStephen Warren 
397922a9f93SMark Brown static const struct regmap_bus regmap_mmio = {
39845f5ff81SStephen Warren 	.fast_io = true,
399922a9f93SMark Brown 	.reg_write = regmap_mmio_write,
400922a9f93SMark Brown 	.reg_read = regmap_mmio_read,
40181c0386cSLinus Walleij 	.reg_noinc_write = regmap_mmio_noinc_write,
40281c0386cSLinus Walleij 	.reg_noinc_read = regmap_mmio_noinc_read,
40345f5ff81SStephen Warren 	.free_context = regmap_mmio_free_context,
4042ed94f6fSMark Brown 	.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
40545f5ff81SStephen Warren };
40645f5ff81SStephen Warren 
regmap_mmio_gen_context(struct device * dev,const char * clk_id,void __iomem * regs,const struct regmap_config * config)407878ec67bSPhilipp Zabel static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
408878ec67bSPhilipp Zabel 					const char *clk_id,
409878ec67bSPhilipp Zabel 					void __iomem *regs,
41045f5ff81SStephen Warren 					const struct regmap_config *config)
41145f5ff81SStephen Warren {
41245f5ff81SStephen Warren 	struct regmap_mmio_context *ctx;
413f01ee60fSStephen Warren 	int min_stride;
414878ec67bSPhilipp Zabel 	int ret;
41545f5ff81SStephen Warren 
416451485baSXiubo Li 	ret = regmap_mmio_regbits_check(config->reg_bits);
417451485baSXiubo Li 	if (ret)
418451485baSXiubo Li 		return ERR_PTR(ret);
41945f5ff81SStephen Warren 
42045f5ff81SStephen Warren 	if (config->pad_bits)
42145f5ff81SStephen Warren 		return ERR_PTR(-EINVAL);
42245f5ff81SStephen Warren 
42375fb0aaeSXiubo Li 	min_stride = regmap_mmio_get_min_stride(config->val_bits);
42475fb0aaeSXiubo Li 	if (min_stride < 0)
42575fb0aaeSXiubo Li 		return ERR_PTR(min_stride);
42645f5ff81SStephen Warren 
427*e12ff287SMaxime Chevallier 	if (config->reg_stride && config->reg_stride < min_stride)
428f01ee60fSStephen Warren 		return ERR_PTR(-EINVAL);
429f01ee60fSStephen Warren 
43093ce5576SAndy Shevchenko 	if (config->use_relaxed_mmio && config->io_port)
43193ce5576SAndy Shevchenko 		return ERR_PTR(-EINVAL);
43293ce5576SAndy Shevchenko 
43346335119SDimitris Papastamos 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
43445f5ff81SStephen Warren 	if (!ctx)
43545f5ff81SStephen Warren 		return ERR_PTR(-ENOMEM);
43645f5ff81SStephen Warren 
43745f5ff81SStephen Warren 	ctx->regs = regs;
43845f5ff81SStephen Warren 	ctx->val_bytes = config->val_bits / 8;
4396b8e090eSStephen Warren 	ctx->clk = ERR_PTR(-ENODEV);
44045f5ff81SStephen Warren 
4410dbdb76cSMark Brown 	switch (regmap_get_val_endian(dev, &regmap_mmio, config)) {
442922a9f93SMark Brown 	case REGMAP_ENDIAN_DEFAULT:
443922a9f93SMark Brown 	case REGMAP_ENDIAN_LITTLE:
444922a9f93SMark Brown #ifdef __LITTLE_ENDIAN
445922a9f93SMark Brown 	case REGMAP_ENDIAN_NATIVE:
446922a9f93SMark Brown #endif
447922a9f93SMark Brown 		switch (config->val_bits) {
448922a9f93SMark Brown 		case 8:
44993ce5576SAndy Shevchenko 			if (config->io_port) {
45093ce5576SAndy Shevchenko 				ctx->reg_read = regmap_mmio_ioread8;
45193ce5576SAndy Shevchenko 				ctx->reg_write = regmap_mmio_iowrite8;
45293ce5576SAndy Shevchenko 			} else if (config->use_relaxed_mmio) {
4536e1e90ecSAdrian Ratiu 				ctx->reg_read = regmap_mmio_read8_relaxed;
4546e1e90ecSAdrian Ratiu 				ctx->reg_write = regmap_mmio_write8_relaxed;
4556e1e90ecSAdrian Ratiu 			} else {
456922a9f93SMark Brown 				ctx->reg_read = regmap_mmio_read8;
457922a9f93SMark Brown 				ctx->reg_write = regmap_mmio_write8;
4586e1e90ecSAdrian Ratiu 			}
459922a9f93SMark Brown 			break;
460922a9f93SMark Brown 		case 16:
46193ce5576SAndy Shevchenko 			if (config->io_port) {
46293ce5576SAndy Shevchenko 				ctx->reg_read = regmap_mmio_ioread16le;
46393ce5576SAndy Shevchenko 				ctx->reg_write = regmap_mmio_iowrite16le;
46493ce5576SAndy Shevchenko 			} else if (config->use_relaxed_mmio) {
4656e1e90ecSAdrian Ratiu 				ctx->reg_read = regmap_mmio_read16le_relaxed;
4666e1e90ecSAdrian Ratiu 				ctx->reg_write = regmap_mmio_write16le_relaxed;
4676e1e90ecSAdrian Ratiu 			} else {
468922a9f93SMark Brown 				ctx->reg_read = regmap_mmio_read16le;
469922a9f93SMark Brown 				ctx->reg_write = regmap_mmio_write16le;
4706e1e90ecSAdrian Ratiu 			}
471922a9f93SMark Brown 			break;
472922a9f93SMark Brown 		case 32:
47393ce5576SAndy Shevchenko 			if (config->io_port) {
47493ce5576SAndy Shevchenko 				ctx->reg_read = regmap_mmio_ioread32le;
47593ce5576SAndy Shevchenko 				ctx->reg_write = regmap_mmio_iowrite32le;
47693ce5576SAndy Shevchenko 			} else if (config->use_relaxed_mmio) {
4776e1e90ecSAdrian Ratiu 				ctx->reg_read = regmap_mmio_read32le_relaxed;
4786e1e90ecSAdrian Ratiu 				ctx->reg_write = regmap_mmio_write32le_relaxed;
4796e1e90ecSAdrian Ratiu 			} else {
480922a9f93SMark Brown 				ctx->reg_read = regmap_mmio_read32le;
481922a9f93SMark Brown 				ctx->reg_write = regmap_mmio_write32le;
4826e1e90ecSAdrian Ratiu 			}
483922a9f93SMark Brown 			break;
484922a9f93SMark Brown 		default:
485922a9f93SMark Brown 			ret = -EINVAL;
486922a9f93SMark Brown 			goto err_free;
487922a9f93SMark Brown 		}
488922a9f93SMark Brown 		break;
489922a9f93SMark Brown 	case REGMAP_ENDIAN_BIG:
490922a9f93SMark Brown #ifdef __BIG_ENDIAN
491922a9f93SMark Brown 	case REGMAP_ENDIAN_NATIVE:
492922a9f93SMark Brown #endif
49381c0386cSLinus Walleij 		ctx->big_endian = true;
494922a9f93SMark Brown 		switch (config->val_bits) {
495922a9f93SMark Brown 		case 8:
49693ce5576SAndy Shevchenko 			if (config->io_port) {
49793ce5576SAndy Shevchenko 				ctx->reg_read = regmap_mmio_ioread8;
49893ce5576SAndy Shevchenko 				ctx->reg_write = regmap_mmio_iowrite8;
49993ce5576SAndy Shevchenko 			} else {
500922a9f93SMark Brown 				ctx->reg_read = regmap_mmio_read8;
501922a9f93SMark Brown 				ctx->reg_write = regmap_mmio_write8;
50293ce5576SAndy Shevchenko 			}
503922a9f93SMark Brown 			break;
504922a9f93SMark Brown 		case 16:
50593ce5576SAndy Shevchenko 			if (config->io_port) {
50693ce5576SAndy Shevchenko 				ctx->reg_read = regmap_mmio_ioread16be;
50793ce5576SAndy Shevchenko 				ctx->reg_write = regmap_mmio_iowrite16be;
50893ce5576SAndy Shevchenko 			} else {
509922a9f93SMark Brown 				ctx->reg_read = regmap_mmio_read16be;
510922a9f93SMark Brown 				ctx->reg_write = regmap_mmio_write16be;
51193ce5576SAndy Shevchenko 			}
512922a9f93SMark Brown 			break;
513922a9f93SMark Brown 		case 32:
51493ce5576SAndy Shevchenko 			if (config->io_port) {
51593ce5576SAndy Shevchenko 				ctx->reg_read = regmap_mmio_ioread32be;
51693ce5576SAndy Shevchenko 				ctx->reg_write = regmap_mmio_iowrite32be;
51793ce5576SAndy Shevchenko 			} else {
518922a9f93SMark Brown 				ctx->reg_read = regmap_mmio_read32be;
519922a9f93SMark Brown 				ctx->reg_write = regmap_mmio_write32be;
52093ce5576SAndy Shevchenko 			}
521922a9f93SMark Brown 			break;
522922a9f93SMark Brown 		default:
523922a9f93SMark Brown 			ret = -EINVAL;
524922a9f93SMark Brown 			goto err_free;
525922a9f93SMark Brown 		}
526922a9f93SMark Brown 		break;
527922a9f93SMark Brown 	default:
528922a9f93SMark Brown 		ret = -EINVAL;
529922a9f93SMark Brown 		goto err_free;
530922a9f93SMark Brown 	}
531922a9f93SMark Brown 
532878ec67bSPhilipp Zabel 	if (clk_id == NULL)
53345f5ff81SStephen Warren 		return ctx;
534878ec67bSPhilipp Zabel 
535878ec67bSPhilipp Zabel 	ctx->clk = clk_get(dev, clk_id);
536878ec67bSPhilipp Zabel 	if (IS_ERR(ctx->clk)) {
537878ec67bSPhilipp Zabel 		ret = PTR_ERR(ctx->clk);
538878ec67bSPhilipp Zabel 		goto err_free;
539878ec67bSPhilipp Zabel 	}
540878ec67bSPhilipp Zabel 
541878ec67bSPhilipp Zabel 	ret = clk_prepare(ctx->clk);
542878ec67bSPhilipp Zabel 	if (ret < 0) {
543878ec67bSPhilipp Zabel 		clk_put(ctx->clk);
544878ec67bSPhilipp Zabel 		goto err_free;
545878ec67bSPhilipp Zabel 	}
546878ec67bSPhilipp Zabel 
547878ec67bSPhilipp Zabel 	return ctx;
548878ec67bSPhilipp Zabel 
549878ec67bSPhilipp Zabel err_free:
550878ec67bSPhilipp Zabel 	kfree(ctx);
551878ec67bSPhilipp Zabel 
552878ec67bSPhilipp Zabel 	return ERR_PTR(ret);
55345f5ff81SStephen Warren }
55445f5ff81SStephen Warren 
__regmap_init_mmio_clk(struct device * dev,const char * clk_id,void __iomem * regs,const struct regmap_config * config,struct lock_class_key * lock_key,const char * lock_name)5553cfe7a74SNicolas Boichat struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id,
55645f5ff81SStephen Warren 				      void __iomem *regs,
5573cfe7a74SNicolas Boichat 				      const struct regmap_config *config,
5583cfe7a74SNicolas Boichat 				      struct lock_class_key *lock_key,
5593cfe7a74SNicolas Boichat 				      const char *lock_name)
56045f5ff81SStephen Warren {
56145f5ff81SStephen Warren 	struct regmap_mmio_context *ctx;
56245f5ff81SStephen Warren 
563878ec67bSPhilipp Zabel 	ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
56445f5ff81SStephen Warren 	if (IS_ERR(ctx))
56545f5ff81SStephen Warren 		return ERR_CAST(ctx);
56645f5ff81SStephen Warren 
5673cfe7a74SNicolas Boichat 	return __regmap_init(dev, &regmap_mmio, ctx, config,
5683cfe7a74SNicolas Boichat 			     lock_key, lock_name);
56945f5ff81SStephen Warren }
5703cfe7a74SNicolas Boichat EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk);
57145f5ff81SStephen Warren 
__devm_regmap_init_mmio_clk(struct device * dev,const char * clk_id,void __iomem * regs,const struct regmap_config * config,struct lock_class_key * lock_key,const char * lock_name)5723cfe7a74SNicolas Boichat struct regmap *__devm_regmap_init_mmio_clk(struct device *dev,
5733cfe7a74SNicolas Boichat 					   const char *clk_id,
57445f5ff81SStephen Warren 					   void __iomem *regs,
5753cfe7a74SNicolas Boichat 					   const struct regmap_config *config,
5763cfe7a74SNicolas Boichat 					   struct lock_class_key *lock_key,
5773cfe7a74SNicolas Boichat 					   const char *lock_name)
57845f5ff81SStephen Warren {
57945f5ff81SStephen Warren 	struct regmap_mmio_context *ctx;
58045f5ff81SStephen Warren 
581878ec67bSPhilipp Zabel 	ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
58245f5ff81SStephen Warren 	if (IS_ERR(ctx))
58345f5ff81SStephen Warren 		return ERR_CAST(ctx);
58445f5ff81SStephen Warren 
5853cfe7a74SNicolas Boichat 	return __devm_regmap_init(dev, &regmap_mmio, ctx, config,
5863cfe7a74SNicolas Boichat 				  lock_key, lock_name);
58745f5ff81SStephen Warren }
5883cfe7a74SNicolas Boichat EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk);
58945f5ff81SStephen Warren 
regmap_mmio_attach_clk(struct regmap * map,struct clk * clk)59031895662SMaxime Ripard int regmap_mmio_attach_clk(struct regmap *map, struct clk *clk)
59131895662SMaxime Ripard {
59231895662SMaxime Ripard 	struct regmap_mmio_context *ctx = map->bus_context;
59331895662SMaxime Ripard 
59431895662SMaxime Ripard 	ctx->clk = clk;
59531895662SMaxime Ripard 	ctx->attached_clk = true;
59631895662SMaxime Ripard 
59731895662SMaxime Ripard 	return clk_prepare(ctx->clk);
59831895662SMaxime Ripard }
59931895662SMaxime Ripard EXPORT_SYMBOL_GPL(regmap_mmio_attach_clk);
60031895662SMaxime Ripard 
regmap_mmio_detach_clk(struct regmap * map)60131895662SMaxime Ripard void regmap_mmio_detach_clk(struct regmap *map)
60231895662SMaxime Ripard {
60331895662SMaxime Ripard 	struct regmap_mmio_context *ctx = map->bus_context;
60431895662SMaxime Ripard 
60531895662SMaxime Ripard 	clk_unprepare(ctx->clk);
60631895662SMaxime Ripard 
60731895662SMaxime Ripard 	ctx->attached_clk = false;
60831895662SMaxime Ripard 	ctx->clk = NULL;
60931895662SMaxime Ripard }
61031895662SMaxime Ripard EXPORT_SYMBOL_GPL(regmap_mmio_detach_clk);
61131895662SMaxime Ripard 
61245f5ff81SStephen Warren MODULE_LICENSE("GPL v2");
613