1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2021 ROHM Semiconductors |
4 | * |
5 | * ROHM BD9576MUF and BD9573MUF PMIC driver |
6 | */ |
7 | |
8 | #include <linux/i2c.h> |
9 | #include <linux/interrupt.h> |
10 | #include <linux/ioport.h> |
11 | #include <linux/irq.h> |
12 | #include <linux/mfd/core.h> |
13 | #include <linux/mfd/rohm-bd957x.h> |
14 | #include <linux/mfd/rohm-generic.h> |
15 | #include <linux/module.h> |
16 | #include <linux/of.h> |
17 | #include <linux/regmap.h> |
18 | #include <linux/types.h> |
19 | |
20 | enum { |
21 | BD957X_REGULATOR_CELL, |
22 | BD957X_WDT_CELL, |
23 | }; |
24 | |
25 | /* |
26 | * Due to the BD9576MUF nasty IRQ behaviour we don't always populate IRQs. |
27 | * These will be added to regulator resources only if IRQ information for the |
28 | * PMIC is populated in device-tree. |
29 | */ |
30 | static const struct resource bd9576_regulator_irqs[] = { |
31 | DEFINE_RES_IRQ_NAMED(BD9576_INT_THERM, "bd9576-temp" ), |
32 | DEFINE_RES_IRQ_NAMED(BD9576_INT_OVD, "bd9576-ovd" ), |
33 | DEFINE_RES_IRQ_NAMED(BD9576_INT_UVD, "bd9576-uvd" ), |
34 | }; |
35 | |
36 | static struct mfd_cell bd9573_mfd_cells[] = { |
37 | [BD957X_REGULATOR_CELL] = { .name = "bd9573-regulator" , }, |
38 | [BD957X_WDT_CELL] = { .name = "bd9576-wdt" , }, |
39 | }; |
40 | |
41 | static struct mfd_cell bd9576_mfd_cells[] = { |
42 | [BD957X_REGULATOR_CELL] = { .name = "bd9576-regulator" , }, |
43 | [BD957X_WDT_CELL] = { .name = "bd9576-wdt" , }, |
44 | }; |
45 | |
46 | static const struct regmap_range volatile_ranges[] = { |
47 | regmap_reg_range(BD957X_REG_SMRB_ASSERT, BD957X_REG_SMRB_ASSERT), |
48 | regmap_reg_range(BD957X_REG_PMIC_INTERNAL_STAT, |
49 | BD957X_REG_PMIC_INTERNAL_STAT), |
50 | regmap_reg_range(BD957X_REG_INT_THERM_STAT, BD957X_REG_INT_THERM_STAT), |
51 | regmap_reg_range(BD957X_REG_INT_OVP_STAT, BD957X_REG_INT_SYS_STAT), |
52 | regmap_reg_range(BD957X_REG_INT_MAIN_STAT, BD957X_REG_INT_MAIN_STAT), |
53 | }; |
54 | |
55 | static const struct regmap_access_table volatile_regs = { |
56 | .yes_ranges = &volatile_ranges[0], |
57 | .n_yes_ranges = ARRAY_SIZE(volatile_ranges), |
58 | }; |
59 | |
60 | static struct regmap_config bd957x_regmap = { |
61 | .reg_bits = 8, |
62 | .val_bits = 8, |
63 | .volatile_table = &volatile_regs, |
64 | .max_register = BD957X_MAX_REGISTER, |
65 | .cache_type = REGCACHE_MAPLE, |
66 | }; |
67 | |
68 | static struct regmap_irq bd9576_irqs[] = { |
69 | REGMAP_IRQ_REG(BD9576_INT_THERM, 0, BD957X_MASK_INT_MAIN_THERM), |
70 | REGMAP_IRQ_REG(BD9576_INT_OVP, 0, BD957X_MASK_INT_MAIN_OVP), |
71 | REGMAP_IRQ_REG(BD9576_INT_SCP, 0, BD957X_MASK_INT_MAIN_SCP), |
72 | REGMAP_IRQ_REG(BD9576_INT_OCP, 0, BD957X_MASK_INT_MAIN_OCP), |
73 | REGMAP_IRQ_REG(BD9576_INT_OVD, 0, BD957X_MASK_INT_MAIN_OVD), |
74 | REGMAP_IRQ_REG(BD9576_INT_UVD, 0, BD957X_MASK_INT_MAIN_UVD), |
75 | REGMAP_IRQ_REG(BD9576_INT_UVP, 0, BD957X_MASK_INT_MAIN_UVP), |
76 | REGMAP_IRQ_REG(BD9576_INT_SYS, 0, BD957X_MASK_INT_MAIN_SYS), |
77 | }; |
78 | |
79 | static struct regmap_irq_chip bd9576_irq_chip = { |
80 | .name = "bd9576_irq" , |
81 | .irqs = &bd9576_irqs[0], |
82 | .num_irqs = ARRAY_SIZE(bd9576_irqs), |
83 | .status_base = BD957X_REG_INT_MAIN_STAT, |
84 | .mask_base = BD957X_REG_INT_MAIN_MASK, |
85 | .ack_base = BD957X_REG_INT_MAIN_STAT, |
86 | .init_ack_masked = true, |
87 | .num_regs = 1, |
88 | .irq_reg_stride = 1, |
89 | }; |
90 | |
91 | static int bd957x_i2c_probe(struct i2c_client *i2c) |
92 | { |
93 | int ret; |
94 | struct regmap *regmap; |
95 | struct mfd_cell *cells; |
96 | int num_cells; |
97 | unsigned long chip_type; |
98 | struct irq_domain *domain; |
99 | bool usable_irqs; |
100 | |
101 | chip_type = (unsigned long)of_device_get_match_data(dev: &i2c->dev); |
102 | |
103 | switch (chip_type) { |
104 | case ROHM_CHIP_TYPE_BD9576: |
105 | cells = bd9576_mfd_cells; |
106 | num_cells = ARRAY_SIZE(bd9576_mfd_cells); |
107 | usable_irqs = !!i2c->irq; |
108 | break; |
109 | case ROHM_CHIP_TYPE_BD9573: |
110 | cells = bd9573_mfd_cells; |
111 | num_cells = ARRAY_SIZE(bd9573_mfd_cells); |
112 | /* |
113 | * BD9573 only supports fatal IRQs which we can not handle |
114 | * because SoC is going to lose the power. |
115 | */ |
116 | usable_irqs = false; |
117 | break; |
118 | default: |
119 | dev_err(&i2c->dev, "Unknown device type" ); |
120 | return -EINVAL; |
121 | } |
122 | |
123 | regmap = devm_regmap_init_i2c(i2c, &bd957x_regmap); |
124 | if (IS_ERR(ptr: regmap)) |
125 | return dev_err_probe(dev: &i2c->dev, err: PTR_ERR(ptr: regmap), |
126 | fmt: "Failed to initialize Regmap\n" ); |
127 | |
128 | /* |
129 | * BD9576 behaves badly. It kepts IRQ line asserted for the whole |
130 | * duration of detected HW condition (like over temperature). So we |
131 | * don't require IRQ to be populated. |
132 | * If IRQ information is not given, then we mask all IRQs and do not |
133 | * provide IRQ resources to regulator driver - which then just omits |
134 | * the notifiers. |
135 | */ |
136 | if (usable_irqs) { |
137 | struct regmap_irq_chip_data *irq_data; |
138 | struct mfd_cell *regulators; |
139 | |
140 | regulators = &bd9576_mfd_cells[BD957X_REGULATOR_CELL]; |
141 | regulators->resources = bd9576_regulator_irqs; |
142 | regulators->num_resources = ARRAY_SIZE(bd9576_regulator_irqs); |
143 | |
144 | ret = devm_regmap_add_irq_chip(dev: &i2c->dev, map: regmap, irq: i2c->irq, |
145 | IRQF_ONESHOT, irq_base: 0, |
146 | chip: &bd9576_irq_chip, data: &irq_data); |
147 | if (ret) |
148 | return dev_err_probe(dev: &i2c->dev, err: ret, |
149 | fmt: "Failed to add IRQ chip\n" ); |
150 | |
151 | domain = regmap_irq_get_domain(data: irq_data); |
152 | } else { |
153 | ret = regmap_update_bits(map: regmap, BD957X_REG_INT_MAIN_MASK, |
154 | BD957X_MASK_INT_ALL, |
155 | BD957X_MASK_INT_ALL); |
156 | if (ret) |
157 | return ret; |
158 | domain = NULL; |
159 | } |
160 | |
161 | ret = devm_mfd_add_devices(dev: &i2c->dev, PLATFORM_DEVID_AUTO, cells, |
162 | n_devs: num_cells, NULL, irq_base: 0, irq_domain: domain); |
163 | if (ret) |
164 | dev_err_probe(dev: &i2c->dev, err: ret, fmt: "Failed to create subdevices\n" ); |
165 | |
166 | return ret; |
167 | } |
168 | |
169 | static const struct of_device_id bd957x_of_match[] = { |
170 | { .compatible = "rohm,bd9576" , .data = (void *)ROHM_CHIP_TYPE_BD9576, }, |
171 | { .compatible = "rohm,bd9573" , .data = (void *)ROHM_CHIP_TYPE_BD9573, }, |
172 | { }, |
173 | }; |
174 | MODULE_DEVICE_TABLE(of, bd957x_of_match); |
175 | |
176 | static struct i2c_driver bd957x_drv = { |
177 | .driver = { |
178 | .name = "rohm-bd957x" , |
179 | .of_match_table = bd957x_of_match, |
180 | }, |
181 | .probe = bd957x_i2c_probe, |
182 | }; |
183 | module_i2c_driver(bd957x_drv); |
184 | |
185 | MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>" ); |
186 | MODULE_DESCRIPTION("ROHM BD9576MUF and BD9573MUF Power Management IC driver" ); |
187 | MODULE_LICENSE("GPL" ); |
188 | |