1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2019 Bootlin
4 * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
8 #include <linux/gpio/driver.h>
9 #include <linux/module.h>
11 #include <linux/of_address.h>
12 #include <linux/platform_device.h>
13 #include <linux/regmap.h>
14 #include <linux/mfd/syscon.h>
16 #define LOGICVC_CTRL_REG 0x40
17 #define LOGICVC_CTRL_GPIO_SHIFT 11
18 #define LOGICVC_CTRL_GPIO_BITS 5
20 #define LOGICVC_POWER_CTRL_REG 0x78
21 #define LOGICVC_POWER_CTRL_GPIO_SHIFT 0
22 #define LOGICVC_POWER_CTRL_GPIO_BITS 4
25 struct gpio_chip chip
;
26 struct regmap
*regmap
;
29 static void logicvc_gpio_offset(struct logicvc_gpio
*logicvc
, unsigned offset
,
30 unsigned int *reg
, unsigned int *bit
)
32 if (offset
>= LOGICVC_CTRL_GPIO_BITS
) {
33 *reg
= LOGICVC_POWER_CTRL_REG
;
35 /* To the (virtual) power ctrl offset. */
36 offset
-= LOGICVC_CTRL_GPIO_BITS
;
37 /* To the actual bit offset in reg. */
38 offset
+= LOGICVC_POWER_CTRL_GPIO_SHIFT
;
40 *reg
= LOGICVC_CTRL_REG
;
42 /* To the actual bit offset in reg. */
43 offset
+= LOGICVC_CTRL_GPIO_SHIFT
;
49 static int logicvc_gpio_get(struct gpio_chip
*chip
, unsigned offset
)
51 struct logicvc_gpio
*logicvc
= gpiochip_get_data(chip
);
52 unsigned int reg
, bit
, value
;
55 logicvc_gpio_offset(logicvc
, offset
, ®
, &bit
);
57 ret
= regmap_read(logicvc
->regmap
, reg
, &value
);
61 return !!(value
& bit
);
64 static void logicvc_gpio_set(struct gpio_chip
*chip
, unsigned offset
, int value
)
66 struct logicvc_gpio
*logicvc
= gpiochip_get_data(chip
);
67 unsigned int reg
, bit
;
69 logicvc_gpio_offset(logicvc
, offset
, ®
, &bit
);
71 regmap_update_bits(logicvc
->regmap
, reg
, bit
, value
? bit
: 0);
74 static int logicvc_gpio_direction_output(struct gpio_chip
*chip
,
75 unsigned offset
, int value
)
77 /* Pins are always configured as output, so just set the value. */
78 logicvc_gpio_set(chip
, offset
, value
);
83 static struct regmap_config logicvc_gpio_regmap_config
= {
87 .name
= "logicvc-gpio",
90 static int logicvc_gpio_probe(struct platform_device
*pdev
)
92 struct device
*dev
= &pdev
->dev
;
93 struct device_node
*of_node
= dev
->of_node
;
94 struct logicvc_gpio
*logicvc
;
97 logicvc
= devm_kzalloc(dev
, sizeof(*logicvc
), GFP_KERNEL
);
101 /* Try to get regmap from parent first. */
102 logicvc
->regmap
= syscon_node_to_regmap(of_node
->parent
);
104 /* Grab our own regmap if that fails. */
105 if (IS_ERR(logicvc
->regmap
)) {
109 ret
= of_address_to_resource(of_node
, 0, &res
);
111 dev_err(dev
, "Failed to get resource from address\n");
115 base
= devm_ioremap_resource(dev
, &res
);
117 return PTR_ERR(base
);
119 logicvc_gpio_regmap_config
.max_register
= resource_size(&res
) -
120 logicvc_gpio_regmap_config
.reg_stride
;
123 devm_regmap_init_mmio(dev
, base
,
124 &logicvc_gpio_regmap_config
);
125 if (IS_ERR(logicvc
->regmap
)) {
126 dev_err(dev
, "Failed to create regmap for I/O\n");
127 return PTR_ERR(logicvc
->regmap
);
131 logicvc
->chip
.parent
= dev
;
132 logicvc
->chip
.owner
= THIS_MODULE
;
133 logicvc
->chip
.label
= dev_name(dev
);
134 logicvc
->chip
.base
= -1;
135 logicvc
->chip
.ngpio
= LOGICVC_CTRL_GPIO_BITS
+
136 LOGICVC_POWER_CTRL_GPIO_BITS
;
137 logicvc
->chip
.get
= logicvc_gpio_get
;
138 logicvc
->chip
.set
= logicvc_gpio_set
;
139 logicvc
->chip
.direction_output
= logicvc_gpio_direction_output
;
141 return devm_gpiochip_add_data(dev
, &logicvc
->chip
, logicvc
);
144 static const struct of_device_id logicivc_gpio_of_table
[] = {
146 .compatible
= "xylon,logicvc-3.02.a-gpio",
151 MODULE_DEVICE_TABLE(of
, logicivc_gpio_of_table
);
153 static struct platform_driver logicvc_gpio_driver
= {
155 .name
= "gpio-logicvc",
156 .of_match_table
= logicivc_gpio_of_table
,
158 .probe
= logicvc_gpio_probe
,
161 module_platform_driver(logicvc_gpio_driver
);
163 MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
164 MODULE_DESCRIPTION("Xylon LogiCVC GPIO driver");
165 MODULE_LICENSE("GPL");