1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Maxim MAX77714 Core Driver |
4 | * |
5 | * Copyright (C) 2022 Luca Ceresoli |
6 | * Author: Luca Ceresoli <luca.ceresoli@bootlin.com> |
7 | */ |
8 | |
9 | #include <linux/i2c.h> |
10 | #include <linux/interrupt.h> |
11 | #include <linux/mfd/core.h> |
12 | #include <linux/mfd/max77714.h> |
13 | #include <linux/module.h> |
14 | #include <linux/of.h> |
15 | #include <linux/regmap.h> |
16 | |
17 | static const struct mfd_cell max77714_cells[] = { |
18 | { .name = "max77714-watchdog" }, |
19 | { .name = "max77714-rtc" }, |
20 | }; |
21 | |
22 | static const struct regmap_range max77714_readable_ranges[] = { |
23 | regmap_reg_range(MAX77714_INT_TOP, MAX77714_INT_TOP), |
24 | regmap_reg_range(MAX77714_INT_TOPM, MAX77714_INT_TOPM), |
25 | regmap_reg_range(MAX77714_32K_STATUS, MAX77714_32K_CONFIG), |
26 | regmap_reg_range(MAX77714_CNFG_GLBL2, MAX77714_CNFG2_ONOFF), |
27 | }; |
28 | |
29 | static const struct regmap_range max77714_writable_ranges[] = { |
30 | regmap_reg_range(MAX77714_INT_TOPM, MAX77714_INT_TOPM), |
31 | regmap_reg_range(MAX77714_32K_CONFIG, MAX77714_32K_CONFIG), |
32 | regmap_reg_range(MAX77714_CNFG_GLBL2, MAX77714_CNFG2_ONOFF), |
33 | }; |
34 | |
35 | static const struct regmap_access_table max77714_readable_table = { |
36 | .yes_ranges = max77714_readable_ranges, |
37 | .n_yes_ranges = ARRAY_SIZE(max77714_readable_ranges), |
38 | }; |
39 | |
40 | static const struct regmap_access_table max77714_writable_table = { |
41 | .yes_ranges = max77714_writable_ranges, |
42 | .n_yes_ranges = ARRAY_SIZE(max77714_writable_ranges), |
43 | }; |
44 | |
45 | static const struct regmap_config max77714_regmap_config = { |
46 | .reg_bits = 8, |
47 | .val_bits = 8, |
48 | .max_register = MAX77714_CNFG2_ONOFF, |
49 | .rd_table = &max77714_readable_table, |
50 | .wr_table = &max77714_writable_table, |
51 | }; |
52 | |
53 | static const struct regmap_irq max77714_top_irqs[] = { |
54 | REGMAP_IRQ_REG(MAX77714_IRQ_TOP_ONOFF, 0, MAX77714_INT_TOP_ONOFF), |
55 | REGMAP_IRQ_REG(MAX77714_IRQ_TOP_RTC, 0, MAX77714_INT_TOP_RTC), |
56 | REGMAP_IRQ_REG(MAX77714_IRQ_TOP_GPIO, 0, MAX77714_INT_TOP_GPIO), |
57 | REGMAP_IRQ_REG(MAX77714_IRQ_TOP_LDO, 0, MAX77714_INT_TOP_LDO), |
58 | REGMAP_IRQ_REG(MAX77714_IRQ_TOP_SD, 0, MAX77714_INT_TOP_SD), |
59 | REGMAP_IRQ_REG(MAX77714_IRQ_TOP_GLBL, 0, MAX77714_INT_TOP_GLBL), |
60 | }; |
61 | |
62 | static const struct regmap_irq_chip max77714_irq_chip = { |
63 | .name = "max77714-pmic" , |
64 | .status_base = MAX77714_INT_TOP, |
65 | .mask_base = MAX77714_INT_TOPM, |
66 | .num_regs = 1, |
67 | .irqs = max77714_top_irqs, |
68 | .num_irqs = ARRAY_SIZE(max77714_top_irqs), |
69 | }; |
70 | |
71 | /* |
72 | * MAX77714 initially uses the internal, low precision oscillator. Enable |
73 | * the external oscillator by setting the XOSC_RETRY bit. If the external |
74 | * oscillator is not OK (probably not installed) this has no effect. |
75 | */ |
76 | static int max77714_setup_xosc(struct device *dev, struct regmap *regmap) |
77 | { |
78 | /* Internal Crystal Load Capacitance, indexed by value of 32KLOAD bits */ |
79 | static const unsigned int load_cap[4] = {0, 10, 12, 22}; /* pF */ |
80 | unsigned int load_cap_idx; |
81 | unsigned int status; |
82 | int err; |
83 | |
84 | err = regmap_update_bits(map: regmap, MAX77714_32K_CONFIG, |
85 | MAX77714_32K_CONFIG_XOSC_RETRY, |
86 | MAX77714_32K_CONFIG_XOSC_RETRY); |
87 | if (err) |
88 | return dev_err_probe(dev, err, fmt: "Failed to configure the external oscillator\n" ); |
89 | |
90 | err = regmap_read(map: regmap, MAX77714_32K_STATUS, val: &status); |
91 | if (err) |
92 | return dev_err_probe(dev, err, fmt: "Failed to read external oscillator status\n" ); |
93 | |
94 | load_cap_idx = (status >> MAX77714_32K_STATUS_32KLOAD_SHF) |
95 | & MAX77714_32K_STATUS_32KLOAD_MSK; |
96 | |
97 | dev_info(dev, "Using %s oscillator, %d pF load cap\n" , |
98 | status & MAX77714_32K_STATUS_32KSOURCE ? "internal" : "external" , |
99 | load_cap[load_cap_idx]); |
100 | |
101 | return 0; |
102 | } |
103 | |
104 | static int max77714_probe(struct i2c_client *client) |
105 | { |
106 | struct device *dev = &client->dev; |
107 | struct regmap *regmap; |
108 | struct regmap_irq_chip_data *irq_data; |
109 | int err; |
110 | |
111 | regmap = devm_regmap_init_i2c(client, &max77714_regmap_config); |
112 | if (IS_ERR(ptr: regmap)) |
113 | return dev_err_probe(dev, err: PTR_ERR(ptr: regmap), |
114 | fmt: "Failed to initialise regmap\n" ); |
115 | |
116 | err = max77714_setup_xosc(dev, regmap); |
117 | if (err) |
118 | return err; |
119 | |
120 | err = devm_regmap_add_irq_chip(dev, map: regmap, irq: client->irq, |
121 | IRQF_ONESHOT | IRQF_SHARED, irq_base: 0, |
122 | chip: &max77714_irq_chip, data: &irq_data); |
123 | if (err) |
124 | return dev_err_probe(dev, err, fmt: "Failed to add PMIC IRQ chip\n" ); |
125 | |
126 | err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, |
127 | cells: max77714_cells, ARRAY_SIZE(max77714_cells), |
128 | NULL, irq_base: 0, NULL); |
129 | if (err) |
130 | return dev_err_probe(dev, err, fmt: "Failed to register child devices\n" ); |
131 | |
132 | return 0; |
133 | } |
134 | |
135 | static const struct of_device_id max77714_dt_match[] = { |
136 | { .compatible = "maxim,max77714" }, |
137 | {}, |
138 | }; |
139 | MODULE_DEVICE_TABLE(of, max77714_dt_match); |
140 | |
141 | static struct i2c_driver max77714_driver = { |
142 | .driver = { |
143 | .name = "max77714" , |
144 | .of_match_table = max77714_dt_match, |
145 | }, |
146 | .probe = max77714_probe, |
147 | }; |
148 | module_i2c_driver(max77714_driver); |
149 | |
150 | MODULE_DESCRIPTION("Maxim MAX77714 MFD core driver" ); |
151 | MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>" ); |
152 | MODULE_LICENSE("GPL" ); |
153 | |