xref: /openbmc/linux/drivers/mfd/max77714.c (revision 9816d859)
160b050ffSLuca Ceresoli // SPDX-License-Identifier: GPL-2.0-only
260b050ffSLuca Ceresoli /*
360b050ffSLuca Ceresoli  * Maxim MAX77714 Core Driver
460b050ffSLuca Ceresoli  *
560b050ffSLuca Ceresoli  * Copyright (C) 2022 Luca Ceresoli
662fa5c98SLuca Ceresoli  * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
760b050ffSLuca Ceresoli  */
860b050ffSLuca Ceresoli 
960b050ffSLuca Ceresoli #include <linux/i2c.h>
1060b050ffSLuca Ceresoli #include <linux/interrupt.h>
1160b050ffSLuca Ceresoli #include <linux/mfd/core.h>
1260b050ffSLuca Ceresoli #include <linux/mfd/max77714.h>
1360b050ffSLuca Ceresoli #include <linux/module.h>
1460b050ffSLuca Ceresoli #include <linux/of.h>
1560b050ffSLuca Ceresoli #include <linux/regmap.h>
1660b050ffSLuca Ceresoli 
1760b050ffSLuca Ceresoli static const struct mfd_cell max77714_cells[] = {
1860b050ffSLuca Ceresoli 	{ .name = "max77714-watchdog" },
1960b050ffSLuca Ceresoli 	{ .name = "max77714-rtc" },
2060b050ffSLuca Ceresoli };
2160b050ffSLuca Ceresoli 
2260b050ffSLuca Ceresoli static const struct regmap_range max77714_readable_ranges[] = {
2360b050ffSLuca Ceresoli 	regmap_reg_range(MAX77714_INT_TOP,     MAX77714_INT_TOP),
2460b050ffSLuca Ceresoli 	regmap_reg_range(MAX77714_INT_TOPM,    MAX77714_INT_TOPM),
2560b050ffSLuca Ceresoli 	regmap_reg_range(MAX77714_32K_STATUS,  MAX77714_32K_CONFIG),
2660b050ffSLuca Ceresoli 	regmap_reg_range(MAX77714_CNFG_GLBL2,  MAX77714_CNFG2_ONOFF),
2760b050ffSLuca Ceresoli };
2860b050ffSLuca Ceresoli 
2960b050ffSLuca Ceresoli static const struct regmap_range max77714_writable_ranges[] = {
3060b050ffSLuca Ceresoli 	regmap_reg_range(MAX77714_INT_TOPM,    MAX77714_INT_TOPM),
3160b050ffSLuca Ceresoli 	regmap_reg_range(MAX77714_32K_CONFIG,  MAX77714_32K_CONFIG),
3260b050ffSLuca Ceresoli 	regmap_reg_range(MAX77714_CNFG_GLBL2,  MAX77714_CNFG2_ONOFF),
3360b050ffSLuca Ceresoli };
3460b050ffSLuca Ceresoli 
3560b050ffSLuca Ceresoli static const struct regmap_access_table max77714_readable_table = {
3660b050ffSLuca Ceresoli 	.yes_ranges = max77714_readable_ranges,
3760b050ffSLuca Ceresoli 	.n_yes_ranges = ARRAY_SIZE(max77714_readable_ranges),
3860b050ffSLuca Ceresoli };
3960b050ffSLuca Ceresoli 
4060b050ffSLuca Ceresoli static const struct regmap_access_table max77714_writable_table = {
4160b050ffSLuca Ceresoli 	.yes_ranges = max77714_writable_ranges,
4260b050ffSLuca Ceresoli 	.n_yes_ranges = ARRAY_SIZE(max77714_writable_ranges),
4360b050ffSLuca Ceresoli };
4460b050ffSLuca Ceresoli 
4560b050ffSLuca Ceresoli static const struct regmap_config max77714_regmap_config = {
4660b050ffSLuca Ceresoli 	.reg_bits = 8,
4760b050ffSLuca Ceresoli 	.val_bits = 8,
4860b050ffSLuca Ceresoli 	.max_register = MAX77714_CNFG2_ONOFF,
4960b050ffSLuca Ceresoli 	.rd_table = &max77714_readable_table,
5060b050ffSLuca Ceresoli 	.wr_table = &max77714_writable_table,
5160b050ffSLuca Ceresoli };
5260b050ffSLuca Ceresoli 
5360b050ffSLuca Ceresoli static const struct regmap_irq max77714_top_irqs[] = {
5460b050ffSLuca Ceresoli 	REGMAP_IRQ_REG(MAX77714_IRQ_TOP_ONOFF,   0, MAX77714_INT_TOP_ONOFF),
5560b050ffSLuca Ceresoli 	REGMAP_IRQ_REG(MAX77714_IRQ_TOP_RTC,     0, MAX77714_INT_TOP_RTC),
5660b050ffSLuca Ceresoli 	REGMAP_IRQ_REG(MAX77714_IRQ_TOP_GPIO,    0, MAX77714_INT_TOP_GPIO),
5760b050ffSLuca Ceresoli 	REGMAP_IRQ_REG(MAX77714_IRQ_TOP_LDO,     0, MAX77714_INT_TOP_LDO),
5860b050ffSLuca Ceresoli 	REGMAP_IRQ_REG(MAX77714_IRQ_TOP_SD,      0, MAX77714_INT_TOP_SD),
5960b050ffSLuca Ceresoli 	REGMAP_IRQ_REG(MAX77714_IRQ_TOP_GLBL,    0, MAX77714_INT_TOP_GLBL),
6060b050ffSLuca Ceresoli };
6160b050ffSLuca Ceresoli 
6260b050ffSLuca Ceresoli static const struct regmap_irq_chip max77714_irq_chip = {
6360b050ffSLuca Ceresoli 	.name			= "max77714-pmic",
6460b050ffSLuca Ceresoli 	.status_base		= MAX77714_INT_TOP,
6560b050ffSLuca Ceresoli 	.mask_base		= MAX77714_INT_TOPM,
6660b050ffSLuca Ceresoli 	.num_regs		= 1,
6760b050ffSLuca Ceresoli 	.irqs			= max77714_top_irqs,
6860b050ffSLuca Ceresoli 	.num_irqs		= ARRAY_SIZE(max77714_top_irqs),
6960b050ffSLuca Ceresoli };
7060b050ffSLuca Ceresoli 
7160b050ffSLuca Ceresoli /*
7260b050ffSLuca Ceresoli  * MAX77714 initially uses the internal, low precision oscillator. Enable
7360b050ffSLuca Ceresoli  * the external oscillator by setting the XOSC_RETRY bit. If the external
7460b050ffSLuca Ceresoli  * oscillator is not OK (probably not installed) this has no effect.
7560b050ffSLuca Ceresoli  */
max77714_setup_xosc(struct device * dev,struct regmap * regmap)7660b050ffSLuca Ceresoli static int max77714_setup_xosc(struct device *dev, struct regmap *regmap)
7760b050ffSLuca Ceresoli {
7860b050ffSLuca Ceresoli 	/* Internal Crystal Load Capacitance, indexed by value of 32KLOAD bits */
7960b050ffSLuca Ceresoli 	static const unsigned int load_cap[4] = {0, 10, 12, 22}; /* pF */
8060b050ffSLuca Ceresoli 	unsigned int load_cap_idx;
8160b050ffSLuca Ceresoli 	unsigned int status;
8260b050ffSLuca Ceresoli 	int err;
8360b050ffSLuca Ceresoli 
8460b050ffSLuca Ceresoli 	err = regmap_update_bits(regmap, MAX77714_32K_CONFIG,
8560b050ffSLuca Ceresoli 				 MAX77714_32K_CONFIG_XOSC_RETRY,
8660b050ffSLuca Ceresoli 				 MAX77714_32K_CONFIG_XOSC_RETRY);
8760b050ffSLuca Ceresoli 	if (err)
8860b050ffSLuca Ceresoli 		return dev_err_probe(dev, err, "Failed to configure the external oscillator\n");
8960b050ffSLuca Ceresoli 
9060b050ffSLuca Ceresoli 	err = regmap_read(regmap, MAX77714_32K_STATUS, &status);
9160b050ffSLuca Ceresoli 	if (err)
9260b050ffSLuca Ceresoli 		return dev_err_probe(dev, err, "Failed to read external oscillator status\n");
9360b050ffSLuca Ceresoli 
9460b050ffSLuca Ceresoli 	load_cap_idx = (status >> MAX77714_32K_STATUS_32KLOAD_SHF)
9560b050ffSLuca Ceresoli 		& MAX77714_32K_STATUS_32KLOAD_MSK;
9660b050ffSLuca Ceresoli 
9760b050ffSLuca Ceresoli 	dev_info(dev, "Using %s oscillator, %d pF load cap\n",
9860b050ffSLuca Ceresoli 		 status & MAX77714_32K_STATUS_32KSOURCE ? "internal" : "external",
9960b050ffSLuca Ceresoli 		 load_cap[load_cap_idx]);
10060b050ffSLuca Ceresoli 
10160b050ffSLuca Ceresoli 	return 0;
10260b050ffSLuca Ceresoli }
10360b050ffSLuca Ceresoli 
max77714_probe(struct i2c_client * client)10460b050ffSLuca Ceresoli static int max77714_probe(struct i2c_client *client)
10560b050ffSLuca Ceresoli {
10660b050ffSLuca Ceresoli 	struct device *dev = &client->dev;
10760b050ffSLuca Ceresoli 	struct regmap *regmap;
10860b050ffSLuca Ceresoli 	struct regmap_irq_chip_data *irq_data;
10960b050ffSLuca Ceresoli 	int err;
11060b050ffSLuca Ceresoli 
11160b050ffSLuca Ceresoli 	regmap = devm_regmap_init_i2c(client, &max77714_regmap_config);
11260b050ffSLuca Ceresoli 	if (IS_ERR(regmap))
11360b050ffSLuca Ceresoli 		return dev_err_probe(dev, PTR_ERR(regmap),
11460b050ffSLuca Ceresoli 				     "Failed to initialise regmap\n");
11560b050ffSLuca Ceresoli 
11660b050ffSLuca Ceresoli 	err = max77714_setup_xosc(dev, regmap);
11760b050ffSLuca Ceresoli 	if (err)
11860b050ffSLuca Ceresoli 		return err;
11960b050ffSLuca Ceresoli 
12060b050ffSLuca Ceresoli 	err = devm_regmap_add_irq_chip(dev, regmap, client->irq,
12160b050ffSLuca Ceresoli 				       IRQF_ONESHOT | IRQF_SHARED, 0,
12260b050ffSLuca Ceresoli 				       &max77714_irq_chip, &irq_data);
12360b050ffSLuca Ceresoli 	if (err)
12460b050ffSLuca Ceresoli 		return dev_err_probe(dev, err, "Failed to add PMIC IRQ chip\n");
12560b050ffSLuca Ceresoli 
12660b050ffSLuca Ceresoli 	err =  devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
12760b050ffSLuca Ceresoli 				    max77714_cells, ARRAY_SIZE(max77714_cells),
12860b050ffSLuca Ceresoli 				    NULL, 0, NULL);
12960b050ffSLuca Ceresoli 	if (err)
13060b050ffSLuca Ceresoli 		return dev_err_probe(dev, err, "Failed to register child devices\n");
13160b050ffSLuca Ceresoli 
13260b050ffSLuca Ceresoli 	return 0;
13360b050ffSLuca Ceresoli }
13460b050ffSLuca Ceresoli 
13560b050ffSLuca Ceresoli static const struct of_device_id max77714_dt_match[] = {
13660b050ffSLuca Ceresoli 	{ .compatible = "maxim,max77714" },
13760b050ffSLuca Ceresoli 	{},
13860b050ffSLuca Ceresoli };
13960b050ffSLuca Ceresoli MODULE_DEVICE_TABLE(of, max77714_dt_match);
14060b050ffSLuca Ceresoli 
14160b050ffSLuca Ceresoli static struct i2c_driver max77714_driver = {
14260b050ffSLuca Ceresoli 	.driver = {
14360b050ffSLuca Ceresoli 		.name = "max77714",
14460b050ffSLuca Ceresoli 		.of_match_table = max77714_dt_match,
14560b050ffSLuca Ceresoli 	},
146*9816d859SUwe Kleine-König 	.probe = max77714_probe,
14760b050ffSLuca Ceresoli };
14860b050ffSLuca Ceresoli module_i2c_driver(max77714_driver);
14960b050ffSLuca Ceresoli 
15060b050ffSLuca Ceresoli MODULE_DESCRIPTION("Maxim MAX77714 MFD core driver");
15162fa5c98SLuca Ceresoli MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
15260b050ffSLuca Ceresoli MODULE_LICENSE("GPL");
153