162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Base driver for Marvell 88PM8607
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2009 Marvell International Ltd.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Haojian Zhuang <haojian.zhuang@marvell.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <linux/i2c.h>
1462306a36Sopenharmony_ci#include <linux/irq.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/irqdomain.h>
1762306a36Sopenharmony_ci#include <linux/of.h>
1862306a36Sopenharmony_ci#include <linux/of_platform.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/regmap.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/mfd/core.h>
2362306a36Sopenharmony_ci#include <linux/mfd/88pm860x.h>
2462306a36Sopenharmony_ci#include <linux/regulator/machine.h>
2562306a36Sopenharmony_ci#include <linux/power/charger-manager.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define INT_STATUS_NUM			3
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic const struct resource bk0_resources[] = {
3062306a36Sopenharmony_ci	{2, 2, "duty cycle", IORESOURCE_REG, },
3162306a36Sopenharmony_ci	{3, 3, "always on",  IORESOURCE_REG, },
3262306a36Sopenharmony_ci	{3, 3, "current",    IORESOURCE_REG, },
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_cistatic const struct resource bk1_resources[] = {
3562306a36Sopenharmony_ci	{4, 4, "duty cycle", IORESOURCE_REG, },
3662306a36Sopenharmony_ci	{5, 5, "always on",  IORESOURCE_REG, },
3762306a36Sopenharmony_ci	{5, 5, "current",    IORESOURCE_REG, },
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_cistatic const struct resource bk2_resources[] = {
4062306a36Sopenharmony_ci	{6, 6, "duty cycle", IORESOURCE_REG, },
4162306a36Sopenharmony_ci	{7, 7, "always on",  IORESOURCE_REG, },
4262306a36Sopenharmony_ci	{5, 5, "current",    IORESOURCE_REG, },
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic const struct resource led0_resources[] = {
4662306a36Sopenharmony_ci	/* RGB1 Red LED */
4762306a36Sopenharmony_ci	{0xd, 0xd, "control", IORESOURCE_REG, },
4862306a36Sopenharmony_ci	{0xc, 0xc, "blink",   IORESOURCE_REG, },
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_cistatic const struct resource led1_resources[] = {
5162306a36Sopenharmony_ci	/* RGB1 Green LED */
5262306a36Sopenharmony_ci	{0xe, 0xe, "control", IORESOURCE_REG, },
5362306a36Sopenharmony_ci	{0xc, 0xc, "blink",   IORESOURCE_REG, },
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_cistatic const struct resource led2_resources[] = {
5662306a36Sopenharmony_ci	/* RGB1 Blue LED */
5762306a36Sopenharmony_ci	{0xf, 0xf, "control", IORESOURCE_REG, },
5862306a36Sopenharmony_ci	{0xc, 0xc, "blink",   IORESOURCE_REG, },
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_cistatic const struct resource led3_resources[] = {
6162306a36Sopenharmony_ci	/* RGB2 Red LED */
6262306a36Sopenharmony_ci	{0x9, 0x9, "control", IORESOURCE_REG, },
6362306a36Sopenharmony_ci	{0x8, 0x8, "blink",   IORESOURCE_REG, },
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_cistatic const struct resource led4_resources[] = {
6662306a36Sopenharmony_ci	/* RGB2 Green LED */
6762306a36Sopenharmony_ci	{0xa, 0xa, "control", IORESOURCE_REG, },
6862306a36Sopenharmony_ci	{0x8, 0x8, "blink",   IORESOURCE_REG, },
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_cistatic const struct resource led5_resources[] = {
7162306a36Sopenharmony_ci	/* RGB2 Blue LED */
7262306a36Sopenharmony_ci	{0xb, 0xb, "control", IORESOURCE_REG, },
7362306a36Sopenharmony_ci	{0x8, 0x8, "blink",   IORESOURCE_REG, },
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic const struct resource buck1_resources[] = {
7762306a36Sopenharmony_ci	{0x24, 0x24, "buck set", IORESOURCE_REG, },
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_cistatic const struct resource buck2_resources[] = {
8062306a36Sopenharmony_ci	{0x25, 0x25, "buck set", IORESOURCE_REG, },
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_cistatic const struct resource buck3_resources[] = {
8362306a36Sopenharmony_ci	{0x26, 0x26, "buck set", IORESOURCE_REG, },
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_cistatic const struct resource ldo1_resources[] = {
8662306a36Sopenharmony_ci	{0x10, 0x10, "ldo set", IORESOURCE_REG, },
8762306a36Sopenharmony_ci};
8862306a36Sopenharmony_cistatic const struct resource ldo2_resources[] = {
8962306a36Sopenharmony_ci	{0x11, 0x11, "ldo set", IORESOURCE_REG, },
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_cistatic const struct resource ldo3_resources[] = {
9262306a36Sopenharmony_ci	{0x12, 0x12, "ldo set", IORESOURCE_REG, },
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_cistatic const struct resource ldo4_resources[] = {
9562306a36Sopenharmony_ci	{0x13, 0x13, "ldo set", IORESOURCE_REG, },
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_cistatic const struct resource ldo5_resources[] = {
9862306a36Sopenharmony_ci	{0x14, 0x14, "ldo set", IORESOURCE_REG, },
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_cistatic const struct resource ldo6_resources[] = {
10162306a36Sopenharmony_ci	{0x15, 0x15, "ldo set", IORESOURCE_REG, },
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_cistatic const struct resource ldo7_resources[] = {
10462306a36Sopenharmony_ci	{0x16, 0x16, "ldo set", IORESOURCE_REG, },
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_cistatic const struct resource ldo8_resources[] = {
10762306a36Sopenharmony_ci	{0x17, 0x17, "ldo set", IORESOURCE_REG, },
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_cistatic const struct resource ldo9_resources[] = {
11062306a36Sopenharmony_ci	{0x18, 0x18, "ldo set", IORESOURCE_REG, },
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_cistatic const struct resource ldo10_resources[] = {
11362306a36Sopenharmony_ci	{0x19, 0x19, "ldo set", IORESOURCE_REG, },
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_cistatic const struct resource ldo12_resources[] = {
11662306a36Sopenharmony_ci	{0x1a, 0x1a, "ldo set", IORESOURCE_REG, },
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_cistatic const struct resource ldo_vibrator_resources[] = {
11962306a36Sopenharmony_ci	{0x28, 0x28, "ldo set", IORESOURCE_REG, },
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_cistatic const struct resource ldo14_resources[] = {
12262306a36Sopenharmony_ci	{0x1b, 0x1b, "ldo set", IORESOURCE_REG, },
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic struct resource touch_resources[] = {
12662306a36Sopenharmony_ci	{PM8607_IRQ_PEN, PM8607_IRQ_PEN, "touch", IORESOURCE_IRQ,},
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic struct resource onkey_resources[] = {
13062306a36Sopenharmony_ci	{PM8607_IRQ_ONKEY, PM8607_IRQ_ONKEY, "onkey", IORESOURCE_IRQ,},
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic struct resource codec_resources[] = {
13462306a36Sopenharmony_ci	/* Headset microphone insertion or removal */
13562306a36Sopenharmony_ci	{PM8607_IRQ_MICIN,   PM8607_IRQ_MICIN,   "micin",   IORESOURCE_IRQ,},
13662306a36Sopenharmony_ci	/* Hook-switch press or release */
13762306a36Sopenharmony_ci	{PM8607_IRQ_HOOK,    PM8607_IRQ_HOOK,    "hook",    IORESOURCE_IRQ,},
13862306a36Sopenharmony_ci	/* Headset insertion or removal */
13962306a36Sopenharmony_ci	{PM8607_IRQ_HEADSET, PM8607_IRQ_HEADSET, "headset", IORESOURCE_IRQ,},
14062306a36Sopenharmony_ci	/* Audio short */
14162306a36Sopenharmony_ci	{PM8607_IRQ_AUDIO_SHORT, PM8607_IRQ_AUDIO_SHORT, "audio-short",
14262306a36Sopenharmony_ci	 IORESOURCE_IRQ,},
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic struct resource battery_resources[] = {
14662306a36Sopenharmony_ci	{PM8607_IRQ_CC,  PM8607_IRQ_CC,  "columb counter", IORESOURCE_IRQ,},
14762306a36Sopenharmony_ci	{PM8607_IRQ_BAT, PM8607_IRQ_BAT, "battery",        IORESOURCE_IRQ,},
14862306a36Sopenharmony_ci};
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic struct resource charger_resources[] = {
15162306a36Sopenharmony_ci	{PM8607_IRQ_CHG,  PM8607_IRQ_CHG,  "charger detect",  IORESOURCE_IRQ,},
15262306a36Sopenharmony_ci	{PM8607_IRQ_CHG_DONE,  PM8607_IRQ_CHG_DONE,  "charging done",
15362306a36Sopenharmony_ci	 IORESOURCE_IRQ,},
15462306a36Sopenharmony_ci	{PM8607_IRQ_CHG_FAIL,  PM8607_IRQ_CHG_FAIL,  "charging timeout",
15562306a36Sopenharmony_ci	 IORESOURCE_IRQ,},
15662306a36Sopenharmony_ci	{PM8607_IRQ_CHG_FAULT, PM8607_IRQ_CHG_FAULT, "charging fault",
15762306a36Sopenharmony_ci	 IORESOURCE_IRQ,},
15862306a36Sopenharmony_ci	{PM8607_IRQ_GPADC1,    PM8607_IRQ_GPADC1,    "battery temperature",
15962306a36Sopenharmony_ci	 IORESOURCE_IRQ,},
16062306a36Sopenharmony_ci	{PM8607_IRQ_VBAT, PM8607_IRQ_VBAT, "battery voltage", IORESOURCE_IRQ,},
16162306a36Sopenharmony_ci	{PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage",    IORESOURCE_IRQ,},
16262306a36Sopenharmony_ci};
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic struct resource rtc_resources[] = {
16562306a36Sopenharmony_ci	{PM8607_IRQ_RTC, PM8607_IRQ_RTC, "rtc", IORESOURCE_IRQ,},
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic struct mfd_cell bk_devs[] = {
16962306a36Sopenharmony_ci	{
17062306a36Sopenharmony_ci		.name = "88pm860x-backlight",
17162306a36Sopenharmony_ci		.id = 0,
17262306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(bk0_resources),
17362306a36Sopenharmony_ci		.resources = bk0_resources,
17462306a36Sopenharmony_ci	}, {
17562306a36Sopenharmony_ci		.name = "88pm860x-backlight",
17662306a36Sopenharmony_ci		.id = 1,
17762306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(bk1_resources),
17862306a36Sopenharmony_ci		.resources = bk1_resources,
17962306a36Sopenharmony_ci	}, {
18062306a36Sopenharmony_ci		.name = "88pm860x-backlight",
18162306a36Sopenharmony_ci		.id = 2,
18262306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(bk2_resources),
18362306a36Sopenharmony_ci		.resources = bk2_resources,
18462306a36Sopenharmony_ci	},
18562306a36Sopenharmony_ci};
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic struct mfd_cell led_devs[] = {
18862306a36Sopenharmony_ci	{
18962306a36Sopenharmony_ci		.name = "88pm860x-led",
19062306a36Sopenharmony_ci		.id = 0,
19162306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(led0_resources),
19262306a36Sopenharmony_ci		.resources = led0_resources,
19362306a36Sopenharmony_ci	}, {
19462306a36Sopenharmony_ci		.name = "88pm860x-led",
19562306a36Sopenharmony_ci		.id = 1,
19662306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(led1_resources),
19762306a36Sopenharmony_ci		.resources = led1_resources,
19862306a36Sopenharmony_ci	}, {
19962306a36Sopenharmony_ci		.name = "88pm860x-led",
20062306a36Sopenharmony_ci		.id = 2,
20162306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(led2_resources),
20262306a36Sopenharmony_ci		.resources = led2_resources,
20362306a36Sopenharmony_ci	}, {
20462306a36Sopenharmony_ci		.name = "88pm860x-led",
20562306a36Sopenharmony_ci		.id = 3,
20662306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(led3_resources),
20762306a36Sopenharmony_ci		.resources = led3_resources,
20862306a36Sopenharmony_ci	}, {
20962306a36Sopenharmony_ci		.name = "88pm860x-led",
21062306a36Sopenharmony_ci		.id = 4,
21162306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(led4_resources),
21262306a36Sopenharmony_ci		.resources = led4_resources,
21362306a36Sopenharmony_ci	}, {
21462306a36Sopenharmony_ci		.name = "88pm860x-led",
21562306a36Sopenharmony_ci		.id = 5,
21662306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(led5_resources),
21762306a36Sopenharmony_ci		.resources = led5_resources,
21862306a36Sopenharmony_ci	},
21962306a36Sopenharmony_ci};
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic struct mfd_cell reg_devs[] = {
22262306a36Sopenharmony_ci	{
22362306a36Sopenharmony_ci		.name = "88pm860x-regulator",
22462306a36Sopenharmony_ci		.id = 0,
22562306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(buck1_resources),
22662306a36Sopenharmony_ci		.resources = buck1_resources,
22762306a36Sopenharmony_ci	}, {
22862306a36Sopenharmony_ci		.name = "88pm860x-regulator",
22962306a36Sopenharmony_ci		.id = 1,
23062306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(buck2_resources),
23162306a36Sopenharmony_ci		.resources = buck2_resources,
23262306a36Sopenharmony_ci	}, {
23362306a36Sopenharmony_ci		.name = "88pm860x-regulator",
23462306a36Sopenharmony_ci		.id = 2,
23562306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(buck3_resources),
23662306a36Sopenharmony_ci		.resources = buck3_resources,
23762306a36Sopenharmony_ci	}, {
23862306a36Sopenharmony_ci		.name = "88pm860x-regulator",
23962306a36Sopenharmony_ci		.id = 3,
24062306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(ldo1_resources),
24162306a36Sopenharmony_ci		.resources = ldo1_resources,
24262306a36Sopenharmony_ci	}, {
24362306a36Sopenharmony_ci		.name = "88pm860x-regulator",
24462306a36Sopenharmony_ci		.id = 4,
24562306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(ldo2_resources),
24662306a36Sopenharmony_ci		.resources = ldo2_resources,
24762306a36Sopenharmony_ci	}, {
24862306a36Sopenharmony_ci		.name = "88pm860x-regulator",
24962306a36Sopenharmony_ci		.id = 5,
25062306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(ldo3_resources),
25162306a36Sopenharmony_ci		.resources = ldo3_resources,
25262306a36Sopenharmony_ci	}, {
25362306a36Sopenharmony_ci		.name = "88pm860x-regulator",
25462306a36Sopenharmony_ci		.id = 6,
25562306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(ldo4_resources),
25662306a36Sopenharmony_ci		.resources = ldo4_resources,
25762306a36Sopenharmony_ci	}, {
25862306a36Sopenharmony_ci		.name = "88pm860x-regulator",
25962306a36Sopenharmony_ci		.id = 7,
26062306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(ldo5_resources),
26162306a36Sopenharmony_ci		.resources = ldo5_resources,
26262306a36Sopenharmony_ci	}, {
26362306a36Sopenharmony_ci		.name = "88pm860x-regulator",
26462306a36Sopenharmony_ci		.id = 8,
26562306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(ldo6_resources),
26662306a36Sopenharmony_ci		.resources = ldo6_resources,
26762306a36Sopenharmony_ci	}, {
26862306a36Sopenharmony_ci		.name = "88pm860x-regulator",
26962306a36Sopenharmony_ci		.id = 9,
27062306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(ldo7_resources),
27162306a36Sopenharmony_ci		.resources = ldo7_resources,
27262306a36Sopenharmony_ci	}, {
27362306a36Sopenharmony_ci		.name = "88pm860x-regulator",
27462306a36Sopenharmony_ci		.id = 10,
27562306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(ldo8_resources),
27662306a36Sopenharmony_ci		.resources = ldo8_resources,
27762306a36Sopenharmony_ci	}, {
27862306a36Sopenharmony_ci		.name = "88pm860x-regulator",
27962306a36Sopenharmony_ci		.id = 11,
28062306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(ldo9_resources),
28162306a36Sopenharmony_ci		.resources = ldo9_resources,
28262306a36Sopenharmony_ci	}, {
28362306a36Sopenharmony_ci		.name = "88pm860x-regulator",
28462306a36Sopenharmony_ci		.id = 12,
28562306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(ldo10_resources),
28662306a36Sopenharmony_ci		.resources = ldo10_resources,
28762306a36Sopenharmony_ci	}, {
28862306a36Sopenharmony_ci		.name = "88pm860x-regulator",
28962306a36Sopenharmony_ci		.id = 13,
29062306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(ldo12_resources),
29162306a36Sopenharmony_ci		.resources = ldo12_resources,
29262306a36Sopenharmony_ci	}, {
29362306a36Sopenharmony_ci		.name = "88pm860x-regulator",
29462306a36Sopenharmony_ci		.id = 14,
29562306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(ldo_vibrator_resources),
29662306a36Sopenharmony_ci		.resources = ldo_vibrator_resources,
29762306a36Sopenharmony_ci	}, {
29862306a36Sopenharmony_ci		.name = "88pm860x-regulator",
29962306a36Sopenharmony_ci		.id = 15,
30062306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(ldo14_resources),
30162306a36Sopenharmony_ci		.resources = ldo14_resources,
30262306a36Sopenharmony_ci	},
30362306a36Sopenharmony_ci};
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic struct mfd_cell touch_devs[] = {
30662306a36Sopenharmony_ci	{"88pm860x-touch", -1,},
30762306a36Sopenharmony_ci};
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic struct mfd_cell onkey_devs[] = {
31062306a36Sopenharmony_ci	{"88pm860x-onkey", -1,},
31162306a36Sopenharmony_ci};
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic struct mfd_cell codec_devs[] = {
31462306a36Sopenharmony_ci	{"88pm860x-codec", -1,},
31562306a36Sopenharmony_ci};
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic struct regulator_consumer_supply preg_supply[] = {
31862306a36Sopenharmony_ci	REGULATOR_SUPPLY("preg", "charger-manager"),
31962306a36Sopenharmony_ci};
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic struct regulator_init_data preg_init_data = {
32262306a36Sopenharmony_ci	.num_consumer_supplies	= ARRAY_SIZE(preg_supply),
32362306a36Sopenharmony_ci	.consumer_supplies	= &preg_supply[0],
32462306a36Sopenharmony_ci};
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic struct charger_regulator chg_desc_regulator_data[] = {
32762306a36Sopenharmony_ci	{ .regulator_name = "preg", },
32862306a36Sopenharmony_ci};
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic struct mfd_cell power_devs[] = {
33162306a36Sopenharmony_ci	{"88pm860x-battery", -1,},
33262306a36Sopenharmony_ci	{"88pm860x-charger", -1,},
33362306a36Sopenharmony_ci	{"88pm860x-preg",    -1,},
33462306a36Sopenharmony_ci	{"charger-manager", -1,},
33562306a36Sopenharmony_ci};
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic struct mfd_cell rtc_devs[] = {
33862306a36Sopenharmony_ci	{"88pm860x-rtc", -1,},
33962306a36Sopenharmony_ci};
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistruct pm860x_irq_data {
34362306a36Sopenharmony_ci	int	reg;
34462306a36Sopenharmony_ci	int	mask_reg;
34562306a36Sopenharmony_ci	int	enable;		/* enable or not */
34662306a36Sopenharmony_ci	int	offs;		/* bit offset in mask register */
34762306a36Sopenharmony_ci};
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic struct pm860x_irq_data pm860x_irqs[] = {
35062306a36Sopenharmony_ci	[PM8607_IRQ_ONKEY] = {
35162306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS1,
35262306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_1,
35362306a36Sopenharmony_ci		.offs		= 1 << 0,
35462306a36Sopenharmony_ci	},
35562306a36Sopenharmony_ci	[PM8607_IRQ_EXTON] = {
35662306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS1,
35762306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_1,
35862306a36Sopenharmony_ci		.offs		= 1 << 1,
35962306a36Sopenharmony_ci	},
36062306a36Sopenharmony_ci	[PM8607_IRQ_CHG] = {
36162306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS1,
36262306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_1,
36362306a36Sopenharmony_ci		.offs		= 1 << 2,
36462306a36Sopenharmony_ci	},
36562306a36Sopenharmony_ci	[PM8607_IRQ_BAT] = {
36662306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS1,
36762306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_1,
36862306a36Sopenharmony_ci		.offs		= 1 << 3,
36962306a36Sopenharmony_ci	},
37062306a36Sopenharmony_ci	[PM8607_IRQ_RTC] = {
37162306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS1,
37262306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_1,
37362306a36Sopenharmony_ci		.offs		= 1 << 4,
37462306a36Sopenharmony_ci	},
37562306a36Sopenharmony_ci	[PM8607_IRQ_CC] = {
37662306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS1,
37762306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_1,
37862306a36Sopenharmony_ci		.offs		= 1 << 5,
37962306a36Sopenharmony_ci	},
38062306a36Sopenharmony_ci	[PM8607_IRQ_VBAT] = {
38162306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS2,
38262306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_2,
38362306a36Sopenharmony_ci		.offs		= 1 << 0,
38462306a36Sopenharmony_ci	},
38562306a36Sopenharmony_ci	[PM8607_IRQ_VCHG] = {
38662306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS2,
38762306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_2,
38862306a36Sopenharmony_ci		.offs		= 1 << 1,
38962306a36Sopenharmony_ci	},
39062306a36Sopenharmony_ci	[PM8607_IRQ_VSYS] = {
39162306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS2,
39262306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_2,
39362306a36Sopenharmony_ci		.offs		= 1 << 2,
39462306a36Sopenharmony_ci	},
39562306a36Sopenharmony_ci	[PM8607_IRQ_TINT] = {
39662306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS2,
39762306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_2,
39862306a36Sopenharmony_ci		.offs		= 1 << 3,
39962306a36Sopenharmony_ci	},
40062306a36Sopenharmony_ci	[PM8607_IRQ_GPADC0] = {
40162306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS2,
40262306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_2,
40362306a36Sopenharmony_ci		.offs		= 1 << 4,
40462306a36Sopenharmony_ci	},
40562306a36Sopenharmony_ci	[PM8607_IRQ_GPADC1] = {
40662306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS2,
40762306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_2,
40862306a36Sopenharmony_ci		.offs		= 1 << 5,
40962306a36Sopenharmony_ci	},
41062306a36Sopenharmony_ci	[PM8607_IRQ_GPADC2] = {
41162306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS2,
41262306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_2,
41362306a36Sopenharmony_ci		.offs		= 1 << 6,
41462306a36Sopenharmony_ci	},
41562306a36Sopenharmony_ci	[PM8607_IRQ_GPADC3] = {
41662306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS2,
41762306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_2,
41862306a36Sopenharmony_ci		.offs		= 1 << 7,
41962306a36Sopenharmony_ci	},
42062306a36Sopenharmony_ci	[PM8607_IRQ_AUDIO_SHORT] = {
42162306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS3,
42262306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_3,
42362306a36Sopenharmony_ci		.offs		= 1 << 0,
42462306a36Sopenharmony_ci	},
42562306a36Sopenharmony_ci	[PM8607_IRQ_PEN] = {
42662306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS3,
42762306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_3,
42862306a36Sopenharmony_ci		.offs		= 1 << 1,
42962306a36Sopenharmony_ci	},
43062306a36Sopenharmony_ci	[PM8607_IRQ_HEADSET] = {
43162306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS3,
43262306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_3,
43362306a36Sopenharmony_ci		.offs		= 1 << 2,
43462306a36Sopenharmony_ci	},
43562306a36Sopenharmony_ci	[PM8607_IRQ_HOOK] = {
43662306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS3,
43762306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_3,
43862306a36Sopenharmony_ci		.offs		= 1 << 3,
43962306a36Sopenharmony_ci	},
44062306a36Sopenharmony_ci	[PM8607_IRQ_MICIN] = {
44162306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS3,
44262306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_3,
44362306a36Sopenharmony_ci		.offs		= 1 << 4,
44462306a36Sopenharmony_ci	},
44562306a36Sopenharmony_ci	[PM8607_IRQ_CHG_FAIL] = {
44662306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS3,
44762306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_3,
44862306a36Sopenharmony_ci		.offs		= 1 << 5,
44962306a36Sopenharmony_ci	},
45062306a36Sopenharmony_ci	[PM8607_IRQ_CHG_DONE] = {
45162306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS3,
45262306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_3,
45362306a36Sopenharmony_ci		.offs		= 1 << 6,
45462306a36Sopenharmony_ci	},
45562306a36Sopenharmony_ci	[PM8607_IRQ_CHG_FAULT] = {
45662306a36Sopenharmony_ci		.reg		= PM8607_INT_STATUS3,
45762306a36Sopenharmony_ci		.mask_reg	= PM8607_INT_MASK_3,
45862306a36Sopenharmony_ci		.offs		= 1 << 7,
45962306a36Sopenharmony_ci	},
46062306a36Sopenharmony_ci};
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic irqreturn_t pm860x_irq(int irq, void *data)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	struct pm860x_chip *chip = data;
46562306a36Sopenharmony_ci	struct pm860x_irq_data *irq_data;
46662306a36Sopenharmony_ci	struct i2c_client *i2c;
46762306a36Sopenharmony_ci	int read_reg = -1, value = 0;
46862306a36Sopenharmony_ci	int i;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
47162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
47262306a36Sopenharmony_ci		irq_data = &pm860x_irqs[i];
47362306a36Sopenharmony_ci		if (read_reg != irq_data->reg) {
47462306a36Sopenharmony_ci			read_reg = irq_data->reg;
47562306a36Sopenharmony_ci			value = pm860x_reg_read(i2c, irq_data->reg);
47662306a36Sopenharmony_ci		}
47762306a36Sopenharmony_ci		if (value & irq_data->enable)
47862306a36Sopenharmony_ci			handle_nested_irq(chip->irq_base + i);
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci	return IRQ_HANDLED;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic void pm860x_irq_lock(struct irq_data *data)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	mutex_lock(&chip->irq_lock);
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic void pm860x_irq_sync_unlock(struct irq_data *data)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
49362306a36Sopenharmony_ci	struct pm860x_irq_data *irq_data;
49462306a36Sopenharmony_ci	struct i2c_client *i2c;
49562306a36Sopenharmony_ci	static unsigned char cached[3] = {0x0, 0x0, 0x0};
49662306a36Sopenharmony_ci	unsigned char mask[3];
49762306a36Sopenharmony_ci	int i;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
50062306a36Sopenharmony_ci	/* Load cached value. In initial, all IRQs are masked */
50162306a36Sopenharmony_ci	for (i = 0; i < 3; i++)
50262306a36Sopenharmony_ci		mask[i] = cached[i];
50362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
50462306a36Sopenharmony_ci		irq_data = &pm860x_irqs[i];
50562306a36Sopenharmony_ci		switch (irq_data->mask_reg) {
50662306a36Sopenharmony_ci		case PM8607_INT_MASK_1:
50762306a36Sopenharmony_ci			mask[0] &= ~irq_data->offs;
50862306a36Sopenharmony_ci			mask[0] |= irq_data->enable;
50962306a36Sopenharmony_ci			break;
51062306a36Sopenharmony_ci		case PM8607_INT_MASK_2:
51162306a36Sopenharmony_ci			mask[1] &= ~irq_data->offs;
51262306a36Sopenharmony_ci			mask[1] |= irq_data->enable;
51362306a36Sopenharmony_ci			break;
51462306a36Sopenharmony_ci		case PM8607_INT_MASK_3:
51562306a36Sopenharmony_ci			mask[2] &= ~irq_data->offs;
51662306a36Sopenharmony_ci			mask[2] |= irq_data->enable;
51762306a36Sopenharmony_ci			break;
51862306a36Sopenharmony_ci		default:
51962306a36Sopenharmony_ci			dev_err(chip->dev, "wrong IRQ\n");
52062306a36Sopenharmony_ci			break;
52162306a36Sopenharmony_ci		}
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci	/* update mask into registers */
52462306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
52562306a36Sopenharmony_ci		if (mask[i] != cached[i]) {
52662306a36Sopenharmony_ci			cached[i] = mask[i];
52762306a36Sopenharmony_ci			pm860x_reg_write(i2c, PM8607_INT_MASK_1 + i, mask[i]);
52862306a36Sopenharmony_ci		}
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	mutex_unlock(&chip->irq_lock);
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic void pm860x_irq_enable(struct irq_data *data)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	pm860x_irqs[data->hwirq].enable = pm860x_irqs[data->hwirq].offs;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic void pm860x_irq_disable(struct irq_data *data)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	pm860x_irqs[data->hwirq].enable = 0;
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic struct irq_chip pm860x_irq_chip = {
54562306a36Sopenharmony_ci	.name		= "88pm860x",
54662306a36Sopenharmony_ci	.irq_bus_lock	= pm860x_irq_lock,
54762306a36Sopenharmony_ci	.irq_bus_sync_unlock = pm860x_irq_sync_unlock,
54862306a36Sopenharmony_ci	.irq_enable	= pm860x_irq_enable,
54962306a36Sopenharmony_ci	.irq_disable	= pm860x_irq_disable,
55062306a36Sopenharmony_ci};
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic int pm860x_irq_domain_map(struct irq_domain *d, unsigned int virq,
55362306a36Sopenharmony_ci				 irq_hw_number_t hw)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	irq_set_chip_data(virq, d->host_data);
55662306a36Sopenharmony_ci	irq_set_chip_and_handler(virq, &pm860x_irq_chip, handle_edge_irq);
55762306a36Sopenharmony_ci	irq_set_nested_thread(virq, 1);
55862306a36Sopenharmony_ci	irq_set_noprobe(virq);
55962306a36Sopenharmony_ci	return 0;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic const struct irq_domain_ops pm860x_irq_domain_ops = {
56362306a36Sopenharmony_ci	.map	= pm860x_irq_domain_map,
56462306a36Sopenharmony_ci	.xlate	= irq_domain_xlate_onetwocell,
56562306a36Sopenharmony_ci};
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic int device_irq_init(struct pm860x_chip *chip,
56862306a36Sopenharmony_ci				     struct pm860x_platform_data *pdata)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct i2c_client *i2c = (chip->id == CHIP_PM8607) ?
57162306a36Sopenharmony_ci		chip->client : chip->companion;
57262306a36Sopenharmony_ci	unsigned char status_buf[INT_STATUS_NUM];
57362306a36Sopenharmony_ci	unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
57462306a36Sopenharmony_ci	int data, mask, ret = -EINVAL;
57562306a36Sopenharmony_ci	int nr_irqs, irq_base = -1;
57662306a36Sopenharmony_ci	struct device_node *node = i2c->dev.of_node;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
57962306a36Sopenharmony_ci		| PM8607_B0_MISC1_INT_MASK;
58062306a36Sopenharmony_ci	data = 0;
58162306a36Sopenharmony_ci	chip->irq_mode = 0;
58262306a36Sopenharmony_ci	if (pdata && pdata->irq_mode) {
58362306a36Sopenharmony_ci		/*
58462306a36Sopenharmony_ci		 * irq_mode defines the way of clearing interrupt. If it's 1,
58562306a36Sopenharmony_ci		 * clear IRQ by write. Otherwise, clear it by read.
58662306a36Sopenharmony_ci		 * This control bit is valid from 88PM8607 B0 steping.
58762306a36Sopenharmony_ci		 */
58862306a36Sopenharmony_ci		data |= PM8607_B0_MISC1_INT_CLEAR;
58962306a36Sopenharmony_ci		chip->irq_mode = 1;
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci	ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, mask, data);
59262306a36Sopenharmony_ci	if (ret < 0)
59362306a36Sopenharmony_ci		goto out;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	/* mask all IRQs */
59662306a36Sopenharmony_ci	memset(status_buf, 0, INT_STATUS_NUM);
59762306a36Sopenharmony_ci	ret = pm860x_bulk_write(i2c, PM8607_INT_MASK_1,
59862306a36Sopenharmony_ci				INT_STATUS_NUM, status_buf);
59962306a36Sopenharmony_ci	if (ret < 0)
60062306a36Sopenharmony_ci		goto out;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	if (chip->irq_mode) {
60362306a36Sopenharmony_ci		/* clear interrupt status by write */
60462306a36Sopenharmony_ci		memset(status_buf, 0xFF, INT_STATUS_NUM);
60562306a36Sopenharmony_ci		ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
60662306a36Sopenharmony_ci					INT_STATUS_NUM, status_buf);
60762306a36Sopenharmony_ci	} else {
60862306a36Sopenharmony_ci		/* clear interrupt status by read */
60962306a36Sopenharmony_ci		ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
61062306a36Sopenharmony_ci					INT_STATUS_NUM, status_buf);
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci	if (ret < 0)
61362306a36Sopenharmony_ci		goto out;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	mutex_init(&chip->irq_lock);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	if (pdata && pdata->irq_base)
61862306a36Sopenharmony_ci		irq_base = pdata->irq_base;
61962306a36Sopenharmony_ci	nr_irqs = ARRAY_SIZE(pm860x_irqs);
62062306a36Sopenharmony_ci	chip->irq_base = irq_alloc_descs(irq_base, 0, nr_irqs, 0);
62162306a36Sopenharmony_ci	if (chip->irq_base < 0) {
62262306a36Sopenharmony_ci		dev_err(&i2c->dev, "Failed to allocate interrupts, ret:%d\n",
62362306a36Sopenharmony_ci			chip->irq_base);
62462306a36Sopenharmony_ci		ret = -EBUSY;
62562306a36Sopenharmony_ci		goto out;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci	irq_domain_add_legacy(node, nr_irqs, chip->irq_base, 0,
62862306a36Sopenharmony_ci			      &pm860x_irq_domain_ops, chip);
62962306a36Sopenharmony_ci	chip->core_irq = i2c->irq;
63062306a36Sopenharmony_ci	if (!chip->core_irq)
63162306a36Sopenharmony_ci		goto out;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	ret = request_threaded_irq(chip->core_irq, NULL, pm860x_irq,
63462306a36Sopenharmony_ci				   flags | IRQF_ONESHOT, "88pm860x", chip);
63562306a36Sopenharmony_ci	if (ret) {
63662306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to request IRQ: %d\n", ret);
63762306a36Sopenharmony_ci		chip->core_irq = 0;
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	return 0;
64162306a36Sopenharmony_ciout:
64262306a36Sopenharmony_ci	chip->core_irq = 0;
64362306a36Sopenharmony_ci	return ret;
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic void device_irq_exit(struct pm860x_chip *chip)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	if (chip->core_irq)
64962306a36Sopenharmony_ci		free_irq(chip->core_irq, chip);
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ciint pm8606_osc_enable(struct pm860x_chip *chip, unsigned short client)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	int ret = -EIO;
65562306a36Sopenharmony_ci	struct i2c_client *i2c = (chip->id == CHIP_PM8606) ?
65662306a36Sopenharmony_ci		chip->client : chip->companion;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client);
65962306a36Sopenharmony_ci	dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n",
66062306a36Sopenharmony_ci			__func__, chip->osc_vote,
66162306a36Sopenharmony_ci			chip->osc_status);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	mutex_lock(&chip->osc_lock);
66462306a36Sopenharmony_ci	/* Update voting status */
66562306a36Sopenharmony_ci	chip->osc_vote |= client;
66662306a36Sopenharmony_ci	/* If reference group is off - turn on*/
66762306a36Sopenharmony_ci	if (chip->osc_status != PM8606_REF_GP_OSC_ON) {
66862306a36Sopenharmony_ci		chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN;
66962306a36Sopenharmony_ci		/* Enable Reference group Vsys */
67062306a36Sopenharmony_ci		if (pm860x_set_bits(i2c, PM8606_VSYS,
67162306a36Sopenharmony_ci				PM8606_VSYS_EN, PM8606_VSYS_EN))
67262306a36Sopenharmony_ci			goto out;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci		/*Enable Internal Oscillator */
67562306a36Sopenharmony_ci		if (pm860x_set_bits(i2c, PM8606_MISC,
67662306a36Sopenharmony_ci				PM8606_MISC_OSC_EN, PM8606_MISC_OSC_EN))
67762306a36Sopenharmony_ci			goto out;
67862306a36Sopenharmony_ci		/* Update status (only if writes succeed) */
67962306a36Sopenharmony_ci		chip->osc_status = PM8606_REF_GP_OSC_ON;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci	mutex_unlock(&chip->osc_lock);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n",
68462306a36Sopenharmony_ci			__func__, chip->osc_vote,
68562306a36Sopenharmony_ci			chip->osc_status, ret);
68662306a36Sopenharmony_ci	return 0;
68762306a36Sopenharmony_ciout:
68862306a36Sopenharmony_ci	mutex_unlock(&chip->osc_lock);
68962306a36Sopenharmony_ci	return ret;
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ciEXPORT_SYMBOL(pm8606_osc_enable);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ciint pm8606_osc_disable(struct pm860x_chip *chip, unsigned short client)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	int ret = -EIO;
69662306a36Sopenharmony_ci	struct i2c_client *i2c = (chip->id == CHIP_PM8606) ?
69762306a36Sopenharmony_ci		chip->client : chip->companion;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client);
70062306a36Sopenharmony_ci	dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n",
70162306a36Sopenharmony_ci			__func__, chip->osc_vote,
70262306a36Sopenharmony_ci			chip->osc_status);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	mutex_lock(&chip->osc_lock);
70562306a36Sopenharmony_ci	/* Update voting status */
70662306a36Sopenharmony_ci	chip->osc_vote &= ~(client);
70762306a36Sopenharmony_ci	/*
70862306a36Sopenharmony_ci	 * If reference group is off and this is the last client to release
70962306a36Sopenharmony_ci	 * - turn off
71062306a36Sopenharmony_ci	 */
71162306a36Sopenharmony_ci	if ((chip->osc_status != PM8606_REF_GP_OSC_OFF) &&
71262306a36Sopenharmony_ci			(chip->osc_vote == REF_GP_NO_CLIENTS)) {
71362306a36Sopenharmony_ci		chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN;
71462306a36Sopenharmony_ci		/* Disable Reference group Vsys */
71562306a36Sopenharmony_ci		if (pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0))
71662306a36Sopenharmony_ci			goto out;
71762306a36Sopenharmony_ci		/* Disable Internal Oscillator */
71862306a36Sopenharmony_ci		if (pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0))
71962306a36Sopenharmony_ci			goto out;
72062306a36Sopenharmony_ci		chip->osc_status = PM8606_REF_GP_OSC_OFF;
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci	mutex_unlock(&chip->osc_lock);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n",
72562306a36Sopenharmony_ci			__func__, chip->osc_vote,
72662306a36Sopenharmony_ci			chip->osc_status, ret);
72762306a36Sopenharmony_ci	return 0;
72862306a36Sopenharmony_ciout:
72962306a36Sopenharmony_ci	mutex_unlock(&chip->osc_lock);
73062306a36Sopenharmony_ci	return ret;
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ciEXPORT_SYMBOL(pm8606_osc_disable);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_cistatic void device_osc_init(struct i2c_client *i2c)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	mutex_init(&chip->osc_lock);
73962306a36Sopenharmony_ci	/* init portofino reference group voting and status */
74062306a36Sopenharmony_ci	/* Disable Reference group Vsys */
74162306a36Sopenharmony_ci	pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0);
74262306a36Sopenharmony_ci	/* Disable Internal Oscillator */
74362306a36Sopenharmony_ci	pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	chip->osc_vote = REF_GP_NO_CLIENTS;
74662306a36Sopenharmony_ci	chip->osc_status = PM8606_REF_GP_OSC_OFF;
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic void device_bk_init(struct pm860x_chip *chip,
75062306a36Sopenharmony_ci				     struct pm860x_platform_data *pdata)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	int ret, i;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	if (pdata && pdata->backlight) {
75562306a36Sopenharmony_ci		if (pdata->num_backlights > ARRAY_SIZE(bk_devs))
75662306a36Sopenharmony_ci			pdata->num_backlights = ARRAY_SIZE(bk_devs);
75762306a36Sopenharmony_ci		for (i = 0; i < pdata->num_backlights; i++) {
75862306a36Sopenharmony_ci			bk_devs[i].platform_data = &pdata->backlight[i];
75962306a36Sopenharmony_ci			bk_devs[i].pdata_size =
76062306a36Sopenharmony_ci				sizeof(struct pm860x_backlight_pdata);
76162306a36Sopenharmony_ci		}
76262306a36Sopenharmony_ci	}
76362306a36Sopenharmony_ci	ret = mfd_add_devices(chip->dev, 0, bk_devs,
76462306a36Sopenharmony_ci			      ARRAY_SIZE(bk_devs), NULL, 0, NULL);
76562306a36Sopenharmony_ci	if (ret < 0)
76662306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to add backlight subdev\n");
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic void device_led_init(struct pm860x_chip *chip,
77062306a36Sopenharmony_ci				      struct pm860x_platform_data *pdata)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	int ret, i;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if (pdata && pdata->led) {
77562306a36Sopenharmony_ci		if (pdata->num_leds > ARRAY_SIZE(led_devs))
77662306a36Sopenharmony_ci			pdata->num_leds = ARRAY_SIZE(led_devs);
77762306a36Sopenharmony_ci		for (i = 0; i < pdata->num_leds; i++) {
77862306a36Sopenharmony_ci			led_devs[i].platform_data = &pdata->led[i];
77962306a36Sopenharmony_ci			led_devs[i].pdata_size =
78062306a36Sopenharmony_ci				sizeof(struct pm860x_led_pdata);
78162306a36Sopenharmony_ci		}
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci	ret = mfd_add_devices(chip->dev, 0, led_devs,
78462306a36Sopenharmony_ci			      ARRAY_SIZE(led_devs), NULL, 0, NULL);
78562306a36Sopenharmony_ci	if (ret < 0) {
78662306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to add led subdev\n");
78762306a36Sopenharmony_ci		return;
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cistatic void device_regulator_init(struct pm860x_chip *chip,
79262306a36Sopenharmony_ci					    struct pm860x_platform_data *pdata)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	int ret;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	if (pdata == NULL)
79762306a36Sopenharmony_ci		return;
79862306a36Sopenharmony_ci	if (pdata->buck1) {
79962306a36Sopenharmony_ci		reg_devs[0].platform_data = pdata->buck1;
80062306a36Sopenharmony_ci		reg_devs[0].pdata_size = sizeof(struct regulator_init_data);
80162306a36Sopenharmony_ci	}
80262306a36Sopenharmony_ci	if (pdata->buck2) {
80362306a36Sopenharmony_ci		reg_devs[1].platform_data = pdata->buck2;
80462306a36Sopenharmony_ci		reg_devs[1].pdata_size = sizeof(struct regulator_init_data);
80562306a36Sopenharmony_ci	}
80662306a36Sopenharmony_ci	if (pdata->buck3) {
80762306a36Sopenharmony_ci		reg_devs[2].platform_data = pdata->buck3;
80862306a36Sopenharmony_ci		reg_devs[2].pdata_size = sizeof(struct regulator_init_data);
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci	if (pdata->ldo1) {
81162306a36Sopenharmony_ci		reg_devs[3].platform_data = pdata->ldo1;
81262306a36Sopenharmony_ci		reg_devs[3].pdata_size = sizeof(struct regulator_init_data);
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci	if (pdata->ldo2) {
81562306a36Sopenharmony_ci		reg_devs[4].platform_data = pdata->ldo2;
81662306a36Sopenharmony_ci		reg_devs[4].pdata_size = sizeof(struct regulator_init_data);
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci	if (pdata->ldo3) {
81962306a36Sopenharmony_ci		reg_devs[5].platform_data = pdata->ldo3;
82062306a36Sopenharmony_ci		reg_devs[5].pdata_size = sizeof(struct regulator_init_data);
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci	if (pdata->ldo4) {
82362306a36Sopenharmony_ci		reg_devs[6].platform_data = pdata->ldo4;
82462306a36Sopenharmony_ci		reg_devs[6].pdata_size = sizeof(struct regulator_init_data);
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci	if (pdata->ldo5) {
82762306a36Sopenharmony_ci		reg_devs[7].platform_data = pdata->ldo5;
82862306a36Sopenharmony_ci		reg_devs[7].pdata_size = sizeof(struct regulator_init_data);
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci	if (pdata->ldo6) {
83162306a36Sopenharmony_ci		reg_devs[8].platform_data = pdata->ldo6;
83262306a36Sopenharmony_ci		reg_devs[8].pdata_size = sizeof(struct regulator_init_data);
83362306a36Sopenharmony_ci	}
83462306a36Sopenharmony_ci	if (pdata->ldo7) {
83562306a36Sopenharmony_ci		reg_devs[9].platform_data = pdata->ldo7;
83662306a36Sopenharmony_ci		reg_devs[9].pdata_size = sizeof(struct regulator_init_data);
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci	if (pdata->ldo8) {
83962306a36Sopenharmony_ci		reg_devs[10].platform_data = pdata->ldo8;
84062306a36Sopenharmony_ci		reg_devs[10].pdata_size = sizeof(struct regulator_init_data);
84162306a36Sopenharmony_ci	}
84262306a36Sopenharmony_ci	if (pdata->ldo9) {
84362306a36Sopenharmony_ci		reg_devs[11].platform_data = pdata->ldo9;
84462306a36Sopenharmony_ci		reg_devs[11].pdata_size = sizeof(struct regulator_init_data);
84562306a36Sopenharmony_ci	}
84662306a36Sopenharmony_ci	if (pdata->ldo10) {
84762306a36Sopenharmony_ci		reg_devs[12].platform_data = pdata->ldo10;
84862306a36Sopenharmony_ci		reg_devs[12].pdata_size = sizeof(struct regulator_init_data);
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci	if (pdata->ldo12) {
85162306a36Sopenharmony_ci		reg_devs[13].platform_data = pdata->ldo12;
85262306a36Sopenharmony_ci		reg_devs[13].pdata_size = sizeof(struct regulator_init_data);
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci	if (pdata->ldo_vibrator) {
85562306a36Sopenharmony_ci		reg_devs[14].platform_data = pdata->ldo_vibrator;
85662306a36Sopenharmony_ci		reg_devs[14].pdata_size = sizeof(struct regulator_init_data);
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci	if (pdata->ldo14) {
85962306a36Sopenharmony_ci		reg_devs[15].platform_data = pdata->ldo14;
86062306a36Sopenharmony_ci		reg_devs[15].pdata_size = sizeof(struct regulator_init_data);
86162306a36Sopenharmony_ci	}
86262306a36Sopenharmony_ci	ret = mfd_add_devices(chip->dev, 0, reg_devs,
86362306a36Sopenharmony_ci			      ARRAY_SIZE(reg_devs), NULL, 0, NULL);
86462306a36Sopenharmony_ci	if (ret < 0) {
86562306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to add regulator subdev\n");
86662306a36Sopenharmony_ci		return;
86762306a36Sopenharmony_ci	}
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_cistatic void device_rtc_init(struct pm860x_chip *chip,
87162306a36Sopenharmony_ci				      struct pm860x_platform_data *pdata)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	int ret;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	if (!pdata)
87662306a36Sopenharmony_ci		return;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	rtc_devs[0].platform_data = pdata->rtc;
87962306a36Sopenharmony_ci	rtc_devs[0].pdata_size = sizeof(struct pm860x_rtc_pdata);
88062306a36Sopenharmony_ci	rtc_devs[0].num_resources = ARRAY_SIZE(rtc_resources);
88162306a36Sopenharmony_ci	rtc_devs[0].resources = &rtc_resources[0];
88262306a36Sopenharmony_ci	ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
88362306a36Sopenharmony_ci			      ARRAY_SIZE(rtc_devs), &rtc_resources[0],
88462306a36Sopenharmony_ci			      chip->irq_base, NULL);
88562306a36Sopenharmony_ci	if (ret < 0)
88662306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to add rtc subdev\n");
88762306a36Sopenharmony_ci}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_cistatic void device_touch_init(struct pm860x_chip *chip,
89062306a36Sopenharmony_ci					struct pm860x_platform_data *pdata)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	int ret;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	if (pdata == NULL)
89562306a36Sopenharmony_ci		return;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	touch_devs[0].platform_data = pdata->touch;
89862306a36Sopenharmony_ci	touch_devs[0].pdata_size = sizeof(struct pm860x_touch_pdata);
89962306a36Sopenharmony_ci	touch_devs[0].num_resources = ARRAY_SIZE(touch_resources);
90062306a36Sopenharmony_ci	touch_devs[0].resources = &touch_resources[0];
90162306a36Sopenharmony_ci	ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
90262306a36Sopenharmony_ci			      ARRAY_SIZE(touch_devs), &touch_resources[0],
90362306a36Sopenharmony_ci			      chip->irq_base, NULL);
90462306a36Sopenharmony_ci	if (ret < 0)
90562306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to add touch subdev\n");
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_cistatic void device_power_init(struct pm860x_chip *chip,
90962306a36Sopenharmony_ci					struct pm860x_platform_data *pdata)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	int ret;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	if (pdata == NULL)
91462306a36Sopenharmony_ci		return;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	power_devs[0].platform_data = pdata->power;
91762306a36Sopenharmony_ci	power_devs[0].pdata_size = sizeof(struct pm860x_power_pdata);
91862306a36Sopenharmony_ci	power_devs[0].num_resources = ARRAY_SIZE(battery_resources);
91962306a36Sopenharmony_ci	power_devs[0].resources = &battery_resources[0],
92062306a36Sopenharmony_ci	ret = mfd_add_devices(chip->dev, 0, &power_devs[0], 1,
92162306a36Sopenharmony_ci			      &battery_resources[0], chip->irq_base, NULL);
92262306a36Sopenharmony_ci	if (ret < 0)
92362306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to add battery subdev\n");
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	power_devs[1].platform_data = pdata->power;
92662306a36Sopenharmony_ci	power_devs[1].pdata_size = sizeof(struct pm860x_power_pdata);
92762306a36Sopenharmony_ci	power_devs[1].num_resources = ARRAY_SIZE(charger_resources);
92862306a36Sopenharmony_ci	power_devs[1].resources = &charger_resources[0],
92962306a36Sopenharmony_ci	ret = mfd_add_devices(chip->dev, 0, &power_devs[1], 1,
93062306a36Sopenharmony_ci			      &charger_resources[0], chip->irq_base, NULL);
93162306a36Sopenharmony_ci	if (ret < 0)
93262306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to add charger subdev\n");
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	power_devs[2].platform_data = &preg_init_data;
93562306a36Sopenharmony_ci	power_devs[2].pdata_size = sizeof(struct regulator_init_data);
93662306a36Sopenharmony_ci	ret = mfd_add_devices(chip->dev, 0, &power_devs[2], 1,
93762306a36Sopenharmony_ci			      NULL, chip->irq_base, NULL);
93862306a36Sopenharmony_ci	if (ret < 0)
93962306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to add preg subdev\n");
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	if (pdata->chg_desc) {
94262306a36Sopenharmony_ci		pdata->chg_desc->charger_regulators =
94362306a36Sopenharmony_ci			&chg_desc_regulator_data[0];
94462306a36Sopenharmony_ci		pdata->chg_desc->num_charger_regulators	=
94562306a36Sopenharmony_ci			ARRAY_SIZE(chg_desc_regulator_data),
94662306a36Sopenharmony_ci		power_devs[3].platform_data = pdata->chg_desc;
94762306a36Sopenharmony_ci		power_devs[3].pdata_size = sizeof(*pdata->chg_desc);
94862306a36Sopenharmony_ci		ret = mfd_add_devices(chip->dev, 0, &power_devs[3], 1,
94962306a36Sopenharmony_ci				      NULL, chip->irq_base, NULL);
95062306a36Sopenharmony_ci		if (ret < 0)
95162306a36Sopenharmony_ci			dev_err(chip->dev, "Failed to add chg-manager subdev\n");
95262306a36Sopenharmony_ci	}
95362306a36Sopenharmony_ci}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_cistatic void device_onkey_init(struct pm860x_chip *chip,
95662306a36Sopenharmony_ci					struct pm860x_platform_data *pdata)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	int ret;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	onkey_devs[0].num_resources = ARRAY_SIZE(onkey_resources);
96162306a36Sopenharmony_ci	onkey_devs[0].resources = &onkey_resources[0],
96262306a36Sopenharmony_ci	ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
96362306a36Sopenharmony_ci			      ARRAY_SIZE(onkey_devs), &onkey_resources[0],
96462306a36Sopenharmony_ci			      chip->irq_base, NULL);
96562306a36Sopenharmony_ci	if (ret < 0)
96662306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to add onkey subdev\n");
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic void device_codec_init(struct pm860x_chip *chip,
97062306a36Sopenharmony_ci					struct pm860x_platform_data *pdata)
97162306a36Sopenharmony_ci{
97262306a36Sopenharmony_ci	int ret;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	codec_devs[0].num_resources = ARRAY_SIZE(codec_resources);
97562306a36Sopenharmony_ci	codec_devs[0].resources = &codec_resources[0],
97662306a36Sopenharmony_ci	ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
97762306a36Sopenharmony_ci			      ARRAY_SIZE(codec_devs), &codec_resources[0], 0,
97862306a36Sopenharmony_ci			      NULL);
97962306a36Sopenharmony_ci	if (ret < 0)
98062306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to add codec subdev\n");
98162306a36Sopenharmony_ci}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_cistatic void device_8607_init(struct pm860x_chip *chip,
98462306a36Sopenharmony_ci				       struct i2c_client *i2c,
98562306a36Sopenharmony_ci				       struct pm860x_platform_data *pdata)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	int data, ret;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
99062306a36Sopenharmony_ci	if (ret < 0) {
99162306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
99262306a36Sopenharmony_ci		goto out;
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci	switch (ret & PM8607_VERSION_MASK) {
99562306a36Sopenharmony_ci	case 0x40:
99662306a36Sopenharmony_ci	case 0x50:
99762306a36Sopenharmony_ci		dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
99862306a36Sopenharmony_ci			 ret);
99962306a36Sopenharmony_ci		break;
100062306a36Sopenharmony_ci	default:
100162306a36Sopenharmony_ci		dev_err(chip->dev,
100262306a36Sopenharmony_ci			"Failed to detect Marvell 88PM8607. Chip ID: %02x\n",
100362306a36Sopenharmony_ci			ret);
100462306a36Sopenharmony_ci		goto out;
100562306a36Sopenharmony_ci	}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	ret = pm860x_reg_read(i2c, PM8607_BUCK3);
100862306a36Sopenharmony_ci	if (ret < 0) {
100962306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
101062306a36Sopenharmony_ci		goto out;
101162306a36Sopenharmony_ci	}
101262306a36Sopenharmony_ci	if (ret & PM8607_BUCK3_DOUBLE)
101362306a36Sopenharmony_ci		chip->buck3_double = 1;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
101662306a36Sopenharmony_ci	if (ret < 0) {
101762306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
101862306a36Sopenharmony_ci		goto out;
101962306a36Sopenharmony_ci	}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	if (pdata && (pdata->i2c_port == PI2C_PORT))
102262306a36Sopenharmony_ci		data = PM8607_B0_MISC1_PI2C;
102362306a36Sopenharmony_ci	else
102462306a36Sopenharmony_ci		data = 0;
102562306a36Sopenharmony_ci	ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, PM8607_B0_MISC1_PI2C, data);
102662306a36Sopenharmony_ci	if (ret < 0) {
102762306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to access MISC1:%d\n", ret);
102862306a36Sopenharmony_ci		goto out;
102962306a36Sopenharmony_ci	}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	ret = device_irq_init(chip, pdata);
103262306a36Sopenharmony_ci	if (ret < 0)
103362306a36Sopenharmony_ci		goto out;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	device_regulator_init(chip, pdata);
103662306a36Sopenharmony_ci	device_rtc_init(chip, pdata);
103762306a36Sopenharmony_ci	device_onkey_init(chip, pdata);
103862306a36Sopenharmony_ci	device_touch_init(chip, pdata);
103962306a36Sopenharmony_ci	device_power_init(chip, pdata);
104062306a36Sopenharmony_ci	device_codec_init(chip, pdata);
104162306a36Sopenharmony_ciout:
104262306a36Sopenharmony_ci	return;
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_cistatic void device_8606_init(struct pm860x_chip *chip,
104662306a36Sopenharmony_ci				       struct i2c_client *i2c,
104762306a36Sopenharmony_ci				       struct pm860x_platform_data *pdata)
104862306a36Sopenharmony_ci{
104962306a36Sopenharmony_ci	device_osc_init(i2c);
105062306a36Sopenharmony_ci	device_bk_init(chip, pdata);
105162306a36Sopenharmony_ci	device_led_init(chip, pdata);
105262306a36Sopenharmony_ci}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_cistatic int pm860x_device_init(struct pm860x_chip *chip,
105562306a36Sopenharmony_ci					struct pm860x_platform_data *pdata)
105662306a36Sopenharmony_ci{
105762306a36Sopenharmony_ci	chip->core_irq = 0;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	switch (chip->id) {
106062306a36Sopenharmony_ci	case CHIP_PM8606:
106162306a36Sopenharmony_ci		device_8606_init(chip, chip->client, pdata);
106262306a36Sopenharmony_ci		break;
106362306a36Sopenharmony_ci	case CHIP_PM8607:
106462306a36Sopenharmony_ci		device_8607_init(chip, chip->client, pdata);
106562306a36Sopenharmony_ci		break;
106662306a36Sopenharmony_ci	}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	if (chip->companion) {
106962306a36Sopenharmony_ci		switch (chip->id) {
107062306a36Sopenharmony_ci		case CHIP_PM8607:
107162306a36Sopenharmony_ci			device_8606_init(chip, chip->companion, pdata);
107262306a36Sopenharmony_ci			break;
107362306a36Sopenharmony_ci		case CHIP_PM8606:
107462306a36Sopenharmony_ci			device_8607_init(chip, chip->companion, pdata);
107562306a36Sopenharmony_ci			break;
107662306a36Sopenharmony_ci		}
107762306a36Sopenharmony_ci	}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	return 0;
108062306a36Sopenharmony_ci}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_cistatic void pm860x_device_exit(struct pm860x_chip *chip)
108362306a36Sopenharmony_ci{
108462306a36Sopenharmony_ci	device_irq_exit(chip);
108562306a36Sopenharmony_ci	mfd_remove_devices(chip->dev);
108662306a36Sopenharmony_ci}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_cistatic int verify_addr(struct i2c_client *i2c)
108962306a36Sopenharmony_ci{
109062306a36Sopenharmony_ci	unsigned short addr_8607[] = {0x30, 0x34};
109162306a36Sopenharmony_ci	unsigned short addr_8606[] = {0x10, 0x11};
109262306a36Sopenharmony_ci	int size, i;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	if (i2c == NULL)
109562306a36Sopenharmony_ci		return 0;
109662306a36Sopenharmony_ci	size = ARRAY_SIZE(addr_8606);
109762306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
109862306a36Sopenharmony_ci		if (i2c->addr == *(addr_8606 + i))
109962306a36Sopenharmony_ci			return CHIP_PM8606;
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci	size = ARRAY_SIZE(addr_8607);
110262306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
110362306a36Sopenharmony_ci		if (i2c->addr == *(addr_8607 + i))
110462306a36Sopenharmony_ci			return CHIP_PM8607;
110562306a36Sopenharmony_ci	}
110662306a36Sopenharmony_ci	return 0;
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_cistatic const struct regmap_config pm860x_regmap_config = {
111062306a36Sopenharmony_ci	.reg_bits = 8,
111162306a36Sopenharmony_ci	.val_bits = 8,
111262306a36Sopenharmony_ci};
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_cistatic int pm860x_dt_init(struct device_node *np,
111562306a36Sopenharmony_ci				    struct device *dev,
111662306a36Sopenharmony_ci				    struct pm860x_platform_data *pdata)
111762306a36Sopenharmony_ci{
111862306a36Sopenharmony_ci	int ret;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	pdata->irq_mode = of_property_read_bool(np, "marvell,88pm860x-irq-read-clr");
112162306a36Sopenharmony_ci	ret = of_property_read_u32(np, "marvell,88pm860x-slave-addr",
112262306a36Sopenharmony_ci				   &pdata->companion_addr);
112362306a36Sopenharmony_ci	if (ret) {
112462306a36Sopenharmony_ci		dev_err(dev,
112562306a36Sopenharmony_ci			"Not found \"marvell,88pm860x-slave-addr\" property\n");
112662306a36Sopenharmony_ci		pdata->companion_addr = 0;
112762306a36Sopenharmony_ci	}
112862306a36Sopenharmony_ci	return 0;
112962306a36Sopenharmony_ci}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_cistatic int pm860x_probe(struct i2c_client *client)
113262306a36Sopenharmony_ci{
113362306a36Sopenharmony_ci	struct pm860x_platform_data *pdata = dev_get_platdata(&client->dev);
113462306a36Sopenharmony_ci	struct device_node *node = client->dev.of_node;
113562306a36Sopenharmony_ci	struct pm860x_chip *chip;
113662306a36Sopenharmony_ci	int ret;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	if (node && !pdata) {
113962306a36Sopenharmony_ci		/* parse DT to get platform data */
114062306a36Sopenharmony_ci		pdata = devm_kzalloc(&client->dev,
114162306a36Sopenharmony_ci				     sizeof(struct pm860x_platform_data),
114262306a36Sopenharmony_ci				     GFP_KERNEL);
114362306a36Sopenharmony_ci		if (!pdata)
114462306a36Sopenharmony_ci			return -ENOMEM;
114562306a36Sopenharmony_ci		ret = pm860x_dt_init(node, &client->dev, pdata);
114662306a36Sopenharmony_ci		if (ret)
114762306a36Sopenharmony_ci			return ret;
114862306a36Sopenharmony_ci	} else if (!pdata) {
114962306a36Sopenharmony_ci		pr_info("No platform data in %s!\n", __func__);
115062306a36Sopenharmony_ci		return -EINVAL;
115162306a36Sopenharmony_ci	}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	chip = devm_kzalloc(&client->dev,
115462306a36Sopenharmony_ci			    sizeof(struct pm860x_chip), GFP_KERNEL);
115562306a36Sopenharmony_ci	if (chip == NULL)
115662306a36Sopenharmony_ci		return -ENOMEM;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	chip->id = verify_addr(client);
115962306a36Sopenharmony_ci	chip->regmap = devm_regmap_init_i2c(client, &pm860x_regmap_config);
116062306a36Sopenharmony_ci	if (IS_ERR(chip->regmap)) {
116162306a36Sopenharmony_ci		ret = PTR_ERR(chip->regmap);
116262306a36Sopenharmony_ci		dev_err(&client->dev, "Failed to allocate register map: %d\n",
116362306a36Sopenharmony_ci				ret);
116462306a36Sopenharmony_ci		return ret;
116562306a36Sopenharmony_ci	}
116662306a36Sopenharmony_ci	chip->client = client;
116762306a36Sopenharmony_ci	i2c_set_clientdata(client, chip);
116862306a36Sopenharmony_ci	chip->dev = &client->dev;
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	/*
117162306a36Sopenharmony_ci	 * Both client and companion client shares same platform driver.
117262306a36Sopenharmony_ci	 * Driver distinguishes them by pdata->companion_addr.
117362306a36Sopenharmony_ci	 * pdata->companion_addr is only assigned if companion chip exists.
117462306a36Sopenharmony_ci	 * At the same time, the companion_addr shouldn't equal to client
117562306a36Sopenharmony_ci	 * address.
117662306a36Sopenharmony_ci	 */
117762306a36Sopenharmony_ci	if (pdata->companion_addr && (pdata->companion_addr != client->addr)) {
117862306a36Sopenharmony_ci		chip->companion_addr = pdata->companion_addr;
117962306a36Sopenharmony_ci		chip->companion = i2c_new_dummy_device(chip->client->adapter,
118062306a36Sopenharmony_ci						chip->companion_addr);
118162306a36Sopenharmony_ci		if (IS_ERR(chip->companion)) {
118262306a36Sopenharmony_ci			dev_err(&client->dev,
118362306a36Sopenharmony_ci				"Failed to allocate I2C companion device\n");
118462306a36Sopenharmony_ci			return PTR_ERR(chip->companion);
118562306a36Sopenharmony_ci		}
118662306a36Sopenharmony_ci		chip->regmap_companion = regmap_init_i2c(chip->companion,
118762306a36Sopenharmony_ci							&pm860x_regmap_config);
118862306a36Sopenharmony_ci		if (IS_ERR(chip->regmap_companion)) {
118962306a36Sopenharmony_ci			ret = PTR_ERR(chip->regmap_companion);
119062306a36Sopenharmony_ci			dev_err(&chip->companion->dev,
119162306a36Sopenharmony_ci				"Failed to allocate register map: %d\n", ret);
119262306a36Sopenharmony_ci			i2c_unregister_device(chip->companion);
119362306a36Sopenharmony_ci			return ret;
119462306a36Sopenharmony_ci		}
119562306a36Sopenharmony_ci		i2c_set_clientdata(chip->companion, chip);
119662306a36Sopenharmony_ci	}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	pm860x_device_init(chip, pdata);
119962306a36Sopenharmony_ci	return 0;
120062306a36Sopenharmony_ci}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_cistatic void pm860x_remove(struct i2c_client *client)
120362306a36Sopenharmony_ci{
120462306a36Sopenharmony_ci	struct pm860x_chip *chip = i2c_get_clientdata(client);
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	pm860x_device_exit(chip);
120762306a36Sopenharmony_ci	if (chip->companion) {
120862306a36Sopenharmony_ci		regmap_exit(chip->regmap_companion);
120962306a36Sopenharmony_ci		i2c_unregister_device(chip->companion);
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci}
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_cistatic int pm860x_suspend(struct device *dev)
121462306a36Sopenharmony_ci{
121562306a36Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
121662306a36Sopenharmony_ci	struct pm860x_chip *chip = i2c_get_clientdata(client);
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	if (device_may_wakeup(dev) && chip->wakeup_flag)
121962306a36Sopenharmony_ci		enable_irq_wake(chip->core_irq);
122062306a36Sopenharmony_ci	return 0;
122162306a36Sopenharmony_ci}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_cistatic int pm860x_resume(struct device *dev)
122462306a36Sopenharmony_ci{
122562306a36Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
122662306a36Sopenharmony_ci	struct pm860x_chip *chip = i2c_get_clientdata(client);
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	if (device_may_wakeup(dev) && chip->wakeup_flag)
122962306a36Sopenharmony_ci		disable_irq_wake(chip->core_irq);
123062306a36Sopenharmony_ci	return 0;
123162306a36Sopenharmony_ci}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(pm860x_pm_ops, pm860x_suspend, pm860x_resume);
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_cistatic const struct i2c_device_id pm860x_id_table[] = {
123662306a36Sopenharmony_ci	{ "88PM860x", 0 },
123762306a36Sopenharmony_ci	{}
123862306a36Sopenharmony_ci};
123962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, pm860x_id_table);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_cistatic const struct of_device_id pm860x_dt_ids[] = {
124262306a36Sopenharmony_ci	{ .compatible = "marvell,88pm860x", },
124362306a36Sopenharmony_ci	{},
124462306a36Sopenharmony_ci};
124562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pm860x_dt_ids);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_cistatic struct i2c_driver pm860x_driver = {
124862306a36Sopenharmony_ci	.driver	= {
124962306a36Sopenharmony_ci		.name	= "88PM860x",
125062306a36Sopenharmony_ci		.pm     = pm_sleep_ptr(&pm860x_pm_ops),
125162306a36Sopenharmony_ci		.of_match_table	= pm860x_dt_ids,
125262306a36Sopenharmony_ci	},
125362306a36Sopenharmony_ci	.probe		= pm860x_probe,
125462306a36Sopenharmony_ci	.remove		= pm860x_remove,
125562306a36Sopenharmony_ci	.id_table	= pm860x_id_table,
125662306a36Sopenharmony_ci};
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_cistatic int __init pm860x_i2c_init(void)
125962306a36Sopenharmony_ci{
126062306a36Sopenharmony_ci	int ret;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	ret = i2c_add_driver(&pm860x_driver);
126362306a36Sopenharmony_ci	if (ret != 0)
126462306a36Sopenharmony_ci		pr_err("Failed to register 88PM860x I2C driver: %d\n", ret);
126562306a36Sopenharmony_ci	return ret;
126662306a36Sopenharmony_ci}
126762306a36Sopenharmony_cisubsys_initcall(pm860x_i2c_init);
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_cistatic void __exit pm860x_i2c_exit(void)
127062306a36Sopenharmony_ci{
127162306a36Sopenharmony_ci	i2c_del_driver(&pm860x_driver);
127262306a36Sopenharmony_ci}
127362306a36Sopenharmony_cimodule_exit(pm860x_i2c_exit);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ciMODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x");
127662306a36Sopenharmony_ciMODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
1277