kernel-brax3-ubuntu-touch/drivers/mfd/rt9490.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

322 lines
7.9 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022 MediaTek Inc.
*/
#include <dt-bindings/mfd/richtek,rt9490.h>
#include <linux/bits.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/regmap.h>
#define RT9490_REG_CHG_IRQ_FLAG0 0x22
#define RT9490_REG_CHG_IRQ_MASK0 0x28
#define RT9490_REG_DEVICE_INFO 0x48
#define RT9490_REG_ADD_IRQ_FLAG 0x4D
#define RT9490_REG_ADD_IRQ_MASK 0x4E
#define RT9490_NUM_IRQ_REGS 7
#define RT9490_ADD_IRQ_OFFSET 6
#define RT9490_DEVICE_ID_MASK GENMASK(6, 3)
#define RT9490_VENDOR_ID 0x60
//#define RT9490_VENDOR_ID 0x00
struct rt9490_data {
struct i2c_client *i2c;
struct device *dev;
struct regmap *regmap;
struct irq_domain *irq_domain;
struct mutex lock;
struct mutex hidden_mode_lock;
struct irq_chip irq_chip;
u8 mask_buf[RT9490_NUM_IRQ_REGS];
int hidden_mode_cnt;
};
static const struct mfd_cell rt9490_devs[] = {
MFD_CELL_OF("rt9490-adc", NULL, NULL, 0, 0, "richtek,rt9490-adc"),
MFD_CELL_OF("rt9490-chg", NULL, NULL, 0, 0, "richtek,rt9490-chg")
};
static void rt9490_irq_bus_lock(struct irq_data *irq_data)
{
struct rt9490_data *data = irq_data_get_irq_chip_data(irq_data);
mutex_lock(&data->lock);
}
static void rt9490_irq_bus_unlock(struct irq_data *irq_data)
{
struct rt9490_data *data = irq_data_get_irq_chip_data(irq_data);
unsigned int reg_offset = irq_data->hwirq / 8;
unsigned int reg_addr;
int ret;
if (reg_offset == RT9490_ADD_IRQ_OFFSET)
reg_addr = RT9490_REG_ADD_IRQ_MASK;
else
reg_addr = RT9490_REG_CHG_IRQ_MASK0 + reg_offset;
ret = regmap_write(data->regmap, reg_addr, data->mask_buf[reg_offset]);
if (ret)
dev_err(data->dev, "Failed to write mask [%d]\n", reg_offset);
mutex_unlock(&data->lock);
}
static void rt9490_irq_enable(struct irq_data *irq_data)
{
struct rt9490_data *data = irq_data_get_irq_chip_data(irq_data);
data->mask_buf[irq_data->hwirq / 8] &= ~BIT(irq_data->hwirq % 8);
}
static void rt9490_irq_disable(struct irq_data *irq_data)
{
struct rt9490_data *data = irq_data_get_irq_chip_data(irq_data);
data->mask_buf[irq_data->hwirq / 8] |= BIT(irq_data->hwirq % 8);
}
static int rt9490_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hwirq)
{
struct rt9490_data *data = h->host_data;
struct i2c_client *i2c = to_i2c_client(data->dev);
irq_set_chip_data(virq, data);
irq_set_chip(virq, &data->irq_chip);
irq_set_nested_thread(virq, true);
irq_set_parent(virq, i2c->irq);
irq_set_noprobe(virq);
return 0;
}
static const struct irq_domain_ops rt9490_irq_domain_ops = {
.map = rt9490_irq_map,
.xlate = irq_domain_xlate_onetwocell,
};
static irqreturn_t rt9490_irq_handler(int irq, void *priv)
{
struct rt9490_data *data = priv;
u8 status_buf[RT9490_NUM_IRQ_REGS], evts;
unsigned int add_event;
bool handled = false;
int i, j, ret;
ret = regmap_raw_read(data->regmap, RT9490_REG_CHG_IRQ_FLAG0,
status_buf, RT9490_ADD_IRQ_OFFSET);
ret |= regmap_read(data->regmap, RT9490_REG_ADD_IRQ_FLAG, &add_event);
if (ret) {
dev_err(data->dev, "Failed to read irq flag\n");
return IRQ_NONE;
}
status_buf[RT9490_ADD_IRQ_OFFSET] = add_event;
for (i = 0; i < RT9490_NUM_IRQ_REGS; i++) {
evts = status_buf[i] & ~data->mask_buf[i];
if (!evts)
continue;
for (j = 0; j < 8; j++) {
if (!(evts & BIT(j)))
continue;
handle_nested_irq(irq_find_mapping(data->irq_domain,
i * 8 + j));
handled = true;
}
}
return handled ? IRQ_HANDLED : IRQ_NONE;
}
static int rt9490_add_irq_chip(struct rt9490_data *data)
{
struct i2c_client *i2c = to_i2c_client(data->dev);
u8 status_buf[RT9490_ADD_IRQ_OFFSET];
unsigned int add_event;
int ret;
mutex_init(&data->lock);
data->irq_chip.name = dev_name(data->dev);
data->irq_chip.irq_bus_lock = rt9490_irq_bus_lock;
data->irq_chip.irq_bus_sync_unlock = rt9490_irq_bus_unlock;
data->irq_chip.irq_enable = rt9490_irq_enable;
data->irq_chip.irq_disable = rt9490_irq_disable;
/* Mask and read clear all events by default */
memset(data->mask_buf, 0xff, RT9490_NUM_IRQ_REGS);
ret = regmap_raw_write(data->regmap, RT9490_REG_CHG_IRQ_MASK0,
data->mask_buf, RT9490_ADD_IRQ_OFFSET);
ret |= regmap_write(data->regmap, RT9490_REG_ADD_IRQ_MASK,
data->mask_buf[RT9490_ADD_IRQ_OFFSET]);
if (ret) {
dev_err(data->dev, "Failed to write irq mask\n");
return ret;
}
ret = regmap_raw_read(data->regmap, RT9490_REG_CHG_IRQ_FLAG0,
status_buf, RT9490_ADD_IRQ_OFFSET);
ret |= regmap_read(data->regmap, RT9490_REG_ADD_IRQ_FLAG, &add_event);
if (ret) {
dev_err(data->dev, "Failed to read clear irq flag\n");
return ret;
}
data->irq_domain = irq_domain_add_linear(data->dev->of_node,
RT9490_NUM_IRQ_REGS * 8,
&rt9490_irq_domain_ops, data);
if (!data->irq_domain) {
dev_err(data->dev, "Failed to create irq domain\n");
return -ENOMEM;
}
ret = request_threaded_irq(i2c->irq, NULL, rt9490_irq_handler,
IRQF_ONESHOT, dev_name(data->dev), data);
if (ret) {
dev_err(data->dev, "Failed to request irq(%d)\n", ret);
irq_domain_remove(data->irq_domain);
return ret;
}
device_init_wakeup(data->dev, true);
return 0;
}
static void rt9490_del_irq_chip(struct rt9490_data *data)
{
struct i2c_client *i2c = to_i2c_client(data->dev);
device_init_wakeup(data->dev, false);
free_irq(i2c->irq, data);
irq_domain_remove(data->irq_domain);
mutex_destroy(&data->lock);
}
static const struct regmap_config rt9490_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = RT9490_REG_ADD_IRQ_MASK,
};
static int rt9490_check_vendor_info(struct rt9490_data *data)
{
unsigned int devid;
int ret;
ret = regmap_read(data->regmap, RT9490_REG_DEVICE_INFO, &devid);
if (ret)
return ret;
if ((devid & RT9490_DEVICE_ID_MASK) != RT9490_VENDOR_ID) {
dev_err(data->dev, "VID not correct [0x%02x]\n", devid);
return -ENODEV;
}
return 0;
}
static int rt9490_probe(struct i2c_client *i2c)
{
struct rt9490_data *data;
int ret;
data = devm_kzalloc(&i2c->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
mutex_init(&data->hidden_mode_lock);
data->hidden_mode_cnt = 0;
data->dev = &i2c->dev;
data->i2c = i2c;
i2c_set_clientdata(i2c, data);
data->regmap = devm_regmap_init_i2c(i2c, &rt9490_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
ret = rt9490_check_vendor_info(data);
if (ret) {
dev_err(&i2c->dev, "Failed to check vendor info\n");
return ret;
}
ret = rt9490_add_irq_chip(data);
if (ret)
return ret;
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, rt9490_devs,
ARRAY_SIZE(rt9490_devs), NULL, 0,
data->irq_domain);
if (ret) {
dev_err(&i2c->dev, "Failed to add sub devices\n");
rt9490_del_irq_chip(data);
}
return 0;
}
static int rt9490_remove(struct i2c_client *i2c)
{
struct rt9490_data *data = i2c_get_clientdata(i2c);
rt9490_del_irq_chip(data);
return 0;
}
static int __maybe_unused rt9490_suspend(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
if (device_may_wakeup(dev))
enable_irq_wake(i2c->irq);
return 0;
}
static int __maybe_unused rt9490_resume(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
if (device_may_wakeup(dev))
disable_irq_wake(i2c->irq);
return 0;
}
static SIMPLE_DEV_PM_OPS(rt9490_pm_ops, rt9490_suspend, rt9490_resume);
static const struct of_device_id rt9490_of_match_table[] = {
{ .compatible = "richtek,rt9490", },
{ }
};
MODULE_DEVICE_TABLE(of, rt9490_of_match_table);
static struct i2c_driver rt9490_driver = {
.driver = {
.name = "rt9490",
.of_match_table = rt9490_of_match_table,
.pm = &rt9490_pm_ops,
},
.probe_new = rt9490_probe,
.remove = rt9490_remove,
};
module_i2c_driver(rt9490_driver);
MODULE_DESCRIPTION("Richtek RT9490 MFD driver");
MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
MODULE_LICENSE("GPL");