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