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