diff options
author | Hans de Goede <hdegoede@redhat.com> | 2016-06-02 13:18:53 -0400 |
---|---|---|
committer | Sebastian Reichel <sre@kernel.org> | 2016-06-09 21:31:27 -0400 |
commit | cecbf8d52e6c7f16d954091be9283b928199ed92 (patch) | |
tree | 117fd8bda4e60c160513f6873733cf5417588c9e | |
parent | 5630b4334c676ff60d9613e7b6a4cc89cb562acb (diff) |
power: axp20x_usb: Add support for usb power-supply on axp22x pmics
The usb power-supply on the axp22x pmics is mostly identical to the
one on the axp20x pmics. One significant difference is that it cannot
measure / monitor the usb voltage / current.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Sebastian Reichel <sre@kernel.org>
-rw-r--r-- | Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt | 3 | ||||
-rw-r--r-- | drivers/power/axp20x_usb_power.c | 92 |
2 files changed, 71 insertions, 24 deletions
diff --git a/Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt b/Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt index 862f4a49dc49..f1d7beec45bf 100644 --- a/Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt +++ b/Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt | |||
@@ -1,7 +1,8 @@ | |||
1 | AXP20x USB power supply | 1 | AXP20x USB power supply |
2 | 2 | ||
3 | Required Properties: | 3 | Required Properties: |
4 | -compatible: "x-powers,axp202-usb-power-supply" | 4 | -compatible: One of: "x-powers,axp202-usb-power-supply" |
5 | "x-powers,axp221-usb-power-supply" | ||
5 | 6 | ||
6 | This node is a subnode of the axp20x PMIC. | 7 | This node is a subnode of the axp20x PMIC. |
7 | 8 | ||
diff --git a/drivers/power/axp20x_usb_power.c b/drivers/power/axp20x_usb_power.c index 421a90b83567..6af6feb7058d 100644 --- a/drivers/power/axp20x_usb_power.c +++ b/drivers/power/axp20x_usb_power.c | |||
@@ -42,6 +42,7 @@ | |||
42 | #define AXP20X_VBUS_MON_VBUS_VALID BIT(3) | 42 | #define AXP20X_VBUS_MON_VBUS_VALID BIT(3) |
43 | 43 | ||
44 | struct axp20x_usb_power { | 44 | struct axp20x_usb_power { |
45 | struct device_node *np; | ||
45 | struct regmap *regmap; | 46 | struct regmap *regmap; |
46 | struct power_supply *supply; | 47 | struct power_supply *supply; |
47 | }; | 48 | }; |
@@ -85,7 +86,12 @@ static int axp20x_usb_power_get_property(struct power_supply *psy, | |||
85 | 86 | ||
86 | switch (v & AXP20X_VBUS_CLIMIT_MASK) { | 87 | switch (v & AXP20X_VBUS_CLIMIT_MASK) { |
87 | case AXP20X_VBUC_CLIMIT_100mA: | 88 | case AXP20X_VBUC_CLIMIT_100mA: |
88 | val->intval = 100000; | 89 | if (of_device_is_compatible(power->np, |
90 | "x-powers,axp202-usb-power-supply")) { | ||
91 | val->intval = 100000; | ||
92 | } else { | ||
93 | val->intval = -1; /* No 100mA limit */ | ||
94 | } | ||
89 | break; | 95 | break; |
90 | case AXP20X_VBUC_CLIMIT_500mA: | 96 | case AXP20X_VBUC_CLIMIT_500mA: |
91 | val->intval = 500000; | 97 | val->intval = 500000; |
@@ -122,16 +128,19 @@ static int axp20x_usb_power_get_property(struct power_supply *psy, | |||
122 | break; | 128 | break; |
123 | } | 129 | } |
124 | 130 | ||
125 | ret = regmap_read(power->regmap, AXP20X_USB_OTG_STATUS, &v); | 131 | val->intval = POWER_SUPPLY_HEALTH_GOOD; |
126 | if (ret) | ||
127 | return ret; | ||
128 | 132 | ||
129 | if (!(v & AXP20X_USB_STATUS_VBUS_VALID)) { | 133 | if (of_device_is_compatible(power->np, |
130 | val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; | 134 | "x-powers,axp202-usb-power-supply")) { |
131 | break; | 135 | ret = regmap_read(power->regmap, |
132 | } | 136 | AXP20X_USB_OTG_STATUS, &v); |
137 | if (ret) | ||
138 | return ret; | ||
133 | 139 | ||
134 | val->intval = POWER_SUPPLY_HEALTH_GOOD; | 140 | if (!(v & AXP20X_USB_STATUS_VBUS_VALID)) |
141 | val->intval = | ||
142 | POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; | ||
143 | } | ||
135 | break; | 144 | break; |
136 | case POWER_SUPPLY_PROP_PRESENT: | 145 | case POWER_SUPPLY_PROP_PRESENT: |
137 | val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT); | 146 | val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT); |
@@ -156,6 +165,14 @@ static enum power_supply_property axp20x_usb_power_properties[] = { | |||
156 | POWER_SUPPLY_PROP_CURRENT_NOW, | 165 | POWER_SUPPLY_PROP_CURRENT_NOW, |
157 | }; | 166 | }; |
158 | 167 | ||
168 | static enum power_supply_property axp22x_usb_power_properties[] = { | ||
169 | POWER_SUPPLY_PROP_HEALTH, | ||
170 | POWER_SUPPLY_PROP_PRESENT, | ||
171 | POWER_SUPPLY_PROP_ONLINE, | ||
172 | POWER_SUPPLY_PROP_VOLTAGE_MIN, | ||
173 | POWER_SUPPLY_PROP_CURRENT_MAX, | ||
174 | }; | ||
175 | |||
159 | static const struct power_supply_desc axp20x_usb_power_desc = { | 176 | static const struct power_supply_desc axp20x_usb_power_desc = { |
160 | .name = "axp20x-usb", | 177 | .name = "axp20x-usb", |
161 | .type = POWER_SUPPLY_TYPE_USB, | 178 | .type = POWER_SUPPLY_TYPE_USB, |
@@ -164,13 +181,25 @@ static const struct power_supply_desc axp20x_usb_power_desc = { | |||
164 | .get_property = axp20x_usb_power_get_property, | 181 | .get_property = axp20x_usb_power_get_property, |
165 | }; | 182 | }; |
166 | 183 | ||
184 | static const struct power_supply_desc axp22x_usb_power_desc = { | ||
185 | .name = "axp20x-usb", | ||
186 | .type = POWER_SUPPLY_TYPE_USB, | ||
187 | .properties = axp22x_usb_power_properties, | ||
188 | .num_properties = ARRAY_SIZE(axp22x_usb_power_properties), | ||
189 | .get_property = axp20x_usb_power_get_property, | ||
190 | }; | ||
191 | |||
167 | static int axp20x_usb_power_probe(struct platform_device *pdev) | 192 | static int axp20x_usb_power_probe(struct platform_device *pdev) |
168 | { | 193 | { |
169 | struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); | 194 | struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); |
170 | struct power_supply_config psy_cfg = {}; | 195 | struct power_supply_config psy_cfg = {}; |
171 | struct axp20x_usb_power *power; | 196 | struct axp20x_usb_power *power; |
172 | static const char * const irq_names[] = { "VBUS_PLUGIN", | 197 | static const char * const axp20x_irq_names[] = { "VBUS_PLUGIN", |
173 | "VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID" }; | 198 | "VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID", NULL }; |
199 | static const char * const axp22x_irq_names[] = { | ||
200 | "VBUS_PLUGIN", "VBUS_REMOVAL", NULL }; | ||
201 | static const char * const *irq_names; | ||
202 | const struct power_supply_desc *usb_power_desc; | ||
174 | int i, irq, ret; | 203 | int i, irq, ret; |
175 | 204 | ||
176 | if (!of_device_is_available(pdev->dev.of_node)) | 205 | if (!of_device_is_available(pdev->dev.of_node)) |
@@ -185,31 +214,47 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) | |||
185 | if (!power) | 214 | if (!power) |
186 | return -ENOMEM; | 215 | return -ENOMEM; |
187 | 216 | ||
217 | power->np = pdev->dev.of_node; | ||
188 | power->regmap = axp20x->regmap; | 218 | power->regmap = axp20x->regmap; |
189 | 219 | ||
190 | /* Enable vbus valid checking */ | 220 | if (of_device_is_compatible(power->np, |
191 | ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON, | 221 | "x-powers,axp202-usb-power-supply")) { |
192 | AXP20X_VBUS_MON_VBUS_VALID, AXP20X_VBUS_MON_VBUS_VALID); | 222 | /* Enable vbus valid checking */ |
193 | if (ret) | 223 | ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON, |
194 | return ret; | 224 | AXP20X_VBUS_MON_VBUS_VALID, |
225 | AXP20X_VBUS_MON_VBUS_VALID); | ||
226 | if (ret) | ||
227 | return ret; | ||
195 | 228 | ||
196 | /* Enable vbus voltage and current measurement */ | 229 | /* Enable vbus voltage and current measurement */ |
197 | ret = regmap_update_bits(power->regmap, AXP20X_ADC_EN1, | 230 | ret = regmap_update_bits(power->regmap, AXP20X_ADC_EN1, |
198 | AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT, | 231 | AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT, |
199 | AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT); | 232 | AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT); |
200 | if (ret) | 233 | if (ret) |
201 | return ret; | 234 | return ret; |
235 | |||
236 | usb_power_desc = &axp20x_usb_power_desc; | ||
237 | irq_names = axp20x_irq_names; | ||
238 | } else if (of_device_is_compatible(power->np, | ||
239 | "x-powers,axp221-usb-power-supply")) { | ||
240 | usb_power_desc = &axp22x_usb_power_desc; | ||
241 | irq_names = axp22x_irq_names; | ||
242 | } else { | ||
243 | dev_err(&pdev->dev, "Unsupported AXP variant: %ld\n", | ||
244 | axp20x->variant); | ||
245 | return -EINVAL; | ||
246 | } | ||
202 | 247 | ||
203 | psy_cfg.of_node = pdev->dev.of_node; | 248 | psy_cfg.of_node = pdev->dev.of_node; |
204 | psy_cfg.drv_data = power; | 249 | psy_cfg.drv_data = power; |
205 | 250 | ||
206 | power->supply = devm_power_supply_register(&pdev->dev, | 251 | power->supply = devm_power_supply_register(&pdev->dev, usb_power_desc, |
207 | &axp20x_usb_power_desc, &psy_cfg); | 252 | &psy_cfg); |
208 | if (IS_ERR(power->supply)) | 253 | if (IS_ERR(power->supply)) |
209 | return PTR_ERR(power->supply); | 254 | return PTR_ERR(power->supply); |
210 | 255 | ||
211 | /* Request irqs after registering, as irqs may trigger immediately */ | 256 | /* Request irqs after registering, as irqs may trigger immediately */ |
212 | for (i = 0; i < ARRAY_SIZE(irq_names); i++) { | 257 | for (i = 0; irq_names[i]; i++) { |
213 | irq = platform_get_irq_byname(pdev, irq_names[i]); | 258 | irq = platform_get_irq_byname(pdev, irq_names[i]); |
214 | if (irq < 0) { | 259 | if (irq < 0) { |
215 | dev_warn(&pdev->dev, "No IRQ for %s: %d\n", | 260 | dev_warn(&pdev->dev, "No IRQ for %s: %d\n", |
@@ -229,6 +274,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) | |||
229 | 274 | ||
230 | static const struct of_device_id axp20x_usb_power_match[] = { | 275 | static const struct of_device_id axp20x_usb_power_match[] = { |
231 | { .compatible = "x-powers,axp202-usb-power-supply" }, | 276 | { .compatible = "x-powers,axp202-usb-power-supply" }, |
277 | { .compatible = "x-powers,axp221-usb-power-supply" }, | ||
232 | { } | 278 | { } |
233 | }; | 279 | }; |
234 | MODULE_DEVICE_TABLE(of, axp20x_usb_power_match); | 280 | MODULE_DEVICE_TABLE(of, axp20x_usb_power_match); |