xref: /openbmc/linux/drivers/mfd/hi655x-pmic.c (revision 9e3d5c99)
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 mfd_cell hi655x_pmic_devs[] = {
28b52207efSChen Feng 	{ .name = "hi655x-regulator", },
29b52207efSChen Feng };
30b52207efSChen Feng 
31b52207efSChen Feng static const struct regmap_irq hi655x_irqs[] = {
329e3d5c99SJohn Stultz 	{ .reg_offset = 0, .mask = OTMP_D1R_INT_MASK },
339e3d5c99SJohn Stultz 	{ .reg_offset = 0, .mask = VSYS_2P5_R_INT_MASK },
349e3d5c99SJohn Stultz 	{ .reg_offset = 0, .mask = VSYS_UV_D3R_INT_MASK },
359e3d5c99SJohn Stultz 	{ .reg_offset = 0, .mask = VSYS_6P0_D200UR_INT_MASK },
369e3d5c99SJohn Stultz 	{ .reg_offset = 0, .mask = PWRON_D4SR_INT_MASK },
379e3d5c99SJohn Stultz 	{ .reg_offset = 0, .mask = PWRON_D20F_INT_MASK },
389e3d5c99SJohn Stultz 	{ .reg_offset = 0, .mask = PWRON_D20R_INT_MASK },
399e3d5c99SJohn Stultz 	{ .reg_offset = 0, .mask = RESERVE_INT_MASK },
40b52207efSChen Feng };
41b52207efSChen Feng 
42b52207efSChen Feng static const struct regmap_irq_chip hi655x_irq_chip = {
43b52207efSChen Feng 	.name = "hi655x-pmic",
44b52207efSChen Feng 	.irqs = hi655x_irqs,
45b52207efSChen Feng 	.num_regs = 1,
46b52207efSChen Feng 	.num_irqs = ARRAY_SIZE(hi655x_irqs),
47b52207efSChen Feng 	.status_base = HI655X_IRQ_STAT_BASE,
48417e06bbSJohn Stultz 	.ack_base = HI655X_IRQ_STAT_BASE,
49b52207efSChen Feng 	.mask_base = HI655X_IRQ_MASK_BASE,
50b52207efSChen Feng };
51b52207efSChen Feng 
52b52207efSChen Feng static struct regmap_config hi655x_regmap_config = {
53b52207efSChen Feng 	.reg_bits = 32,
54b52207efSChen Feng 	.reg_stride = HI655X_STRIDE,
55b52207efSChen Feng 	.val_bits = 8,
56b52207efSChen Feng 	.max_register = HI655X_BUS_ADDR(0xFFF),
57b52207efSChen Feng };
58b52207efSChen Feng 
59b52207efSChen Feng static void hi655x_local_irq_clear(struct regmap *map)
60b52207efSChen Feng {
61b52207efSChen Feng 	int i;
62b52207efSChen Feng 
63b52207efSChen Feng 	regmap_write(map, HI655X_ANA_IRQM_BASE, HI655X_IRQ_CLR);
64b52207efSChen Feng 	for (i = 0; i < HI655X_IRQ_ARRAY; i++) {
65b52207efSChen Feng 		regmap_write(map, HI655X_IRQ_STAT_BASE + i * HI655X_STRIDE,
66b52207efSChen Feng 			     HI655X_IRQ_CLR);
67b52207efSChen Feng 	}
68b52207efSChen Feng }
69b52207efSChen Feng 
70b52207efSChen Feng static int hi655x_pmic_probe(struct platform_device *pdev)
71b52207efSChen Feng {
72b52207efSChen Feng 	int ret;
73b52207efSChen Feng 	struct hi655x_pmic *pmic;
74b52207efSChen Feng 	struct device *dev = &pdev->dev;
75b52207efSChen Feng 	struct device_node *np = dev->of_node;
76b52207efSChen Feng 	void __iomem *base;
77b52207efSChen Feng 
78b52207efSChen Feng 	pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
79b52207efSChen Feng 	if (!pmic)
80b52207efSChen Feng 		return -ENOMEM;
81b52207efSChen Feng 	pmic->dev = dev;
82b52207efSChen Feng 
83b52207efSChen Feng 	pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
84b52207efSChen Feng 	if (!pmic->res)
85b52207efSChen Feng 		return -ENOENT;
86b52207efSChen Feng 
87b52207efSChen Feng 	base = devm_ioremap_resource(dev, pmic->res);
88b52207efSChen Feng 	if (!base)
89b52207efSChen Feng 		return -ENOMEM;
90b52207efSChen Feng 
91b52207efSChen Feng 	pmic->regmap = devm_regmap_init_mmio_clk(dev, NULL, base,
92b52207efSChen Feng 						 &hi655x_regmap_config);
93b52207efSChen Feng 
94b52207efSChen Feng 	regmap_read(pmic->regmap, HI655X_BUS_ADDR(HI655X_VER_REG), &pmic->ver);
95b52207efSChen Feng 	if ((pmic->ver < PMU_VER_START) || (pmic->ver > PMU_VER_END)) {
96b52207efSChen Feng 		dev_warn(dev, "PMU version %d unsupported\n", pmic->ver);
97b52207efSChen Feng 		return -EINVAL;
98b52207efSChen Feng 	}
99b52207efSChen Feng 
100b52207efSChen Feng 	hi655x_local_irq_clear(pmic->regmap);
101b52207efSChen Feng 
102b52207efSChen Feng 	pmic->gpio = of_get_named_gpio(np, "pmic-gpios", 0);
103b52207efSChen Feng 	if (!gpio_is_valid(pmic->gpio)) {
104b52207efSChen Feng 		dev_err(dev, "Failed to get the pmic-gpios\n");
105b52207efSChen Feng 		return -ENODEV;
106b52207efSChen Feng 	}
107b52207efSChen Feng 
108b52207efSChen Feng 	ret = devm_gpio_request_one(dev, pmic->gpio, GPIOF_IN,
109b52207efSChen Feng 				    "hi655x_pmic_irq");
110b52207efSChen Feng 	if (ret < 0) {
111b52207efSChen Feng 		dev_err(dev, "Failed to request gpio %d  ret = %d\n",
112b52207efSChen Feng 			pmic->gpio, ret);
113b52207efSChen Feng 		return ret;
114b52207efSChen Feng 	}
115b52207efSChen Feng 
116b52207efSChen Feng 	ret = regmap_add_irq_chip(pmic->regmap, gpio_to_irq(pmic->gpio),
117b52207efSChen Feng 				  IRQF_TRIGGER_LOW | IRQF_NO_SUSPEND, 0,
118b52207efSChen Feng 				  &hi655x_irq_chip, &pmic->irq_data);
119b52207efSChen Feng 	if (ret) {
120b52207efSChen Feng 		dev_err(dev, "Failed to obtain 'hi655x_pmic_irq' %d\n", ret);
121b52207efSChen Feng 		return ret;
122b52207efSChen Feng 	}
123b52207efSChen Feng 
124b52207efSChen Feng 	platform_set_drvdata(pdev, pmic);
125b52207efSChen Feng 
126b52207efSChen Feng 	ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO, hi655x_pmic_devs,
127b52207efSChen Feng 			      ARRAY_SIZE(hi655x_pmic_devs), NULL, 0, NULL);
128b52207efSChen Feng 	if (ret) {
129b52207efSChen Feng 		dev_err(dev, "Failed to register device %d\n", ret);
130b52207efSChen Feng 		regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data);
131b52207efSChen Feng 		return ret;
132b52207efSChen Feng 	}
133b52207efSChen Feng 
134b52207efSChen Feng 	return 0;
135b52207efSChen Feng }
136b52207efSChen Feng 
137b52207efSChen Feng static int hi655x_pmic_remove(struct platform_device *pdev)
138b52207efSChen Feng {
139b52207efSChen Feng 	struct hi655x_pmic *pmic = platform_get_drvdata(pdev);
140b52207efSChen Feng 
141b52207efSChen Feng 	regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data);
142b52207efSChen Feng 	mfd_remove_devices(&pdev->dev);
143b52207efSChen Feng 	return 0;
144b52207efSChen Feng }
145b52207efSChen Feng 
146b52207efSChen Feng static const struct of_device_id hi655x_pmic_match[] = {
147b52207efSChen Feng 	{ .compatible = "hisilicon,hi655x-pmic", },
148b52207efSChen Feng 	{},
149b52207efSChen Feng };
150b52207efSChen Feng 
151b52207efSChen Feng static struct platform_driver hi655x_pmic_driver = {
152b52207efSChen Feng 	.driver	= {
153b52207efSChen Feng 		.name =	"hi655x-pmic",
154b52207efSChen Feng 		.of_match_table = of_match_ptr(hi655x_pmic_match),
155b52207efSChen Feng 	},
156b52207efSChen Feng 	.probe  = hi655x_pmic_probe,
157b52207efSChen Feng 	.remove = hi655x_pmic_remove,
158b52207efSChen Feng };
159b52207efSChen Feng module_platform_driver(hi655x_pmic_driver);
160b52207efSChen Feng 
161b52207efSChen Feng MODULE_AUTHOR("Chen Feng <puck.chen@hisilicon.com>");
162b52207efSChen Feng MODULE_DESCRIPTION("Hisilicon hi655x PMIC driver");
163b52207efSChen Feng MODULE_LICENSE("GPL v2");
164