mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-10 04:48:04 +09:00
drm/rockchip: ebc: Add fitipower fp9931 regulator driver
Signed-off-by: Chaoyi Chen <chaoyi.chen@rock-chips.com> Change-Id: I0cca4a032d51f1e1e336545c14fb39016e9c45b3
This commit is contained in:
@@ -17,3 +17,10 @@ config EPD_SY7636A_PMIC
|
||||
default y
|
||||
help
|
||||
If you say yes here you get support for the Silergy SY7636A PMIC.
|
||||
|
||||
config EPD_FP9931_PMIC
|
||||
bool "EINK PMIC FP9931 Support"
|
||||
depends on ROCKCHIP_EBC_DEV
|
||||
default y
|
||||
help
|
||||
If you say yes here you get support for the Fitipower FP9931 PMIC.
|
||||
|
||||
@@ -4,3 +4,4 @@ obj-$(CONFIG_ROCKCHIP_EBC_DEV) += ebc_pmic.o
|
||||
obj-$(CONFIG_EPD_TPS65185_SENSOR) += tps65185.o
|
||||
obj-$(CONFIG_ROCKCHIP_EBC_DEV) += ebc_pmic_mfd.o
|
||||
obj-$(CONFIG_EPD_SY7636A_PMIC) += sy7636a-regulator.o sy7636a-thermal.o
|
||||
obj-$(CONFIG_EPD_FP9931_PMIC) += fp9931-regulator.o
|
||||
|
||||
@@ -72,8 +72,18 @@ static struct pmic_mfd_data silergy_sy7636a = {
|
||||
.mfd_cell_size = ARRAY_SIZE(sy7636a_cells),
|
||||
};
|
||||
|
||||
static const struct mfd_cell fp9931_cells[] = {
|
||||
{ .name = "fp9931-regulator", },
|
||||
};
|
||||
|
||||
static struct pmic_mfd_data fitipower_fp9931 = {
|
||||
.mfd_cell = fp9931_cells,
|
||||
.mfd_cell_size = ARRAY_SIZE(fp9931_cells),
|
||||
};
|
||||
|
||||
static const struct of_device_id pmic_mfd_i2c_of_match[] = {
|
||||
{ .compatible = "silergy,sy7636a-pmic", .data = &silergy_sy7636a},
|
||||
{ .compatible = "fitipower,fp9931-pmic", .data = &fitipower_fp9931},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pmic_mfd_i2c_of_match);
|
||||
|
||||
352
drivers/gpu/drm/rockchip/ebc-dev/pmic/fp9931-regulator.c
Normal file
352
drivers/gpu/drm/rockchip/ebc-dev/pmic/fp9931-regulator.c
Normal file
@@ -0,0 +1,352 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2024 Rockchip Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/regmap.h>
|
||||
#include "fp9931.h"
|
||||
|
||||
#define FP9931_MAX_ENABLE_GPIO_NUM 4
|
||||
|
||||
struct fp9931_data {
|
||||
struct regmap *regmap;
|
||||
struct gpio_descs *power_gpio;
|
||||
struct gpio_desc *enable_gpio;
|
||||
/*
|
||||
* When registering a new regulator, regulator core will try to
|
||||
* call "set_suspend_disable" to check whether regulator device
|
||||
* working well.
|
||||
* Just skip this suspend to avoid reconfiguring pinctrl.
|
||||
*/
|
||||
bool initial_suspend;
|
||||
u8 vcom_reg;
|
||||
u8 vpos_vneg_reg;
|
||||
};
|
||||
|
||||
/* in uV */
|
||||
static const int fp9931_vpos_vneg_voltages[] = {
|
||||
7040000, 7040000, 7040000, 7040000, 7040000,
|
||||
7260000, 7490000, 7710000, 7930000, 8150000,
|
||||
8380000, 8600000, 8820000, 9040000, 9270000,
|
||||
9490000, 9710000, 9940000, 10160000, 10380000,
|
||||
10600000, 10830000, 11050000, 11270000, 11490000,
|
||||
11720000, 11940000, 12160000, 12380000, 12610000,
|
||||
12830000, 13050000, 13280000, 13500000, 13720000,
|
||||
13940000, 14170000, 14390000, 14610000, 14830000,
|
||||
15060000, 15060000, 15060000, 15060000, 15060000,
|
||||
15060000, 15060000, 15060000, 15060000, 15060000,
|
||||
15060000, 15060000, 15060000, 15060000, 15060000,
|
||||
15060000, 15060000, 15060000, 15060000, 15060000,
|
||||
15060000, 15060000, 15060000, 15060000,
|
||||
};
|
||||
|
||||
static int fp9931_vcom_get_voltage_sel(struct regulator_dev *rdev)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(rdev->regmap, FP9931_VCOM_SETTING, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* 0V for Vcom is meaningless */
|
||||
if (!val)
|
||||
return -EINVAL;
|
||||
|
||||
return --val;
|
||||
}
|
||||
|
||||
static int fp9931_vcom_set_voltage_sel(struct regulator_dev *rdev,
|
||||
unsigned selector)
|
||||
{
|
||||
struct fp9931_data *data = rdev->reg_data;
|
||||
int ret;
|
||||
|
||||
if (++selector > 0xFF)
|
||||
return -EINVAL;
|
||||
|
||||
data->vcom_reg = selector;
|
||||
ret = regmap_write(rdev->regmap, FP9931_VCOM_SETTING, selector);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* VCOM = 0V + [(-5 / 255) * N]V, N = 1~255 (0 is meaningless) */
|
||||
static int fp9931_vcom_list_voltage(struct regulator_dev *rdev, unsigned selector)
|
||||
{
|
||||
int vol_uV;
|
||||
|
||||
if (++selector > 0xFF)
|
||||
return -EINVAL;
|
||||
|
||||
vol_uV = selector * 1000 * 1000;
|
||||
|
||||
return DIV_ROUND_CLOSEST(vol_uV * 5, 255);
|
||||
}
|
||||
|
||||
static int fp9931_vcom_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct fp9931_data *data = rdev->reg_data;
|
||||
int ret;
|
||||
|
||||
ret = regmap_write_bits(rdev->regmap, FP9931_CONTROL_REG1, CONTROL_REG1_V3P3_EN,
|
||||
CONTROL_REG1_V3P3_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
gpiod_set_value_cansleep(data->enable_gpio, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fp9931_vcom_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct fp9931_data *data = rdev->reg_data;
|
||||
|
||||
/* V3P3 is auto off when EN pin disable */
|
||||
gpiod_set_value_cansleep(data->enable_gpio, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fp9931_vpos_vneg_set_voltage_sel(struct regulator_dev *rdev, unsigned int selector)
|
||||
{
|
||||
struct fp9931_data *data = rdev->reg_data;
|
||||
int ret;
|
||||
|
||||
if (unlikely(selector > VPOS_VNEG_SETTING))
|
||||
return -EINVAL;
|
||||
|
||||
data->vpos_vneg_reg = selector;
|
||||
ret = regmap_write_bits(rdev->regmap, FP9931_VPOS_VNEG_SETTING, VPOS_VNEG_SETTING,
|
||||
selector);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fp9931_vpos_vneg_get_voltage_sel(struct regulator_dev *rdev)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(rdev->regmap, FP9931_VPOS_VNEG_SETTING, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return val & VPOS_VNEG_SETTING;
|
||||
}
|
||||
|
||||
static int fp9931_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct fp9931_data *data = rdev->reg_data;
|
||||
|
||||
return !!gpiod_get_value_cansleep(data->enable_gpio);
|
||||
}
|
||||
|
||||
static int fp9931_get_status(struct regulator_dev *rdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fp9931_set_suspend_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
DECLARE_BITMAP(values, FP9931_MAX_ENABLE_GPIO_NUM);
|
||||
struct fp9931_data *data = rdev->reg_data;
|
||||
int ret;
|
||||
|
||||
bitmap_zero(values, FP9931_MAX_ENABLE_GPIO_NUM);
|
||||
|
||||
/* Skip initial suspend */
|
||||
if (data->initial_suspend) {
|
||||
data->initial_suspend = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
gpiod_set_value_cansleep(data->enable_gpio, 0);
|
||||
|
||||
if (data->power_gpio) {
|
||||
ret = gpiod_set_array_value_cansleep(data->power_gpio->ndescs,
|
||||
data->power_gpio->desc,
|
||||
data->power_gpio->info, values);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fp9931_resume(struct regulator_dev *rdev)
|
||||
{
|
||||
DECLARE_BITMAP(values, FP9931_MAX_ENABLE_GPIO_NUM);
|
||||
struct fp9931_data *data = rdev->reg_data;
|
||||
int ret;
|
||||
|
||||
bitmap_fill(values, FP9931_MAX_ENABLE_GPIO_NUM);
|
||||
|
||||
if (data->power_gpio) {
|
||||
ret = gpiod_set_array_value_cansleep(data->power_gpio->ndescs,
|
||||
data->power_gpio->desc,
|
||||
data->power_gpio->info, values);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Waiting for i2c to become available after power up */
|
||||
usleep_range(1000, 1500);
|
||||
|
||||
/* VCOM/VPOS_VNEG setting resume */
|
||||
ret = regmap_write(rdev->regmap, FP9931_VCOM_SETTING, data->vcom_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write_bits(rdev->regmap, FP9931_VPOS_VNEG_SETTING, VPOS_VNEG_SETTING,
|
||||
data->vpos_vneg_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
gpiod_set_value_cansleep(data->enable_gpio, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regulator_ops fp9931_vpos_vneg_volt_ops = {
|
||||
.list_voltage = regulator_list_voltage_table,
|
||||
.set_voltage_sel = fp9931_vpos_vneg_set_voltage_sel,
|
||||
.get_voltage_sel = fp9931_vpos_vneg_get_voltage_sel,
|
||||
.is_enabled = fp9931_is_enabled,
|
||||
};
|
||||
|
||||
static const struct regulator_desc vpos_vneg_desc = {
|
||||
.name = "vpos_vneg",
|
||||
.id = 0,
|
||||
.ops = &fp9931_vpos_vneg_volt_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.owner = THIS_MODULE,
|
||||
.n_voltages = ARRAY_SIZE(fp9931_vpos_vneg_voltages),
|
||||
.volt_table = fp9931_vpos_vneg_voltages,
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.of_match = of_match_ptr("vpos_vneg"),
|
||||
};
|
||||
|
||||
static const struct regulator_ops fp9931_vcom_volt_ops = {
|
||||
.list_voltage = fp9931_vcom_list_voltage,
|
||||
.get_voltage_sel = fp9931_vcom_get_voltage_sel,
|
||||
.set_voltage_sel = fp9931_vcom_set_voltage_sel,
|
||||
.enable = fp9931_vcom_enable,
|
||||
.disable = fp9931_vcom_disable,
|
||||
.is_enabled = fp9931_is_enabled,
|
||||
.get_status = fp9931_get_status,
|
||||
.set_suspend_disable = fp9931_set_suspend_disable,
|
||||
.resume = fp9931_resume,
|
||||
};
|
||||
|
||||
static const struct regulator_desc vcom_desc = {
|
||||
.name = "vcom",
|
||||
.id = 0,
|
||||
.ops = &fp9931_vcom_volt_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.owner = THIS_MODULE,
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.of_match = of_match_ptr("vcom"),
|
||||
};
|
||||
|
||||
static int fp9931_regulator_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct regmap *regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
struct regulator_config config = { };
|
||||
struct regulator_dev *rdev;
|
||||
struct fp9931_data *data;
|
||||
int ret, val;
|
||||
|
||||
if (!regmap)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(struct fp9931_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->regmap = regmap;
|
||||
data->initial_suspend = true;
|
||||
|
||||
data->power_gpio = devm_gpiod_get_array_optional(pdev->dev.parent, "power",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(data->power_gpio)) {
|
||||
dev_err(pdev->dev.parent, "Failed to get fp9931 power gpio %ld\n",
|
||||
PTR_ERR(data->power_gpio));
|
||||
return PTR_ERR(data->power_gpio);
|
||||
}
|
||||
|
||||
/* Waiting for i2c to become available after power up */
|
||||
usleep_range(1000, 1500);
|
||||
|
||||
data->enable_gpio = devm_gpiod_get(pdev->dev.parent, "enable", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(data->enable_gpio)) {
|
||||
dev_err(pdev->dev.parent, "Failed to get fp9931 enable gpio %ld\n",
|
||||
PTR_ERR(data->enable_gpio));
|
||||
return PTR_ERR(data->enable_gpio);
|
||||
}
|
||||
|
||||
ret = regmap_read(regmap, FP9931_VCOM_SETTING, &val);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
data->vcom_reg = val & 0xFF;
|
||||
|
||||
ret = regmap_read(regmap, FP9931_VPOS_VNEG_SETTING, &val);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
data->vpos_vneg_reg = val & VPOS_VNEG_SETTING;
|
||||
|
||||
gpiod_set_value_cansleep(data->enable_gpio, 1);
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
config.dev = &pdev->dev;
|
||||
config.dev->of_node = pdev->dev.parent->of_node;
|
||||
config.regmap = regmap;
|
||||
config.driver_data = data;
|
||||
|
||||
rdev = devm_regulator_register(&pdev->dev, &vcom_desc, &config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(pdev->dev.parent, "Failed to register %s vcom\n", pdev->name);
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
|
||||
rdev = devm_regulator_register(&pdev->dev, &vpos_vneg_desc, &config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(pdev->dev.parent, "Failed to register %s vpos_vneg\n", pdev->name);
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
dev_err(pdev->dev.parent, "Failed to initialize regulator: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct platform_device_id fp9931_regulator_id_table[] = {
|
||||
{ "fp9931-regulator", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, fp9931_regulator_id_table);
|
||||
|
||||
static struct platform_driver fp9931_regulator_driver = {
|
||||
.driver = {
|
||||
.name = "fp9931-regulator",
|
||||
},
|
||||
.probe = fp9931_regulator_probe,
|
||||
.id_table = fp9931_regulator_id_table,
|
||||
};
|
||||
module_platform_driver(fp9931_regulator_driver);
|
||||
|
||||
MODULE_DESCRIPTION("FP9931 voltage regulator driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
27
drivers/gpu/drm/rockchip/ebc-dev/pmic/fp9931.h
Normal file
27
drivers/gpu/drm/rockchip/ebc-dev/pmic/fp9931.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2024 Rockchip Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef FP9931_H
|
||||
#define FP9931_H
|
||||
|
||||
#define FP9931_TMST_VALUE 0x00
|
||||
#define FP9931_VCOM_SETTING 0x01
|
||||
#define FP9931_VPOS_VNEG_SETTING 0x02
|
||||
#define FP9931_PWRON_DELAY 0x03
|
||||
#define FP9931_CONTROL_REG1 0x0B
|
||||
#define FP9931_CONTROL_REG2 0x0C
|
||||
|
||||
#define VPOS_VNEG_SETTING GENMASK(5, 0)
|
||||
#define PWRON_DELAY_tDLY1 GENMASK(1, 0)
|
||||
#define PWRON_DELAY_tDLY2 GENMASK(3, 2)
|
||||
#define PWRON_DELAY_tDLY3 GENMASK(5, 4)
|
||||
#define PWRON_DELAY_tDLY4 GENMASK(7, 6)
|
||||
#define CONTROL_REG1_V3P3_EN BIT(1)
|
||||
#define CONTROL_REG1_SS_TIME GENMASK(7, 6)
|
||||
#define CONTROL_REG2_VN_CL GENMASK(1, 0)
|
||||
#define CONTROL_REG2_VP_CL GENMASK(3, 2)
|
||||
#define CONTROL_REG2_FIX_RD_PTR BIT(7)
|
||||
|
||||
#endif /* FP9931_H */
|
||||
Reference in New Issue
Block a user