diff mbox series

[v2] gpio: tegra186: Check GPIO pin permission before access.

Message ID 20221004074845.29583-1-pshete@nvidia.com
State New
Headers show
Series [v2] gpio: tegra186: Check GPIO pin permission before access. | expand

Commit Message

Prathamesh Shete Oct. 4, 2022, 7:48 a.m. UTC
This change checks if we have the necessary permission to
access the GPIO. For devices that have support for virtualisation
we need to check both the TEGRA186_GPIO_VM_REG and the
TEGRA186_GPIO_SCR_REG registers. For device that do not have
virtualisation support for GPIOs we only need to check the
TEGRA186_GPIO_SCR_REG register.

Signed-off-by: Manish Bhardwaj <mbhardwaj@nvidia.com>
Signed-off-by: Prathamesh Shete <pshete@nvidia.com>
---
 drivers/gpio/gpio-tegra186.c | 74 ++++++++++++++++++++++++++++++++++++
 1 file changed, 74 insertions(+)

Comments

Thierry Reding Oct. 6, 2022, 11:05 a.m. UTC | #1
On Tue, Oct 04, 2022 at 01:18:45PM +0530, Prathamesh Shete wrote:
> This change checks if we have the necessary permission to
> access the GPIO. For devices that have support for virtualisation
> we need to check both the TEGRA186_GPIO_VM_REG and the
> TEGRA186_GPIO_SCR_REG registers. For device that do not have
> virtualisation support for GPIOs we only need to check the
> TEGRA186_GPIO_SCR_REG register.
> 
> Signed-off-by: Manish Bhardwaj <mbhardwaj@nvidia.com>
> Signed-off-by: Prathamesh Shete <pshete@nvidia.com>
> ---
>  drivers/gpio/gpio-tegra186.c | 74 ++++++++++++++++++++++++++++++++++++
>  1 file changed, 74 insertions(+)

This looks mostly good. A few small comments, see below.

> diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c
> index 54d9fa7da9c1..34b6c287d608 100644
> --- a/drivers/gpio/gpio-tegra186.c
> +++ b/drivers/gpio/gpio-tegra186.c
> @@ -26,6 +26,22 @@
>  
>  #define TEGRA186_GPIO_INT_ROUTE_MAPPING(p, x) (0x14 + (p) * 0x20 + (x) * 4)
>  
> +#define  TEGRA186_GPIO_VM			0x00
> +#define  TEGRA186_GPIO_VM_RW_MASK		0x03
> +#define  TEGRA186_GPIO_SCR			0x04
> +#define  TEGRA186_GPIO_SCR_PIN_SIZE		0x08
> +#define  TEGRA186_GPIO_SCR_PORT_SIZE		0x40
> +#define  TEGRA186_GPIO_SCR_SEC_WEN		BIT(28)
> +#define  TEGRA186_GPIO_SCR_SEC_REN		BIT(27)
> +#define  TEGRA186_GPIO_SCR_SEC_G1W		BIT(9)
> +#define  TEGRA186_GPIO_SCR_SEC_G1R		BIT(1)
> +#define  TEGRA186_GPIO_FULL_ACCESS		(TEGRA186_GPIO_SCR_SEC_WEN | \
> +						 TEGRA186_GPIO_SCR_SEC_REN | \
> +						 TEGRA186_GPIO_SCR_SEC_G1R | \
> +						 TEGRA186_GPIO_SCR_SEC_G1W)
> +#define  TEGRA186_GPIO_SCR_SEC_ENABLE		(TEGRA186_GPIO_SCR_SEC_WEN | \
> +						 TEGRA186_GPIO_SCR_SEC_REN)
> +
>  /* control registers */
>  #define TEGRA186_GPIO_ENABLE_CONFIG 0x00
>  #define  TEGRA186_GPIO_ENABLE_CONFIG_ENABLE BIT(0)
> @@ -77,6 +93,7 @@ struct tegra_gpio_soc {
>  	unsigned int num_irqs_per_bank;
>  
>  	const struct tegra186_pin_range *pin_ranges;
> +	bool has_vm_support;

You've placed this right between two fields that clearly belong
together. Best to add new fields towards the end of the structure if
they are not related to any of the other fields.

>  	unsigned int num_pin_ranges;
>  	const char *pinmux;
>  	bool has_gte;
> @@ -129,6 +146,58 @@ static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio,
>  	return gpio->base + offset + pin * 0x20;
>  }
>  
> +static void __iomem *tegra186_gpio_get_secure_base(struct tegra_gpio *gpio,
> +					    unsigned int pin)
> +{
> +	const struct tegra_gpio_port *port;
> +	unsigned int offset;
> +
> +	port = tegra186_gpio_get_port(gpio, &pin);
> +	if (!port)
> +		return NULL;
> +
> +	offset = port->bank * 0x1000 + port->port * TEGRA186_GPIO_SCR_PORT_SIZE;
> +
> +	return gpio->secure + offset + pin * TEGRA186_GPIO_SCR_PIN_SIZE;
> +}
> +
> +static inline bool tegra186_gpio_is_accessible(struct tegra_gpio *gpio, u32 pin)

Pin offsets are "unsigned int" in the rest of the driver, so please
stick to that here as well.

> +{
> +	void __iomem *secure;
> +	u32 val;

The drivers uses "u32 value;" everywhere else, so best stick to that for
consistency.

> +
> +	secure = tegra186_gpio_get_secure_base(gpio, pin);
> +
> +	if (gpio->soc->has_vm_support) {
> +		val = readl(secure + TEGRA186_GPIO_VM);
> +		if ((val & TEGRA186_GPIO_VM_RW_MASK) != TEGRA186_GPIO_VM_RW_MASK)
> +			return false;
> +	}
> +
> +	val = __raw_readl(secure + TEGRA186_GPIO_SCR);
> +
> +	if ((val & TEGRA186_GPIO_SCR_SEC_ENABLE) == 0)
> +		return true;
> +
> +	if ((val & TEGRA186_GPIO_FULL_ACCESS) == TEGRA186_GPIO_FULL_ACCESS)
> +		return true;
> +
> +	return false;
> +}
> +
> +static int tegra186_init_valid_mask(struct gpio_chip *chip,
> +		unsigned long *valid_mask, unsigned int ngpios)
> +{
> +	struct tegra_gpio *gpio = gpiochip_get_data(chip);
> +	int j;

Make that unsigned int to match the type of ngpios.

> +
> +	for (j = 0; j < ngpios; j++) {
> +		if (!tegra186_gpio_is_accessible(gpio, j))
> +			clear_bit(j, valid_mask);
> +	}
> +	return 0;
> +}
> +
>  static int tegra186_gpio_get_direction(struct gpio_chip *chip,
>  				       unsigned int offset)
>  {
> @@ -763,6 +832,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
>  	gpio->soc = device_get_match_data(&pdev->dev);
>  	gpio->gpio.label = gpio->soc->name;
>  	gpio->gpio.parent = &pdev->dev;
> +	gpio->gpio.init_valid_mask = tegra186_init_valid_mask;

Maybe slot this in before the gpio->gpio.add_pin_ranges assignment
further below in this function to keep things ordered a bit better.

>  
>  	/* count the number of banks in the controller */
>  	for (i = 0; i < gpio->soc->num_ports; i++)
> @@ -1042,6 +1112,7 @@ static const struct tegra_gpio_soc tegra194_main_soc = {
>  	.num_pin_ranges = ARRAY_SIZE(tegra194_main_pin_ranges),
>  	.pin_ranges = tegra194_main_pin_ranges,
>  	.pinmux = "nvidia,tegra194-pinmux",
> +	.has_vm_support = true,
>  };
>  
>  #define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins)	\
> @@ -1067,6 +1138,7 @@ static const struct tegra_gpio_soc tegra194_aon_soc = {
>  	.instance = 1,
>  	.num_irqs_per_bank = 8,
>  	.has_gte = true,
> +	.has_vm_support = false,
>  };
>  
>  #define TEGRA234_MAIN_GPIO_PORT(_name, _bank, _port, _pins)	\
> @@ -1111,6 +1183,7 @@ static const struct tegra_gpio_soc tegra234_main_soc = {
>  	.name = "tegra234-gpio",
>  	.instance = 0,
>  	.num_irqs_per_bank = 8,
> +	.has_vm_support = true,
>  };
>  
>  #define TEGRA234_AON_GPIO_PORT(_name, _bank, _port, _pins)	\
> @@ -1136,6 +1209,7 @@ static const struct tegra_gpio_soc tegra234_aon_soc = {
>  	.name = "tegra234-gpio-aon",
>  	.instance = 1,
>  	.num_irqs_per_bank = 8,
> +	.has_vm_support = false,

It's not technically necessary to initialize 0/false fields because
that's what they end up with if you don't initialize them at all. I do
like to be explicit, so no objection from me on this. However, since we
are being explicit, we should set this new field for Tegra186 and
Tegra241 as well.

Thierry
Thierry Reding Oct. 7, 2022, 10:25 a.m. UTC | #2
On Fri, Oct 07, 2022 at 11:29:36AM +0530, Prathamesh Shete wrote:
> This change checks if we have the necessary permission to
> access the GPIO. For devices that have support for virtualisation
> we need to check both the TEGRA186_GPIO_VM_REG and the
> TEGRA186_GPIO_SCR_REG registers. For device that do not have
> virtualisation support for GPIOs we only need to check the
> TEGRA186_GPIO_SCR_REG register.
> 
> Signed-off-by: Manish Bhardwaj <mbhardwaj@nvidia.com>
> Signed-off-by: Prathamesh Shete <pshete@nvidia.com>
> ---
>  drivers/gpio/gpio-tegra186.c | 78 ++++++++++++++++++++++++++++++++++++
>  1 file changed, 78 insertions(+)

I like it, thanks!

Acked-by: Thierry Reding <treding@nvidia.com>
diff mbox series

Patch

diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c
index 54d9fa7da9c1..34b6c287d608 100644
--- a/drivers/gpio/gpio-tegra186.c
+++ b/drivers/gpio/gpio-tegra186.c
@@ -26,6 +26,22 @@ 
 
 #define TEGRA186_GPIO_INT_ROUTE_MAPPING(p, x) (0x14 + (p) * 0x20 + (x) * 4)
 
+#define  TEGRA186_GPIO_VM			0x00
+#define  TEGRA186_GPIO_VM_RW_MASK		0x03
+#define  TEGRA186_GPIO_SCR			0x04
+#define  TEGRA186_GPIO_SCR_PIN_SIZE		0x08
+#define  TEGRA186_GPIO_SCR_PORT_SIZE		0x40
+#define  TEGRA186_GPIO_SCR_SEC_WEN		BIT(28)
+#define  TEGRA186_GPIO_SCR_SEC_REN		BIT(27)
+#define  TEGRA186_GPIO_SCR_SEC_G1W		BIT(9)
+#define  TEGRA186_GPIO_SCR_SEC_G1R		BIT(1)
+#define  TEGRA186_GPIO_FULL_ACCESS		(TEGRA186_GPIO_SCR_SEC_WEN | \
+						 TEGRA186_GPIO_SCR_SEC_REN | \
+						 TEGRA186_GPIO_SCR_SEC_G1R | \
+						 TEGRA186_GPIO_SCR_SEC_G1W)
+#define  TEGRA186_GPIO_SCR_SEC_ENABLE		(TEGRA186_GPIO_SCR_SEC_WEN | \
+						 TEGRA186_GPIO_SCR_SEC_REN)
+
 /* control registers */
 #define TEGRA186_GPIO_ENABLE_CONFIG 0x00
 #define  TEGRA186_GPIO_ENABLE_CONFIG_ENABLE BIT(0)
@@ -77,6 +93,7 @@  struct tegra_gpio_soc {
 	unsigned int num_irqs_per_bank;
 
 	const struct tegra186_pin_range *pin_ranges;
+	bool has_vm_support;
 	unsigned int num_pin_ranges;
 	const char *pinmux;
 	bool has_gte;
@@ -129,6 +146,58 @@  static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio,
 	return gpio->base + offset + pin * 0x20;
 }
 
+static void __iomem *tegra186_gpio_get_secure_base(struct tegra_gpio *gpio,
+					    unsigned int pin)
+{
+	const struct tegra_gpio_port *port;
+	unsigned int offset;
+
+	port = tegra186_gpio_get_port(gpio, &pin);
+	if (!port)
+		return NULL;
+
+	offset = port->bank * 0x1000 + port->port * TEGRA186_GPIO_SCR_PORT_SIZE;
+
+	return gpio->secure + offset + pin * TEGRA186_GPIO_SCR_PIN_SIZE;
+}
+
+static inline bool tegra186_gpio_is_accessible(struct tegra_gpio *gpio, u32 pin)
+{
+	void __iomem *secure;
+	u32 val;
+
+	secure = tegra186_gpio_get_secure_base(gpio, pin);
+
+	if (gpio->soc->has_vm_support) {
+		val = readl(secure + TEGRA186_GPIO_VM);
+		if ((val & TEGRA186_GPIO_VM_RW_MASK) != TEGRA186_GPIO_VM_RW_MASK)
+			return false;
+	}
+
+	val = __raw_readl(secure + TEGRA186_GPIO_SCR);
+
+	if ((val & TEGRA186_GPIO_SCR_SEC_ENABLE) == 0)
+		return true;
+
+	if ((val & TEGRA186_GPIO_FULL_ACCESS) == TEGRA186_GPIO_FULL_ACCESS)
+		return true;
+
+	return false;
+}
+
+static int tegra186_init_valid_mask(struct gpio_chip *chip,
+		unsigned long *valid_mask, unsigned int ngpios)
+{
+	struct tegra_gpio *gpio = gpiochip_get_data(chip);
+	int j;
+
+	for (j = 0; j < ngpios; j++) {
+		if (!tegra186_gpio_is_accessible(gpio, j))
+			clear_bit(j, valid_mask);
+	}
+	return 0;
+}
+
 static int tegra186_gpio_get_direction(struct gpio_chip *chip,
 				       unsigned int offset)
 {
@@ -763,6 +832,7 @@  static int tegra186_gpio_probe(struct platform_device *pdev)
 	gpio->soc = device_get_match_data(&pdev->dev);
 	gpio->gpio.label = gpio->soc->name;
 	gpio->gpio.parent = &pdev->dev;
+	gpio->gpio.init_valid_mask = tegra186_init_valid_mask;
 
 	/* count the number of banks in the controller */
 	for (i = 0; i < gpio->soc->num_ports; i++)
@@ -1042,6 +1112,7 @@  static const struct tegra_gpio_soc tegra194_main_soc = {
 	.num_pin_ranges = ARRAY_SIZE(tegra194_main_pin_ranges),
 	.pin_ranges = tegra194_main_pin_ranges,
 	.pinmux = "nvidia,tegra194-pinmux",
+	.has_vm_support = true,
 };
 
 #define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins)	\
@@ -1067,6 +1138,7 @@  static const struct tegra_gpio_soc tegra194_aon_soc = {
 	.instance = 1,
 	.num_irqs_per_bank = 8,
 	.has_gte = true,
+	.has_vm_support = false,
 };
 
 #define TEGRA234_MAIN_GPIO_PORT(_name, _bank, _port, _pins)	\
@@ -1111,6 +1183,7 @@  static const struct tegra_gpio_soc tegra234_main_soc = {
 	.name = "tegra234-gpio",
 	.instance = 0,
 	.num_irqs_per_bank = 8,
+	.has_vm_support = true,
 };
 
 #define TEGRA234_AON_GPIO_PORT(_name, _bank, _port, _pins)	\
@@ -1136,6 +1209,7 @@  static const struct tegra_gpio_soc tegra234_aon_soc = {
 	.name = "tegra234-gpio-aon",
 	.instance = 1,
 	.num_irqs_per_bank = 8,
+	.has_vm_support = false,
 };
 
 #define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins)	\