1f73f88acSzhanghongchen // SPDX-License-Identifier: GPL-2.0+
2f73f88acSzhanghongchen /*
3f73f88acSzhanghongchen * Author: zhanghongchen <zhanghongchen@loongson.cn>
4f73f88acSzhanghongchen * Yinbo Zhu <zhuyinbo@loongson.cn>
5f73f88acSzhanghongchen * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
6f73f88acSzhanghongchen */
7f73f88acSzhanghongchen
8f73f88acSzhanghongchen #include <linux/init.h>
9f73f88acSzhanghongchen #include <linux/module.h>
10f73f88acSzhanghongchen #include <linux/platform_device.h>
11f73f88acSzhanghongchen #include <linux/mod_devicetable.h>
12f73f88acSzhanghongchen #include <linux/pinctrl/pinmux.h>
13f73f88acSzhanghongchen #include <linux/pinctrl/pinconf-generic.h>
14f73f88acSzhanghongchen #include <linux/pinctrl/pinctrl.h>
15f73f88acSzhanghongchen #include <linux/bitops.h>
16f73f88acSzhanghongchen #include <linux/io.h>
17f73f88acSzhanghongchen #include <linux/seq_file.h>
18f73f88acSzhanghongchen
19f73f88acSzhanghongchen #include "core.h"
20f73f88acSzhanghongchen #include "pinctrl-utils.h"
21f73f88acSzhanghongchen
22f73f88acSzhanghongchen #define PMX_GROUP(name, offset, bitv) \
23f73f88acSzhanghongchen { \
24f73f88acSzhanghongchen .grp = PINCTRL_PINGROUP((#name), (name ## _pins), \
25f73f88acSzhanghongchen ARRAY_SIZE((name ## _pins))), \
26f73f88acSzhanghongchen .reg = offset, \
27f73f88acSzhanghongchen .bit = bitv, \
28f73f88acSzhanghongchen }
29f73f88acSzhanghongchen
30f73f88acSzhanghongchen #define SPECIFIC_GROUP(group) \
31f73f88acSzhanghongchen static const char * const group##_groups[] = { \
32f73f88acSzhanghongchen #group \
33f73f88acSzhanghongchen }
34f73f88acSzhanghongchen
35f73f88acSzhanghongchen #define FUNCTION(fn) \
36f73f88acSzhanghongchen { \
37f73f88acSzhanghongchen .name = #fn, \
38f73f88acSzhanghongchen .groups = fn ## _groups, \
39f73f88acSzhanghongchen .num_groups = ARRAY_SIZE(fn ## _groups), \
40f73f88acSzhanghongchen }
41f73f88acSzhanghongchen
42f73f88acSzhanghongchen struct loongson2_pinctrl {
43f73f88acSzhanghongchen struct device *dev;
44f73f88acSzhanghongchen struct pinctrl_dev *pcdev;
45f73f88acSzhanghongchen struct pinctrl_desc desc;
46f73f88acSzhanghongchen struct device_node *of_node;
47f73f88acSzhanghongchen spinlock_t lock;
48*7ebfe10eSLinus Walleij void __iomem *reg_base;
49f73f88acSzhanghongchen };
50f73f88acSzhanghongchen
51f73f88acSzhanghongchen struct loongson2_pmx_group {
52f73f88acSzhanghongchen struct pingroup grp;
53f73f88acSzhanghongchen unsigned int reg;
54f73f88acSzhanghongchen unsigned int bit;
55f73f88acSzhanghongchen };
56f73f88acSzhanghongchen
57f73f88acSzhanghongchen struct loongson2_pmx_func {
58f73f88acSzhanghongchen const char *name;
59f73f88acSzhanghongchen const char * const *groups;
60f73f88acSzhanghongchen unsigned int num_groups;
61f73f88acSzhanghongchen };
62f73f88acSzhanghongchen
63f73f88acSzhanghongchen #define LOONGSON2_PIN(x) PINCTRL_PIN(x, "gpio"#x)
64f73f88acSzhanghongchen static const struct pinctrl_pin_desc loongson2_pctrl_pins[] = {
65f73f88acSzhanghongchen LOONGSON2_PIN(0), LOONGSON2_PIN(1), LOONGSON2_PIN(2), LOONGSON2_PIN(3),
66f73f88acSzhanghongchen LOONGSON2_PIN(4), LOONGSON2_PIN(5), LOONGSON2_PIN(6), LOONGSON2_PIN(7),
67f73f88acSzhanghongchen LOONGSON2_PIN(8), LOONGSON2_PIN(9), LOONGSON2_PIN(10), LOONGSON2_PIN(11),
68f73f88acSzhanghongchen LOONGSON2_PIN(12), LOONGSON2_PIN(13), LOONGSON2_PIN(14),
69f73f88acSzhanghongchen LOONGSON2_PIN(16), LOONGSON2_PIN(17), LOONGSON2_PIN(18), LOONGSON2_PIN(19),
70f73f88acSzhanghongchen LOONGSON2_PIN(20), LOONGSON2_PIN(21), LOONGSON2_PIN(22), LOONGSON2_PIN(23),
71f73f88acSzhanghongchen LOONGSON2_PIN(24), LOONGSON2_PIN(25), LOONGSON2_PIN(26), LOONGSON2_PIN(27),
72f73f88acSzhanghongchen LOONGSON2_PIN(28), LOONGSON2_PIN(29), LOONGSON2_PIN(30),
73f73f88acSzhanghongchen LOONGSON2_PIN(32), LOONGSON2_PIN(33), LOONGSON2_PIN(34), LOONGSON2_PIN(35),
74f73f88acSzhanghongchen LOONGSON2_PIN(36), LOONGSON2_PIN(37), LOONGSON2_PIN(38), LOONGSON2_PIN(39),
75f73f88acSzhanghongchen LOONGSON2_PIN(40), LOONGSON2_PIN(41),
76f73f88acSzhanghongchen LOONGSON2_PIN(44), LOONGSON2_PIN(45), LOONGSON2_PIN(46), LOONGSON2_PIN(47),
77f73f88acSzhanghongchen LOONGSON2_PIN(48), LOONGSON2_PIN(49), LOONGSON2_PIN(50), LOONGSON2_PIN(51),
78f73f88acSzhanghongchen LOONGSON2_PIN(52), LOONGSON2_PIN(53), LOONGSON2_PIN(54), LOONGSON2_PIN(55),
79f73f88acSzhanghongchen LOONGSON2_PIN(56), LOONGSON2_PIN(57), LOONGSON2_PIN(58), LOONGSON2_PIN(59),
80f73f88acSzhanghongchen LOONGSON2_PIN(60), LOONGSON2_PIN(61), LOONGSON2_PIN(62), LOONGSON2_PIN(63),
81f73f88acSzhanghongchen };
82f73f88acSzhanghongchen
83f73f88acSzhanghongchen static const unsigned int gpio_pins[] = {0, 1, 2, 3, 4, 5, 6, 7,
84f73f88acSzhanghongchen 8, 9, 10, 11, 12, 13, 14,
85f73f88acSzhanghongchen 16, 17, 18, 19, 20, 21, 22, 23,
86f73f88acSzhanghongchen 24, 25, 26, 27, 28, 29, 30,
87f73f88acSzhanghongchen 32, 33, 34, 35, 36, 37, 38, 39,
88f73f88acSzhanghongchen 40, 43, 44, 45, 46, 47,
89f73f88acSzhanghongchen 48, 49, 50, 51, 52, 53, 46, 55,
90f73f88acSzhanghongchen 56, 57, 58, 59, 60, 61, 62, 63};
91f73f88acSzhanghongchen static const unsigned int sdio_pins[] = {36, 37, 38, 39, 40, 41};
92f73f88acSzhanghongchen static const unsigned int can1_pins[] = {34, 35};
93f73f88acSzhanghongchen static const unsigned int can0_pins[] = {32, 33};
94f73f88acSzhanghongchen static const unsigned int pwm3_pins[] = {23};
95f73f88acSzhanghongchen static const unsigned int pwm2_pins[] = {22};
96f73f88acSzhanghongchen static const unsigned int pwm1_pins[] = {21};
97f73f88acSzhanghongchen static const unsigned int pwm0_pins[] = {20};
98f73f88acSzhanghongchen static const unsigned int i2c1_pins[] = {18, 19};
99f73f88acSzhanghongchen static const unsigned int i2c0_pins[] = {16, 17};
100f73f88acSzhanghongchen static const unsigned int nand_pins[] = {44, 45, 46, 47, 48, 49, 50, 51,
101f73f88acSzhanghongchen 52, 53, 54, 55, 56, 57, 58, 59, 60,
102f73f88acSzhanghongchen 61, 62, 63};
103f73f88acSzhanghongchen static const unsigned int sata_led_pins[] = {14};
104f73f88acSzhanghongchen static const unsigned int i2s_pins[] = {24, 25, 26, 27, 28};
105f73f88acSzhanghongchen static const unsigned int hda_pins[] = {24, 25, 26, 27, 28, 29, 30};
106f73f88acSzhanghongchen
107f73f88acSzhanghongchen static struct loongson2_pmx_group loongson2_pmx_groups[] = {
108f73f88acSzhanghongchen PMX_GROUP(gpio, 0x0, 64),
109f73f88acSzhanghongchen PMX_GROUP(sdio, 0x0, 20),
110f73f88acSzhanghongchen PMX_GROUP(can1, 0x0, 17),
111f73f88acSzhanghongchen PMX_GROUP(can0, 0x0, 16),
112f73f88acSzhanghongchen PMX_GROUP(pwm3, 0x0, 15),
113f73f88acSzhanghongchen PMX_GROUP(pwm2, 0x0, 14),
114f73f88acSzhanghongchen PMX_GROUP(pwm1, 0x0, 13),
115f73f88acSzhanghongchen PMX_GROUP(pwm0, 0x0, 12),
116f73f88acSzhanghongchen PMX_GROUP(i2c1, 0x0, 11),
117f73f88acSzhanghongchen PMX_GROUP(i2c0, 0x0, 10),
118f73f88acSzhanghongchen PMX_GROUP(nand, 0x0, 9),
119f73f88acSzhanghongchen PMX_GROUP(sata_led, 0x0, 8),
120f73f88acSzhanghongchen PMX_GROUP(i2s, 0x0, 6),
121f73f88acSzhanghongchen PMX_GROUP(hda, 0x0, 4),
122f73f88acSzhanghongchen };
123f73f88acSzhanghongchen
124f73f88acSzhanghongchen SPECIFIC_GROUP(sdio);
125f73f88acSzhanghongchen SPECIFIC_GROUP(can1);
126f73f88acSzhanghongchen SPECIFIC_GROUP(can0);
127f73f88acSzhanghongchen SPECIFIC_GROUP(pwm3);
128f73f88acSzhanghongchen SPECIFIC_GROUP(pwm2);
129f73f88acSzhanghongchen SPECIFIC_GROUP(pwm1);
130f73f88acSzhanghongchen SPECIFIC_GROUP(pwm0);
131f73f88acSzhanghongchen SPECIFIC_GROUP(i2c1);
132f73f88acSzhanghongchen SPECIFIC_GROUP(i2c0);
133f73f88acSzhanghongchen SPECIFIC_GROUP(nand);
134f73f88acSzhanghongchen SPECIFIC_GROUP(sata_led);
135f73f88acSzhanghongchen SPECIFIC_GROUP(i2s);
136f73f88acSzhanghongchen SPECIFIC_GROUP(hda);
137f73f88acSzhanghongchen
138f73f88acSzhanghongchen static const char * const gpio_groups[] = {
139f73f88acSzhanghongchen "sdio",
140f73f88acSzhanghongchen "can1", "can0",
141f73f88acSzhanghongchen "pwm3", "pwm2", "pwm1", "pwm0",
142f73f88acSzhanghongchen "i2c1", "i2c0",
143f73f88acSzhanghongchen "nand",
144f73f88acSzhanghongchen "sata_led",
145f73f88acSzhanghongchen "i2s",
146f73f88acSzhanghongchen "hda",
147f73f88acSzhanghongchen };
148f73f88acSzhanghongchen
149f73f88acSzhanghongchen static const struct loongson2_pmx_func loongson2_pmx_functions[] = {
150f73f88acSzhanghongchen FUNCTION(gpio),
151f73f88acSzhanghongchen FUNCTION(sdio),
152f73f88acSzhanghongchen FUNCTION(can1),
153f73f88acSzhanghongchen FUNCTION(can0),
154f73f88acSzhanghongchen FUNCTION(pwm3),
155f73f88acSzhanghongchen FUNCTION(pwm2),
156f73f88acSzhanghongchen FUNCTION(pwm1),
157f73f88acSzhanghongchen FUNCTION(pwm0),
158f73f88acSzhanghongchen FUNCTION(i2c1),
159f73f88acSzhanghongchen FUNCTION(i2c0),
160f73f88acSzhanghongchen FUNCTION(nand),
161f73f88acSzhanghongchen FUNCTION(sata_led),
162f73f88acSzhanghongchen FUNCTION(i2s),
163f73f88acSzhanghongchen FUNCTION(hda),
164f73f88acSzhanghongchen };
165f73f88acSzhanghongchen
loongson2_get_groups_count(struct pinctrl_dev * pcdev)166f73f88acSzhanghongchen static int loongson2_get_groups_count(struct pinctrl_dev *pcdev)
167f73f88acSzhanghongchen {
168f73f88acSzhanghongchen return ARRAY_SIZE(loongson2_pmx_groups);
169f73f88acSzhanghongchen }
170f73f88acSzhanghongchen
loongson2_get_group_name(struct pinctrl_dev * pcdev,unsigned int selector)171f73f88acSzhanghongchen static const char *loongson2_get_group_name(struct pinctrl_dev *pcdev,
172f73f88acSzhanghongchen unsigned int selector)
173f73f88acSzhanghongchen {
174f73f88acSzhanghongchen return loongson2_pmx_groups[selector].grp.name;
175f73f88acSzhanghongchen }
176f73f88acSzhanghongchen
loongson2_get_group_pins(struct pinctrl_dev * pcdev,unsigned int selector,const unsigned int ** pins,unsigned int * num_pins)177f73f88acSzhanghongchen static int loongson2_get_group_pins(struct pinctrl_dev *pcdev, unsigned int selector,
178f73f88acSzhanghongchen const unsigned int **pins, unsigned int *num_pins)
179f73f88acSzhanghongchen {
180f73f88acSzhanghongchen *pins = loongson2_pmx_groups[selector].grp.pins;
181f73f88acSzhanghongchen *num_pins = loongson2_pmx_groups[selector].grp.npins;
182f73f88acSzhanghongchen
183f73f88acSzhanghongchen return 0;
184f73f88acSzhanghongchen }
185f73f88acSzhanghongchen
loongson2_pin_dbg_show(struct pinctrl_dev * pcdev,struct seq_file * s,unsigned int offset)186f73f88acSzhanghongchen static void loongson2_pin_dbg_show(struct pinctrl_dev *pcdev, struct seq_file *s,
187f73f88acSzhanghongchen unsigned int offset)
188f73f88acSzhanghongchen {
189f73f88acSzhanghongchen seq_printf(s, " %s", dev_name(pcdev->dev));
190f73f88acSzhanghongchen }
191f73f88acSzhanghongchen
192f73f88acSzhanghongchen static const struct pinctrl_ops loongson2_pctrl_ops = {
193f73f88acSzhanghongchen .get_groups_count = loongson2_get_groups_count,
194f73f88acSzhanghongchen .get_group_name = loongson2_get_group_name,
195f73f88acSzhanghongchen .get_group_pins = loongson2_get_group_pins,
196f73f88acSzhanghongchen .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
197f73f88acSzhanghongchen .dt_free_map = pinctrl_utils_free_map,
198f73f88acSzhanghongchen .pin_dbg_show = loongson2_pin_dbg_show,
199f73f88acSzhanghongchen };
200f73f88acSzhanghongchen
loongson2_pmx_set_mux(struct pinctrl_dev * pcdev,unsigned int func_num,unsigned int group_num)201f73f88acSzhanghongchen static int loongson2_pmx_set_mux(struct pinctrl_dev *pcdev, unsigned int func_num,
202f73f88acSzhanghongchen unsigned int group_num)
203f73f88acSzhanghongchen {
204f73f88acSzhanghongchen struct loongson2_pinctrl *pctrl = pinctrl_dev_get_drvdata(pcdev);
205*7ebfe10eSLinus Walleij void __iomem *reg = pctrl->reg_base +
206f73f88acSzhanghongchen loongson2_pmx_groups[group_num].reg;
207f73f88acSzhanghongchen unsigned int mux_bit = loongson2_pmx_groups[group_num].bit;
208f73f88acSzhanghongchen unsigned int val;
209f73f88acSzhanghongchen unsigned long flags;
210f73f88acSzhanghongchen
211f73f88acSzhanghongchen spin_lock_irqsave(&pctrl->lock, flags);
212f73f88acSzhanghongchen val = readl(reg);
213f73f88acSzhanghongchen if (func_num == 0)
214f73f88acSzhanghongchen val &= ~BIT(mux_bit);
215f73f88acSzhanghongchen else
216f73f88acSzhanghongchen val |= BIT(mux_bit);
217f73f88acSzhanghongchen writel(val, reg);
218f73f88acSzhanghongchen spin_unlock_irqrestore(&pctrl->lock, flags);
219f73f88acSzhanghongchen
220f73f88acSzhanghongchen return 0;
221f73f88acSzhanghongchen }
222f73f88acSzhanghongchen
loongson2_pmx_get_funcs_count(struct pinctrl_dev * pcdev)223f73f88acSzhanghongchen static int loongson2_pmx_get_funcs_count(struct pinctrl_dev *pcdev)
224f73f88acSzhanghongchen {
225f73f88acSzhanghongchen return ARRAY_SIZE(loongson2_pmx_functions);
226f73f88acSzhanghongchen }
227f73f88acSzhanghongchen
loongson2_pmx_get_func_name(struct pinctrl_dev * pcdev,unsigned int selector)228f73f88acSzhanghongchen static const char *loongson2_pmx_get_func_name(struct pinctrl_dev *pcdev,
229f73f88acSzhanghongchen unsigned int selector)
230f73f88acSzhanghongchen {
231f73f88acSzhanghongchen return loongson2_pmx_functions[selector].name;
232f73f88acSzhanghongchen }
233f73f88acSzhanghongchen
loongson2_pmx_get_groups(struct pinctrl_dev * pcdev,unsigned int selector,const char * const ** groups,unsigned int * const num_groups)234f73f88acSzhanghongchen static int loongson2_pmx_get_groups(struct pinctrl_dev *pcdev,
235f73f88acSzhanghongchen unsigned int selector,
236f73f88acSzhanghongchen const char * const **groups,
237f73f88acSzhanghongchen unsigned int * const num_groups)
238f73f88acSzhanghongchen {
239f73f88acSzhanghongchen *groups = loongson2_pmx_functions[selector].groups;
240f73f88acSzhanghongchen *num_groups = loongson2_pmx_functions[selector].num_groups;
241f73f88acSzhanghongchen
242f73f88acSzhanghongchen return 0;
243f73f88acSzhanghongchen }
244f73f88acSzhanghongchen
245f73f88acSzhanghongchen static const struct pinmux_ops loongson2_pmx_ops = {
246f73f88acSzhanghongchen .set_mux = loongson2_pmx_set_mux,
247f73f88acSzhanghongchen .get_functions_count = loongson2_pmx_get_funcs_count,
248f73f88acSzhanghongchen .get_function_name = loongson2_pmx_get_func_name,
249f73f88acSzhanghongchen .get_function_groups = loongson2_pmx_get_groups,
250f73f88acSzhanghongchen };
251f73f88acSzhanghongchen
loongson2_pinctrl_probe(struct platform_device * pdev)252f73f88acSzhanghongchen static int loongson2_pinctrl_probe(struct platform_device *pdev)
253f73f88acSzhanghongchen {
254f73f88acSzhanghongchen struct device *dev = &pdev->dev;
255f73f88acSzhanghongchen struct loongson2_pinctrl *pctrl;
256f73f88acSzhanghongchen
257f73f88acSzhanghongchen pctrl = devm_kzalloc(dev, sizeof(*pctrl), GFP_KERNEL);
258f73f88acSzhanghongchen if (!pctrl)
259f73f88acSzhanghongchen return -ENOMEM;
260f73f88acSzhanghongchen
261f73f88acSzhanghongchen pctrl->reg_base = devm_platform_ioremap_resource(pdev, 0);
262f73f88acSzhanghongchen if (IS_ERR(pctrl->reg_base))
263f73f88acSzhanghongchen return PTR_ERR(pctrl->reg_base);
264f73f88acSzhanghongchen
265f73f88acSzhanghongchen spin_lock_init(&pctrl->lock);
266f73f88acSzhanghongchen
267f73f88acSzhanghongchen pctrl->dev = dev;
268f73f88acSzhanghongchen pctrl->desc.name = "pinctrl-loongson2";
269f73f88acSzhanghongchen pctrl->desc.owner = THIS_MODULE;
270f73f88acSzhanghongchen pctrl->desc.pctlops = &loongson2_pctrl_ops;
271f73f88acSzhanghongchen pctrl->desc.pmxops = &loongson2_pmx_ops;
272f73f88acSzhanghongchen pctrl->desc.pins = loongson2_pctrl_pins;
273f73f88acSzhanghongchen pctrl->desc.npins = ARRAY_SIZE(loongson2_pctrl_pins);
274f73f88acSzhanghongchen
275f73f88acSzhanghongchen pctrl->pcdev = devm_pinctrl_register(pctrl->dev, &pctrl->desc, pctrl);
276f73f88acSzhanghongchen if (IS_ERR(pctrl->pcdev))
277f73f88acSzhanghongchen return dev_err_probe(pctrl->dev, PTR_ERR(pctrl->pcdev),
278f73f88acSzhanghongchen "can't register pinctrl device");
279f73f88acSzhanghongchen
280f73f88acSzhanghongchen return 0;
281f73f88acSzhanghongchen }
282f73f88acSzhanghongchen
283f73f88acSzhanghongchen static const struct of_device_id loongson2_pinctrl_dt_match[] = {
284f73f88acSzhanghongchen {
285f73f88acSzhanghongchen .compatible = "loongson,ls2k-pinctrl",
286f73f88acSzhanghongchen },
287f73f88acSzhanghongchen { }
288f73f88acSzhanghongchen };
289f73f88acSzhanghongchen
290f73f88acSzhanghongchen static struct platform_driver loongson2_pinctrl_driver = {
291f73f88acSzhanghongchen .probe = loongson2_pinctrl_probe,
292f73f88acSzhanghongchen .driver = {
293f73f88acSzhanghongchen .name = "loongson2-pinctrl",
294f73f88acSzhanghongchen .of_match_table = loongson2_pinctrl_dt_match,
295f73f88acSzhanghongchen },
296f73f88acSzhanghongchen };
297f73f88acSzhanghongchen
loongson2_pinctrl_init(void)298f73f88acSzhanghongchen static int __init loongson2_pinctrl_init(void)
299f73f88acSzhanghongchen {
300f73f88acSzhanghongchen return platform_driver_register(&loongson2_pinctrl_driver);
301f73f88acSzhanghongchen }
302f73f88acSzhanghongchen arch_initcall(loongson2_pinctrl_init);
303f73f88acSzhanghongchen
loongson2_pinctrl_exit(void)304f73f88acSzhanghongchen static void __exit loongson2_pinctrl_exit(void)
305f73f88acSzhanghongchen {
306f73f88acSzhanghongchen platform_driver_unregister(&loongson2_pinctrl_driver);
307f73f88acSzhanghongchen }
308f73f88acSzhanghongchen module_exit(loongson2_pinctrl_exit);
309f73f88acSzhanghongchen
310f73f88acSzhanghongchen MODULE_DESCRIPTION("Loongson2 Pinctrl driver");
311f73f88acSzhanghongchen MODULE_LICENSE("GPL");
312