diff mbox series

firmware: arm_scmi: clock: implement get permissions

Message ID 20231129082957.1319895-1-peng.fan@oss.nxp.com (mailing list archive)
State Changes Requested, archived
Headers show
Series firmware: arm_scmi: clock: implement get permissions | expand

Commit Message

Peng Fan (OSS) Nov. 29, 2023, 8:29 a.m. UTC
From: Peng Fan <peng.fan@nxp.com>

ARM SCMI Spec 3.2 introduces Clock Get Permission command. This patch
is to add the support. For clock enable/disable, directly return zero
if not allow to config. For rate & parent set, directly return -EACCES
if not allow to set.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/firmware/arm_scmi/clock.c | 51 +++++++++++++++++++++++++++++++
 include/linux/scmi_protocol.h     |  2 ++
 2 files changed, 53 insertions(+)

Comments

Cristian Marussi Nov. 30, 2023, 3:30 p.m. UTC | #1
On Wed, Nov 29, 2023 at 04:29:57PM +0800, Peng Fan (OSS) wrote:
> From: Peng Fan <peng.fan@nxp.com>
> 
> ARM SCMI Spec 3.2 introduces Clock Get Permission command. This patch
> is to add the support. For clock enable/disable, directly return zero
> if not allow to config. For rate & parent set, directly return -EACCES
> if not allow to set.
> 

Hi Peng,

thanks for this, a few comments below.

> Signed-off-by: Peng Fan <peng.fan@nxp.com>
> ---
>  drivers/firmware/arm_scmi/clock.c | 51 +++++++++++++++++++++++++++++++
>  include/linux/scmi_protocol.h     |  2 ++
>  2 files changed, 53 insertions(+)
> 
> diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
> index 98511a3aa367..ebc140f2a3c0 100644
> --- a/drivers/firmware/arm_scmi/clock.c
> +++ b/drivers/firmware/arm_scmi/clock.c
> @@ -25,8 +25,13 @@ enum scmi_clock_protocol_cmd {
>  	CLOCK_POSSIBLE_PARENTS_GET = 0xC,
>  	CLOCK_PARENT_SET = 0xD,
>  	CLOCK_PARENT_GET = 0xE,
> +	CLOCK_GET_PERMISSIONS = 0xF,
>  };
>  
> +#define CLOCK_STATE_CONTROL_ALLOWED	BIT(31)
> +#define CLOCK_PARENT_CONTROL_ALLOWED	BIT(30)
> +#define CLOCK_RATE_CONTROL_ALLOWED	BIT(29)
> +
>  enum clk_state {
>  	CLK_STATE_DISABLE,
>  	CLK_STATE_ENABLE,
> @@ -46,6 +51,7 @@ struct scmi_msg_resp_clock_attributes {
>  #define SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(x)	((x) & BIT(30))
>  #define SUPPORTS_EXTENDED_NAMES(x)		((x) & BIT(29))
>  #define SUPPORTS_PARENT_CLOCK(x)		((x) & BIT(28))
> +#define SUPPORTS_GET_PERMISSIONS(x)		((x) & BIT(1))
>  	u8 name[SCMI_SHORT_NAME_MAX_SIZE];
>  	__le32 clock_enable_latency;
>  };
> @@ -281,6 +287,29 @@ static int scmi_clock_possible_parents(const struct scmi_protocol_handle *ph, u3
>  	return ret;
>  }
>  
> +static int
> +scmi_clock_get_permissions(const struct scmi_protocol_handle *ph, u32 clk_id,
> +			   struct scmi_clock_info *clk, u32 *perm)
> +{

mmmm...what's the *clk parameter needed for ?
(but more on this later...)

> +	struct scmi_xfer *t;
> +	int ret;
> +
> +	ret = ph->xops->xfer_get_init(ph, CLOCK_GET_PERMISSIONS,
> +				      sizeof(clk_id), sizeof(*perm), &t);
> +	if (ret)
> +		return ret;
> +
> +	put_unaligned_le32(clk_id, t->tx.buf);
> +
> +	ret = ph->xops->do_xfer(ph, t);
> +	if (!ret)
> +		*perm = get_unaligned_le32(t->rx.buf);
> +
> +	ph->xops->xfer_put(ph, t);
> +
> +	return ret;
> +}
> +
>  static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
>  				     u32 clk_id, struct scmi_clock_info *clk,
>  				     u32 version)
> @@ -307,6 +336,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
>  		if (PROTOCOL_REV_MAJOR(version) >= 0x2)
>  			latency = le32_to_cpu(attr->clock_enable_latency);
>  		clk->enable_latency = latency ? : U32_MAX;
> +		clk->attributes = attributes;
>  	}
>  
>  	ph->xops->xfer_put(ph, t);
> @@ -327,6 +357,8 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
>  			clk->rate_change_requested_notifications = true;
>  		if (SUPPORTS_PARENT_CLOCK(attributes))
>  			scmi_clock_possible_parents(ph, clk_id, clk);
> +		if (SUPPORTS_GET_PERMISSIONS(attributes))
> +			scmi_clock_get_permissions(ph, clk_id, clk, &clk->perm);
>  	}
>  
>  	return ret;
> @@ -499,6 +531,11 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
>  	struct scmi_xfer *t;
>  	struct scmi_clock_set_rate *cfg;
>  	struct clock_info *ci = ph->get_priv(ph);
> +	struct scmi_clock_info *clk = ci->clk + clk_id;
> +
> +	if (SUPPORTS_GET_PERMISSIONS(clk->attributes) &&
> +	    !(clk->perm & CLOCK_RATE_CONTROL_ALLOWED))
> +		return -EACCES;
>  
>  	ret = ph->xops->xfer_get_init(ph, CLOCK_RATE_SET, sizeof(*cfg), 0, &t);
>  	if (ret)
> @@ -585,6 +622,10 @@ scmi_clock_set_parent(const struct scmi_protocol_handle *ph, u32 clk_id,
>  	if (parent_id >= clk->num_parents)
>  		return -EINVAL;
>  
> +	if (SUPPORTS_GET_PERMISSIONS(clk->attributes) &&
> +	    !(clk->perm & CLOCK_PARENT_CONTROL_ALLOWED))
> +		return -EACCES;
> +
>  	ret = ph->xops->xfer_get_init(ph, CLOCK_PARENT_SET,
>  				      sizeof(*cfg), 0, &t);
>  	if (ret)
> @@ -668,6 +709,11 @@ static int scmi_clock_enable(const struct scmi_protocol_handle *ph, u32 clk_id,
>  			     bool atomic)
>  {
>  	struct clock_info *ci = ph->get_priv(ph);
> +	struct scmi_clock_info *clk = ci->clk + clk_id;
> +
> +	if (SUPPORTS_GET_PERMISSIONS(clk->attributes) &&
> +	    !(clk->perm & CLOCK_STATE_CONTROL_ALLOWED))
> +		return 0;

So this returning success when not allowed to change could be controversial,
as said, but anyway I would not hide this here at the protocol layer:
IOW, it is good that we now have the perm bits to check to avoid sending an
un-needed message that will be denied, but you should report -EACCESS from
the protocol operation here up to the caller (like you did with the other
parent/rate ops) and let instead the clk-scmi driver to deal with the -EACCESS
retcode deciding (maybe) to return success when the clock is marked as NOT
allowing control of the state.

Moreover I would not expose all the attributes and perm flag mask in
clk_info...maybe you could just perform all these perms checks in
get_permission and set per-clock individual flags that can be then checked
here AND in the clk-scmi driver just by looking up clk_info (instead of
having to export all the macros to dissect the bitfields)

IOW you could check all the perms once for all in clock_get_permission
and setting something like

	ret = ph->xops->do_xfer(ph, t);
	if (!ret) {
		perm = get_unaligned_le32(t->rx.buf);

		clk->state_ctrl_forbidden = !(perm & CLOCK_PARENT_CONTROL_ALLOWED);
		clk->rate_ctrl_forbidden = !(perm & CLOCK_RATE_CONTROL_ALLOWED);
		clk->parent_ctrl_forbidden = !(perm & CLOCK_PARENT_CONTROL_ALLOWED);
	}

for each clock and then here in the clock operations just:

	if (clk->state_ctrl_forbidden)
		return -EACCESS;

etc etc

(with all the clocks NOT supporting the new commands so defaulting to
forbidden=false....)

while in the clk-scmi driver you could similarly filter out the DENY with

	if (ret == -EACCESS && clk->state_control_forbidden)
		return 0;

(if accepted and not too much controversial ...)

This way we avoid exposing all the attributes and all the related macros
while keeping all the strictly protocol related stuff in the protocol
layer...so that in case of further changes the above boolean wont have
to be touched again. (even though is a bit wasteful in terms of space....)

Does this make any sense ? :P

Thanks,
Cristian
diff mbox series

Patch

diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index 98511a3aa367..ebc140f2a3c0 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -25,8 +25,13 @@  enum scmi_clock_protocol_cmd {
 	CLOCK_POSSIBLE_PARENTS_GET = 0xC,
 	CLOCK_PARENT_SET = 0xD,
 	CLOCK_PARENT_GET = 0xE,
+	CLOCK_GET_PERMISSIONS = 0xF,
 };
 
+#define CLOCK_STATE_CONTROL_ALLOWED	BIT(31)
+#define CLOCK_PARENT_CONTROL_ALLOWED	BIT(30)
+#define CLOCK_RATE_CONTROL_ALLOWED	BIT(29)
+
 enum clk_state {
 	CLK_STATE_DISABLE,
 	CLK_STATE_ENABLE,
@@ -46,6 +51,7 @@  struct scmi_msg_resp_clock_attributes {
 #define SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(x)	((x) & BIT(30))
 #define SUPPORTS_EXTENDED_NAMES(x)		((x) & BIT(29))
 #define SUPPORTS_PARENT_CLOCK(x)		((x) & BIT(28))
+#define SUPPORTS_GET_PERMISSIONS(x)		((x) & BIT(1))
 	u8 name[SCMI_SHORT_NAME_MAX_SIZE];
 	__le32 clock_enable_latency;
 };
@@ -281,6 +287,29 @@  static int scmi_clock_possible_parents(const struct scmi_protocol_handle *ph, u3
 	return ret;
 }
 
+static int
+scmi_clock_get_permissions(const struct scmi_protocol_handle *ph, u32 clk_id,
+			   struct scmi_clock_info *clk, u32 *perm)
+{
+	struct scmi_xfer *t;
+	int ret;
+
+	ret = ph->xops->xfer_get_init(ph, CLOCK_GET_PERMISSIONS,
+				      sizeof(clk_id), sizeof(*perm), &t);
+	if (ret)
+		return ret;
+
+	put_unaligned_le32(clk_id, t->tx.buf);
+
+	ret = ph->xops->do_xfer(ph, t);
+	if (!ret)
+		*perm = get_unaligned_le32(t->rx.buf);
+
+	ph->xops->xfer_put(ph, t);
+
+	return ret;
+}
+
 static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
 				     u32 clk_id, struct scmi_clock_info *clk,
 				     u32 version)
@@ -307,6 +336,7 @@  static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
 		if (PROTOCOL_REV_MAJOR(version) >= 0x2)
 			latency = le32_to_cpu(attr->clock_enable_latency);
 		clk->enable_latency = latency ? : U32_MAX;
+		clk->attributes = attributes;
 	}
 
 	ph->xops->xfer_put(ph, t);
@@ -327,6 +357,8 @@  static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
 			clk->rate_change_requested_notifications = true;
 		if (SUPPORTS_PARENT_CLOCK(attributes))
 			scmi_clock_possible_parents(ph, clk_id, clk);
+		if (SUPPORTS_GET_PERMISSIONS(attributes))
+			scmi_clock_get_permissions(ph, clk_id, clk, &clk->perm);
 	}
 
 	return ret;
@@ -499,6 +531,11 @@  static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
 	struct scmi_xfer *t;
 	struct scmi_clock_set_rate *cfg;
 	struct clock_info *ci = ph->get_priv(ph);
+	struct scmi_clock_info *clk = ci->clk + clk_id;
+
+	if (SUPPORTS_GET_PERMISSIONS(clk->attributes) &&
+	    !(clk->perm & CLOCK_RATE_CONTROL_ALLOWED))
+		return -EACCES;
 
 	ret = ph->xops->xfer_get_init(ph, CLOCK_RATE_SET, sizeof(*cfg), 0, &t);
 	if (ret)
@@ -585,6 +622,10 @@  scmi_clock_set_parent(const struct scmi_protocol_handle *ph, u32 clk_id,
 	if (parent_id >= clk->num_parents)
 		return -EINVAL;
 
+	if (SUPPORTS_GET_PERMISSIONS(clk->attributes) &&
+	    !(clk->perm & CLOCK_PARENT_CONTROL_ALLOWED))
+		return -EACCES;
+
 	ret = ph->xops->xfer_get_init(ph, CLOCK_PARENT_SET,
 				      sizeof(*cfg), 0, &t);
 	if (ret)
@@ -668,6 +709,11 @@  static int scmi_clock_enable(const struct scmi_protocol_handle *ph, u32 clk_id,
 			     bool atomic)
 {
 	struct clock_info *ci = ph->get_priv(ph);
+	struct scmi_clock_info *clk = ci->clk + clk_id;
+
+	if (SUPPORTS_GET_PERMISSIONS(clk->attributes) &&
+	    !(clk->perm & CLOCK_STATE_CONTROL_ALLOWED))
+		return 0;
 
 	return ci->clock_config_set(ph, clk_id, CLK_STATE_ENABLE,
 				    NULL_OEM_TYPE, 0, atomic);
@@ -677,6 +723,11 @@  static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id,
 			      bool atomic)
 {
 	struct clock_info *ci = ph->get_priv(ph);
+	struct scmi_clock_info *clk = ci->clk + clk_id;
+
+	if (SUPPORTS_GET_PERMISSIONS(clk->attributes) &&
+	    !(clk->perm & CLOCK_STATE_CONTROL_ALLOWED))
+		return 0;
 
 	return ci->clock_config_set(ph, clk_id, CLK_STATE_DISABLE,
 				    NULL_OEM_TYPE, 0, atomic);
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index f2f05fb42d28..88703b0ce961 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -60,6 +60,8 @@  struct scmi_clock_info {
 	};
 	int num_parents;
 	u32 *parents;
+	u32 attributes;
+	u32 perm;
 };
 
 enum scmi_power_scale {