1 // SPDX-License-Identifier: GPL-2.0-or-later 2 // 3 // Copyright (C) 2018 ROHM Semiconductors 4 // 5 // ROHM BD71837MWV and BD71847MWV PMIC driver 6 // 7 // Datasheet for BD71837MWV available from 8 // https://www.rohm.com/datasheet/BD71837MWV/bd71837mwv-e 9 10 #include <linux/device/devres.h> 11 #include <linux/gfp_types.h> 12 #include <linux/i2c.h> 13 #include <linux/input.h> 14 #include <linux/interrupt.h> 15 #include <linux/mfd/rohm-bd718x7.h> 16 #include <linux/mfd/core.h> 17 #include <linux/module.h> 18 #include <linux/of.h> 19 #include <linux/property.h> 20 #include <linux/regmap.h> 21 #include <linux/types.h> 22 23 static struct mfd_cell bd71837_mfd_cells[] = { 24 { .name = "bd71837-clk", }, 25 { .name = "bd71837-pmic", }, 26 }; 27 28 static struct mfd_cell bd71847_mfd_cells[] = { 29 { .name = "bd71847-clk", }, 30 { .name = "bd71847-pmic", }, 31 }; 32 33 static const struct regmap_irq bd718xx_irqs[] = { 34 REGMAP_IRQ_REG(BD718XX_INT_SWRST, 0, BD718XX_INT_SWRST_MASK), 35 REGMAP_IRQ_REG(BD718XX_INT_PWRBTN_S, 0, BD718XX_INT_PWRBTN_S_MASK), 36 REGMAP_IRQ_REG(BD718XX_INT_PWRBTN_L, 0, BD718XX_INT_PWRBTN_L_MASK), 37 REGMAP_IRQ_REG(BD718XX_INT_PWRBTN, 0, BD718XX_INT_PWRBTN_MASK), 38 REGMAP_IRQ_REG(BD718XX_INT_WDOG, 0, BD718XX_INT_WDOG_MASK), 39 REGMAP_IRQ_REG(BD718XX_INT_ON_REQ, 0, BD718XX_INT_ON_REQ_MASK), 40 REGMAP_IRQ_REG(BD718XX_INT_STBY_REQ, 0, BD718XX_INT_STBY_REQ_MASK), 41 }; 42 43 static const struct regmap_irq_chip bd718xx_irq_chip = { 44 .name = "bd718xx-irq", 45 .irqs = bd718xx_irqs, 46 .num_irqs = ARRAY_SIZE(bd718xx_irqs), 47 .num_regs = 1, 48 .irq_reg_stride = 1, 49 .status_base = BD718XX_REG_IRQ, 50 .mask_base = BD718XX_REG_MIRQ, 51 .ack_base = BD718XX_REG_IRQ, 52 .init_ack_masked = true, 53 }; 54 55 static const struct regmap_range pmic_status_range[] = { 56 regmap_reg_range(BD718XX_REG_IRQ, BD718XX_REG_POW_STATE), 57 }; 58 59 static const struct regmap_access_table volatile_regs = { 60 .yes_ranges = &pmic_status_range[0], 61 .n_yes_ranges = ARRAY_SIZE(pmic_status_range), 62 }; 63 64 static const struct regmap_config bd718xx_regmap_config = { 65 .reg_bits = 8, 66 .val_bits = 8, 67 .volatile_table = &volatile_regs, 68 .max_register = BD718XX_MAX_REGISTER - 1, 69 .cache_type = REGCACHE_MAPLE, 70 }; 71 72 static int bd718xx_init_press_duration(struct regmap *regmap, 73 struct device *dev) 74 { 75 u32 short_press_ms, long_press_ms; 76 u32 short_press_value, long_press_value; 77 int ret; 78 79 ret = of_property_read_u32(dev->of_node, "rohm,short-press-ms", 80 &short_press_ms); 81 if (!ret) { 82 short_press_value = min(15u, (short_press_ms + 250) / 500); 83 ret = regmap_update_bits(regmap, BD718XX_REG_PWRONCONFIG0, 84 BD718XX_PWRBTN_PRESS_DURATION_MASK, 85 short_press_value); 86 if (ret) { 87 dev_err(dev, "Failed to init pwron short press\n"); 88 return ret; 89 } 90 } 91 92 ret = of_property_read_u32(dev->of_node, "rohm,long-press-ms", 93 &long_press_ms); 94 if (!ret) { 95 long_press_value = min(15u, (long_press_ms + 500) / 1000); 96 ret = regmap_update_bits(regmap, BD718XX_REG_PWRONCONFIG1, 97 BD718XX_PWRBTN_PRESS_DURATION_MASK, 98 long_press_value); 99 if (ret) { 100 dev_err(dev, "Failed to init pwron long press\n"); 101 return ret; 102 } 103 } 104 105 return 0; 106 } 107 108 static const struct property_entry bd718xx_powerkey_parent_props[] = { 109 PROPERTY_ENTRY_STRING("label", "bd718xx-pwrkey"), 110 { } 111 }; 112 113 static const struct property_entry bd718xx_powerkey_props[] = { 114 PROPERTY_ENTRY_U32("linux,code", KEY_POWER), 115 { } 116 }; 117 118 static const struct resource bd718xx_powerkey_resources[] = { 119 DEFINE_RES_IRQ_NAMED(BD718XX_INT_PWRBTN_S, "bd718xx-pwrkey"), 120 }; 121 122 #define GPIO_KEYS 0 /* Node corresponding to gpio-keys device itself */ 123 #define PWRON_KEY 1 /* Node describing power button in gpio-keys */ 124 125 static int bd718xx_i2c_register_swnodes(const struct software_node *nodes) 126 { 127 const struct software_node * const node_group[] = { 128 &nodes[GPIO_KEYS], &nodes[PWRON_KEY], NULL 129 }; 130 131 return software_node_register_node_group(node_group); 132 } 133 134 static void bd718xx_i2c_unregister_swnodes(void *data) 135 { 136 const struct software_node *nodes = data; 137 const struct software_node * const node_group[] = { 138 &nodes[GPIO_KEYS], &nodes[PWRON_KEY], NULL 139 }; 140 141 software_node_unregister_node_group(node_group); 142 } 143 144 static int bd718xx_i2c_register_pwrbutton(struct device *dev, 145 struct irq_domain *irq_domain) 146 { 147 struct mfd_cell gpio_keys_cell = { 148 .name = "gpio-keys", 149 .resources = bd718xx_powerkey_resources, 150 .num_resources = ARRAY_SIZE(bd718xx_powerkey_resources), 151 }; 152 struct software_node *nodes; 153 int ret; 154 155 nodes = devm_kcalloc(dev, 2, sizeof(*nodes), GFP_KERNEL); 156 if (!nodes) 157 return -ENOMEM; 158 159 nodes[GPIO_KEYS].name = devm_kasprintf(dev, GFP_KERNEL, "%s-power-key", dev_name(dev)); 160 if (!nodes[GPIO_KEYS].name) 161 return -ENOMEM; 162 163 nodes[GPIO_KEYS].properties = bd718xx_powerkey_parent_props; 164 165 nodes[PWRON_KEY].parent = &nodes[GPIO_KEYS]; 166 nodes[PWRON_KEY].properties = bd718xx_powerkey_props; 167 168 ret = bd718xx_i2c_register_swnodes(nodes); 169 if (ret) 170 return ret; 171 172 ret = devm_add_action_or_reset(dev, bd718xx_i2c_unregister_swnodes, nodes); 173 if (ret) 174 return ret; 175 176 gpio_keys_cell.swnode = &nodes[GPIO_KEYS]; 177 178 ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, &gpio_keys_cell, 1, 179 NULL, 0, irq_domain); 180 if (ret) 181 return dev_err_probe(dev, ret, "Failed to register power-button"); 182 183 return 0; 184 } 185 186 static int bd718xx_i2c_probe(struct i2c_client *i2c) 187 { 188 struct regmap *regmap; 189 struct regmap_irq_chip_data *irq_data; 190 struct irq_domain *irq_domain; 191 int ret; 192 unsigned int chip_type; 193 struct mfd_cell *mfd; 194 int cells; 195 196 if (!i2c->irq) { 197 dev_err(&i2c->dev, "No IRQ configured\n"); 198 return -EINVAL; 199 } 200 chip_type = (unsigned int)(uintptr_t) 201 of_device_get_match_data(&i2c->dev); 202 switch (chip_type) { 203 case ROHM_CHIP_TYPE_BD71837: 204 mfd = bd71837_mfd_cells; 205 cells = ARRAY_SIZE(bd71837_mfd_cells); 206 break; 207 case ROHM_CHIP_TYPE_BD71847: 208 mfd = bd71847_mfd_cells; 209 cells = ARRAY_SIZE(bd71847_mfd_cells); 210 break; 211 default: 212 dev_err(&i2c->dev, "Unknown device type"); 213 return -EINVAL; 214 } 215 216 regmap = devm_regmap_init_i2c(i2c, &bd718xx_regmap_config); 217 if (IS_ERR(regmap)) 218 return dev_err_probe(&i2c->dev, PTR_ERR(regmap), 219 "regmap initialization failed\n"); 220 221 ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, i2c->irq, 222 IRQF_ONESHOT, 0, &bd718xx_irq_chip, 223 &irq_data); 224 if (ret) 225 return dev_err_probe(&i2c->dev, ret, "Failed to add irq_chip\n"); 226 227 ret = bd718xx_init_press_duration(regmap, &i2c->dev); 228 if (ret) 229 return ret; 230 231 irq_domain = regmap_irq_get_domain(irq_data); 232 233 ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, 234 mfd, cells, NULL, 0, irq_domain); 235 if (ret) 236 return dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n"); 237 238 ret = bd718xx_i2c_register_pwrbutton(&i2c->dev, irq_domain); 239 if (ret) 240 return ret; 241 242 return 0; 243 } 244 245 static const struct of_device_id bd718xx_of_match[] = { 246 { 247 .compatible = "rohm,bd71837", 248 .data = (void *)ROHM_CHIP_TYPE_BD71837, 249 }, 250 { 251 .compatible = "rohm,bd71847", 252 .data = (void *)ROHM_CHIP_TYPE_BD71847, 253 }, 254 { 255 .compatible = "rohm,bd71850", 256 .data = (void *)ROHM_CHIP_TYPE_BD71847, 257 }, 258 { } 259 }; 260 MODULE_DEVICE_TABLE(of, bd718xx_of_match); 261 262 static struct i2c_driver bd718xx_i2c_driver = { 263 .driver = { 264 .name = "rohm-bd718x7", 265 .of_match_table = bd718xx_of_match, 266 }, 267 .probe = bd718xx_i2c_probe, 268 }; 269 270 static int __init bd718xx_i2c_init(void) 271 { 272 return i2c_add_driver(&bd718xx_i2c_driver); 273 } 274 275 /* Initialise early so consumer devices can complete system boot */ 276 subsys_initcall(bd718xx_i2c_init); 277 278 static void __exit bd718xx_i2c_exit(void) 279 { 280 i2c_del_driver(&bd718xx_i2c_driver); 281 } 282 module_exit(bd718xx_i2c_exit); 283 284 MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); 285 MODULE_DESCRIPTION("ROHM BD71837/BD71847 Power Management IC driver"); 286 MODULE_LICENSE("GPL"); 287