1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Device access for Dollar Cove TI PMIC |
4 | * |
5 | * Copyright (c) 2014, Intel Corporation. |
6 | * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com> |
7 | * |
8 | * Cleanup and forward-ported |
9 | * Copyright (c) 2017 Takashi Iwai <tiwai@suse.de> |
10 | */ |
11 | |
12 | #include <linux/acpi.h> |
13 | #include <linux/interrupt.h> |
14 | #include <linux/i2c.h> |
15 | #include <linux/mfd/core.h> |
16 | #include <linux/mfd/intel_soc_pmic.h> |
17 | #include <linux/module.h> |
18 | #include <linux/regmap.h> |
19 | |
20 | #define CHTDC_TI_IRQLVL1 0x01 |
21 | #define CHTDC_TI_MASK_IRQLVL1 0x02 |
22 | |
23 | /* Level 1 IRQs */ |
24 | enum { |
25 | CHTDC_TI_PWRBTN = 0, /* power button */ |
26 | CHTDC_TI_DIETMPWARN, /* thermal */ |
27 | CHTDC_TI_ADCCMPL, /* ADC */ |
28 | /* No IRQ 3 */ |
29 | CHTDC_TI_VBATLOW = 4, /* battery */ |
30 | CHTDC_TI_VBUSDET, /* power source */ |
31 | /* No IRQ 6 */ |
32 | CHTDC_TI_CCEOCAL = 7, /* battery */ |
33 | }; |
34 | |
35 | static const struct resource power_button_resources[] = { |
36 | DEFINE_RES_IRQ(CHTDC_TI_PWRBTN), |
37 | }; |
38 | |
39 | static const struct resource thermal_resources[] = { |
40 | DEFINE_RES_IRQ(CHTDC_TI_DIETMPWARN), |
41 | }; |
42 | |
43 | static const struct resource adc_resources[] = { |
44 | DEFINE_RES_IRQ(CHTDC_TI_ADCCMPL), |
45 | }; |
46 | |
47 | static const struct resource pwrsrc_resources[] = { |
48 | DEFINE_RES_IRQ(CHTDC_TI_VBUSDET), |
49 | }; |
50 | |
51 | static const struct resource battery_resources[] = { |
52 | DEFINE_RES_IRQ(CHTDC_TI_VBATLOW), |
53 | DEFINE_RES_IRQ(CHTDC_TI_CCEOCAL), |
54 | }; |
55 | |
56 | static struct mfd_cell chtdc_ti_dev[] = { |
57 | { |
58 | .name = "chtdc_ti_pwrbtn" , |
59 | .num_resources = ARRAY_SIZE(power_button_resources), |
60 | .resources = power_button_resources, |
61 | }, { |
62 | .name = "chtdc_ti_adc" , |
63 | .num_resources = ARRAY_SIZE(adc_resources), |
64 | .resources = adc_resources, |
65 | }, { |
66 | .name = "chtdc_ti_thermal" , |
67 | .num_resources = ARRAY_SIZE(thermal_resources), |
68 | .resources = thermal_resources, |
69 | }, { |
70 | .name = "chtdc_ti_pwrsrc" , |
71 | .num_resources = ARRAY_SIZE(pwrsrc_resources), |
72 | .resources = pwrsrc_resources, |
73 | }, { |
74 | .name = "chtdc_ti_battery" , |
75 | .num_resources = ARRAY_SIZE(battery_resources), |
76 | .resources = battery_resources, |
77 | }, |
78 | { .name = "chtdc_ti_region" , }, |
79 | }; |
80 | |
81 | static const struct regmap_config chtdc_ti_regmap_config = { |
82 | .reg_bits = 8, |
83 | .val_bits = 8, |
84 | .max_register = 128, |
85 | .cache_type = REGCACHE_NONE, |
86 | }; |
87 | |
88 | static const struct regmap_irq chtdc_ti_irqs[] = { |
89 | REGMAP_IRQ_REG(CHTDC_TI_PWRBTN, 0, BIT(CHTDC_TI_PWRBTN)), |
90 | REGMAP_IRQ_REG(CHTDC_TI_DIETMPWARN, 0, BIT(CHTDC_TI_DIETMPWARN)), |
91 | REGMAP_IRQ_REG(CHTDC_TI_ADCCMPL, 0, BIT(CHTDC_TI_ADCCMPL)), |
92 | REGMAP_IRQ_REG(CHTDC_TI_VBATLOW, 0, BIT(CHTDC_TI_VBATLOW)), |
93 | REGMAP_IRQ_REG(CHTDC_TI_VBUSDET, 0, BIT(CHTDC_TI_VBUSDET)), |
94 | REGMAP_IRQ_REG(CHTDC_TI_CCEOCAL, 0, BIT(CHTDC_TI_CCEOCAL)), |
95 | }; |
96 | |
97 | static const struct regmap_irq_chip chtdc_ti_irq_chip = { |
98 | .name = KBUILD_MODNAME, |
99 | .irqs = chtdc_ti_irqs, |
100 | .num_irqs = ARRAY_SIZE(chtdc_ti_irqs), |
101 | .num_regs = 1, |
102 | .status_base = CHTDC_TI_IRQLVL1, |
103 | .mask_base = CHTDC_TI_MASK_IRQLVL1, |
104 | .ack_base = CHTDC_TI_IRQLVL1, |
105 | }; |
106 | |
107 | static int chtdc_ti_probe(struct i2c_client *i2c) |
108 | { |
109 | struct device *dev = &i2c->dev; |
110 | struct intel_soc_pmic *pmic; |
111 | int ret; |
112 | |
113 | pmic = devm_kzalloc(dev, size: sizeof(*pmic), GFP_KERNEL); |
114 | if (!pmic) |
115 | return -ENOMEM; |
116 | |
117 | i2c_set_clientdata(client: i2c, data: pmic); |
118 | |
119 | pmic->regmap = devm_regmap_init_i2c(i2c, &chtdc_ti_regmap_config); |
120 | if (IS_ERR(ptr: pmic->regmap)) |
121 | return PTR_ERR(ptr: pmic->regmap); |
122 | pmic->irq = i2c->irq; |
123 | |
124 | ret = devm_regmap_add_irq_chip(dev, map: pmic->regmap, irq: pmic->irq, |
125 | IRQF_ONESHOT, irq_base: 0, |
126 | chip: &chtdc_ti_irq_chip, |
127 | data: &pmic->irq_chip_data); |
128 | if (ret) |
129 | return ret; |
130 | |
131 | return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, cells: chtdc_ti_dev, |
132 | ARRAY_SIZE(chtdc_ti_dev), NULL, irq_base: 0, |
133 | irq_domain: regmap_irq_get_domain(data: pmic->irq_chip_data)); |
134 | } |
135 | |
136 | static void chtdc_ti_shutdown(struct i2c_client *i2c) |
137 | { |
138 | struct intel_soc_pmic *pmic = i2c_get_clientdata(client: i2c); |
139 | |
140 | disable_irq(irq: pmic->irq); |
141 | } |
142 | |
143 | static int chtdc_ti_suspend(struct device *dev) |
144 | { |
145 | struct intel_soc_pmic *pmic = dev_get_drvdata(dev); |
146 | |
147 | disable_irq(irq: pmic->irq); |
148 | |
149 | return 0; |
150 | } |
151 | |
152 | static int chtdc_ti_resume(struct device *dev) |
153 | { |
154 | struct intel_soc_pmic *pmic = dev_get_drvdata(dev); |
155 | |
156 | enable_irq(irq: pmic->irq); |
157 | |
158 | return 0; |
159 | } |
160 | |
161 | static DEFINE_SIMPLE_DEV_PM_OPS(chtdc_ti_pm_ops, chtdc_ti_suspend, chtdc_ti_resume); |
162 | |
163 | static const struct acpi_device_id chtdc_ti_acpi_ids[] = { |
164 | { "INT33F5" }, |
165 | { }, |
166 | }; |
167 | MODULE_DEVICE_TABLE(acpi, chtdc_ti_acpi_ids); |
168 | |
169 | static struct i2c_driver chtdc_ti_i2c_driver = { |
170 | .driver = { |
171 | .name = "intel_soc_pmic_chtdc_ti" , |
172 | .pm = pm_sleep_ptr(&chtdc_ti_pm_ops), |
173 | .acpi_match_table = chtdc_ti_acpi_ids, |
174 | }, |
175 | .probe = chtdc_ti_probe, |
176 | .shutdown = chtdc_ti_shutdown, |
177 | }; |
178 | module_i2c_driver(chtdc_ti_i2c_driver); |
179 | |
180 | MODULE_DESCRIPTION("I2C driver for Intel SoC Dollar Cove TI PMIC" ); |
181 | MODULE_LICENSE("GPL v2" ); |
182 | |