1 /*
2  * Copyright (c) 2022 Nuvoton Technology Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <assert.h>
7 #include <zephyr/drivers/pinctrl.h>
8 #include <soc.h>
9 
10 /* Driver config */
11 struct npcx_pinctrl_config {
12 	/* Device base address used for pinctrl driver */
13 	uintptr_t base_scfg;
14 	uintptr_t base_glue;
15 };
16 
17 static const struct npcx_pinctrl_config npcx_pinctrl_cfg = {
18 	.base_scfg = NPCX_SCFG_REG_ADDR,
19 	.base_glue = NPCX_GLUE_REG_ADDR,
20 };
21 
22 /* PWM pinctrl config */
23 struct npcx_pwm_pinctrl_config {
24 	uintptr_t base;
25 	int channel;
26 };
27 
28 #define NPCX_PWM_PINCTRL_CFG_INIT(node_id)			\
29 	{							\
30 		.base = DT_REG_ADDR(node_id),			\
31 		.channel = DT_PROP(node_id, pwm_channel),	\
32 	},
33 
34 static const struct npcx_pwm_pinctrl_config pwm_pinctrl_cfg[] = {
35 	DT_FOREACH_STATUS_OKAY(nuvoton_npcx_pwm, NPCX_PWM_PINCTRL_CFG_INIT)
36 };
37 
38 /* Pin-control local functions for peripheral devices */
npcx_periph_pinmux_has_lock(int group)39 static bool npcx_periph_pinmux_has_lock(int group)
40 {
41 	if ((BIT(group) & NPCX_DEVALT_LK_GROUP_MASK) != 0) {
42 		return true;
43 	}
44 
45 	return false;
46 }
47 
npcx_periph_pinmux_configure(const struct npcx_periph * alt,bool is_alternate,bool is_locked)48 static void npcx_periph_pinmux_configure(const struct npcx_periph *alt, bool is_alternate,
49 	bool is_locked)
50 {
51 	const uintptr_t scfg_base = npcx_pinctrl_cfg.base_scfg;
52 	uint8_t alt_mask = BIT(alt->bit);
53 
54 	/*
55 	 * is_alternate == 0 means select GPIO, otherwise Alternate Func.
56 	 * inverted == 0:
57 	 *    Set devalt bit to select Alternate Func.
58 	 * inverted == 1:
59 	 *    Clear devalt bit to select Alternate Func.
60 	 */
61 	if (is_alternate != alt->inverted) {
62 		NPCX_DEVALT(scfg_base, alt->group) |=  alt_mask;
63 	} else {
64 		NPCX_DEVALT(scfg_base, alt->group) &= ~alt_mask;
65 	}
66 
67 	if (is_locked && npcx_periph_pinmux_has_lock(alt->group)) {
68 		NPCX_DEVALT_LK(scfg_base, alt->group) |= alt_mask;
69 	}
70 }
71 
npcx_periph_pupd_configure(const struct npcx_periph * pupd,enum npcx_io_bias_type type)72 static void npcx_periph_pupd_configure(const struct npcx_periph *pupd,
73 	enum npcx_io_bias_type type)
74 {
75 	const uintptr_t scfg_base = npcx_pinctrl_cfg.base_scfg;
76 
77 	if (type == NPCX_BIAS_TYPE_NONE) {
78 		NPCX_PUPD_EN(scfg_base, pupd->group) &= ~BIT(pupd->bit);
79 	} else {
80 		NPCX_PUPD_EN(scfg_base, pupd->group) |=  BIT(pupd->bit);
81 	}
82 }
83 
npcx_periph_pwm_drive_mode_configure(const struct npcx_periph * periph,bool is_od)84 static void npcx_periph_pwm_drive_mode_configure(const struct npcx_periph *periph,
85 	bool is_od)
86 {
87 	uintptr_t reg = 0;
88 
89 	/* Find selected pwm module which enables open-drain prop. */
90 	for (int i = 0; i < ARRAY_SIZE(pwm_pinctrl_cfg); i++) {
91 		if (periph->group == pwm_pinctrl_cfg[i].channel) {
92 			reg = pwm_pinctrl_cfg[i].base;
93 			break;
94 		}
95 	}
96 
97 	if (reg == 0) {
98 		return;
99 	}
100 
101 	struct pwm_reg *const inst = (struct pwm_reg *)(reg);
102 
103 	if (is_od) {
104 		inst->PWMCTLEX |= BIT(NPCX_PWMCTLEX_OD_OUT);
105 	} else {
106 		inst->PWMCTLEX &= ~BIT(NPCX_PWMCTLEX_OD_OUT);
107 	}
108 }
109 
npcx_periph_configure(const pinctrl_soc_pin_t * pin,uintptr_t reg)110 static void npcx_periph_configure(const pinctrl_soc_pin_t *pin, uintptr_t reg)
111 {
112 	if (pin->cfg.periph.type == NPCX_PINCTRL_TYPE_PERIPH_PINMUX) {
113 		/* Configure peripheral device's pinmux functionality */
114 		npcx_periph_pinmux_configure(&pin->cfg.periph,
115 					     !pin->flags.pinmux_gpio,
116 					     pin->flags.pinmux_lock);
117 	} else if (pin->cfg.periph.type == NPCX_PINCTRL_TYPE_PERIPH_PUPD) {
118 		/* Configure peripheral device's internal PU/PD */
119 		npcx_periph_pupd_configure(&pin->cfg.periph,
120 			pin->flags.io_bias_type);
121 	} else if (pin->cfg.periph.type == NPCX_PINCTRL_TYPE_PERIPH_DRIVE) {
122 		/* Configure peripheral device's drive mode. (Only PWM pads support it) */
123 		npcx_periph_pwm_drive_mode_configure(&pin->cfg.periph,
124 			pin->flags.io_drive_type == NPCX_DRIVE_TYPE_OPEN_DRAIN);
125 	}
126 }
127 
npcx_psl_input_detection_configure(const pinctrl_soc_pin_t * pin)128 static void npcx_psl_input_detection_configure(const pinctrl_soc_pin_t *pin)
129 {
130 	struct glue_reg *inst_glue = (struct glue_reg *)(npcx_pinctrl_cfg.base_glue);
131 	const uintptr_t scfg_base = npcx_pinctrl_cfg.base_scfg;
132 	const struct npcx_psl_input *psl_in = (const struct npcx_psl_input *)&pin->cfg.psl_in;
133 
134 	/* Configure detection polarity of PSL input pads */
135 	if (pin->flags.psl_in_polarity == NPCX_PSL_IN_POL_HIGH) {
136 		NPCX_DEVALT(scfg_base, psl_in->pol_group) |=  BIT(psl_in->pol_bit);
137 	} else {
138 		NPCX_DEVALT(scfg_base, psl_in->pol_group) &= ~BIT(psl_in->pol_bit);
139 	}
140 
141 	/* Configure detection mode of PSL input pads */
142 	if (pin->flags.psl_in_mode == NPCX_PSL_IN_MODE_EDGE) {
143 		inst_glue->PSL_CTS |= NPCX_PSL_CTS_MODE_BIT(psl_in->port);
144 	} else {
145 		inst_glue->PSL_CTS &= ~NPCX_PSL_CTS_MODE_BIT(psl_in->port);
146 	}
147 }
148 
npcx_device_control_configure(const pinctrl_soc_pin_t * pin)149 static void npcx_device_control_configure(const pinctrl_soc_pin_t *pin)
150 {
151 	const struct npcx_dev_ctl *ctrl = (const struct npcx_dev_ctl *)&pin->cfg.dev_ctl;
152 	const uintptr_t scfg_base = npcx_pinctrl_cfg.base_scfg;
153 
154 	SET_FIELD(NPCX_DEV_CTL(scfg_base, ctrl->offest),
155 			      FIELD(ctrl->field_offset, ctrl->field_size),
156 			      ctrl->field_value);
157 }
158 
159 /* Pinctrl API implementation */
pinctrl_configure_pins(const pinctrl_soc_pin_t * pins,uint8_t pin_cnt,uintptr_t reg)160 int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt,
161 			   uintptr_t reg)
162 {
163 	ARG_UNUSED(reg);
164 
165 	/* Configure all peripheral devices' properties here. */
166 	for (uint8_t i = 0; i < pin_cnt; i++) {
167 		if (pins[i].flags.type == NPCX_PINCTRL_TYPE_PERIPH) {
168 			/* Configure peripheral device's pinmux functionality */
169 			npcx_periph_configure(&pins[i], reg);
170 		} else if (pins[i].flags.type == NPCX_PINCTRL_TYPE_DEVICE_CTRL) {
171 			/* Configure device's io characteristics */
172 			npcx_device_control_configure(&pins[i]);
173 		} else if (pins[i].flags.type == NPCX_PINCTRL_TYPE_PSL_IN) {
174 			/* Configure SPL input's detection mode */
175 			npcx_psl_input_detection_configure(&pins[i]);
176 		} else {
177 			return -ENOTSUP;
178 		}
179 	}
180 
181 	return 0;
182 }
183