diff options
author | Marek Vasut <marek.vasut@gmail.com> | 2017-07-17 16:45:12 -0400 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2017-09-05 03:46:00 -0400 |
commit | d3ea212720948acff862b4c842d5b464ad338841 (patch) | |
tree | 1cc0b6df8618c6f74995dea3073e540288813b33 /drivers/mfd | |
parent | ec58871fb9c50429d6b5570066a7166da8faf086 (diff) |
mfd: Add ROHM BD9571MWV-M MFD PMIC driver
Add the MFD part of the ROHM BD9571MWV-M PMIC driver and MAINTAINERS
entry. The MFD part only specifies the regmap bits for the PMIC and
binds the subdevs together.
Signed-off-by: Marek Vasut <marek.vasut+renesas@gmail.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/Kconfig | 14 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/bd9571mwv.c | 230 |
3 files changed, 245 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 42fb90ae3345..79e4ec6f19a8 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig | |||
@@ -133,6 +133,20 @@ config MFD_BCM590XX | |||
133 | help | 133 | help |
134 | Support for the BCM590xx PMUs from Broadcom | 134 | Support for the BCM590xx PMUs from Broadcom |
135 | 135 | ||
136 | config MFD_BD9571MWV | ||
137 | tristate "ROHM BD9571MWV PMIC" | ||
138 | select MFD_CORE | ||
139 | select REGMAP_I2C | ||
140 | select REGMAP_IRQ | ||
141 | depends on I2C | ||
142 | help | ||
143 | Support for the ROHM BD9571MWV PMIC, which contains single | ||
144 | voltage regulator, voltage sampling units, GPIO block and | ||
145 | watchdog block. | ||
146 | |||
147 | This driver can also be built as a module. If so, the module | ||
148 | will be called bd9571mwv. | ||
149 | |||
136 | config MFD_AC100 | 150 | config MFD_AC100 |
137 | tristate "X-Powers AC100" | 151 | tristate "X-Powers AC100" |
138 | select MFD_CORE | 152 | select MFD_CORE |
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index b80b1a314ca5..21e19a5f3f3c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile | |||
@@ -10,6 +10,7 @@ obj-$(CONFIG_MFD_ACT8945A) += act8945a.o | |||
10 | obj-$(CONFIG_MFD_SM501) += sm501.o | 10 | obj-$(CONFIG_MFD_SM501) += sm501.o |
11 | obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o | 11 | obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o |
12 | obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o | 12 | obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o |
13 | obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o | ||
13 | cros_ec_core-objs := cros_ec.o | 14 | cros_ec_core-objs := cros_ec.o |
14 | cros_ec_core-$(CONFIG_ACPI) += cros_ec_acpi_gpe.o | 15 | cros_ec_core-$(CONFIG_ACPI) += cros_ec_acpi_gpe.o |
15 | obj-$(CONFIG_MFD_CROS_EC) += cros_ec_core.o | 16 | obj-$(CONFIG_MFD_CROS_EC) += cros_ec_core.o |
diff --git a/drivers/mfd/bd9571mwv.c b/drivers/mfd/bd9571mwv.c new file mode 100644 index 000000000000..64e088dfe7b0 --- /dev/null +++ b/drivers/mfd/bd9571mwv.c | |||
@@ -0,0 +1,230 @@ | |||
1 | /* | ||
2 | * ROHM BD9571MWV-M MFD driver | ||
3 | * | ||
4 | * Copyright (C) 2017 Marek Vasut <marek.vasut+renesas@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
11 | * kind, whether expressed or implied; without even the implied warranty | ||
12 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License version 2 for more details. | ||
14 | * | ||
15 | * Based on the TPS65086 driver | ||
16 | */ | ||
17 | |||
18 | #include <linux/i2c.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/mfd/core.h> | ||
21 | #include <linux/module.h> | ||
22 | |||
23 | #include <linux/mfd/bd9571mwv.h> | ||
24 | |||
25 | static const struct mfd_cell bd9571mwv_cells[] = { | ||
26 | { .name = "bd9571mwv-regulator", }, | ||
27 | { .name = "bd9571mwv-gpio", }, | ||
28 | }; | ||
29 | |||
30 | static const struct regmap_range bd9571mwv_readable_yes_ranges[] = { | ||
31 | regmap_reg_range(BD9571MWV_VENDOR_CODE, BD9571MWV_PRODUCT_REVISION), | ||
32 | regmap_reg_range(BD9571MWV_AVS_SET_MONI, BD9571MWV_AVS_DVFS_VID(3)), | ||
33 | regmap_reg_range(BD9571MWV_VD18_VID, BD9571MWV_VD33_VID), | ||
34 | regmap_reg_range(BD9571MWV_DVFS_VINIT, BD9571MWV_DVFS_VINIT), | ||
35 | regmap_reg_range(BD9571MWV_DVFS_SETVMAX, BD9571MWV_DVFS_MONIVDAC), | ||
36 | regmap_reg_range(BD9571MWV_GPIO_IN, BD9571MWV_GPIO_IN), | ||
37 | regmap_reg_range(BD9571MWV_GPIO_INT, BD9571MWV_GPIO_INTMASK), | ||
38 | regmap_reg_range(BD9571MWV_INT_INTREQ, BD9571MWV_INT_INTMASK), | ||
39 | }; | ||
40 | |||
41 | static const struct regmap_access_table bd9571mwv_readable_table = { | ||
42 | .yes_ranges = bd9571mwv_readable_yes_ranges, | ||
43 | .n_yes_ranges = ARRAY_SIZE(bd9571mwv_readable_yes_ranges), | ||
44 | }; | ||
45 | |||
46 | static const struct regmap_range bd9571mwv_writable_yes_ranges[] = { | ||
47 | regmap_reg_range(BD9571MWV_AVS_VD09_VID(0), BD9571MWV_AVS_VD09_VID(3)), | ||
48 | regmap_reg_range(BD9571MWV_DVFS_SETVID, BD9571MWV_DVFS_SETVID), | ||
49 | regmap_reg_range(BD9571MWV_GPIO_DIR, BD9571MWV_GPIO_OUT), | ||
50 | regmap_reg_range(BD9571MWV_GPIO_INT_SET, BD9571MWV_GPIO_INTMASK), | ||
51 | regmap_reg_range(BD9571MWV_INT_INTREQ, BD9571MWV_INT_INTMASK), | ||
52 | }; | ||
53 | |||
54 | static const struct regmap_access_table bd9571mwv_writable_table = { | ||
55 | .yes_ranges = bd9571mwv_writable_yes_ranges, | ||
56 | .n_yes_ranges = ARRAY_SIZE(bd9571mwv_writable_yes_ranges), | ||
57 | }; | ||
58 | |||
59 | static const struct regmap_range bd9571mwv_volatile_yes_ranges[] = { | ||
60 | regmap_reg_range(BD9571MWV_GPIO_IN, BD9571MWV_GPIO_IN), | ||
61 | regmap_reg_range(BD9571MWV_GPIO_INT, BD9571MWV_GPIO_INT), | ||
62 | regmap_reg_range(BD9571MWV_INT_INTREQ, BD9571MWV_INT_INTREQ), | ||
63 | }; | ||
64 | |||
65 | static const struct regmap_access_table bd9571mwv_volatile_table = { | ||
66 | .yes_ranges = bd9571mwv_volatile_yes_ranges, | ||
67 | .n_yes_ranges = ARRAY_SIZE(bd9571mwv_volatile_yes_ranges), | ||
68 | }; | ||
69 | |||
70 | static const struct regmap_config bd9571mwv_regmap_config = { | ||
71 | .reg_bits = 8, | ||
72 | .val_bits = 8, | ||
73 | .cache_type = REGCACHE_RBTREE, | ||
74 | .rd_table = &bd9571mwv_readable_table, | ||
75 | .wr_table = &bd9571mwv_writable_table, | ||
76 | .volatile_table = &bd9571mwv_volatile_table, | ||
77 | .max_register = 0xff, | ||
78 | }; | ||
79 | |||
80 | static const struct regmap_irq bd9571mwv_irqs[] = { | ||
81 | REGMAP_IRQ_REG(BD9571MWV_IRQ_MD1, 0, | ||
82 | BD9571MWV_INT_INTREQ_MD1_INT), | ||
83 | REGMAP_IRQ_REG(BD9571MWV_IRQ_MD2_E1, 0, | ||
84 | BD9571MWV_INT_INTREQ_MD2_E1_INT), | ||
85 | REGMAP_IRQ_REG(BD9571MWV_IRQ_MD2_E2, 0, | ||
86 | BD9571MWV_INT_INTREQ_MD2_E2_INT), | ||
87 | REGMAP_IRQ_REG(BD9571MWV_IRQ_PROT_ERR, 0, | ||
88 | BD9571MWV_INT_INTREQ_PROT_ERR_INT), | ||
89 | REGMAP_IRQ_REG(BD9571MWV_IRQ_GP, 0, | ||
90 | BD9571MWV_INT_INTREQ_GP_INT), | ||
91 | REGMAP_IRQ_REG(BD9571MWV_IRQ_128H_OF, 0, | ||
92 | BD9571MWV_INT_INTREQ_128H_OF_INT), | ||
93 | REGMAP_IRQ_REG(BD9571MWV_IRQ_WDT_OF, 0, | ||
94 | BD9571MWV_INT_INTREQ_WDT_OF_INT), | ||
95 | REGMAP_IRQ_REG(BD9571MWV_IRQ_BKUP_TRG, 0, | ||
96 | BD9571MWV_INT_INTREQ_BKUP_TRG_INT), | ||
97 | }; | ||
98 | |||
99 | static struct regmap_irq_chip bd9571mwv_irq_chip = { | ||
100 | .name = "bd9571mwv", | ||
101 | .status_base = BD9571MWV_INT_INTREQ, | ||
102 | .mask_base = BD9571MWV_INT_INTMASK, | ||
103 | .ack_base = BD9571MWV_INT_INTREQ, | ||
104 | .init_ack_masked = true, | ||
105 | .num_regs = 1, | ||
106 | .irqs = bd9571mwv_irqs, | ||
107 | .num_irqs = ARRAY_SIZE(bd9571mwv_irqs), | ||
108 | }; | ||
109 | |||
110 | static int bd9571mwv_identify(struct bd9571mwv *bd) | ||
111 | { | ||
112 | struct device *dev = bd->dev; | ||
113 | unsigned int value; | ||
114 | int ret; | ||
115 | |||
116 | ret = regmap_read(bd->regmap, BD9571MWV_VENDOR_CODE, &value); | ||
117 | if (ret) { | ||
118 | dev_err(dev, "Failed to read vendor code register (ret=%i)\n", | ||
119 | ret); | ||
120 | return ret; | ||
121 | } | ||
122 | |||
123 | if (value != BD9571MWV_VENDOR_CODE_VAL) { | ||
124 | dev_err(dev, "Invalid vendor code ID %02x (expected %02x)\n", | ||
125 | value, BD9571MWV_VENDOR_CODE_VAL); | ||
126 | return -EINVAL; | ||
127 | } | ||
128 | |||
129 | ret = regmap_read(bd->regmap, BD9571MWV_PRODUCT_CODE, &value); | ||
130 | if (ret) { | ||
131 | dev_err(dev, "Failed to read product code register (ret=%i)\n", | ||
132 | ret); | ||
133 | return ret; | ||
134 | } | ||
135 | |||
136 | if (value != BD9571MWV_PRODUCT_CODE_VAL) { | ||
137 | dev_err(dev, "Invalid product code ID %02x (expected %02x)\n", | ||
138 | value, BD9571MWV_PRODUCT_CODE_VAL); | ||
139 | return -EINVAL; | ||
140 | } | ||
141 | |||
142 | ret = regmap_read(bd->regmap, BD9571MWV_PRODUCT_REVISION, &value); | ||
143 | if (ret) { | ||
144 | dev_err(dev, "Failed to read revision register (ret=%i)\n", | ||
145 | ret); | ||
146 | return ret; | ||
147 | } | ||
148 | |||
149 | dev_info(dev, "Device: BD9571MWV rev. %d\n", value & 0xff); | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static int bd9571mwv_probe(struct i2c_client *client, | ||
155 | const struct i2c_device_id *ids) | ||
156 | { | ||
157 | struct bd9571mwv *bd; | ||
158 | int ret; | ||
159 | |||
160 | bd = devm_kzalloc(&client->dev, sizeof(*bd), GFP_KERNEL); | ||
161 | if (!bd) | ||
162 | return -ENOMEM; | ||
163 | |||
164 | i2c_set_clientdata(client, bd); | ||
165 | bd->dev = &client->dev; | ||
166 | bd->irq = client->irq; | ||
167 | |||
168 | bd->regmap = devm_regmap_init_i2c(client, &bd9571mwv_regmap_config); | ||
169 | if (IS_ERR(bd->regmap)) { | ||
170 | dev_err(bd->dev, "Failed to initialize register map\n"); | ||
171 | return PTR_ERR(bd->regmap); | ||
172 | } | ||
173 | |||
174 | ret = bd9571mwv_identify(bd); | ||
175 | if (ret) | ||
176 | return ret; | ||
177 | |||
178 | ret = regmap_add_irq_chip(bd->regmap, bd->irq, IRQF_ONESHOT, 0, | ||
179 | &bd9571mwv_irq_chip, &bd->irq_data); | ||
180 | if (ret) { | ||
181 | dev_err(bd->dev, "Failed to register IRQ chip\n"); | ||
182 | return ret; | ||
183 | } | ||
184 | |||
185 | ret = mfd_add_devices(bd->dev, PLATFORM_DEVID_AUTO, bd9571mwv_cells, | ||
186 | ARRAY_SIZE(bd9571mwv_cells), NULL, 0, | ||
187 | regmap_irq_get_domain(bd->irq_data)); | ||
188 | if (ret) { | ||
189 | regmap_del_irq_chip(bd->irq, bd->irq_data); | ||
190 | return ret; | ||
191 | } | ||
192 | |||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static int bd9571mwv_remove(struct i2c_client *client) | ||
197 | { | ||
198 | struct bd9571mwv *bd = i2c_get_clientdata(client); | ||
199 | |||
200 | regmap_del_irq_chip(bd->irq, bd->irq_data); | ||
201 | |||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static const struct of_device_id bd9571mwv_of_match_table[] = { | ||
206 | { .compatible = "rohm,bd9571mwv", }, | ||
207 | { /* sentinel */ } | ||
208 | }; | ||
209 | MODULE_DEVICE_TABLE(of, bd9571mwv_of_match_table); | ||
210 | |||
211 | static const struct i2c_device_id bd9571mwv_id_table[] = { | ||
212 | { "bd9571mwv", 0 }, | ||
213 | { /* sentinel */ } | ||
214 | }; | ||
215 | MODULE_DEVICE_TABLE(i2c, bd9571mwv_id_table); | ||
216 | |||
217 | static struct i2c_driver bd9571mwv_driver = { | ||
218 | .driver = { | ||
219 | .name = "bd9571mwv", | ||
220 | .of_match_table = bd9571mwv_of_match_table, | ||
221 | }, | ||
222 | .probe = bd9571mwv_probe, | ||
223 | .remove = bd9571mwv_remove, | ||
224 | .id_table = bd9571mwv_id_table, | ||
225 | }; | ||
226 | module_i2c_driver(bd9571mwv_driver); | ||
227 | |||
228 | MODULE_AUTHOR("Marek Vasut <marek.vasut+renesas@gmail.com>"); | ||
229 | MODULE_DESCRIPTION("BD9571MWV PMIC Driver"); | ||
230 | MODULE_LICENSE("GPL v2"); | ||