On 02/05/16 13:17, Laxman Dewangan wrote:
> The IO pins of Tegra SoCs are grouped for common control of IO
> interface like setting voltage signal levels and power state of
> the interface. The group is generally referred as IO pads. The
> power state and voltage control of IO pins can be done at IO pads
> level.
> 
> Tegra generation SoC supports the power down of IO pads when it
> is not used even in the active state of system. This saves power
> from that IO interface.
> 
> Tegra generation SoC supports multiple voltage level in IO pins for
> interfacing on some of pads. Till Tegra124, the IO rail voltage was
> detected automatically and IO pads power voltage level sets accordingly.
> But from Tegra210, it is required to program by SW explicitly.
> 
> Add support to set the power states and voltage level of the IO pads
> from client driver. The implementation for the APIs are in generic
> which is applicable for all generation os Tegra SoC.
> 
> IO pads ID and information of bit field for power state and voltage
> level controls are added for Tegra210.
> 
> Signed-off-by: Laxman Dewangan <ldewan...@nvidia.com>
> 
> ---
> Changes from V1:
> This is reworked on earlier path to have separation between IO rails and
> io pads and add power state and voltage control APIs in single call.
> ---
>  drivers/soc/tegra/pmc.c | 182 
> +++++++++++++++++++++++++++++++++++++++++++++++-
>  include/soc/tegra/pmc.h |  87 +++++++++++++++++++++++
>  2 files changed, 268 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
> index fc4f7b2..b3be4b9 100644
> --- a/drivers/soc/tegra/pmc.c
> +++ b/drivers/soc/tegra/pmc.c
> @@ -76,6 +76,10 @@
>  
>  #define PMC_SCRATCH41                        0x140
>  
> +/* Power detect for pad voltage */
> +#define PMC_PWR_DET                  0x48
> +#define PMC_PWR_DET_VAL                      0xe4
> +
>  #define PMC_SENSOR_CTRL                      0x1b0
>  #define PMC_SENSOR_CTRL_SCRATCH_WRITE        BIT(2)
>  #define PMC_SENSOR_CTRL_ENABLE_RST   BIT(1)
> @@ -115,12 +119,19 @@ struct tegra_powergate {
>       unsigned int num_resets;
>  };
>  
> +struct tegra_io_pads_control {
> +     int pad_id;
> +     int dpd_bit_pos;
> +     int pwr_val_pos;
> +};
> +
>  struct tegra_pmc_soc {
>       unsigned int num_powergates;
>       const char *const *powergates;
>       unsigned int num_cpu_powergates;
>       const u8 *cpu_powergates;
> -
> +     const struct tegra_io_pads_control *io_pads_control;
> +     unsigned int num_io_pads;
>       bool has_tsense_reset;
>       bool has_gpu_clamps;
>  };
> @@ -196,6 +207,14 @@ static void tegra_pmc_writel(u32 value, unsigned long 
> offset)
>       writel(value, pmc->base + offset);
>  }
>  
> +static void tegra_pmc_read_modify_write(unsigned long offset, u32 mask, u32 
> val)
> +{
> +     u32 pmc_reg = tegra_pmc_readl(offset);
> +
> +     pmc_reg = (pmc_reg & ~mask) | (val & mask);
> +     tegra_pmc_writel(pmc_reg, offset);
> +}
> +
>  static inline bool tegra_powergate_state(int id)
>  {
>       if (id == TEGRA_POWERGATE_3D && pmc->soc->has_gpu_clamps)
> @@ -970,6 +989,165 @@ error:
>  }
>  EXPORT_SYMBOL(tegra_io_rail_power_off);
>  
> +#define TEGRA_IO_PADS_CONTROL(_pad, _dpd, _pwr)              \
> +{                                                    \
> +     .pad_id = (TEGRA_IO_PAD_##_pad),                \
> +     .dpd_bit_pos = (_dpd),                          \
> +     .pwr_val_pos = (_pwr),                          \
> +}
> +
> +struct tegra_io_pads_control tegra210_io_pads_control[] = {
> +     TEGRA_IO_PADS_CONTROL(CSIA, 0, -1),
> +     TEGRA_IO_PADS_CONTROL(CSIB, 1, -1),
> +     TEGRA_IO_PADS_CONTROL(DSI, 2, -1),
> +     TEGRA_IO_PADS_CONTROL(MIPI_BIAS, 3, -1),
> +     TEGRA_IO_PADS_CONTROL(PEX_BIAS, 4, -1),
> +     TEGRA_IO_PADS_CONTROL(PEX_CLK1, 5, -1),
> +     TEGRA_IO_PADS_CONTROL(PEX_CLK2, 6, -1),
> +     TEGRA_IO_PADS_CONTROL(USB0, 9, -1),
> +     TEGRA_IO_PADS_CONTROL(USB1, 10, -1),
> +     TEGRA_IO_PADS_CONTROL(USB2, 11, -1),
> +     TEGRA_IO_PADS_CONTROL(USB_BIAS, 12, -1),
> +     TEGRA_IO_PADS_CONTROL(UART, 14, -1),
> +     TEGRA_IO_PADS_CONTROL(AUDIO, 17, -1),
> +     TEGRA_IO_PADS_CONTROL(USB3, 18, -1),
> +     TEGRA_IO_PADS_CONTROL(HSIC, 19, -1),
> +     TEGRA_IO_PADS_CONTROL(DBG, 25, -1),
> +     TEGRA_IO_PADS_CONTROL(DEBUG_NONAO, 26, -1),
> +     TEGRA_IO_PADS_CONTROL(GPIO, 27, 21),
> +     TEGRA_IO_PADS_CONTROL(HDMI, 28, -1),
> +     TEGRA_IO_PADS_CONTROL(SDMMC1, 33, 12),
> +     TEGRA_IO_PADS_CONTROL(SDMMC3, 34, 13),
> +     TEGRA_IO_PADS_CONTROL(EMMC, 35, -1),
> +     TEGRA_IO_PADS_CONTROL(CAM, 36, -1),
> +     TEGRA_IO_PADS_CONTROL(EMMC2, 37, -1),
> +     TEGRA_IO_PADS_CONTROL(DSIB, 39, -1),
> +     TEGRA_IO_PADS_CONTROL(DSIC, 40, -1),
> +     TEGRA_IO_PADS_CONTROL(DSID, 41, -1),
> +     TEGRA_IO_PADS_CONTROL(CSIC, 42, -1),
> +     TEGRA_IO_PADS_CONTROL(CSID, 43, -1),
> +     TEGRA_IO_PADS_CONTROL(CSIE, 44, -1),
> +     TEGRA_IO_PADS_CONTROL(CSIF, 45, -1),
> +     TEGRA_IO_PADS_CONTROL(SPI, 46, -1),
> +     TEGRA_IO_PADS_CONTROL(SPI_HV, 47, 23),
> +     TEGRA_IO_PADS_CONTROL(DMIC, 50, -1),
> +     TEGRA_IO_PADS_CONTROL(DP, 51, -1),
> +     TEGRA_IO_PADS_CONTROL(LVDS, 57, -1),
> +     TEGRA_IO_PADS_CONTROL(AUDIO_HV, 61, 18),
> +};
> +
> +static int tegra_io_pads_to_dpd(const struct tegra_pmc_soc *soc, int pad_id)
> +{
> +     int i;
> +
> +     if (!soc || !soc->num_io_pads)
> +             return -EINVAL;
> +
> +     for (i = 0; i < soc->num_io_pads; ++i) {
> +             if (soc->io_pads_control[i].pad_id == pad_id)
> +                     return soc->io_pads_control[i].dpd_bit_pos;
> +     }
> +
> +     return -EINVAL;
> +}
> +
> +static int tegra_io_pads_to_power_val(const struct tegra_pmc_soc *soc,
> +                                   int pad_id)
> +{
> +     int i;
> +
> +     if (!soc || !soc->num_io_pads)
> +             return -EINVAL;
> +
> +     for (i = 0; i < soc->num_io_pads; ++i) {
> +             if (soc->io_pads_control[i].pad_id == pad_id)
> +                     return soc->io_pads_control[i].pwr_val_pos;
> +     }
> +
> +     return -EINVAL;
> +}
> +
> +int tegra_io_pads_power_enable(int io_pad_id)
> +{
> +     int dpd_bit;
> +
> +     dpd_bit = tegra_io_pads_to_dpd(pmc->soc, io_pad_id);
> +     if (dpd_bit < 0)
> +             return dpd_bit;
> +
> +     return tegra_io_rail_power_on(dpd_bit);

>From a readability standpoint the above seems weird because
tegra_io_pads_power_enable() takes an ID as the argument, translates it
to a bit value and passes it to tegra_io_rail_power_on() which also
takes an ID for the argument.

> +}
> +EXPORT_SYMBOL(tegra_io_pads_power_enable);
> +
> +int tegra_io_pads_power_disable(int io_pad_id)
> +{
> +     int dpd_bit;
> +
> +     dpd_bit = tegra_io_pads_to_dpd(pmc->soc, io_pad_id);
> +     if (dpd_bit < 0)
> +             return dpd_bit;
> +
> +     return tegra_io_rail_power_off(dpd_bit);
> +}
> +EXPORT_SYMBOL(tegra_io_pads_power_disable);
> +
> +int tegra_io_pads_power_is_enabled(int io_pad_id)
> +{
> +     unsigned long status_reg;
> +     u32 status;
> +     int dpd_bit;
> +
> +     dpd_bit = tegra_io_pads_to_dpd(pmc->soc, io_pad_id);
> +     if (dpd_bit < 0)
> +             return dpd_bit;
> +
> +     status_reg = (dpd_bit < 32) ? IO_DPD_STATUS : IO_DPD2_STATUS;
> +     status = tegra_pmc_readl(status_reg);
> +
> +     return !!(status & BIT(dpd_bit % 32));
> +}
> +EXPORT_SYMBOL(tegra_io_pads_power_is_enabled);
> +
> +int tegra_io_pads_configure_voltage(int io_pad_id, int io_volt_uv)
> +{
> +     int pwr_bit;
> +     u32 bval;
> +
> +     if ((io_volt_uv != 3300000) && (io_volt_uv != 1800000))
> +             return -EINVAL;
> +
> +     pwr_bit = tegra_io_pads_to_power_val(pmc->soc, io_pad_id);
> +     if (pwr_bit < 0)
> +             return pwr_bit;
> +
> +     bval = (io_volt_uv == 3300000) ? BIT(pwr_bit) : 0;
> +
> +     mutex_lock(&pmc->powergates_lock);
> +     tegra_pmc_read_modify_write(PMC_PWR_DET, BIT(pwr_bit), BIT(pwr_bit));
> +     tegra_pmc_read_modify_write(PMC_PWR_DET_VAL, BIT(pwr_bit), bval);
> +     mutex_unlock(&pmc->powergates_lock);

There are 4 instances of BIT(pwr_bit). May be we should do this once or
have tegra_io_pads_to_power_val() return the bit?

> +     usleep_range(100, 250);
> +
> +     return 0;
> +}
> +EXPORT_SYMBOL(tegra_io_pads_configure_voltage);
> +
> +int tegra_io_pads_get_configured_voltage(int io_pad_id)
> +{
> +     int pwr_bit;
> +     u32 pwr_det_val;
> +
> +     pwr_bit = tegra_io_pads_to_power_val(pmc->soc, io_pad_id);
> +     if (pwr_bit < 0)
> +             return pwr_bit;
> +
> +     pwr_det_val = tegra_pmc_readl(PMC_PWR_DET_VAL);
> +
> +     return (pwr_det_val & BIT(pwr_bit)) ? 3300000 : 1800000;
> +}
> +EXPORT_SYMBOL(tegra_io_pads_get_configured_voltage);
> +
>  #ifdef CONFIG_PM_SLEEP
>  enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void)
>  {
> @@ -1443,6 +1621,8 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
>       .powergates = tegra210_powergates,
>       .num_cpu_powergates = ARRAY_SIZE(tegra210_cpu_powergates),
>       .cpu_powergates = tegra210_cpu_powergates,
> +     .io_pads_control = tegra210_io_pads_control,
> +     .num_io_pads = ARRAY_SIZE(tegra210_io_pads_control),
>       .has_tsense_reset = true,
>       .has_gpu_clamps = true,
>  };
> diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h
> index e9e5347..79e38f5 100644
> --- a/include/soc/tegra/pmc.h
> +++ b/include/soc/tegra/pmc.h
> @@ -108,6 +108,58 @@ int tegra_pmc_cpu_remove_clamping(unsigned int cpuid);
>  #define TEGRA_IO_RAIL_LVDS   57
>  #define TEGRA_IO_RAIL_SYS_DDC        58
>  
> +/* TEGRA_IO_PAD: The IO pins of Tegra SoCs are grouped for common control
> + * of IO interface like setting voltage signal levels, power state of the
> + * interface. The group is generally referred as io-pads. The power and
> + * voltage control of IO pins are available at io-pads level.
> + * The following macros make the super list all IO pads found on Tegra SoC
> + * generations.
> + */
> +#define TEGRA_IO_PAD_AUDIO           0
> +#define TEGRA_IO_PAD_AUDIO_HV                1
> +#define TEGRA_IO_PAD_BB                      2
> +#define TEGRA_IO_PAD_CAM             3
> +#define TEGRA_IO_PAD_COMP            4
> +#define TEGRA_IO_PAD_CSIA            5
> +#define TEGRA_IO_PAD_CSIB            6
> +#define TEGRA_IO_PAD_CSIC            7
> +#define TEGRA_IO_PAD_CSID            8
> +#define TEGRA_IO_PAD_CSIE            9
> +#define TEGRA_IO_PAD_CSIF            10
> +#define TEGRA_IO_PAD_DBG             11
> +#define TEGRA_IO_PAD_DEBUG_NONAO     12
> +#define TEGRA_IO_PAD_DMIC            13
> +#define TEGRA_IO_PAD_DP                      14
> +#define TEGRA_IO_PAD_DSI             15
> +#define TEGRA_IO_PAD_DSIB            16
> +#define TEGRA_IO_PAD_DSIC            17
> +#define TEGRA_IO_PAD_DSID            18
> +#define TEGRA_IO_PAD_EMMC            19
> +#define TEGRA_IO_PAD_EMMC2           20
> +#define TEGRA_IO_PAD_GPIO            21
> +#define TEGRA_IO_PAD_HDMI            22
> +#define TEGRA_IO_PAD_HSIC            23
> +#define TEGRA_IO_PAD_HV                      24
> +#define TEGRA_IO_PAD_LVDS            25
> +#define TEGRA_IO_PAD_MIPI_BIAS               26
> +#define TEGRA_IO_PAD_NAND            27
> +#define TEGRA_IO_PAD_PEX_BIAS                28
> +#define TEGRA_IO_PAD_PEX_CLK1                29
> +#define TEGRA_IO_PAD_PEX_CLK2                30
> +#define TEGRA_IO_PAD_PEX_CNTRL               31
> +#define TEGRA_IO_PAD_SDMMC1          32
> +#define TEGRA_IO_PAD_SDMMC3          33
> +#define TEGRA_IO_PAD_SDMMC4          34
> +#define TEGRA_IO_PAD_SPI             35
> +#define TEGRA_IO_PAD_SPI_HV          36
> +#define TEGRA_IO_PAD_SYS_DDC         37
> +#define TEGRA_IO_PAD_UART            38
> +#define TEGRA_IO_PAD_USB0            39
> +#define TEGRA_IO_PAD_USB1            40
> +#define TEGRA_IO_PAD_USB2            41
> +#define TEGRA_IO_PAD_USB3            42
> +#define TEGRA_IO_PAD_USB_BIAS                43
> +
>  #ifdef CONFIG_ARCH_TEGRA
>  int tegra_powergate_is_powered(unsigned int id);
>  int tegra_powergate_power_on(unsigned int id);
> @@ -120,6 +172,16 @@ int tegra_powergate_sequence_power_up(unsigned int id, 
> struct clk *clk,
>  
>  int tegra_io_rail_power_on(unsigned int id);
>  int tegra_io_rail_power_off(unsigned int id);
> +
> +/* Power enable/disable of the IO pads */
> +int tegra_io_pads_power_enable(int io_pad_id);
> +int tegra_io_pads_power_disable(int io_pad_id);
> +int tegra_io_pads_power_is_enabled(int io_pad_id);

What I don't like here, is now we have two public APIs to do the same
job because tegra_io_pads_power_enable/disable() calls
tegra_io_rail_power_off/on() internally. Furthermore, the two APIs use
different ID definitions to accomplish the same job. This shouldn't be
necessary.

Jon


Reply via email to