1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Copyright (C) 2016 Freescale Semiconductor, Inc. |
4 | * Copyright 2017-2018 NXP |
5 | * Dong Aisheng <aisheng.dong@nxp.com> |
6 | */ |
7 | |
8 | #include <linux/err.h> |
9 | #include <linux/firmware/imx/sci.h> |
10 | #include <linux/module.h> |
11 | #include <linux/of_address.h> |
12 | #include <linux/pinctrl/pinctrl.h> |
13 | #include <linux/platform_device.h> |
14 | |
15 | #include "../core.h" |
16 | #include "pinctrl-imx.h" |
17 | |
18 | #define IMX_SC_PAD_FUNC_GET_WAKEUP 9 |
19 | #define IMX_SC_PAD_FUNC_SET_WAKEUP 4 |
20 | #define IMX_SC_IRQ_GROUP_WAKE 3 /* Wakeup interrupts */ |
21 | #define IMX_SC_IRQ_PAD 2 /* Pad wakeup */ |
22 | |
23 | enum pad_func_e { |
24 | IMX_SC_PAD_FUNC_SET = 15, |
25 | IMX_SC_PAD_FUNC_GET = 16, |
26 | }; |
27 | |
28 | struct imx_sc_msg_req_pad_set { |
29 | struct imx_sc_rpc_msg hdr; |
30 | u32 val; |
31 | u16 pad; |
32 | } __packed __aligned(4); |
33 | |
34 | struct imx_sc_msg_req_pad_get { |
35 | struct imx_sc_rpc_msg hdr; |
36 | u16 pad; |
37 | } __packed __aligned(4); |
38 | |
39 | struct imx_sc_msg_resp_pad_get { |
40 | struct imx_sc_rpc_msg hdr; |
41 | u32 val; |
42 | } __packed; |
43 | |
44 | struct imx_sc_msg_gpio_set_pad_wakeup { |
45 | struct imx_sc_rpc_msg hdr; |
46 | u16 pad; |
47 | u8 wakeup; |
48 | } __packed __aligned(4); |
49 | |
50 | static struct imx_sc_ipc *pinctrl_ipc_handle; |
51 | |
52 | int imx_pinctrl_sc_ipc_init(struct platform_device *pdev) |
53 | { |
54 | imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_WAKE, |
55 | IMX_SC_IRQ_PAD, enable: true); |
56 | return imx_scu_get_handle(ipc: &pinctrl_ipc_handle); |
57 | } |
58 | EXPORT_SYMBOL_GPL(imx_pinctrl_sc_ipc_init); |
59 | |
60 | int imx_pinconf_get_scu(struct pinctrl_dev *pctldev, unsigned pin_id, |
61 | unsigned long *config) |
62 | { |
63 | struct imx_sc_msg_req_pad_get msg; |
64 | struct imx_sc_msg_resp_pad_get *resp; |
65 | struct imx_sc_rpc_msg *hdr = &msg.hdr; |
66 | int ret; |
67 | |
68 | hdr->ver = IMX_SC_RPC_VERSION; |
69 | hdr->svc = IMX_SC_RPC_SVC_PAD; |
70 | hdr->func = IMX_SC_PAD_FUNC_GET; |
71 | hdr->size = 2; |
72 | |
73 | msg.pad = pin_id; |
74 | |
75 | ret = imx_scu_call_rpc(ipc: pinctrl_ipc_handle, msg: &msg, have_resp: true); |
76 | if (ret) |
77 | return ret; |
78 | |
79 | resp = (struct imx_sc_msg_resp_pad_get *)&msg; |
80 | *config = resp->val; |
81 | |
82 | return 0; |
83 | } |
84 | EXPORT_SYMBOL_GPL(imx_pinconf_get_scu); |
85 | |
86 | int imx_pinconf_set_scu(struct pinctrl_dev *pctldev, unsigned pin_id, |
87 | unsigned long *configs, unsigned num_configs) |
88 | { |
89 | struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); |
90 | struct imx_sc_msg_req_pad_set msg; |
91 | struct imx_sc_rpc_msg *hdr = &msg.hdr; |
92 | unsigned int mux = configs[0]; |
93 | unsigned int conf; |
94 | unsigned int val; |
95 | int ret; |
96 | |
97 | if (num_configs == 1) { |
98 | struct imx_sc_msg_gpio_set_pad_wakeup wmsg; |
99 | |
100 | hdr = &wmsg.hdr; |
101 | hdr->ver = IMX_SC_RPC_VERSION; |
102 | hdr->svc = IMX_SC_RPC_SVC_PAD; |
103 | hdr->func = IMX_SC_PAD_FUNC_SET_WAKEUP; |
104 | hdr->size = 2; |
105 | wmsg.pad = pin_id; |
106 | wmsg.wakeup = *configs; |
107 | ret = imx_scu_call_rpc(ipc: pinctrl_ipc_handle, msg: &wmsg, have_resp: true); |
108 | |
109 | dev_dbg(ipctl->dev, "wakeup pin_id: %d type: %ld\n" , |
110 | pin_id, *configs); |
111 | return ret; |
112 | } |
113 | |
114 | /* |
115 | * Set mux and conf together in one IPC call |
116 | */ |
117 | WARN_ON(num_configs != 2); |
118 | conf = configs[1]; |
119 | |
120 | val = conf | BM_PAD_CTL_IFMUX_ENABLE | BM_PAD_CTL_GP_ENABLE; |
121 | val |= mux << BP_PAD_CTL_IFMUX; |
122 | |
123 | hdr->ver = IMX_SC_RPC_VERSION; |
124 | hdr->svc = IMX_SC_RPC_SVC_PAD; |
125 | hdr->func = IMX_SC_PAD_FUNC_SET; |
126 | hdr->size = 3; |
127 | |
128 | msg.pad = pin_id; |
129 | msg.val = val; |
130 | |
131 | ret = imx_scu_call_rpc(ipc: pinctrl_ipc_handle, msg: &msg, have_resp: true); |
132 | |
133 | dev_dbg(ipctl->dev, "write: pin_id %u config 0x%x val 0x%x\n" , |
134 | pin_id, conf, val); |
135 | |
136 | return ret; |
137 | } |
138 | EXPORT_SYMBOL_GPL(imx_pinconf_set_scu); |
139 | |
140 | void imx_pinctrl_parse_pin_scu(struct imx_pinctrl *ipctl, |
141 | unsigned int *pin_id, struct imx_pin *pin, |
142 | const __be32 **list_p) |
143 | { |
144 | const struct imx_pinctrl_soc_info *info = ipctl->info; |
145 | struct imx_pin_scu *pin_scu = &pin->conf.scu; |
146 | const __be32 *list = *list_p; |
147 | |
148 | pin->pin = be32_to_cpu(*list++); |
149 | *pin_id = pin->pin; |
150 | pin_scu->mux_mode = be32_to_cpu(*list++); |
151 | pin_scu->config = be32_to_cpu(*list++); |
152 | *list_p = list; |
153 | |
154 | dev_dbg(ipctl->dev, "%s: 0x%x 0x%08lx" , info->pins[pin->pin].name, |
155 | pin_scu->mux_mode, pin_scu->config); |
156 | } |
157 | EXPORT_SYMBOL_GPL(imx_pinctrl_parse_pin_scu); |
158 | |
159 | MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>" ); |
160 | MODULE_DESCRIPTION("NXP i.MX SCU common pinctrl driver" ); |
161 | MODULE_LICENSE("GPL v2" ); |
162 | |