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