]>
Commit | Line | Data |
---|---|---|
b886d83c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
f436bc27 AW |
2 | /* |
3 | * MEN 16Z127 GPIO driver | |
4 | * | |
5 | * Copyright (C) 2016 MEN Mikroelektronik GmbH (www.men.de) | |
f436bc27 AW |
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; | |
f436bc27 | 35 | struct resource *mem; |
f436bc27 AW |
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); | |
86d3f367 | 42 | struct device *dev = gc->parent; |
f436bc27 AW |
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(debounce) - 1; | |
54 | ||
55 | if (rnd && (debounce & BIT(rnd - 1))) | |
7279d991 | 56 | debounce = roundup(debounce, MEN_Z127_DB_MIN_US); |
f436bc27 | 57 | else |
7279d991 | 58 | debounce = rounddown(debounce, MEN_Z127_DB_MIN_US); |
f436bc27 AW |
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 | ||
fd975a7b | 67 | spin_lock(&gc->bgpio_lock); |
f436bc27 AW |
68 | |
69 | db_en = readl(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(db_en, priv->reg_base + MEN_Z127_DBER); | |
80 | writel(db_cnt, priv->reg_base + GPIO_TO_DBCNT_REG(gpio)); | |
81 | ||
fd975a7b | 82 | spin_unlock(&gc->bgpio_lock); |
f436bc27 AW |
83 | |
84 | return 0; | |
85 | } | |
86 | ||
811a1882 LW |
87 | static int men_z127_set_single_ended(struct gpio_chip *gc, |
88 | unsigned offset, | |
2956b5d9 | 89 | enum pin_config_param param) |
f436bc27 AW |
90 | { |
91 | struct men_z127_gpio *priv = gpiochip_get_data(gc); | |
92 | u32 od_en; | |
93 | ||
fd975a7b | 94 | spin_lock(&gc->bgpio_lock); |
f436bc27 AW |
95 | od_en = readl(priv->reg_base + MEN_Z127_ODER); |
96 | ||
2956b5d9 | 97 | if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN) |
811a1882 | 98 | od_en |= BIT(offset); |
f436bc27 | 99 | else |
2956b5d9 | 100 | /* Implicitly PIN_CONFIG_DRIVE_PUSH_PULL */ |
811a1882 | 101 | od_en &= ~BIT(offset); |
f436bc27 AW |
102 | |
103 | writel(od_en, priv->reg_base + MEN_Z127_ODER); | |
fd975a7b | 104 | spin_unlock(&gc->bgpio_lock); |
f436bc27 AW |
105 | |
106 | return 0; | |
107 | } | |
108 | ||
2956b5d9 MW |
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, offset, | |
121 | pinconf_to_config_argument(config)); | |
122 | ||
123 | default: | |
124 | break; | |
125 | } | |
126 | ||
127 | return -ENOTSUPP; | |
128 | } | |
129 | ||
f436bc27 AW |
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, 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(mdev, dev_name(dev)); | |
143 | if (IS_ERR(men_z127_gpio->mem)) { | |
144 | dev_err(dev, "failed to request device memory"); | |
145 | return PTR_ERR(men_z127_gpio->mem); | |
146 | } | |
147 | ||
148 | men_z127_gpio->reg_base = ioremap(men_z127_gpio->mem->start, | |
149 | resource_size(men_z127_gpio->mem)); | |
150 | if (men_z127_gpio->reg_base == NULL) { | |
151 | ret = -ENXIO; | |
152 | goto err_release; | |
153 | } | |
154 | ||
f436bc27 AW |
155 | mcb_set_drvdata(mdev, men_z127_gpio); |
156 | ||
157 | ret = bgpio_init(&men_z127_gpio->gc, &mdev->dev, 4, | |
158 | men_z127_gpio->reg_base + MEN_Z127_PSR, | |
159 | men_z127_gpio->reg_base + MEN_Z127_CTRL, | |
160 | NULL, | |
161 | men_z127_gpio->reg_base + MEN_Z127_GPIODR, | |
162 | NULL, 0); | |
163 | if (ret) | |
164 | goto err_unmap; | |
165 | ||
2956b5d9 | 166 | men_z127_gpio->gc.set_config = men_z127_set_config; |
f436bc27 AW |
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(men_z127_gpio->reg_base); | |
180 | err_release: | |
181 | mcb_release_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(mdev); | |
188 | ||
189 | gpiochip_remove(&men_z127_gpio->gc); | |
190 | iounmap(men_z127_gpio->reg_base); | |
191 | mcb_release_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", | |
f436bc27 AW |
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"); |