162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// wm8350.c -- Voltage and current regulation for the Wolfson WM8350 PMIC 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright 2007, 2008 Wolfson Microelectronics PLC. 662306a36Sopenharmony_ci// 762306a36Sopenharmony_ci// Author: Liam Girdwood 862306a36Sopenharmony_ci// linux@wolfsonmicro.com 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/moduleparam.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/bitops.h> 1462306a36Sopenharmony_ci#include <linux/err.h> 1562306a36Sopenharmony_ci#include <linux/i2c.h> 1662306a36Sopenharmony_ci#include <linux/mfd/wm8350/core.h> 1762306a36Sopenharmony_ci#include <linux/mfd/wm8350/pmic.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci#include <linux/regulator/driver.h> 2062306a36Sopenharmony_ci#include <linux/regulator/machine.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Maximum value possible for VSEL */ 2362306a36Sopenharmony_ci#define WM8350_DCDC_MAX_VSEL 0x66 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* Microamps */ 2662306a36Sopenharmony_cistatic const unsigned int isink_cur[] = { 2762306a36Sopenharmony_ci 4, 2862306a36Sopenharmony_ci 5, 2962306a36Sopenharmony_ci 6, 3062306a36Sopenharmony_ci 7, 3162306a36Sopenharmony_ci 8, 3262306a36Sopenharmony_ci 10, 3362306a36Sopenharmony_ci 11, 3462306a36Sopenharmony_ci 14, 3562306a36Sopenharmony_ci 16, 3662306a36Sopenharmony_ci 19, 3762306a36Sopenharmony_ci 23, 3862306a36Sopenharmony_ci 27, 3962306a36Sopenharmony_ci 32, 4062306a36Sopenharmony_ci 39, 4162306a36Sopenharmony_ci 46, 4262306a36Sopenharmony_ci 54, 4362306a36Sopenharmony_ci 65, 4462306a36Sopenharmony_ci 77, 4562306a36Sopenharmony_ci 92, 4662306a36Sopenharmony_ci 109, 4762306a36Sopenharmony_ci 130, 4862306a36Sopenharmony_ci 154, 4962306a36Sopenharmony_ci 183, 5062306a36Sopenharmony_ci 218, 5162306a36Sopenharmony_ci 259, 5262306a36Sopenharmony_ci 308, 5362306a36Sopenharmony_ci 367, 5462306a36Sopenharmony_ci 436, 5562306a36Sopenharmony_ci 518, 5662306a36Sopenharmony_ci 616, 5762306a36Sopenharmony_ci 733, 5862306a36Sopenharmony_ci 872, 5962306a36Sopenharmony_ci 1037, 6062306a36Sopenharmony_ci 1233, 6162306a36Sopenharmony_ci 1466, 6262306a36Sopenharmony_ci 1744, 6362306a36Sopenharmony_ci 2073, 6462306a36Sopenharmony_ci 2466, 6562306a36Sopenharmony_ci 2933, 6662306a36Sopenharmony_ci 3487, 6762306a36Sopenharmony_ci 4147, 6862306a36Sopenharmony_ci 4932, 6962306a36Sopenharmony_ci 5865, 7062306a36Sopenharmony_ci 6975, 7162306a36Sopenharmony_ci 8294, 7262306a36Sopenharmony_ci 9864, 7362306a36Sopenharmony_ci 11730, 7462306a36Sopenharmony_ci 13949, 7562306a36Sopenharmony_ci 16589, 7662306a36Sopenharmony_ci 19728, 7762306a36Sopenharmony_ci 23460, 7862306a36Sopenharmony_ci 27899, 7962306a36Sopenharmony_ci 33178, 8062306a36Sopenharmony_ci 39455, 8162306a36Sopenharmony_ci 46920, 8262306a36Sopenharmony_ci 55798, 8362306a36Sopenharmony_ci 66355, 8462306a36Sopenharmony_ci 78910, 8562306a36Sopenharmony_ci 93840, 8662306a36Sopenharmony_ci 111596, 8762306a36Sopenharmony_ci 132710, 8862306a36Sopenharmony_ci 157820, 8962306a36Sopenharmony_ci 187681, 9062306a36Sopenharmony_ci 223191 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* turn on ISINK followed by DCDC */ 9462306a36Sopenharmony_cistatic int wm8350_isink_enable(struct regulator_dev *rdev) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct wm8350 *wm8350 = rdev_get_drvdata(rdev); 9762306a36Sopenharmony_ci int isink = rdev_get_id(rdev); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci switch (isink) { 10062306a36Sopenharmony_ci case WM8350_ISINK_A: 10162306a36Sopenharmony_ci switch (wm8350->pmic.isink_A_dcdc) { 10262306a36Sopenharmony_ci case WM8350_DCDC_2: 10362306a36Sopenharmony_ci case WM8350_DCDC_5: 10462306a36Sopenharmony_ci wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7, 10562306a36Sopenharmony_ci WM8350_CS1_ENA); 10662306a36Sopenharmony_ci wm8350_set_bits(wm8350, WM8350_CSA_FLASH_CONTROL, 10762306a36Sopenharmony_ci WM8350_CS1_DRIVE); 10862306a36Sopenharmony_ci wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 10962306a36Sopenharmony_ci 1 << (wm8350->pmic.isink_A_dcdc - 11062306a36Sopenharmony_ci WM8350_DCDC_1)); 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci default: 11362306a36Sopenharmony_ci return -EINVAL; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci case WM8350_ISINK_B: 11762306a36Sopenharmony_ci switch (wm8350->pmic.isink_B_dcdc) { 11862306a36Sopenharmony_ci case WM8350_DCDC_2: 11962306a36Sopenharmony_ci case WM8350_DCDC_5: 12062306a36Sopenharmony_ci wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7, 12162306a36Sopenharmony_ci WM8350_CS2_ENA); 12262306a36Sopenharmony_ci wm8350_set_bits(wm8350, WM8350_CSB_FLASH_CONTROL, 12362306a36Sopenharmony_ci WM8350_CS2_DRIVE); 12462306a36Sopenharmony_ci wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 12562306a36Sopenharmony_ci 1 << (wm8350->pmic.isink_B_dcdc - 12662306a36Sopenharmony_ci WM8350_DCDC_1)); 12762306a36Sopenharmony_ci break; 12862306a36Sopenharmony_ci default: 12962306a36Sopenharmony_ci return -EINVAL; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci default: 13362306a36Sopenharmony_ci return -EINVAL; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int wm8350_isink_disable(struct regulator_dev *rdev) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct wm8350 *wm8350 = rdev_get_drvdata(rdev); 14162306a36Sopenharmony_ci int isink = rdev_get_id(rdev); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci switch (isink) { 14462306a36Sopenharmony_ci case WM8350_ISINK_A: 14562306a36Sopenharmony_ci switch (wm8350->pmic.isink_A_dcdc) { 14662306a36Sopenharmony_ci case WM8350_DCDC_2: 14762306a36Sopenharmony_ci case WM8350_DCDC_5: 14862306a36Sopenharmony_ci wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 14962306a36Sopenharmony_ci 1 << (wm8350->pmic.isink_A_dcdc - 15062306a36Sopenharmony_ci WM8350_DCDC_1)); 15162306a36Sopenharmony_ci wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_7, 15262306a36Sopenharmony_ci WM8350_CS1_ENA); 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci default: 15562306a36Sopenharmony_ci return -EINVAL; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci case WM8350_ISINK_B: 15962306a36Sopenharmony_ci switch (wm8350->pmic.isink_B_dcdc) { 16062306a36Sopenharmony_ci case WM8350_DCDC_2: 16162306a36Sopenharmony_ci case WM8350_DCDC_5: 16262306a36Sopenharmony_ci wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 16362306a36Sopenharmony_ci 1 << (wm8350->pmic.isink_B_dcdc - 16462306a36Sopenharmony_ci WM8350_DCDC_1)); 16562306a36Sopenharmony_ci wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_7, 16662306a36Sopenharmony_ci WM8350_CS2_ENA); 16762306a36Sopenharmony_ci break; 16862306a36Sopenharmony_ci default: 16962306a36Sopenharmony_ci return -EINVAL; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci default: 17362306a36Sopenharmony_ci return -EINVAL; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic int wm8350_isink_is_enabled(struct regulator_dev *rdev) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct wm8350 *wm8350 = rdev_get_drvdata(rdev); 18162306a36Sopenharmony_ci int isink = rdev_get_id(rdev); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci switch (isink) { 18462306a36Sopenharmony_ci case WM8350_ISINK_A: 18562306a36Sopenharmony_ci return wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) & 18662306a36Sopenharmony_ci 0x8000; 18762306a36Sopenharmony_ci case WM8350_ISINK_B: 18862306a36Sopenharmony_ci return wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) & 18962306a36Sopenharmony_ci 0x8000; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci return -EINVAL; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int wm8350_isink_enable_time(struct regulator_dev *rdev) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct wm8350 *wm8350 = rdev_get_drvdata(rdev); 19762306a36Sopenharmony_ci int isink = rdev_get_id(rdev); 19862306a36Sopenharmony_ci int reg; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci switch (isink) { 20162306a36Sopenharmony_ci case WM8350_ISINK_A: 20262306a36Sopenharmony_ci reg = wm8350_reg_read(wm8350, WM8350_CSA_FLASH_CONTROL); 20362306a36Sopenharmony_ci break; 20462306a36Sopenharmony_ci case WM8350_ISINK_B: 20562306a36Sopenharmony_ci reg = wm8350_reg_read(wm8350, WM8350_CSB_FLASH_CONTROL); 20662306a36Sopenharmony_ci break; 20762306a36Sopenharmony_ci default: 20862306a36Sopenharmony_ci return -EINVAL; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (reg & WM8350_CS1_FLASH_MODE) { 21262306a36Sopenharmony_ci switch (reg & WM8350_CS1_ON_RAMP_MASK) { 21362306a36Sopenharmony_ci case 0: 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci case 1: 21662306a36Sopenharmony_ci return 1950; 21762306a36Sopenharmony_ci case 2: 21862306a36Sopenharmony_ci return 3910; 21962306a36Sopenharmony_ci case 3: 22062306a36Sopenharmony_ci return 7800; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci } else { 22362306a36Sopenharmony_ci switch (reg & WM8350_CS1_ON_RAMP_MASK) { 22462306a36Sopenharmony_ci case 0: 22562306a36Sopenharmony_ci return 0; 22662306a36Sopenharmony_ci case 1: 22762306a36Sopenharmony_ci return 250000; 22862306a36Sopenharmony_ci case 2: 22962306a36Sopenharmony_ci return 500000; 23062306a36Sopenharmony_ci case 3: 23162306a36Sopenharmony_ci return 1000000; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return -EINVAL; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ciint wm8350_isink_set_flash(struct wm8350 *wm8350, int isink, u16 mode, 24062306a36Sopenharmony_ci u16 trigger, u16 duration, u16 on_ramp, u16 off_ramp, 24162306a36Sopenharmony_ci u16 drive) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci switch (isink) { 24462306a36Sopenharmony_ci case WM8350_ISINK_A: 24562306a36Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_CSA_FLASH_CONTROL, 24662306a36Sopenharmony_ci (mode ? WM8350_CS1_FLASH_MODE : 0) | 24762306a36Sopenharmony_ci (trigger ? WM8350_CS1_TRIGSRC : 0) | 24862306a36Sopenharmony_ci duration | on_ramp | off_ramp | drive); 24962306a36Sopenharmony_ci break; 25062306a36Sopenharmony_ci case WM8350_ISINK_B: 25162306a36Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_CSB_FLASH_CONTROL, 25262306a36Sopenharmony_ci (mode ? WM8350_CS2_FLASH_MODE : 0) | 25362306a36Sopenharmony_ci (trigger ? WM8350_CS2_TRIGSRC : 0) | 25462306a36Sopenharmony_ci duration | on_ramp | off_ramp | drive); 25562306a36Sopenharmony_ci break; 25662306a36Sopenharmony_ci default: 25762306a36Sopenharmony_ci return -EINVAL; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci return 0; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wm8350_isink_set_flash); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int wm8350_dcdc_set_suspend_voltage(struct regulator_dev *rdev, int uV) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct wm8350 *wm8350 = rdev_get_drvdata(rdev); 26662306a36Sopenharmony_ci int sel, volt_reg, dcdc = rdev_get_id(rdev); 26762306a36Sopenharmony_ci u16 val; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, dcdc, uV / 1000); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci switch (dcdc) { 27262306a36Sopenharmony_ci case WM8350_DCDC_1: 27362306a36Sopenharmony_ci volt_reg = WM8350_DCDC1_LOW_POWER; 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci case WM8350_DCDC_3: 27662306a36Sopenharmony_ci volt_reg = WM8350_DCDC3_LOW_POWER; 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci case WM8350_DCDC_4: 27962306a36Sopenharmony_ci volt_reg = WM8350_DCDC4_LOW_POWER; 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci case WM8350_DCDC_6: 28262306a36Sopenharmony_ci volt_reg = WM8350_DCDC6_LOW_POWER; 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci case WM8350_DCDC_2: 28562306a36Sopenharmony_ci case WM8350_DCDC_5: 28662306a36Sopenharmony_ci default: 28762306a36Sopenharmony_ci return -EINVAL; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci sel = regulator_map_voltage_linear(rdev, uV, uV); 29162306a36Sopenharmony_ci if (sel < 0) 29262306a36Sopenharmony_ci return sel; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* all DCDCs have same mV bits */ 29562306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK; 29662306a36Sopenharmony_ci wm8350_reg_write(wm8350, volt_reg, val | sel); 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic int wm8350_dcdc_set_suspend_enable(struct regulator_dev *rdev) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct wm8350 *wm8350 = rdev_get_drvdata(rdev); 30362306a36Sopenharmony_ci int dcdc = rdev_get_id(rdev); 30462306a36Sopenharmony_ci u16 val; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci switch (dcdc) { 30762306a36Sopenharmony_ci case WM8350_DCDC_1: 30862306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER) 30962306a36Sopenharmony_ci & ~WM8350_DCDC_HIB_MODE_MASK; 31062306a36Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER, 31162306a36Sopenharmony_ci val | wm8350->pmic.dcdc1_hib_mode); 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci case WM8350_DCDC_3: 31462306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER) 31562306a36Sopenharmony_ci & ~WM8350_DCDC_HIB_MODE_MASK; 31662306a36Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER, 31762306a36Sopenharmony_ci val | wm8350->pmic.dcdc3_hib_mode); 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci case WM8350_DCDC_4: 32062306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER) 32162306a36Sopenharmony_ci & ~WM8350_DCDC_HIB_MODE_MASK; 32262306a36Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER, 32362306a36Sopenharmony_ci val | wm8350->pmic.dcdc4_hib_mode); 32462306a36Sopenharmony_ci break; 32562306a36Sopenharmony_ci case WM8350_DCDC_6: 32662306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER) 32762306a36Sopenharmony_ci & ~WM8350_DCDC_HIB_MODE_MASK; 32862306a36Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER, 32962306a36Sopenharmony_ci val | wm8350->pmic.dcdc6_hib_mode); 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci case WM8350_DCDC_2: 33262306a36Sopenharmony_ci case WM8350_DCDC_5: 33362306a36Sopenharmony_ci default: 33462306a36Sopenharmony_ci return -EINVAL; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic int wm8350_dcdc_set_suspend_disable(struct regulator_dev *rdev) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct wm8350 *wm8350 = rdev_get_drvdata(rdev); 34362306a36Sopenharmony_ci int dcdc = rdev_get_id(rdev); 34462306a36Sopenharmony_ci u16 val; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci switch (dcdc) { 34762306a36Sopenharmony_ci case WM8350_DCDC_1: 34862306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER); 34962306a36Sopenharmony_ci wm8350->pmic.dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; 35062306a36Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER, 35162306a36Sopenharmony_ci val | WM8350_DCDC_HIB_MODE_DIS); 35262306a36Sopenharmony_ci break; 35362306a36Sopenharmony_ci case WM8350_DCDC_3: 35462306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER); 35562306a36Sopenharmony_ci wm8350->pmic.dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; 35662306a36Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER, 35762306a36Sopenharmony_ci val | WM8350_DCDC_HIB_MODE_DIS); 35862306a36Sopenharmony_ci break; 35962306a36Sopenharmony_ci case WM8350_DCDC_4: 36062306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER); 36162306a36Sopenharmony_ci wm8350->pmic.dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; 36262306a36Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER, 36362306a36Sopenharmony_ci val | WM8350_DCDC_HIB_MODE_DIS); 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci case WM8350_DCDC_6: 36662306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER); 36762306a36Sopenharmony_ci wm8350->pmic.dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; 36862306a36Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER, 36962306a36Sopenharmony_ci val | WM8350_DCDC_HIB_MODE_DIS); 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci case WM8350_DCDC_2: 37262306a36Sopenharmony_ci case WM8350_DCDC_5: 37362306a36Sopenharmony_ci default: 37462306a36Sopenharmony_ci return -EINVAL; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return 0; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic int wm8350_dcdc25_set_suspend_enable(struct regulator_dev *rdev) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct wm8350 *wm8350 = rdev_get_drvdata(rdev); 38362306a36Sopenharmony_ci int dcdc = rdev_get_id(rdev); 38462306a36Sopenharmony_ci u16 val; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci switch (dcdc) { 38762306a36Sopenharmony_ci case WM8350_DCDC_2: 38862306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL) 38962306a36Sopenharmony_ci & ~WM8350_DC2_HIB_MODE_MASK; 39062306a36Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val | 39162306a36Sopenharmony_ci (WM8350_DC2_HIB_MODE_ACTIVE << WM8350_DC2_HIB_MODE_SHIFT)); 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci case WM8350_DCDC_5: 39462306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL) 39562306a36Sopenharmony_ci & ~WM8350_DC5_HIB_MODE_MASK; 39662306a36Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val | 39762306a36Sopenharmony_ci (WM8350_DC5_HIB_MODE_ACTIVE << WM8350_DC5_HIB_MODE_SHIFT)); 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci default: 40062306a36Sopenharmony_ci return -EINVAL; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic int wm8350_dcdc25_set_suspend_disable(struct regulator_dev *rdev) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct wm8350 *wm8350 = rdev_get_drvdata(rdev); 40862306a36Sopenharmony_ci int dcdc = rdev_get_id(rdev); 40962306a36Sopenharmony_ci u16 val; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci switch (dcdc) { 41262306a36Sopenharmony_ci case WM8350_DCDC_2: 41362306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL) 41462306a36Sopenharmony_ci & ~WM8350_DC2_HIB_MODE_MASK; 41562306a36Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val | 41662306a36Sopenharmony_ci (WM8350_DC2_HIB_MODE_DISABLE << WM8350_DC2_HIB_MODE_SHIFT)); 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci case WM8350_DCDC_5: 41962306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL) 42062306a36Sopenharmony_ci & ~WM8350_DC5_HIB_MODE_MASK; 42162306a36Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val | 42262306a36Sopenharmony_ci (WM8350_DC5_HIB_MODE_DISABLE << WM8350_DC5_HIB_MODE_SHIFT)); 42362306a36Sopenharmony_ci break; 42462306a36Sopenharmony_ci default: 42562306a36Sopenharmony_ci return -EINVAL; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic int wm8350_dcdc_set_suspend_mode(struct regulator_dev *rdev, 43162306a36Sopenharmony_ci unsigned int mode) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct wm8350 *wm8350 = rdev_get_drvdata(rdev); 43462306a36Sopenharmony_ci int dcdc = rdev_get_id(rdev); 43562306a36Sopenharmony_ci u16 *hib_mode; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci switch (dcdc) { 43862306a36Sopenharmony_ci case WM8350_DCDC_1: 43962306a36Sopenharmony_ci hib_mode = &wm8350->pmic.dcdc1_hib_mode; 44062306a36Sopenharmony_ci break; 44162306a36Sopenharmony_ci case WM8350_DCDC_3: 44262306a36Sopenharmony_ci hib_mode = &wm8350->pmic.dcdc3_hib_mode; 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci case WM8350_DCDC_4: 44562306a36Sopenharmony_ci hib_mode = &wm8350->pmic.dcdc4_hib_mode; 44662306a36Sopenharmony_ci break; 44762306a36Sopenharmony_ci case WM8350_DCDC_6: 44862306a36Sopenharmony_ci hib_mode = &wm8350->pmic.dcdc6_hib_mode; 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci case WM8350_DCDC_2: 45162306a36Sopenharmony_ci case WM8350_DCDC_5: 45262306a36Sopenharmony_ci default: 45362306a36Sopenharmony_ci return -EINVAL; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci switch (mode) { 45762306a36Sopenharmony_ci case REGULATOR_MODE_NORMAL: 45862306a36Sopenharmony_ci *hib_mode = WM8350_DCDC_HIB_MODE_IMAGE; 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci case REGULATOR_MODE_IDLE: 46162306a36Sopenharmony_ci *hib_mode = WM8350_DCDC_HIB_MODE_STANDBY; 46262306a36Sopenharmony_ci break; 46362306a36Sopenharmony_ci case REGULATOR_MODE_STANDBY: 46462306a36Sopenharmony_ci *hib_mode = WM8350_DCDC_HIB_MODE_LDO_IM; 46562306a36Sopenharmony_ci break; 46662306a36Sopenharmony_ci default: 46762306a36Sopenharmony_ci return -EINVAL; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return 0; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic const struct linear_range wm8350_ldo_ranges[] = { 47462306a36Sopenharmony_ci REGULATOR_LINEAR_RANGE(900000, 0, 15, 50000), 47562306a36Sopenharmony_ci REGULATOR_LINEAR_RANGE(1800000, 16, 31, 100000), 47662306a36Sopenharmony_ci}; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic int wm8350_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct wm8350 *wm8350 = rdev_get_drvdata(rdev); 48162306a36Sopenharmony_ci int sel, volt_reg, ldo = rdev_get_id(rdev); 48262306a36Sopenharmony_ci u16 val; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, ldo, uV / 1000); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci switch (ldo) { 48762306a36Sopenharmony_ci case WM8350_LDO_1: 48862306a36Sopenharmony_ci volt_reg = WM8350_LDO1_LOW_POWER; 48962306a36Sopenharmony_ci break; 49062306a36Sopenharmony_ci case WM8350_LDO_2: 49162306a36Sopenharmony_ci volt_reg = WM8350_LDO2_LOW_POWER; 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci case WM8350_LDO_3: 49462306a36Sopenharmony_ci volt_reg = WM8350_LDO3_LOW_POWER; 49562306a36Sopenharmony_ci break; 49662306a36Sopenharmony_ci case WM8350_LDO_4: 49762306a36Sopenharmony_ci volt_reg = WM8350_LDO4_LOW_POWER; 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci default: 50062306a36Sopenharmony_ci return -EINVAL; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci sel = regulator_map_voltage_linear_range(rdev, uV, uV); 50462306a36Sopenharmony_ci if (sel < 0) 50562306a36Sopenharmony_ci return sel; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* all LDOs have same mV bits */ 50862306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK; 50962306a36Sopenharmony_ci wm8350_reg_write(wm8350, volt_reg, val | sel); 51062306a36Sopenharmony_ci return 0; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic int wm8350_ldo_set_suspend_enable(struct regulator_dev *rdev) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct wm8350 *wm8350 = rdev_get_drvdata(rdev); 51662306a36Sopenharmony_ci int volt_reg, ldo = rdev_get_id(rdev); 51762306a36Sopenharmony_ci u16 val; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci switch (ldo) { 52062306a36Sopenharmony_ci case WM8350_LDO_1: 52162306a36Sopenharmony_ci volt_reg = WM8350_LDO1_LOW_POWER; 52262306a36Sopenharmony_ci break; 52362306a36Sopenharmony_ci case WM8350_LDO_2: 52462306a36Sopenharmony_ci volt_reg = WM8350_LDO2_LOW_POWER; 52562306a36Sopenharmony_ci break; 52662306a36Sopenharmony_ci case WM8350_LDO_3: 52762306a36Sopenharmony_ci volt_reg = WM8350_LDO3_LOW_POWER; 52862306a36Sopenharmony_ci break; 52962306a36Sopenharmony_ci case WM8350_LDO_4: 53062306a36Sopenharmony_ci volt_reg = WM8350_LDO4_LOW_POWER; 53162306a36Sopenharmony_ci break; 53262306a36Sopenharmony_ci default: 53362306a36Sopenharmony_ci return -EINVAL; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* all LDOs have same mV bits */ 53762306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_HIB_MODE_MASK; 53862306a36Sopenharmony_ci wm8350_reg_write(wm8350, volt_reg, val); 53962306a36Sopenharmony_ci return 0; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic int wm8350_ldo_set_suspend_disable(struct regulator_dev *rdev) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct wm8350 *wm8350 = rdev_get_drvdata(rdev); 54562306a36Sopenharmony_ci int volt_reg, ldo = rdev_get_id(rdev); 54662306a36Sopenharmony_ci u16 val; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci switch (ldo) { 54962306a36Sopenharmony_ci case WM8350_LDO_1: 55062306a36Sopenharmony_ci volt_reg = WM8350_LDO1_LOW_POWER; 55162306a36Sopenharmony_ci break; 55262306a36Sopenharmony_ci case WM8350_LDO_2: 55362306a36Sopenharmony_ci volt_reg = WM8350_LDO2_LOW_POWER; 55462306a36Sopenharmony_ci break; 55562306a36Sopenharmony_ci case WM8350_LDO_3: 55662306a36Sopenharmony_ci volt_reg = WM8350_LDO3_LOW_POWER; 55762306a36Sopenharmony_ci break; 55862306a36Sopenharmony_ci case WM8350_LDO_4: 55962306a36Sopenharmony_ci volt_reg = WM8350_LDO4_LOW_POWER; 56062306a36Sopenharmony_ci break; 56162306a36Sopenharmony_ci default: 56262306a36Sopenharmony_ci return -EINVAL; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* all LDOs have same mV bits */ 56662306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_HIB_MODE_MASK; 56762306a36Sopenharmony_ci wm8350_reg_write(wm8350, volt_reg, val | WM8350_LDO1_HIB_MODE_DIS); 56862306a36Sopenharmony_ci return 0; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ciint wm8350_dcdc_set_slot(struct wm8350 *wm8350, int dcdc, u16 start, 57262306a36Sopenharmony_ci u16 stop, u16 fault) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci int slot_reg; 57562306a36Sopenharmony_ci u16 val; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci dev_dbg(wm8350->dev, "%s %d start %d stop %d\n", 57862306a36Sopenharmony_ci __func__, dcdc, start, stop); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* slot valid ? */ 58162306a36Sopenharmony_ci if (start > 15 || stop > 15) 58262306a36Sopenharmony_ci return -EINVAL; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci switch (dcdc) { 58562306a36Sopenharmony_ci case WM8350_DCDC_1: 58662306a36Sopenharmony_ci slot_reg = WM8350_DCDC1_TIMEOUTS; 58762306a36Sopenharmony_ci break; 58862306a36Sopenharmony_ci case WM8350_DCDC_2: 58962306a36Sopenharmony_ci slot_reg = WM8350_DCDC2_TIMEOUTS; 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci case WM8350_DCDC_3: 59262306a36Sopenharmony_ci slot_reg = WM8350_DCDC3_TIMEOUTS; 59362306a36Sopenharmony_ci break; 59462306a36Sopenharmony_ci case WM8350_DCDC_4: 59562306a36Sopenharmony_ci slot_reg = WM8350_DCDC4_TIMEOUTS; 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci case WM8350_DCDC_5: 59862306a36Sopenharmony_ci slot_reg = WM8350_DCDC5_TIMEOUTS; 59962306a36Sopenharmony_ci break; 60062306a36Sopenharmony_ci case WM8350_DCDC_6: 60162306a36Sopenharmony_ci slot_reg = WM8350_DCDC6_TIMEOUTS; 60262306a36Sopenharmony_ci break; 60362306a36Sopenharmony_ci default: 60462306a36Sopenharmony_ci return -EINVAL; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, slot_reg) & 60862306a36Sopenharmony_ci ~(WM8350_DC1_ENSLOT_MASK | WM8350_DC1_SDSLOT_MASK | 60962306a36Sopenharmony_ci WM8350_DC1_ERRACT_MASK); 61062306a36Sopenharmony_ci wm8350_reg_write(wm8350, slot_reg, 61162306a36Sopenharmony_ci val | (start << WM8350_DC1_ENSLOT_SHIFT) | 61262306a36Sopenharmony_ci (stop << WM8350_DC1_SDSLOT_SHIFT) | 61362306a36Sopenharmony_ci (fault << WM8350_DC1_ERRACT_SHIFT)); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci return 0; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wm8350_dcdc_set_slot); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ciint wm8350_ldo_set_slot(struct wm8350 *wm8350, int ldo, u16 start, u16 stop) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci int slot_reg; 62262306a36Sopenharmony_ci u16 val; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci dev_dbg(wm8350->dev, "%s %d start %d stop %d\n", 62562306a36Sopenharmony_ci __func__, ldo, start, stop); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* slot valid ? */ 62862306a36Sopenharmony_ci if (start > 15 || stop > 15) 62962306a36Sopenharmony_ci return -EINVAL; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci switch (ldo) { 63262306a36Sopenharmony_ci case WM8350_LDO_1: 63362306a36Sopenharmony_ci slot_reg = WM8350_LDO1_TIMEOUTS; 63462306a36Sopenharmony_ci break; 63562306a36Sopenharmony_ci case WM8350_LDO_2: 63662306a36Sopenharmony_ci slot_reg = WM8350_LDO2_TIMEOUTS; 63762306a36Sopenharmony_ci break; 63862306a36Sopenharmony_ci case WM8350_LDO_3: 63962306a36Sopenharmony_ci slot_reg = WM8350_LDO3_TIMEOUTS; 64062306a36Sopenharmony_ci break; 64162306a36Sopenharmony_ci case WM8350_LDO_4: 64262306a36Sopenharmony_ci slot_reg = WM8350_LDO4_TIMEOUTS; 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci default: 64562306a36Sopenharmony_ci return -EINVAL; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, slot_reg) & ~WM8350_LDO1_SDSLOT_MASK; 64962306a36Sopenharmony_ci wm8350_reg_write(wm8350, slot_reg, val | ((start << 10) | (stop << 6))); 65062306a36Sopenharmony_ci return 0; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wm8350_ldo_set_slot); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ciint wm8350_dcdc25_set_mode(struct wm8350 *wm8350, int dcdc, u16 mode, 65562306a36Sopenharmony_ci u16 ilim, u16 ramp, u16 feedback) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci u16 val; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci dev_dbg(wm8350->dev, "%s %d mode: %s %s\n", __func__, dcdc, 66062306a36Sopenharmony_ci mode ? "normal" : "boost", ilim ? "low" : "normal"); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci switch (dcdc) { 66362306a36Sopenharmony_ci case WM8350_DCDC_2: 66462306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL) 66562306a36Sopenharmony_ci & ~(WM8350_DC2_MODE_MASK | WM8350_DC2_ILIM_MASK | 66662306a36Sopenharmony_ci WM8350_DC2_RMP_MASK | WM8350_DC2_FBSRC_MASK); 66762306a36Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val | 66862306a36Sopenharmony_ci (mode << WM8350_DC2_MODE_SHIFT) | 66962306a36Sopenharmony_ci (ilim << WM8350_DC2_ILIM_SHIFT) | 67062306a36Sopenharmony_ci (ramp << WM8350_DC2_RMP_SHIFT) | 67162306a36Sopenharmony_ci (feedback << WM8350_DC2_FBSRC_SHIFT)); 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci case WM8350_DCDC_5: 67462306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL) 67562306a36Sopenharmony_ci & ~(WM8350_DC5_MODE_MASK | WM8350_DC5_ILIM_MASK | 67662306a36Sopenharmony_ci WM8350_DC5_RMP_MASK | WM8350_DC5_FBSRC_MASK); 67762306a36Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val | 67862306a36Sopenharmony_ci (mode << WM8350_DC5_MODE_SHIFT) | 67962306a36Sopenharmony_ci (ilim << WM8350_DC5_ILIM_SHIFT) | 68062306a36Sopenharmony_ci (ramp << WM8350_DC5_RMP_SHIFT) | 68162306a36Sopenharmony_ci (feedback << WM8350_DC5_FBSRC_SHIFT)); 68262306a36Sopenharmony_ci break; 68362306a36Sopenharmony_ci default: 68462306a36Sopenharmony_ci return -EINVAL; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci return 0; 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wm8350_dcdc25_set_mode); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic int force_continuous_enable(struct wm8350 *wm8350, int dcdc, int enable) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci int reg = 0, ret; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci switch (dcdc) { 69662306a36Sopenharmony_ci case WM8350_DCDC_1: 69762306a36Sopenharmony_ci reg = WM8350_DCDC1_FORCE_PWM; 69862306a36Sopenharmony_ci break; 69962306a36Sopenharmony_ci case WM8350_DCDC_3: 70062306a36Sopenharmony_ci reg = WM8350_DCDC3_FORCE_PWM; 70162306a36Sopenharmony_ci break; 70262306a36Sopenharmony_ci case WM8350_DCDC_4: 70362306a36Sopenharmony_ci reg = WM8350_DCDC4_FORCE_PWM; 70462306a36Sopenharmony_ci break; 70562306a36Sopenharmony_ci case WM8350_DCDC_6: 70662306a36Sopenharmony_ci reg = WM8350_DCDC6_FORCE_PWM; 70762306a36Sopenharmony_ci break; 70862306a36Sopenharmony_ci default: 70962306a36Sopenharmony_ci return -EINVAL; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (enable) 71362306a36Sopenharmony_ci ret = wm8350_set_bits(wm8350, reg, 71462306a36Sopenharmony_ci WM8350_DCDC1_FORCE_PWM_ENA); 71562306a36Sopenharmony_ci else 71662306a36Sopenharmony_ci ret = wm8350_clear_bits(wm8350, reg, 71762306a36Sopenharmony_ci WM8350_DCDC1_FORCE_PWM_ENA); 71862306a36Sopenharmony_ci return ret; 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic int wm8350_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci struct wm8350 *wm8350 = rdev_get_drvdata(rdev); 72462306a36Sopenharmony_ci int dcdc = rdev_get_id(rdev); 72562306a36Sopenharmony_ci u16 val; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6) 72862306a36Sopenharmony_ci return -EINVAL; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (dcdc == WM8350_DCDC_2 || dcdc == WM8350_DCDC_5) 73162306a36Sopenharmony_ci return -EINVAL; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci val = 1 << (dcdc - WM8350_DCDC_1); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci switch (mode) { 73662306a36Sopenharmony_ci case REGULATOR_MODE_FAST: 73762306a36Sopenharmony_ci /* force continuous mode */ 73862306a36Sopenharmony_ci wm8350_set_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val); 73962306a36Sopenharmony_ci wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val); 74062306a36Sopenharmony_ci force_continuous_enable(wm8350, dcdc, 1); 74162306a36Sopenharmony_ci break; 74262306a36Sopenharmony_ci case REGULATOR_MODE_NORMAL: 74362306a36Sopenharmony_ci /* active / pulse skipping */ 74462306a36Sopenharmony_ci wm8350_set_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val); 74562306a36Sopenharmony_ci wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val); 74662306a36Sopenharmony_ci force_continuous_enable(wm8350, dcdc, 0); 74762306a36Sopenharmony_ci break; 74862306a36Sopenharmony_ci case REGULATOR_MODE_IDLE: 74962306a36Sopenharmony_ci /* standby mode */ 75062306a36Sopenharmony_ci force_continuous_enable(wm8350, dcdc, 0); 75162306a36Sopenharmony_ci wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val); 75262306a36Sopenharmony_ci wm8350_clear_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val); 75362306a36Sopenharmony_ci break; 75462306a36Sopenharmony_ci case REGULATOR_MODE_STANDBY: 75562306a36Sopenharmony_ci /* LDO mode */ 75662306a36Sopenharmony_ci force_continuous_enable(wm8350, dcdc, 0); 75762306a36Sopenharmony_ci wm8350_set_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val); 75862306a36Sopenharmony_ci break; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci return 0; 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_cistatic unsigned int wm8350_dcdc_get_mode(struct regulator_dev *rdev) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci struct wm8350 *wm8350 = rdev_get_drvdata(rdev); 76762306a36Sopenharmony_ci int dcdc = rdev_get_id(rdev); 76862306a36Sopenharmony_ci u16 mask, sleep, active, force; 76962306a36Sopenharmony_ci int mode = REGULATOR_MODE_NORMAL; 77062306a36Sopenharmony_ci int reg; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci switch (dcdc) { 77362306a36Sopenharmony_ci case WM8350_DCDC_1: 77462306a36Sopenharmony_ci reg = WM8350_DCDC1_FORCE_PWM; 77562306a36Sopenharmony_ci break; 77662306a36Sopenharmony_ci case WM8350_DCDC_3: 77762306a36Sopenharmony_ci reg = WM8350_DCDC3_FORCE_PWM; 77862306a36Sopenharmony_ci break; 77962306a36Sopenharmony_ci case WM8350_DCDC_4: 78062306a36Sopenharmony_ci reg = WM8350_DCDC4_FORCE_PWM; 78162306a36Sopenharmony_ci break; 78262306a36Sopenharmony_ci case WM8350_DCDC_6: 78362306a36Sopenharmony_ci reg = WM8350_DCDC6_FORCE_PWM; 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci default: 78662306a36Sopenharmony_ci return -EINVAL; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci mask = 1 << (dcdc - WM8350_DCDC_1); 79062306a36Sopenharmony_ci active = wm8350_reg_read(wm8350, WM8350_DCDC_ACTIVE_OPTIONS) & mask; 79162306a36Sopenharmony_ci force = wm8350_reg_read(wm8350, reg) & WM8350_DCDC1_FORCE_PWM_ENA; 79262306a36Sopenharmony_ci sleep = wm8350_reg_read(wm8350, WM8350_DCDC_SLEEP_OPTIONS) & mask; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci dev_dbg(wm8350->dev, "mask %x active %x sleep %x force %x", 79562306a36Sopenharmony_ci mask, active, sleep, force); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci if (active && !sleep) { 79862306a36Sopenharmony_ci if (force) 79962306a36Sopenharmony_ci mode = REGULATOR_MODE_FAST; 80062306a36Sopenharmony_ci else 80162306a36Sopenharmony_ci mode = REGULATOR_MODE_NORMAL; 80262306a36Sopenharmony_ci } else if (!active && !sleep) 80362306a36Sopenharmony_ci mode = REGULATOR_MODE_IDLE; 80462306a36Sopenharmony_ci else if (sleep) 80562306a36Sopenharmony_ci mode = REGULATOR_MODE_STANDBY; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci return mode; 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic unsigned int wm8350_ldo_get_mode(struct regulator_dev *rdev) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci return REGULATOR_MODE_NORMAL; 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistruct wm8350_dcdc_efficiency { 81662306a36Sopenharmony_ci int uA_load_min; 81762306a36Sopenharmony_ci int uA_load_max; 81862306a36Sopenharmony_ci unsigned int mode; 81962306a36Sopenharmony_ci}; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_cistatic const struct wm8350_dcdc_efficiency dcdc1_6_efficiency[] = { 82262306a36Sopenharmony_ci {0, 10000, REGULATOR_MODE_STANDBY}, /* 0 - 10mA - LDO */ 82362306a36Sopenharmony_ci {10000, 100000, REGULATOR_MODE_IDLE}, /* 10mA - 100mA - Standby */ 82462306a36Sopenharmony_ci {100000, 1000000, REGULATOR_MODE_NORMAL}, /* > 100mA - Active */ 82562306a36Sopenharmony_ci {-1, -1, REGULATOR_MODE_NORMAL}, 82662306a36Sopenharmony_ci}; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic const struct wm8350_dcdc_efficiency dcdc3_4_efficiency[] = { 82962306a36Sopenharmony_ci {0, 10000, REGULATOR_MODE_STANDBY}, /* 0 - 10mA - LDO */ 83062306a36Sopenharmony_ci {10000, 100000, REGULATOR_MODE_IDLE}, /* 10mA - 100mA - Standby */ 83162306a36Sopenharmony_ci {100000, 800000, REGULATOR_MODE_NORMAL}, /* > 100mA - Active */ 83262306a36Sopenharmony_ci {-1, -1, REGULATOR_MODE_NORMAL}, 83362306a36Sopenharmony_ci}; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic unsigned int get_mode(int uA, const struct wm8350_dcdc_efficiency *eff) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci int i = 0; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci while (eff[i].uA_load_min != -1) { 84062306a36Sopenharmony_ci if (uA >= eff[i].uA_load_min && uA <= eff[i].uA_load_max) 84162306a36Sopenharmony_ci return eff[i].mode; 84262306a36Sopenharmony_ci i++; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci return REGULATOR_MODE_NORMAL; 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci/* Query the regulator for it's most efficient mode @ uV,uA 84862306a36Sopenharmony_ci * WM8350 regulator efficiency is pretty similar over 84962306a36Sopenharmony_ci * different input and output uV. 85062306a36Sopenharmony_ci */ 85162306a36Sopenharmony_cistatic unsigned int wm8350_dcdc_get_optimum_mode(struct regulator_dev *rdev, 85262306a36Sopenharmony_ci int input_uV, int output_uV, 85362306a36Sopenharmony_ci int output_uA) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci int dcdc = rdev_get_id(rdev), mode; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci switch (dcdc) { 85862306a36Sopenharmony_ci case WM8350_DCDC_1: 85962306a36Sopenharmony_ci case WM8350_DCDC_6: 86062306a36Sopenharmony_ci mode = get_mode(output_uA, dcdc1_6_efficiency); 86162306a36Sopenharmony_ci break; 86262306a36Sopenharmony_ci case WM8350_DCDC_3: 86362306a36Sopenharmony_ci case WM8350_DCDC_4: 86462306a36Sopenharmony_ci mode = get_mode(output_uA, dcdc3_4_efficiency); 86562306a36Sopenharmony_ci break; 86662306a36Sopenharmony_ci default: 86762306a36Sopenharmony_ci mode = REGULATOR_MODE_NORMAL; 86862306a36Sopenharmony_ci break; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci return mode; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic const struct regulator_ops wm8350_dcdc_ops = { 87462306a36Sopenharmony_ci .set_voltage_sel = regulator_set_voltage_sel_regmap, 87562306a36Sopenharmony_ci .get_voltage_sel = regulator_get_voltage_sel_regmap, 87662306a36Sopenharmony_ci .list_voltage = regulator_list_voltage_linear, 87762306a36Sopenharmony_ci .map_voltage = regulator_map_voltage_linear, 87862306a36Sopenharmony_ci .enable = regulator_enable_regmap, 87962306a36Sopenharmony_ci .disable = regulator_disable_regmap, 88062306a36Sopenharmony_ci .is_enabled = regulator_is_enabled_regmap, 88162306a36Sopenharmony_ci .get_mode = wm8350_dcdc_get_mode, 88262306a36Sopenharmony_ci .set_mode = wm8350_dcdc_set_mode, 88362306a36Sopenharmony_ci .get_optimum_mode = wm8350_dcdc_get_optimum_mode, 88462306a36Sopenharmony_ci .set_suspend_voltage = wm8350_dcdc_set_suspend_voltage, 88562306a36Sopenharmony_ci .set_suspend_enable = wm8350_dcdc_set_suspend_enable, 88662306a36Sopenharmony_ci .set_suspend_disable = wm8350_dcdc_set_suspend_disable, 88762306a36Sopenharmony_ci .set_suspend_mode = wm8350_dcdc_set_suspend_mode, 88862306a36Sopenharmony_ci}; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic const struct regulator_ops wm8350_dcdc2_5_ops = { 89162306a36Sopenharmony_ci .enable = regulator_enable_regmap, 89262306a36Sopenharmony_ci .disable = regulator_disable_regmap, 89362306a36Sopenharmony_ci .is_enabled = regulator_is_enabled_regmap, 89462306a36Sopenharmony_ci .set_suspend_enable = wm8350_dcdc25_set_suspend_enable, 89562306a36Sopenharmony_ci .set_suspend_disable = wm8350_dcdc25_set_suspend_disable, 89662306a36Sopenharmony_ci}; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic const struct regulator_ops wm8350_ldo_ops = { 89962306a36Sopenharmony_ci .map_voltage = regulator_map_voltage_linear_range, 90062306a36Sopenharmony_ci .set_voltage_sel = regulator_set_voltage_sel_regmap, 90162306a36Sopenharmony_ci .get_voltage_sel = regulator_get_voltage_sel_regmap, 90262306a36Sopenharmony_ci .list_voltage = regulator_list_voltage_linear_range, 90362306a36Sopenharmony_ci .enable = regulator_enable_regmap, 90462306a36Sopenharmony_ci .disable = regulator_disable_regmap, 90562306a36Sopenharmony_ci .is_enabled = regulator_is_enabled_regmap, 90662306a36Sopenharmony_ci .get_mode = wm8350_ldo_get_mode, 90762306a36Sopenharmony_ci .set_suspend_voltage = wm8350_ldo_set_suspend_voltage, 90862306a36Sopenharmony_ci .set_suspend_enable = wm8350_ldo_set_suspend_enable, 90962306a36Sopenharmony_ci .set_suspend_disable = wm8350_ldo_set_suspend_disable, 91062306a36Sopenharmony_ci}; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic const struct regulator_ops wm8350_isink_ops = { 91362306a36Sopenharmony_ci .set_current_limit = regulator_set_current_limit_regmap, 91462306a36Sopenharmony_ci .get_current_limit = regulator_get_current_limit_regmap, 91562306a36Sopenharmony_ci .enable = wm8350_isink_enable, 91662306a36Sopenharmony_ci .disable = wm8350_isink_disable, 91762306a36Sopenharmony_ci .is_enabled = wm8350_isink_is_enabled, 91862306a36Sopenharmony_ci .enable_time = wm8350_isink_enable_time, 91962306a36Sopenharmony_ci}; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_cistatic const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { 92262306a36Sopenharmony_ci { 92362306a36Sopenharmony_ci .name = "DCDC1", 92462306a36Sopenharmony_ci .id = WM8350_DCDC_1, 92562306a36Sopenharmony_ci .ops = &wm8350_dcdc_ops, 92662306a36Sopenharmony_ci .irq = WM8350_IRQ_UV_DC1, 92762306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, 92862306a36Sopenharmony_ci .n_voltages = WM8350_DCDC_MAX_VSEL + 1, 92962306a36Sopenharmony_ci .min_uV = 850000, 93062306a36Sopenharmony_ci .uV_step = 25000, 93162306a36Sopenharmony_ci .vsel_reg = WM8350_DCDC1_CONTROL, 93262306a36Sopenharmony_ci .vsel_mask = WM8350_DC1_VSEL_MASK, 93362306a36Sopenharmony_ci .enable_reg = WM8350_DCDC_LDO_REQUESTED, 93462306a36Sopenharmony_ci .enable_mask = WM8350_DC1_ENA, 93562306a36Sopenharmony_ci .owner = THIS_MODULE, 93662306a36Sopenharmony_ci }, 93762306a36Sopenharmony_ci { 93862306a36Sopenharmony_ci .name = "DCDC2", 93962306a36Sopenharmony_ci .id = WM8350_DCDC_2, 94062306a36Sopenharmony_ci .ops = &wm8350_dcdc2_5_ops, 94162306a36Sopenharmony_ci .irq = WM8350_IRQ_UV_DC2, 94262306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, 94362306a36Sopenharmony_ci .enable_reg = WM8350_DCDC_LDO_REQUESTED, 94462306a36Sopenharmony_ci .enable_mask = WM8350_DC2_ENA, 94562306a36Sopenharmony_ci .owner = THIS_MODULE, 94662306a36Sopenharmony_ci }, 94762306a36Sopenharmony_ci { 94862306a36Sopenharmony_ci .name = "DCDC3", 94962306a36Sopenharmony_ci .id = WM8350_DCDC_3, 95062306a36Sopenharmony_ci .ops = &wm8350_dcdc_ops, 95162306a36Sopenharmony_ci .irq = WM8350_IRQ_UV_DC3, 95262306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, 95362306a36Sopenharmony_ci .n_voltages = WM8350_DCDC_MAX_VSEL + 1, 95462306a36Sopenharmony_ci .min_uV = 850000, 95562306a36Sopenharmony_ci .uV_step = 25000, 95662306a36Sopenharmony_ci .vsel_reg = WM8350_DCDC3_CONTROL, 95762306a36Sopenharmony_ci .vsel_mask = WM8350_DC3_VSEL_MASK, 95862306a36Sopenharmony_ci .enable_reg = WM8350_DCDC_LDO_REQUESTED, 95962306a36Sopenharmony_ci .enable_mask = WM8350_DC3_ENA, 96062306a36Sopenharmony_ci .owner = THIS_MODULE, 96162306a36Sopenharmony_ci }, 96262306a36Sopenharmony_ci { 96362306a36Sopenharmony_ci .name = "DCDC4", 96462306a36Sopenharmony_ci .id = WM8350_DCDC_4, 96562306a36Sopenharmony_ci .ops = &wm8350_dcdc_ops, 96662306a36Sopenharmony_ci .irq = WM8350_IRQ_UV_DC4, 96762306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, 96862306a36Sopenharmony_ci .n_voltages = WM8350_DCDC_MAX_VSEL + 1, 96962306a36Sopenharmony_ci .min_uV = 850000, 97062306a36Sopenharmony_ci .uV_step = 25000, 97162306a36Sopenharmony_ci .vsel_reg = WM8350_DCDC4_CONTROL, 97262306a36Sopenharmony_ci .vsel_mask = WM8350_DC4_VSEL_MASK, 97362306a36Sopenharmony_ci .enable_reg = WM8350_DCDC_LDO_REQUESTED, 97462306a36Sopenharmony_ci .enable_mask = WM8350_DC4_ENA, 97562306a36Sopenharmony_ci .owner = THIS_MODULE, 97662306a36Sopenharmony_ci }, 97762306a36Sopenharmony_ci { 97862306a36Sopenharmony_ci .name = "DCDC5", 97962306a36Sopenharmony_ci .id = WM8350_DCDC_5, 98062306a36Sopenharmony_ci .ops = &wm8350_dcdc2_5_ops, 98162306a36Sopenharmony_ci .irq = WM8350_IRQ_UV_DC5, 98262306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, 98362306a36Sopenharmony_ci .enable_reg = WM8350_DCDC_LDO_REQUESTED, 98462306a36Sopenharmony_ci .enable_mask = WM8350_DC5_ENA, 98562306a36Sopenharmony_ci .owner = THIS_MODULE, 98662306a36Sopenharmony_ci }, 98762306a36Sopenharmony_ci { 98862306a36Sopenharmony_ci .name = "DCDC6", 98962306a36Sopenharmony_ci .id = WM8350_DCDC_6, 99062306a36Sopenharmony_ci .ops = &wm8350_dcdc_ops, 99162306a36Sopenharmony_ci .irq = WM8350_IRQ_UV_DC6, 99262306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, 99362306a36Sopenharmony_ci .n_voltages = WM8350_DCDC_MAX_VSEL + 1, 99462306a36Sopenharmony_ci .min_uV = 850000, 99562306a36Sopenharmony_ci .uV_step = 25000, 99662306a36Sopenharmony_ci .vsel_reg = WM8350_DCDC6_CONTROL, 99762306a36Sopenharmony_ci .vsel_mask = WM8350_DC6_VSEL_MASK, 99862306a36Sopenharmony_ci .enable_reg = WM8350_DCDC_LDO_REQUESTED, 99962306a36Sopenharmony_ci .enable_mask = WM8350_DC6_ENA, 100062306a36Sopenharmony_ci .owner = THIS_MODULE, 100162306a36Sopenharmony_ci }, 100262306a36Sopenharmony_ci { 100362306a36Sopenharmony_ci .name = "LDO1", 100462306a36Sopenharmony_ci .id = WM8350_LDO_1, 100562306a36Sopenharmony_ci .ops = &wm8350_ldo_ops, 100662306a36Sopenharmony_ci .irq = WM8350_IRQ_UV_LDO1, 100762306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, 100862306a36Sopenharmony_ci .n_voltages = WM8350_LDO1_VSEL_MASK + 1, 100962306a36Sopenharmony_ci .linear_ranges = wm8350_ldo_ranges, 101062306a36Sopenharmony_ci .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges), 101162306a36Sopenharmony_ci .vsel_reg = WM8350_LDO1_CONTROL, 101262306a36Sopenharmony_ci .vsel_mask = WM8350_LDO1_VSEL_MASK, 101362306a36Sopenharmony_ci .enable_reg = WM8350_DCDC_LDO_REQUESTED, 101462306a36Sopenharmony_ci .enable_mask = WM8350_LDO1_ENA, 101562306a36Sopenharmony_ci .owner = THIS_MODULE, 101662306a36Sopenharmony_ci }, 101762306a36Sopenharmony_ci { 101862306a36Sopenharmony_ci .name = "LDO2", 101962306a36Sopenharmony_ci .id = WM8350_LDO_2, 102062306a36Sopenharmony_ci .ops = &wm8350_ldo_ops, 102162306a36Sopenharmony_ci .irq = WM8350_IRQ_UV_LDO2, 102262306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, 102362306a36Sopenharmony_ci .n_voltages = WM8350_LDO2_VSEL_MASK + 1, 102462306a36Sopenharmony_ci .linear_ranges = wm8350_ldo_ranges, 102562306a36Sopenharmony_ci .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges), 102662306a36Sopenharmony_ci .vsel_reg = WM8350_LDO2_CONTROL, 102762306a36Sopenharmony_ci .vsel_mask = WM8350_LDO2_VSEL_MASK, 102862306a36Sopenharmony_ci .enable_reg = WM8350_DCDC_LDO_REQUESTED, 102962306a36Sopenharmony_ci .enable_mask = WM8350_LDO2_ENA, 103062306a36Sopenharmony_ci .owner = THIS_MODULE, 103162306a36Sopenharmony_ci }, 103262306a36Sopenharmony_ci { 103362306a36Sopenharmony_ci .name = "LDO3", 103462306a36Sopenharmony_ci .id = WM8350_LDO_3, 103562306a36Sopenharmony_ci .ops = &wm8350_ldo_ops, 103662306a36Sopenharmony_ci .irq = WM8350_IRQ_UV_LDO3, 103762306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, 103862306a36Sopenharmony_ci .n_voltages = WM8350_LDO3_VSEL_MASK + 1, 103962306a36Sopenharmony_ci .linear_ranges = wm8350_ldo_ranges, 104062306a36Sopenharmony_ci .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges), 104162306a36Sopenharmony_ci .vsel_reg = WM8350_LDO3_CONTROL, 104262306a36Sopenharmony_ci .vsel_mask = WM8350_LDO3_VSEL_MASK, 104362306a36Sopenharmony_ci .enable_reg = WM8350_DCDC_LDO_REQUESTED, 104462306a36Sopenharmony_ci .enable_mask = WM8350_LDO3_ENA, 104562306a36Sopenharmony_ci .owner = THIS_MODULE, 104662306a36Sopenharmony_ci }, 104762306a36Sopenharmony_ci { 104862306a36Sopenharmony_ci .name = "LDO4", 104962306a36Sopenharmony_ci .id = WM8350_LDO_4, 105062306a36Sopenharmony_ci .ops = &wm8350_ldo_ops, 105162306a36Sopenharmony_ci .irq = WM8350_IRQ_UV_LDO4, 105262306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, 105362306a36Sopenharmony_ci .n_voltages = WM8350_LDO4_VSEL_MASK + 1, 105462306a36Sopenharmony_ci .linear_ranges = wm8350_ldo_ranges, 105562306a36Sopenharmony_ci .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges), 105662306a36Sopenharmony_ci .vsel_reg = WM8350_LDO4_CONTROL, 105762306a36Sopenharmony_ci .vsel_mask = WM8350_LDO4_VSEL_MASK, 105862306a36Sopenharmony_ci .enable_reg = WM8350_DCDC_LDO_REQUESTED, 105962306a36Sopenharmony_ci .enable_mask = WM8350_LDO4_ENA, 106062306a36Sopenharmony_ci .owner = THIS_MODULE, 106162306a36Sopenharmony_ci }, 106262306a36Sopenharmony_ci { 106362306a36Sopenharmony_ci .name = "ISINKA", 106462306a36Sopenharmony_ci .id = WM8350_ISINK_A, 106562306a36Sopenharmony_ci .ops = &wm8350_isink_ops, 106662306a36Sopenharmony_ci .irq = WM8350_IRQ_CS1, 106762306a36Sopenharmony_ci .type = REGULATOR_CURRENT, 106862306a36Sopenharmony_ci .owner = THIS_MODULE, 106962306a36Sopenharmony_ci .curr_table = isink_cur, 107062306a36Sopenharmony_ci .n_current_limits = ARRAY_SIZE(isink_cur), 107162306a36Sopenharmony_ci .csel_reg = WM8350_CURRENT_SINK_DRIVER_A, 107262306a36Sopenharmony_ci .csel_mask = WM8350_CS1_ISEL_MASK, 107362306a36Sopenharmony_ci }, 107462306a36Sopenharmony_ci { 107562306a36Sopenharmony_ci .name = "ISINKB", 107662306a36Sopenharmony_ci .id = WM8350_ISINK_B, 107762306a36Sopenharmony_ci .ops = &wm8350_isink_ops, 107862306a36Sopenharmony_ci .irq = WM8350_IRQ_CS2, 107962306a36Sopenharmony_ci .type = REGULATOR_CURRENT, 108062306a36Sopenharmony_ci .owner = THIS_MODULE, 108162306a36Sopenharmony_ci .curr_table = isink_cur, 108262306a36Sopenharmony_ci .n_current_limits = ARRAY_SIZE(isink_cur), 108362306a36Sopenharmony_ci .csel_reg = WM8350_CURRENT_SINK_DRIVER_B, 108462306a36Sopenharmony_ci .csel_mask = WM8350_CS2_ISEL_MASK, 108562306a36Sopenharmony_ci }, 108662306a36Sopenharmony_ci}; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cistatic irqreturn_t pmic_uv_handler(int irq, void *data) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci struct regulator_dev *rdev = (struct regulator_dev *)data; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci if (irq == WM8350_IRQ_CS1 || irq == WM8350_IRQ_CS2) 109362306a36Sopenharmony_ci regulator_notifier_call_chain(rdev, 109462306a36Sopenharmony_ci REGULATOR_EVENT_REGULATION_OUT, 109562306a36Sopenharmony_ci NULL); 109662306a36Sopenharmony_ci else 109762306a36Sopenharmony_ci regulator_notifier_call_chain(rdev, 109862306a36Sopenharmony_ci REGULATOR_EVENT_UNDER_VOLTAGE, 109962306a36Sopenharmony_ci NULL); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci return IRQ_HANDLED; 110262306a36Sopenharmony_ci} 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_cistatic int wm8350_regulator_probe(struct platform_device *pdev) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev); 110762306a36Sopenharmony_ci struct regulator_config config = { }; 110862306a36Sopenharmony_ci struct regulator_dev *rdev; 110962306a36Sopenharmony_ci int ret; 111062306a36Sopenharmony_ci u16 val; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci if (pdev->id < WM8350_DCDC_1 || pdev->id > WM8350_ISINK_B) 111362306a36Sopenharmony_ci return -ENODEV; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci /* do any regulator specific init */ 111662306a36Sopenharmony_ci switch (pdev->id) { 111762306a36Sopenharmony_ci case WM8350_DCDC_1: 111862306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER); 111962306a36Sopenharmony_ci wm8350->pmic.dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; 112062306a36Sopenharmony_ci break; 112162306a36Sopenharmony_ci case WM8350_DCDC_3: 112262306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER); 112362306a36Sopenharmony_ci wm8350->pmic.dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; 112462306a36Sopenharmony_ci break; 112562306a36Sopenharmony_ci case WM8350_DCDC_4: 112662306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER); 112762306a36Sopenharmony_ci wm8350->pmic.dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; 112862306a36Sopenharmony_ci break; 112962306a36Sopenharmony_ci case WM8350_DCDC_6: 113062306a36Sopenharmony_ci val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER); 113162306a36Sopenharmony_ci wm8350->pmic.dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; 113262306a36Sopenharmony_ci break; 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci config.dev = &pdev->dev; 113662306a36Sopenharmony_ci config.init_data = dev_get_platdata(&pdev->dev); 113762306a36Sopenharmony_ci config.driver_data = dev_get_drvdata(&pdev->dev); 113862306a36Sopenharmony_ci config.regmap = wm8350->regmap; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci /* register regulator */ 114162306a36Sopenharmony_ci rdev = devm_regulator_register(&pdev->dev, &wm8350_reg[pdev->id], 114262306a36Sopenharmony_ci &config); 114362306a36Sopenharmony_ci if (IS_ERR(rdev)) { 114462306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register %s\n", 114562306a36Sopenharmony_ci wm8350_reg[pdev->id].name); 114662306a36Sopenharmony_ci return PTR_ERR(rdev); 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci /* register regulator IRQ */ 115062306a36Sopenharmony_ci ret = wm8350_register_irq(wm8350, wm8350_reg[pdev->id].irq, 115162306a36Sopenharmony_ci pmic_uv_handler, 0, "UV", rdev); 115262306a36Sopenharmony_ci if (ret < 0) { 115362306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register regulator %s IRQ\n", 115462306a36Sopenharmony_ci wm8350_reg[pdev->id].name); 115562306a36Sopenharmony_ci return ret; 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci return 0; 115962306a36Sopenharmony_ci} 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_cistatic int wm8350_regulator_remove(struct platform_device *pdev) 116262306a36Sopenharmony_ci{ 116362306a36Sopenharmony_ci struct regulator_dev *rdev = platform_get_drvdata(pdev); 116462306a36Sopenharmony_ci struct wm8350 *wm8350 = rdev_get_drvdata(rdev); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci wm8350_free_irq(wm8350, wm8350_reg[pdev->id].irq, rdev); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci return 0; 116962306a36Sopenharmony_ci} 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ciint wm8350_register_regulator(struct wm8350 *wm8350, int reg, 117262306a36Sopenharmony_ci struct regulator_init_data *initdata) 117362306a36Sopenharmony_ci{ 117462306a36Sopenharmony_ci struct platform_device *pdev; 117562306a36Sopenharmony_ci int ret; 117662306a36Sopenharmony_ci if (reg < 0 || reg >= NUM_WM8350_REGULATORS) 117762306a36Sopenharmony_ci return -EINVAL; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci if (wm8350->pmic.pdev[reg]) 118062306a36Sopenharmony_ci return -EBUSY; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci if (reg >= WM8350_DCDC_1 && reg <= WM8350_DCDC_6 && 118362306a36Sopenharmony_ci reg > wm8350->pmic.max_dcdc) 118462306a36Sopenharmony_ci return -ENODEV; 118562306a36Sopenharmony_ci if (reg >= WM8350_ISINK_A && reg <= WM8350_ISINK_B && 118662306a36Sopenharmony_ci reg > wm8350->pmic.max_isink) 118762306a36Sopenharmony_ci return -ENODEV; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci pdev = platform_device_alloc("wm8350-regulator", reg); 119062306a36Sopenharmony_ci if (!pdev) 119162306a36Sopenharmony_ci return -ENOMEM; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci wm8350->pmic.pdev[reg] = pdev; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci initdata->driver_data = wm8350; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci pdev->dev.platform_data = initdata; 119862306a36Sopenharmony_ci pdev->dev.parent = wm8350->dev; 119962306a36Sopenharmony_ci platform_set_drvdata(pdev, wm8350); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci ret = platform_device_add(pdev); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci if (ret != 0) { 120462306a36Sopenharmony_ci dev_err(wm8350->dev, "Failed to register regulator %d: %d\n", 120562306a36Sopenharmony_ci reg, ret); 120662306a36Sopenharmony_ci platform_device_put(pdev); 120762306a36Sopenharmony_ci wm8350->pmic.pdev[reg] = NULL; 120862306a36Sopenharmony_ci } 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci return ret; 121162306a36Sopenharmony_ci} 121262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wm8350_register_regulator); 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci/** 121562306a36Sopenharmony_ci * wm8350_register_led - Register a WM8350 LED output 121662306a36Sopenharmony_ci * 121762306a36Sopenharmony_ci * @wm8350: The WM8350 device to configure. 121862306a36Sopenharmony_ci * @lednum: LED device index to create. 121962306a36Sopenharmony_ci * @dcdc: The DCDC to use for the LED. 122062306a36Sopenharmony_ci * @isink: The ISINK to use for the LED. 122162306a36Sopenharmony_ci * @pdata: Configuration for the LED. 122262306a36Sopenharmony_ci * 122362306a36Sopenharmony_ci * The WM8350 supports the use of an ISINK together with a DCDC to 122462306a36Sopenharmony_ci * provide a power-efficient LED driver. This function registers the 122562306a36Sopenharmony_ci * regulators and instantiates the platform device for a LED. The 122662306a36Sopenharmony_ci * operating modes for the LED regulators must be configured using 122762306a36Sopenharmony_ci * wm8350_isink_set_flash(), wm8350_dcdc25_set_mode() and 122862306a36Sopenharmony_ci * wm8350_dcdc_set_slot() prior to calling this function. 122962306a36Sopenharmony_ci */ 123062306a36Sopenharmony_ciint wm8350_register_led(struct wm8350 *wm8350, int lednum, int dcdc, int isink, 123162306a36Sopenharmony_ci struct wm8350_led_platform_data *pdata) 123262306a36Sopenharmony_ci{ 123362306a36Sopenharmony_ci struct wm8350_led *led; 123462306a36Sopenharmony_ci struct platform_device *pdev; 123562306a36Sopenharmony_ci int ret; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci if (lednum >= ARRAY_SIZE(wm8350->pmic.led) || lednum < 0) { 123862306a36Sopenharmony_ci dev_err(wm8350->dev, "Invalid LED index %d\n", lednum); 123962306a36Sopenharmony_ci return -ENODEV; 124062306a36Sopenharmony_ci } 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci led = &wm8350->pmic.led[lednum]; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci if (led->pdev) { 124562306a36Sopenharmony_ci dev_err(wm8350->dev, "LED %d already allocated\n", lednum); 124662306a36Sopenharmony_ci return -EINVAL; 124762306a36Sopenharmony_ci } 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci pdev = platform_device_alloc("wm8350-led", lednum); 125062306a36Sopenharmony_ci if (pdev == NULL) { 125162306a36Sopenharmony_ci dev_err(wm8350->dev, "Failed to allocate LED %d\n", lednum); 125262306a36Sopenharmony_ci return -ENOMEM; 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci led->isink_consumer.dev_name = dev_name(&pdev->dev); 125662306a36Sopenharmony_ci led->isink_consumer.supply = "led_isink"; 125762306a36Sopenharmony_ci led->isink_init.num_consumer_supplies = 1; 125862306a36Sopenharmony_ci led->isink_init.consumer_supplies = &led->isink_consumer; 125962306a36Sopenharmony_ci led->isink_init.constraints.min_uA = 0; 126062306a36Sopenharmony_ci led->isink_init.constraints.max_uA = pdata->max_uA; 126162306a36Sopenharmony_ci led->isink_init.constraints.valid_ops_mask 126262306a36Sopenharmony_ci = REGULATOR_CHANGE_CURRENT | REGULATOR_CHANGE_STATUS; 126362306a36Sopenharmony_ci led->isink_init.constraints.valid_modes_mask = REGULATOR_MODE_NORMAL; 126462306a36Sopenharmony_ci ret = wm8350_register_regulator(wm8350, isink, &led->isink_init); 126562306a36Sopenharmony_ci if (ret != 0) { 126662306a36Sopenharmony_ci platform_device_put(pdev); 126762306a36Sopenharmony_ci return ret; 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci led->dcdc_consumer.dev_name = dev_name(&pdev->dev); 127162306a36Sopenharmony_ci led->dcdc_consumer.supply = "led_vcc"; 127262306a36Sopenharmony_ci led->dcdc_init.num_consumer_supplies = 1; 127362306a36Sopenharmony_ci led->dcdc_init.consumer_supplies = &led->dcdc_consumer; 127462306a36Sopenharmony_ci led->dcdc_init.constraints.valid_modes_mask = REGULATOR_MODE_NORMAL; 127562306a36Sopenharmony_ci led->dcdc_init.constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS; 127662306a36Sopenharmony_ci ret = wm8350_register_regulator(wm8350, dcdc, &led->dcdc_init); 127762306a36Sopenharmony_ci if (ret != 0) { 127862306a36Sopenharmony_ci platform_device_put(pdev); 127962306a36Sopenharmony_ci return ret; 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci switch (isink) { 128362306a36Sopenharmony_ci case WM8350_ISINK_A: 128462306a36Sopenharmony_ci wm8350->pmic.isink_A_dcdc = dcdc; 128562306a36Sopenharmony_ci break; 128662306a36Sopenharmony_ci case WM8350_ISINK_B: 128762306a36Sopenharmony_ci wm8350->pmic.isink_B_dcdc = dcdc; 128862306a36Sopenharmony_ci break; 128962306a36Sopenharmony_ci } 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci pdev->dev.platform_data = pdata; 129262306a36Sopenharmony_ci pdev->dev.parent = wm8350->dev; 129362306a36Sopenharmony_ci ret = platform_device_add(pdev); 129462306a36Sopenharmony_ci if (ret != 0) { 129562306a36Sopenharmony_ci dev_err(wm8350->dev, "Failed to register LED %d: %d\n", 129662306a36Sopenharmony_ci lednum, ret); 129762306a36Sopenharmony_ci platform_device_put(pdev); 129862306a36Sopenharmony_ci return ret; 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci led->pdev = pdev; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci return 0; 130462306a36Sopenharmony_ci} 130562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wm8350_register_led); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_cistatic struct platform_driver wm8350_regulator_driver = { 130862306a36Sopenharmony_ci .probe = wm8350_regulator_probe, 130962306a36Sopenharmony_ci .remove = wm8350_regulator_remove, 131062306a36Sopenharmony_ci .driver = { 131162306a36Sopenharmony_ci .name = "wm8350-regulator", 131262306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 131362306a36Sopenharmony_ci }, 131462306a36Sopenharmony_ci}; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_cistatic int __init wm8350_regulator_init(void) 131762306a36Sopenharmony_ci{ 131862306a36Sopenharmony_ci return platform_driver_register(&wm8350_regulator_driver); 131962306a36Sopenharmony_ci} 132062306a36Sopenharmony_cisubsys_initcall(wm8350_regulator_init); 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_cistatic void __exit wm8350_regulator_exit(void) 132362306a36Sopenharmony_ci{ 132462306a36Sopenharmony_ci platform_driver_unregister(&wm8350_regulator_driver); 132562306a36Sopenharmony_ci} 132662306a36Sopenharmony_cimodule_exit(wm8350_regulator_exit); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci/* Module information */ 132962306a36Sopenharmony_ciMODULE_AUTHOR("Liam Girdwood"); 133062306a36Sopenharmony_ciMODULE_DESCRIPTION("WM8350 voltage and current regulator driver"); 133162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 133262306a36Sopenharmony_ciMODULE_ALIAS("platform:wm8350-regulator"); 1333