1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright Intel Corporation (C) 2014-2016. All Rights Reserved |
4 | * |
5 | * GPIO driver for Altera Arria10 MAX5 System Resource Chip |
6 | * |
7 | * Adapted from gpio-tps65910.c |
8 | */ |
9 | |
10 | #include <linux/gpio/driver.h> |
11 | #include <linux/mfd/altera-a10sr.h> |
12 | #include <linux/mod_devicetable.h> |
13 | #include <linux/module.h> |
14 | #include <linux/property.h> |
15 | |
16 | /** |
17 | * struct altr_a10sr_gpio - Altera Max5 GPIO device private data structure |
18 | * @gp: : instance of the gpio_chip |
19 | * @regmap: the regmap from the parent device. |
20 | */ |
21 | struct altr_a10sr_gpio { |
22 | struct gpio_chip gp; |
23 | struct regmap *regmap; |
24 | }; |
25 | |
26 | static int altr_a10sr_gpio_get(struct gpio_chip *chip, unsigned int offset) |
27 | { |
28 | struct altr_a10sr_gpio *gpio = gpiochip_get_data(gc: chip); |
29 | int ret, val; |
30 | |
31 | ret = regmap_read(map: gpio->regmap, ALTR_A10SR_PBDSW_REG, val: &val); |
32 | if (ret < 0) |
33 | return ret; |
34 | |
35 | return !!(val & BIT(offset - ALTR_A10SR_LED_VALID_SHIFT)); |
36 | } |
37 | |
38 | static void altr_a10sr_gpio_set(struct gpio_chip *chip, unsigned int offset, |
39 | int value) |
40 | { |
41 | struct altr_a10sr_gpio *gpio = gpiochip_get_data(gc: chip); |
42 | |
43 | regmap_update_bits(map: gpio->regmap, ALTR_A10SR_LED_REG, |
44 | BIT(ALTR_A10SR_LED_VALID_SHIFT + offset), |
45 | val: value ? BIT(ALTR_A10SR_LED_VALID_SHIFT + offset) |
46 | : 0); |
47 | } |
48 | |
49 | static int altr_a10sr_gpio_direction_input(struct gpio_chip *gc, |
50 | unsigned int nr) |
51 | { |
52 | if (nr < (ALTR_A10SR_IN_VALID_RANGE_LO - ALTR_A10SR_LED_VALID_SHIFT)) |
53 | return -EINVAL; |
54 | |
55 | return 0; |
56 | } |
57 | |
58 | static int altr_a10sr_gpio_direction_output(struct gpio_chip *gc, |
59 | unsigned int nr, int value) |
60 | { |
61 | if (nr > (ALTR_A10SR_OUT_VALID_RANGE_HI - ALTR_A10SR_LED_VALID_SHIFT)) |
62 | return -EINVAL; |
63 | |
64 | altr_a10sr_gpio_set(chip: gc, offset: nr, value); |
65 | return 0; |
66 | } |
67 | |
68 | static const struct gpio_chip altr_a10sr_gc = { |
69 | .label = "altr_a10sr_gpio" , |
70 | .owner = THIS_MODULE, |
71 | .get = altr_a10sr_gpio_get, |
72 | .set = altr_a10sr_gpio_set, |
73 | .direction_input = altr_a10sr_gpio_direction_input, |
74 | .direction_output = altr_a10sr_gpio_direction_output, |
75 | .can_sleep = true, |
76 | .ngpio = 12, |
77 | .base = -1, |
78 | }; |
79 | |
80 | static int altr_a10sr_gpio_probe(struct platform_device *pdev) |
81 | { |
82 | struct altr_a10sr_gpio *gpio; |
83 | struct altr_a10sr *a10sr = dev_get_drvdata(dev: pdev->dev.parent); |
84 | |
85 | gpio = devm_kzalloc(dev: &pdev->dev, size: sizeof(*gpio), GFP_KERNEL); |
86 | if (!gpio) |
87 | return -ENOMEM; |
88 | |
89 | gpio->regmap = a10sr->regmap; |
90 | |
91 | gpio->gp = altr_a10sr_gc; |
92 | gpio->gp.parent = pdev->dev.parent; |
93 | gpio->gp.fwnode = dev_fwnode(&pdev->dev); |
94 | |
95 | return devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio); |
96 | } |
97 | |
98 | static const struct of_device_id altr_a10sr_gpio_of_match[] = { |
99 | { .compatible = "altr,a10sr-gpio" }, |
100 | { }, |
101 | }; |
102 | MODULE_DEVICE_TABLE(of, altr_a10sr_gpio_of_match); |
103 | |
104 | static struct platform_driver altr_a10sr_gpio_driver = { |
105 | .probe = altr_a10sr_gpio_probe, |
106 | .driver = { |
107 | .name = "altr_a10sr_gpio" , |
108 | .of_match_table = altr_a10sr_gpio_of_match, |
109 | }, |
110 | }; |
111 | module_platform_driver(altr_a10sr_gpio_driver); |
112 | |
113 | MODULE_LICENSE("GPL v2" ); |
114 | MODULE_AUTHOR("Thor Thayer <tthayer@opensource.altera.com>" ); |
115 | MODULE_DESCRIPTION("Altera Arria10 System Resource Chip GPIO" ); |
116 | |