From 439e8211a78ad31615c76b18e4f13eb900c89758 Mon Sep 17 00:00:00 2001
From: Xiubo Li
Date: Thu, 9 Oct 2014 17:02:54 +0800
Subject: [PATCH 1/8] regmap: cache: fix errno in regcache_hw_init()
When kmalloc() fails, we should return -ENOMEM.
Signed-off-by: Xiubo Li
Signed-off-by: Mark Brown
---
drivers/base/regmap/regcache.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index f1280dc356d0..44e6a48019a5 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -44,7 +44,7 @@ static int regcache_hw_init(struct regmap *map)
map->cache_bypass = 1;
tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL);
if (!tmp_buf)
- return -EINVAL;
+ return -ENOMEM;
ret = regmap_raw_read(map, 0, tmp_buf,
map->num_reg_defaults_raw);
map->cache_bypass = cache_bypass;
From 5bd83ed0984b0b0ac51d648b0ec167b36291f87e Mon Sep 17 00:00:00 2001
From: Xiubo Li
Date: Thu, 9 Oct 2014 17:02:53 +0800
Subject: [PATCH 2/8] regmap: cache: cleanup regcache_hw_init()
Remove the redundant code for regmap cache.
Signed-off-by: Xiubo Li
Signed-off-by: Mark Brown
---
drivers/base/regmap/regcache.c | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index f1280dc356d0..7558e6b01c04 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -57,12 +57,9 @@ static int regcache_hw_init(struct regmap *map)
}
/* calculate the size of reg_defaults */
- for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) {
- val = regcache_get_val(map, map->reg_defaults_raw, i);
- if (regmap_volatile(map, i * map->reg_stride))
- continue;
- count++;
- }
+ for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++)
+ if (!regmap_volatile(map, i * map->reg_stride))
+ count++;
map->reg_defaults = kmalloc(count * sizeof(struct reg_default),
GFP_KERNEL);
From ba3f1c85a6e05e0ff90215d817249617fcbee523 Mon Sep 17 00:00:00 2001
From: Xiubo Li
Date: Thu, 9 Oct 2014 17:02:54 +0800
Subject: [PATCH 3/8] regmap: cache: fix errno in regcache_hw_init()
When kmalloc() fails, we should return -ENOMEM.
Signed-off-by: Xiubo Li
Signed-off-by: Mark Brown
---
drivers/base/regmap/regcache.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 7558e6b01c04..6bec659e35d9 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -44,7 +44,7 @@ static int regcache_hw_init(struct regmap *map)
map->cache_bypass = 1;
tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL);
if (!tmp_buf)
- return -EINVAL;
+ return -ENOMEM;
ret = regmap_raw_read(map, 0, tmp_buf,
map->num_reg_defaults_raw);
map->cache_bypass = cache_bypass;
From fbba43c527d851088a891ed83346d5cc9f095b64 Mon Sep 17 00:00:00 2001
From: Xiubo Li
Date: Thu, 9 Oct 2014 17:02:55 +0800
Subject: [PATCH 4/8] regmap: cache: speed regcache_hw_init() up.
This may speed regcache_hw_init() up for some cases that there
has volatile registers.
Signed-off-by: Xiubo Li
Signed-off-by: Mark Brown
---
drivers/base/regmap/regcache.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 6bec659e35d9..0852c890b4b5 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -71,9 +71,9 @@ static int regcache_hw_init(struct regmap *map)
/* fill the reg_defaults */
map->num_reg_defaults = count;
for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) {
- val = regcache_get_val(map, map->reg_defaults_raw, i);
if (regmap_volatile(map, i * map->reg_stride))
continue;
+ val = regcache_get_val(map, map->reg_defaults_raw, i);
map->reg_defaults[j].reg = i * map->reg_stride;
map->reg_defaults[j].def = val;
j++;
From 06f9c24e55075e50ebc8b249a7853ad31bc1b3d1 Mon Sep 17 00:00:00 2001
From: Xiubo Li
Date: Thu, 9 Oct 2014 17:02:56 +0800
Subject: [PATCH 5/8] regmap: cache: use kmalloc_array instead of kmalloc
This patch fixes checkpatch.pl warning for regmap cache.
WARNING : prefer kmalloc_array over kmalloc with multiply
Signed-off-by: Xiubo Li
Signed-off-by: Mark Brown
---
drivers/base/regmap/regcache.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 0852c890b4b5..726d956f6a29 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -61,8 +61,8 @@ static int regcache_hw_init(struct regmap *map)
if (!regmap_volatile(map, i * map->reg_stride))
count++;
- map->reg_defaults = kmalloc(count * sizeof(struct reg_default),
- GFP_KERNEL);
+ map->reg_defaults = kmalloc_array(count, sizeof(struct reg_default),
+ GFP_KERNEL);
if (!map->reg_defaults) {
ret = -ENOMEM;
goto err_free;
From fb70067e4a2cc45d273864f071fd6cf62143137a Mon Sep 17 00:00:00 2001
From: Xiubo Li
Date: Thu, 9 Oct 2014 17:02:57 +0800
Subject: [PATCH 6/8] regmap: cache: Fix possible ZERO_SIZE_PTR pointer
dereferencing error.
When all the registers are volatile(unlikely, but logically and mostly
will happen for some 'device' who has very few registers), then the
count will be euqal to 0, then kmalloc() will return ZERO_SIZE_PTR,
which equals to ((void *)16).
So this patch fix this with just doing the zero check before calling
kmalloc(). If the count == 0, so we can make sure that all the registers
are volatile, so no cache is need.
Signed-off-by: Xiubo Li
Signed-off-by: Mark Brown
---
drivers/base/regmap/regcache.c | 50 ++++++++++++++++++++--------------
1 file changed, 29 insertions(+), 21 deletions(-)
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 726d956f6a29..d815929ba745 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -36,6 +36,23 @@ static int regcache_hw_init(struct regmap *map)
if (!map->num_reg_defaults_raw)
return -EINVAL;
+ /* calculate the size of reg_defaults */
+ for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++)
+ if (!regmap_volatile(map, i * map->reg_stride))
+ count++;
+
+ /* all registers are volatile, so just bypass */
+ if (!count) {
+ map->cache_bypass = true;
+ return 0;
+ }
+
+ map->num_reg_defaults = count;
+ map->reg_defaults = kmalloc_array(count, sizeof(struct reg_default),
+ GFP_KERNEL);
+ if (!map->reg_defaults)
+ return -ENOMEM;
+
if (!map->reg_defaults_raw) {
u32 cache_bypass = map->cache_bypass;
dev_warn(map->dev, "No cache defaults, reading back from HW\n");
@@ -43,33 +60,21 @@ static int regcache_hw_init(struct regmap *map)
/* Bypass the cache access till data read from HW*/
map->cache_bypass = 1;
tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL);
- if (!tmp_buf)
- return -ENOMEM;
+ if (!tmp_buf) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
ret = regmap_raw_read(map, 0, tmp_buf,
map->num_reg_defaults_raw);
map->cache_bypass = cache_bypass;
- if (ret < 0) {
- kfree(tmp_buf);
- return ret;
- }
+ if (ret < 0)
+ goto err_cache_free;
+
map->reg_defaults_raw = tmp_buf;
map->cache_free = 1;
}
- /* calculate the size of reg_defaults */
- for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++)
- if (!regmap_volatile(map, i * map->reg_stride))
- count++;
-
- map->reg_defaults = kmalloc_array(count, sizeof(struct reg_default),
- GFP_KERNEL);
- if (!map->reg_defaults) {
- ret = -ENOMEM;
- goto err_free;
- }
-
/* fill the reg_defaults */
- map->num_reg_defaults = count;
for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) {
if (regmap_volatile(map, i * map->reg_stride))
continue;
@@ -81,9 +86,10 @@ static int regcache_hw_init(struct regmap *map)
return 0;
+err_cache_free:
+ kfree(tmp_buf);
err_free:
- if (map->cache_free)
- kfree(map->reg_defaults_raw);
+ kfree(map->reg_defaults);
return ret;
}
@@ -147,6 +153,8 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
ret = regcache_hw_init(map);
if (ret < 0)
return ret;
+ if (map->cache_bypass)
+ return 0;
}
if (!map->max_register)
From e39be3a31b8f16d92fff096e92b593a9bffecb93 Mon Sep 17 00:00:00 2001
From: Xiubo Li
Date: Thu, 9 Oct 2014 17:02:52 +0800
Subject: [PATCH 7/8] regmap: cache: Sort include headers alphabetically
If the inlcude headers aren't sorted alphabetically, then the
logical choice is to append new ones, however that creates a
lot of potential for conflicts or duplicates because every change
will then add new includes in the same location.
Signed-off-by: Xiubo Li
Signed-off-by: Mark Brown
---
drivers/base/regmap/regcache-flat.c | 2 +-
drivers/base/regmap/regcache-lzo.c | 2 +-
drivers/base/regmap/regcache-rbtree.c | 4 ++--
drivers/base/regmap/regcache.c | 8 ++++----
4 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c
index d9762e41959b..0246f44ded74 100644
--- a/drivers/base/regmap/regcache-flat.c
+++ b/drivers/base/regmap/regcache-flat.c
@@ -10,9 +10,9 @@
* published by the Free Software Foundation.
*/
-#include
#include
#include
+#include
#include "internal.h"
diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c
index e210a6d1406a..2d53f6f138e1 100644
--- a/drivers/base/regmap/regcache-lzo.c
+++ b/drivers/base/regmap/regcache-lzo.c
@@ -10,9 +10,9 @@
* published by the Free Software Foundation.
*/
-#include
#include
#include
+#include
#include "internal.h"
diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c
index f3e8fe0cc650..d453a2c98ad0 100644
--- a/drivers/base/regmap/regcache-rbtree.c
+++ b/drivers/base/regmap/regcache-rbtree.c
@@ -10,11 +10,11 @@
* published by the Free Software Foundation.
*/
-#include
-#include
#include
+#include
#include
#include
+#include
#include "internal.h"
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index f1280dc356d0..489680433f9d 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -10,12 +10,12 @@
* published by the Free Software Foundation.
*/
-#include
-#include
-#include
-#include
#include
+#include
+#include
+#include
#include
+#include
#include "internal.h"
From 22853223d15b3a626de62cf9e40ce2d3881bc0a8 Mon Sep 17 00:00:00 2001
From: Mark Brown
Date: Tue, 18 Nov 2014 19:45:51 +0100
Subject: [PATCH 8/8] regmap: ac97: Add generic AC'97 callbacks
Use the recently added support for bus operations to provide a standard
mapping for AC'97 register I/O.
Signed-off-by: Mark Brown
Signed-off-by: Lars-Peter Clausen
---
drivers/base/regmap/Kconfig | 5 +-
drivers/base/regmap/Makefile | 1 +
drivers/base/regmap/regmap-ac97.c | 114 ++++++++++++++++++++++++++++++
include/linux/regmap.h | 7 ++
4 files changed, 126 insertions(+), 1 deletion(-)
create mode 100644 drivers/base/regmap/regmap-ac97.c
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index 8a3f51f7b1b9..db9d00c36a3e 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -3,12 +3,15 @@
# subsystems should select the appropriate symbols.
config REGMAP
- default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_MMIO || REGMAP_IRQ)
+ default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ)
select LZO_COMPRESS
select LZO_DECOMPRESS
select IRQ_DOMAIN if REGMAP_IRQ
bool
+config REGMAP_AC97
+ tristate
+
config REGMAP_I2C
tristate
depends on I2C
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index a7c670b4123a..0a533653ef3b 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_REGMAP) += regmap.o regcache.o
obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o
obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
+obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
diff --git a/drivers/base/regmap/regmap-ac97.c b/drivers/base/regmap/regmap-ac97.c
new file mode 100644
index 000000000000..e4c45d2299c1
--- /dev/null
+++ b/drivers/base/regmap/regmap-ac97.c
@@ -0,0 +1,114 @@
+/*
+ * Register map access API - AC'97 support
+ *
+ * Copyright 2013 Linaro Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AC97_RESET:
+ case AC97_POWERDOWN:
+ case AC97_INT_PAGING:
+ case AC97_EXTENDED_ID:
+ case AC97_EXTENDED_STATUS:
+ case AC97_EXTENDED_MID:
+ case AC97_EXTENDED_MSTATUS:
+ case AC97_GPIO_STATUS:
+ case AC97_MISC_AFE:
+ case AC97_VENDOR_ID1:
+ case AC97_VENDOR_ID2:
+ case AC97_CODEC_CLASS_REV:
+ case AC97_PCI_SVID:
+ case AC97_PCI_SID:
+ case AC97_FUNC_SELECT:
+ case AC97_FUNC_INFO:
+ case AC97_SENSE_INFO:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_GPL(regmap_ac97_default_volatile);
+
+static int regmap_ac97_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct snd_ac97 *ac97 = context;
+
+ *val = ac97->bus->ops->read(ac97, reg);
+
+ return 0;
+}
+
+static int regmap_ac97_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct snd_ac97 *ac97 = context;
+
+ ac97->bus->ops->write(ac97, reg, val);
+
+ return 0;
+}
+
+static const struct regmap_bus ac97_regmap_bus = {
+ .reg_write = regmap_ac97_reg_write,
+ .reg_read = regmap_ac97_reg_read,
+};
+
+/**
+ * regmap_init_ac97(): Initialise AC'97 register map
+ *
+ * @ac97: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer to
+ * a struct regmap.
+ */
+struct regmap *regmap_init_ac97(struct snd_ac97 *ac97,
+ const struct regmap_config *config)
+{
+ return regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config);
+}
+EXPORT_SYMBOL_GPL(regmap_init_ac97);
+
+/**
+ * devm_regmap_init_ac97(): Initialise AC'97 register map
+ *
+ * @ac97: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap. The regmap will be automatically freed by the
+ * device management code.
+ */
+struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97,
+ const struct regmap_config *config)
+{
+ return devm_regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config);
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init_ac97);
+
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index c5ed83f49c4e..4419b99d8d6e 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -27,6 +27,7 @@ struct spmi_device;
struct regmap;
struct regmap_range_cfg;
struct regmap_field;
+struct snd_ac97;
/* An enum of all the supported cache types */
enum regcache_type {
@@ -340,6 +341,8 @@ struct regmap *regmap_init_spmi_ext(struct spmi_device *dev,
struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs,
const struct regmap_config *config);
+struct regmap *regmap_init_ac97(struct snd_ac97 *ac97,
+ const struct regmap_config *config);
struct regmap *devm_regmap_init(struct device *dev,
const struct regmap_bus *bus,
@@ -356,6 +359,10 @@ struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *dev,
struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs,
const struct regmap_config *config);
+struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97,
+ const struct regmap_config *config);
+
+bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
/**
* regmap_init_mmio(): Initialise register map