162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Maxim MAX77714 Core Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2022 Luca Ceresoli 662306a36Sopenharmony_ci * Author: Luca Ceresoli <luca.ceresoli@bootlin.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/i2c.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/mfd/core.h> 1262306a36Sopenharmony_ci#include <linux/mfd/max77714.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/regmap.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic const struct mfd_cell max77714_cells[] = { 1862306a36Sopenharmony_ci { .name = "max77714-watchdog" }, 1962306a36Sopenharmony_ci { .name = "max77714-rtc" }, 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic const struct regmap_range max77714_readable_ranges[] = { 2362306a36Sopenharmony_ci regmap_reg_range(MAX77714_INT_TOP, MAX77714_INT_TOP), 2462306a36Sopenharmony_ci regmap_reg_range(MAX77714_INT_TOPM, MAX77714_INT_TOPM), 2562306a36Sopenharmony_ci regmap_reg_range(MAX77714_32K_STATUS, MAX77714_32K_CONFIG), 2662306a36Sopenharmony_ci regmap_reg_range(MAX77714_CNFG_GLBL2, MAX77714_CNFG2_ONOFF), 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic const struct regmap_range max77714_writable_ranges[] = { 3062306a36Sopenharmony_ci regmap_reg_range(MAX77714_INT_TOPM, MAX77714_INT_TOPM), 3162306a36Sopenharmony_ci regmap_reg_range(MAX77714_32K_CONFIG, MAX77714_32K_CONFIG), 3262306a36Sopenharmony_ci regmap_reg_range(MAX77714_CNFG_GLBL2, MAX77714_CNFG2_ONOFF), 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic const struct regmap_access_table max77714_readable_table = { 3662306a36Sopenharmony_ci .yes_ranges = max77714_readable_ranges, 3762306a36Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(max77714_readable_ranges), 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic const struct regmap_access_table max77714_writable_table = { 4162306a36Sopenharmony_ci .yes_ranges = max77714_writable_ranges, 4262306a36Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(max77714_writable_ranges), 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic const struct regmap_config max77714_regmap_config = { 4662306a36Sopenharmony_ci .reg_bits = 8, 4762306a36Sopenharmony_ci .val_bits = 8, 4862306a36Sopenharmony_ci .max_register = MAX77714_CNFG2_ONOFF, 4962306a36Sopenharmony_ci .rd_table = &max77714_readable_table, 5062306a36Sopenharmony_ci .wr_table = &max77714_writable_table, 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic const struct regmap_irq max77714_top_irqs[] = { 5462306a36Sopenharmony_ci REGMAP_IRQ_REG(MAX77714_IRQ_TOP_ONOFF, 0, MAX77714_INT_TOP_ONOFF), 5562306a36Sopenharmony_ci REGMAP_IRQ_REG(MAX77714_IRQ_TOP_RTC, 0, MAX77714_INT_TOP_RTC), 5662306a36Sopenharmony_ci REGMAP_IRQ_REG(MAX77714_IRQ_TOP_GPIO, 0, MAX77714_INT_TOP_GPIO), 5762306a36Sopenharmony_ci REGMAP_IRQ_REG(MAX77714_IRQ_TOP_LDO, 0, MAX77714_INT_TOP_LDO), 5862306a36Sopenharmony_ci REGMAP_IRQ_REG(MAX77714_IRQ_TOP_SD, 0, MAX77714_INT_TOP_SD), 5962306a36Sopenharmony_ci REGMAP_IRQ_REG(MAX77714_IRQ_TOP_GLBL, 0, MAX77714_INT_TOP_GLBL), 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic const struct regmap_irq_chip max77714_irq_chip = { 6362306a36Sopenharmony_ci .name = "max77714-pmic", 6462306a36Sopenharmony_ci .status_base = MAX77714_INT_TOP, 6562306a36Sopenharmony_ci .mask_base = MAX77714_INT_TOPM, 6662306a36Sopenharmony_ci .num_regs = 1, 6762306a36Sopenharmony_ci .irqs = max77714_top_irqs, 6862306a36Sopenharmony_ci .num_irqs = ARRAY_SIZE(max77714_top_irqs), 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* 7262306a36Sopenharmony_ci * MAX77714 initially uses the internal, low precision oscillator. Enable 7362306a36Sopenharmony_ci * the external oscillator by setting the XOSC_RETRY bit. If the external 7462306a36Sopenharmony_ci * oscillator is not OK (probably not installed) this has no effect. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_cistatic int max77714_setup_xosc(struct device *dev, struct regmap *regmap) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci /* Internal Crystal Load Capacitance, indexed by value of 32KLOAD bits */ 7962306a36Sopenharmony_ci static const unsigned int load_cap[4] = {0, 10, 12, 22}; /* pF */ 8062306a36Sopenharmony_ci unsigned int load_cap_idx; 8162306a36Sopenharmony_ci unsigned int status; 8262306a36Sopenharmony_ci int err; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci err = regmap_update_bits(regmap, MAX77714_32K_CONFIG, 8562306a36Sopenharmony_ci MAX77714_32K_CONFIG_XOSC_RETRY, 8662306a36Sopenharmony_ci MAX77714_32K_CONFIG_XOSC_RETRY); 8762306a36Sopenharmony_ci if (err) 8862306a36Sopenharmony_ci return dev_err_probe(dev, err, "Failed to configure the external oscillator\n"); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci err = regmap_read(regmap, MAX77714_32K_STATUS, &status); 9162306a36Sopenharmony_ci if (err) 9262306a36Sopenharmony_ci return dev_err_probe(dev, err, "Failed to read external oscillator status\n"); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci load_cap_idx = (status >> MAX77714_32K_STATUS_32KLOAD_SHF) 9562306a36Sopenharmony_ci & MAX77714_32K_STATUS_32KLOAD_MSK; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci dev_info(dev, "Using %s oscillator, %d pF load cap\n", 9862306a36Sopenharmony_ci status & MAX77714_32K_STATUS_32KSOURCE ? "internal" : "external", 9962306a36Sopenharmony_ci load_cap[load_cap_idx]); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int max77714_probe(struct i2c_client *client) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct device *dev = &client->dev; 10762306a36Sopenharmony_ci struct regmap *regmap; 10862306a36Sopenharmony_ci struct regmap_irq_chip_data *irq_data; 10962306a36Sopenharmony_ci int err; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci regmap = devm_regmap_init_i2c(client, &max77714_regmap_config); 11262306a36Sopenharmony_ci if (IS_ERR(regmap)) 11362306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(regmap), 11462306a36Sopenharmony_ci "Failed to initialise regmap\n"); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci err = max77714_setup_xosc(dev, regmap); 11762306a36Sopenharmony_ci if (err) 11862306a36Sopenharmony_ci return err; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci err = devm_regmap_add_irq_chip(dev, regmap, client->irq, 12162306a36Sopenharmony_ci IRQF_ONESHOT | IRQF_SHARED, 0, 12262306a36Sopenharmony_ci &max77714_irq_chip, &irq_data); 12362306a36Sopenharmony_ci if (err) 12462306a36Sopenharmony_ci return dev_err_probe(dev, err, "Failed to add PMIC IRQ chip\n"); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 12762306a36Sopenharmony_ci max77714_cells, ARRAY_SIZE(max77714_cells), 12862306a36Sopenharmony_ci NULL, 0, NULL); 12962306a36Sopenharmony_ci if (err) 13062306a36Sopenharmony_ci return dev_err_probe(dev, err, "Failed to register child devices\n"); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic const struct of_device_id max77714_dt_match[] = { 13662306a36Sopenharmony_ci { .compatible = "maxim,max77714" }, 13762306a36Sopenharmony_ci {}, 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, max77714_dt_match); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic struct i2c_driver max77714_driver = { 14262306a36Sopenharmony_ci .driver = { 14362306a36Sopenharmony_ci .name = "max77714", 14462306a36Sopenharmony_ci .of_match_table = max77714_dt_match, 14562306a36Sopenharmony_ci }, 14662306a36Sopenharmony_ci .probe = max77714_probe, 14762306a36Sopenharmony_ci}; 14862306a36Sopenharmony_cimodule_i2c_driver(max77714_driver); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ciMODULE_DESCRIPTION("Maxim MAX77714 MFD core driver"); 15162306a36Sopenharmony_ciMODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>"); 15262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 153