1 // SPDX-License-Identifier: GPL-2.0
3 * Turris Mox Moxtet GPIO expander
5 * Copyright (C) 2018 Marek Behun <marek.behun@nic.cz>
8 #include <linux/bitops.h>
9 #include <linux/gpio/driver.h>
10 #include <linux/moxtet.h>
11 #include <linux/module.h>
13 #define MOXTET_GPIO_NGPIOS 12
14 #define MOXTET_GPIO_INPUTS 4
16 struct moxtet_gpio_desc
{
21 static const struct moxtet_gpio_desc descs
[] = {
22 [TURRIS_MOX_MODULE_SFP
] = {
23 .in_mask
= GENMASK(2, 0),
24 .out_mask
= GENMASK(5, 4),
28 struct moxtet_gpio_chip
{
30 struct gpio_chip gpio_chip
;
31 const struct moxtet_gpio_desc
*desc
;
34 static int moxtet_gpio_get_value(struct gpio_chip
*gc
, unsigned int offset
)
36 struct moxtet_gpio_chip
*chip
= gpiochip_get_data(gc
);
39 if (chip
->desc
->in_mask
& BIT(offset
)) {
40 ret
= moxtet_device_read(chip
->dev
);
41 } else if (chip
->desc
->out_mask
& BIT(offset
)) {
42 ret
= moxtet_device_written(chip
->dev
);
44 ret
<<= MOXTET_GPIO_INPUTS
;
52 return !!(ret
& BIT(offset
));
55 static void moxtet_gpio_set_value(struct gpio_chip
*gc
, unsigned int offset
,
58 struct moxtet_gpio_chip
*chip
= gpiochip_get_data(gc
);
61 state
= moxtet_device_written(chip
->dev
);
65 offset
-= MOXTET_GPIO_INPUTS
;
70 state
&= ~BIT(offset
);
72 moxtet_device_write(chip
->dev
, state
);
75 static int moxtet_gpio_get_direction(struct gpio_chip
*gc
, unsigned int offset
)
77 struct moxtet_gpio_chip
*chip
= gpiochip_get_data(gc
);
79 /* All lines are hard wired to be either input or output, not both. */
80 if (chip
->desc
->in_mask
& BIT(offset
))
81 return GPIO_LINE_DIRECTION_IN
;
82 else if (chip
->desc
->out_mask
& BIT(offset
))
83 return GPIO_LINE_DIRECTION_OUT
;
88 static int moxtet_gpio_direction_input(struct gpio_chip
*gc
,
91 struct moxtet_gpio_chip
*chip
= gpiochip_get_data(gc
);
93 if (chip
->desc
->in_mask
& BIT(offset
))
95 else if (chip
->desc
->out_mask
& BIT(offset
))
101 static int moxtet_gpio_direction_output(struct gpio_chip
*gc
,
102 unsigned int offset
, int val
)
104 struct moxtet_gpio_chip
*chip
= gpiochip_get_data(gc
);
106 if (chip
->desc
->out_mask
& BIT(offset
))
107 moxtet_gpio_set_value(gc
, offset
, val
);
108 else if (chip
->desc
->in_mask
& BIT(offset
))
116 static int moxtet_gpio_probe(struct device
*dev
)
118 struct moxtet_gpio_chip
*chip
;
119 struct device_node
*nc
= dev
->of_node
;
122 id
= to_moxtet_device(dev
)->id
;
124 if (id
>= ARRAY_SIZE(descs
)) {
125 dev_err(dev
, "%pOF Moxtet device id 0x%x is not supported by gpio-moxtet driver\n",
130 chip
= devm_kzalloc(dev
, sizeof(*chip
), GFP_KERNEL
);
135 chip
->gpio_chip
.parent
= dev
;
136 chip
->desc
= &descs
[id
];
138 dev_set_drvdata(dev
, chip
);
140 chip
->gpio_chip
.label
= dev_name(dev
);
141 chip
->gpio_chip
.get_direction
= moxtet_gpio_get_direction
;
142 chip
->gpio_chip
.direction_input
= moxtet_gpio_direction_input
;
143 chip
->gpio_chip
.direction_output
= moxtet_gpio_direction_output
;
144 chip
->gpio_chip
.get
= moxtet_gpio_get_value
;
145 chip
->gpio_chip
.set
= moxtet_gpio_set_value
;
146 chip
->gpio_chip
.base
= -1;
148 chip
->gpio_chip
.ngpio
= MOXTET_GPIO_NGPIOS
;
150 chip
->gpio_chip
.can_sleep
= true;
151 chip
->gpio_chip
.owner
= THIS_MODULE
;
153 return devm_gpiochip_add_data(dev
, &chip
->gpio_chip
, chip
);
156 static const struct of_device_id moxtet_gpio_dt_ids
[] = {
157 { .compatible
= "cznic,moxtet-gpio", },
160 MODULE_DEVICE_TABLE(of
, moxtet_gpio_dt_ids
);
162 static const enum turris_mox_module_id moxtet_gpio_module_table
[] = {
163 TURRIS_MOX_MODULE_SFP
,
167 static struct moxtet_driver moxtet_gpio_driver
= {
169 .name
= "moxtet-gpio",
170 .of_match_table
= moxtet_gpio_dt_ids
,
171 .probe
= moxtet_gpio_probe
,
173 .id_table
= moxtet_gpio_module_table
,
175 module_moxtet_driver(moxtet_gpio_driver
);
177 MODULE_AUTHOR("Marek Behun <marek.behun@nic.cz>");
178 MODULE_DESCRIPTION("Turris Mox Moxtet GPIO expander");
179 MODULE_LICENSE("GPL v2");