Jetpack/kernel/nvidia/drivers/padctrl/padctrl-tegra-io-pads.c

255 lines
6.5 KiB
C

/*
* Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <dt-bindings/soc/tegra-io-pads.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/padctrl/padctrl.h>
#include <linux/slab.h>
#include <soc/tegra/pmc.h>
struct tegra_io_pad_info {
const char *name;
int id;
};
#define TEGRA_IO_PAD_INFO(_id, _name) \
{ \
.name = #_name, \
.id = TEGRA_IO_PAD_GROUP_##_id, \
}
static struct tegra_io_pad_info tegra_io_pads_info[] = {
TEGRA_IO_PAD_INFO(AUDIO, audio),
TEGRA_IO_PAD_INFO(AUDIO_HV, audio-hv),
TEGRA_IO_PAD_INFO(CAM, cam),
TEGRA_IO_PAD_INFO(CONN, conn),
TEGRA_IO_PAD_INFO(CSIA, csia),
TEGRA_IO_PAD_INFO(CSIB, csib),
TEGRA_IO_PAD_INFO(CSIC, csic),
TEGRA_IO_PAD_INFO(CSID, csid),
TEGRA_IO_PAD_INFO(CSIE, csie),
TEGRA_IO_PAD_INFO(CSIF, csif),
TEGRA_IO_PAD_INFO(DBG, dbg),
TEGRA_IO_PAD_INFO(DEBUG_NONAO, debug-nonao),
TEGRA_IO_PAD_INFO(DMIC, dmic),
TEGRA_IO_PAD_INFO(DMIC_HV, dmic-hv),
TEGRA_IO_PAD_INFO(DP, dp),
TEGRA_IO_PAD_INFO(DSI, dsi),
TEGRA_IO_PAD_INFO(DSIB, dsib),
TEGRA_IO_PAD_INFO(DSIC, dsic),
TEGRA_IO_PAD_INFO(DSID, dsid),
TEGRA_IO_PAD_INFO(EMMC, emmc),
TEGRA_IO_PAD_INFO(EMMC2, emmc2),
TEGRA_IO_PAD_INFO(GPIO, gpio),
TEGRA_IO_PAD_INFO(EDP, edp),
TEGRA_IO_PAD_INFO(HDMI, hdmi),
TEGRA_IO_PAD_INFO(HDMI_DP0, hdmi-dp0),
TEGRA_IO_PAD_INFO(HDMI_DP1, hdmi-dp1),
TEGRA_IO_PAD_INFO(HSIC, hsic),
TEGRA_IO_PAD_INFO(LVDS, lvds),
TEGRA_IO_PAD_INFO(MIPI_BIAS, mipi-bias),
TEGRA_IO_PAD_INFO(PEX_BIAS, pex-bias),
TEGRA_IO_PAD_INFO(PEX_CLK_BIAS, pex-clk-bias),
TEGRA_IO_PAD_INFO(PEX_CLK1, pex-clk1),
TEGRA_IO_PAD_INFO(PEX_CLK2, pex-clk2),
TEGRA_IO_PAD_INFO(PEX_CLK3, pex-clk3),
TEGRA_IO_PAD_INFO(PEX_CTRL, pex-ctrl),
TEGRA_IO_PAD_INFO(SDMMC1, sdmmc1),
TEGRA_IO_PAD_INFO(SDMMC1_HV, sdmmc1-hv),
TEGRA_IO_PAD_INFO(SDMMC2_HV, sdmmc2-hv),
TEGRA_IO_PAD_INFO(SDMMC3, sdmmc3),
TEGRA_IO_PAD_INFO(SDMMC3_HV, sdmmc3-hv),
TEGRA_IO_PAD_INFO(SDMMC4, sdmmc4),
TEGRA_IO_PAD_INFO(SPI, spi),
TEGRA_IO_PAD_INFO(SPI_HV, spi-hv),
TEGRA_IO_PAD_INFO(UART, uart),
TEGRA_IO_PAD_INFO(USB_BIAS, usb-bias),
TEGRA_IO_PAD_INFO(USB0, usb0),
TEGRA_IO_PAD_INFO(USB1, usb1),
TEGRA_IO_PAD_INFO(USB2, usb2),
TEGRA_IO_PAD_INFO(USB3, usb3),
TEGRA_IO_PAD_INFO(BB, bb),
TEGRA_IO_PAD_INFO(SYS, sys),
TEGRA_IO_PAD_INFO(HV, hv),
TEGRA_IO_PAD_INFO(UFS, ufs),
TEGRA_IO_PAD_INFO(AO_HV, ao-hv),
TEGRA_IO_PAD_INFO(DDR_DVI, ddr_dvi),
TEGRA_IO_PAD_INFO(DDR_GMI, ddr-gmi),
TEGRA_IO_PAD_INFO(DDR_SDMMC2, ddr-sdmmc2),
TEGRA_IO_PAD_INFO(DDR_SPI, ddr-spi),
TEGRA_IO_PAD_INFO(HDMI_DP2, hdmi-dp2),
TEGRA_IO_PAD_INFO(HDMI_DP3, hdmi-dp3),
};
struct tegra_io_pads_padcontrol {
struct device *dev;
struct padctrl_dev *pad_dev;
};
static struct tegra_io_pad_info *tegra_get_io_pad_info(int id)
{
int i;
for (i = 0; i < ARRAY_SIZE(tegra_io_pads_info); ++i) {
if (tegra_io_pads_info[i].id == id)
break;
}
if (i == ARRAY_SIZE(tegra_io_pads_info))
return NULL;
return &tegra_io_pads_info[i];
}
static int tegra_io_pad_set_voltage(struct padctrl_dev *pad_dev,
int id, u32 voltage)
{
struct tegra_io_pads_padcontrol *padctrl = padctrl_get_drvdata(pad_dev);
struct tegra_io_pad_info *pad;
u32 curr_volt;
int ret;
switch (voltage) {
case 1200000:
case 1800000:
case 3300000:
break;
default:
dev_err(padctrl->dev, "Pad voltage %u is not valid\n", voltage);
return -EINVAL;
}
pad = tegra_get_io_pad_info(id);
if (!pad) {
dev_err(padctrl->dev, "Pad ID %d not identified\n", id);
return -EINVAL;
}
ret = tegra_pmc_io_pad_get_voltage(pad->name);
if (ret < 0)
return ret;
curr_volt = ret;
if (voltage == curr_volt)
return 0;
ret = tegra_pmc_io_pad_set_voltage(pad->name, voltage);
if (!ret)
udelay(100);
return ret;
}
static int tegra_io_pad_get_voltage(struct padctrl_dev *pad_dev,
int id, u32 *voltage)
{
struct tegra_io_pads_padcontrol *padctrl = padctrl_get_drvdata(pad_dev);
struct tegra_io_pad_info *pad;
int ret;
pad = tegra_get_io_pad_info(id);
if (!pad) {
dev_err(padctrl->dev, "Pad ID %d not identified\n", id);
return -EINVAL;
}
ret = tegra_pmc_io_pad_get_voltage(pad->name);
if (ret < 0)
return ret;
*voltage = ret;
return 0;
}
static int tegra_io_pad_set_power(struct padctrl_dev *pad_dev,
int id, u32 enable)
{
struct tegra_io_pads_padcontrol *padctrl = padctrl_get_drvdata(pad_dev);
struct tegra_io_pad_info *pad;
pad = tegra_get_io_pad_info(id);
if (!pad) {
dev_err(padctrl->dev, "Pad ID %d not identified\n", id);
return -EINVAL;
}
if (enable)
return tegra_pmc_io_pad_low_power_disable(pad->name);
return tegra_pmc_io_pad_low_power_enable(pad->name);
}
static int tegra_io_pad_power_enable(struct padctrl_dev *pad_dev, int id)
{
return tegra_io_pad_set_power(pad_dev, id, 1);
}
static int tegra_io_pad_power_disable(struct padctrl_dev *pad_dev,
int id)
{
return tegra_io_pad_set_power(pad_dev, id, 0);
}
static struct padctrl_ops tegra_io_pad_padctrl_ops = {
.set_voltage = &tegra_io_pad_set_voltage,
.get_voltage = &tegra_io_pad_get_voltage,
.power_enable = &tegra_io_pad_power_enable,
.power_disable = &tegra_io_pad_power_disable,
};
static struct padctrl_desc tegra_io_pads_padctrl_desc = {
.name = "tegra-pmc-padctrl",
.ops = &tegra_io_pad_padctrl_ops,
};
int tegra_io_pads_padctrl_init(struct device *dev)
{
struct tegra_io_pads_padcontrol *padctrl;
struct padctrl_config config = { };
struct device_node *pad_np;
int ret;
padctrl = devm_kzalloc(dev, sizeof(*padctrl), GFP_KERNEL);
if (!padctrl)
return -ENOMEM;
config.of_node = dev->of_node;
padctrl->dev = dev;
padctrl->pad_dev = padctrl_register(dev, &tegra_io_pads_padctrl_desc,
&config);
if (IS_ERR(padctrl->pad_dev)) {
ret = PTR_ERR(padctrl->pad_dev);
dev_err(dev, "Failed to register padcontrol: %d\n", ret);
return ret;
}
padctrl_set_drvdata(padctrl->pad_dev, padctrl);
/* Clear all DPD */
if (of_property_read_bool(dev->of_node, "clear-all-io-pads-dpd"))
tegra_pmc_io_dpd_clear();
pad_np = of_get_child_by_name(dev->of_node, "io-pad-defaults");
if (pad_np) {
dev_warn(dev, "WARNING: IO pad defaults is supported via pinctrl\n");
WARN_ON(1);
return 0;
}
dev_info(dev, "IO padctrl driver initialized\n");
return 0;
}