1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * MEN 16Z127 GPIO driver |
4 | * |
5 | * Copyright (C) 2016 MEN Mikroelektronik GmbH (www.men.de) |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/module.h> |
10 | #include <linux/io.h> |
11 | #include <linux/err.h> |
12 | #include <linux/mcb.h> |
13 | #include <linux/bitops.h> |
14 | #include <linux/gpio/driver.h> |
15 | |
16 | #define MEN_Z127_CTRL 0x00 |
17 | #define MEN_Z127_PSR 0x04 |
18 | #define MEN_Z127_IRQR 0x08 |
19 | #define MEN_Z127_GPIODR 0x0c |
20 | #define MEN_Z127_IER1 0x10 |
21 | #define MEN_Z127_IER2 0x14 |
22 | #define MEN_Z127_DBER 0x18 |
23 | #define MEN_Z127_ODER 0x1C |
24 | #define GPIO_TO_DBCNT_REG(gpio) ((gpio * 4) + 0x80) |
25 | |
26 | #define MEN_Z127_DB_MIN_US 50 |
27 | /* 16 bit compare register. Each bit represents 50us */ |
28 | #define MEN_Z127_DB_MAX_US (0xffff * MEN_Z127_DB_MIN_US) |
29 | #define MEN_Z127_DB_IN_RANGE(db) ((db >= MEN_Z127_DB_MIN_US) && \ |
30 | (db <= MEN_Z127_DB_MAX_US)) |
31 | |
32 | struct men_z127_gpio { |
33 | struct gpio_chip gc; |
34 | void __iomem *reg_base; |
35 | struct resource *mem; |
36 | }; |
37 | |
38 | static int men_z127_debounce(struct gpio_chip *gc, unsigned gpio, |
39 | unsigned debounce) |
40 | { |
41 | struct men_z127_gpio *priv = gpiochip_get_data(gc); |
42 | struct device *dev = gc->parent; |
43 | unsigned int rnd; |
44 | u32 db_en, db_cnt; |
45 | |
46 | if (!MEN_Z127_DB_IN_RANGE(debounce)) { |
47 | dev_err(dev, "debounce value %u out of range" , debounce); |
48 | return -EINVAL; |
49 | } |
50 | |
51 | if (debounce > 0) { |
52 | /* round up or down depending on MSB-1 */ |
53 | rnd = fls(x: debounce) - 1; |
54 | |
55 | if (rnd && (debounce & BIT(rnd - 1))) |
56 | debounce = roundup(debounce, MEN_Z127_DB_MIN_US); |
57 | else |
58 | debounce = rounddown(debounce, MEN_Z127_DB_MIN_US); |
59 | |
60 | if (debounce > MEN_Z127_DB_MAX_US) |
61 | debounce = MEN_Z127_DB_MAX_US; |
62 | |
63 | /* 50us per register unit */ |
64 | debounce /= 50; |
65 | } |
66 | |
67 | raw_spin_lock(&gc->bgpio_lock); |
68 | |
69 | db_en = readl(addr: priv->reg_base + MEN_Z127_DBER); |
70 | |
71 | if (debounce == 0) { |
72 | db_en &= ~BIT(gpio); |
73 | db_cnt = 0; |
74 | } else { |
75 | db_en |= BIT(gpio); |
76 | db_cnt = debounce; |
77 | } |
78 | |
79 | writel(val: db_en, addr: priv->reg_base + MEN_Z127_DBER); |
80 | writel(val: db_cnt, addr: priv->reg_base + GPIO_TO_DBCNT_REG(gpio)); |
81 | |
82 | raw_spin_unlock(&gc->bgpio_lock); |
83 | |
84 | return 0; |
85 | } |
86 | |
87 | static int men_z127_set_single_ended(struct gpio_chip *gc, |
88 | unsigned offset, |
89 | enum pin_config_param param) |
90 | { |
91 | struct men_z127_gpio *priv = gpiochip_get_data(gc); |
92 | u32 od_en; |
93 | |
94 | raw_spin_lock(&gc->bgpio_lock); |
95 | od_en = readl(addr: priv->reg_base + MEN_Z127_ODER); |
96 | |
97 | if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN) |
98 | od_en |= BIT(offset); |
99 | else |
100 | /* Implicitly PIN_CONFIG_DRIVE_PUSH_PULL */ |
101 | od_en &= ~BIT(offset); |
102 | |
103 | writel(val: od_en, addr: priv->reg_base + MEN_Z127_ODER); |
104 | raw_spin_unlock(&gc->bgpio_lock); |
105 | |
106 | return 0; |
107 | } |
108 | |
109 | static int men_z127_set_config(struct gpio_chip *gc, unsigned offset, |
110 | unsigned long config) |
111 | { |
112 | enum pin_config_param param = pinconf_to_config_param(config); |
113 | |
114 | switch (param) { |
115 | case PIN_CONFIG_DRIVE_OPEN_DRAIN: |
116 | case PIN_CONFIG_DRIVE_PUSH_PULL: |
117 | return men_z127_set_single_ended(gc, offset, param); |
118 | |
119 | case PIN_CONFIG_INPUT_DEBOUNCE: |
120 | return men_z127_debounce(gc, gpio: offset, |
121 | debounce: pinconf_to_config_argument(config)); |
122 | |
123 | default: |
124 | break; |
125 | } |
126 | |
127 | return -ENOTSUPP; |
128 | } |
129 | |
130 | static int men_z127_probe(struct mcb_device *mdev, |
131 | const struct mcb_device_id *id) |
132 | { |
133 | struct men_z127_gpio *men_z127_gpio; |
134 | struct device *dev = &mdev->dev; |
135 | int ret; |
136 | |
137 | men_z127_gpio = devm_kzalloc(dev, size: sizeof(struct men_z127_gpio), |
138 | GFP_KERNEL); |
139 | if (!men_z127_gpio) |
140 | return -ENOMEM; |
141 | |
142 | men_z127_gpio->mem = mcb_request_mem(dev: mdev, name: dev_name(dev)); |
143 | if (IS_ERR(ptr: men_z127_gpio->mem)) { |
144 | dev_err(dev, "failed to request device memory" ); |
145 | return PTR_ERR(ptr: men_z127_gpio->mem); |
146 | } |
147 | |
148 | men_z127_gpio->reg_base = ioremap(offset: men_z127_gpio->mem->start, |
149 | size: resource_size(res: men_z127_gpio->mem)); |
150 | if (men_z127_gpio->reg_base == NULL) { |
151 | ret = -ENXIO; |
152 | goto err_release; |
153 | } |
154 | |
155 | mcb_set_drvdata(dev: mdev, data: men_z127_gpio); |
156 | |
157 | ret = bgpio_init(gc: &men_z127_gpio->gc, dev: &mdev->dev, sz: 4, |
158 | dat: men_z127_gpio->reg_base + MEN_Z127_PSR, |
159 | set: men_z127_gpio->reg_base + MEN_Z127_CTRL, |
160 | NULL, |
161 | dirout: men_z127_gpio->reg_base + MEN_Z127_GPIODR, |
162 | NULL, flags: 0); |
163 | if (ret) |
164 | goto err_unmap; |
165 | |
166 | men_z127_gpio->gc.set_config = men_z127_set_config; |
167 | |
168 | ret = gpiochip_add_data(&men_z127_gpio->gc, men_z127_gpio); |
169 | if (ret) { |
170 | dev_err(dev, "failed to register MEN 16Z127 GPIO controller" ); |
171 | goto err_unmap; |
172 | } |
173 | |
174 | dev_info(dev, "MEN 16Z127 GPIO driver registered" ); |
175 | |
176 | return 0; |
177 | |
178 | err_unmap: |
179 | iounmap(addr: men_z127_gpio->reg_base); |
180 | err_release: |
181 | mcb_release_mem(mem: men_z127_gpio->mem); |
182 | return ret; |
183 | } |
184 | |
185 | static void men_z127_remove(struct mcb_device *mdev) |
186 | { |
187 | struct men_z127_gpio *men_z127_gpio = mcb_get_drvdata(dev: mdev); |
188 | |
189 | gpiochip_remove(gc: &men_z127_gpio->gc); |
190 | iounmap(addr: men_z127_gpio->reg_base); |
191 | mcb_release_mem(mem: men_z127_gpio->mem); |
192 | } |
193 | |
194 | static const struct mcb_device_id men_z127_ids[] = { |
195 | { .device = 0x7f }, |
196 | { } |
197 | }; |
198 | MODULE_DEVICE_TABLE(mcb, men_z127_ids); |
199 | |
200 | static struct mcb_driver men_z127_driver = { |
201 | .driver = { |
202 | .name = "z127-gpio" , |
203 | }, |
204 | .probe = men_z127_probe, |
205 | .remove = men_z127_remove, |
206 | .id_table = men_z127_ids, |
207 | }; |
208 | module_mcb_driver(men_z127_driver); |
209 | |
210 | MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>" ); |
211 | MODULE_DESCRIPTION("MEN 16z127 GPIO Controller" ); |
212 | MODULE_LICENSE("GPL v2" ); |
213 | MODULE_ALIAS("mcb:16z127" ); |
214 | MODULE_IMPORT_NS(MCB); |
215 | |