162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2016 Freescale Semiconductor, Inc.
462306a36Sopenharmony_ci * Copyright 2017-2018 NXP
562306a36Sopenharmony_ci *	Dong Aisheng <aisheng.dong@nxp.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/err.h>
962306a36Sopenharmony_ci#include <linux/firmware/imx/sci.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/of_address.h>
1262306a36Sopenharmony_ci#include <linux/pinctrl/pinctrl.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "../core.h"
1662306a36Sopenharmony_ci#include "pinctrl-imx.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define IMX_SC_PAD_FUNC_GET_WAKEUP	9
1962306a36Sopenharmony_ci#define IMX_SC_PAD_FUNC_SET_WAKEUP	4
2062306a36Sopenharmony_ci#define IMX_SC_IRQ_GROUP_WAKE           3   /* Wakeup interrupts */
2162306a36Sopenharmony_ci#define IMX_SC_IRQ_PAD			2   /* Pad wakeup */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cienum pad_func_e {
2462306a36Sopenharmony_ci	IMX_SC_PAD_FUNC_SET = 15,
2562306a36Sopenharmony_ci	IMX_SC_PAD_FUNC_GET = 16,
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct imx_sc_msg_req_pad_set {
2962306a36Sopenharmony_ci	struct imx_sc_rpc_msg hdr;
3062306a36Sopenharmony_ci	u32 val;
3162306a36Sopenharmony_ci	u16 pad;
3262306a36Sopenharmony_ci} __packed __aligned(4);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistruct imx_sc_msg_req_pad_get {
3562306a36Sopenharmony_ci	struct imx_sc_rpc_msg hdr;
3662306a36Sopenharmony_ci	u16 pad;
3762306a36Sopenharmony_ci} __packed __aligned(4);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistruct imx_sc_msg_resp_pad_get {
4062306a36Sopenharmony_ci	struct imx_sc_rpc_msg hdr;
4162306a36Sopenharmony_ci	u32 val;
4262306a36Sopenharmony_ci} __packed;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct imx_sc_msg_gpio_set_pad_wakeup {
4562306a36Sopenharmony_ci	struct imx_sc_rpc_msg hdr;
4662306a36Sopenharmony_ci	u16 pad;
4762306a36Sopenharmony_ci	u8 wakeup;
4862306a36Sopenharmony_ci} __packed __aligned(4);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic struct imx_sc_ipc *pinctrl_ipc_handle;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ciint imx_pinctrl_sc_ipc_init(struct platform_device *pdev)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_WAKE,
5562306a36Sopenharmony_ci					 IMX_SC_IRQ_PAD, true);
5662306a36Sopenharmony_ci	return imx_scu_get_handle(&pinctrl_ipc_handle);
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(imx_pinctrl_sc_ipc_init);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ciint imx_pinconf_get_scu(struct pinctrl_dev *pctldev, unsigned pin_id,
6162306a36Sopenharmony_ci			unsigned long *config)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct imx_sc_msg_req_pad_get msg;
6462306a36Sopenharmony_ci	struct imx_sc_msg_resp_pad_get *resp;
6562306a36Sopenharmony_ci	struct imx_sc_rpc_msg *hdr = &msg.hdr;
6662306a36Sopenharmony_ci	int ret;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	hdr->ver = IMX_SC_RPC_VERSION;
6962306a36Sopenharmony_ci	hdr->svc = IMX_SC_RPC_SVC_PAD;
7062306a36Sopenharmony_ci	hdr->func = IMX_SC_PAD_FUNC_GET;
7162306a36Sopenharmony_ci	hdr->size = 2;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	msg.pad = pin_id;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	ret = imx_scu_call_rpc(pinctrl_ipc_handle, &msg, true);
7662306a36Sopenharmony_ci	if (ret)
7762306a36Sopenharmony_ci		return ret;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	resp = (struct imx_sc_msg_resp_pad_get *)&msg;
8062306a36Sopenharmony_ci	*config = resp->val;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	return 0;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(imx_pinconf_get_scu);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciint imx_pinconf_set_scu(struct pinctrl_dev *pctldev, unsigned pin_id,
8762306a36Sopenharmony_ci			unsigned long *configs, unsigned num_configs)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
9062306a36Sopenharmony_ci	struct imx_sc_msg_req_pad_set msg;
9162306a36Sopenharmony_ci	struct imx_sc_rpc_msg *hdr = &msg.hdr;
9262306a36Sopenharmony_ci	unsigned int mux = configs[0];
9362306a36Sopenharmony_ci	unsigned int conf;
9462306a36Sopenharmony_ci	unsigned int val;
9562306a36Sopenharmony_ci	int ret;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (num_configs == 1) {
9862306a36Sopenharmony_ci		struct imx_sc_msg_gpio_set_pad_wakeup wmsg;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		hdr = &wmsg.hdr;
10162306a36Sopenharmony_ci		hdr->ver = IMX_SC_RPC_VERSION;
10262306a36Sopenharmony_ci		hdr->svc = IMX_SC_RPC_SVC_PAD;
10362306a36Sopenharmony_ci		hdr->func = IMX_SC_PAD_FUNC_SET_WAKEUP;
10462306a36Sopenharmony_ci		hdr->size = 2;
10562306a36Sopenharmony_ci		wmsg.pad = pin_id;
10662306a36Sopenharmony_ci		wmsg.wakeup = *configs;
10762306a36Sopenharmony_ci		ret = imx_scu_call_rpc(pinctrl_ipc_handle, &wmsg, true);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci		dev_dbg(ipctl->dev, "wakeup pin_id: %d type: %ld\n",
11062306a36Sopenharmony_ci				pin_id, *configs);
11162306a36Sopenharmony_ci		return ret;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/*
11562306a36Sopenharmony_ci	 * Set mux and conf together in one IPC call
11662306a36Sopenharmony_ci	 */
11762306a36Sopenharmony_ci	WARN_ON(num_configs != 2);
11862306a36Sopenharmony_ci	conf = configs[1];
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	val = conf | BM_PAD_CTL_IFMUX_ENABLE | BM_PAD_CTL_GP_ENABLE;
12162306a36Sopenharmony_ci	val |= mux << BP_PAD_CTL_IFMUX;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	hdr->ver = IMX_SC_RPC_VERSION;
12462306a36Sopenharmony_ci	hdr->svc = IMX_SC_RPC_SVC_PAD;
12562306a36Sopenharmony_ci	hdr->func = IMX_SC_PAD_FUNC_SET;
12662306a36Sopenharmony_ci	hdr->size = 3;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	msg.pad = pin_id;
12962306a36Sopenharmony_ci	msg.val = val;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	ret = imx_scu_call_rpc(pinctrl_ipc_handle, &msg, true);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	dev_dbg(ipctl->dev, "write: pin_id %u config 0x%x val 0x%x\n",
13462306a36Sopenharmony_ci		pin_id, conf, val);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return ret;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(imx_pinconf_set_scu);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_civoid imx_pinctrl_parse_pin_scu(struct imx_pinctrl *ipctl,
14162306a36Sopenharmony_ci			       unsigned int *pin_id, struct imx_pin *pin,
14262306a36Sopenharmony_ci			       const __be32 **list_p)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	const struct imx_pinctrl_soc_info *info = ipctl->info;
14562306a36Sopenharmony_ci	struct imx_pin_scu *pin_scu = &pin->conf.scu;
14662306a36Sopenharmony_ci	const __be32 *list = *list_p;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	pin->pin = be32_to_cpu(*list++);
14962306a36Sopenharmony_ci	*pin_id = pin->pin;
15062306a36Sopenharmony_ci	pin_scu->mux_mode = be32_to_cpu(*list++);
15162306a36Sopenharmony_ci	pin_scu->config = be32_to_cpu(*list++);
15262306a36Sopenharmony_ci	*list_p = list;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	dev_dbg(ipctl->dev, "%s: 0x%x 0x%08lx", info->pins[pin->pin].name,
15562306a36Sopenharmony_ci		pin_scu->mux_mode, pin_scu->config);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(imx_pinctrl_parse_pin_scu);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ciMODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>");
16062306a36Sopenharmony_ciMODULE_DESCRIPTION("NXP i.MX SCU common pinctrl driver");
16162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
162