428 lines
9.8 KiB
C
428 lines
9.8 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* serdes-core.c -- Device access for different serdes chips
|
|
*
|
|
* Copyright (c) 2023-2028 Rockchip Electronics Co., Ltd.
|
|
*
|
|
* Author: luowei <lw@rock-chips.com>
|
|
*/
|
|
|
|
#include "core.h"
|
|
|
|
static const struct mfd_cell serdes_bu18tl82_devs[] = {
|
|
{
|
|
.name = "serdes-pinctrl",
|
|
.of_compatible = "rohm,bu18tl82-pinctrl",
|
|
},
|
|
{
|
|
.name = "serdes-bridge",
|
|
.of_compatible = "rohm,bu18tl82-bridge",
|
|
},
|
|
};
|
|
|
|
static const struct mfd_cell serdes_bu18rl82_devs[] = {
|
|
{
|
|
.name = "serdes-pinctrl",
|
|
.of_compatible = "rohm,bu18rl82-pinctrl",
|
|
},
|
|
{
|
|
.name = "serdes-bridge",
|
|
.of_compatible = "rohm,bu18rl82-bridge",
|
|
},
|
|
};
|
|
|
|
static const struct mfd_cell serdes_max96745_devs[] = {
|
|
{
|
|
.name = "serdes-pinctrl",
|
|
.of_compatible = "maxim,max96745-pinctrl",
|
|
},
|
|
{
|
|
.name = "serdes-bridge",
|
|
.of_compatible = "maxim,max96745-bridge",
|
|
},
|
|
{
|
|
.name = "serdes-bridge-split",
|
|
.of_compatible = "maxim,max96745-bridge-split",
|
|
},
|
|
};
|
|
|
|
static const struct mfd_cell serdes_max96755_devs[] = {
|
|
{
|
|
.name = "serdes-pinctrl",
|
|
.of_compatible = "maxim,max96755-pinctrl",
|
|
},
|
|
{
|
|
.name = "serdes-bridge",
|
|
.of_compatible = "maxim,max96755-bridge",
|
|
},
|
|
};
|
|
|
|
static const struct mfd_cell serdes_max96789_devs[] = {
|
|
{
|
|
.name = "serdes-pinctrl",
|
|
.of_compatible = "maxim,max96789-pinctrl",
|
|
},
|
|
{
|
|
.name = "serdes-bridge",
|
|
.of_compatible = "maxim,max96789-bridge",
|
|
},
|
|
{
|
|
.name = "serdes-bridge-split",
|
|
.of_compatible = "maxim,max96789-bridge-split",
|
|
},
|
|
};
|
|
|
|
static const struct mfd_cell serdes_max96752_devs[] = {
|
|
{
|
|
.name = "serdes-pinctrl",
|
|
.of_compatible = "maxim,max96752-pinctrl",
|
|
},
|
|
{
|
|
.name = "serdes-panel",
|
|
.of_compatible = "maxim,max96752-panel",
|
|
},
|
|
{
|
|
.name = "serdes-panel-split",
|
|
.of_compatible = "maxim,max96752-panel-split",
|
|
},
|
|
};
|
|
|
|
static const struct mfd_cell serdes_max96772_devs[] = {
|
|
{
|
|
.name = "serdes-pinctrl",
|
|
.of_compatible = "maxim,max96772-pinctrl",
|
|
},
|
|
{
|
|
.name = "serdes-panel",
|
|
.of_compatible = "maxim,max96772-panel",
|
|
},
|
|
};
|
|
|
|
static const struct mfd_cell serdes_rkx111_devs[] = {
|
|
{
|
|
.name = "serdes-pinctrl",
|
|
.of_compatible = "rockchip,rkx111-pinctrl",
|
|
},
|
|
{
|
|
.name = "serdes-bridge",
|
|
.of_compatible = "rockchip,rkx111-bridge",
|
|
},
|
|
};
|
|
|
|
static const struct mfd_cell serdes_rkx121_devs[] = {
|
|
{
|
|
.name = "serdes-pinctrl",
|
|
.of_compatible = "rockchip,rkx121-pinctrl",
|
|
},
|
|
{
|
|
.name = "serdes-bridge",
|
|
.of_compatible = "rockchip,rkx121-bridge",
|
|
},
|
|
};
|
|
|
|
static const struct mfd_cell serdes_nca9539_devs[] = {
|
|
{
|
|
.name = "serdes-pinctrl",
|
|
.of_compatible = "novo,nca9539-pinctrl",
|
|
},
|
|
};
|
|
|
|
/**
|
|
* serdes_reg_read: Read a single serdes register.
|
|
*
|
|
* @serdes: Device to read from.
|
|
* @reg: Register to read.
|
|
* @val: Data from register.
|
|
*/
|
|
int serdes_reg_read(struct serdes *serdes, unsigned int reg, unsigned int *val)
|
|
{
|
|
int ret;
|
|
|
|
ret = regmap_read(serdes->regmap, reg, val);
|
|
SERDES_DBG_I2C("%s %s %s Read Reg%04x %04x ret=%d\n", __func__, dev_name(serdes->dev),
|
|
serdes->chip_data->name, reg, *val, ret);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(serdes_reg_read);
|
|
|
|
/**
|
|
* serdes_bulk_read: Read multiple serdes registers
|
|
*
|
|
* @serdes: Device to read from
|
|
* @reg: First register
|
|
* @count: Number of registers
|
|
* @buf: Buffer to fill.
|
|
*/
|
|
int serdes_bulk_read(struct serdes *serdes, unsigned int reg,
|
|
int count, u16 *buf)
|
|
{
|
|
int i = 0, ret = 0;
|
|
|
|
ret = regmap_bulk_read(serdes->regmap, reg, buf, count);
|
|
for (i = 0; i < count; i++) {
|
|
SERDES_DBG_I2C("%s %s %s Read Reg%04x %04x ret=%d\n",
|
|
__func__, dev_name(serdes->dev),
|
|
serdes->chip_data->name, reg + i, buf[i], ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(serdes_bulk_read);
|
|
|
|
int serdes_bulk_write(struct serdes *serdes, unsigned int reg,
|
|
int count, void *src)
|
|
{
|
|
u16 *buf = src;
|
|
int i, ret;
|
|
|
|
WARN_ON(count <= 0);
|
|
|
|
mutex_lock(&serdes->io_lock);
|
|
for (i = 0; i < count; i++) {
|
|
ret = regmap_write(serdes->regmap, reg, buf[i]);
|
|
SERDES_DBG_I2C("%s %s %s Write Reg%04x %04x ret=%d\n",
|
|
__func__, dev_name(serdes->dev),
|
|
serdes->chip_data->name, reg, buf[i], ret);
|
|
if (ret != 0) {
|
|
mutex_unlock(&serdes->io_lock);
|
|
return ret;
|
|
}
|
|
}
|
|
mutex_unlock(&serdes->io_lock);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(serdes_bulk_write);
|
|
|
|
/**
|
|
* serdes_multi_reg_write: Write many serdes register.
|
|
*
|
|
* @serdes: Device to write to.
|
|
* @regs: Registers to write to.
|
|
* @num_regs: Number of registers to write.
|
|
*/
|
|
int serdes_multi_reg_write(struct serdes *serdes, const struct reg_sequence *regs,
|
|
int num_regs)
|
|
{
|
|
int i, ret;
|
|
|
|
SERDES_DBG_I2C("%s %s %s num=%d\n", __func__, dev_name(serdes->dev),
|
|
serdes->chip_data->name, num_regs);
|
|
ret = regmap_multi_reg_write(serdes->regmap, regs, num_regs);
|
|
for (i = 0; i < num_regs; i++) {
|
|
SERDES_DBG_I2C("serdes %s Write Reg%04x %04x ret=%d\n",
|
|
serdes->chip_data->name, regs[i].reg, regs[i].def, ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(serdes_multi_reg_write);
|
|
|
|
/**
|
|
* serdes_reg_write: Write a single serdes register.
|
|
*
|
|
* @serdes: Device to write to.
|
|
* @reg: Register to write to.
|
|
* @val: Value to write.
|
|
*/
|
|
int serdes_reg_write(struct serdes *serdes, unsigned int reg,
|
|
unsigned int val)
|
|
{
|
|
int ret;
|
|
|
|
ret = regmap_write(serdes->regmap, reg, val);
|
|
SERDES_DBG_I2C("%s %s %s Write Reg%04x %04x ret=%d\n", __func__, dev_name(serdes->dev),
|
|
serdes->chip_data->name, reg, val, ret);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(serdes_reg_write);
|
|
|
|
/**
|
|
* serdes_set_bits: Set the value of a bitfield in a serdes register
|
|
*
|
|
* @serdes: Device to write to.
|
|
* @reg: Register to write to.
|
|
* @mask: Mask of bits to set.
|
|
* @val: Value to set (unshifted)
|
|
*/
|
|
int serdes_set_bits(struct serdes *serdes, unsigned int reg,
|
|
unsigned int mask, unsigned int val)
|
|
{
|
|
int ret;
|
|
|
|
SERDES_DBG_I2C("%s %s %s Write Reg%04x %04x) mask=%04x\n", __func__,
|
|
dev_name(serdes->dev), serdes->chip_data->name, reg, val, mask);
|
|
ret = regmap_update_bits(serdes->regmap, reg, mask, val);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(serdes_set_bits);
|
|
|
|
/*
|
|
* Instantiate the generic non-control parts of the device.
|
|
*/
|
|
int serdes_device_init(struct serdes *serdes)
|
|
{
|
|
struct serdes_chip_data *chip_data = serdes->chip_data;
|
|
int ret = 0;
|
|
const struct mfd_cell *serdes_devs = NULL;
|
|
int mfd_num = 0;
|
|
|
|
switch (chip_data->serdes_id) {
|
|
case ROHM_ID_BU18TL82:
|
|
serdes_devs = serdes_bu18tl82_devs;
|
|
mfd_num = ARRAY_SIZE(serdes_bu18tl82_devs);
|
|
break;
|
|
case ROHM_ID_BU18RL82:
|
|
serdes_devs = serdes_bu18rl82_devs;
|
|
mfd_num = ARRAY_SIZE(serdes_bu18rl82_devs);
|
|
break;
|
|
case MAXIM_ID_MAX96745:
|
|
serdes_devs = serdes_max96745_devs;
|
|
mfd_num = ARRAY_SIZE(serdes_max96745_devs);
|
|
break;
|
|
case MAXIM_ID_MAX96752:
|
|
serdes_devs = serdes_max96752_devs;
|
|
mfd_num = ARRAY_SIZE(serdes_max96752_devs);
|
|
break;
|
|
case MAXIM_ID_MAX96755:
|
|
serdes_devs = serdes_max96755_devs;
|
|
mfd_num = ARRAY_SIZE(serdes_max96755_devs);
|
|
break;
|
|
case MAXIM_ID_MAX96772:
|
|
serdes_devs = serdes_max96772_devs;
|
|
mfd_num = ARRAY_SIZE(serdes_max96772_devs);
|
|
break;
|
|
case MAXIM_ID_MAX96789:
|
|
serdes_devs = serdes_max96789_devs;
|
|
mfd_num = ARRAY_SIZE(serdes_max96789_devs);
|
|
break;
|
|
case ROCKCHIP_ID_RKX111:
|
|
serdes_devs = serdes_rkx111_devs;
|
|
mfd_num = ARRAY_SIZE(serdes_rkx111_devs);
|
|
break;
|
|
case ROCKCHIP_ID_RKX121:
|
|
serdes_devs = serdes_rkx121_devs;
|
|
mfd_num = ARRAY_SIZE(serdes_rkx121_devs);
|
|
break;
|
|
case NOVO_ID_NCA9539:
|
|
serdes_devs = serdes_nca9539_devs;
|
|
mfd_num = ARRAY_SIZE(serdes_nca9539_devs);
|
|
break;
|
|
default:
|
|
dev_info(serdes->dev, "%s: unknown device\n", __func__);
|
|
break;
|
|
}
|
|
|
|
ret = devm_mfd_add_devices(serdes->dev, PLATFORM_DEVID_AUTO, serdes_devs,
|
|
mfd_num, NULL, 0, NULL);
|
|
if (ret != 0) {
|
|
dev_err(serdes->dev, "Failed to add serdes children\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(serdes_device_init);
|
|
|
|
int serdes_set_pinctrl_default(struct serdes *serdes)
|
|
{
|
|
int ret = 0;
|
|
|
|
if ((!IS_ERR(serdes->pinctrl_node)) && (!IS_ERR(serdes->pins_init))) {
|
|
ret = pinctrl_select_state(serdes->pinctrl_node, serdes->pins_init);
|
|
if (ret)
|
|
dev_err(serdes->dev, "could not set init pins\n");
|
|
SERDES_DBG_MFD("%s: name=%s init\n", __func__, dev_name(serdes->dev));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(serdes_set_pinctrl_default);
|
|
|
|
int serdes_set_pinctrl_sleep(struct serdes *serdes)
|
|
{
|
|
int ret = 0;
|
|
|
|
if ((!IS_ERR(serdes->pinctrl_node)) && (!IS_ERR(serdes->pins_sleep))) {
|
|
ret = pinctrl_select_state(serdes->pinctrl_node, serdes->pins_sleep);
|
|
if (ret)
|
|
dev_err(serdes->dev, "could not set sleep pins\n");
|
|
SERDES_DBG_MFD("%s: name=%s\n", __func__, dev_name(serdes->dev));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(serdes_set_pinctrl_sleep);
|
|
|
|
int serdes_device_suspend(struct serdes *serdes)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!IS_ERR(serdes->vpower)) {
|
|
ret = regulator_disable(serdes->vpower);
|
|
if (ret) {
|
|
dev_err(serdes->dev, "fail to disable vpower regulator\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(serdes_device_suspend);
|
|
|
|
int serdes_device_resume(struct serdes *serdes)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!IS_ERR(serdes->vpower)) {
|
|
ret = regulator_enable(serdes->vpower);
|
|
if (ret) {
|
|
dev_err(serdes->dev, "fail to enable vpower regulator\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(serdes_device_resume);
|
|
|
|
void serdes_device_poweroff(struct serdes *serdes)
|
|
{
|
|
int ret = 0;
|
|
|
|
if ((!IS_ERR(serdes->pinctrl_node)) && (!IS_ERR(serdes->pins_sleep))) {
|
|
ret = pinctrl_select_state(serdes->pinctrl_node, serdes->pins_sleep);
|
|
if (ret)
|
|
dev_err(serdes->dev, "could not set sleep pins\n");
|
|
}
|
|
|
|
if (!IS_ERR(serdes->vpower)) {
|
|
ret = regulator_disable(serdes->vpower);
|
|
if (ret)
|
|
dev_err(serdes->dev, "fail to disable vpower regulator\n");
|
|
}
|
|
|
|
}
|
|
EXPORT_SYMBOL_GPL(serdes_device_poweroff);
|
|
|
|
int serdes_device_shutdown(struct serdes *serdes)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!IS_ERR(serdes->vpower)) {
|
|
ret = regulator_disable(serdes->vpower);
|
|
if (ret) {
|
|
dev_err(serdes->dev, "fail to disable vpower regulator\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(serdes_device_shutdown);
|
|
|
|
MODULE_LICENSE("GPL");
|