183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0
260599ea6SJaehoon Chung /*
360599ea6SJaehoon Chung  *  Copyright (C) 2018 Samsung Electronics
460599ea6SJaehoon Chung  *  Jaehoon Chung <jh80.chung@samsung.com>
560599ea6SJaehoon Chung  */
660599ea6SJaehoon Chung 
760599ea6SJaehoon Chung #include <common.h>
860599ea6SJaehoon Chung #include <fdtdec.h>
960599ea6SJaehoon Chung #include <errno.h>
1060599ea6SJaehoon Chung #include <dm.h>
1160599ea6SJaehoon Chung #include <i2c.h>
1260599ea6SJaehoon Chung #include <power/pmic.h>
1360599ea6SJaehoon Chung #include <power/regulator.h>
1460599ea6SJaehoon Chung #include <power/s2mps11.h>
1560599ea6SJaehoon Chung 
1660599ea6SJaehoon Chung #define MODE(_id, _val, _name) { \
1760599ea6SJaehoon Chung 	.id = _id, \
1860599ea6SJaehoon Chung 	.register_value = _val, \
1960599ea6SJaehoon Chung 	.name = _name, \
2060599ea6SJaehoon Chung }
2160599ea6SJaehoon Chung 
2260599ea6SJaehoon Chung /* BUCK : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 */
2360599ea6SJaehoon Chung static struct dm_regulator_mode s2mps11_buck_modes[] = {
2460599ea6SJaehoon Chung 	MODE(OP_OFF, S2MPS11_BUCK_MODE_OFF, "OFF"),
2560599ea6SJaehoon Chung 	MODE(OP_STANDBY, S2MPS11_BUCK_MODE_STANDBY, "ON/OFF"),
2660599ea6SJaehoon Chung 	MODE(OP_ON, S2MPS11_BUCK_MODE_STANDBY, "ON"),
2760599ea6SJaehoon Chung };
2860599ea6SJaehoon Chung 
2960599ea6SJaehoon Chung static struct dm_regulator_mode s2mps11_ldo_modes[] = {
3060599ea6SJaehoon Chung 	MODE(OP_OFF, S2MPS11_LDO_MODE_OFF, "OFF"),
3160599ea6SJaehoon Chung 	MODE(OP_STANDBY, S2MPS11_LDO_MODE_STANDBY, "ON/OFF"),
3260599ea6SJaehoon Chung 	MODE(OP_STANDBY_LPM, S2MPS11_LDO_MODE_STANDBY_LPM, "ON/LPM"),
3360599ea6SJaehoon Chung 	MODE(OP_ON, S2MPS11_LDO_MODE_ON, "ON"),
3460599ea6SJaehoon Chung };
3560599ea6SJaehoon Chung 
3660599ea6SJaehoon Chung static const char s2mps11_buck_ctrl[] = {
3760599ea6SJaehoon Chung 	0xff, 0x25, 0x27, 0x29, 0x2b, 0x2d, 0x33, 0x35, 0x37, 0x39, 0x3b
3860599ea6SJaehoon Chung };
3960599ea6SJaehoon Chung 
4060599ea6SJaehoon Chung static const char s2mps11_buck_out[] = {
4160599ea6SJaehoon Chung 	0xff, 0x26, 0x28, 0x2a, 0x2c, 0x2f, 0x34, 0x36, 0x38, 0x3a, 0x3c
4260599ea6SJaehoon Chung };
4360599ea6SJaehoon Chung 
s2mps11_buck_hex2volt(int buck,int hex)4460599ea6SJaehoon Chung static int s2mps11_buck_hex2volt(int buck, int hex)
4560599ea6SJaehoon Chung {
4660599ea6SJaehoon Chung 	unsigned int uV = 0;
4760599ea6SJaehoon Chung 
4860599ea6SJaehoon Chung 	if (hex < 0)
4960599ea6SJaehoon Chung 		goto bad;
5060599ea6SJaehoon Chung 
5160599ea6SJaehoon Chung 	switch (buck) {
5260599ea6SJaehoon Chung 	case 7:
5360599ea6SJaehoon Chung 	case 8:
5460599ea6SJaehoon Chung 	case 10:
5560599ea6SJaehoon Chung 		if (hex > S2MPS11_BUCK7_8_10_VOLT_MAX_HEX)
5660599ea6SJaehoon Chung 			goto bad;
5760599ea6SJaehoon Chung 
5860599ea6SJaehoon Chung 		uV = hex * S2MPS11_BUCK_HSTEP + S2MPS11_BUCK_UV_HMIN;
5960599ea6SJaehoon Chung 		break;
6060599ea6SJaehoon Chung 	case 9:
6160599ea6SJaehoon Chung 		if (hex > S2MPS11_BUCK9_VOLT_MAX_HEX)
6260599ea6SJaehoon Chung 			goto bad;
6360599ea6SJaehoon Chung 		uV = hex * S2MPS11_BUCK9_STEP * 2 + S2MPS11_BUCK9_UV_MIN;
6460599ea6SJaehoon Chung 		break;
6560599ea6SJaehoon Chung 	default:
6660599ea6SJaehoon Chung 		if (buck == 5 && hex > S2MPS11_BUCK5_VOLT_MAX_HEX)
6760599ea6SJaehoon Chung 			goto bad;
6860599ea6SJaehoon Chung 		else if (buck != 5 && hex > S2MPS11_BUCK_VOLT_MAX_HEX)
6960599ea6SJaehoon Chung 			goto bad;
7060599ea6SJaehoon Chung 
7160599ea6SJaehoon Chung 		uV = hex * S2MPS11_BUCK_LSTEP + S2MPS11_BUCK_UV_MIN;
7260599ea6SJaehoon Chung 		break;
7360599ea6SJaehoon Chung 	}
7460599ea6SJaehoon Chung 
7560599ea6SJaehoon Chung 	return uV;
7660599ea6SJaehoon Chung bad:
7760599ea6SJaehoon Chung 	pr_err("Value: %#x is wrong for BUCK%d", hex, buck);
7860599ea6SJaehoon Chung 	return -EINVAL;
7960599ea6SJaehoon Chung }
8060599ea6SJaehoon Chung 
s2mps11_buck_volt2hex(int buck,int uV)8160599ea6SJaehoon Chung static int s2mps11_buck_volt2hex(int buck, int uV)
8260599ea6SJaehoon Chung {
8360599ea6SJaehoon Chung 	int hex;
8460599ea6SJaehoon Chung 
8560599ea6SJaehoon Chung 	switch (buck) {
8660599ea6SJaehoon Chung 	case 7:
8760599ea6SJaehoon Chung 	case 8:
8860599ea6SJaehoon Chung 	case 10:
8960599ea6SJaehoon Chung 		hex = (uV - S2MPS11_BUCK_UV_HMIN) / S2MPS11_BUCK_HSTEP;
9060599ea6SJaehoon Chung 		if (hex > S2MPS11_BUCK7_8_10_VOLT_MAX_HEX)
9160599ea6SJaehoon Chung 			goto bad;
9260599ea6SJaehoon Chung 
9360599ea6SJaehoon Chung 		break;
9460599ea6SJaehoon Chung 	case 9:
9560599ea6SJaehoon Chung 		hex = (uV - S2MPS11_BUCK9_UV_MIN) / S2MPS11_BUCK9_STEP;
9660599ea6SJaehoon Chung 		if (hex > S2MPS11_BUCK9_VOLT_MAX_HEX)
9760599ea6SJaehoon Chung 			goto bad;
9860599ea6SJaehoon Chung 		break;
9960599ea6SJaehoon Chung 	default:
10060599ea6SJaehoon Chung 		hex = (uV - S2MPS11_BUCK_UV_MIN) / S2MPS11_BUCK_LSTEP;
10160599ea6SJaehoon Chung 		if (buck == 5 && hex > S2MPS11_BUCK5_VOLT_MAX_HEX)
10260599ea6SJaehoon Chung 			goto bad;
10360599ea6SJaehoon Chung 		else if (buck != 5 && hex > S2MPS11_BUCK_VOLT_MAX_HEX)
10460599ea6SJaehoon Chung 			goto bad;
10560599ea6SJaehoon Chung 		break;
10660599ea6SJaehoon Chung 	};
10760599ea6SJaehoon Chung 
10860599ea6SJaehoon Chung 	if (hex >= 0)
10960599ea6SJaehoon Chung 		return hex;
11060599ea6SJaehoon Chung 
11160599ea6SJaehoon Chung bad:
11260599ea6SJaehoon Chung 	pr_err("Value: %d uV is wrong for BUCK%d", uV, buck);
11360599ea6SJaehoon Chung 	return -EINVAL;
11460599ea6SJaehoon Chung }
11560599ea6SJaehoon Chung 
s2mps11_buck_val(struct udevice * dev,int op,int * uV)11660599ea6SJaehoon Chung static int s2mps11_buck_val(struct udevice *dev, int op, int *uV)
11760599ea6SJaehoon Chung {
11860599ea6SJaehoon Chung 	int hex, buck, ret;
11960599ea6SJaehoon Chung 	u32 mask, addr;
12060599ea6SJaehoon Chung 	u8 val;
12160599ea6SJaehoon Chung 
12260599ea6SJaehoon Chung 	buck = dev->driver_data;
12360599ea6SJaehoon Chung 	if (buck < 1 || buck > S2MPS11_BUCK_NUM) {
12460599ea6SJaehoon Chung 		pr_err("Wrong buck number: %d\n", buck);
12560599ea6SJaehoon Chung 		return -EINVAL;
12660599ea6SJaehoon Chung 	}
12760599ea6SJaehoon Chung 
12860599ea6SJaehoon Chung 	if (op == PMIC_OP_GET)
12960599ea6SJaehoon Chung 		*uV = 0;
13060599ea6SJaehoon Chung 
13160599ea6SJaehoon Chung 	addr = s2mps11_buck_out[buck];
13260599ea6SJaehoon Chung 
13360599ea6SJaehoon Chung 	switch (buck) {
13460599ea6SJaehoon Chung 	case 9:
13560599ea6SJaehoon Chung 		mask = S2MPS11_BUCK9_VOLT_MASK;
13660599ea6SJaehoon Chung 		break;
13760599ea6SJaehoon Chung 	default:
13860599ea6SJaehoon Chung 		mask = S2MPS11_BUCK_VOLT_MASK;
13960599ea6SJaehoon Chung 		break;
14060599ea6SJaehoon Chung 	}
14160599ea6SJaehoon Chung 
14260599ea6SJaehoon Chung 	ret = pmic_read(dev->parent, addr, &val, 1);
14360599ea6SJaehoon Chung 	if (ret)
14460599ea6SJaehoon Chung 		return ret;
14560599ea6SJaehoon Chung 
14660599ea6SJaehoon Chung 	if (op == PMIC_OP_GET) {
14760599ea6SJaehoon Chung 		val &= mask;
14860599ea6SJaehoon Chung 		ret = s2mps11_buck_hex2volt(buck, val);
14960599ea6SJaehoon Chung 		if (ret < 0)
15060599ea6SJaehoon Chung 			return ret;
15160599ea6SJaehoon Chung 		*uV = ret;
15260599ea6SJaehoon Chung 		return 0;
15360599ea6SJaehoon Chung 	}
15460599ea6SJaehoon Chung 
15560599ea6SJaehoon Chung 	hex = s2mps11_buck_volt2hex(buck, *uV);
15660599ea6SJaehoon Chung 	if (hex < 0)
15760599ea6SJaehoon Chung 		return hex;
15860599ea6SJaehoon Chung 
15960599ea6SJaehoon Chung 	val &= ~mask;
16060599ea6SJaehoon Chung 	val |= hex;
16160599ea6SJaehoon Chung 	ret = pmic_write(dev->parent, addr, &val, 1);
16260599ea6SJaehoon Chung 
16360599ea6SJaehoon Chung 	return ret;
16460599ea6SJaehoon Chung }
16560599ea6SJaehoon Chung 
s2mps11_buck_mode(struct udevice * dev,int op,int * opmode)16660599ea6SJaehoon Chung static int s2mps11_buck_mode(struct udevice *dev, int op, int *opmode)
16760599ea6SJaehoon Chung {
16860599ea6SJaehoon Chung 	unsigned int addr, mode;
16960599ea6SJaehoon Chung 	unsigned char val;
17060599ea6SJaehoon Chung 	int buck, ret;
17160599ea6SJaehoon Chung 
17260599ea6SJaehoon Chung 	buck = dev->driver_data;
17360599ea6SJaehoon Chung 	if (buck < 1 || buck > S2MPS11_BUCK_NUM) {
17460599ea6SJaehoon Chung 		pr_err("Wrong buck number: %d\n", buck);
17560599ea6SJaehoon Chung 		return -EINVAL;
17660599ea6SJaehoon Chung 	}
17760599ea6SJaehoon Chung 
17860599ea6SJaehoon Chung 	addr = s2mps11_buck_ctrl[buck];
17960599ea6SJaehoon Chung 
18060599ea6SJaehoon Chung 	ret = pmic_read(dev->parent, addr, &val, 1);
18160599ea6SJaehoon Chung 	if (ret)
18260599ea6SJaehoon Chung 		return ret;
18360599ea6SJaehoon Chung 
18460599ea6SJaehoon Chung 	if (op == PMIC_OP_GET) {
18560599ea6SJaehoon Chung 		val &= (S2MPS11_BUCK_MODE_MASK << S2MPS11_BUCK_MODE_SHIFT);
18660599ea6SJaehoon Chung 		switch (val) {
18760599ea6SJaehoon Chung 		case S2MPS11_BUCK_MODE_OFF:
18860599ea6SJaehoon Chung 			*opmode = OP_OFF;
18960599ea6SJaehoon Chung 			break;
19060599ea6SJaehoon Chung 		case S2MPS11_BUCK_MODE_STANDBY:
19160599ea6SJaehoon Chung 			*opmode = OP_STANDBY;
19260599ea6SJaehoon Chung 			break;
19360599ea6SJaehoon Chung 		case S2MPS11_BUCK_MODE_ON:
19460599ea6SJaehoon Chung 			*opmode = OP_ON;
19560599ea6SJaehoon Chung 			break;
19660599ea6SJaehoon Chung 		default:
19760599ea6SJaehoon Chung 			return -EINVAL;
19860599ea6SJaehoon Chung 		}
19960599ea6SJaehoon Chung 		return 0;
20060599ea6SJaehoon Chung 	}
20160599ea6SJaehoon Chung 
20260599ea6SJaehoon Chung 	switch (*opmode) {
20360599ea6SJaehoon Chung 	case OP_OFF:
20460599ea6SJaehoon Chung 		mode = S2MPS11_BUCK_MODE_OFF;
20560599ea6SJaehoon Chung 		break;
20660599ea6SJaehoon Chung 	case OP_STANDBY:
20760599ea6SJaehoon Chung 		mode = S2MPS11_BUCK_MODE_STANDBY;
20860599ea6SJaehoon Chung 		break;
20960599ea6SJaehoon Chung 	case OP_ON:
21060599ea6SJaehoon Chung 		mode = S2MPS11_BUCK_MODE_ON;
21160599ea6SJaehoon Chung 		break;
21260599ea6SJaehoon Chung 	default:
21360599ea6SJaehoon Chung 		pr_err("Wrong mode: %d for buck: %d\n", *opmode, buck);
21460599ea6SJaehoon Chung 		return -EINVAL;
21560599ea6SJaehoon Chung 	}
21660599ea6SJaehoon Chung 
21760599ea6SJaehoon Chung 	val &= ~(S2MPS11_BUCK_MODE_MASK << S2MPS11_BUCK_MODE_SHIFT);
21860599ea6SJaehoon Chung 	val |= mode;
21960599ea6SJaehoon Chung 	ret = pmic_write(dev->parent, addr, &val, 1);
22060599ea6SJaehoon Chung 
22160599ea6SJaehoon Chung 	return ret;
22260599ea6SJaehoon Chung }
22360599ea6SJaehoon Chung 
s2mps11_buck_enable(struct udevice * dev,int op,bool * enable)22460599ea6SJaehoon Chung static int s2mps11_buck_enable(struct udevice *dev, int op, bool *enable)
22560599ea6SJaehoon Chung {
22660599ea6SJaehoon Chung 	int ret, on_off;
22760599ea6SJaehoon Chung 
22860599ea6SJaehoon Chung 	if (op == PMIC_OP_GET) {
22960599ea6SJaehoon Chung 		ret = s2mps11_buck_mode(dev, op, &on_off);
23060599ea6SJaehoon Chung 		if (ret)
23160599ea6SJaehoon Chung 			return ret;
23260599ea6SJaehoon Chung 		switch (on_off) {
23360599ea6SJaehoon Chung 		case OP_OFF:
23460599ea6SJaehoon Chung 			*enable = false;
23560599ea6SJaehoon Chung 			break;
23660599ea6SJaehoon Chung 		case OP_ON:
23760599ea6SJaehoon Chung 			*enable = true;
23860599ea6SJaehoon Chung 			break;
23960599ea6SJaehoon Chung 		default:
24060599ea6SJaehoon Chung 			return -EINVAL;
24160599ea6SJaehoon Chung 		}
24260599ea6SJaehoon Chung 	} else if (op == PMIC_OP_SET) {
24360599ea6SJaehoon Chung 		if (*enable)
24460599ea6SJaehoon Chung 			on_off = OP_ON;
24560599ea6SJaehoon Chung 		else
24660599ea6SJaehoon Chung 			on_off = OP_OFF;
24760599ea6SJaehoon Chung 
24860599ea6SJaehoon Chung 		ret = s2mps11_buck_mode(dev, op, &on_off);
24960599ea6SJaehoon Chung 		if (ret)
25060599ea6SJaehoon Chung 			return ret;
25160599ea6SJaehoon Chung 	}
25260599ea6SJaehoon Chung 
25360599ea6SJaehoon Chung 	return 0;
25460599ea6SJaehoon Chung }
25560599ea6SJaehoon Chung 
buck_get_value(struct udevice * dev)25660599ea6SJaehoon Chung static int buck_get_value(struct udevice *dev)
25760599ea6SJaehoon Chung {
25860599ea6SJaehoon Chung 	int uV;
25960599ea6SJaehoon Chung 	int ret;
26060599ea6SJaehoon Chung 
26160599ea6SJaehoon Chung 	ret = s2mps11_buck_val(dev, PMIC_OP_GET, &uV);
26260599ea6SJaehoon Chung 	if (ret)
26360599ea6SJaehoon Chung 		return ret;
26460599ea6SJaehoon Chung 	return uV;
26560599ea6SJaehoon Chung }
26660599ea6SJaehoon Chung 
buck_set_value(struct udevice * dev,int uV)26760599ea6SJaehoon Chung static int buck_set_value(struct udevice *dev, int uV)
26860599ea6SJaehoon Chung {
26960599ea6SJaehoon Chung 	return s2mps11_buck_val(dev, PMIC_OP_SET, &uV);
27060599ea6SJaehoon Chung }
27160599ea6SJaehoon Chung 
buck_get_enable(struct udevice * dev)27260599ea6SJaehoon Chung static int buck_get_enable(struct udevice *dev)
27360599ea6SJaehoon Chung {
27460599ea6SJaehoon Chung 	bool enable = false;
27560599ea6SJaehoon Chung 	int ret;
27660599ea6SJaehoon Chung 
27760599ea6SJaehoon Chung 	ret = s2mps11_buck_enable(dev, PMIC_OP_GET, &enable);
27860599ea6SJaehoon Chung 	if (ret)
27960599ea6SJaehoon Chung 		return ret;
28060599ea6SJaehoon Chung 	return enable;
28160599ea6SJaehoon Chung }
28260599ea6SJaehoon Chung 
buck_set_enable(struct udevice * dev,bool enable)28360599ea6SJaehoon Chung static int buck_set_enable(struct udevice *dev, bool enable)
28460599ea6SJaehoon Chung {
28560599ea6SJaehoon Chung 	return s2mps11_buck_enable(dev, PMIC_OP_SET, &enable);
28660599ea6SJaehoon Chung }
28760599ea6SJaehoon Chung 
buck_get_mode(struct udevice * dev)28860599ea6SJaehoon Chung static int buck_get_mode(struct udevice *dev)
28960599ea6SJaehoon Chung {
29060599ea6SJaehoon Chung 	int mode;
29160599ea6SJaehoon Chung 	int ret;
29260599ea6SJaehoon Chung 
29360599ea6SJaehoon Chung 	ret = s2mps11_buck_mode(dev, PMIC_OP_GET, &mode);
29460599ea6SJaehoon Chung 	if (ret)
29560599ea6SJaehoon Chung 		return ret;
29660599ea6SJaehoon Chung 
29760599ea6SJaehoon Chung 	return mode;
29860599ea6SJaehoon Chung }
29960599ea6SJaehoon Chung 
buck_set_mode(struct udevice * dev,int mode)30060599ea6SJaehoon Chung static int buck_set_mode(struct udevice *dev, int mode)
30160599ea6SJaehoon Chung {
30260599ea6SJaehoon Chung 	return s2mps11_buck_mode(dev, PMIC_OP_SET, &mode);
30360599ea6SJaehoon Chung }
30460599ea6SJaehoon Chung 
s2mps11_buck_probe(struct udevice * dev)30560599ea6SJaehoon Chung static int s2mps11_buck_probe(struct udevice *dev)
30660599ea6SJaehoon Chung {
30760599ea6SJaehoon Chung 	struct dm_regulator_uclass_platdata *uc_pdata;
30860599ea6SJaehoon Chung 
30960599ea6SJaehoon Chung 	uc_pdata = dev_get_uclass_platdata(dev);
31060599ea6SJaehoon Chung 
31160599ea6SJaehoon Chung 	uc_pdata->type = REGULATOR_TYPE_BUCK;
31260599ea6SJaehoon Chung 	uc_pdata->mode = s2mps11_buck_modes;
31360599ea6SJaehoon Chung 	uc_pdata->mode_count = ARRAY_SIZE(s2mps11_buck_modes);
31460599ea6SJaehoon Chung 
31560599ea6SJaehoon Chung 	return 0;
31660599ea6SJaehoon Chung }
31760599ea6SJaehoon Chung 
31860599ea6SJaehoon Chung static const struct dm_regulator_ops s2mps11_buck_ops = {
31960599ea6SJaehoon Chung 	.get_value	= buck_get_value,
32060599ea6SJaehoon Chung 	.set_value	= buck_set_value,
32160599ea6SJaehoon Chung 	.get_enable	= buck_get_enable,
32260599ea6SJaehoon Chung 	.set_enable	= buck_set_enable,
32360599ea6SJaehoon Chung 	.get_mode	= buck_get_mode,
32460599ea6SJaehoon Chung 	.set_mode	= buck_set_mode,
32560599ea6SJaehoon Chung };
32660599ea6SJaehoon Chung 
32760599ea6SJaehoon Chung U_BOOT_DRIVER(s2mps11_buck) = {
32860599ea6SJaehoon Chung 	.name = S2MPS11_BUCK_DRIVER,
32960599ea6SJaehoon Chung 	.id = UCLASS_REGULATOR,
33060599ea6SJaehoon Chung 	.ops = &s2mps11_buck_ops,
33160599ea6SJaehoon Chung 	.probe = s2mps11_buck_probe,
33260599ea6SJaehoon Chung };
33360599ea6SJaehoon Chung 
s2mps11_ldo_hex2volt(int ldo,int hex)33460599ea6SJaehoon Chung static int s2mps11_ldo_hex2volt(int ldo, int hex)
33560599ea6SJaehoon Chung {
33660599ea6SJaehoon Chung 	unsigned int uV = 0;
33760599ea6SJaehoon Chung 
33860599ea6SJaehoon Chung 	if (hex > S2MPS11_LDO_VOLT_MAX_HEX) {
33960599ea6SJaehoon Chung 		pr_err("Value: %#x is wrong for LDO%d", hex, ldo);
34060599ea6SJaehoon Chung 		return -EINVAL;
34160599ea6SJaehoon Chung 	}
34260599ea6SJaehoon Chung 
34360599ea6SJaehoon Chung 	switch (ldo) {
34460599ea6SJaehoon Chung 	case 1:
34560599ea6SJaehoon Chung 	case 6:
34660599ea6SJaehoon Chung 	case 11:
34760599ea6SJaehoon Chung 	case 22:
34860599ea6SJaehoon Chung 	case 23:
349311eaf74SKrzysztof Kozlowski 	case 27:
350311eaf74SKrzysztof Kozlowski 	case 35:
35160599ea6SJaehoon Chung 		uV = hex * S2MPS11_LDO_STEP + S2MPS11_LDO_UV_MIN;
35260599ea6SJaehoon Chung 		break;
35360599ea6SJaehoon Chung 	default:
35460599ea6SJaehoon Chung 		uV = hex * S2MPS11_LDO_STEP * 2 + S2MPS11_LDO_UV_MIN;
35560599ea6SJaehoon Chung 		break;
35660599ea6SJaehoon Chung 	}
35760599ea6SJaehoon Chung 
35860599ea6SJaehoon Chung 	return uV;
35960599ea6SJaehoon Chung }
36060599ea6SJaehoon Chung 
s2mps11_ldo_volt2hex(int ldo,int uV)36160599ea6SJaehoon Chung static int s2mps11_ldo_volt2hex(int ldo, int uV)
36260599ea6SJaehoon Chung {
36360599ea6SJaehoon Chung 	int hex = 0;
36460599ea6SJaehoon Chung 
36560599ea6SJaehoon Chung 	switch (ldo) {
36660599ea6SJaehoon Chung 	case 1:
36760599ea6SJaehoon Chung 	case 6:
36860599ea6SJaehoon Chung 	case 11:
36960599ea6SJaehoon Chung 	case 22:
37060599ea6SJaehoon Chung 	case 23:
371311eaf74SKrzysztof Kozlowski 	case 27:
372311eaf74SKrzysztof Kozlowski 	case 35:
37360599ea6SJaehoon Chung 		hex = (uV - S2MPS11_LDO_UV_MIN) / S2MPS11_LDO_STEP;
37460599ea6SJaehoon Chung 		break;
37560599ea6SJaehoon Chung 	default:
37660599ea6SJaehoon Chung 		hex = (uV - S2MPS11_LDO_UV_MIN) / (S2MPS11_LDO_STEP * 2);
37760599ea6SJaehoon Chung 		break;
37860599ea6SJaehoon Chung 	}
37960599ea6SJaehoon Chung 
38060599ea6SJaehoon Chung 	if (hex >= 0 && hex <= S2MPS11_LDO_VOLT_MAX_HEX)
38160599ea6SJaehoon Chung 		return hex;
38260599ea6SJaehoon Chung 
38360599ea6SJaehoon Chung 	pr_err("Value: %d uV is wrong for LDO%d", uV, ldo);
38460599ea6SJaehoon Chung 	return -EINVAL;
38560599ea6SJaehoon Chung 
38660599ea6SJaehoon Chung 	return 0;
38760599ea6SJaehoon Chung }
38860599ea6SJaehoon Chung 
s2mps11_ldo_val(struct udevice * dev,int op,int * uV)38960599ea6SJaehoon Chung static int s2mps11_ldo_val(struct udevice *dev, int op, int *uV)
39060599ea6SJaehoon Chung {
39160599ea6SJaehoon Chung 	unsigned int addr;
39260599ea6SJaehoon Chung 	unsigned char val;
39360599ea6SJaehoon Chung 	int hex, ldo, ret;
39460599ea6SJaehoon Chung 
39560599ea6SJaehoon Chung 	ldo = dev->driver_data;
39660599ea6SJaehoon Chung 	if (ldo < 1 || ldo > S2MPS11_LDO_NUM) {
39760599ea6SJaehoon Chung 		pr_err("Wrong ldo number: %d\n", ldo);
39860599ea6SJaehoon Chung 		return -EINVAL;
39960599ea6SJaehoon Chung 	}
40060599ea6SJaehoon Chung 
40160599ea6SJaehoon Chung 	addr = S2MPS11_REG_L1CTRL + ldo - 1;
40260599ea6SJaehoon Chung 
40360599ea6SJaehoon Chung 	ret = pmic_read(dev->parent, addr, &val, 1);
40460599ea6SJaehoon Chung 	if (ret)
40560599ea6SJaehoon Chung 		return ret;
40660599ea6SJaehoon Chung 
40760599ea6SJaehoon Chung 	if (op == PMIC_OP_GET) {
40860599ea6SJaehoon Chung 		*uV = 0;
40960599ea6SJaehoon Chung 		val &= S2MPS11_LDO_VOLT_MASK;
41060599ea6SJaehoon Chung 		ret = s2mps11_ldo_hex2volt(ldo, val);
41160599ea6SJaehoon Chung 		if (ret < 0)
41260599ea6SJaehoon Chung 			return ret;
41360599ea6SJaehoon Chung 
41460599ea6SJaehoon Chung 		*uV = ret;
41560599ea6SJaehoon Chung 		return 0;
41660599ea6SJaehoon Chung 	}
41760599ea6SJaehoon Chung 
41860599ea6SJaehoon Chung 	hex = s2mps11_ldo_volt2hex(ldo, *uV);
41960599ea6SJaehoon Chung 	if (hex < 0)
42060599ea6SJaehoon Chung 		return hex;
42160599ea6SJaehoon Chung 
42260599ea6SJaehoon Chung 	val &= ~S2MPS11_LDO_VOLT_MASK;
42360599ea6SJaehoon Chung 	val |= hex;
42460599ea6SJaehoon Chung 	ret = pmic_write(dev->parent, addr, &val, 1);
42560599ea6SJaehoon Chung 
42660599ea6SJaehoon Chung 	return ret;
42760599ea6SJaehoon Chung }
42860599ea6SJaehoon Chung 
s2mps11_ldo_mode(struct udevice * dev,int op,int * opmode)42960599ea6SJaehoon Chung static int s2mps11_ldo_mode(struct udevice *dev, int op, int *opmode)
43060599ea6SJaehoon Chung {
43160599ea6SJaehoon Chung 	unsigned int addr, mode;
43260599ea6SJaehoon Chung 	unsigned char val;
43360599ea6SJaehoon Chung 	int ldo, ret;
43460599ea6SJaehoon Chung 
43560599ea6SJaehoon Chung 	ldo = dev->driver_data;
43660599ea6SJaehoon Chung 	if (ldo < 1 || ldo > S2MPS11_LDO_NUM) {
43760599ea6SJaehoon Chung 		pr_err("Wrong ldo number: %d\n", ldo);
43860599ea6SJaehoon Chung 		return -EINVAL;
43960599ea6SJaehoon Chung 	}
44060599ea6SJaehoon Chung 	addr = S2MPS11_REG_L1CTRL + ldo - 1;
44160599ea6SJaehoon Chung 
44260599ea6SJaehoon Chung 	ret = pmic_read(dev->parent, addr, &val, 1);
44360599ea6SJaehoon Chung 	if (ret)
44460599ea6SJaehoon Chung 		return ret;
44560599ea6SJaehoon Chung 
44660599ea6SJaehoon Chung 	if (op == PMIC_OP_GET) {
44760599ea6SJaehoon Chung 		val &= (S2MPS11_LDO_MODE_MASK << S2MPS11_LDO_MODE_SHIFT);
44860599ea6SJaehoon Chung 		switch (val) {
44960599ea6SJaehoon Chung 		case S2MPS11_LDO_MODE_OFF:
45060599ea6SJaehoon Chung 			*opmode = OP_OFF;
45160599ea6SJaehoon Chung 			break;
45260599ea6SJaehoon Chung 		case S2MPS11_LDO_MODE_STANDBY:
45360599ea6SJaehoon Chung 			*opmode = OP_STANDBY;
45460599ea6SJaehoon Chung 			break;
45560599ea6SJaehoon Chung 		case S2MPS11_LDO_MODE_STANDBY_LPM:
45660599ea6SJaehoon Chung 			*opmode = OP_STANDBY_LPM;
45760599ea6SJaehoon Chung 			break;
45860599ea6SJaehoon Chung 		case S2MPS11_LDO_MODE_ON:
45960599ea6SJaehoon Chung 			*opmode = OP_ON;
46060599ea6SJaehoon Chung 			break;
46160599ea6SJaehoon Chung 		default:
46260599ea6SJaehoon Chung 			return -EINVAL;
46360599ea6SJaehoon Chung 		}
46460599ea6SJaehoon Chung 		return 0;
46560599ea6SJaehoon Chung 	}
46660599ea6SJaehoon Chung 
46760599ea6SJaehoon Chung 	switch (*opmode) {
46860599ea6SJaehoon Chung 	case OP_OFF:
46960599ea6SJaehoon Chung 		mode = S2MPS11_LDO_MODE_OFF;
47060599ea6SJaehoon Chung 		break;
47160599ea6SJaehoon Chung 	case OP_STANDBY:
47260599ea6SJaehoon Chung 		mode = S2MPS11_LDO_MODE_STANDBY;
47360599ea6SJaehoon Chung 		break;
47460599ea6SJaehoon Chung 	case OP_STANDBY_LPM:
47560599ea6SJaehoon Chung 		mode = S2MPS11_LDO_MODE_STANDBY_LPM;
47660599ea6SJaehoon Chung 		break;
47760599ea6SJaehoon Chung 	case OP_ON:
47860599ea6SJaehoon Chung 		mode = S2MPS11_LDO_MODE_ON;
47960599ea6SJaehoon Chung 		break;
48060599ea6SJaehoon Chung 	default:
48160599ea6SJaehoon Chung 		pr_err("Wrong mode: %d for ldo: %d\n", *opmode, ldo);
48260599ea6SJaehoon Chung 		return -EINVAL;
48360599ea6SJaehoon Chung 	}
48460599ea6SJaehoon Chung 
48560599ea6SJaehoon Chung 	val &= ~(S2MPS11_LDO_MODE_MASK << S2MPS11_LDO_MODE_SHIFT);
48660599ea6SJaehoon Chung 	val |= mode;
48760599ea6SJaehoon Chung 	ret = pmic_write(dev->parent, addr, &val, 1);
48860599ea6SJaehoon Chung 
48960599ea6SJaehoon Chung 	return ret;
49060599ea6SJaehoon Chung }
49160599ea6SJaehoon Chung 
s2mps11_ldo_enable(struct udevice * dev,int op,bool * enable)49260599ea6SJaehoon Chung static int s2mps11_ldo_enable(struct udevice *dev, int op, bool *enable)
49360599ea6SJaehoon Chung {
49460599ea6SJaehoon Chung 	int ret, on_off;
49560599ea6SJaehoon Chung 
49660599ea6SJaehoon Chung 	if (op == PMIC_OP_GET) {
49760599ea6SJaehoon Chung 		ret = s2mps11_ldo_mode(dev, op, &on_off);
49860599ea6SJaehoon Chung 		if (ret)
49960599ea6SJaehoon Chung 			return ret;
50060599ea6SJaehoon Chung 		switch (on_off) {
50160599ea6SJaehoon Chung 		case OP_OFF:
50260599ea6SJaehoon Chung 			*enable = false;
50360599ea6SJaehoon Chung 			break;
50460599ea6SJaehoon Chung 		case OP_ON:
50560599ea6SJaehoon Chung 			*enable = true;
50660599ea6SJaehoon Chung 			break;
50760599ea6SJaehoon Chung 		default:
50860599ea6SJaehoon Chung 			return -EINVAL;
50960599ea6SJaehoon Chung 		}
51060599ea6SJaehoon Chung 	} else if (op == PMIC_OP_SET) {
51160599ea6SJaehoon Chung 		if (*enable)
51260599ea6SJaehoon Chung 			on_off = OP_ON;
51360599ea6SJaehoon Chung 		else
51460599ea6SJaehoon Chung 			on_off = OP_OFF;
51560599ea6SJaehoon Chung 
51660599ea6SJaehoon Chung 		ret = s2mps11_ldo_mode(dev, op, &on_off);
51760599ea6SJaehoon Chung 		if (ret)
51860599ea6SJaehoon Chung 			return ret;
51960599ea6SJaehoon Chung 	}
52060599ea6SJaehoon Chung 
52160599ea6SJaehoon Chung 	return 0;
52260599ea6SJaehoon Chung }
52360599ea6SJaehoon Chung 
ldo_get_value(struct udevice * dev)52460599ea6SJaehoon Chung static int ldo_get_value(struct udevice *dev)
52560599ea6SJaehoon Chung {
52660599ea6SJaehoon Chung 	int uV;
52760599ea6SJaehoon Chung 	int ret;
52860599ea6SJaehoon Chung 
52960599ea6SJaehoon Chung 	ret = s2mps11_ldo_val(dev, PMIC_OP_GET, &uV);
53060599ea6SJaehoon Chung 	if (ret)
53160599ea6SJaehoon Chung 		return ret;
53260599ea6SJaehoon Chung 
53360599ea6SJaehoon Chung 	return uV;
53460599ea6SJaehoon Chung }
53560599ea6SJaehoon Chung 
ldo_set_value(struct udevice * dev,int uV)53660599ea6SJaehoon Chung static int ldo_set_value(struct udevice *dev, int uV)
53760599ea6SJaehoon Chung {
53860599ea6SJaehoon Chung 	return s2mps11_ldo_val(dev, PMIC_OP_SET, &uV);
53960599ea6SJaehoon Chung }
54060599ea6SJaehoon Chung 
ldo_get_enable(struct udevice * dev)54160599ea6SJaehoon Chung static int ldo_get_enable(struct udevice *dev)
54260599ea6SJaehoon Chung {
54360599ea6SJaehoon Chung 	bool enable = false;
54460599ea6SJaehoon Chung 	int ret;
54560599ea6SJaehoon Chung 
54660599ea6SJaehoon Chung 	ret = s2mps11_ldo_enable(dev, PMIC_OP_GET, &enable);
54760599ea6SJaehoon Chung 	if (ret)
54860599ea6SJaehoon Chung 		return ret;
54960599ea6SJaehoon Chung 	return enable;
55060599ea6SJaehoon Chung }
55160599ea6SJaehoon Chung 
ldo_set_enable(struct udevice * dev,bool enable)55260599ea6SJaehoon Chung static int ldo_set_enable(struct udevice *dev, bool enable)
55360599ea6SJaehoon Chung {
554*000ee4b7SKrzysztof Kozlowski 	int ret;
555*000ee4b7SKrzysztof Kozlowski 
556*000ee4b7SKrzysztof Kozlowski 	ret = s2mps11_ldo_enable(dev, PMIC_OP_SET, &enable);
557*000ee4b7SKrzysztof Kozlowski 	if (ret)
558*000ee4b7SKrzysztof Kozlowski 		return ret;
559*000ee4b7SKrzysztof Kozlowski 
560*000ee4b7SKrzysztof Kozlowski 	/* Wait the "enable delay" for voltage to start to rise */
561*000ee4b7SKrzysztof Kozlowski 	udelay(15);
562*000ee4b7SKrzysztof Kozlowski 
563*000ee4b7SKrzysztof Kozlowski 	return 0;
56460599ea6SJaehoon Chung }
56560599ea6SJaehoon Chung 
ldo_get_mode(struct udevice * dev)56660599ea6SJaehoon Chung static int ldo_get_mode(struct udevice *dev)
56760599ea6SJaehoon Chung {
56860599ea6SJaehoon Chung 	int mode, ret;
56960599ea6SJaehoon Chung 
57060599ea6SJaehoon Chung 	ret = s2mps11_ldo_mode(dev, PMIC_OP_GET, &mode);
57160599ea6SJaehoon Chung 	if (ret)
57260599ea6SJaehoon Chung 		return ret;
57360599ea6SJaehoon Chung 	return mode;
57460599ea6SJaehoon Chung }
57560599ea6SJaehoon Chung 
ldo_set_mode(struct udevice * dev,int mode)57660599ea6SJaehoon Chung static int ldo_set_mode(struct udevice *dev, int mode)
57760599ea6SJaehoon Chung {
57860599ea6SJaehoon Chung 	return s2mps11_ldo_mode(dev, PMIC_OP_SET, &mode);
57960599ea6SJaehoon Chung }
58060599ea6SJaehoon Chung 
s2mps11_ldo_probe(struct udevice * dev)58160599ea6SJaehoon Chung static int s2mps11_ldo_probe(struct udevice *dev)
58260599ea6SJaehoon Chung {
58360599ea6SJaehoon Chung 	struct dm_regulator_uclass_platdata *uc_pdata;
58460599ea6SJaehoon Chung 
58560599ea6SJaehoon Chung 	uc_pdata = dev_get_uclass_platdata(dev);
58660599ea6SJaehoon Chung 	uc_pdata->type = REGULATOR_TYPE_LDO;
58760599ea6SJaehoon Chung 	uc_pdata->mode = s2mps11_ldo_modes;
58860599ea6SJaehoon Chung 	uc_pdata->mode_count = ARRAY_SIZE(s2mps11_ldo_modes);
58960599ea6SJaehoon Chung 
59060599ea6SJaehoon Chung 	return 0;
59160599ea6SJaehoon Chung }
59260599ea6SJaehoon Chung 
59360599ea6SJaehoon Chung static const struct dm_regulator_ops s2mps11_ldo_ops = {
59460599ea6SJaehoon Chung 	.get_value	= ldo_get_value,
59560599ea6SJaehoon Chung 	.set_value	= ldo_set_value,
59660599ea6SJaehoon Chung 	.get_enable	= ldo_get_enable,
59760599ea6SJaehoon Chung 	.set_enable	= ldo_set_enable,
59860599ea6SJaehoon Chung 	.get_mode	= ldo_get_mode,
59960599ea6SJaehoon Chung 	.set_mode	= ldo_set_mode,
60060599ea6SJaehoon Chung };
60160599ea6SJaehoon Chung 
60260599ea6SJaehoon Chung U_BOOT_DRIVER(s2mps11_ldo) = {
60360599ea6SJaehoon Chung 	.name = S2MPS11_LDO_DRIVER,
60460599ea6SJaehoon Chung 	.id = UCLASS_REGULATOR,
60560599ea6SJaehoon Chung 	.ops = &s2mps11_ldo_ops,
60660599ea6SJaehoon Chung 	.probe = s2mps11_ldo_probe,
60760599ea6SJaehoon Chung };
608