Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
b52207ef CF |
2 | /* |
3 | * Device driver for MFD hi655x PMIC | |
4 | * | |
07a0b7d6 | 5 | * Copyright (c) 2016 HiSilicon Ltd. |
b52207ef CF |
6 | * |
7 | * Authors: | |
8 | * Chen Feng <puck.chen@hisilicon.com> | |
9 | * Fei Wang <w.f@huawei.com> | |
b52207ef CF |
10 | */ |
11 | ||
b52207ef CF |
12 | #include <linux/io.h> |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/init.h> | |
15 | #include <linux/mfd/core.h> | |
16 | #include <linux/mfd/hi655x-pmic.h> | |
17 | #include <linux/module.h> | |
7f5aaa4a | 18 | #include <linux/gpio/consumer.h> |
b52207ef CF |
19 | #include <linux/of_platform.h> |
20 | #include <linux/platform_device.h> | |
21 | #include <linux/regmap.h> | |
22 | ||
b52207ef | 23 | static const struct regmap_irq hi655x_irqs[] = { |
9e3d5c99 JS |
24 | { .reg_offset = 0, .mask = OTMP_D1R_INT_MASK }, |
25 | { .reg_offset = 0, .mask = VSYS_2P5_R_INT_MASK }, | |
26 | { .reg_offset = 0, .mask = VSYS_UV_D3R_INT_MASK }, | |
27 | { .reg_offset = 0, .mask = VSYS_6P0_D200UR_INT_MASK }, | |
28 | { .reg_offset = 0, .mask = PWRON_D4SR_INT_MASK }, | |
29 | { .reg_offset = 0, .mask = PWRON_D20F_INT_MASK }, | |
30 | { .reg_offset = 0, .mask = PWRON_D20R_INT_MASK }, | |
31 | { .reg_offset = 0, .mask = RESERVE_INT_MASK }, | |
b52207ef CF |
32 | }; |
33 | ||
34 | static const struct regmap_irq_chip hi655x_irq_chip = { | |
35 | .name = "hi655x-pmic", | |
36 | .irqs = hi655x_irqs, | |
37 | .num_regs = 1, | |
38 | .num_irqs = ARRAY_SIZE(hi655x_irqs), | |
39 | .status_base = HI655X_IRQ_STAT_BASE, | |
417e06bb | 40 | .ack_base = HI655X_IRQ_STAT_BASE, |
b52207ef CF |
41 | .mask_base = HI655X_IRQ_MASK_BASE, |
42 | }; | |
43 | ||
44 | static struct regmap_config hi655x_regmap_config = { | |
45 | .reg_bits = 32, | |
46 | .reg_stride = HI655X_STRIDE, | |
47 | .val_bits = 8, | |
6afebb70 | 48 | .max_register = HI655X_BUS_ADDR(0x400) - HI655X_STRIDE, |
b52207ef CF |
49 | }; |
50 | ||
c4a164f4 | 51 | static const struct resource pwrkey_resources[] = { |
eb10245f JS |
52 | { |
53 | .name = "down", | |
54 | .start = PWRON_D20R_INT, | |
55 | .end = PWRON_D20R_INT, | |
56 | .flags = IORESOURCE_IRQ, | |
57 | }, { | |
58 | .name = "up", | |
59 | .start = PWRON_D20F_INT, | |
60 | .end = PWRON_D20F_INT, | |
61 | .flags = IORESOURCE_IRQ, | |
62 | }, { | |
63 | .name = "hold 4s", | |
64 | .start = PWRON_D4SR_INT, | |
65 | .end = PWRON_D4SR_INT, | |
66 | .flags = IORESOURCE_IRQ, | |
67 | }, | |
68 | }; | |
69 | ||
70 | static const struct mfd_cell hi655x_pmic_devs[] = { | |
71 | { | |
72 | .name = "hi65xx-powerkey", | |
73 | .num_resources = ARRAY_SIZE(pwrkey_resources), | |
74 | .resources = &pwrkey_resources[0], | |
75 | }, | |
ac03fec1 DL |
76 | { .name = "hi655x-regulator", }, |
77 | { .name = "hi655x-clk", }, | |
eb10245f JS |
78 | }; |
79 | ||
b52207ef CF |
80 | static void hi655x_local_irq_clear(struct regmap *map) |
81 | { | |
82 | int i; | |
83 | ||
84 | regmap_write(map, HI655X_ANA_IRQM_BASE, HI655X_IRQ_CLR); | |
85 | for (i = 0; i < HI655X_IRQ_ARRAY; i++) { | |
86 | regmap_write(map, HI655X_IRQ_STAT_BASE + i * HI655X_STRIDE, | |
87 | HI655X_IRQ_CLR); | |
88 | } | |
89 | } | |
90 | ||
91 | static int hi655x_pmic_probe(struct platform_device *pdev) | |
92 | { | |
93 | int ret; | |
94 | struct hi655x_pmic *pmic; | |
95 | struct device *dev = &pdev->dev; | |
b52207ef CF |
96 | void __iomem *base; |
97 | ||
98 | pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); | |
99 | if (!pmic) | |
100 | return -ENOMEM; | |
101 | pmic->dev = dev; | |
102 | ||
103 | pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
b52207ef | 104 | base = devm_ioremap_resource(dev, pmic->res); |
7e28abda WY |
105 | if (IS_ERR(base)) |
106 | return PTR_ERR(base); | |
b52207ef CF |
107 | |
108 | pmic->regmap = devm_regmap_init_mmio_clk(dev, NULL, base, | |
109 | &hi655x_regmap_config); | |
7efd105c AL |
110 | if (IS_ERR(pmic->regmap)) |
111 | return PTR_ERR(pmic->regmap); | |
b52207ef CF |
112 | |
113 | regmap_read(pmic->regmap, HI655X_BUS_ADDR(HI655X_VER_REG), &pmic->ver); | |
114 | if ((pmic->ver < PMU_VER_START) || (pmic->ver > PMU_VER_END)) { | |
115 | dev_warn(dev, "PMU version %d unsupported\n", pmic->ver); | |
116 | return -EINVAL; | |
117 | } | |
118 | ||
119 | hi655x_local_irq_clear(pmic->regmap); | |
120 | ||
7f5aaa4a MC |
121 | pmic->gpio = devm_gpiod_get_optional(dev, "pmic", GPIOD_IN); |
122 | if (IS_ERR(pmic->gpio)) | |
123 | return dev_err_probe(dev, PTR_ERR(pmic->gpio), | |
124 | "Failed to request hi655x pmic-gpio"); | |
b52207ef | 125 | |
7f5aaa4a | 126 | ret = regmap_add_irq_chip(pmic->regmap, gpiod_to_irq(pmic->gpio), |
b52207ef CF |
127 | IRQF_TRIGGER_LOW | IRQF_NO_SUSPEND, 0, |
128 | &hi655x_irq_chip, &pmic->irq_data); | |
129 | if (ret) { | |
130 | dev_err(dev, "Failed to obtain 'hi655x_pmic_irq' %d\n", ret); | |
131 | return ret; | |
132 | } | |
133 | ||
134 | platform_set_drvdata(pdev, pmic); | |
135 | ||
136 | ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO, hi655x_pmic_devs, | |
eb10245f JS |
137 | ARRAY_SIZE(hi655x_pmic_devs), NULL, 0, |
138 | regmap_irq_get_domain(pmic->irq_data)); | |
b52207ef CF |
139 | if (ret) { |
140 | dev_err(dev, "Failed to register device %d\n", ret); | |
7f5aaa4a | 141 | regmap_del_irq_chip(gpiod_to_irq(pmic->gpio), pmic->irq_data); |
b52207ef CF |
142 | return ret; |
143 | } | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
148 | static int hi655x_pmic_remove(struct platform_device *pdev) | |
149 | { | |
150 | struct hi655x_pmic *pmic = platform_get_drvdata(pdev); | |
151 | ||
7f5aaa4a | 152 | regmap_del_irq_chip(gpiod_to_irq(pmic->gpio), pmic->irq_data); |
b52207ef CF |
153 | mfd_remove_devices(&pdev->dev); |
154 | return 0; | |
155 | } | |
156 | ||
157 | static const struct of_device_id hi655x_pmic_match[] = { | |
158 | { .compatible = "hisilicon,hi655x-pmic", }, | |
159 | {}, | |
160 | }; | |
e45b6c80 | 161 | MODULE_DEVICE_TABLE(of, hi655x_pmic_match); |
b52207ef CF |
162 | |
163 | static struct platform_driver hi655x_pmic_driver = { | |
164 | .driver = { | |
165 | .name = "hi655x-pmic", | |
166 | .of_match_table = of_match_ptr(hi655x_pmic_match), | |
167 | }, | |
168 | .probe = hi655x_pmic_probe, | |
169 | .remove = hi655x_pmic_remove, | |
170 | }; | |
171 | module_platform_driver(hi655x_pmic_driver); | |
172 | ||
173 | MODULE_AUTHOR("Chen Feng <puck.chen@hisilicon.com>"); | |
174 | MODULE_DESCRIPTION("Hisilicon hi655x PMIC driver"); | |
175 | MODULE_LICENSE("GPL v2"); |