Commit | Line | Data |
---|---|---|
b52207ef CF |
1 | /* |
2 | * Device driver for MFD hi655x PMIC | |
3 | * | |
4 | * Copyright (c) 2016 Hisilicon. | |
5 | * | |
6 | * Authors: | |
7 | * Chen Feng <puck.chen@hisilicon.com> | |
8 | * Fei Wang <w.f@huawei.com> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | */ | |
14 | ||
15 | #include <linux/gpio.h> | |
16 | #include <linux/io.h> | |
17 | #include <linux/interrupt.h> | |
18 | #include <linux/init.h> | |
19 | #include <linux/mfd/core.h> | |
20 | #include <linux/mfd/hi655x-pmic.h> | |
21 | #include <linux/module.h> | |
22 | #include <linux/of_gpio.h> | |
23 | #include <linux/of_platform.h> | |
24 | #include <linux/platform_device.h> | |
25 | #include <linux/regmap.h> | |
26 | ||
27 | static const struct mfd_cell hi655x_pmic_devs[] = { | |
28 | { .name = "hi655x-regulator", }, | |
29 | }; | |
30 | ||
31 | static const struct regmap_irq hi655x_irqs[] = { | |
32 | { .reg_offset = 0, .mask = OTMP_D1R_INT }, | |
33 | { .reg_offset = 0, .mask = VSYS_2P5_R_INT }, | |
34 | { .reg_offset = 0, .mask = VSYS_UV_D3R_INT }, | |
35 | { .reg_offset = 0, .mask = VSYS_6P0_D200UR_INT }, | |
36 | { .reg_offset = 0, .mask = PWRON_D4SR_INT }, | |
37 | { .reg_offset = 0, .mask = PWRON_D20F_INT }, | |
38 | { .reg_offset = 0, .mask = PWRON_D20R_INT }, | |
39 | { .reg_offset = 0, .mask = RESERVE_INT }, | |
40 | }; | |
41 | ||
42 | static const struct regmap_irq_chip hi655x_irq_chip = { | |
43 | .name = "hi655x-pmic", | |
44 | .irqs = hi655x_irqs, | |
45 | .num_regs = 1, | |
46 | .num_irqs = ARRAY_SIZE(hi655x_irqs), | |
47 | .status_base = HI655X_IRQ_STAT_BASE, | |
48 | .mask_base = HI655X_IRQ_MASK_BASE, | |
49 | }; | |
50 | ||
51 | static struct regmap_config hi655x_regmap_config = { | |
52 | .reg_bits = 32, | |
53 | .reg_stride = HI655X_STRIDE, | |
54 | .val_bits = 8, | |
55 | .max_register = HI655X_BUS_ADDR(0xFFF), | |
56 | }; | |
57 | ||
58 | static void hi655x_local_irq_clear(struct regmap *map) | |
59 | { | |
60 | int i; | |
61 | ||
62 | regmap_write(map, HI655X_ANA_IRQM_BASE, HI655X_IRQ_CLR); | |
63 | for (i = 0; i < HI655X_IRQ_ARRAY; i++) { | |
64 | regmap_write(map, HI655X_IRQ_STAT_BASE + i * HI655X_STRIDE, | |
65 | HI655X_IRQ_CLR); | |
66 | } | |
67 | } | |
68 | ||
69 | static int hi655x_pmic_probe(struct platform_device *pdev) | |
70 | { | |
71 | int ret; | |
72 | struct hi655x_pmic *pmic; | |
73 | struct device *dev = &pdev->dev; | |
74 | struct device_node *np = dev->of_node; | |
75 | void __iomem *base; | |
76 | ||
77 | pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); | |
78 | if (!pmic) | |
79 | return -ENOMEM; | |
80 | pmic->dev = dev; | |
81 | ||
82 | pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
83 | if (!pmic->res) | |
84 | return -ENOENT; | |
85 | ||
86 | base = devm_ioremap_resource(dev, pmic->res); | |
87 | if (!base) | |
88 | return -ENOMEM; | |
89 | ||
90 | pmic->regmap = devm_regmap_init_mmio_clk(dev, NULL, base, | |
91 | &hi655x_regmap_config); | |
92 | ||
93 | regmap_read(pmic->regmap, HI655X_BUS_ADDR(HI655X_VER_REG), &pmic->ver); | |
94 | if ((pmic->ver < PMU_VER_START) || (pmic->ver > PMU_VER_END)) { | |
95 | dev_warn(dev, "PMU version %d unsupported\n", pmic->ver); | |
96 | return -EINVAL; | |
97 | } | |
98 | ||
99 | hi655x_local_irq_clear(pmic->regmap); | |
100 | ||
101 | pmic->gpio = of_get_named_gpio(np, "pmic-gpios", 0); | |
102 | if (!gpio_is_valid(pmic->gpio)) { | |
103 | dev_err(dev, "Failed to get the pmic-gpios\n"); | |
104 | return -ENODEV; | |
105 | } | |
106 | ||
107 | ret = devm_gpio_request_one(dev, pmic->gpio, GPIOF_IN, | |
108 | "hi655x_pmic_irq"); | |
109 | if (ret < 0) { | |
110 | dev_err(dev, "Failed to request gpio %d ret = %d\n", | |
111 | pmic->gpio, ret); | |
112 | return ret; | |
113 | } | |
114 | ||
115 | ret = regmap_add_irq_chip(pmic->regmap, gpio_to_irq(pmic->gpio), | |
116 | IRQF_TRIGGER_LOW | IRQF_NO_SUSPEND, 0, | |
117 | &hi655x_irq_chip, &pmic->irq_data); | |
118 | if (ret) { | |
119 | dev_err(dev, "Failed to obtain 'hi655x_pmic_irq' %d\n", ret); | |
120 | return ret; | |
121 | } | |
122 | ||
123 | platform_set_drvdata(pdev, pmic); | |
124 | ||
125 | ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO, hi655x_pmic_devs, | |
126 | ARRAY_SIZE(hi655x_pmic_devs), NULL, 0, NULL); | |
127 | if (ret) { | |
128 | dev_err(dev, "Failed to register device %d\n", ret); | |
129 | regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data); | |
130 | return ret; | |
131 | } | |
132 | ||
133 | return 0; | |
134 | } | |
135 | ||
136 | static int hi655x_pmic_remove(struct platform_device *pdev) | |
137 | { | |
138 | struct hi655x_pmic *pmic = platform_get_drvdata(pdev); | |
139 | ||
140 | regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data); | |
141 | mfd_remove_devices(&pdev->dev); | |
142 | return 0; | |
143 | } | |
144 | ||
145 | static const struct of_device_id hi655x_pmic_match[] = { | |
146 | { .compatible = "hisilicon,hi655x-pmic", }, | |
147 | {}, | |
148 | }; | |
149 | ||
150 | static struct platform_driver hi655x_pmic_driver = { | |
151 | .driver = { | |
152 | .name = "hi655x-pmic", | |
153 | .of_match_table = of_match_ptr(hi655x_pmic_match), | |
154 | }, | |
155 | .probe = hi655x_pmic_probe, | |
156 | .remove = hi655x_pmic_remove, | |
157 | }; | |
158 | module_platform_driver(hi655x_pmic_driver); | |
159 | ||
160 | MODULE_AUTHOR("Chen Feng <puck.chen@hisilicon.com>"); | |
161 | MODULE_DESCRIPTION("Hisilicon hi655x PMIC driver"); | |
162 | MODULE_LICENSE("GPL v2"); |