[U-Boot] [PATCH] Tegra30: Cardhu: Add pad config tables/code based on pinmux code

Pad config registers exist in APB_MISC_GP space, and control slew rate, drive strengh, schmidt, high-speed, and low-power modes for all of the pingroups in Tegra30. This builds off of the pinmux way of constructing init tables to configure select pads (SDIOCFG, for instance) during pinmux_init().
Currently, only SDIO1CFG is changed as per the TRM to work with the SD-card slot on Cardhu.
Thanks to StephenW for the suggestion/original idea.
Signed-off-by: Tom Warren twarren@nvidia.com --- arch/arm/cpu/tegra30-common/pinmux.c | 210 +++++++++++++++++++++--- arch/arm/include/asm/arch-tegra30/gp_padctrl.h | 8 +- arch/arm/include/asm/arch-tegra30/pinmux.h | 68 ++++++++- board/nvidia/cardhu/cardhu.c | 6 +- board/nvidia/cardhu/pinmux-config-cardhu.h | 21 ++- 5 files changed, 278 insertions(+), 35 deletions(-)
diff --git a/arch/arm/cpu/tegra30-common/pinmux.c b/arch/arm/cpu/tegra30-common/pinmux.c index 9ba0ec8..5a5862a 100644 --- a/arch/arm/cpu/tegra30-common/pinmux.c +++ b/arch/arm/cpu/tegra30-common/pinmux.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2010-2013, 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, @@ -39,6 +39,19 @@ struct tegra_pingroup_desc { #define PMUX_LOCK_SHIFT 7 #define PMUX_IO_RESET_SHIFT 8
+#define PGRP_HSM_SHIFT 2 +#define PGRP_SCHMT_SHIFT 3 +#define PGRP_LPMD_SHIFT 4 +#define PGRP_LPMD_MASK (3 << PGRP_LPMD_SHIFT) +#define PGRP_DRVDN_SHIFT 12 +#define PGRP_DRVDN_MASK (0x7F << PGRP_DRVDN_SHIFT) +#define PGRP_DRVUP_SHIFT 20 +#define PGRP_DRVUP_MASK (0x7F << PGRP_DRVUP_SHIFT) +#define PGRP_SLWR_SHIFT 28 +#define PGRP_SLWR_MASK (3 << PGRP_SLWR_SHIFT) +#define PGRP_SLWF_SHIFT 30 +#define PGRP_SLWF_MASK (3 << PGRP_SLWF_SHIFT) + /* Convenient macro for defining pin group properties */ #define PIN(pg_name, vdd, f0, f1, f2, f3, iod) \ { \ @@ -506,28 +519,177 @@ void pinmux_config_table(struct pingroup_config *config, int len) pinmux_config_pingroup(&config[i]); }
-void pinmux_sdio_pad_cfg(void) +static int padgrp_set_drvup_slwf(enum pdrive_pingrp pad, + int slwf) +{ + struct pmux_tri_ctlr *pmt = + (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; + u32 *pad_slwf = &pmt->pmt_drive[pad]; + u32 reg; + + /* Error check on pad and slwf */ + assert(pmux_padgrp_isvalid(pad)); + assert(pmux_pad_slw_isvalid(slwf)); + + /* NONE means unspecified/do not change/use POR value */ + if (slwf == PGRP_SLWF_NONE) + return 0; + + reg = readl(pad_slwf); + reg &= ~PGRP_SLWF_MASK; + reg |= (slwf << PGRP_SLWF_SHIFT); + writel(reg, pad_slwf); + + return 0; +} + +static int padgrp_set_drvdn_slwr(enum pdrive_pingrp pad, int slwr) +{ + struct pmux_tri_ctlr *pmt = + (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; + u32 *pad_slwr = &pmt->pmt_drive[pad]; + u32 reg; + + /* Error check on pad and slwr */ + assert(pmux_padgrp_isvalid(pad)); + assert(pmux_pad_slw_isvalid(slwr)); + + /* NONE means unspecified/do not change/use POR value */ + if (slwr == PGRP_SLWR_NONE) + return 0; + + reg = readl(pad_slwr); + reg &= ~PGRP_SLWR_MASK; + reg |= (slwr << PGRP_SLWR_SHIFT); + writel(reg, pad_slwr); + + return 0; +} + +static int padgrp_set_drvup(enum pdrive_pingrp pad, int drvup) +{ + struct pmux_tri_ctlr *pmt = + (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; + u32 *pad_drvup = &pmt->pmt_drive[pad]; + u32 reg; + + /* Error check on pad and drvup */ + assert(pmux_padgrp_isvalid(pad)); + assert(pmux_pad_drv_isvalid(drvup)); + + /* NONE means unspecified/do not change/use POR value */ + if (drvup == PGRP_DRVUP_NONE) + return 0; + + reg = readl(pad_drvup); + reg &= ~PGRP_DRVUP_MASK; + reg |= (drvup << PGRP_DRVUP_SHIFT); + writel(reg, pad_drvup); + + return 0; +} + +static int padgrp_set_drvdn(enum pdrive_pingrp pad, int drvdn) +{ + struct pmux_tri_ctlr *pmt = + (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; + u32 *pad_drvdn = &pmt->pmt_drive[pad]; + u32 reg; + + /* Error check on pad and drvdn */ + assert(pmux_padgrp_isvalid(pad)); + assert(pmux_pad_drv_isvalid(drvdn)); + + /* NONE means unspecified/do not change/use POR value */ + if (drvdn == PGRP_DRVDN_NONE) + return 0; + + reg = readl(pad_drvdn); + reg &= ~PGRP_DRVDN_MASK; + reg |= (drvdn << PGRP_DRVDN_SHIFT); + writel(reg, pad_drvdn); + + return 0; +} + +static int padgrp_set_lpmd(enum pdrive_pingrp pad, enum pgrp_lpmd lpmd) +{ + struct pmux_tri_ctlr *pmt = + (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; + u32 *pad_lpmd = &pmt->pmt_drive[pad]; + u32 reg; + + /* Error check pad and lpmd value */ + assert(pmux_padgrp_isvalid(pad)); + assert(pmux_pad_lpmd_isvalid(lpmd)); + + /* NONE means unspecified/do not change/use POR value */ + if (lpmd == PGRP_LPMD_NONE) + return 0; + + reg = readl(pad_lpmd); + reg &= ~PGRP_LPMD_MASK; + reg |= (lpmd << PGRP_LPMD_SHIFT); + writel(reg, pad_lpmd); + + return 0; +} + +static int padgrp_set_schmt(enum pdrive_pingrp pad, enum pgrp_schmt schmt) { - struct apb_misc_gp_ctlr *const gpc = - (struct apb_misc_gp_ctlr *)NV_PA_APB_MISC_GP_BASE; - u32 val, padcfg, padmask; - - /* - * Set pads as per T30 TRM, section 24.6.1.2. - * Slam both SDIO1CFG and SDIO3CFG in the APB_MISC_GP space - * since we can't parse the MMC cfg this early. - */ - padcfg = (GP_SDIOCFG_DRVUP_SLWF | GP_SDIOCFG_DRVDN_SLWR | \ - GP_SDIOCFG_DRVUP | GP_SDIOCFG_DRVDN); - padmask = 0x00000FFF; - - val = readl(&gpc->sdio1cfg); - val &= padmask; - val |= padcfg; - writel(val, &gpc->sdio1cfg); - - val = readl(&gpc->sdio3cfg); - val &= padmask; - val |= padcfg; - writel(val, &gpc->sdio3cfg); + struct pmux_tri_ctlr *pmt = + (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; + u32 *pad_schmt = &pmt->pmt_drive[pad]; + u32 reg; + + /* Error check pad */ + assert(pmux_padgrp_isvalid(pad)); + + reg = readl(pad_schmt); + reg &= ~(1 << PGRP_SCHMT_SHIFT); + if (schmt == PGRP_SCHMT_ENABLE) + reg |= (0x1 << PGRP_SCHMT_SHIFT); + writel(reg, pad_schmt); + + return 0; +} +static int padgrp_set_hsm(enum pdrive_pingrp pad, + enum pgrp_hsm hsm) +{ + struct pmux_tri_ctlr *pmt = + (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; + u32 *pad_hsm = &pmt->pmt_drive[pad]; + u32 reg; + + /* Error check pad */ + assert(pmux_padgrp_isvalid(pad)); + + reg = readl(pad_hsm); + reg &= ~(1 << PGRP_HSM_SHIFT); + if (hsm == PGRP_HSM_ENABLE) + reg |= (0x1 << PGRP_HSM_SHIFT); + writel(reg, pad_hsm); + + return 0; +} + +void padctrl_config_pingroup(struct padctrl_config *config) +{ + enum pdrive_pingrp pad = config->padgrp; + + padgrp_set_drvup_slwf(pad, config->slwf); + padgrp_set_drvdn_slwr(pad, config->slwr); + padgrp_set_drvup(pad, config->drvup); + padgrp_set_drvdn(pad, config->drvdn); + padgrp_set_lpmd(pad, config->lpmd); + padgrp_set_schmt(pad, config->schmt); + padgrp_set_hsm(pad, config->hsm); +} + +void padgrp_config_table(struct padctrl_config *config, int len) +{ + int i; + + for (i = 0; i < len; i++) + padctrl_config_pingroup(&config[i]); } diff --git a/arch/arm/include/asm/arch-tegra30/gp_padctrl.h b/arch/arm/include/asm/arch-tegra30/gp_padctrl.h index 48b9a3b..23d184f 100644 --- a/arch/arm/include/asm/arch-tegra30/gp_padctrl.h +++ b/arch/arm/include/asm/arch-tegra30/gp_padctrl.h @@ -57,9 +57,9 @@ struct apb_misc_gp_ctlr { };
/* SDMMC1/3 settings from section 24.6 of T30 TRM */ -#define GP_SDIOCFG_DRVUP_SLWF (1 << 30) -#define GP_SDIOCFG_DRVDN_SLWR (1 << 28) -#define GP_SDIOCFG_DRVUP (0x2E << 20) -#define GP_SDIOCFG_DRVDN (0x2A << 12) +#define SDIOCFG_DRVUP_SLWF 1 +#define SDIOCFG_DRVDN_SLWR 1 +#define SDIOCFG_DRVUP 0x2E +#define SDIOCFG_DRVDN 0x2A
#endif /* _TEGRA30_GP_PADCTRL_H_ */ diff --git a/arch/arm/include/asm/arch-tegra30/pinmux.h b/arch/arm/include/asm/arch-tegra30/pinmux.h index 0a341b4..a9e1b46 100644 --- a/arch/arm/include/asm/arch-tegra30/pinmux.h +++ b/arch/arm/include/asm/arch-tegra30/pinmux.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2010-2013, 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, @@ -531,6 +531,63 @@ enum pmux_vddio { PMUX_VDDIO_NONE };
+#define PGRP_SLWF_NONE -1 +#define PGRP_SLWF_MAX 3 +#define PGRP_SLWR_NONE PGRP_SLWF_NONE +#define PGRP_SLWR_MAX PGRP_SLWF_MAX + +#define PGRP_DRVUP_NONE -1 +#define PGRP_DRVUP_MAX 127 +#define PGRP_DRVDN_NONE PGRP_DRVUP_NONE +#define PGRP_DRVDN_MAX PGRP_DRVUP_MAX + +/* return 1 if a padgrp is in range */ +#define pmux_padgrp_isvalid(pd) (((pd) >= 0) && ((pd) < PDRIVE_PINGROUP_COUNT)) + +/* return 1 if a slew-rate rising/falling edge value is in range */ +#define pmux_pad_slw_isvalid(slw) (((slw) >= 0) && ((slw) <= PGRP_SLWF_MAX)) + +/* return 1 if a driver output pull-up/down strength code value is in range */ +#define pmux_pad_drv_isvalid(drv) (((drv) >= 0) && ((drv) <= PGRP_DRVUP_MAX)) + +/* return 1 if a low-power mode value is in range */ +#define pmux_pad_lpmd_isvalid(lpm) (((lpm) >= 0) && ((lpm) <= PGRP_LPMD_X)) + +/* Defines a pin group cfg's low-power mode select */ +enum pgrp_lpmd { + PGRP_LPMD_X8 = 0, + PGRP_LPMD_X4, + PGRP_LPMD_X2, + PGRP_LPMD_X, + PGRP_LPMD_NONE = -1, +}; + +/* Defines whether a pin group cfg's schmidt is enabled or not */ +enum pgrp_schmt { + PGRP_SCHMT_DISABLE = 0, + PGRP_SCHMT_ENABLE = 1, +}; + +/* Defines whether a pin group cfg's high-speed mode is enabled or not */ +enum pgrp_hsm { + PGRP_HSM_DISABLE = 0, + PGRP_HSM_ENABLE = 1, +}; + +/* + * This defines the configuration for a pin group's pad control config + */ +struct padctrl_config { + enum pdrive_pingrp padgrp; /* pin group PDRIVE_PINGRP_x */ + int slwf; /* falling edge slew */ + int slwr; /* rising edge slew */ + int drvup; /* pull-up drive strength */ + int drvdn; /* pull-down drive strength */ + enum pgrp_lpmd lpmd; /* low-power mode selection */ + enum pgrp_schmt schmt; /* schmidt enable */ + enum pgrp_hsm hsm; /* high-speed mode enable */ +}; + /* t30 pin drive group and pin mux registers */ #define PDRIVE_PINGROUP_OFFSET (0x868 >> 2) #define PMUX_OFFSET ((0x3000 >> 2) - PDRIVE_PINGROUP_OFFSET - \ @@ -600,7 +657,12 @@ void pinmux_config_table(struct pingroup_config *config, int len); /* Set a group of pins from a table */ void pinmux_init(void);
-/* Set the GP pad cfg as per the TRM for SDIO */ -void pinmux_sdio_pad_cfg(void); +/** + * Set the GP pad configs + * + * @param config List of config items + * @param len Number of config items in list + */ +void padgrp_config_table(struct padctrl_config *config, int len);
#endif /* _TEGRA30_PINMUX_H_ */ diff --git a/board/nvidia/cardhu/cardhu.c b/board/nvidia/cardhu/cardhu.c index ae9afba..3544b41 100644 --- a/board/nvidia/cardhu/cardhu.c +++ b/board/nvidia/cardhu/cardhu.c @@ -1,5 +1,5 @@ /* - * (C) Copyright 2010-2012 + * (C) Copyright 2010-2013 * NVIDIA Corporation <www.nvidia.com> * * See file CREDITS for list of people who contributed to this @@ -23,6 +23,7 @@
#include <common.h> #include <asm/arch/pinmux.h> +#include <asm/arch/gp_padctrl.h> #include "pinmux-config-cardhu.h" #include <i2c.h>
@@ -41,7 +42,8 @@ void pinmux_init(void) pinmux_config_table(unused_pins_lowpower, ARRAY_SIZE(unused_pins_lowpower));
- pinmux_sdio_pad_cfg(); + /* Initialize any non-default pad configs (APB_MISC_GP regs) */ + padgrp_config_table(cardhu_padctrl, ARRAY_SIZE(cardhu_padctrl)); }
#if defined(CONFIG_TEGRA_MMC) diff --git a/board/nvidia/cardhu/pinmux-config-cardhu.h b/board/nvidia/cardhu/pinmux-config-cardhu.h index 8428bba..51d2b94 100644 --- a/board/nvidia/cardhu/pinmux-config-cardhu.h +++ b/board/nvidia/cardhu/pinmux-config-cardhu.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2010-2013, 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, @@ -53,6 +53,18 @@ .ioreset = PMUX_PIN_IO_RESET_##_ioreset \ }
+#define DEFAULT_PADCFG(_padgrp, _slwf, _slwr, _drvup, _drvdn, _lpmd, _schmt, _hsm) \ + { \ + .padgrp = PDRIVE_PINGROUP_##_padgrp, \ + .slwf = _slwf, \ + .slwr = _slwr, \ + .drvup = _drvup, \ + .drvdn = _drvdn, \ + .lpmd = PGRP_LPMD_##_lpmd, \ + .schmt = PGRP_SCHMT_##_schmt, \ + .hsm = PGRP_HSM_##_hsm, \ + } + static struct pingroup_config tegra3_pinmux_common[] = { /* SDMMC1 pinmux */ DEFAULT_PINMUX(SDMMC1_CLK, SDMMC1, NORMAL, NORMAL, INPUT), @@ -326,4 +338,9 @@ static struct pingroup_config unused_pins_lowpower[] = { DEFAULT_PINMUX(GMI_DQS, NAND, NORMAL, TRISTATE, OUTPUT), };
-#endif /* _PINMUX_CONFIG_CARDHU_H_ */ +static struct padctrl_config cardhu_padctrl[] = { + /* (_padgrp, _slwf, _slwr, _drvup, _drvdn, _lpmd, _schmt, _hsm) */ + DEFAULT_PADCFG(SDIO1, SDIOCFG_DRVUP_SLWF, SDIOCFG_DRVDN_SLWR, \ + SDIOCFG_DRVUP, SDIOCFG_DRVDN, NONE, DISABLE, DISABLE), +}; +#endif /* _PINMUX_CONFIG_CARDHU_H_ */

On 03/06/2013 04:51 PM, Tom Warren wrote:
Pad config registers exist in APB_MISC_GP space, and control slew rate, drive strengh, schmidt, high-speed, and low-power modes for all of the pingroups in Tegra30. This builds off of the pinmux way of constructing init tables to configure select pads (SDIOCFG, for instance) during pinmux_init().
Currently, only SDIO1CFG is changed as per the TRM to work with the SD-card slot on Cardhu.
Thanks to StephenW for the suggestion/original idea.
As a general rule, I'd expect the pinmux driver implementation to be a separate patch from the changes to the Cardhu board file to use the new features, but I guess you're replacing an existing pinmux_sdio_pad_cfg(), so that isn't possible. So it's fine.
diff --git a/board/nvidia/cardhu/pinmux-config-cardhu.h b/board/nvidia/cardhu/pinmux-config-cardhu.h
+static struct padctrl_config cardhu_padctrl[] = {
- /* (_padgrp, _slwf, _slwr, _drvup, _drvdn, _lpmd, _schmt, _hsm) */
- DEFAULT_PADCFG(SDIO1, SDIOCFG_DRVUP_SLWF, SDIOCFG_DRVDN_SLWR, \
SDIOCFG_DRVUP, SDIOCFG_DRVDN, NONE, DISABLE, DISABLE),
+}; +#endif /* _PINMUX_CONFIG_CARDHU_H_ */
I didn't quite get why there wasn't an SDIO3 entry in that table, since the original pinmux_sdio_pad_cfg() set up both SDIO1 and SDIO3 pin groups, and SDIO3 appears to be used on Cardhu for SDIO-based WiFi.
Aside from that, Reviewed-by: Stephen Warren swarren@nvidia.com

Stephen,
On Thu, Mar 7, 2013 at 1:27 PM, Stephen Warren swarren@wwwdotorg.org wrote:
On 03/06/2013 04:51 PM, Tom Warren wrote:
Pad config registers exist in APB_MISC_GP space, and control slew rate, drive strengh, schmidt, high-speed, and low-power modes for all of the pingroups in Tegra30. This builds off of the pinmux way of constructing init tables to configure select pads (SDIOCFG, for instance) during pinmux_init().
Currently, only SDIO1CFG is changed as per the TRM to work with the SD-card slot on Cardhu.
Thanks to StephenW for the suggestion/original idea.
As a general rule, I'd expect the pinmux driver implementation to be a separate patch from the changes to the Cardhu board file to use the new features, but I guess you're replacing an existing pinmux_sdio_pad_cfg(), so that isn't possible. So it's fine.
OK. Thanks.
diff --git a/board/nvidia/cardhu/pinmux-config-cardhu.h b/board/nvidia/cardhu/pinmux-config-cardhu.h
+static struct padctrl_config cardhu_padctrl[] = {
/* (_padgrp, _slwf, _slwr, _drvup, _drvdn, _lpmd, _schmt, _hsm) */
DEFAULT_PADCFG(SDIO1, SDIOCFG_DRVUP_SLWF, SDIOCFG_DRVDN_SLWR, \
SDIOCFG_DRVUP, SDIOCFG_DRVDN, NONE, DISABLE, DISABLE),
+}; +#endif /* _PINMUX_CONFIG_CARDHU_H_ */
I didn't quite get why there wasn't an SDIO3 entry in that table, since the original pinmux_sdio_pad_cfg() set up both SDIO1 and SDIO3 pin groups, and SDIO3 appears to be used on Cardhu for SDIO-based WiFi.
The previous code did check for SDMMC1 or SDMMC3, but on Cardhu, SDMMC1 is called out in the dts file, and SDMMC3 isn't (you had said earlier when reviewing some Ventana changes that leaving out a SDIO WIFI node was ok as U-Boot wouldn't be using it, so I didn't add it to Cardhu). So the old code would only have called the pad init code for SDMMC1 and SDMMC4, and SDMMC4 would have been rejected since it's not SDIO. So I only added SDIO1 to the table. I can add an entry for SDIO3 (even if it's not in the dts file for Cardhu), since it does no harm to set those pad cfg registers as per the TRM (I assume the TRM is calling out cal numbers that will work with both an SD-card slot and a WIFI device?).
Let me know if you'd like to see either the WIFI node added to the Cardhu DT file, or the SDIO3 entry added to the cardhu_padctrl table, or both.
Tom
Aside from that, Reviewed-by: Stephen Warren swarren@nvidia.com

On 03/07/2013 02:13 PM, Tom Warren wrote:
Stephen,
On Thu, Mar 7, 2013 at 1:27 PM, Stephen Warren swarren@wwwdotorg.org wrote:
On 03/06/2013 04:51 PM, Tom Warren wrote:
Pad config registers exist in APB_MISC_GP space, and control slew rate, drive strengh, schmidt, high-speed, and low-power modes for all of the pingroups in Tegra30. This builds off of the pinmux way of constructing init tables to configure select pads (SDIOCFG, for instance) during pinmux_init().
Currently, only SDIO1CFG is changed as per the TRM to work with the SD-card slot on Cardhu.
...
I didn't quite get why there wasn't an SDIO3 entry in that table, since the original pinmux_sdio_pad_cfg() set up both SDIO1 and SDIO3 pin groups, and SDIO3 appears to be used on Cardhu for SDIO-based WiFi.
The previous code did check for SDMMC1 or SDMMC3, but on Cardhu, SDMMC1 is called out in the dts file, and SDMMC3 isn't (you had said earlier when reviewing some Ventana changes that leaving out a SDIO WIFI node was ok as U-Boot wouldn't be using it, so I didn't add it to Cardhu). So the old code would only have called the pad init code for SDMMC1 and SDMMC4, and SDMMC4 would have been rejected since it's not SDIO. So I only added SDIO1 to the table. I can add an entry for SDIO3 (even if it's not in the dts file for Cardhu), since it does no harm to set those pad cfg registers as per the TRM (I assume the TRM is calling out cal numbers that will work with both an SD-card slot and a WIFI device?).
Let me know if you'd like to see either the WIFI node added to the Cardhu DT file, or the SDIO3 entry added to the cardhu_padctrl table, or both.
Ah OK, now I understand. This is fine for now; we can add the extra table entries if we need them. The kernel does set them up for its own use of WiFi.
participants (2)
-
Stephen Warren
-
Tom Warren