blob: 5b7122b08a50616600b791f1dab2ef5fe70ba97b [file] [log] [blame]
Alice Guoaeb03192025-04-28 18:37:26 +08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2025 NXP
4 */
5
6#include <asm/io.h>
7#include <asm/mach-imx/sys_proto.h>
8#include <dm.h>
9#include <dm/device_compat.h>
10#include <dm/devres.h>
11#include <dm/pinctrl.h>
12#include <scmi_agent.h>
13#include <scmi_protocols.h>
14
15#include "pinctrl-imx.h"
16
17#define DAISY_OFFSET_IMX95 0x408
18
19/* SCMI pin control types */
20#define PINCTRL_TYPE_MUX 192
21#define PINCTRL_TYPE_CONFIG 193
22#define PINCTRL_TYPE_DAISY_ID 194
23#define PINCTRL_TYPE_DAISY_CFG 195
24#define PINCTRL_NUM_CFGS_SHIFT 2
25
26struct imx_scmi_pinctrl_priv {
27 u16 daisy_offset;
28};
29
30static int imx_pinconf_scmi_set(struct udevice *dev, u32 mux_ofs, u32 mux, u32 config_val,
31 u32 input_ofs, u32 input_val)
32{
33 struct imx_scmi_pinctrl_priv *priv = dev_get_priv(dev);
34 int ret, num_cfgs = 0;
35 struct scmi_msg msg;
36
37 /* Call SCMI API to set the pin mux and configuration. */
38 struct scmi_pinctrl_config_set_out out;
39 struct scmi_pinctrl_config_set_in in = {
40 .identifier = mux_ofs / 4,
41 .function_id = 0xFFFFFFFF,
42 .attributes = 0,
43 };
44
45 if (mux_ofs) {
46 in.configs[num_cfgs].type = PINCTRL_TYPE_MUX;
47 in.configs[num_cfgs].val = mux;
48 num_cfgs++;
49 }
50
51 if (config_val) {
52 in.configs[num_cfgs].type = PINCTRL_TYPE_CONFIG;
53 in.configs[num_cfgs].val = config_val;
54 num_cfgs++;
55 }
56
57 if (input_ofs) {
58 in.configs[num_cfgs].type = PINCTRL_TYPE_DAISY_ID;
59 in.configs[num_cfgs].val = (input_ofs - priv->daisy_offset) / 4;
60 num_cfgs++;
61 in.configs[num_cfgs].type = PINCTRL_TYPE_DAISY_CFG;
62 in.configs[num_cfgs].val = input_val;
63 num_cfgs++;
64 }
65
66 /* Update the number of configs sent in this call. */
67 in.attributes = num_cfgs << PINCTRL_NUM_CFGS_SHIFT;
68
69 msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_PINCTRL,
70 SCMI_MSG_PINCTRL_CONFIG_SET, in, out);
71
72 ret = devm_scmi_process_msg(dev, &msg);
73 if (ret || out.status) {
74 dev_err(dev, "Failed to set PAD = %d, daisy = %d, scmi_err = %d, ret = %d\n",
75 mux_ofs / 4, input_ofs / 4, out.status, ret);
76 }
77
78 return ret;
79}
80
81static int imx_pinctrl_set_state_scmi(struct udevice *dev, struct udevice *config)
82{
83 int mux_ofs, mux, config_val, input_reg, input_val;
84 u32 *pin_data;
85 int i, j = 0;
86 int npins;
87 int ret;
88
89 ret = imx_pinctrl_set_state_common(dev, config, FSL_PIN_SIZE,
90 &pin_data, &npins);
91 if (ret)
92 return ret;
93
94 /*
95 * Refer to linux documentation for details:
96 * Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt
97 */
98 for (i = 0; i < npins; i++) {
99 mux_ofs = pin_data[j++];
100 /* Skip config_reg */
101 j++;
102 input_reg = pin_data[j++];
103
104 mux = pin_data[j++];
105 input_val = pin_data[j++];
106 config_val = pin_data[j++];
107
108 if (config_val & IMX_PAD_SION)
109 mux |= IOMUXC_CONFIG_SION;
110
111 config_val &= ~IMX_PAD_SION;
112
113 ret = imx_pinconf_scmi_set(dev, mux_ofs, mux, config_val, input_reg, input_val);
114 if (ret && ret != -EPERM) {
115 dev_err(dev, "Set pin %d, mux %d, val %d, error\n",
116 mux_ofs, mux, config_val);
117 }
118 }
119
120 devm_kfree(dev, pin_data);
121
122 return ret;
123}
124
125static const struct pinctrl_ops imx_scmi_pinctrl_ops = {
126 .set_state = imx_pinctrl_set_state_scmi,
127};
128
129static int imx_scmi_pinctrl_probe(struct udevice *dev)
130{
131 struct imx_scmi_pinctrl_priv *priv = dev_get_priv(dev);
132
133 if (IS_ENABLED(CONFIG_IMX95))
134 priv->daisy_offset = DAISY_OFFSET_IMX95;
135 else
136 return -EINVAL;
137
138 return devm_scmi_of_get_channel(dev);
139}
140
141static int imx_scmi_pinctrl_bind(struct udevice *dev)
142{
143 if (IS_ENABLED(CONFIG_IMX95))
144 return 0;
145
146 return -ENODEV;
147}
148
149U_BOOT_DRIVER(scmi_pinctrl_imx) = {
150 .name = "scmi_pinctrl_imx",
151 .id = UCLASS_PINCTRL,
152 .bind = imx_scmi_pinctrl_bind,
153 .probe = imx_scmi_pinctrl_probe,
154 .priv_auto = sizeof(struct imx_scmi_pinctrl_priv),
155 .ops = &imx_scmi_pinctrl_ops,
156 .flags = DM_FLAG_PRE_RELOC,
157};