xref: /linux/drivers/mux/gpio.c (revision 8f3addf1b451784104ba3593d80702b4815d5a94)
1*8f3addf1SPeter Rosin /*
2*8f3addf1SPeter Rosin  * GPIO-controlled multiplexer driver
3*8f3addf1SPeter Rosin  *
4*8f3addf1SPeter Rosin  * Copyright (C) 2017 Axentia Technologies AB
5*8f3addf1SPeter Rosin  *
6*8f3addf1SPeter Rosin  * Author: Peter Rosin <peda@axentia.se>
7*8f3addf1SPeter Rosin  *
8*8f3addf1SPeter Rosin  * This program is free software; you can redistribute it and/or modify
9*8f3addf1SPeter Rosin  * it under the terms of the GNU General Public License version 2 as
10*8f3addf1SPeter Rosin  * published by the Free Software Foundation.
11*8f3addf1SPeter Rosin  */
12*8f3addf1SPeter Rosin 
13*8f3addf1SPeter Rosin #include <linux/err.h>
14*8f3addf1SPeter Rosin #include <linux/gpio/consumer.h>
15*8f3addf1SPeter Rosin #include <linux/module.h>
16*8f3addf1SPeter Rosin #include <linux/mux/driver.h>
17*8f3addf1SPeter Rosin #include <linux/of_platform.h>
18*8f3addf1SPeter Rosin #include <linux/platform_device.h>
19*8f3addf1SPeter Rosin #include <linux/property.h>
20*8f3addf1SPeter Rosin 
21*8f3addf1SPeter Rosin struct mux_gpio {
22*8f3addf1SPeter Rosin 	struct gpio_descs *gpios;
23*8f3addf1SPeter Rosin 	int *val;
24*8f3addf1SPeter Rosin };
25*8f3addf1SPeter Rosin 
26*8f3addf1SPeter Rosin static int mux_gpio_set(struct mux_control *mux, int state)
27*8f3addf1SPeter Rosin {
28*8f3addf1SPeter Rosin 	struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
29*8f3addf1SPeter Rosin 	int i;
30*8f3addf1SPeter Rosin 
31*8f3addf1SPeter Rosin 	for (i = 0; i < mux_gpio->gpios->ndescs; i++)
32*8f3addf1SPeter Rosin 		mux_gpio->val[i] = (state >> i) & 1;
33*8f3addf1SPeter Rosin 
34*8f3addf1SPeter Rosin 	gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
35*8f3addf1SPeter Rosin 				       mux_gpio->gpios->desc,
36*8f3addf1SPeter Rosin 				       mux_gpio->val);
37*8f3addf1SPeter Rosin 
38*8f3addf1SPeter Rosin 	return 0;
39*8f3addf1SPeter Rosin }
40*8f3addf1SPeter Rosin 
41*8f3addf1SPeter Rosin static const struct mux_control_ops mux_gpio_ops = {
42*8f3addf1SPeter Rosin 	.set = mux_gpio_set,
43*8f3addf1SPeter Rosin };
44*8f3addf1SPeter Rosin 
45*8f3addf1SPeter Rosin static const struct of_device_id mux_gpio_dt_ids[] = {
46*8f3addf1SPeter Rosin 	{ .compatible = "gpio-mux", },
47*8f3addf1SPeter Rosin 	{ /* sentinel */ }
48*8f3addf1SPeter Rosin };
49*8f3addf1SPeter Rosin MODULE_DEVICE_TABLE(of, mux_gpio_dt_ids);
50*8f3addf1SPeter Rosin 
51*8f3addf1SPeter Rosin static int mux_gpio_probe(struct platform_device *pdev)
52*8f3addf1SPeter Rosin {
53*8f3addf1SPeter Rosin 	struct device *dev = &pdev->dev;
54*8f3addf1SPeter Rosin 	struct mux_chip *mux_chip;
55*8f3addf1SPeter Rosin 	struct mux_gpio *mux_gpio;
56*8f3addf1SPeter Rosin 	int pins;
57*8f3addf1SPeter Rosin 	s32 idle_state;
58*8f3addf1SPeter Rosin 	int ret;
59*8f3addf1SPeter Rosin 
60*8f3addf1SPeter Rosin 	pins = gpiod_count(dev, "mux");
61*8f3addf1SPeter Rosin 	if (pins < 0)
62*8f3addf1SPeter Rosin 		return pins;
63*8f3addf1SPeter Rosin 
64*8f3addf1SPeter Rosin 	mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio) +
65*8f3addf1SPeter Rosin 				       pins * sizeof(*mux_gpio->val));
66*8f3addf1SPeter Rosin 	if (IS_ERR(mux_chip))
67*8f3addf1SPeter Rosin 		return PTR_ERR(mux_chip);
68*8f3addf1SPeter Rosin 
69*8f3addf1SPeter Rosin 	mux_gpio = mux_chip_priv(mux_chip);
70*8f3addf1SPeter Rosin 	mux_gpio->val = (int *)(mux_gpio + 1);
71*8f3addf1SPeter Rosin 	mux_chip->ops = &mux_gpio_ops;
72*8f3addf1SPeter Rosin 
73*8f3addf1SPeter Rosin 	mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW);
74*8f3addf1SPeter Rosin 	if (IS_ERR(mux_gpio->gpios)) {
75*8f3addf1SPeter Rosin 		ret = PTR_ERR(mux_gpio->gpios);
76*8f3addf1SPeter Rosin 		if (ret != -EPROBE_DEFER)
77*8f3addf1SPeter Rosin 			dev_err(dev, "failed to get gpios\n");
78*8f3addf1SPeter Rosin 		return ret;
79*8f3addf1SPeter Rosin 	}
80*8f3addf1SPeter Rosin 	WARN_ON(pins != mux_gpio->gpios->ndescs);
81*8f3addf1SPeter Rosin 	mux_chip->mux->states = 1 << pins;
82*8f3addf1SPeter Rosin 
83*8f3addf1SPeter Rosin 	ret = device_property_read_u32(dev, "idle-state", (u32 *)&idle_state);
84*8f3addf1SPeter Rosin 	if (ret >= 0 && idle_state != MUX_IDLE_AS_IS) {
85*8f3addf1SPeter Rosin 		if (idle_state < 0 || idle_state >= mux_chip->mux->states) {
86*8f3addf1SPeter Rosin 			dev_err(dev, "invalid idle-state %u\n", idle_state);
87*8f3addf1SPeter Rosin 			return -EINVAL;
88*8f3addf1SPeter Rosin 		}
89*8f3addf1SPeter Rosin 
90*8f3addf1SPeter Rosin 		mux_chip->mux->idle_state = idle_state;
91*8f3addf1SPeter Rosin 	}
92*8f3addf1SPeter Rosin 
93*8f3addf1SPeter Rosin 	ret = devm_mux_chip_register(dev, mux_chip);
94*8f3addf1SPeter Rosin 	if (ret < 0)
95*8f3addf1SPeter Rosin 		return ret;
96*8f3addf1SPeter Rosin 
97*8f3addf1SPeter Rosin 	dev_info(dev, "%u-way mux-controller registered\n",
98*8f3addf1SPeter Rosin 		 mux_chip->mux->states);
99*8f3addf1SPeter Rosin 
100*8f3addf1SPeter Rosin 	return 0;
101*8f3addf1SPeter Rosin }
102*8f3addf1SPeter Rosin 
103*8f3addf1SPeter Rosin static struct platform_driver mux_gpio_driver = {
104*8f3addf1SPeter Rosin 	.driver = {
105*8f3addf1SPeter Rosin 		.name = "gpio-mux",
106*8f3addf1SPeter Rosin 		.of_match_table	= of_match_ptr(mux_gpio_dt_ids),
107*8f3addf1SPeter Rosin 	},
108*8f3addf1SPeter Rosin 	.probe = mux_gpio_probe,
109*8f3addf1SPeter Rosin };
110*8f3addf1SPeter Rosin module_platform_driver(mux_gpio_driver);
111*8f3addf1SPeter Rosin 
112*8f3addf1SPeter Rosin MODULE_DESCRIPTION("GPIO-controlled multiplexer driver");
113*8f3addf1SPeter Rosin MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
114*8f3addf1SPeter Rosin MODULE_LICENSE("GPL v2");
115