xref: /openbmc/linux/drivers/gpio/gpio-madera.c (revision f220d3eb)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * GPIO support for Cirrus Logic Madera codecs
4  *
5  * Copyright (C) 2015-2018 Cirrus Logic
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by the
9  * Free Software Foundation; version 2.
10  */
11 
12 #include <linux/gpio/driver.h>
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 
17 #include <linux/mfd/madera/core.h>
18 #include <linux/mfd/madera/pdata.h>
19 #include <linux/mfd/madera/registers.h>
20 
21 struct madera_gpio {
22 	struct madera *madera;
23 	/* storage space for the gpio_chip we're using */
24 	struct gpio_chip gpio_chip;
25 };
26 
27 static int madera_gpio_get_direction(struct gpio_chip *chip,
28 				     unsigned int offset)
29 {
30 	struct madera_gpio *madera_gpio = gpiochip_get_data(chip);
31 	struct madera *madera = madera_gpio->madera;
32 	unsigned int reg_offset = 2 * offset;
33 	unsigned int val;
34 	int ret;
35 
36 	ret = regmap_read(madera->regmap, MADERA_GPIO1_CTRL_2 + reg_offset,
37 			  &val);
38 	if (ret < 0)
39 		return ret;
40 
41 	return !!(val & MADERA_GP1_DIR_MASK);
42 }
43 
44 static int madera_gpio_direction_in(struct gpio_chip *chip, unsigned int offset)
45 {
46 	struct madera_gpio *madera_gpio = gpiochip_get_data(chip);
47 	struct madera *madera = madera_gpio->madera;
48 	unsigned int reg_offset = 2 * offset;
49 
50 	return regmap_update_bits(madera->regmap,
51 				  MADERA_GPIO1_CTRL_2 + reg_offset,
52 				  MADERA_GP1_DIR_MASK, MADERA_GP1_DIR);
53 }
54 
55 static int madera_gpio_get(struct gpio_chip *chip, unsigned int offset)
56 {
57 	struct madera_gpio *madera_gpio = gpiochip_get_data(chip);
58 	struct madera *madera = madera_gpio->madera;
59 	unsigned int reg_offset = 2 * offset;
60 	unsigned int val;
61 	int ret;
62 
63 	ret = regmap_read(madera->regmap, MADERA_GPIO1_CTRL_1 + reg_offset,
64 			  &val);
65 	if (ret < 0)
66 		return ret;
67 
68 	return !!(val & MADERA_GP1_LVL_MASK);
69 }
70 
71 static int madera_gpio_direction_out(struct gpio_chip *chip,
72 				     unsigned int offset, int value)
73 {
74 	struct madera_gpio *madera_gpio = gpiochip_get_data(chip);
75 	struct madera *madera = madera_gpio->madera;
76 	unsigned int reg_offset = 2 * offset;
77 	unsigned int reg_val = value ? MADERA_GP1_LVL : 0;
78 	int ret;
79 
80 	ret = regmap_update_bits(madera->regmap,
81 				 MADERA_GPIO1_CTRL_2 + reg_offset,
82 				 MADERA_GP1_DIR_MASK, 0);
83 	if (ret < 0)
84 		return ret;
85 
86 	return regmap_update_bits(madera->regmap,
87 				  MADERA_GPIO1_CTRL_1 + reg_offset,
88 				  MADERA_GP1_LVL_MASK, reg_val);
89 }
90 
91 static void madera_gpio_set(struct gpio_chip *chip, unsigned int offset,
92 			    int value)
93 {
94 	struct madera_gpio *madera_gpio = gpiochip_get_data(chip);
95 	struct madera *madera = madera_gpio->madera;
96 	unsigned int reg_offset = 2 * offset;
97 	unsigned int reg_val = value ? MADERA_GP1_LVL : 0;
98 	int ret;
99 
100 	ret = regmap_update_bits(madera->regmap,
101 				 MADERA_GPIO1_CTRL_1 + reg_offset,
102 				 MADERA_GP1_LVL_MASK, reg_val);
103 
104 	/* set() doesn't return an error so log a warning */
105 	if (ret)
106 		dev_warn(madera->dev, "Failed to write to 0x%x (%d)\n",
107 			 MADERA_GPIO1_CTRL_1 + reg_offset, ret);
108 }
109 
110 static struct gpio_chip madera_gpio_chip = {
111 	.label			= "madera",
112 	.owner			= THIS_MODULE,
113 	.request		= gpiochip_generic_request,
114 	.free			= gpiochip_generic_free,
115 	.get_direction		= madera_gpio_get_direction,
116 	.direction_input	= madera_gpio_direction_in,
117 	.get			= madera_gpio_get,
118 	.direction_output	= madera_gpio_direction_out,
119 	.set			= madera_gpio_set,
120 	.set_config		= gpiochip_generic_config,
121 	.can_sleep		= true,
122 };
123 
124 static int madera_gpio_probe(struct platform_device *pdev)
125 {
126 	struct madera *madera = dev_get_drvdata(pdev->dev.parent);
127 	struct madera_pdata *pdata = dev_get_platdata(madera->dev);
128 	struct madera_gpio *madera_gpio;
129 	int ret;
130 
131 	madera_gpio = devm_kzalloc(&pdev->dev, sizeof(*madera_gpio),
132 				   GFP_KERNEL);
133 	if (!madera_gpio)
134 		return -ENOMEM;
135 
136 	madera_gpio->madera = madera;
137 
138 	/* Construct suitable gpio_chip from the template in madera_gpio_chip */
139 	madera_gpio->gpio_chip = madera_gpio_chip;
140 	madera_gpio->gpio_chip.parent = pdev->dev.parent;
141 
142 	switch (madera->type) {
143 	case CS47L35:
144 		madera_gpio->gpio_chip.ngpio = CS47L35_NUM_GPIOS;
145 		break;
146 	case CS47L85:
147 	case WM1840:
148 		madera_gpio->gpio_chip.ngpio = CS47L85_NUM_GPIOS;
149 		break;
150 	case CS47L90:
151 	case CS47L91:
152 		madera_gpio->gpio_chip.ngpio = CS47L90_NUM_GPIOS;
153 		break;
154 	default:
155 		dev_err(&pdev->dev, "Unknown chip variant %d\n", madera->type);
156 		return -EINVAL;
157 	}
158 
159 	/* We want to be usable on systems that don't use devicetree or acpi */
160 	if (pdata && pdata->gpio_base)
161 		madera_gpio->gpio_chip.base = pdata->gpio_base;
162 	else
163 		madera_gpio->gpio_chip.base = -1;
164 
165 	ret = devm_gpiochip_add_data(&pdev->dev,
166 				     &madera_gpio->gpio_chip,
167 				     madera_gpio);
168 	if (ret < 0) {
169 		dev_dbg(&pdev->dev, "Could not register gpiochip, %d\n", ret);
170 		return ret;
171 	}
172 
173 	/*
174 	 * This is part of a composite MFD device which can only be used with
175 	 * the corresponding pinctrl driver. On all supported silicon the GPIO
176 	 * to pinctrl mapping is fixed in the silicon, so we register it
177 	 * explicitly instead of requiring a redundant gpio-ranges in the
178 	 * devicetree.
179 	 * In any case we also want to work on systems that don't use devicetree
180 	 * or acpi.
181 	 */
182 	ret = gpiochip_add_pin_range(&madera_gpio->gpio_chip, "madera-pinctrl",
183 				     0, 0, madera_gpio->gpio_chip.ngpio);
184 	if (ret) {
185 		dev_dbg(&pdev->dev, "Failed to add pin range (%d)\n", ret);
186 		return ret;
187 	}
188 
189 	return 0;
190 }
191 
192 static struct platform_driver madera_gpio_driver = {
193 	.driver = {
194 		.name	= "madera-gpio",
195 	},
196 	.probe		= madera_gpio_probe,
197 };
198 
199 module_platform_driver(madera_gpio_driver);
200 
201 MODULE_SOFTDEP("pre: pinctrl-madera");
202 MODULE_DESCRIPTION("GPIO interface for Madera codecs");
203 MODULE_AUTHOR("Nariman Poushin <nariman@opensource.cirrus.com>");
204 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
205 MODULE_LICENSE("GPL v2");
206 MODULE_ALIAS("platform:madera-gpio");
207