1b355f0cbSAndré Draszik // SPDX-License-Identifier: GPL-2.0+ 2b355f0cbSAndré Draszik /* 3b355f0cbSAndré Draszik * Copyright 2012 Samsung Electronics Co., Ltd 4b355f0cbSAndré Draszik * http://www.samsung.com 5b355f0cbSAndré Draszik * Copyright 2025 Linaro Ltd. 6b355f0cbSAndré Draszik * 7b355f0cbSAndré Draszik * Samsung SxM core driver 8b355f0cbSAndré Draszik */ 9b355f0cbSAndré Draszik 10b355f0cbSAndré Draszik #include <linux/device.h> 11b355f0cbSAndré Draszik #include <linux/err.h> 12b355f0cbSAndré Draszik #include <linux/export.h> 13b355f0cbSAndré Draszik #include <linux/interrupt.h> 14b355f0cbSAndré Draszik #include <linux/mfd/core.h> 15b355f0cbSAndré Draszik #include <linux/mfd/samsung/core.h> 16b355f0cbSAndré Draszik #include <linux/mfd/samsung/irq.h> 17b355f0cbSAndré Draszik #include <linux/mfd/samsung/s2mps11.h> 18b355f0cbSAndré Draszik #include <linux/mfd/samsung/s2mps13.h> 19b355f0cbSAndré Draszik #include <linux/module.h> 20b355f0cbSAndré Draszik #include <linux/of.h> 21b355f0cbSAndré Draszik #include <linux/pm.h> 22b355f0cbSAndré Draszik #include <linux/pm_runtime.h> 23b355f0cbSAndré Draszik #include <linux/regmap.h> 24b355f0cbSAndré Draszik #include "sec-core.h" 25b355f0cbSAndré Draszik 26b355f0cbSAndré Draszik static const struct mfd_cell s5m8767_devs[] = { 2755684cbbSAndré Draszik MFD_CELL_NAME("s5m8767-pmic"), 2855684cbbSAndré Draszik MFD_CELL_NAME("s5m-rtc"), 2955684cbbSAndré Draszik MFD_CELL_OF("s5m8767-clk", NULL, NULL, 0, 0, "samsung,s5m8767-clk"), 30b355f0cbSAndré Draszik }; 31b355f0cbSAndré Draszik 32b355f0cbSAndré Draszik static const struct mfd_cell s2dos05_devs[] = { 3355684cbbSAndré Draszik MFD_CELL_NAME("s2dos05-regulator"), 34b355f0cbSAndré Draszik }; 35b355f0cbSAndré Draszik 36b355f0cbSAndré Draszik static const struct mfd_cell s2mpg10_devs[] = { 37b355f0cbSAndré Draszik MFD_CELL_NAME("s2mpg10-meter"), 38b355f0cbSAndré Draszik MFD_CELL_NAME("s2mpg10-regulator"), 39b355f0cbSAndré Draszik MFD_CELL_NAME("s2mpg10-rtc"), 40b355f0cbSAndré Draszik MFD_CELL_OF("s2mpg10-clk", NULL, NULL, 0, 0, "samsung,s2mpg10-clk"), 41b355f0cbSAndré Draszik MFD_CELL_OF("s2mpg10-gpio", NULL, NULL, 0, 0, "samsung,s2mpg10-gpio"), 42b355f0cbSAndré Draszik }; 43b355f0cbSAndré Draszik 44b355f0cbSAndré Draszik static const struct mfd_cell s2mps11_devs[] = { 4555684cbbSAndré Draszik MFD_CELL_NAME("s2mps11-regulator"), 4655684cbbSAndré Draszik MFD_CELL_NAME("s2mps14-rtc"), 4755684cbbSAndré Draszik MFD_CELL_OF("s2mps11-clk", NULL, NULL, 0, 0, "samsung,s2mps11-clk"), 48b355f0cbSAndré Draszik }; 49b355f0cbSAndré Draszik 50b355f0cbSAndré Draszik static const struct mfd_cell s2mps13_devs[] = { 5155684cbbSAndré Draszik MFD_CELL_NAME("s2mps13-regulator"), 5255684cbbSAndré Draszik MFD_CELL_NAME("s2mps13-rtc"), 5355684cbbSAndré Draszik MFD_CELL_OF("s2mps13-clk", NULL, NULL, 0, 0, "samsung,s2mps13-clk"), 54b355f0cbSAndré Draszik }; 55b355f0cbSAndré Draszik 56b355f0cbSAndré Draszik static const struct mfd_cell s2mps14_devs[] = { 5755684cbbSAndré Draszik MFD_CELL_NAME("s2mps14-regulator"), 5855684cbbSAndré Draszik MFD_CELL_NAME("s2mps14-rtc"), 5955684cbbSAndré Draszik MFD_CELL_OF("s2mps14-clk", NULL, NULL, 0, 0, "samsung,s2mps14-clk"), 60b355f0cbSAndré Draszik }; 61b355f0cbSAndré Draszik 62b355f0cbSAndré Draszik static const struct mfd_cell s2mps15_devs[] = { 6355684cbbSAndré Draszik MFD_CELL_NAME("s2mps15-regulator"), 6455684cbbSAndré Draszik MFD_CELL_NAME("s2mps15-rtc"), 6555684cbbSAndré Draszik MFD_CELL_OF("s2mps13-clk", NULL, NULL, 0, 0, "samsung,s2mps13-clk"), 66b355f0cbSAndré Draszik }; 67b355f0cbSAndré Draszik 68b355f0cbSAndré Draszik static const struct mfd_cell s2mpa01_devs[] = { 6955684cbbSAndré Draszik MFD_CELL_NAME("s2mpa01-pmic"), 7055684cbbSAndré Draszik MFD_CELL_NAME("s2mps14-rtc"), 71b355f0cbSAndré Draszik }; 72b355f0cbSAndré Draszik 73b355f0cbSAndré Draszik static const struct mfd_cell s2mpu02_devs[] = { 7455684cbbSAndré Draszik MFD_CELL_NAME("s2mpu02-regulator"), 75b355f0cbSAndré Draszik }; 76b355f0cbSAndré Draszik 77b355f0cbSAndré Draszik static const struct mfd_cell s2mpu05_devs[] = { 7855684cbbSAndré Draszik MFD_CELL_NAME("s2mpu05-regulator"), 7955684cbbSAndré Draszik MFD_CELL_NAME("s2mps15-rtc"), 80b355f0cbSAndré Draszik }; 81b355f0cbSAndré Draszik 82b355f0cbSAndré Draszik static void sec_pmic_dump_rev(struct sec_pmic_dev *sec_pmic) 83b355f0cbSAndré Draszik { 84b355f0cbSAndré Draszik unsigned int val; 85b355f0cbSAndré Draszik 86b355f0cbSAndré Draszik /* For s2mpg1x, the revision is in a different regmap */ 87b355f0cbSAndré Draszik if (sec_pmic->device_type == S2MPG10) 88b355f0cbSAndré Draszik return; 89b355f0cbSAndré Draszik 90b355f0cbSAndré Draszik /* For each device type, the REG_ID is always the first register */ 91b355f0cbSAndré Draszik if (!regmap_read(sec_pmic->regmap_pmic, S2MPS11_REG_ID, &val)) 92b355f0cbSAndré Draszik dev_dbg(sec_pmic->dev, "Revision: 0x%x\n", val); 93b355f0cbSAndré Draszik } 94b355f0cbSAndré Draszik 95b355f0cbSAndré Draszik static void sec_pmic_configure(struct sec_pmic_dev *sec_pmic) 96b355f0cbSAndré Draszik { 97b355f0cbSAndré Draszik int err; 98b355f0cbSAndré Draszik 99b355f0cbSAndré Draszik if (sec_pmic->device_type != S2MPS13X) 100b355f0cbSAndré Draszik return; 101b355f0cbSAndré Draszik 102b355f0cbSAndré Draszik if (sec_pmic->pdata->disable_wrstbi) { 103b355f0cbSAndré Draszik /* 104b355f0cbSAndré Draszik * If WRSTBI pin is pulled down this feature must be disabled 105b355f0cbSAndré Draszik * because each Suspend to RAM will trigger buck voltage reset 106b355f0cbSAndré Draszik * to default values. 107b355f0cbSAndré Draszik */ 108b355f0cbSAndré Draszik err = regmap_update_bits(sec_pmic->regmap_pmic, 109b355f0cbSAndré Draszik S2MPS13_REG_WRSTBI, 110b355f0cbSAndré Draszik S2MPS13_REG_WRSTBI_MASK, 0x0); 111b355f0cbSAndré Draszik if (err) 112b355f0cbSAndré Draszik dev_warn(sec_pmic->dev, 113b355f0cbSAndré Draszik "Cannot initialize WRSTBI config: %d\n", 114b355f0cbSAndré Draszik err); 115b355f0cbSAndré Draszik } 116b355f0cbSAndré Draszik } 117b355f0cbSAndré Draszik 118b355f0cbSAndré Draszik /* 119b355f0cbSAndré Draszik * Only the common platform data elements for s5m8767 are parsed here from the 120b355f0cbSAndré Draszik * device tree. Other sub-modules of s5m8767 such as pmic, rtc , charger and 121b355f0cbSAndré Draszik * others have to parse their own platform data elements from device tree. 122b355f0cbSAndré Draszik * 123b355f0cbSAndré Draszik * The s5m8767 platform data structure is instantiated here and the drivers for 124b355f0cbSAndré Draszik * the sub-modules need not instantiate another instance while parsing their 125b355f0cbSAndré Draszik * platform data. 126b355f0cbSAndré Draszik */ 127b355f0cbSAndré Draszik static struct sec_platform_data * 128b355f0cbSAndré Draszik sec_pmic_parse_dt_pdata(struct device *dev) 129b355f0cbSAndré Draszik { 130b355f0cbSAndré Draszik struct sec_platform_data *pd; 131b355f0cbSAndré Draszik 132b355f0cbSAndré Draszik pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); 133b355f0cbSAndré Draszik if (!pd) 134b355f0cbSAndré Draszik return ERR_PTR(-ENOMEM); 135b355f0cbSAndré Draszik 136b355f0cbSAndré Draszik pd->manual_poweroff = of_property_read_bool(dev->of_node, 137b355f0cbSAndré Draszik "samsung,s2mps11-acokb-ground"); 138b355f0cbSAndré Draszik pd->disable_wrstbi = of_property_read_bool(dev->of_node, 139b355f0cbSAndré Draszik "samsung,s2mps11-wrstbi-ground"); 140b355f0cbSAndré Draszik return pd; 141b355f0cbSAndré Draszik } 142b355f0cbSAndré Draszik 143adf91d9eSAndré Draszik int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq, 144adf91d9eSAndré Draszik struct regmap *regmap, struct i2c_client *client) 145b355f0cbSAndré Draszik { 146b355f0cbSAndré Draszik struct sec_platform_data *pdata; 147b355f0cbSAndré Draszik const struct mfd_cell *sec_devs; 148b355f0cbSAndré Draszik struct sec_pmic_dev *sec_pmic; 149b355f0cbSAndré Draszik int ret, num_sec_devs; 150b355f0cbSAndré Draszik 1512b897a1cSAndré Draszik sec_pmic = devm_kzalloc(dev, sizeof(*sec_pmic), GFP_KERNEL); 152856c6514SAndré Draszik if (!sec_pmic) 153b355f0cbSAndré Draszik return -ENOMEM; 154b355f0cbSAndré Draszik 155b355f0cbSAndré Draszik dev_set_drvdata(dev, sec_pmic); 156b355f0cbSAndré Draszik sec_pmic->dev = dev; 157b355f0cbSAndré Draszik sec_pmic->device_type = device_type; 158b355f0cbSAndré Draszik sec_pmic->i2c = client; 159b355f0cbSAndré Draszik sec_pmic->irq = irq; 160b355f0cbSAndré Draszik sec_pmic->regmap_pmic = regmap; 161b355f0cbSAndré Draszik 162b355f0cbSAndré Draszik pdata = sec_pmic_parse_dt_pdata(sec_pmic->dev); 163b355f0cbSAndré Draszik if (IS_ERR(pdata)) { 164b355f0cbSAndré Draszik ret = PTR_ERR(pdata); 165b355f0cbSAndré Draszik return ret; 166b355f0cbSAndré Draszik } 167b355f0cbSAndré Draszik 168b355f0cbSAndré Draszik sec_pmic->pdata = pdata; 169b355f0cbSAndré Draszik 1700c33784aSAndré Draszik ret = sec_irq_init(sec_pmic); 1710c33784aSAndré Draszik if (ret) 1720c33784aSAndré Draszik return ret; 173b355f0cbSAndré Draszik 174b355f0cbSAndré Draszik pm_runtime_set_active(sec_pmic->dev); 175b355f0cbSAndré Draszik 176b355f0cbSAndré Draszik switch (sec_pmic->device_type) { 177b355f0cbSAndré Draszik case S5M8767X: 178b355f0cbSAndré Draszik sec_devs = s5m8767_devs; 179b355f0cbSAndré Draszik num_sec_devs = ARRAY_SIZE(s5m8767_devs); 180b355f0cbSAndré Draszik break; 181b355f0cbSAndré Draszik case S2DOS05: 182b355f0cbSAndré Draszik sec_devs = s2dos05_devs; 183b355f0cbSAndré Draszik num_sec_devs = ARRAY_SIZE(s2dos05_devs); 184b355f0cbSAndré Draszik break; 185b355f0cbSAndré Draszik case S2MPA01: 186b355f0cbSAndré Draszik sec_devs = s2mpa01_devs; 187b355f0cbSAndré Draszik num_sec_devs = ARRAY_SIZE(s2mpa01_devs); 188b355f0cbSAndré Draszik break; 189b355f0cbSAndré Draszik case S2MPG10: 190b355f0cbSAndré Draszik sec_devs = s2mpg10_devs; 191b355f0cbSAndré Draszik num_sec_devs = ARRAY_SIZE(s2mpg10_devs); 192b355f0cbSAndré Draszik break; 193b355f0cbSAndré Draszik case S2MPS11X: 194b355f0cbSAndré Draszik sec_devs = s2mps11_devs; 195b355f0cbSAndré Draszik num_sec_devs = ARRAY_SIZE(s2mps11_devs); 196b355f0cbSAndré Draszik break; 197b355f0cbSAndré Draszik case S2MPS13X: 198b355f0cbSAndré Draszik sec_devs = s2mps13_devs; 199b355f0cbSAndré Draszik num_sec_devs = ARRAY_SIZE(s2mps13_devs); 200b355f0cbSAndré Draszik break; 201b355f0cbSAndré Draszik case S2MPS14X: 202b355f0cbSAndré Draszik sec_devs = s2mps14_devs; 203b355f0cbSAndré Draszik num_sec_devs = ARRAY_SIZE(s2mps14_devs); 204b355f0cbSAndré Draszik break; 205b355f0cbSAndré Draszik case S2MPS15X: 206b355f0cbSAndré Draszik sec_devs = s2mps15_devs; 207b355f0cbSAndré Draszik num_sec_devs = ARRAY_SIZE(s2mps15_devs); 208b355f0cbSAndré Draszik break; 209b355f0cbSAndré Draszik case S2MPU02: 210b355f0cbSAndré Draszik sec_devs = s2mpu02_devs; 211b355f0cbSAndré Draszik num_sec_devs = ARRAY_SIZE(s2mpu02_devs); 212b355f0cbSAndré Draszik break; 213b355f0cbSAndré Draszik case S2MPU05: 214b355f0cbSAndré Draszik sec_devs = s2mpu05_devs; 215b355f0cbSAndré Draszik num_sec_devs = ARRAY_SIZE(s2mpu05_devs); 216b355f0cbSAndré Draszik break; 217b355f0cbSAndré Draszik default: 218176a3068SAndré Draszik return dev_err_probe(sec_pmic->dev, -EINVAL, 219adf91d9eSAndré Draszik "Unsupported device type %d\n", 220b355f0cbSAndré Draszik sec_pmic->device_type); 221b355f0cbSAndré Draszik } 222b355f0cbSAndré Draszik ret = devm_mfd_add_devices(sec_pmic->dev, -1, sec_devs, num_sec_devs, 223b355f0cbSAndré Draszik NULL, 0, NULL); 224b355f0cbSAndré Draszik if (ret) 225b355f0cbSAndré Draszik return ret; 226b355f0cbSAndré Draszik 227b355f0cbSAndré Draszik sec_pmic_configure(sec_pmic); 228b355f0cbSAndré Draszik sec_pmic_dump_rev(sec_pmic); 229b355f0cbSAndré Draszik 230b355f0cbSAndré Draszik return ret; 231b355f0cbSAndré Draszik } 232b355f0cbSAndré Draszik EXPORT_SYMBOL_GPL(sec_pmic_probe); 233b355f0cbSAndré Draszik 234b355f0cbSAndré Draszik void sec_pmic_shutdown(struct device *dev) 235b355f0cbSAndré Draszik { 236b355f0cbSAndré Draszik struct sec_pmic_dev *sec_pmic = dev_get_drvdata(dev); 237b355f0cbSAndré Draszik unsigned int reg, mask; 238b355f0cbSAndré Draszik 239b355f0cbSAndré Draszik if (!sec_pmic->pdata->manual_poweroff) 240b355f0cbSAndré Draszik return; 241b355f0cbSAndré Draszik 242b355f0cbSAndré Draszik switch (sec_pmic->device_type) { 243b355f0cbSAndré Draszik case S2MPS11X: 244b355f0cbSAndré Draszik reg = S2MPS11_REG_CTRL1; 245b355f0cbSAndré Draszik mask = S2MPS11_CTRL1_PWRHOLD_MASK; 246b355f0cbSAndré Draszik break; 247b355f0cbSAndré Draszik default: 248b355f0cbSAndré Draszik /* 249b355f0cbSAndré Draszik * Currently only one board with S2MPS11 needs this, so just 250b355f0cbSAndré Draszik * ignore the rest. 251b355f0cbSAndré Draszik */ 252b355f0cbSAndré Draszik dev_warn(sec_pmic->dev, 253adf91d9eSAndré Draszik "Unsupported device %d for manual power off\n", 254b355f0cbSAndré Draszik sec_pmic->device_type); 255b355f0cbSAndré Draszik return; 256b355f0cbSAndré Draszik } 257b355f0cbSAndré Draszik 258b355f0cbSAndré Draszik regmap_update_bits(sec_pmic->regmap_pmic, reg, mask, 0); 259b355f0cbSAndré Draszik } 260b355f0cbSAndré Draszik EXPORT_SYMBOL_GPL(sec_pmic_shutdown); 261b355f0cbSAndré Draszik 262b355f0cbSAndré Draszik static int sec_pmic_suspend(struct device *dev) 263b355f0cbSAndré Draszik { 264b355f0cbSAndré Draszik struct sec_pmic_dev *sec_pmic = dev_get_drvdata(dev); 265b355f0cbSAndré Draszik 266b355f0cbSAndré Draszik if (device_may_wakeup(dev)) 267b355f0cbSAndré Draszik enable_irq_wake(sec_pmic->irq); 268b355f0cbSAndré Draszik /* 269b355f0cbSAndré Draszik * PMIC IRQ must be disabled during suspend for RTC alarm 270b355f0cbSAndré Draszik * to work properly. 271b355f0cbSAndré Draszik * When device is woken up from suspend, an 272b355f0cbSAndré Draszik * interrupt occurs before resuming I2C bus controller. 273b355f0cbSAndré Draszik * The interrupt is handled by regmap_irq_thread which tries 274b355f0cbSAndré Draszik * to read RTC registers. This read fails (I2C is still 275b355f0cbSAndré Draszik * suspended) and RTC Alarm interrupt is disabled. 276b355f0cbSAndré Draszik */ 277b355f0cbSAndré Draszik disable_irq(sec_pmic->irq); 278b355f0cbSAndré Draszik 279b355f0cbSAndré Draszik return 0; 280b355f0cbSAndré Draszik } 281b355f0cbSAndré Draszik 282b355f0cbSAndré Draszik static int sec_pmic_resume(struct device *dev) 283b355f0cbSAndré Draszik { 284b355f0cbSAndré Draszik struct sec_pmic_dev *sec_pmic = dev_get_drvdata(dev); 285b355f0cbSAndré Draszik 286b355f0cbSAndré Draszik if (device_may_wakeup(dev)) 287b355f0cbSAndré Draszik disable_irq_wake(sec_pmic->irq); 288b355f0cbSAndré Draszik enable_irq(sec_pmic->irq); 289b355f0cbSAndré Draszik 290b355f0cbSAndré Draszik return 0; 291b355f0cbSAndré Draszik } 292b355f0cbSAndré Draszik 293b355f0cbSAndré Draszik DEFINE_SIMPLE_DEV_PM_OPS(sec_pmic_pm_ops, sec_pmic_suspend, sec_pmic_resume); 294b355f0cbSAndré Draszik EXPORT_SYMBOL_GPL(sec_pmic_pm_ops); 295b355f0cbSAndré Draszik 296b355f0cbSAndré Draszik MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); 297b355f0cbSAndré Draszik MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>"); 298b355f0cbSAndré Draszik MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); 299*217c445cSAndré Draszik MODULE_AUTHOR("André Draszik <andre.draszik@linaro.org>"); 300b355f0cbSAndré Draszik MODULE_DESCRIPTION("Core driver for the Samsung S5M"); 301b355f0cbSAndré Draszik MODULE_LICENSE("GPL"); 302