xref: /openbmc/linux/drivers/mfd/hi655x-pmic.c (revision ac03fec1)
1b52207efSChen Feng /*
2b52207efSChen Feng  * Device driver for MFD hi655x PMIC
3b52207efSChen Feng  *
4b52207efSChen Feng  * Copyright (c) 2016 Hisilicon.
5b52207efSChen Feng  *
6b52207efSChen Feng  * Authors:
7b52207efSChen Feng  * Chen Feng <puck.chen@hisilicon.com>
8b52207efSChen Feng  * Fei  Wang <w.f@huawei.com>
9b52207efSChen Feng  *
10b52207efSChen Feng  * This program is free software; you can redistribute it and/or modify
11b52207efSChen Feng  * it under the terms of the GNU General Public License version 2 as
12b52207efSChen Feng  * published by the Free Software Foundation.
13b52207efSChen Feng  */
14b52207efSChen Feng 
15b52207efSChen Feng #include <linux/gpio.h>
16b52207efSChen Feng #include <linux/io.h>
17b52207efSChen Feng #include <linux/interrupt.h>
18b52207efSChen Feng #include <linux/init.h>
19b52207efSChen Feng #include <linux/mfd/core.h>
20b52207efSChen Feng #include <linux/mfd/hi655x-pmic.h>
21b52207efSChen Feng #include <linux/module.h>
22b52207efSChen Feng #include <linux/of_gpio.h>
23b52207efSChen Feng #include <linux/of_platform.h>
24b52207efSChen Feng #include <linux/platform_device.h>
25b52207efSChen Feng #include <linux/regmap.h>
26b52207efSChen Feng 
27b52207efSChen Feng static const struct regmap_irq hi655x_irqs[] = {
289e3d5c99SJohn Stultz 	{ .reg_offset = 0, .mask = OTMP_D1R_INT_MASK },
299e3d5c99SJohn Stultz 	{ .reg_offset = 0, .mask = VSYS_2P5_R_INT_MASK },
309e3d5c99SJohn Stultz 	{ .reg_offset = 0, .mask = VSYS_UV_D3R_INT_MASK },
319e3d5c99SJohn Stultz 	{ .reg_offset = 0, .mask = VSYS_6P0_D200UR_INT_MASK },
329e3d5c99SJohn Stultz 	{ .reg_offset = 0, .mask = PWRON_D4SR_INT_MASK },
339e3d5c99SJohn Stultz 	{ .reg_offset = 0, .mask = PWRON_D20F_INT_MASK },
349e3d5c99SJohn Stultz 	{ .reg_offset = 0, .mask = PWRON_D20R_INT_MASK },
359e3d5c99SJohn Stultz 	{ .reg_offset = 0, .mask = RESERVE_INT_MASK },
36b52207efSChen Feng };
37b52207efSChen Feng 
38b52207efSChen Feng static const struct regmap_irq_chip hi655x_irq_chip = {
39b52207efSChen Feng 	.name = "hi655x-pmic",
40b52207efSChen Feng 	.irqs = hi655x_irqs,
41b52207efSChen Feng 	.num_regs = 1,
42b52207efSChen Feng 	.num_irqs = ARRAY_SIZE(hi655x_irqs),
43b52207efSChen Feng 	.status_base = HI655X_IRQ_STAT_BASE,
44417e06bbSJohn Stultz 	.ack_base = HI655X_IRQ_STAT_BASE,
45b52207efSChen Feng 	.mask_base = HI655X_IRQ_MASK_BASE,
46b52207efSChen Feng };
47b52207efSChen Feng 
48b52207efSChen Feng static struct regmap_config hi655x_regmap_config = {
49b52207efSChen Feng 	.reg_bits = 32,
50b52207efSChen Feng 	.reg_stride = HI655X_STRIDE,
51b52207efSChen Feng 	.val_bits = 8,
52b52207efSChen Feng 	.max_register = HI655X_BUS_ADDR(0xFFF),
53b52207efSChen Feng };
54b52207efSChen Feng 
55eb10245fSJohn Stultz static struct resource pwrkey_resources[] = {
56eb10245fSJohn Stultz 	{
57eb10245fSJohn Stultz 		.name	= "down",
58eb10245fSJohn Stultz 		.start	= PWRON_D20R_INT,
59eb10245fSJohn Stultz 		.end	= PWRON_D20R_INT,
60eb10245fSJohn Stultz 		.flags	= IORESOURCE_IRQ,
61eb10245fSJohn Stultz 	}, {
62eb10245fSJohn Stultz 		.name	= "up",
63eb10245fSJohn Stultz 		.start	= PWRON_D20F_INT,
64eb10245fSJohn Stultz 		.end	= PWRON_D20F_INT,
65eb10245fSJohn Stultz 		.flags	= IORESOURCE_IRQ,
66eb10245fSJohn Stultz 	}, {
67eb10245fSJohn Stultz 		.name	= "hold 4s",
68eb10245fSJohn Stultz 		.start	= PWRON_D4SR_INT,
69eb10245fSJohn Stultz 		.end	= PWRON_D4SR_INT,
70eb10245fSJohn Stultz 		.flags	= IORESOURCE_IRQ,
71eb10245fSJohn Stultz 	},
72eb10245fSJohn Stultz };
73eb10245fSJohn Stultz 
74eb10245fSJohn Stultz static const struct mfd_cell hi655x_pmic_devs[] = {
75eb10245fSJohn Stultz 	{
76eb10245fSJohn Stultz 		.name		= "hi65xx-powerkey",
77eb10245fSJohn Stultz 		.num_resources	= ARRAY_SIZE(pwrkey_resources),
78eb10245fSJohn Stultz 		.resources	= &pwrkey_resources[0],
79eb10245fSJohn Stultz 	},
80eb10245fSJohn Stultz 	{	.name		= "hi655x-regulator",	},
81ac03fec1SDaniel Lezcano 	{	.name		= "hi655x-clk",		},
82eb10245fSJohn Stultz };
83eb10245fSJohn Stultz 
84b52207efSChen Feng static void hi655x_local_irq_clear(struct regmap *map)
85b52207efSChen Feng {
86b52207efSChen Feng 	int i;
87b52207efSChen Feng 
88b52207efSChen Feng 	regmap_write(map, HI655X_ANA_IRQM_BASE, HI655X_IRQ_CLR);
89b52207efSChen Feng 	for (i = 0; i < HI655X_IRQ_ARRAY; i++) {
90b52207efSChen Feng 		regmap_write(map, HI655X_IRQ_STAT_BASE + i * HI655X_STRIDE,
91b52207efSChen Feng 			     HI655X_IRQ_CLR);
92b52207efSChen Feng 	}
93b52207efSChen Feng }
94b52207efSChen Feng 
95b52207efSChen Feng static int hi655x_pmic_probe(struct platform_device *pdev)
96b52207efSChen Feng {
97b52207efSChen Feng 	int ret;
98b52207efSChen Feng 	struct hi655x_pmic *pmic;
99b52207efSChen Feng 	struct device *dev = &pdev->dev;
100b52207efSChen Feng 	struct device_node *np = dev->of_node;
101b52207efSChen Feng 	void __iomem *base;
102b52207efSChen Feng 
103b52207efSChen Feng 	pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
104b52207efSChen Feng 	if (!pmic)
105b52207efSChen Feng 		return -ENOMEM;
106b52207efSChen Feng 	pmic->dev = dev;
107b52207efSChen Feng 
108b52207efSChen Feng 	pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
109b52207efSChen Feng 	base = devm_ioremap_resource(dev, pmic->res);
1107e28abdaSWei Yongjun 	if (IS_ERR(base))
1117e28abdaSWei Yongjun 		return PTR_ERR(base);
112b52207efSChen Feng 
113b52207efSChen Feng 	pmic->regmap = devm_regmap_init_mmio_clk(dev, NULL, base,
114b52207efSChen Feng 						 &hi655x_regmap_config);
115b52207efSChen Feng 
116b52207efSChen Feng 	regmap_read(pmic->regmap, HI655X_BUS_ADDR(HI655X_VER_REG), &pmic->ver);
117b52207efSChen Feng 	if ((pmic->ver < PMU_VER_START) || (pmic->ver > PMU_VER_END)) {
118b52207efSChen Feng 		dev_warn(dev, "PMU version %d unsupported\n", pmic->ver);
119b52207efSChen Feng 		return -EINVAL;
120b52207efSChen Feng 	}
121b52207efSChen Feng 
122b52207efSChen Feng 	hi655x_local_irq_clear(pmic->regmap);
123b52207efSChen Feng 
124b52207efSChen Feng 	pmic->gpio = of_get_named_gpio(np, "pmic-gpios", 0);
125b52207efSChen Feng 	if (!gpio_is_valid(pmic->gpio)) {
126b52207efSChen Feng 		dev_err(dev, "Failed to get the pmic-gpios\n");
127b52207efSChen Feng 		return -ENODEV;
128b52207efSChen Feng 	}
129b52207efSChen Feng 
130b52207efSChen Feng 	ret = devm_gpio_request_one(dev, pmic->gpio, GPIOF_IN,
131b52207efSChen Feng 				    "hi655x_pmic_irq");
132b52207efSChen Feng 	if (ret < 0) {
133b52207efSChen Feng 		dev_err(dev, "Failed to request gpio %d  ret = %d\n",
134b52207efSChen Feng 			pmic->gpio, ret);
135b52207efSChen Feng 		return ret;
136b52207efSChen Feng 	}
137b52207efSChen Feng 
138b52207efSChen Feng 	ret = regmap_add_irq_chip(pmic->regmap, gpio_to_irq(pmic->gpio),
139b52207efSChen Feng 				  IRQF_TRIGGER_LOW | IRQF_NO_SUSPEND, 0,
140b52207efSChen Feng 				  &hi655x_irq_chip, &pmic->irq_data);
141b52207efSChen Feng 	if (ret) {
142b52207efSChen Feng 		dev_err(dev, "Failed to obtain 'hi655x_pmic_irq' %d\n", ret);
143b52207efSChen Feng 		return ret;
144b52207efSChen Feng 	}
145b52207efSChen Feng 
146b52207efSChen Feng 	platform_set_drvdata(pdev, pmic);
147b52207efSChen Feng 
148b52207efSChen Feng 	ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO, hi655x_pmic_devs,
149eb10245fSJohn Stultz 			      ARRAY_SIZE(hi655x_pmic_devs), NULL, 0,
150eb10245fSJohn Stultz 			      regmap_irq_get_domain(pmic->irq_data));
151b52207efSChen Feng 	if (ret) {
152b52207efSChen Feng 		dev_err(dev, "Failed to register device %d\n", ret);
153b52207efSChen Feng 		regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data);
154b52207efSChen Feng 		return ret;
155b52207efSChen Feng 	}
156b52207efSChen Feng 
157b52207efSChen Feng 	return 0;
158b52207efSChen Feng }
159b52207efSChen Feng 
160b52207efSChen Feng static int hi655x_pmic_remove(struct platform_device *pdev)
161b52207efSChen Feng {
162b52207efSChen Feng 	struct hi655x_pmic *pmic = platform_get_drvdata(pdev);
163b52207efSChen Feng 
164b52207efSChen Feng 	regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data);
165b52207efSChen Feng 	mfd_remove_devices(&pdev->dev);
166b52207efSChen Feng 	return 0;
167b52207efSChen Feng }
168b52207efSChen Feng 
169b52207efSChen Feng static const struct of_device_id hi655x_pmic_match[] = {
170b52207efSChen Feng 	{ .compatible = "hisilicon,hi655x-pmic", },
171b52207efSChen Feng 	{},
172b52207efSChen Feng };
173e45b6c80SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, hi655x_pmic_match);
174b52207efSChen Feng 
175b52207efSChen Feng static struct platform_driver hi655x_pmic_driver = {
176b52207efSChen Feng 	.driver	= {
177b52207efSChen Feng 		.name =	"hi655x-pmic",
178b52207efSChen Feng 		.of_match_table = of_match_ptr(hi655x_pmic_match),
179b52207efSChen Feng 	},
180b52207efSChen Feng 	.probe  = hi655x_pmic_probe,
181b52207efSChen Feng 	.remove = hi655x_pmic_remove,
182b52207efSChen Feng };
183b52207efSChen Feng module_platform_driver(hi655x_pmic_driver);
184b52207efSChen Feng 
185b52207efSChen Feng MODULE_AUTHOR("Chen Feng <puck.chen@hisilicon.com>");
186b52207efSChen Feng MODULE_DESCRIPTION("Hisilicon hi655x PMIC driver");
187b52207efSChen Feng MODULE_LICENSE("GPL v2");
188