1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Device access for Crystal Cove PMIC 4 * 5 * Copyright (C) 2012-2014, 2022 Intel Corporation. All rights reserved. 6 * 7 * Author: Yang, Bin <bin.yang@intel.com> 8 * Author: Zhu, Lejun <lejun.zhu@linux.intel.com> 9 */ 10 11 #include <linux/i2c.h> 12 #include <linux/interrupt.h> 13 #include <linux/module.h> 14 #include <linux/mfd/core.h> 15 #include <linux/mfd/intel_soc_pmic.h> 16 #include <linux/platform_data/x86/soc.h> 17 #include <linux/pwm.h> 18 #include <linux/regmap.h> 19 20 #define CRYSTAL_COVE_MAX_REGISTER 0xC6 21 22 #define CRYSTAL_COVE_REG_IRQLVL1 0x02 23 #define CRYSTAL_COVE_REG_MIRQLVL1 0x0E 24 25 #define CRYSTAL_COVE_IRQ_PWRSRC 0 26 #define CRYSTAL_COVE_IRQ_THRM 1 27 #define CRYSTAL_COVE_IRQ_BCU 2 28 #define CRYSTAL_COVE_IRQ_ADC 3 29 #define CRYSTAL_COVE_IRQ_CHGR 4 30 #define CRYSTAL_COVE_IRQ_GPIO 5 31 #define CRYSTAL_COVE_IRQ_VHDMIOCP 6 32 33 static const struct resource pwrsrc_resources[] = { 34 DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_PWRSRC, "PWRSRC"), 35 }; 36 37 static const struct resource thermal_resources[] = { 38 DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_THRM, "THERMAL"), 39 }; 40 41 static const struct resource bcu_resources[] = { 42 DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_BCU, "BCU"), 43 }; 44 45 static const struct resource adc_resources[] = { 46 DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_ADC, "ADC"), 47 }; 48 49 static const struct resource charger_resources[] = { 50 DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_CHGR, "CHGR"), 51 }; 52 53 static const struct resource gpio_resources[] = { 54 DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_GPIO, "GPIO"), 55 }; 56 57 static struct mfd_cell crystal_cove_byt_dev[] = { 58 { 59 .name = "crystal_cove_pwrsrc", 60 .num_resources = ARRAY_SIZE(pwrsrc_resources), 61 .resources = pwrsrc_resources, 62 }, 63 { 64 .name = "crystal_cove_thermal", 65 .num_resources = ARRAY_SIZE(thermal_resources), 66 .resources = thermal_resources, 67 }, 68 { 69 .name = "crystal_cove_bcu", 70 .num_resources = ARRAY_SIZE(bcu_resources), 71 .resources = bcu_resources, 72 }, 73 { 74 .name = "crystal_cove_adc", 75 .num_resources = ARRAY_SIZE(adc_resources), 76 .resources = adc_resources, 77 }, 78 { 79 .name = "crystal_cove_charger", 80 .num_resources = ARRAY_SIZE(charger_resources), 81 .resources = charger_resources, 82 }, 83 { 84 .name = "crystal_cove_gpio", 85 .num_resources = ARRAY_SIZE(gpio_resources), 86 .resources = gpio_resources, 87 }, 88 { 89 .name = "byt_crystal_cove_pmic", 90 }, 91 { 92 .name = "crystal_cove_pwm", 93 }, 94 }; 95 96 static struct mfd_cell crystal_cove_cht_dev[] = { 97 { 98 .name = "crystal_cove_gpio", 99 .num_resources = ARRAY_SIZE(gpio_resources), 100 .resources = gpio_resources, 101 }, 102 { 103 .name = "cht_crystal_cove_pmic", 104 }, 105 { 106 .name = "crystal_cove_pwm", 107 }, 108 }; 109 110 static const struct regmap_config crystal_cove_regmap_config = { 111 .reg_bits = 8, 112 .val_bits = 8, 113 114 .max_register = CRYSTAL_COVE_MAX_REGISTER, 115 }; 116 117 static const struct regmap_irq crystal_cove_irqs[] = { 118 REGMAP_IRQ_REG(CRYSTAL_COVE_IRQ_PWRSRC, 0, BIT(CRYSTAL_COVE_IRQ_PWRSRC)), 119 REGMAP_IRQ_REG(CRYSTAL_COVE_IRQ_THRM, 0, BIT(CRYSTAL_COVE_IRQ_THRM)), 120 REGMAP_IRQ_REG(CRYSTAL_COVE_IRQ_BCU, 0, BIT(CRYSTAL_COVE_IRQ_BCU)), 121 REGMAP_IRQ_REG(CRYSTAL_COVE_IRQ_ADC, 0, BIT(CRYSTAL_COVE_IRQ_ADC)), 122 REGMAP_IRQ_REG(CRYSTAL_COVE_IRQ_CHGR, 0, BIT(CRYSTAL_COVE_IRQ_CHGR)), 123 REGMAP_IRQ_REG(CRYSTAL_COVE_IRQ_GPIO, 0, BIT(CRYSTAL_COVE_IRQ_GPIO)), 124 REGMAP_IRQ_REG(CRYSTAL_COVE_IRQ_VHDMIOCP, 0, BIT(CRYSTAL_COVE_IRQ_VHDMIOCP)), 125 }; 126 127 static const struct regmap_irq_chip crystal_cove_irq_chip = { 128 .name = "Crystal Cove", 129 .irqs = crystal_cove_irqs, 130 .num_irqs = ARRAY_SIZE(crystal_cove_irqs), 131 .num_regs = 1, 132 .status_base = CRYSTAL_COVE_REG_IRQLVL1, 133 .mask_base = CRYSTAL_COVE_REG_MIRQLVL1, 134 }; 135 136 /* PWM consumed by the Intel GFX */ 137 static struct pwm_lookup crc_pwm_lookup[] = { 138 PWM_LOOKUP_WITH_MODULE("crystal_cove_pwm", 0, "0000:00:02.0", 139 "pwm_pmic_backlight", 0, PWM_POLARITY_NORMAL, 140 "pwm-crc"), 141 }; 142 143 struct crystal_cove_config { 144 unsigned long irq_flags; 145 struct mfd_cell *cell_dev; 146 int n_cell_devs; 147 const struct regmap_config *regmap_config; 148 const struct regmap_irq_chip *irq_chip; 149 }; 150 151 static const struct crystal_cove_config crystal_cove_config_byt_crc = { 152 .irq_flags = IRQF_TRIGGER_RISING, 153 .cell_dev = crystal_cove_byt_dev, 154 .n_cell_devs = ARRAY_SIZE(crystal_cove_byt_dev), 155 .regmap_config = &crystal_cove_regmap_config, 156 .irq_chip = &crystal_cove_irq_chip, 157 }; 158 159 static const struct crystal_cove_config crystal_cove_config_cht_crc = { 160 .irq_flags = IRQF_TRIGGER_RISING, 161 .cell_dev = crystal_cove_cht_dev, 162 .n_cell_devs = ARRAY_SIZE(crystal_cove_cht_dev), 163 .regmap_config = &crystal_cove_regmap_config, 164 .irq_chip = &crystal_cove_irq_chip, 165 }; 166 167 static int crystal_cove_i2c_probe(struct i2c_client *i2c) 168 { 169 const struct crystal_cove_config *config; 170 struct device *dev = &i2c->dev; 171 struct intel_soc_pmic *pmic; 172 int ret; 173 174 if (soc_intel_is_byt()) 175 config = &crystal_cove_config_byt_crc; 176 else 177 config = &crystal_cove_config_cht_crc; 178 179 pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); 180 if (!pmic) 181 return -ENOMEM; 182 183 i2c_set_clientdata(i2c, pmic); 184 185 pmic->regmap = devm_regmap_init_i2c(i2c, config->regmap_config); 186 if (IS_ERR(pmic->regmap)) 187 return PTR_ERR(pmic->regmap); 188 189 pmic->irq = i2c->irq; 190 191 ret = devm_regmap_add_irq_chip(dev, pmic->regmap, pmic->irq, 192 config->irq_flags | IRQF_ONESHOT, 193 0, config->irq_chip, &pmic->irq_chip_data); 194 if (ret) 195 return ret; 196 197 ret = enable_irq_wake(pmic->irq); 198 if (ret) 199 dev_warn(dev, "Can't enable IRQ as wake source: %d\n", ret); 200 201 /* Add lookup table for crc-pwm */ 202 pwm_add_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup)); 203 204 /* To distuingish this domain from the GPIO/charger's irqchip domains */ 205 irq_domain_update_bus_token(regmap_irq_get_domain(pmic->irq_chip_data), 206 DOMAIN_BUS_NEXUS); 207 208 ret = mfd_add_devices(dev, PLATFORM_DEVID_NONE, config->cell_dev, 209 config->n_cell_devs, NULL, 0, 210 regmap_irq_get_domain(pmic->irq_chip_data)); 211 if (ret) 212 pwm_remove_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup)); 213 214 return ret; 215 } 216 217 static void crystal_cove_i2c_remove(struct i2c_client *i2c) 218 { 219 /* remove crc-pwm lookup table */ 220 pwm_remove_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup)); 221 222 mfd_remove_devices(&i2c->dev); 223 } 224 225 static void crystal_cove_shutdown(struct i2c_client *i2c) 226 { 227 struct intel_soc_pmic *pmic = i2c_get_clientdata(i2c); 228 229 disable_irq(pmic->irq); 230 231 return; 232 } 233 234 static int crystal_cove_suspend(struct device *dev) 235 { 236 struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 237 238 disable_irq(pmic->irq); 239 240 return 0; 241 } 242 243 static int crystal_cove_resume(struct device *dev) 244 { 245 struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 246 247 enable_irq(pmic->irq); 248 249 return 0; 250 } 251 252 static DEFINE_SIMPLE_DEV_PM_OPS(crystal_cove_pm_ops, crystal_cove_suspend, crystal_cove_resume); 253 254 static const struct acpi_device_id crystal_cove_acpi_match[] = { 255 { "INT33FD" }, 256 { }, 257 }; 258 MODULE_DEVICE_TABLE(acpi, crystal_cove_acpi_match); 259 260 static const struct i2c_device_id crystal_cove_i2c_match[] = { 261 { "intel_soc_pmic_crc" }, 262 { } 263 }; 264 MODULE_DEVICE_TABLE(i2c, crystal_cove_i2c_match); 265 266 static struct i2c_driver crystal_cove_i2c_driver = { 267 .driver = { 268 .name = "intel_soc_pmic_crc", 269 .pm = pm_sleep_ptr(&crystal_cove_pm_ops), 270 .acpi_match_table = crystal_cove_acpi_match, 271 }, 272 .id_table = crystal_cove_i2c_match, 273 .probe = crystal_cove_i2c_probe, 274 .remove = crystal_cove_i2c_remove, 275 .shutdown = crystal_cove_shutdown, 276 }; 277 278 module_i2c_driver(crystal_cove_i2c_driver); 279 280 MODULE_DESCRIPTION("I2C driver for Intel SoC PMIC"); 281 MODULE_AUTHOR("Yang, Bin <bin.yang@intel.com>"); 282 MODULE_AUTHOR("Zhu, Lejun <lejun.zhu@linux.intel.com>"); 283