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 const 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