android13/kernel-5.10/drivers/mfd/rk1000-core.c

178 lines
4.3 KiB
C

/*
* Driver for rockchip rk1000 core controller
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co.Ltd
* Author:
* Algea Cao <algea.cao@rock-chips.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; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/gpio/consumer.h>
#include <linux/mfd/core.h>
#define CTRL_ADC 0x00
#define ADC_OFF 0x88
#define CTRL_CODEC 0x01
#define CODEC_OFF 0x0d
#define CTRL_I2C 0x02
#define I2C_TIMEOUT_PERIOD 0x22
#define CTRL_TVE 0x03
#define TVE_OFF 0x00
struct rk1000 {
struct i2c_client *client;
struct device *dev;
struct regmap *regmap;
struct gpio_desc *io_reset;
struct clk *mclk;
};
static const struct mfd_cell rk1000_devs[] = {
{
.name = "rk1000-tve",
.of_compatible = "rockchip,rk1000-tve",
},
};
static const struct regmap_range rk1000_ctl_volatile_ranges[] = {
{ .range_min = 0x00, .range_max = 0x05 },
};
static const struct regmap_access_table rk1000_ctl_reg_table = {
.yes_ranges = rk1000_ctl_volatile_ranges,
.n_yes_ranges = ARRAY_SIZE(rk1000_ctl_volatile_ranges),
};
struct regmap_config rk1000_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.volatile_table = &rk1000_ctl_reg_table,
};
static int rk1000_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
bool uboot_logo;
int ret, val = 0;
struct rk1000 *rk1000 = devm_kzalloc(&client->dev, sizeof(*rk1000),
GFP_KERNEL);
if (!rk1000)
return -ENOMEM;
rk1000->client = client;
rk1000->dev = &client->dev;
rk1000->regmap = devm_regmap_init_i2c(client, &rk1000_regmap_config);
if (IS_ERR(rk1000->regmap))
return PTR_ERR(rk1000->regmap);
ret = regmap_read(rk1000->regmap, CTRL_TVE, &val);
/*
* If rk1000's registers can be read and rk1000 cvbs output is
* enabled, we think uboot logo is on.
*/
if (!ret && val & BIT(6))
uboot_logo = true;
else
uboot_logo = false;
if (!uboot_logo) {
/********Get reset pin***********/
rk1000->io_reset = devm_gpiod_get_optional(rk1000->dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(rk1000->io_reset)) {
dev_err(rk1000->dev, "can't get rk1000 reset gpio\n");
return PTR_ERR(rk1000->io_reset);
}
gpiod_set_value(rk1000->io_reset, 0);
usleep_range(500, 1000);
gpiod_set_value(rk1000->io_reset, 1);
usleep_range(500, 1000);
gpiod_set_value(rk1000->io_reset, 0);
}
rk1000->mclk = devm_clk_get(rk1000->dev, "mclk");
if (IS_ERR(rk1000->mclk)) {
dev_err(rk1000->dev, "get mclk err\n");
return PTR_ERR(rk1000->mclk);
}
ret = clk_prepare_enable(rk1000->mclk);
if (ret < 0) {
dev_err(rk1000->dev, "prepare mclk err\n");
goto clk_err;
}
regmap_write(rk1000->regmap, CTRL_ADC, ADC_OFF);
regmap_write(rk1000->regmap, CTRL_CODEC, CODEC_OFF);
regmap_write(rk1000->regmap, CTRL_I2C, I2C_TIMEOUT_PERIOD);
if (!uboot_logo)
regmap_write(rk1000->regmap, CTRL_TVE, TVE_OFF);
ret = mfd_add_devices(rk1000->dev, -1, rk1000_devs,
ARRAY_SIZE(rk1000_devs), NULL, 0, NULL);
if (ret) {
dev_err(rk1000->dev, "rk1000 mfd_add_devices failed\n");
goto mfd_add_err;
}
i2c_set_clientdata(client, rk1000);
dev_dbg(rk1000->dev, "rk1000 probe ok!\n");
return 0;
mfd_add_err:
mfd_remove_devices(rk1000->dev);
clk_err:
clk_disable_unprepare(rk1000->mclk);
return ret;
}
static int rk1000_remove(struct i2c_client *client)
{
struct rk1000 *rk1000 = i2c_get_clientdata(client);
clk_disable_unprepare(rk1000->mclk);
mfd_remove_devices(rk1000->dev);
return 0;
}
static const struct i2c_device_id rk1000_id[] = {
{ "rk1000-ctl", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, rk1000_id);
static const struct of_device_id rk1000_ctl_dt_ids[] = {
{ .compatible = "rockchip,rk1000-ctl" },
{ }
};
MODULE_DEVICE_TABLE(of, rk1000_ctl_dt_ids);
static struct i2c_driver rk1000_driver = {
.driver = {
.name = "rk1000-ctl",
},
.probe = rk1000_probe,
.remove = rk1000_remove,
.id_table = rk1000_id,
};
module_i2c_driver(rk1000_driver);
MODULE_DESCRIPTION("RK1000 core control driver");
MODULE_AUTHOR("Algea Cao <algea.cao@rock-chips.com>");
MODULE_LICENSE("GPL");