Skip to content

Commit 761db35

Browse files
committed
platform/x86: Add intel_crystal_cove_charger driver
Driver for the external-charger IRQ pass-through function of the Intel Bay Trail Crystal Cove PMIC. Note this is NOT a power_supply class driver, it just deals with IRQ pass-through, this requires this separate driver because the PMIC's level 2 interrupt for this must be explicitly acked. This new driver gets enabled by the existing X86_ANDROID_TABLETS Kconfig option because the x86-android-tablets module is the only consumer of the exported external-charger IRQ. Reviewed-by: Andy Shevchenko <[email protected]> Signed-off-by: Hans de Goede <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent c8e2d92 commit 761db35

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed

drivers/platform/x86/intel/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
3030
# Intel PMIC / PMC / P-Unit drivers
3131
intel_bxtwc_tmu-y := bxtwc_tmu.o
3232
obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o
33+
intel_crystal_cove_charger-y := crystal_cove_charger.o
34+
obj-$(CONFIG_X86_ANDROID_TABLETS) += intel_crystal_cove_charger.o
3335
intel_chtdc_ti_pwrbtn-y := chtdc_ti_pwrbtn.o
3436
obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o
3537
intel_mrfld_pwrbtn-y := mrfld_pwrbtn.o
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
/*
3+
* Driver for the external-charger IRQ pass-through function of the
4+
* Intel Bay Trail Crystal Cove PMIC.
5+
*
6+
* Note this is NOT a power_supply class driver, it just deals with IRQ
7+
* pass-through, this requires a separate driver because the PMIC's
8+
* level 2 interrupt for this must be explicitly acked.
9+
*/
10+
11+
#include <linux/interrupt.h>
12+
#include <linux/irq.h>
13+
#include <linux/irqdomain.h>
14+
#include <linux/mfd/intel_soc_pmic.h>
15+
#include <linux/module.h>
16+
#include <linux/platform_device.h>
17+
#include <linux/regmap.h>
18+
19+
#define CHGRIRQ_REG 0x0a
20+
21+
struct crystal_cove_charger_data {
22+
struct mutex buslock; /* irq_bus_lock */
23+
struct irq_chip irqchip;
24+
struct regmap *regmap;
25+
struct irq_domain *irq_domain;
26+
int irq;
27+
int charger_irq;
28+
bool irq_enabled;
29+
bool irq_is_enabled;
30+
};
31+
32+
static irqreturn_t crystal_cove_charger_irq(int irq, void *data)
33+
{
34+
struct crystal_cove_charger_data *charger = data;
35+
36+
/* No need to read CHGRIRQ_REG as there is only 1 IRQ */
37+
handle_nested_irq(charger->charger_irq);
38+
39+
/* Ack CHGRIRQ 0 */
40+
regmap_write(charger->regmap, CHGRIRQ_REG, BIT(0));
41+
42+
return IRQ_HANDLED;
43+
}
44+
45+
static void crystal_cove_charger_irq_bus_lock(struct irq_data *data)
46+
{
47+
struct crystal_cove_charger_data *charger = irq_data_get_irq_chip_data(data);
48+
49+
mutex_lock(&charger->buslock);
50+
}
51+
52+
static void crystal_cove_charger_irq_bus_sync_unlock(struct irq_data *data)
53+
{
54+
struct crystal_cove_charger_data *charger = irq_data_get_irq_chip_data(data);
55+
56+
if (charger->irq_is_enabled != charger->irq_enabled) {
57+
if (charger->irq_enabled)
58+
enable_irq(charger->irq);
59+
else
60+
disable_irq(charger->irq);
61+
62+
charger->irq_is_enabled = charger->irq_enabled;
63+
}
64+
65+
mutex_unlock(&charger->buslock);
66+
}
67+
68+
static void crystal_cove_charger_irq_unmask(struct irq_data *data)
69+
{
70+
struct crystal_cove_charger_data *charger = irq_data_get_irq_chip_data(data);
71+
72+
charger->irq_enabled = true;
73+
}
74+
75+
static void crystal_cove_charger_irq_mask(struct irq_data *data)
76+
{
77+
struct crystal_cove_charger_data *charger = irq_data_get_irq_chip_data(data);
78+
79+
charger->irq_enabled = false;
80+
}
81+
82+
static void crystal_cove_charger_rm_irq_domain(void *data)
83+
{
84+
struct crystal_cove_charger_data *charger = data;
85+
86+
irq_domain_remove(charger->irq_domain);
87+
}
88+
89+
static int crystal_cove_charger_probe(struct platform_device *pdev)
90+
{
91+
struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
92+
struct crystal_cove_charger_data *charger;
93+
int ret;
94+
95+
charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
96+
if (!charger)
97+
return -ENOMEM;
98+
99+
charger->regmap = pmic->regmap;
100+
mutex_init(&charger->buslock);
101+
102+
charger->irq = platform_get_irq(pdev, 0);
103+
if (charger->irq < 0)
104+
return charger->irq;
105+
106+
charger->irq_domain = irq_domain_create_linear(dev_fwnode(pdev->dev.parent), 1,
107+
&irq_domain_simple_ops, NULL);
108+
if (!charger->irq_domain)
109+
return -ENOMEM;
110+
111+
/* Distuingish IRQ domain from others sharing (MFD) the same fwnode */
112+
irq_domain_update_bus_token(charger->irq_domain, DOMAIN_BUS_WAKEUP);
113+
114+
ret = devm_add_action_or_reset(&pdev->dev, crystal_cove_charger_rm_irq_domain, charger);
115+
if (ret)
116+
return ret;
117+
118+
charger->charger_irq = irq_create_mapping(charger->irq_domain, 0);
119+
if (!charger->charger_irq)
120+
return -ENOMEM;
121+
122+
charger->irqchip.name = KBUILD_MODNAME;
123+
charger->irqchip.irq_unmask = crystal_cove_charger_irq_unmask;
124+
charger->irqchip.irq_mask = crystal_cove_charger_irq_mask;
125+
charger->irqchip.irq_bus_lock = crystal_cove_charger_irq_bus_lock;
126+
charger->irqchip.irq_bus_sync_unlock = crystal_cove_charger_irq_bus_sync_unlock;
127+
128+
irq_set_chip_data(charger->charger_irq, charger);
129+
irq_set_chip_and_handler(charger->charger_irq, &charger->irqchip, handle_simple_irq);
130+
irq_set_nested_thread(charger->charger_irq, true);
131+
irq_set_noprobe(charger->charger_irq);
132+
133+
ret = devm_request_threaded_irq(&pdev->dev, charger->irq, NULL,
134+
crystal_cove_charger_irq,
135+
IRQF_ONESHOT | IRQF_NO_AUTOEN,
136+
KBUILD_MODNAME, charger);
137+
if (ret)
138+
return dev_err_probe(&pdev->dev, ret, "requesting irq\n");
139+
140+
return 0;
141+
}
142+
143+
static struct platform_driver crystal_cove_charger_driver = {
144+
.probe = crystal_cove_charger_probe,
145+
.driver = {
146+
.name = "crystal_cove_charger",
147+
},
148+
};
149+
module_platform_driver(crystal_cove_charger_driver);
150+
151+
MODULE_AUTHOR("Hans de Goede <[email protected]");
152+
MODULE_DESCRIPTION("Intel Bay Trail Crystal Cove external charger IRQ pass-through");
153+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)