Blame view
drivers/gpio/gpio-syscon.c
6.67 KB
6a8a0c1d8 gpio: Driver for ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
/* * SYSCON GPIO driver * * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include <linux/err.h> #include <linux/gpio.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/mfd/syscon.h> #define GPIO_SYSCON_FEAT_IN BIT(0) #define GPIO_SYSCON_FEAT_OUT BIT(1) #define GPIO_SYSCON_FEAT_DIR BIT(2) /* SYSCON driver is designed to use 32-bit wide registers */ #define SYSCON_REG_SIZE (4) #define SYSCON_REG_BITS (SYSCON_REG_SIZE * 8) /** * struct syscon_gpio_data - Configuration for the device. * compatible: SYSCON driver compatible string. * flags: Set of GPIO_SYSCON_FEAT_ flags: * GPIO_SYSCON_FEAT_IN: GPIOs supports input, * GPIO_SYSCON_FEAT_OUT: GPIOs supports output, * GPIO_SYSCON_FEAT_DIR: GPIOs supports switch direction. * bit_count: Number of bits used as GPIOs. * dat_bit_offset: Offset (in bits) to the first GPIO bit. * dir_bit_offset: Optional offset (in bits) to the first bit to switch * GPIO direction (Used with GPIO_SYSCON_FEAT_DIR flag). |
2c341d62e gpio: syscon: add... |
40 41 |
* set: HW specific callback to assigns output value * for signal "offset" |
6a8a0c1d8 gpio: Driver for ... |
42 43 44 45 46 47 48 49 |
*/ struct syscon_gpio_data { const char *compatible; unsigned int flags; unsigned int bit_count; unsigned int dat_bit_offset; unsigned int dir_bit_offset; |
2c341d62e gpio: syscon: add... |
50 51 |
void (*set)(struct gpio_chip *chip, unsigned offset, int value); |
6a8a0c1d8 gpio: Driver for ... |
52 53 54 55 56 57 |
}; struct syscon_gpio_priv { struct gpio_chip chip; struct regmap *syscon; const struct syscon_gpio_data *data; |
5a3e3f88b gpio: syscon: ret... |
58 59 |
u32 dreg_offset; u32 dir_reg_offset; |
6a8a0c1d8 gpio: Driver for ... |
60 |
}; |
6a8a0c1d8 gpio: Driver for ... |
61 62 |
static int syscon_gpio_get(struct gpio_chip *chip, unsigned offset) { |
d27ad7a83 gpio: syscon: use... |
63 |
struct syscon_gpio_priv *priv = gpiochip_get_data(chip); |
5a3e3f88b gpio: syscon: ret... |
64 |
unsigned int val, offs; |
6a8a0c1d8 gpio: Driver for ... |
65 |
int ret; |
5a3e3f88b gpio: syscon: ret... |
66 |
offs = priv->dreg_offset + priv->data->dat_bit_offset + offset; |
6a8a0c1d8 gpio: Driver for ... |
67 68 69 70 71 72 73 74 75 76 |
ret = regmap_read(priv->syscon, (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, &val); if (ret) return ret; return !!(val & BIT(offs % SYSCON_REG_BITS)); } static void syscon_gpio_set(struct gpio_chip *chip, unsigned offset, int val) { |
d27ad7a83 gpio: syscon: use... |
77 |
struct syscon_gpio_priv *priv = gpiochip_get_data(chip); |
5a3e3f88b gpio: syscon: ret... |
78 79 80 |
unsigned int offs; offs = priv->dreg_offset + priv->data->dat_bit_offset + offset; |
6a8a0c1d8 gpio: Driver for ... |
81 82 83 84 85 86 87 88 89 |
regmap_update_bits(priv->syscon, (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, BIT(offs % SYSCON_REG_BITS), val ? BIT(offs % SYSCON_REG_BITS) : 0); } static int syscon_gpio_dir_in(struct gpio_chip *chip, unsigned offset) { |
d27ad7a83 gpio: syscon: use... |
90 |
struct syscon_gpio_priv *priv = gpiochip_get_data(chip); |
6a8a0c1d8 gpio: Driver for ... |
91 92 |
if (priv->data->flags & GPIO_SYSCON_FEAT_DIR) { |
5a3e3f88b gpio: syscon: ret... |
93 94 95 96 |
unsigned int offs; offs = priv->dir_reg_offset + priv->data->dir_bit_offset + offset; |
6a8a0c1d8 gpio: Driver for ... |
97 98 99 100 101 102 103 104 105 106 107 |
regmap_update_bits(priv->syscon, (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, BIT(offs % SYSCON_REG_BITS), 0); } return 0; } static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val) { |
d27ad7a83 gpio: syscon: use... |
108 |
struct syscon_gpio_priv *priv = gpiochip_get_data(chip); |
6a8a0c1d8 gpio: Driver for ... |
109 110 |
if (priv->data->flags & GPIO_SYSCON_FEAT_DIR) { |
5a3e3f88b gpio: syscon: ret... |
111 112 113 114 |
unsigned int offs; offs = priv->dir_reg_offset + priv->data->dir_bit_offset + offset; |
6a8a0c1d8 gpio: Driver for ... |
115 116 117 118 119 120 |
regmap_update_bits(priv->syscon, (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, BIT(offs % SYSCON_REG_BITS), BIT(offs % SYSCON_REG_BITS)); } |
2c341d62e gpio: syscon: add... |
121 |
priv->data->set(chip, offset, val); |
6a8a0c1d8 gpio: Driver for ... |
122 123 124 125 126 127 |
return 0; } static const struct syscon_gpio_data clps711x_mctrl_gpio = { /* ARM CLPS711X SYSFLG1 Bits 8-10 */ |
2e607fca7 gpio: syscon: Cha... |
128 |
.compatible = "cirrus,ep7209-syscon1", |
6a8a0c1d8 gpio: Driver for ... |
129 130 131 132 |
.flags = GPIO_SYSCON_FEAT_IN, .bit_count = 3, .dat_bit_offset = 0x40 * 8 + 8, }; |
2134cb997 gpio: syscon: reu... |
133 134 135 136 |
#define KEYSTONE_LOCK_BIT BIT(0) static void keystone_gpio_set(struct gpio_chip *chip, unsigned offset, int val) { |
d27ad7a83 gpio: syscon: use... |
137 |
struct syscon_gpio_priv *priv = gpiochip_get_data(chip); |
2134cb997 gpio: syscon: reu... |
138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
unsigned int offs; int ret; offs = priv->dreg_offset + priv->data->dat_bit_offset + offset; if (!val) return; ret = regmap_update_bits( priv->syscon, (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, BIT(offs % SYSCON_REG_BITS) | KEYSTONE_LOCK_BIT, BIT(offs % SYSCON_REG_BITS) | KEYSTONE_LOCK_BIT); if (ret < 0) |
58383c784 gpio: change memb... |
152 153 |
dev_err(chip->parent, "gpio write failed ret(%d) ", ret); |
2134cb997 gpio: syscon: reu... |
154 155 156 157 158 159 160 161 162 163 |
} static const struct syscon_gpio_data keystone_dsp_gpio = { /* ARM Keystone 2 */ .compatible = NULL, .flags = GPIO_SYSCON_FEAT_OUT, .bit_count = 28, .dat_bit_offset = 4, .set = keystone_gpio_set, }; |
6a8a0c1d8 gpio: Driver for ... |
164 165 |
static const struct of_device_id syscon_gpio_ids[] = { { |
2e607fca7 gpio: syscon: Cha... |
166 |
.compatible = "cirrus,ep7209-mctrl-gpio", |
6a8a0c1d8 gpio: Driver for ... |
167 168 |
.data = &clps711x_mctrl_gpio, }, |
2134cb997 gpio: syscon: reu... |
169 170 171 172 |
{ .compatible = "ti,keystone-dsp-gpio", .data = &keystone_dsp_gpio, }, |
6a8a0c1d8 gpio: Driver for ... |
173 174 175 176 177 178 179 |
{ } }; MODULE_DEVICE_TABLE(of, syscon_gpio_ids); static int syscon_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; |
853f0cb86 gpio: syscon: fix... |
180 |
const struct of_device_id *of_id; |
6a8a0c1d8 gpio: Driver for ... |
181 |
struct syscon_gpio_priv *priv; |
5a3e3f88b gpio: syscon: ret... |
182 183 |
struct device_node *np = dev->of_node; int ret; |
6a8a0c1d8 gpio: Driver for ... |
184 |
|
853f0cb86 gpio: syscon: fix... |
185 186 187 |
of_id = of_match_device(syscon_gpio_ids, dev); if (!of_id) return -ENODEV; |
6a8a0c1d8 gpio: Driver for ... |
188 189 190 191 192 |
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->data = of_id->data; |
5a3e3f88b gpio: syscon: ret... |
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
if (priv->data->compatible) { priv->syscon = syscon_regmap_lookup_by_compatible( priv->data->compatible); if (IS_ERR(priv->syscon)) return PTR_ERR(priv->syscon); } else { priv->syscon = syscon_regmap_lookup_by_phandle(np, "gpio,syscon-dev"); if (IS_ERR(priv->syscon)) return PTR_ERR(priv->syscon); ret = of_property_read_u32_index(np, "gpio,syscon-dev", 1, &priv->dreg_offset); if (ret) dev_err(dev, "can't read the data register offset! "); priv->dreg_offset <<= 3; ret = of_property_read_u32_index(np, "gpio,syscon-dev", 2, &priv->dir_reg_offset); if (ret) |
c6ac19dac gpio: syscon: red... |
215 216 |
dev_dbg(dev, "can't read the dir register offset! "); |
5a3e3f88b gpio: syscon: ret... |
217 218 219 |
priv->dir_reg_offset <<= 3; } |
6a8a0c1d8 gpio: Driver for ... |
220 |
|
58383c784 gpio: change memb... |
221 |
priv->chip.parent = dev; |
6a8a0c1d8 gpio: Driver for ... |
222 223 224 225 226 227 228 229 |
priv->chip.owner = THIS_MODULE; priv->chip.label = dev_name(dev); priv->chip.base = -1; priv->chip.ngpio = priv->data->bit_count; priv->chip.get = syscon_gpio_get; if (priv->data->flags & GPIO_SYSCON_FEAT_IN) priv->chip.direction_input = syscon_gpio_dir_in; if (priv->data->flags & GPIO_SYSCON_FEAT_OUT) { |
2c341d62e gpio: syscon: add... |
230 |
priv->chip.set = priv->data->set ? : syscon_gpio_set; |
6a8a0c1d8 gpio: Driver for ... |
231 232 233 234 |
priv->chip.direction_output = syscon_gpio_dir_out; } platform_set_drvdata(pdev, priv); |
94c683ab8 gpio: syscon: Use... |
235 |
return devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv); |
6a8a0c1d8 gpio: Driver for ... |
236 237 238 239 240 |
} static struct platform_driver syscon_gpio_driver = { .driver = { .name = "gpio-syscon", |
6a8a0c1d8 gpio: Driver for ... |
241 242 243 |
.of_match_table = syscon_gpio_ids, }, .probe = syscon_gpio_probe, |
6a8a0c1d8 gpio: Driver for ... |
244 245 246 247 248 249 |
}; module_platform_driver(syscon_gpio_driver); MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); MODULE_DESCRIPTION("SYSCON GPIO driver"); MODULE_LICENSE("GPL"); |