1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2024-11-19 13:27:49 +00:00
a155-U-u1/kernel-5.10/drivers/mfd/mt6375.c
2024-03-11 06:53:12 +11:00

492 lines
12 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*
* Author: ShuFan Lee <shufan_lee@richtek.com>
*/
#include <dt-bindings/mfd/mt6375.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/atomic.h>
#define MT6375_SLAVEID_TCPC 0x4E
#define MT6375_SLAVEID_PMU 0x34
#define MT6375_SLAVEID_BM 0x1A
#define MT6375_SLAVEID_HK1 0x4A
#define MT6375_SLAVEID_HK2 0x64
#define MT6375_SLAVEID_TM 0x3F
#define MT6375_VID 0x70
#define MT6375_REGADDR_SIZE 2
#define MT6375_REG_DEV_INFO 0x100
#define MT6375_REG_IRQ_SET 0x10D
#define MT6375_REG_CHG_IRQ0 0x1D0
#define MT6375_REG_PD_EVT 0x1DF
#define MT6375_REG_CHG_MSK0 0x1F0
#define MT6375_IRQ_REGS (MT6375_REG_PD_EVT - MT6375_REG_CHG_IRQ0 + 1)
#define MT6375_REG_HK2_END 0x4FF
#define MT6375_BANK_TCPC_RT2 0xF2
#define MT6375_REG_RT2_START 0xF200
#define MT6375_REG_RT2_END 0xF2FF
#define MT6375_MSK_VID 0xF0
#define MT6375_MSK_CHIP_REV 0x0F
enum {
MT6375_SLAVE_TCPC,
MT6375_SLAVE_PMU,
MT6375_SLAVE_BM,
MT6375_SLAVE_HK1,
MT6375_SLAVE_HK2,
MT6375_SLAVE_TM,
MT6375_SLAVE_MAX,
};
struct mt6375_data {
struct device *dev;
struct i2c_client *i2c[MT6375_SLAVE_MAX];
struct regmap *rmap;
struct irq_domain *domain;
struct irq_chip irq_chip;
struct mutex irq_lock;
u8 mask_buf[MT6375_IRQ_REGS];
u8 chip_rev;
atomic_t in_sleep;
};
static const u8 mt6375_slave_addr[MT6375_SLAVE_MAX] = {
MT6375_SLAVEID_TCPC,
MT6375_SLAVEID_PMU,
MT6375_SLAVEID_BM,
MT6375_SLAVEID_HK1,
MT6375_SLAVEID_HK2,
MT6375_SLAVEID_TM
};
static inline struct i2c_client *bank_to_i2c(struct mt6375_data *ddata, u8 bank)
{
if (bank >= MT6375_SLAVE_MAX && bank != MT6375_BANK_TCPC_RT2)
return NULL;
return (bank == MT6375_BANK_TCPC_RT2) ? ddata->i2c[MT6375_SLAVE_TCPC] :
ddata->i2c[bank];
}
static int mt6375_regmap_write(void *context, const void *data, size_t count)
{
int ret;
struct mt6375_data *ddata = context;
struct i2c_client *i2c;
const u8 *_data = data;
if (atomic_read(&ddata->in_sleep)) {
dev_info(ddata->dev, "%s in sleep\n", __func__);
return -EHOSTDOWN;
}
i2c = bank_to_i2c(ddata, _data[0]);
if (!i2c)
return -EINVAL;
if (_data[0] == MT6375_BANK_TCPC_RT2) {
ret = i2c_master_send(i2c, _data, count);
if (ret < 0)
return ret;
return ret != count ? -EIO : 0;
}
return i2c_smbus_write_i2c_block_data(i2c, _data[1],
count - MT6375_REGADDR_SIZE,
_data + MT6375_REGADDR_SIZE);
}
static int mt6375_regmap_read(void *context, const void *reg_buf,
size_t reg_size, void *val_buf, size_t val_size)
{
int ret;
size_t len;
struct mt6375_data *ddata = context;
struct i2c_client *i2c;
const u8 *_reg_buf = reg_buf;
if (atomic_read(&ddata->in_sleep)) {
dev_info(ddata->dev, "%s in sleep\n", __func__);
return -EHOSTDOWN;
}
i2c = bank_to_i2c(ddata, _reg_buf[0]);
if (!i2c)
return -EINVAL;
if (_reg_buf[0] == MT6375_BANK_TCPC_RT2) {
u8 buf[2] = { _reg_buf[0], _reg_buf[1] };
struct i2c_msg msg[2] = {
{
.addr = i2c->addr,
.flags = i2c->flags,
.len = 2,
.buf = buf,
}, {
.addr = i2c->addr,
.flags = i2c->flags | I2C_M_RD,
.len = val_size,
.buf = val_buf,
},
};
len = 2;
ret = i2c_transfer(i2c->adapter, msg, len);
} else {
len = val_size;
ret = i2c_smbus_read_i2c_block_data(i2c, _reg_buf[1], len,
val_buf);
}
if (ret < 0)
return ret;
return ret != len ? -EIO : 0;
}
static const struct regmap_bus mt6375_regmap_bus = {
.write = mt6375_regmap_write,
.read = mt6375_regmap_read,
};
static bool mt6375_is_accessible_reg(struct device *dev, unsigned int reg)
{
if (reg <= MT6375_REG_HK2_END ||
(reg >= MT6375_REG_RT2_START && reg <= MT6375_REG_RT2_END))
return true;
return false;
}
static const struct regmap_config mt6375_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
.reg_format_endian = REGMAP_ENDIAN_BIG,
.max_register = MT6375_REG_RT2_END,
.writeable_reg = mt6375_is_accessible_reg,
.readable_reg = mt6375_is_accessible_reg,
};
static void mt6375_irq_lock(struct irq_data *data)
{
struct mt6375_data *ddata = irq_data_get_irq_chip_data(data);
mutex_lock(&ddata->irq_lock);
}
static void mt6375_irq_sync_unlock(struct irq_data *data)
{
struct mt6375_data *ddata = irq_data_get_irq_chip_data(data);
int idx = data->hwirq / 8, ret;
ret = regmap_write(ddata->rmap, MT6375_REG_CHG_MSK0 + idx,
ddata->mask_buf[idx]);
if (ret)
dev_err(ddata->dev, "failed to mask/unmask irq %d\n",
data->hwirq);
mutex_unlock(&ddata->irq_lock);
}
static void mt6375_irq_enable(struct irq_data *data)
{
struct mt6375_data *ddata = irq_data_get_irq_chip_data(data);
ddata->mask_buf[data->hwirq / 8] &= ~BIT(data->hwirq % 8);
}
static void mt6375_irq_disable(struct irq_data *data)
{
struct mt6375_data *ddata = irq_data_get_irq_chip_data(data);
ddata->mask_buf[data->hwirq / 8] |= BIT(data->hwirq % 8);
}
static int mt6375_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hwirq)
{
struct mt6375_data *ddata = h->host_data;
struct i2c_client *client = to_i2c_client(ddata->dev);
irq_set_chip_data(virq, ddata);
if (hwirq == MT6375_GM30_EVT)
irq_set_chip_and_handler(virq, &ddata->irq_chip,
handle_simple_irq);
else {
irq_set_chip(virq, &ddata->irq_chip);
irq_set_nested_thread(virq, 1);
}
irq_set_parent(virq, client->irq);
irq_set_noprobe(virq);
return 0;
}
static const struct irq_domain_ops mt6375_domain_ops = {
.map = mt6375_irq_map,
.xlate = irq_domain_xlate_onetwocell,
};
static irqreturn_t mt6375_irq_handler(int irq, void *data)
{
struct mt6375_data *ddata = data;
generic_handle_irq(irq_find_mapping(ddata->domain, MT6375_GM30_EVT));
return IRQ_WAKE_THREAD;
}
static irqreturn_t mt6375_irq_thread(int irq, void *data)
{
struct mt6375_data *ddata = data;
u8 evt[MT6375_IRQ_REGS];
bool handled = false;
int i, j, ret;
ret = regmap_bulk_read(ddata->rmap, MT6375_REG_CHG_IRQ0, evt,
MT6375_IRQ_REGS);
if (ret) {
dev_err(ddata->dev, "failed to read irq event\n");
return IRQ_HANDLED;
}
/* ignore masked irq and ack */
for (i = 0; i < MT6375_IRQ_REGS; i++)
evt[i] &= ~ddata->mask_buf[i];
ret = regmap_bulk_write(ddata->rmap, MT6375_REG_CHG_IRQ0, evt,
MT6375_IRQ_REGS);
if (ret < 0)
dev_err(ddata->dev, "failed to ack irq status\n");
/* handle irq */
for (i = 0; i < MT6375_IRQ_REGS; i++) {
if (!evt[i] || i == (MT6375_GM30_EVT / 8))
continue;
for (j = 0; j < 8; j++) {
if (!(evt[i] & BIT(j)))
continue;
handle_nested_irq(irq_find_mapping(ddata->domain,
i * 8 + j));
handled = true;
}
}
return handled ? IRQ_HANDLED : IRQ_NONE;
}
static int mt6375_add_irq_chip(struct mt6375_data *ddata)
{
int ret;
struct i2c_client *client = to_i2c_client(ddata->dev);
memset(ddata->mask_buf, 0xff, MT6375_IRQ_REGS);
ret = regmap_bulk_write(ddata->rmap, MT6375_REG_CHG_MSK0,
ddata->mask_buf, MT6375_IRQ_REGS);
if (ret) {
dev_err(ddata->dev, "failed to mask all irqs\n");
return ret;
}
ret = regmap_bulk_write(ddata->rmap, MT6375_REG_CHG_IRQ0,
ddata->mask_buf, MT6375_IRQ_REGS);
if (ret) {
dev_err(ddata->dev, "failed to clear all irqs\n");
return ret;
}
ddata->irq_chip.name = dev_name(ddata->dev);
ddata->irq_chip.irq_bus_lock = mt6375_irq_lock;
ddata->irq_chip.irq_bus_sync_unlock = mt6375_irq_sync_unlock;
ddata->irq_chip.irq_disable = mt6375_irq_disable;
ddata->irq_chip.irq_enable = mt6375_irq_enable;
ddata->irq_chip.flags = IRQCHIP_SKIP_SET_WAKE;
ddata->domain = irq_domain_add_linear(ddata->dev->of_node,
MT6375_IRQ_REGS * 8,
&mt6375_domain_ops, ddata);
if (!ddata->domain) {
dev_err(ddata->dev, "failed to create irq domain\n");
return -ENOMEM;
}
ret = devm_request_threaded_irq(ddata->dev, client->irq,
mt6375_irq_handler, mt6375_irq_thread,
IRQF_ONESHOT, dev_name(ddata->dev),
ddata);
if (ret) {
dev_err(ddata->dev, "failed to request irq %d for %s\n",
client->irq, dev_name(ddata->dev));
irq_domain_remove(ddata->domain);
return ret;
}
return 0;
}
static void mt6375_del_irq_chip(struct mt6375_data *ddata)
{
unsigned int virq;
int hwirq;
for (hwirq = 0; hwirq < MT6375_IRQ_REGS * 8; hwirq++) {
virq = irq_find_mapping(ddata->domain, hwirq);
if (virq)
irq_dispose_mapping(virq);
}
irq_domain_remove(ddata->domain);
}
static int mt6375_check_devid(struct mt6375_data *ddata)
{
int ret;
u8 vid;
u32 val;
ret = regmap_read(ddata->rmap, MT6375_REG_DEV_INFO, &val);
if (ret < 0)
return ret;
vid = val & MT6375_MSK_VID;
if (vid != MT6375_VID) {
dev_err(ddata->dev, "vendor id 0x%02X is incorrect\n", vid);
return -ENODEV;
}
ddata->chip_rev = val & MT6375_MSK_CHIP_REV;
return 0;
}
static int mt6375_probe(struct i2c_client *client)
{
int i, ret;
struct mt6375_data *ddata;
dev_info(&client->dev, "%s\n", __func__);
ddata = devm_kzalloc(&client->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
ddata->dev = &client->dev;
mutex_init(&ddata->irq_lock);
i2c_set_clientdata(client, ddata);
atomic_set(&ddata->in_sleep, 0);
for (i = 0; i < MT6375_SLAVE_MAX; i++) {
if (i == MT6375_SLAVE_PMU) {
ddata->i2c[i] = client;
continue;
}
ddata->i2c[i] = devm_i2c_new_dummy_device(ddata->dev,
client->adapter,
mt6375_slave_addr[i]);
if (IS_ERR(ddata->i2c[i])) {
dev_err(&client->dev, "failed to new i2c(0x%02X)\n",
mt6375_slave_addr[i]);
ret = PTR_ERR(ddata->i2c[i]);
goto err;
}
}
ddata->rmap = devm_regmap_init(ddata->dev, &mt6375_regmap_bus, ddata,
&mt6375_regmap_config);
if (IS_ERR(ddata->rmap)) {
dev_err(ddata->dev, "failed to init regmap\n");
ret = PTR_ERR(ddata->rmap);
goto err;
}
ret = mt6375_check_devid(ddata);
if (ret < 0) {
dev_err(ddata->dev, "failed to check device id\n");
goto err;
}
ret = mt6375_add_irq_chip(ddata);
if (ret < 0) {
dev_err(ddata->dev, "failed to add irq chip\n");
goto err;
}
return devm_of_platform_populate(ddata->dev);
err:
mutex_destroy(&ddata->irq_lock);
return ret;
}
static int mt6375_remove(struct i2c_client *client)
{
struct mt6375_data *ddata = i2c_get_clientdata(client);
mt6375_del_irq_chip(ddata);
mutex_destroy(&ddata->irq_lock);
return 0;
}
static int __maybe_unused mt6375_suspend(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
if (device_may_wakeup(dev))
enable_irq_wake(i2c->irq);
disable_irq(i2c->irq);
return 0;
}
static int __maybe_unused mt6375_resume(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
enable_irq(i2c->irq);
if (device_may_wakeup(dev))
disable_irq_wake(i2c->irq);
return 0;
}
static int mt6375_suspend_noirq(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
struct mt6375_data *data = i2c_get_clientdata(i2c);
atomic_set(&data->in_sleep, 1);
return 0;
}
static int mt6375_resume_noirq(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
struct mt6375_data *data = i2c_get_clientdata(i2c);
atomic_set(&data->in_sleep, 0);
return 0;
}
static const struct dev_pm_ops mt6375_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(mt6375_suspend, mt6375_resume)
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mt6375_suspend_noirq,
mt6375_resume_noirq)
};
static const struct of_device_id __maybe_unused mt6375_of_match[] = {
{ .compatible = "mediatek,mt6375", },
{ },
};
MODULE_DEVICE_TABLE(of, mt6375_of_match);
static struct i2c_driver mt6375_driver = {
.probe_new = mt6375_probe,
.remove = mt6375_remove,
.driver = {
.name = "mt6375",
.pm = &mt6375_pm_ops,
.of_match_table = of_match_ptr(mt6375_of_match),
},
};
module_i2c_driver(mt6375_driver);
MODULE_AUTHOR("ShuFan Lee <shufan_lee@richtek.com>");
MODULE_DESCRIPTION("MT6375 Core I2C Drvier");
MODULE_LICENSE("GPL v2");