Commit a26033a1f56b7b1f8a56050c0a9095694aecae11
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
Merge branch 'ib-mfd-iio-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/…
…lee/mfd into acpi-pmic Pull MFD changes that the ACPI PMIC changes depend on from Lee Jones.
Showing 6 changed files Side-by-side Diff
drivers/iio/adc/Kconfig
... | ... | @@ -127,6 +127,14 @@ |
127 | 127 | help |
128 | 128 | Say yes here to build support for Atmel AT91 ADC. |
129 | 129 | |
130 | +config AXP288_ADC | |
131 | + tristate "X-Powers AXP288 ADC driver" | |
132 | + depends on MFD_AXP20X | |
133 | + help | |
134 | + Say yes here to have support for X-Powers power management IC (PMIC) ADC | |
135 | + device. Depending on platform configuration, this general purpose ADC can | |
136 | + be used for sampling sensors such as thermal resistors. | |
137 | + | |
130 | 138 | config EXYNOS_ADC |
131 | 139 | tristate "Exynos ADC driver support" |
132 | 140 | depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST) |
drivers/iio/adc/Makefile
... | ... | @@ -14,6 +14,7 @@ |
14 | 14 | obj-$(CONFIG_AD7887) += ad7887.o |
15 | 15 | obj-$(CONFIG_AD799X) += ad799x.o |
16 | 16 | obj-$(CONFIG_AT91_ADC) += at91_adc.o |
17 | +obj-$(CONFIG_AXP288_ADC) += axp288_adc.o | |
17 | 18 | obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o |
18 | 19 | obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o |
19 | 20 | obj-$(CONFIG_MAX1027) += max1027.o |
drivers/iio/adc/axp288_adc.c
1 | +/* | |
2 | + * axp288_adc.c - X-Powers AXP288 PMIC ADC Driver | |
3 | + * | |
4 | + * Copyright (C) 2014 Intel Corporation | |
5 | + * | |
6 | + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License as published by | |
10 | + * the Free Software Foundation; version 2 of the License. | |
11 | + * | |
12 | + * This program is distributed in the hope that it will be useful, but | |
13 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | + * General Public License for more details. | |
16 | + * | |
17 | + */ | |
18 | + | |
19 | +#include <linux/module.h> | |
20 | +#include <linux/kernel.h> | |
21 | +#include <linux/device.h> | |
22 | +#include <linux/regmap.h> | |
23 | +#include <linux/mfd/axp20x.h> | |
24 | +#include <linux/platform_device.h> | |
25 | + | |
26 | +#include <linux/iio/iio.h> | |
27 | +#include <linux/iio/machine.h> | |
28 | +#include <linux/iio/driver.h> | |
29 | + | |
30 | +#define AXP288_ADC_EN_MASK 0xF1 | |
31 | +#define AXP288_ADC_TS_PIN_GPADC 0xF2 | |
32 | +#define AXP288_ADC_TS_PIN_ON 0xF3 | |
33 | + | |
34 | +enum axp288_adc_id { | |
35 | + AXP288_ADC_TS, | |
36 | + AXP288_ADC_PMIC, | |
37 | + AXP288_ADC_GP, | |
38 | + AXP288_ADC_BATT_CHRG_I, | |
39 | + AXP288_ADC_BATT_DISCHRG_I, | |
40 | + AXP288_ADC_BATT_V, | |
41 | + AXP288_ADC_NR_CHAN, | |
42 | +}; | |
43 | + | |
44 | +struct axp288_adc_info { | |
45 | + int irq; | |
46 | + struct regmap *regmap; | |
47 | +}; | |
48 | + | |
49 | +static const struct iio_chan_spec const axp288_adc_channels[] = { | |
50 | + { | |
51 | + .indexed = 1, | |
52 | + .type = IIO_TEMP, | |
53 | + .channel = 0, | |
54 | + .address = AXP288_TS_ADC_H, | |
55 | + .datasheet_name = "TS_PIN", | |
56 | + }, { | |
57 | + .indexed = 1, | |
58 | + .type = IIO_TEMP, | |
59 | + .channel = 1, | |
60 | + .address = AXP288_PMIC_ADC_H, | |
61 | + .datasheet_name = "PMIC_TEMP", | |
62 | + }, { | |
63 | + .indexed = 1, | |
64 | + .type = IIO_TEMP, | |
65 | + .channel = 2, | |
66 | + .address = AXP288_GP_ADC_H, | |
67 | + .datasheet_name = "GPADC", | |
68 | + }, { | |
69 | + .indexed = 1, | |
70 | + .type = IIO_CURRENT, | |
71 | + .channel = 3, | |
72 | + .address = AXP20X_BATT_CHRG_I_H, | |
73 | + .datasheet_name = "BATT_CHG_I", | |
74 | + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), | |
75 | + }, { | |
76 | + .indexed = 1, | |
77 | + .type = IIO_CURRENT, | |
78 | + .channel = 4, | |
79 | + .address = AXP20X_BATT_DISCHRG_I_H, | |
80 | + .datasheet_name = "BATT_DISCHRG_I", | |
81 | + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), | |
82 | + }, { | |
83 | + .indexed = 1, | |
84 | + .type = IIO_VOLTAGE, | |
85 | + .channel = 5, | |
86 | + .address = AXP20X_BATT_V_H, | |
87 | + .datasheet_name = "BATT_V", | |
88 | + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), | |
89 | + }, | |
90 | +}; | |
91 | + | |
92 | +#define AXP288_ADC_MAP(_adc_channel_label, _consumer_dev_name, \ | |
93 | + _consumer_channel) \ | |
94 | + { \ | |
95 | + .adc_channel_label = _adc_channel_label, \ | |
96 | + .consumer_dev_name = _consumer_dev_name, \ | |
97 | + .consumer_channel = _consumer_channel, \ | |
98 | + } | |
99 | + | |
100 | +/* for consumer drivers */ | |
101 | +static struct iio_map axp288_adc_default_maps[] = { | |
102 | + AXP288_ADC_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"), | |
103 | + AXP288_ADC_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"), | |
104 | + AXP288_ADC_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"), | |
105 | + AXP288_ADC_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"), | |
106 | + AXP288_ADC_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"), | |
107 | + AXP288_ADC_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"), | |
108 | + {}, | |
109 | +}; | |
110 | + | |
111 | +static int axp288_adc_read_channel(int *val, unsigned long address, | |
112 | + struct regmap *regmap) | |
113 | +{ | |
114 | + u8 buf[2]; | |
115 | + | |
116 | + if (regmap_bulk_read(regmap, address, buf, 2)) | |
117 | + return -EIO; | |
118 | + *val = (buf[0] << 4) + ((buf[1] >> 4) & 0x0F); | |
119 | + | |
120 | + return IIO_VAL_INT; | |
121 | +} | |
122 | + | |
123 | +static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode, | |
124 | + unsigned long address) | |
125 | +{ | |
126 | + /* channels other than GPADC do not need to switch TS pin */ | |
127 | + if (address != AXP288_GP_ADC_H) | |
128 | + return 0; | |
129 | + | |
130 | + return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode); | |
131 | +} | |
132 | + | |
133 | +static int axp288_adc_read_raw(struct iio_dev *indio_dev, | |
134 | + struct iio_chan_spec const *chan, | |
135 | + int *val, int *val2, long mask) | |
136 | +{ | |
137 | + int ret; | |
138 | + struct axp288_adc_info *info = iio_priv(indio_dev); | |
139 | + | |
140 | + mutex_lock(&indio_dev->mlock); | |
141 | + switch (mask) { | |
142 | + case IIO_CHAN_INFO_RAW: | |
143 | + if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC, | |
144 | + chan->address)) { | |
145 | + dev_err(&indio_dev->dev, "GPADC mode\n"); | |
146 | + ret = -EINVAL; | |
147 | + break; | |
148 | + } | |
149 | + ret = axp288_adc_read_channel(val, chan->address, info->regmap); | |
150 | + if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON, | |
151 | + chan->address)) | |
152 | + dev_err(&indio_dev->dev, "TS pin restore\n"); | |
153 | + break; | |
154 | + case IIO_CHAN_INFO_PROCESSED: | |
155 | + ret = axp288_adc_read_channel(val, chan->address, info->regmap); | |
156 | + break; | |
157 | + default: | |
158 | + ret = -EINVAL; | |
159 | + } | |
160 | + mutex_unlock(&indio_dev->mlock); | |
161 | + | |
162 | + return ret; | |
163 | +} | |
164 | + | |
165 | +static int axp288_adc_set_state(struct regmap *regmap) | |
166 | +{ | |
167 | + /* ADC should be always enabled for internal FG to function */ | |
168 | + if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON)) | |
169 | + return -EIO; | |
170 | + | |
171 | + return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK); | |
172 | +} | |
173 | + | |
174 | +static const struct iio_info axp288_adc_iio_info = { | |
175 | + .read_raw = &axp288_adc_read_raw, | |
176 | + .driver_module = THIS_MODULE, | |
177 | +}; | |
178 | + | |
179 | +static int axp288_adc_probe(struct platform_device *pdev) | |
180 | +{ | |
181 | + int ret; | |
182 | + struct axp288_adc_info *info; | |
183 | + struct iio_dev *indio_dev; | |
184 | + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); | |
185 | + | |
186 | + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); | |
187 | + if (!indio_dev) | |
188 | + return -ENOMEM; | |
189 | + | |
190 | + info = iio_priv(indio_dev); | |
191 | + info->irq = platform_get_irq(pdev, 0); | |
192 | + if (info->irq < 0) { | |
193 | + dev_err(&pdev->dev, "no irq resource?\n"); | |
194 | + return info->irq; | |
195 | + } | |
196 | + platform_set_drvdata(pdev, indio_dev); | |
197 | + info->regmap = axp20x->regmap; | |
198 | + /* | |
199 | + * Set ADC to enabled state at all time, including system suspend. | |
200 | + * otherwise internal fuel gauge functionality may be affected. | |
201 | + */ | |
202 | + ret = axp288_adc_set_state(axp20x->regmap); | |
203 | + if (ret) { | |
204 | + dev_err(&pdev->dev, "unable to enable ADC device\n"); | |
205 | + return ret; | |
206 | + } | |
207 | + | |
208 | + indio_dev->dev.parent = &pdev->dev; | |
209 | + indio_dev->name = pdev->name; | |
210 | + indio_dev->channels = axp288_adc_channels; | |
211 | + indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels); | |
212 | + indio_dev->info = &axp288_adc_iio_info; | |
213 | + indio_dev->modes = INDIO_DIRECT_MODE; | |
214 | + ret = iio_map_array_register(indio_dev, axp288_adc_default_maps); | |
215 | + if (ret < 0) | |
216 | + return ret; | |
217 | + | |
218 | + ret = iio_device_register(indio_dev); | |
219 | + if (ret < 0) { | |
220 | + dev_err(&pdev->dev, "unable to register iio device\n"); | |
221 | + goto err_array_unregister; | |
222 | + } | |
223 | + return 0; | |
224 | + | |
225 | +err_array_unregister: | |
226 | + iio_map_array_unregister(indio_dev); | |
227 | + | |
228 | + return ret; | |
229 | +} | |
230 | + | |
231 | +static int axp288_adc_remove(struct platform_device *pdev) | |
232 | +{ | |
233 | + struct iio_dev *indio_dev = platform_get_drvdata(pdev); | |
234 | + | |
235 | + iio_device_unregister(indio_dev); | |
236 | + iio_map_array_unregister(indio_dev); | |
237 | + | |
238 | + return 0; | |
239 | +} | |
240 | + | |
241 | +static struct platform_device_id axp288_adc_id_table[] = { | |
242 | + { .name = "axp288_adc" }, | |
243 | + {}, | |
244 | +}; | |
245 | + | |
246 | +static struct platform_driver axp288_adc_driver = { | |
247 | + .probe = axp288_adc_probe, | |
248 | + .remove = axp288_adc_remove, | |
249 | + .id_table = axp288_adc_id_table, | |
250 | + .driver = { | |
251 | + .name = "axp288_adc", | |
252 | + }, | |
253 | +}; | |
254 | + | |
255 | +MODULE_DEVICE_TABLE(platform, axp288_adc_id_table); | |
256 | + | |
257 | +module_platform_driver(axp288_adc_driver); | |
258 | + | |
259 | +MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>"); | |
260 | +MODULE_DESCRIPTION("X-Powers AXP288 ADC Driver"); | |
261 | +MODULE_LICENSE("GPL"); |
drivers/mfd/Kconfig
... | ... | @@ -74,7 +74,8 @@ |
74 | 74 | select REGMAP_IRQ |
75 | 75 | depends on I2C=y |
76 | 76 | help |
77 | - If you say Y here you get support for the X-Powers AXP202 and AXP209. | |
77 | + If you say Y here you get support for the X-Powers AXP202, AXP209 and | |
78 | + AXP288 power management IC (PMIC). | |
78 | 79 | This driver include only the core APIs. You have to select individual |
79 | 80 | components like regulators or the PEK (Power Enable Key) under the |
80 | 81 | corresponding menus. |
drivers/mfd/axp20x.c
1 | 1 | /* |
2 | - * axp20x.c - MFD core driver for the X-Powers AXP202 and AXP209 | |
2 | + * axp20x.c - MFD core driver for the X-Powers' Power Management ICs | |
3 | 3 | * |
4 | - * AXP20x comprises an adaptive USB-Compatible PWM charger, 2 BUCK DC-DC | |
5 | - * converters, 5 LDOs, multiple 12-bit ADCs of voltage, current and temperature | |
6 | - * as well as 4 configurable GPIOs. | |
4 | + * AXP20x typically comprises an adaptive USB-Compatible PWM charger, BUCK DC-DC | |
5 | + * converters, LDOs, multiple 12-bit ADCs of voltage, current and temperature | |
6 | + * as well as configurable GPIOs. | |
7 | 7 | * |
8 | 8 | * Author: Carlo Caione <carlo@caione.org> |
9 | 9 | * |
10 | 10 | |
... | ... | @@ -25,9 +25,16 @@ |
25 | 25 | #include <linux/mfd/core.h> |
26 | 26 | #include <linux/of_device.h> |
27 | 27 | #include <linux/of_irq.h> |
28 | +#include <linux/acpi.h> | |
28 | 29 | |
29 | 30 | #define AXP20X_OFF 0x80 |
30 | 31 | |
32 | +static const char const *axp20x_model_names[] = { | |
33 | + "AXP202", | |
34 | + "AXP209", | |
35 | + "AXP288", | |
36 | +}; | |
37 | + | |
31 | 38 | static const struct regmap_range axp20x_writeable_ranges[] = { |
32 | 39 | regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE), |
33 | 40 | regmap_reg_range(AXP20X_DCDC_MODE, AXP20X_FG_RES), |
... | ... | @@ -47,6 +54,25 @@ |
47 | 54 | .n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges), |
48 | 55 | }; |
49 | 56 | |
57 | +static const struct regmap_range axp288_writeable_ranges[] = { | |
58 | + regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ6_STATE), | |
59 | + regmap_reg_range(AXP20X_DCDC_MODE, AXP288_FG_TUNE5), | |
60 | +}; | |
61 | + | |
62 | +static const struct regmap_range axp288_volatile_ranges[] = { | |
63 | + regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IPSOUT_V_HIGH_L), | |
64 | +}; | |
65 | + | |
66 | +static const struct regmap_access_table axp288_writeable_table = { | |
67 | + .yes_ranges = axp288_writeable_ranges, | |
68 | + .n_yes_ranges = ARRAY_SIZE(axp288_writeable_ranges), | |
69 | +}; | |
70 | + | |
71 | +static const struct regmap_access_table axp288_volatile_table = { | |
72 | + .yes_ranges = axp288_volatile_ranges, | |
73 | + .n_yes_ranges = ARRAY_SIZE(axp288_volatile_ranges), | |
74 | +}; | |
75 | + | |
50 | 76 | static struct resource axp20x_pek_resources[] = { |
51 | 77 | { |
52 | 78 | .name = "PEK_DBR", |
... | ... | @@ -61,6 +87,39 @@ |
61 | 87 | }, |
62 | 88 | }; |
63 | 89 | |
90 | +static struct resource axp288_battery_resources[] = { | |
91 | + { | |
92 | + .start = AXP288_IRQ_QWBTU, | |
93 | + .end = AXP288_IRQ_QWBTU, | |
94 | + .flags = IORESOURCE_IRQ, | |
95 | + }, | |
96 | + { | |
97 | + .start = AXP288_IRQ_WBTU, | |
98 | + .end = AXP288_IRQ_WBTU, | |
99 | + .flags = IORESOURCE_IRQ, | |
100 | + }, | |
101 | + { | |
102 | + .start = AXP288_IRQ_QWBTO, | |
103 | + .end = AXP288_IRQ_QWBTO, | |
104 | + .flags = IORESOURCE_IRQ, | |
105 | + }, | |
106 | + { | |
107 | + .start = AXP288_IRQ_WBTO, | |
108 | + .end = AXP288_IRQ_WBTO, | |
109 | + .flags = IORESOURCE_IRQ, | |
110 | + }, | |
111 | + { | |
112 | + .start = AXP288_IRQ_WL2, | |
113 | + .end = AXP288_IRQ_WL2, | |
114 | + .flags = IORESOURCE_IRQ, | |
115 | + }, | |
116 | + { | |
117 | + .start = AXP288_IRQ_WL1, | |
118 | + .end = AXP288_IRQ_WL1, | |
119 | + .flags = IORESOURCE_IRQ, | |
120 | + }, | |
121 | +}; | |
122 | + | |
64 | 123 | static const struct regmap_config axp20x_regmap_config = { |
65 | 124 | .reg_bits = 8, |
66 | 125 | .val_bits = 8, |
67 | 126 | |
68 | 127 | |
69 | 128 | |
... | ... | @@ -70,49 +129,98 @@ |
70 | 129 | .cache_type = REGCACHE_RBTREE, |
71 | 130 | }; |
72 | 131 | |
73 | -#define AXP20X_IRQ(_irq, _off, _mask) \ | |
74 | - [AXP20X_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) } | |
132 | +static const struct regmap_config axp288_regmap_config = { | |
133 | + .reg_bits = 8, | |
134 | + .val_bits = 8, | |
135 | + .wr_table = &axp288_writeable_table, | |
136 | + .volatile_table = &axp288_volatile_table, | |
137 | + .max_register = AXP288_FG_TUNE5, | |
138 | + .cache_type = REGCACHE_RBTREE, | |
139 | +}; | |
75 | 140 | |
141 | +#define INIT_REGMAP_IRQ(_variant, _irq, _off, _mask) \ | |
142 | + [_variant##_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) } | |
143 | + | |
76 | 144 | static const struct regmap_irq axp20x_regmap_irqs[] = { |
77 | - AXP20X_IRQ(ACIN_OVER_V, 0, 7), | |
78 | - AXP20X_IRQ(ACIN_PLUGIN, 0, 6), | |
79 | - AXP20X_IRQ(ACIN_REMOVAL, 0, 5), | |
80 | - AXP20X_IRQ(VBUS_OVER_V, 0, 4), | |
81 | - AXP20X_IRQ(VBUS_PLUGIN, 0, 3), | |
82 | - AXP20X_IRQ(VBUS_REMOVAL, 0, 2), | |
83 | - AXP20X_IRQ(VBUS_V_LOW, 0, 1), | |
84 | - AXP20X_IRQ(BATT_PLUGIN, 1, 7), | |
85 | - AXP20X_IRQ(BATT_REMOVAL, 1, 6), | |
86 | - AXP20X_IRQ(BATT_ENT_ACT_MODE, 1, 5), | |
87 | - AXP20X_IRQ(BATT_EXIT_ACT_MODE, 1, 4), | |
88 | - AXP20X_IRQ(CHARG, 1, 3), | |
89 | - AXP20X_IRQ(CHARG_DONE, 1, 2), | |
90 | - AXP20X_IRQ(BATT_TEMP_HIGH, 1, 1), | |
91 | - AXP20X_IRQ(BATT_TEMP_LOW, 1, 0), | |
92 | - AXP20X_IRQ(DIE_TEMP_HIGH, 2, 7), | |
93 | - AXP20X_IRQ(CHARG_I_LOW, 2, 6), | |
94 | - AXP20X_IRQ(DCDC1_V_LONG, 2, 5), | |
95 | - AXP20X_IRQ(DCDC2_V_LONG, 2, 4), | |
96 | - AXP20X_IRQ(DCDC3_V_LONG, 2, 3), | |
97 | - AXP20X_IRQ(PEK_SHORT, 2, 1), | |
98 | - AXP20X_IRQ(PEK_LONG, 2, 0), | |
99 | - AXP20X_IRQ(N_OE_PWR_ON, 3, 7), | |
100 | - AXP20X_IRQ(N_OE_PWR_OFF, 3, 6), | |
101 | - AXP20X_IRQ(VBUS_VALID, 3, 5), | |
102 | - AXP20X_IRQ(VBUS_NOT_VALID, 3, 4), | |
103 | - AXP20X_IRQ(VBUS_SESS_VALID, 3, 3), | |
104 | - AXP20X_IRQ(VBUS_SESS_END, 3, 2), | |
105 | - AXP20X_IRQ(LOW_PWR_LVL1, 3, 1), | |
106 | - AXP20X_IRQ(LOW_PWR_LVL2, 3, 0), | |
107 | - AXP20X_IRQ(TIMER, 4, 7), | |
108 | - AXP20X_IRQ(PEK_RIS_EDGE, 4, 6), | |
109 | - AXP20X_IRQ(PEK_FAL_EDGE, 4, 5), | |
110 | - AXP20X_IRQ(GPIO3_INPUT, 4, 3), | |
111 | - AXP20X_IRQ(GPIO2_INPUT, 4, 2), | |
112 | - AXP20X_IRQ(GPIO1_INPUT, 4, 1), | |
113 | - AXP20X_IRQ(GPIO0_INPUT, 4, 0), | |
145 | + INIT_REGMAP_IRQ(AXP20X, ACIN_OVER_V, 0, 7), | |
146 | + INIT_REGMAP_IRQ(AXP20X, ACIN_PLUGIN, 0, 6), | |
147 | + INIT_REGMAP_IRQ(AXP20X, ACIN_REMOVAL, 0, 5), | |
148 | + INIT_REGMAP_IRQ(AXP20X, VBUS_OVER_V, 0, 4), | |
149 | + INIT_REGMAP_IRQ(AXP20X, VBUS_PLUGIN, 0, 3), | |
150 | + INIT_REGMAP_IRQ(AXP20X, VBUS_REMOVAL, 0, 2), | |
151 | + INIT_REGMAP_IRQ(AXP20X, VBUS_V_LOW, 0, 1), | |
152 | + INIT_REGMAP_IRQ(AXP20X, BATT_PLUGIN, 1, 7), | |
153 | + INIT_REGMAP_IRQ(AXP20X, BATT_REMOVAL, 1, 6), | |
154 | + INIT_REGMAP_IRQ(AXP20X, BATT_ENT_ACT_MODE, 1, 5), | |
155 | + INIT_REGMAP_IRQ(AXP20X, BATT_EXIT_ACT_MODE, 1, 4), | |
156 | + INIT_REGMAP_IRQ(AXP20X, CHARG, 1, 3), | |
157 | + INIT_REGMAP_IRQ(AXP20X, CHARG_DONE, 1, 2), | |
158 | + INIT_REGMAP_IRQ(AXP20X, BATT_TEMP_HIGH, 1, 1), | |
159 | + INIT_REGMAP_IRQ(AXP20X, BATT_TEMP_LOW, 1, 0), | |
160 | + INIT_REGMAP_IRQ(AXP20X, DIE_TEMP_HIGH, 2, 7), | |
161 | + INIT_REGMAP_IRQ(AXP20X, CHARG_I_LOW, 2, 6), | |
162 | + INIT_REGMAP_IRQ(AXP20X, DCDC1_V_LONG, 2, 5), | |
163 | + INIT_REGMAP_IRQ(AXP20X, DCDC2_V_LONG, 2, 4), | |
164 | + INIT_REGMAP_IRQ(AXP20X, DCDC3_V_LONG, 2, 3), | |
165 | + INIT_REGMAP_IRQ(AXP20X, PEK_SHORT, 2, 1), | |
166 | + INIT_REGMAP_IRQ(AXP20X, PEK_LONG, 2, 0), | |
167 | + INIT_REGMAP_IRQ(AXP20X, N_OE_PWR_ON, 3, 7), | |
168 | + INIT_REGMAP_IRQ(AXP20X, N_OE_PWR_OFF, 3, 6), | |
169 | + INIT_REGMAP_IRQ(AXP20X, VBUS_VALID, 3, 5), | |
170 | + INIT_REGMAP_IRQ(AXP20X, VBUS_NOT_VALID, 3, 4), | |
171 | + INIT_REGMAP_IRQ(AXP20X, VBUS_SESS_VALID, 3, 3), | |
172 | + INIT_REGMAP_IRQ(AXP20X, VBUS_SESS_END, 3, 2), | |
173 | + INIT_REGMAP_IRQ(AXP20X, LOW_PWR_LVL1, 3, 1), | |
174 | + INIT_REGMAP_IRQ(AXP20X, LOW_PWR_LVL2, 3, 0), | |
175 | + INIT_REGMAP_IRQ(AXP20X, TIMER, 4, 7), | |
176 | + INIT_REGMAP_IRQ(AXP20X, PEK_RIS_EDGE, 4, 6), | |
177 | + INIT_REGMAP_IRQ(AXP20X, PEK_FAL_EDGE, 4, 5), | |
178 | + INIT_REGMAP_IRQ(AXP20X, GPIO3_INPUT, 4, 3), | |
179 | + INIT_REGMAP_IRQ(AXP20X, GPIO2_INPUT, 4, 2), | |
180 | + INIT_REGMAP_IRQ(AXP20X, GPIO1_INPUT, 4, 1), | |
181 | + INIT_REGMAP_IRQ(AXP20X, GPIO0_INPUT, 4, 0), | |
114 | 182 | }; |
115 | 183 | |
184 | +/* some IRQs are compatible with axp20x models */ | |
185 | +static const struct regmap_irq axp288_regmap_irqs[] = { | |
186 | + INIT_REGMAP_IRQ(AXP288, VBUS_FALL, 0, 2), | |
187 | + INIT_REGMAP_IRQ(AXP288, VBUS_RISE, 0, 3), | |
188 | + INIT_REGMAP_IRQ(AXP288, OV, 0, 4), | |
189 | + | |
190 | + INIT_REGMAP_IRQ(AXP288, DONE, 1, 2), | |
191 | + INIT_REGMAP_IRQ(AXP288, CHARGING, 1, 3), | |
192 | + INIT_REGMAP_IRQ(AXP288, SAFE_QUIT, 1, 4), | |
193 | + INIT_REGMAP_IRQ(AXP288, SAFE_ENTER, 1, 5), | |
194 | + INIT_REGMAP_IRQ(AXP288, ABSENT, 1, 6), | |
195 | + INIT_REGMAP_IRQ(AXP288, APPEND, 1, 7), | |
196 | + | |
197 | + INIT_REGMAP_IRQ(AXP288, QWBTU, 2, 0), | |
198 | + INIT_REGMAP_IRQ(AXP288, WBTU, 2, 1), | |
199 | + INIT_REGMAP_IRQ(AXP288, QWBTO, 2, 2), | |
200 | + INIT_REGMAP_IRQ(AXP288, WBTO, 2, 3), | |
201 | + INIT_REGMAP_IRQ(AXP288, QCBTU, 2, 4), | |
202 | + INIT_REGMAP_IRQ(AXP288, CBTU, 2, 5), | |
203 | + INIT_REGMAP_IRQ(AXP288, QCBTO, 2, 6), | |
204 | + INIT_REGMAP_IRQ(AXP288, CBTO, 2, 7), | |
205 | + | |
206 | + INIT_REGMAP_IRQ(AXP288, WL2, 3, 0), | |
207 | + INIT_REGMAP_IRQ(AXP288, WL1, 3, 1), | |
208 | + INIT_REGMAP_IRQ(AXP288, GPADC, 3, 2), | |
209 | + INIT_REGMAP_IRQ(AXP288, OT, 3, 7), | |
210 | + | |
211 | + INIT_REGMAP_IRQ(AXP288, GPIO0, 4, 0), | |
212 | + INIT_REGMAP_IRQ(AXP288, GPIO1, 4, 1), | |
213 | + INIT_REGMAP_IRQ(AXP288, POKO, 4, 2), | |
214 | + INIT_REGMAP_IRQ(AXP288, POKL, 4, 3), | |
215 | + INIT_REGMAP_IRQ(AXP288, POKS, 4, 4), | |
216 | + INIT_REGMAP_IRQ(AXP288, POKN, 4, 5), | |
217 | + INIT_REGMAP_IRQ(AXP288, POKP, 4, 6), | |
218 | + INIT_REGMAP_IRQ(AXP288, TIMER, 4, 7), | |
219 | + | |
220 | + INIT_REGMAP_IRQ(AXP288, MV_CHNG, 5, 0), | |
221 | + INIT_REGMAP_IRQ(AXP288, BC_USB_CHNG, 5, 1), | |
222 | +}; | |
223 | + | |
116 | 224 | static const struct of_device_id axp20x_of_match[] = { |
117 | 225 | { .compatible = "x-powers,axp202", .data = (void *) AXP202_ID }, |
118 | 226 | { .compatible = "x-powers,axp209", .data = (void *) AXP209_ID }, |
119 | 227 | |
120 | 228 | |
121 | 229 | |
... | ... | @@ -128,16 +236,39 @@ |
128 | 236 | }; |
129 | 237 | MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id); |
130 | 238 | |
239 | +static struct acpi_device_id axp20x_acpi_match[] = { | |
240 | + { | |
241 | + .id = "INT33F4", | |
242 | + .driver_data = AXP288_ID, | |
243 | + }, | |
244 | + { }, | |
245 | +}; | |
246 | +MODULE_DEVICE_TABLE(acpi, axp20x_acpi_match); | |
247 | + | |
131 | 248 | static const struct regmap_irq_chip axp20x_regmap_irq_chip = { |
132 | 249 | .name = "axp20x_irq_chip", |
133 | 250 | .status_base = AXP20X_IRQ1_STATE, |
134 | 251 | .ack_base = AXP20X_IRQ1_STATE, |
135 | 252 | .mask_base = AXP20X_IRQ1_EN, |
136 | - .num_regs = 5, | |
253 | + .mask_invert = true, | |
254 | + .init_ack_masked = true, | |
137 | 255 | .irqs = axp20x_regmap_irqs, |
138 | 256 | .num_irqs = ARRAY_SIZE(axp20x_regmap_irqs), |
257 | + .num_regs = 5, | |
258 | + | |
259 | +}; | |
260 | + | |
261 | +static const struct regmap_irq_chip axp288_regmap_irq_chip = { | |
262 | + .name = "axp288_irq_chip", | |
263 | + .status_base = AXP20X_IRQ1_STATE, | |
264 | + .ack_base = AXP20X_IRQ1_STATE, | |
265 | + .mask_base = AXP20X_IRQ1_EN, | |
139 | 266 | .mask_invert = true, |
140 | 267 | .init_ack_masked = true, |
268 | + .irqs = axp288_regmap_irqs, | |
269 | + .num_irqs = ARRAY_SIZE(axp288_regmap_irqs), | |
270 | + .num_regs = 6, | |
271 | + | |
141 | 272 | }; |
142 | 273 | |
143 | 274 | static struct mfd_cell axp20x_cells[] = { |
144 | 275 | |
145 | 276 | |
146 | 277 | |
147 | 278 | |
148 | 279 | |
... | ... | @@ -150,36 +281,155 @@ |
150 | 281 | }, |
151 | 282 | }; |
152 | 283 | |
284 | +static struct resource axp288_adc_resources[] = { | |
285 | + { | |
286 | + .name = "GPADC", | |
287 | + .start = AXP288_IRQ_GPADC, | |
288 | + .end = AXP288_IRQ_GPADC, | |
289 | + .flags = IORESOURCE_IRQ, | |
290 | + }, | |
291 | +}; | |
292 | + | |
293 | +static struct resource axp288_charger_resources[] = { | |
294 | + { | |
295 | + .start = AXP288_IRQ_OV, | |
296 | + .end = AXP288_IRQ_OV, | |
297 | + .flags = IORESOURCE_IRQ, | |
298 | + }, | |
299 | + { | |
300 | + .start = AXP288_IRQ_DONE, | |
301 | + .end = AXP288_IRQ_DONE, | |
302 | + .flags = IORESOURCE_IRQ, | |
303 | + }, | |
304 | + { | |
305 | + .start = AXP288_IRQ_CHARGING, | |
306 | + .end = AXP288_IRQ_CHARGING, | |
307 | + .flags = IORESOURCE_IRQ, | |
308 | + }, | |
309 | + { | |
310 | + .start = AXP288_IRQ_SAFE_QUIT, | |
311 | + .end = AXP288_IRQ_SAFE_QUIT, | |
312 | + .flags = IORESOURCE_IRQ, | |
313 | + }, | |
314 | + { | |
315 | + .start = AXP288_IRQ_SAFE_ENTER, | |
316 | + .end = AXP288_IRQ_SAFE_ENTER, | |
317 | + .flags = IORESOURCE_IRQ, | |
318 | + }, | |
319 | + { | |
320 | + .start = AXP288_IRQ_QCBTU, | |
321 | + .end = AXP288_IRQ_QCBTU, | |
322 | + .flags = IORESOURCE_IRQ, | |
323 | + }, | |
324 | + { | |
325 | + .start = AXP288_IRQ_CBTU, | |
326 | + .end = AXP288_IRQ_CBTU, | |
327 | + .flags = IORESOURCE_IRQ, | |
328 | + }, | |
329 | + { | |
330 | + .start = AXP288_IRQ_QCBTO, | |
331 | + .end = AXP288_IRQ_QCBTO, | |
332 | + .flags = IORESOURCE_IRQ, | |
333 | + }, | |
334 | + { | |
335 | + .start = AXP288_IRQ_CBTO, | |
336 | + .end = AXP288_IRQ_CBTO, | |
337 | + .flags = IORESOURCE_IRQ, | |
338 | + }, | |
339 | +}; | |
340 | + | |
341 | +static struct mfd_cell axp288_cells[] = { | |
342 | + { | |
343 | + .name = "axp288_adc", | |
344 | + .num_resources = ARRAY_SIZE(axp288_adc_resources), | |
345 | + .resources = axp288_adc_resources, | |
346 | + }, | |
347 | + { | |
348 | + .name = "axp288_charger", | |
349 | + .num_resources = ARRAY_SIZE(axp288_charger_resources), | |
350 | + .resources = axp288_charger_resources, | |
351 | + }, | |
352 | + { | |
353 | + .name = "axp288_battery", | |
354 | + .num_resources = ARRAY_SIZE(axp288_battery_resources), | |
355 | + .resources = axp288_battery_resources, | |
356 | + }, | |
357 | +}; | |
358 | + | |
153 | 359 | static struct axp20x_dev *axp20x_pm_power_off; |
154 | 360 | static void axp20x_power_off(void) |
155 | 361 | { |
362 | + if (axp20x_pm_power_off->variant == AXP288_ID) | |
363 | + return; | |
364 | + | |
156 | 365 | regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL, |
157 | 366 | AXP20X_OFF); |
158 | 367 | } |
159 | 368 | |
369 | +static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev) | |
370 | +{ | |
371 | + const struct acpi_device_id *acpi_id; | |
372 | + const struct of_device_id *of_id; | |
373 | + | |
374 | + if (dev->of_node) { | |
375 | + of_id = of_match_device(axp20x_of_match, dev); | |
376 | + if (!of_id) { | |
377 | + dev_err(dev, "Unable to match OF ID\n"); | |
378 | + return -ENODEV; | |
379 | + } | |
380 | + axp20x->variant = (long) of_id->data; | |
381 | + } else { | |
382 | + acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev); | |
383 | + if (!acpi_id || !acpi_id->driver_data) { | |
384 | + dev_err(dev, "Unable to match ACPI ID and data\n"); | |
385 | + return -ENODEV; | |
386 | + } | |
387 | + axp20x->variant = (long) acpi_id->driver_data; | |
388 | + } | |
389 | + | |
390 | + switch (axp20x->variant) { | |
391 | + case AXP202_ID: | |
392 | + case AXP209_ID: | |
393 | + axp20x->nr_cells = ARRAY_SIZE(axp20x_cells); | |
394 | + axp20x->cells = axp20x_cells; | |
395 | + axp20x->regmap_cfg = &axp20x_regmap_config; | |
396 | + axp20x->regmap_irq_chip = &axp20x_regmap_irq_chip; | |
397 | + break; | |
398 | + case AXP288_ID: | |
399 | + axp20x->cells = axp288_cells; | |
400 | + axp20x->nr_cells = ARRAY_SIZE(axp288_cells); | |
401 | + axp20x->regmap_cfg = &axp288_regmap_config; | |
402 | + axp20x->regmap_irq_chip = &axp288_regmap_irq_chip; | |
403 | + break; | |
404 | + default: | |
405 | + dev_err(dev, "unsupported AXP20X ID %lu\n", axp20x->variant); | |
406 | + return -EINVAL; | |
407 | + } | |
408 | + dev_info(dev, "AXP20x variant %s found\n", | |
409 | + axp20x_model_names[axp20x->variant]); | |
410 | + | |
411 | + return 0; | |
412 | +} | |
413 | + | |
160 | 414 | static int axp20x_i2c_probe(struct i2c_client *i2c, |
161 | 415 | const struct i2c_device_id *id) |
162 | 416 | { |
163 | 417 | struct axp20x_dev *axp20x; |
164 | - const struct of_device_id *of_id; | |
165 | 418 | int ret; |
166 | 419 | |
167 | 420 | axp20x = devm_kzalloc(&i2c->dev, sizeof(*axp20x), GFP_KERNEL); |
168 | 421 | if (!axp20x) |
169 | 422 | return -ENOMEM; |
170 | 423 | |
171 | - of_id = of_match_device(axp20x_of_match, &i2c->dev); | |
172 | - if (!of_id) { | |
173 | - dev_err(&i2c->dev, "Unable to setup AXP20X data\n"); | |
174 | - return -ENODEV; | |
175 | - } | |
176 | - axp20x->variant = (long) of_id->data; | |
424 | + ret = axp20x_match_device(axp20x, &i2c->dev); | |
425 | + if (ret) | |
426 | + return ret; | |
177 | 427 | |
178 | 428 | axp20x->i2c_client = i2c; |
179 | 429 | axp20x->dev = &i2c->dev; |
180 | 430 | dev_set_drvdata(axp20x->dev, axp20x); |
181 | 431 | |
182 | - axp20x->regmap = devm_regmap_init_i2c(i2c, &axp20x_regmap_config); | |
432 | + axp20x->regmap = devm_regmap_init_i2c(i2c, axp20x->regmap_cfg); | |
183 | 433 | if (IS_ERR(axp20x->regmap)) { |
184 | 434 | ret = PTR_ERR(axp20x->regmap); |
185 | 435 | dev_err(&i2c->dev, "regmap init failed: %d\n", ret); |
186 | 436 | |
... | ... | @@ -188,15 +438,15 @@ |
188 | 438 | |
189 | 439 | ret = regmap_add_irq_chip(axp20x->regmap, i2c->irq, |
190 | 440 | IRQF_ONESHOT | IRQF_SHARED, -1, |
191 | - &axp20x_regmap_irq_chip, | |
441 | + axp20x->regmap_irq_chip, | |
192 | 442 | &axp20x->regmap_irqc); |
193 | 443 | if (ret) { |
194 | 444 | dev_err(&i2c->dev, "failed to add irq chip: %d\n", ret); |
195 | 445 | return ret; |
196 | 446 | } |
197 | 447 | |
198 | - ret = mfd_add_devices(axp20x->dev, -1, axp20x_cells, | |
199 | - ARRAY_SIZE(axp20x_cells), NULL, 0, NULL); | |
448 | + ret = mfd_add_devices(axp20x->dev, -1, axp20x->cells, | |
449 | + axp20x->nr_cells, NULL, 0, NULL); | |
200 | 450 | |
201 | 451 | if (ret) { |
202 | 452 | dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret); |
... | ... | @@ -234,6 +484,7 @@ |
234 | 484 | .name = "axp20x", |
235 | 485 | .owner = THIS_MODULE, |
236 | 486 | .of_match_table = of_match_ptr(axp20x_of_match), |
487 | + .acpi_match_table = ACPI_PTR(axp20x_acpi_match), | |
237 | 488 | }, |
238 | 489 | .probe = axp20x_i2c_probe, |
239 | 490 | .remove = axp20x_i2c_remove, |
include/linux/mfd/axp20x.h
... | ... | @@ -14,6 +14,8 @@ |
14 | 14 | enum { |
15 | 15 | AXP202_ID = 0, |
16 | 16 | AXP209_ID, |
17 | + AXP288_ID, | |
18 | + NR_AXP20X_VARIANTS, | |
17 | 19 | }; |
18 | 20 | |
19 | 21 | #define AXP20X_DATACACHE(m) (0x04 + (m)) |
20 | 22 | |
... | ... | @@ -49,11 +51,13 @@ |
49 | 51 | #define AXP20X_IRQ3_EN 0x42 |
50 | 52 | #define AXP20X_IRQ4_EN 0x43 |
51 | 53 | #define AXP20X_IRQ5_EN 0x44 |
54 | +#define AXP20X_IRQ6_EN 0x45 | |
52 | 55 | #define AXP20X_IRQ1_STATE 0x48 |
53 | 56 | #define AXP20X_IRQ2_STATE 0x49 |
54 | 57 | #define AXP20X_IRQ3_STATE 0x4a |
55 | 58 | #define AXP20X_IRQ4_STATE 0x4b |
56 | 59 | #define AXP20X_IRQ5_STATE 0x4c |
60 | +#define AXP20X_IRQ6_STATE 0x4d | |
57 | 61 | |
58 | 62 | /* ADC */ |
59 | 63 | #define AXP20X_ACIN_V_ADC_H 0x56 |
... | ... | @@ -116,6 +120,15 @@ |
116 | 120 | #define AXP20X_CC_CTRL 0xb8 |
117 | 121 | #define AXP20X_FG_RES 0xb9 |
118 | 122 | |
123 | +/* AXP288 specific registers */ | |
124 | +#define AXP288_PMIC_ADC_H 0x56 | |
125 | +#define AXP288_PMIC_ADC_L 0x57 | |
126 | +#define AXP288_ADC_TS_PIN_CTRL 0x84 | |
127 | + | |
128 | +#define AXP288_PMIC_ADC_EN 0x84 | |
129 | +#define AXP288_FG_TUNE5 0xed | |
130 | + | |
131 | + | |
119 | 132 | /* Regulators IDs */ |
120 | 133 | enum { |
121 | 134 | AXP20X_LDO1 = 0, |
122 | 135 | |
... | ... | @@ -169,12 +182,58 @@ |
169 | 182 | AXP20X_IRQ_GPIO0_INPUT, |
170 | 183 | }; |
171 | 184 | |
185 | +enum axp288_irqs { | |
186 | + AXP288_IRQ_VBUS_FALL = 2, | |
187 | + AXP288_IRQ_VBUS_RISE, | |
188 | + AXP288_IRQ_OV, | |
189 | + AXP288_IRQ_FALLING_ALT, | |
190 | + AXP288_IRQ_RISING_ALT, | |
191 | + AXP288_IRQ_OV_ALT, | |
192 | + AXP288_IRQ_DONE = 10, | |
193 | + AXP288_IRQ_CHARGING, | |
194 | + AXP288_IRQ_SAFE_QUIT, | |
195 | + AXP288_IRQ_SAFE_ENTER, | |
196 | + AXP288_IRQ_ABSENT, | |
197 | + AXP288_IRQ_APPEND, | |
198 | + AXP288_IRQ_QWBTU, | |
199 | + AXP288_IRQ_WBTU, | |
200 | + AXP288_IRQ_QWBTO, | |
201 | + AXP288_IRQ_WBTO, | |
202 | + AXP288_IRQ_QCBTU, | |
203 | + AXP288_IRQ_CBTU, | |
204 | + AXP288_IRQ_QCBTO, | |
205 | + AXP288_IRQ_CBTO, | |
206 | + AXP288_IRQ_WL2, | |
207 | + AXP288_IRQ_WL1, | |
208 | + AXP288_IRQ_GPADC, | |
209 | + AXP288_IRQ_OT = 31, | |
210 | + AXP288_IRQ_GPIO0, | |
211 | + AXP288_IRQ_GPIO1, | |
212 | + AXP288_IRQ_POKO, | |
213 | + AXP288_IRQ_POKL, | |
214 | + AXP288_IRQ_POKS, | |
215 | + AXP288_IRQ_POKN, | |
216 | + AXP288_IRQ_POKP, | |
217 | + AXP288_IRQ_TIMER, | |
218 | + AXP288_IRQ_MV_CHNG, | |
219 | + AXP288_IRQ_BC_USB_CHNG, | |
220 | +}; | |
221 | + | |
222 | +#define AXP288_TS_ADC_H 0x58 | |
223 | +#define AXP288_TS_ADC_L 0x59 | |
224 | +#define AXP288_GP_ADC_H 0x5a | |
225 | +#define AXP288_GP_ADC_L 0x5b | |
226 | + | |
172 | 227 | struct axp20x_dev { |
173 | 228 | struct device *dev; |
174 | 229 | struct i2c_client *i2c_client; |
175 | 230 | struct regmap *regmap; |
176 | 231 | struct regmap_irq_chip_data *regmap_irqc; |
177 | 232 | long variant; |
233 | + int nr_cells; | |
234 | + struct mfd_cell *cells; | |
235 | + const struct regmap_config *regmap_cfg; | |
236 | + const struct regmap_irq_chip *regmap_irq_chip; | |
178 | 237 | }; |
179 | 238 | |
180 | 239 | #endif /* __LINUX_MFD_AXP20X_H */ |