1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * This driver provides regmap to access to analog part of audio codec |
4 | * found on Allwinner A23, A31s, A33, H3 and A64 Socs |
5 | * |
6 | * Copyright 2016 Chen-Yu Tsai <wens@csie.org> |
7 | * Copyright (C) 2018 Vasily Khoruzhick <anarsoul@gmail.com> |
8 | */ |
9 | |
10 | #include <linux/io.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/module.h> |
13 | #include <linux/regmap.h> |
14 | |
15 | #include "sun8i-adda-pr-regmap.h" |
16 | |
17 | /* Analog control register access bits */ |
18 | #define ADDA_PR 0x0 /* PRCM base + 0x1c0 */ |
19 | #define ADDA_PR_RESET BIT(28) |
20 | #define ADDA_PR_WRITE BIT(24) |
21 | #define ADDA_PR_ADDR_SHIFT 16 |
22 | #define ADDA_PR_ADDR_MASK GENMASK(4, 0) |
23 | #define ADDA_PR_DATA_IN_SHIFT 8 |
24 | #define ADDA_PR_DATA_IN_MASK GENMASK(7, 0) |
25 | #define ADDA_PR_DATA_OUT_SHIFT 0 |
26 | #define ADDA_PR_DATA_OUT_MASK GENMASK(7, 0) |
27 | |
28 | /* regmap access bits */ |
29 | static int adda_reg_read(void *context, unsigned int reg, unsigned int *val) |
30 | { |
31 | void __iomem *base = (void __iomem *)context; |
32 | u32 tmp; |
33 | |
34 | /* De-assert reset */ |
35 | writel(readl(addr: base) | ADDA_PR_RESET, addr: base); |
36 | |
37 | /* Clear write bit */ |
38 | writel(readl(addr: base) & ~ADDA_PR_WRITE, addr: base); |
39 | |
40 | /* Set register address */ |
41 | tmp = readl(addr: base); |
42 | tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT); |
43 | tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT; |
44 | writel(val: tmp, addr: base); |
45 | |
46 | /* Read back value */ |
47 | *val = readl(addr: base) & ADDA_PR_DATA_OUT_MASK; |
48 | |
49 | return 0; |
50 | } |
51 | |
52 | static int adda_reg_write(void *context, unsigned int reg, unsigned int val) |
53 | { |
54 | void __iomem *base = (void __iomem *)context; |
55 | u32 tmp; |
56 | |
57 | /* De-assert reset */ |
58 | writel(readl(addr: base) | ADDA_PR_RESET, addr: base); |
59 | |
60 | /* Set register address */ |
61 | tmp = readl(addr: base); |
62 | tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT); |
63 | tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT; |
64 | writel(val: tmp, addr: base); |
65 | |
66 | /* Set data to write */ |
67 | tmp = readl(addr: base); |
68 | tmp &= ~(ADDA_PR_DATA_IN_MASK << ADDA_PR_DATA_IN_SHIFT); |
69 | tmp |= (val & ADDA_PR_DATA_IN_MASK) << ADDA_PR_DATA_IN_SHIFT; |
70 | writel(val: tmp, addr: base); |
71 | |
72 | /* Set write bit to signal a write */ |
73 | writel(readl(addr: base) | ADDA_PR_WRITE, addr: base); |
74 | |
75 | /* Clear write bit */ |
76 | writel(readl(addr: base) & ~ADDA_PR_WRITE, addr: base); |
77 | |
78 | return 0; |
79 | } |
80 | |
81 | static const struct regmap_config adda_pr_regmap_cfg = { |
82 | .name = "adda-pr" , |
83 | .reg_bits = 5, |
84 | .reg_stride = 1, |
85 | .val_bits = 8, |
86 | .reg_read = adda_reg_read, |
87 | .reg_write = adda_reg_write, |
88 | .fast_io = true, |
89 | .max_register = 31, |
90 | }; |
91 | |
92 | struct regmap *sun8i_adda_pr_regmap_init(struct device *dev, |
93 | void __iomem *base) |
94 | { |
95 | return devm_regmap_init(dev, NULL, base, &adda_pr_regmap_cfg); |
96 | } |
97 | EXPORT_SYMBOL_GPL(sun8i_adda_pr_regmap_init); |
98 | |
99 | MODULE_DESCRIPTION("Allwinner analog audio codec regmap driver" ); |
100 | MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>" ); |
101 | MODULE_LICENSE("GPL" ); |
102 | MODULE_ALIAS("platform:sunxi-adda-pr" ); |
103 | |