170c6cce0SQiao Zhou /*
270c6cce0SQiao Zhou * Base driver for Marvell 88PM800
370c6cce0SQiao Zhou *
470c6cce0SQiao Zhou * Copyright (C) 2012 Marvell International Ltd.
570c6cce0SQiao Zhou * Haojian Zhuang <haojian.zhuang@marvell.com>
670c6cce0SQiao Zhou * Joseph(Yossi) Hanin <yhanin@marvell.com>
770c6cce0SQiao Zhou * Qiao Zhou <zhouqiao@marvell.com>
870c6cce0SQiao Zhou *
970c6cce0SQiao Zhou * This file is subject to the terms and conditions of the GNU General
1070c6cce0SQiao Zhou * Public License. See the file "COPYING" in the main directory of this
1170c6cce0SQiao Zhou * archive for more details.
1270c6cce0SQiao Zhou *
1370c6cce0SQiao Zhou * This program is distributed in the hope that it will be useful,
1470c6cce0SQiao Zhou * but WITHOUT ANY WARRANTY; without even the implied warranty of
1570c6cce0SQiao Zhou * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1670c6cce0SQiao Zhou * GNU General Public License for more details.
1770c6cce0SQiao Zhou *
1870c6cce0SQiao Zhou * You should have received a copy of the GNU General Public License
1970c6cce0SQiao Zhou * along with this program; if not, write to the Free Software
2070c6cce0SQiao Zhou * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2170c6cce0SQiao Zhou */
2270c6cce0SQiao Zhou
2370c6cce0SQiao Zhou #include <linux/kernel.h>
2470c6cce0SQiao Zhou #include <linux/module.h>
2552705344SChao Xie #include <linux/err.h>
2670c6cce0SQiao Zhou #include <linux/i2c.h>
2770c6cce0SQiao Zhou #include <linux/mfd/core.h>
2870c6cce0SQiao Zhou #include <linux/mfd/88pm80x.h>
2970c6cce0SQiao Zhou #include <linux/slab.h>
3070c6cce0SQiao Zhou
3170c6cce0SQiao Zhou /* Interrupt Registers */
3270c6cce0SQiao Zhou #define PM800_INT_STATUS1 (0x05)
3370c6cce0SQiao Zhou #define PM800_ONKEY_INT_STS1 (1 << 0)
3470c6cce0SQiao Zhou #define PM800_EXTON_INT_STS1 (1 << 1)
3570c6cce0SQiao Zhou #define PM800_CHG_INT_STS1 (1 << 2)
3670c6cce0SQiao Zhou #define PM800_BAT_INT_STS1 (1 << 3)
3770c6cce0SQiao Zhou #define PM800_RTC_INT_STS1 (1 << 4)
3870c6cce0SQiao Zhou #define PM800_CLASSD_OC_INT_STS1 (1 << 5)
3970c6cce0SQiao Zhou
4070c6cce0SQiao Zhou #define PM800_INT_STATUS2 (0x06)
4170c6cce0SQiao Zhou #define PM800_VBAT_INT_STS2 (1 << 0)
4270c6cce0SQiao Zhou #define PM800_VSYS_INT_STS2 (1 << 1)
4370c6cce0SQiao Zhou #define PM800_VCHG_INT_STS2 (1 << 2)
4470c6cce0SQiao Zhou #define PM800_TINT_INT_STS2 (1 << 3)
4570c6cce0SQiao Zhou #define PM800_GPADC0_INT_STS2 (1 << 4)
4670c6cce0SQiao Zhou #define PM800_TBAT_INT_STS2 (1 << 5)
4770c6cce0SQiao Zhou #define PM800_GPADC2_INT_STS2 (1 << 6)
4870c6cce0SQiao Zhou #define PM800_GPADC3_INT_STS2 (1 << 7)
4970c6cce0SQiao Zhou
5070c6cce0SQiao Zhou #define PM800_INT_STATUS3 (0x07)
5170c6cce0SQiao Zhou
5270c6cce0SQiao Zhou #define PM800_INT_STATUS4 (0x08)
5370c6cce0SQiao Zhou #define PM800_GPIO0_INT_STS4 (1 << 0)
5470c6cce0SQiao Zhou #define PM800_GPIO1_INT_STS4 (1 << 1)
5570c6cce0SQiao Zhou #define PM800_GPIO2_INT_STS4 (1 << 2)
5670c6cce0SQiao Zhou #define PM800_GPIO3_INT_STS4 (1 << 3)
5770c6cce0SQiao Zhou #define PM800_GPIO4_INT_STS4 (1 << 4)
5870c6cce0SQiao Zhou
5970c6cce0SQiao Zhou #define PM800_INT_ENA_1 (0x09)
6070c6cce0SQiao Zhou #define PM800_ONKEY_INT_ENA1 (1 << 0)
6170c6cce0SQiao Zhou #define PM800_EXTON_INT_ENA1 (1 << 1)
6270c6cce0SQiao Zhou #define PM800_CHG_INT_ENA1 (1 << 2)
6370c6cce0SQiao Zhou #define PM800_BAT_INT_ENA1 (1 << 3)
6470c6cce0SQiao Zhou #define PM800_RTC_INT_ENA1 (1 << 4)
6570c6cce0SQiao Zhou #define PM800_CLASSD_OC_INT_ENA1 (1 << 5)
6670c6cce0SQiao Zhou
6770c6cce0SQiao Zhou #define PM800_INT_ENA_2 (0x0A)
6870c6cce0SQiao Zhou #define PM800_VBAT_INT_ENA2 (1 << 0)
6970c6cce0SQiao Zhou #define PM800_VSYS_INT_ENA2 (1 << 1)
7070c6cce0SQiao Zhou #define PM800_VCHG_INT_ENA2 (1 << 2)
7170c6cce0SQiao Zhou #define PM800_TINT_INT_ENA2 (1 << 3)
7270c6cce0SQiao Zhou
7370c6cce0SQiao Zhou #define PM800_INT_ENA_3 (0x0B)
7470c6cce0SQiao Zhou #define PM800_GPADC0_INT_ENA3 (1 << 0)
7570c6cce0SQiao Zhou #define PM800_GPADC1_INT_ENA3 (1 << 1)
7670c6cce0SQiao Zhou #define PM800_GPADC2_INT_ENA3 (1 << 2)
7770c6cce0SQiao Zhou #define PM800_GPADC3_INT_ENA3 (1 << 3)
7870c6cce0SQiao Zhou #define PM800_GPADC4_INT_ENA3 (1 << 4)
7970c6cce0SQiao Zhou
8070c6cce0SQiao Zhou #define PM800_INT_ENA_4 (0x0C)
8170c6cce0SQiao Zhou #define PM800_GPIO0_INT_ENA4 (1 << 0)
8270c6cce0SQiao Zhou #define PM800_GPIO1_INT_ENA4 (1 << 1)
8370c6cce0SQiao Zhou #define PM800_GPIO2_INT_ENA4 (1 << 2)
8470c6cce0SQiao Zhou #define PM800_GPIO3_INT_ENA4 (1 << 3)
8570c6cce0SQiao Zhou #define PM800_GPIO4_INT_ENA4 (1 << 4)
8670c6cce0SQiao Zhou
8770c6cce0SQiao Zhou /* number of INT_ENA & INT_STATUS regs */
8870c6cce0SQiao Zhou #define PM800_INT_REG_NUM (4)
8970c6cce0SQiao Zhou
9070c6cce0SQiao Zhou /* Interrupt Number in 88PM800 */
9170c6cce0SQiao Zhou enum {
9270c6cce0SQiao Zhou PM800_IRQ_ONKEY, /*EN1b0 *//*0 */
9370c6cce0SQiao Zhou PM800_IRQ_EXTON, /*EN1b1 */
9470c6cce0SQiao Zhou PM800_IRQ_CHG, /*EN1b2 */
9570c6cce0SQiao Zhou PM800_IRQ_BAT, /*EN1b3 */
9670c6cce0SQiao Zhou PM800_IRQ_RTC, /*EN1b4 */
9770c6cce0SQiao Zhou PM800_IRQ_CLASSD, /*EN1b5 *//*5 */
9870c6cce0SQiao Zhou PM800_IRQ_VBAT, /*EN2b0 */
9970c6cce0SQiao Zhou PM800_IRQ_VSYS, /*EN2b1 */
10070c6cce0SQiao Zhou PM800_IRQ_VCHG, /*EN2b2 */
10170c6cce0SQiao Zhou PM800_IRQ_TINT, /*EN2b3 */
10270c6cce0SQiao Zhou PM800_IRQ_GPADC0, /*EN3b0 *//*10 */
10370c6cce0SQiao Zhou PM800_IRQ_GPADC1, /*EN3b1 */
10470c6cce0SQiao Zhou PM800_IRQ_GPADC2, /*EN3b2 */
10570c6cce0SQiao Zhou PM800_IRQ_GPADC3, /*EN3b3 */
10670c6cce0SQiao Zhou PM800_IRQ_GPADC4, /*EN3b4 */
10770c6cce0SQiao Zhou PM800_IRQ_GPIO0, /*EN4b0 *//*15 */
10870c6cce0SQiao Zhou PM800_IRQ_GPIO1, /*EN4b1 */
10970c6cce0SQiao Zhou PM800_IRQ_GPIO2, /*EN4b2 */
11070c6cce0SQiao Zhou PM800_IRQ_GPIO3, /*EN4b3 */
11170c6cce0SQiao Zhou PM800_IRQ_GPIO4, /*EN4b4 *//*19 */
11270c6cce0SQiao Zhou PM800_MAX_IRQ,
11370c6cce0SQiao Zhou };
11470c6cce0SQiao Zhou
11503dcc544SChao Xie /* PM800: generation identification number */
11603dcc544SChao Xie #define PM800_CHIP_GEN_ID_NUM 0x3
11770c6cce0SQiao Zhou
11870c6cce0SQiao Zhou static const struct i2c_device_id pm80x_id_table[] = {
11903dcc544SChao Xie {"88PM800", 0},
12031b3ffbdSSamuel Ortiz {} /* NULL terminated */
12170c6cce0SQiao Zhou };
12270c6cce0SQiao Zhou MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
12370c6cce0SQiao Zhou
124c4a164f4SRikard Falkeborn static const struct resource rtc_resources[] = {
1255c058e92SZhen Lei DEFINE_RES_IRQ_NAMED(PM800_IRQ_RTC, "88pm80x-rtc"),
12670c6cce0SQiao Zhou };
12770c6cce0SQiao Zhou
12870c6cce0SQiao Zhou static struct mfd_cell rtc_devs[] = {
12970c6cce0SQiao Zhou {
13070c6cce0SQiao Zhou .name = "88pm80x-rtc",
13170c6cce0SQiao Zhou .num_resources = ARRAY_SIZE(rtc_resources),
13270c6cce0SQiao Zhou .resources = &rtc_resources[0],
13370c6cce0SQiao Zhou .id = -1,
13470c6cce0SQiao Zhou },
13570c6cce0SQiao Zhou };
13670c6cce0SQiao Zhou
13770c6cce0SQiao Zhou static struct resource onkey_resources[] = {
1385c058e92SZhen Lei DEFINE_RES_IRQ_NAMED(PM800_IRQ_ONKEY, "88pm80x-onkey"),
13970c6cce0SQiao Zhou };
14070c6cce0SQiao Zhou
14104e02417SGeert Uytterhoeven static const struct mfd_cell onkey_devs[] = {
14270c6cce0SQiao Zhou {
14370c6cce0SQiao Zhou .name = "88pm80x-onkey",
14470c6cce0SQiao Zhou .num_resources = 1,
14570c6cce0SQiao Zhou .resources = &onkey_resources[0],
14670c6cce0SQiao Zhou .id = -1,
14770c6cce0SQiao Zhou },
14870c6cce0SQiao Zhou };
14970c6cce0SQiao Zhou
15004e02417SGeert Uytterhoeven static const struct mfd_cell regulator_devs[] = {
1512d3aa056SChao Xie {
1522d3aa056SChao Xie .name = "88pm80x-regulator",
1532d3aa056SChao Xie .id = -1,
1542d3aa056SChao Xie },
1552d3aa056SChao Xie };
1562d3aa056SChao Xie
15770c6cce0SQiao Zhou static const struct regmap_irq pm800_irqs[] = {
15870c6cce0SQiao Zhou /* INT0 */
15970c6cce0SQiao Zhou [PM800_IRQ_ONKEY] = {
16070c6cce0SQiao Zhou .mask = PM800_ONKEY_INT_ENA1,
16170c6cce0SQiao Zhou },
16270c6cce0SQiao Zhou [PM800_IRQ_EXTON] = {
16370c6cce0SQiao Zhou .mask = PM800_EXTON_INT_ENA1,
16470c6cce0SQiao Zhou },
16570c6cce0SQiao Zhou [PM800_IRQ_CHG] = {
16670c6cce0SQiao Zhou .mask = PM800_CHG_INT_ENA1,
16770c6cce0SQiao Zhou },
16870c6cce0SQiao Zhou [PM800_IRQ_BAT] = {
16970c6cce0SQiao Zhou .mask = PM800_BAT_INT_ENA1,
17070c6cce0SQiao Zhou },
17170c6cce0SQiao Zhou [PM800_IRQ_RTC] = {
17270c6cce0SQiao Zhou .mask = PM800_RTC_INT_ENA1,
17370c6cce0SQiao Zhou },
17470c6cce0SQiao Zhou [PM800_IRQ_CLASSD] = {
17570c6cce0SQiao Zhou .mask = PM800_CLASSD_OC_INT_ENA1,
17670c6cce0SQiao Zhou },
17770c6cce0SQiao Zhou /* INT1 */
17870c6cce0SQiao Zhou [PM800_IRQ_VBAT] = {
17970c6cce0SQiao Zhou .reg_offset = 1,
18070c6cce0SQiao Zhou .mask = PM800_VBAT_INT_ENA2,
18170c6cce0SQiao Zhou },
18270c6cce0SQiao Zhou [PM800_IRQ_VSYS] = {
18370c6cce0SQiao Zhou .reg_offset = 1,
18470c6cce0SQiao Zhou .mask = PM800_VSYS_INT_ENA2,
18570c6cce0SQiao Zhou },
18670c6cce0SQiao Zhou [PM800_IRQ_VCHG] = {
18770c6cce0SQiao Zhou .reg_offset = 1,
18870c6cce0SQiao Zhou .mask = PM800_VCHG_INT_ENA2,
18970c6cce0SQiao Zhou },
19070c6cce0SQiao Zhou [PM800_IRQ_TINT] = {
19170c6cce0SQiao Zhou .reg_offset = 1,
19270c6cce0SQiao Zhou .mask = PM800_TINT_INT_ENA2,
19370c6cce0SQiao Zhou },
19470c6cce0SQiao Zhou /* INT2 */
19570c6cce0SQiao Zhou [PM800_IRQ_GPADC0] = {
19670c6cce0SQiao Zhou .reg_offset = 2,
19770c6cce0SQiao Zhou .mask = PM800_GPADC0_INT_ENA3,
19870c6cce0SQiao Zhou },
19970c6cce0SQiao Zhou [PM800_IRQ_GPADC1] = {
20070c6cce0SQiao Zhou .reg_offset = 2,
20170c6cce0SQiao Zhou .mask = PM800_GPADC1_INT_ENA3,
20270c6cce0SQiao Zhou },
20370c6cce0SQiao Zhou [PM800_IRQ_GPADC2] = {
20470c6cce0SQiao Zhou .reg_offset = 2,
20570c6cce0SQiao Zhou .mask = PM800_GPADC2_INT_ENA3,
20670c6cce0SQiao Zhou },
20770c6cce0SQiao Zhou [PM800_IRQ_GPADC3] = {
20870c6cce0SQiao Zhou .reg_offset = 2,
20970c6cce0SQiao Zhou .mask = PM800_GPADC3_INT_ENA3,
21070c6cce0SQiao Zhou },
21170c6cce0SQiao Zhou [PM800_IRQ_GPADC4] = {
21270c6cce0SQiao Zhou .reg_offset = 2,
21370c6cce0SQiao Zhou .mask = PM800_GPADC4_INT_ENA3,
21470c6cce0SQiao Zhou },
21570c6cce0SQiao Zhou /* INT3 */
21670c6cce0SQiao Zhou [PM800_IRQ_GPIO0] = {
21770c6cce0SQiao Zhou .reg_offset = 3,
21870c6cce0SQiao Zhou .mask = PM800_GPIO0_INT_ENA4,
21970c6cce0SQiao Zhou },
22070c6cce0SQiao Zhou [PM800_IRQ_GPIO1] = {
22170c6cce0SQiao Zhou .reg_offset = 3,
22270c6cce0SQiao Zhou .mask = PM800_GPIO1_INT_ENA4,
22370c6cce0SQiao Zhou },
22470c6cce0SQiao Zhou [PM800_IRQ_GPIO2] = {
22570c6cce0SQiao Zhou .reg_offset = 3,
22670c6cce0SQiao Zhou .mask = PM800_GPIO2_INT_ENA4,
22770c6cce0SQiao Zhou },
22870c6cce0SQiao Zhou [PM800_IRQ_GPIO3] = {
22970c6cce0SQiao Zhou .reg_offset = 3,
23070c6cce0SQiao Zhou .mask = PM800_GPIO3_INT_ENA4,
23170c6cce0SQiao Zhou },
23270c6cce0SQiao Zhou [PM800_IRQ_GPIO4] = {
23370c6cce0SQiao Zhou .reg_offset = 3,
23470c6cce0SQiao Zhou .mask = PM800_GPIO4_INT_ENA4,
23570c6cce0SQiao Zhou },
23670c6cce0SQiao Zhou };
23770c6cce0SQiao Zhou
device_gpadc_init(struct pm80x_chip * chip,struct pm80x_platform_data * pdata)238f791be49SBill Pemberton static int device_gpadc_init(struct pm80x_chip *chip,
23970c6cce0SQiao Zhou struct pm80x_platform_data *pdata)
24070c6cce0SQiao Zhou {
24170c6cce0SQiao Zhou struct pm80x_subchip *subchip = chip->subchip;
24270c6cce0SQiao Zhou struct regmap *map = subchip->regmap_gpadc;
24370c6cce0SQiao Zhou int data = 0, mask = 0, ret = 0;
24470c6cce0SQiao Zhou
24570c6cce0SQiao Zhou if (!map) {
24670c6cce0SQiao Zhou dev_warn(chip->dev,
24770c6cce0SQiao Zhou "Warning: gpadc regmap is not available!\n");
24870c6cce0SQiao Zhou return -EINVAL;
24970c6cce0SQiao Zhou }
25070c6cce0SQiao Zhou /*
25170c6cce0SQiao Zhou * initialize GPADC without activating it turn on GPADC
25270c6cce0SQiao Zhou * measurments
25370c6cce0SQiao Zhou */
25470c6cce0SQiao Zhou ret = regmap_update_bits(map,
25570c6cce0SQiao Zhou PM800_GPADC_MISC_CONFIG2,
25670c6cce0SQiao Zhou PM800_GPADC_MISC_GPFSM_EN,
25770c6cce0SQiao Zhou PM800_GPADC_MISC_GPFSM_EN);
25870c6cce0SQiao Zhou if (ret < 0)
25970c6cce0SQiao Zhou goto out;
26070c6cce0SQiao Zhou /*
26170c6cce0SQiao Zhou * This function configures the ADC as requires for
26270c6cce0SQiao Zhou * CP implementation.CP does not "own" the ADC configuration
26370c6cce0SQiao Zhou * registers and relies on AP.
26470c6cce0SQiao Zhou * Reason: enable automatic ADC measurements needed
26570c6cce0SQiao Zhou * for CP to get VBAT and RF temperature readings.
26670c6cce0SQiao Zhou */
26770c6cce0SQiao Zhou ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN1,
26870c6cce0SQiao Zhou PM800_MEAS_EN1_VBAT, PM800_MEAS_EN1_VBAT);
26970c6cce0SQiao Zhou if (ret < 0)
27070c6cce0SQiao Zhou goto out;
27170c6cce0SQiao Zhou ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN2,
27270c6cce0SQiao Zhou (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN),
27370c6cce0SQiao Zhou (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN));
27470c6cce0SQiao Zhou if (ret < 0)
27570c6cce0SQiao Zhou goto out;
27670c6cce0SQiao Zhou
27770c6cce0SQiao Zhou /*
27870c6cce0SQiao Zhou * the defult of PM800 is GPADC operates at 100Ks/s rate
27970c6cce0SQiao Zhou * and Number of GPADC slots with active current bias prior
28070c6cce0SQiao Zhou * to GPADC sampling = 1 slot for all GPADCs set for
28170c6cce0SQiao Zhou * Temprature mesurmants
28270c6cce0SQiao Zhou */
28370c6cce0SQiao Zhou mask = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 |
28470c6cce0SQiao Zhou PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3);
28570c6cce0SQiao Zhou
28670c6cce0SQiao Zhou if (pdata && (pdata->batt_det == 0))
28770c6cce0SQiao Zhou data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 |
28870c6cce0SQiao Zhou PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3);
28970c6cce0SQiao Zhou else
29070c6cce0SQiao Zhou data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN2 |
29170c6cce0SQiao Zhou PM800_GPADC_GP_BIAS_EN3);
29270c6cce0SQiao Zhou
29370c6cce0SQiao Zhou ret = regmap_update_bits(map, PM800_GP_BIAS_ENA1, mask, data);
29470c6cce0SQiao Zhou if (ret < 0)
29570c6cce0SQiao Zhou goto out;
29670c6cce0SQiao Zhou
29770c6cce0SQiao Zhou dev_info(chip->dev, "pm800 device_gpadc_init: Done\n");
29870c6cce0SQiao Zhou return 0;
29970c6cce0SQiao Zhou
30070c6cce0SQiao Zhou out:
30170c6cce0SQiao Zhou dev_info(chip->dev, "pm800 device_gpadc_init: Failed!\n");
30270c6cce0SQiao Zhou return ret;
30370c6cce0SQiao Zhou }
30470c6cce0SQiao Zhou
device_onkey_init(struct pm80x_chip * chip,struct pm80x_platform_data * pdata)3053a3ece54SChao Xie static int device_onkey_init(struct pm80x_chip *chip,
3063a3ece54SChao Xie struct pm80x_platform_data *pdata)
3073a3ece54SChao Xie {
3083a3ece54SChao Xie int ret;
3093a3ece54SChao Xie
3103a3ece54SChao Xie ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
3113a3ece54SChao Xie ARRAY_SIZE(onkey_devs), &onkey_resources[0], 0,
3123a3ece54SChao Xie NULL);
3133a3ece54SChao Xie if (ret) {
3143a3ece54SChao Xie dev_err(chip->dev, "Failed to add onkey subdev\n");
3153a3ece54SChao Xie return ret;
3163a3ece54SChao Xie }
3173a3ece54SChao Xie
3183a3ece54SChao Xie return 0;
3193a3ece54SChao Xie }
3203a3ece54SChao Xie
device_rtc_init(struct pm80x_chip * chip,struct pm80x_platform_data * pdata)3213a3ece54SChao Xie static int device_rtc_init(struct pm80x_chip *chip,
3223a3ece54SChao Xie struct pm80x_platform_data *pdata)
3233a3ece54SChao Xie {
3243a3ece54SChao Xie int ret;
3253a3ece54SChao Xie
326b432fc25SChao Xie if (pdata) {
3273a3ece54SChao Xie rtc_devs[0].platform_data = pdata->rtc;
3283a3ece54SChao Xie rtc_devs[0].pdata_size =
3293a3ece54SChao Xie pdata->rtc ? sizeof(struct pm80x_rtc_pdata) : 0;
330b432fc25SChao Xie }
3313a3ece54SChao Xie ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
3323a3ece54SChao Xie ARRAY_SIZE(rtc_devs), NULL, 0, NULL);
3333a3ece54SChao Xie if (ret) {
3343a3ece54SChao Xie dev_err(chip->dev, "Failed to add rtc subdev\n");
3353a3ece54SChao Xie return ret;
3363a3ece54SChao Xie }
3373a3ece54SChao Xie
3383a3ece54SChao Xie return 0;
3393a3ece54SChao Xie }
3403a3ece54SChao Xie
device_regulator_init(struct pm80x_chip * chip,struct pm80x_platform_data * pdata)3412d3aa056SChao Xie static int device_regulator_init(struct pm80x_chip *chip,
3422d3aa056SChao Xie struct pm80x_platform_data *pdata)
3432d3aa056SChao Xie {
3442d3aa056SChao Xie int ret;
3452d3aa056SChao Xie
3462d3aa056SChao Xie ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[0],
3472d3aa056SChao Xie ARRAY_SIZE(regulator_devs), NULL, 0, NULL);
3482d3aa056SChao Xie if (ret) {
3492d3aa056SChao Xie dev_err(chip->dev, "Failed to add regulator subdev\n");
3502d3aa056SChao Xie return ret;
3512d3aa056SChao Xie }
3522d3aa056SChao Xie
3532d3aa056SChao Xie return 0;
3542d3aa056SChao Xie }
3552d3aa056SChao Xie
device_irq_init_800(struct pm80x_chip * chip)356f791be49SBill Pemberton static int device_irq_init_800(struct pm80x_chip *chip)
35770c6cce0SQiao Zhou {
35870c6cce0SQiao Zhou struct regmap *map = chip->regmap;
3591ef5677eSYi Zhang unsigned long flags = IRQF_ONESHOT;
36070c6cce0SQiao Zhou int data, mask, ret = -EINVAL;
36170c6cce0SQiao Zhou
36270c6cce0SQiao Zhou if (!map || !chip->irq) {
36370c6cce0SQiao Zhou dev_err(chip->dev, "incorrect parameters\n");
36470c6cce0SQiao Zhou return -EINVAL;
36570c6cce0SQiao Zhou }
36670c6cce0SQiao Zhou
36770c6cce0SQiao Zhou /*
36870c6cce0SQiao Zhou * irq_mode defines the way of clearing interrupt. it's read-clear by
36970c6cce0SQiao Zhou * default.
37070c6cce0SQiao Zhou */
37170c6cce0SQiao Zhou mask =
37270c6cce0SQiao Zhou PM800_WAKEUP2_INV_INT | PM800_WAKEUP2_INT_CLEAR |
37370c6cce0SQiao Zhou PM800_WAKEUP2_INT_MASK;
37470c6cce0SQiao Zhou
37570c6cce0SQiao Zhou data = PM800_WAKEUP2_INT_CLEAR;
37670c6cce0SQiao Zhou ret = regmap_update_bits(map, PM800_WAKEUP2, mask, data);
37770c6cce0SQiao Zhou
37870c6cce0SQiao Zhou if (ret < 0)
37970c6cce0SQiao Zhou goto out;
38070c6cce0SQiao Zhou
38170c6cce0SQiao Zhou ret =
38270c6cce0SQiao Zhou regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1,
38370c6cce0SQiao Zhou chip->regmap_irq_chip, &chip->irq_data);
38470c6cce0SQiao Zhou
38570c6cce0SQiao Zhou out:
38670c6cce0SQiao Zhou return ret;
38770c6cce0SQiao Zhou }
38870c6cce0SQiao Zhou
device_irq_exit_800(struct pm80x_chip * chip)38970c6cce0SQiao Zhou static void device_irq_exit_800(struct pm80x_chip *chip)
39070c6cce0SQiao Zhou {
39170c6cce0SQiao Zhou regmap_del_irq_chip(chip->irq, chip->irq_data);
39270c6cce0SQiao Zhou }
39370c6cce0SQiao Zhou
39470c6cce0SQiao Zhou static struct regmap_irq_chip pm800_irq_chip = {
39570c6cce0SQiao Zhou .name = "88pm800",
39670c6cce0SQiao Zhou .irqs = pm800_irqs,
39770c6cce0SQiao Zhou .num_irqs = ARRAY_SIZE(pm800_irqs),
39870c6cce0SQiao Zhou
39970c6cce0SQiao Zhou .num_regs = 4,
40070c6cce0SQiao Zhou .status_base = PM800_INT_STATUS1,
4010eeb2ddcSAidan MacDonald .unmask_base = PM800_INT_ENA_1,
40270c6cce0SQiao Zhou .ack_base = PM800_INT_STATUS1,
40370c6cce0SQiao Zhou };
40470c6cce0SQiao Zhou
pm800_pages_init(struct pm80x_chip * chip)40570c6cce0SQiao Zhou static int pm800_pages_init(struct pm80x_chip *chip)
40670c6cce0SQiao Zhou {
40770c6cce0SQiao Zhou struct pm80x_subchip *subchip;
40870c6cce0SQiao Zhou struct i2c_client *client = chip->client;
40970c6cce0SQiao Zhou
41052705344SChao Xie int ret = 0;
41170c6cce0SQiao Zhou
41252705344SChao Xie subchip = chip->subchip;
41352705344SChao Xie if (!subchip || !subchip->power_page_addr || !subchip->gpadc_page_addr)
41452705344SChao Xie return -ENODEV;
41552705344SChao Xie
41652705344SChao Xie /* PM800 block power page */
41783215897SWolfram Sang subchip->power_page = i2c_new_dummy_device(client->adapter,
41852705344SChao Xie subchip->power_page_addr);
41983215897SWolfram Sang if (IS_ERR(subchip->power_page)) {
42083215897SWolfram Sang ret = PTR_ERR(subchip->power_page);
42152705344SChao Xie goto out;
42252705344SChao Xie }
42352705344SChao Xie
42452705344SChao Xie subchip->regmap_power = devm_regmap_init_i2c(subchip->power_page,
42552705344SChao Xie &pm80x_regmap_config);
42652705344SChao Xie if (IS_ERR(subchip->regmap_power)) {
42752705344SChao Xie ret = PTR_ERR(subchip->regmap_power);
42852705344SChao Xie dev_err(chip->dev,
42952705344SChao Xie "Failed to allocate regmap_power: %d\n", ret);
43052705344SChao Xie goto out;
43152705344SChao Xie }
43252705344SChao Xie
43352705344SChao Xie i2c_set_clientdata(subchip->power_page, chip);
43452705344SChao Xie
43552705344SChao Xie /* PM800 block GPADC */
43683215897SWolfram Sang subchip->gpadc_page = i2c_new_dummy_device(client->adapter,
43770c6cce0SQiao Zhou subchip->gpadc_page_addr);
43883215897SWolfram Sang if (IS_ERR(subchip->gpadc_page)) {
43983215897SWolfram Sang ret = PTR_ERR(subchip->gpadc_page);
44052705344SChao Xie goto out;
44152705344SChao Xie }
44270c6cce0SQiao Zhou
44352705344SChao Xie subchip->regmap_gpadc = devm_regmap_init_i2c(subchip->gpadc_page,
44452705344SChao Xie &pm80x_regmap_config);
44552705344SChao Xie if (IS_ERR(subchip->regmap_gpadc)) {
44652705344SChao Xie ret = PTR_ERR(subchip->regmap_gpadc);
44752705344SChao Xie dev_err(chip->dev,
44852705344SChao Xie "Failed to allocate regmap_gpadc: %d\n", ret);
44952705344SChao Xie goto out;
45052705344SChao Xie }
45152705344SChao Xie i2c_set_clientdata(subchip->gpadc_page, chip);
45252705344SChao Xie
45352705344SChao Xie out:
45452705344SChao Xie return ret;
45570c6cce0SQiao Zhou }
45670c6cce0SQiao Zhou
pm800_pages_exit(struct pm80x_chip * chip)45770c6cce0SQiao Zhou static void pm800_pages_exit(struct pm80x_chip *chip)
45870c6cce0SQiao Zhou {
45970c6cce0SQiao Zhou struct pm80x_subchip *subchip;
46070c6cce0SQiao Zhou
46170c6cce0SQiao Zhou subchip = chip->subchip;
46252705344SChao Xie
46352705344SChao Xie if (subchip && subchip->power_page)
46470c6cce0SQiao Zhou i2c_unregister_device(subchip->power_page);
46552705344SChao Xie
46652705344SChao Xie if (subchip && subchip->gpadc_page)
46770c6cce0SQiao Zhou i2c_unregister_device(subchip->gpadc_page);
46870c6cce0SQiao Zhou }
46970c6cce0SQiao Zhou
device_800_init(struct pm80x_chip * chip,struct pm80x_platform_data * pdata)470f791be49SBill Pemberton static int device_800_init(struct pm80x_chip *chip,
47170c6cce0SQiao Zhou struct pm80x_platform_data *pdata)
47270c6cce0SQiao Zhou {
47303dcc544SChao Xie int ret;
47446b65a8fSAxel Lin unsigned int val;
47570c6cce0SQiao Zhou
47670c6cce0SQiao Zhou /*
47770c6cce0SQiao Zhou * alarm wake up bit will be clear in device_irq_init(),
47870c6cce0SQiao Zhou * read before that
47970c6cce0SQiao Zhou */
48046b65a8fSAxel Lin ret = regmap_read(chip->regmap, PM800_RTC_CONTROL, &val);
48170c6cce0SQiao Zhou if (ret < 0) {
48270c6cce0SQiao Zhou dev_err(chip->dev, "Failed to read RTC register: %d\n", ret);
48370c6cce0SQiao Zhou goto out;
48470c6cce0SQiao Zhou }
48546b65a8fSAxel Lin if (val & PM800_ALARM_WAKEUP) {
48670c6cce0SQiao Zhou if (pdata && pdata->rtc)
48770c6cce0SQiao Zhou pdata->rtc->rtc_wakeup = 1;
48870c6cce0SQiao Zhou }
48970c6cce0SQiao Zhou
49070c6cce0SQiao Zhou ret = device_gpadc_init(chip, pdata);
49170c6cce0SQiao Zhou if (ret < 0) {
49270c6cce0SQiao Zhou dev_err(chip->dev, "[%s]Failed to init gpadc\n", __func__);
49370c6cce0SQiao Zhou goto out;
49470c6cce0SQiao Zhou }
49570c6cce0SQiao Zhou
49670c6cce0SQiao Zhou chip->regmap_irq_chip = &pm800_irq_chip;
49770c6cce0SQiao Zhou
49870c6cce0SQiao Zhou ret = device_irq_init_800(chip);
49970c6cce0SQiao Zhou if (ret < 0) {
50070c6cce0SQiao Zhou dev_err(chip->dev, "[%s]Failed to init pm800 irq\n", __func__);
50170c6cce0SQiao Zhou goto out;
50270c6cce0SQiao Zhou }
50370c6cce0SQiao Zhou
5043a3ece54SChao Xie ret = device_onkey_init(chip, pdata);
5053a3ece54SChao Xie if (ret) {
50670c6cce0SQiao Zhou dev_err(chip->dev, "Failed to add onkey subdev\n");
50770c6cce0SQiao Zhou goto out_dev;
5083a3ece54SChao Xie }
50970c6cce0SQiao Zhou
5103a3ece54SChao Xie ret = device_rtc_init(chip, pdata);
5113a3ece54SChao Xie if (ret) {
51270c6cce0SQiao Zhou dev_err(chip->dev, "Failed to add rtc subdev\n");
5133a3ece54SChao Xie goto out;
51470c6cce0SQiao Zhou }
51570c6cce0SQiao Zhou
5162d3aa056SChao Xie ret = device_regulator_init(chip, pdata);
5172d3aa056SChao Xie if (ret) {
5182d3aa056SChao Xie dev_err(chip->dev, "Failed to add regulators subdev\n");
5192d3aa056SChao Xie goto out;
5202d3aa056SChao Xie }
5212d3aa056SChao Xie
52270c6cce0SQiao Zhou return 0;
52370c6cce0SQiao Zhou out_dev:
52470c6cce0SQiao Zhou mfd_remove_devices(chip->dev);
52570c6cce0SQiao Zhou device_irq_exit_800(chip);
52670c6cce0SQiao Zhou out:
52770c6cce0SQiao Zhou return ret;
52870c6cce0SQiao Zhou }
52970c6cce0SQiao Zhou
pm800_probe(struct i2c_client * client)530b9420da8SUwe Kleine-König static int pm800_probe(struct i2c_client *client)
53170c6cce0SQiao Zhou {
53270c6cce0SQiao Zhou int ret = 0;
53370c6cce0SQiao Zhou struct pm80x_chip *chip;
534334a41ceSJingoo Han struct pm80x_platform_data *pdata = dev_get_platdata(&client->dev);
53570c6cce0SQiao Zhou struct pm80x_subchip *subchip;
53670c6cce0SQiao Zhou
53703dcc544SChao Xie ret = pm80x_init(client);
53870c6cce0SQiao Zhou if (ret) {
53970c6cce0SQiao Zhou dev_err(&client->dev, "pm800_init fail\n");
54070c6cce0SQiao Zhou goto out_init;
54170c6cce0SQiao Zhou }
54270c6cce0SQiao Zhou
54370c6cce0SQiao Zhou chip = i2c_get_clientdata(client);
54470c6cce0SQiao Zhou
54570c6cce0SQiao Zhou /* init subchip for PM800 */
54670c6cce0SQiao Zhou subchip =
54770c6cce0SQiao Zhou devm_kzalloc(&client->dev, sizeof(struct pm80x_subchip),
54870c6cce0SQiao Zhou GFP_KERNEL);
54970c6cce0SQiao Zhou if (!subchip) {
55070c6cce0SQiao Zhou ret = -ENOMEM;
55170c6cce0SQiao Zhou goto err_subchip_alloc;
55270c6cce0SQiao Zhou }
55370c6cce0SQiao Zhou
554c750d8e0SChao Xie /* pm800 has 2 addtional pages to support power and gpadc. */
555c750d8e0SChao Xie subchip->power_page_addr = client->addr + 1;
556c750d8e0SChao Xie subchip->gpadc_page_addr = client->addr + 2;
55770c6cce0SQiao Zhou chip->subchip = subchip;
55870c6cce0SQiao Zhou
55970c6cce0SQiao Zhou ret = pm800_pages_init(chip);
56070c6cce0SQiao Zhou if (ret) {
56170c6cce0SQiao Zhou dev_err(&client->dev, "pm800_pages_init failed!\n");
562141050cfSKrzysztof Kozlowski goto err_device_init;
56370c6cce0SQiao Zhou }
56470c6cce0SQiao Zhou
565618fa575SYi Zhang ret = device_800_init(chip, pdata);
566618fa575SYi Zhang if (ret) {
56703dcc544SChao Xie dev_err(chip->dev, "Failed to initialize 88pm800 devices\n");
568618fa575SYi Zhang goto err_device_init;
569618fa575SYi Zhang }
570618fa575SYi Zhang
571b432fc25SChao Xie if (pdata && pdata->plat_config)
57270c6cce0SQiao Zhou pdata->plat_config(chip, pdata);
57370c6cce0SQiao Zhou
574618fa575SYi Zhang return 0;
575618fa575SYi Zhang
576618fa575SYi Zhang err_device_init:
577618fa575SYi Zhang pm800_pages_exit(chip);
57870c6cce0SQiao Zhou err_subchip_alloc:
579306df798SYi Zhang pm80x_deinit();
58070c6cce0SQiao Zhou out_init:
58170c6cce0SQiao Zhou return ret;
58270c6cce0SQiao Zhou }
58370c6cce0SQiao Zhou
pm800_remove(struct i2c_client * client)584ed5c2f5fSUwe Kleine-König static void pm800_remove(struct i2c_client *client)
58570c6cce0SQiao Zhou {
58670c6cce0SQiao Zhou struct pm80x_chip *chip = i2c_get_clientdata(client);
58770c6cce0SQiao Zhou
58870c6cce0SQiao Zhou mfd_remove_devices(chip->dev);
58970c6cce0SQiao Zhou device_irq_exit_800(chip);
59070c6cce0SQiao Zhou
59170c6cce0SQiao Zhou pm800_pages_exit(chip);
592306df798SYi Zhang pm80x_deinit();
59370c6cce0SQiao Zhou }
59470c6cce0SQiao Zhou
59570c6cce0SQiao Zhou static struct i2c_driver pm800_driver = {
59670c6cce0SQiao Zhou .driver = {
59746223a19SChao Xie .name = "88PM800",
59819755a0aSPaul Cercueil .pm = pm_sleep_ptr(&pm80x_pm_ops),
59970c6cce0SQiao Zhou },
600*9816d859SUwe Kleine-König .probe = pm800_probe,
60184449216SBill Pemberton .remove = pm800_remove,
60270c6cce0SQiao Zhou .id_table = pm80x_id_table,
60370c6cce0SQiao Zhou };
60470c6cce0SQiao Zhou
pm800_i2c_init(void)60570c6cce0SQiao Zhou static int __init pm800_i2c_init(void)
60670c6cce0SQiao Zhou {
60770c6cce0SQiao Zhou return i2c_add_driver(&pm800_driver);
60870c6cce0SQiao Zhou }
60970c6cce0SQiao Zhou subsys_initcall(pm800_i2c_init);
61070c6cce0SQiao Zhou
pm800_i2c_exit(void)61170c6cce0SQiao Zhou static void __exit pm800_i2c_exit(void)
61270c6cce0SQiao Zhou {
61370c6cce0SQiao Zhou i2c_del_driver(&pm800_driver);
61470c6cce0SQiao Zhou }
61570c6cce0SQiao Zhou module_exit(pm800_i2c_exit);
61670c6cce0SQiao Zhou
61770c6cce0SQiao Zhou MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM800");
61870c6cce0SQiao Zhou MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
61970c6cce0SQiao Zhou MODULE_LICENSE("GPL");
620