1
0
Fork 0

mfd: Initial commit of sy7636a

Initial support for the Silergy SY7636A Power Management chip
driver.

Signed-off-by: Alistair Francis <alistair@alistair23.me>
rM2-mainline-working-wifi
Alistair Francis 2021-01-17 11:32:27 -08:00
parent f899726ea6
commit 227a3ee195
4 changed files with 305 additions and 0 deletions

View File

@ -1351,6 +1351,16 @@ config MFD_SYSCON
Select this option to enable accessing system control registers
via regmap.
config MFD_SY7636A
tristate "Silergy SY7636A Power Management chip driver"
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
depends on I2C=y
help
Select this option to enable support for the Silergy SY7636A
Power Management chip driver.
config MFD_DAVINCI_VOICECODEC
tristate
select MFD_CORE

View File

@ -265,6 +265,8 @@ obj-$(CONFIG_MFD_STMFX) += stmfx.o
obj-$(CONFIG_MFD_KHADAS_MCU) += khadas-mcu.o
obj-$(CONFIG_MFD_ACER_A500_EC) += acer-ec-a500.o
obj-$(CONFIG_MFD_SY7636A) += sy7636a.o
obj-$(CONFIG_SGI_MFD_IOC3) += ioc3.o
obj-$(CONFIG_MFD_SIMPLE_MFD_I2C) += simple-mfd-i2c.o
obj-$(CONFIG_MFD_INTEL_M10_BMC) += intel-m10-bmc.o

View File

@ -0,0 +1,247 @@
// SPDX-License-Identifier: GPL-2.0+
//
// MFD driver for SY7636A chip
//
// Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
//
// Authors: Lars Ivar Miljeteig <lars.ivar.miljeteig@remarkable.com>
// Alistair Francis <alistair@alistair23.me>
//
// Based on the lp87565 driver by Keerthy <j-keerthy@ti.com>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/sysfs.h>
#include <linux/mfd/sy7636a.h>
static const struct regmap_config sy7636a_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static const struct mfd_cell sy7636a_cells[] = {
{ .name = "sy7636a-regulator", },
{ .name = "sy7636a-temperature", },
{ .name = "sy7636a-thermal", },
};
static const struct of_device_id of_sy7636a_match_table[] = {
{ .compatible = "silergy,sy7636a", },
{}
};
MODULE_DEVICE_TABLE(of, of_sy7636a_match_table);
static const char * const states[] = {
"no fault event",
"UVP at VP rail",
"UVP at VN rail",
"UVP at VPOS rail",
"UVP at VNEG rail",
"UVP at VDDH rail",
"UVP at VEE rail",
"SCP at VP rail",
"SCP at VN rail",
"SCP at VPOS rail",
"SCP at VNEG rail",
"SCP at VDDH rail",
"SCP at VEE rail",
"SCP at V COM rail",
"UVLO",
"Thermal shutdown",
};
static int sy7636a_get_vcom_voltage_mv(struct regmap *regmap)
{
int ret;
unsigned int val, val_h;
ret = regmap_read(regmap, SY7636A_REG_VCOM_ADJUST_CTRL_L, &val);
if (ret)
return ret;
ret = regmap_read(regmap, SY7636A_REG_VCOM_ADJUST_CTRL_H, &val_h);
if (ret)
return ret;
val |= (val_h << 8);
return (val & 0x1FF) * 10;
}
static int sy7636a_set_vcom_voltage_mv(struct regmap *regmap, unsigned int vcom)
{
int ret;
unsigned int val;
if (vcom < 0 || vcom > 5000)
return -EINVAL;
val = (unsigned int)(vcom / 10) & 0x1ff;
ret = regmap_write(regmap, SY7636A_REG_VCOM_ADJUST_CTRL_L, val);
if (ret)
return ret;
ret = regmap_write(regmap, SY7636A_REG_VCOM_ADJUST_CTRL_H, val >> 8);
if (ret)
return ret;
return 0;
}
static ssize_t state_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int ret;
unsigned int val;
struct sy7636a *sy7636a = dev_get_drvdata(dev);
ret = regmap_read(sy7636a->regmap, SY7636A_REG_FAULT_FLAG, &val);
if (ret) {
dev_err(sy7636a->dev, "Failed to read from device\n");
return ret;
}
val = val >> 1;
if (val >= ARRAY_SIZE(states)) {
dev_err(sy7636a->dev, "Unexpected value read from device: %u\n", val);
return -EINVAL;
}
return snprintf(buf, PAGE_SIZE, "%s\n", states[val]);
}
static DEVICE_ATTR(state, 0444, state_show, NULL);
static ssize_t power_good_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int ret;
unsigned int val;
struct sy7636a *sy7636a = dev_get_drvdata(dev);
ret = regmap_read(sy7636a->regmap, SY7636A_REG_FAULT_FLAG, &val);
if (ret) {
dev_err(sy7636a->dev, "Failed to read from device\n");
return ret;
}
val &= 0x01;
return snprintf(buf, PAGE_SIZE, "%s\n", val ? "ON" : "OFF");
}
static DEVICE_ATTR(power_good, 0444, power_good_show, NULL);
static ssize_t vcom_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int ret;
struct sy7636a *sy7636a = dev_get_drvdata(dev);
ret = sy7636a_get_vcom_voltage_mv(sy7636a->regmap);
if (ret < 0)
return ret;
return snprintf(buf, PAGE_SIZE, "%d\n", -ret);
}
static ssize_t vcom_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
int vcom;
struct sy7636a *sy7636a = dev_get_drvdata(dev);
ret = kstrtoint(buf, 0, &vcom);
if (ret)
return ret;
if (vcom > 0 || vcom < -5000)
return -EINVAL;
ret = sy7636a_set_vcom_voltage_mv(sy7636a->regmap, (unsigned int)(-vcom));
if (ret)
return ret;
return count;
}
static DEVICE_ATTR(vcom, 0644, vcom_show, vcom_store);
static struct attribute *sy7636a_sysfs_attrs[] = {
&dev_attr_state.attr,
&dev_attr_power_good.attr,
&dev_attr_vcom.attr,
NULL,
};
static const struct attribute_group sy7636a_sysfs_attr_group = {
.attrs = sy7636a_sysfs_attrs,
};
static int sy7636a_probe(struct i2c_client *client,
const struct i2c_device_id *ids)
{
struct sy7636a *sy7636a;
int ret;
sy7636a = devm_kzalloc(&client->dev, sizeof(struct sy7636a), GFP_KERNEL);
if (sy7636a == NULL)
return -ENOMEM;
sy7636a->dev = &client->dev;
sy7636a->regmap = devm_regmap_init_i2c(client, &sy7636a_regmap_config);
if (IS_ERR(sy7636a->regmap)) {
ret = PTR_ERR(sy7636a->regmap);
dev_err(sy7636a->dev,
"Failed to initialize register map: %d\n", ret);
return ret;
}
i2c_set_clientdata(client, sy7636a);
ret = sysfs_create_group(&client->dev.kobj, &sy7636a_sysfs_attr_group);
if (ret) {
dev_err(sy7636a->dev, "Failed to create sysfs attributes\n");
return ret;
}
ret = devm_mfd_add_devices(sy7636a->dev, PLATFORM_DEVID_AUTO,
sy7636a_cells, ARRAY_SIZE(sy7636a_cells),
NULL, 0, NULL);
if (ret) {
dev_err(sy7636a->dev, "Failed to add mfd devices\n");
sysfs_remove_group(&client->dev.kobj, &sy7636a_sysfs_attr_group);
return ret;
}
return 0;
}
static const struct i2c_device_id sy7636a_id_table[] = {
{ "sy7636a", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, sy7636a_id_table);
static struct i2c_driver sy7636a_driver = {
.driver = {
.name = "sy7636a",
.of_match_table = of_sy7636a_match_table,
},
.probe = sy7636a_probe,
.id_table = sy7636a_id_table,
};
module_i2c_driver(sy7636a_driver);
MODULE_AUTHOR("Lars Ivar Miljeteig <lars.ivar.miljeteig@remarkable.com>");
MODULE_DESCRIPTION("Silergy SY7636A Multi-Function Device Driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,46 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Functions to access SY3686A power management chip.
*
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_MFD_SY7636A_H
#define __LINUX_MFD_SY7636A_H
#include <linux/i2c.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regmap.h>
#define SY7636A_REG_OPERATION_MODE_CRL 0x00
#define SY7636A_OPERATION_MODE_CRL_VCOMCTL (1 << 6)
#define SY7636A_OPERATION_MODE_CRL_ONOFF (1 << 7)
#define SY7636A_REG_VCOM_ADJUST_CTRL_L 0x01
#define SY7636A_REG_VCOM_ADJUST_CTRL_H 0x02
#define SY7636A_REG_VCOM_ADJUST_CTRL_MASK 0x01ff
#define SY7636A_REG_VLDO_VOLTAGE_ADJULST_CTRL 0x03
#define SY7636A_REG_POWER_ON_DELAY_TIME 0x06
#define SY7636A_REG_FAULT_FLAG 0x07
#define SY7636A_FAULT_FLAG_PG (1 << 0)
#define SY7636A_REG_TERMISTOR_READOUT 0x08
#define SY7636A_REG_MAX 0x08
struct sy7636a {
struct device *dev;
struct regmap *regmap;
int vcom;
struct gpio_desc *pgood_gpio;
};
#endif /* __LINUX_MFD_SY7636A_H */