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_device.h>
12 #include <linux/of_address.h>
13 #include <linux/platform_device.h>
14 #include <linux/regmap.h>
15 #include <linux/mfd/syscon.h>
17 #define LOGICVC_CTRL_REG 0x40
18 #define LOGICVC_CTRL_GPIO_SHIFT 11
19 #define LOGICVC_CTRL_GPIO_BITS 5
21 #define LOGICVC_POWER_CTRL_REG 0x78
22 #define LOGICVC_POWER_CTRL_GPIO_SHIFT 0
23 #define LOGICVC_POWER_CTRL_GPIO_BITS 4
26 struct gpio_chip chip
;
27 struct regmap
*regmap
;
30 static void logicvc_gpio_offset(struct logicvc_gpio
*logicvc
, unsigned offset
,
31 unsigned int *reg
, unsigned int *bit
)
33 if (offset
>= LOGICVC_CTRL_GPIO_BITS
) {
34 *reg
= LOGICVC_POWER_CTRL_REG
;
36 /* To the (virtual) power ctrl offset. */
37 offset
-= LOGICVC_CTRL_GPIO_BITS
;
38 /* To the actual bit offset in reg. */
39 offset
+= LOGICVC_POWER_CTRL_GPIO_SHIFT
;
41 *reg
= LOGICVC_CTRL_REG
;
43 /* To the actual bit offset in reg. */
44 offset
+= LOGICVC_CTRL_GPIO_SHIFT
;
50 static int logicvc_gpio_get(struct gpio_chip
*chip
, unsigned offset
)
52 struct logicvc_gpio
*logicvc
= gpiochip_get_data(chip
);
53 unsigned int reg
, bit
, value
;
56 logicvc_gpio_offset(logicvc
, offset
, ®
, &bit
);
58 ret
= regmap_read(logicvc
->regmap
, reg
, &value
);
62 return !!(value
& bit
);
65 static void logicvc_gpio_set(struct gpio_chip
*chip
, unsigned offset
, int value
)
67 struct logicvc_gpio
*logicvc
= gpiochip_get_data(chip
);
68 unsigned int reg
, bit
;
70 logicvc_gpio_offset(logicvc
, offset
, ®
, &bit
);
72 regmap_update_bits(logicvc
->regmap
, reg
, bit
, value
? bit
: 0);
75 static int logicvc_gpio_direction_output(struct gpio_chip
*chip
,
76 unsigned offset
, int value
)
78 /* Pins are always configured as output, so just set the value. */
79 logicvc_gpio_set(chip
, offset
, value
);
84 static struct regmap_config logicvc_gpio_regmap_config
= {
88 .name
= "logicvc-gpio",
91 static int logicvc_gpio_probe(struct platform_device
*pdev
)
93 struct device
*dev
= &pdev
->dev
;
94 struct device_node
*of_node
= dev
->of_node
;
95 struct logicvc_gpio
*logicvc
;
98 logicvc
= devm_kzalloc(dev
, sizeof(*logicvc
), GFP_KERNEL
);
102 /* Try to get regmap from parent first. */
103 logicvc
->regmap
= syscon_node_to_regmap(of_node
->parent
);
105 /* Grab our own regmap if that fails. */
106 if (IS_ERR(logicvc
->regmap
)) {
110 ret
= of_address_to_resource(of_node
, 0, &res
);
112 dev_err(dev
, "Failed to get resource from address\n");
116 base
= devm_ioremap_resource(dev
, &res
);
118 dev_err(dev
, "Failed to map I/O base\n");
119 return PTR_ERR(base
);
122 logicvc_gpio_regmap_config
.max_register
= resource_size(&res
) -
123 logicvc_gpio_regmap_config
.reg_stride
;
126 devm_regmap_init_mmio(dev
, base
,
127 &logicvc_gpio_regmap_config
);
128 if (IS_ERR(logicvc
->regmap
)) {
129 dev_err(dev
, "Failed to create regmap for I/O\n");
130 return PTR_ERR(logicvc
->regmap
);
134 logicvc
->chip
.parent
= dev
;
135 logicvc
->chip
.owner
= THIS_MODULE
;
136 logicvc
->chip
.label
= dev_name(dev
);
137 logicvc
->chip
.base
= -1;
138 logicvc
->chip
.ngpio
= LOGICVC_CTRL_GPIO_BITS
+
139 LOGICVC_POWER_CTRL_GPIO_BITS
;
140 logicvc
->chip
.get
= logicvc_gpio_get
;
141 logicvc
->chip
.set
= logicvc_gpio_set
;
142 logicvc
->chip
.direction_output
= logicvc_gpio_direction_output
;
144 platform_set_drvdata(pdev
, logicvc
);
146 return devm_gpiochip_add_data(dev
, &logicvc
->chip
, logicvc
);
149 static const struct of_device_id logicivc_gpio_of_table
[] = {
151 .compatible
= "xylon,logicvc-3.02.a-gpio",
156 MODULE_DEVICE_TABLE(of
, logicivc_gpio_of_table
);
158 static struct platform_driver logicvc_gpio_driver
= {
160 .name
= "gpio-logicvc",
161 .of_match_table
= logicivc_gpio_of_table
,
163 .probe
= logicvc_gpio_probe
,
166 module_platform_driver(logicvc_gpio_driver
);
168 MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
169 MODULE_DESCRIPTION("Xylon LogiCVC GPIO driver");
170 MODULE_LICENSE("GPL");