162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * GPIO support for Cirrus Logic Madera codecs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015-2018 Cirrus Logic 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/gpio/driver.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/mfd/madera/core.h> 1462306a36Sopenharmony_ci#include <linux/mfd/madera/pdata.h> 1562306a36Sopenharmony_ci#include <linux/mfd/madera/registers.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct madera_gpio { 1862306a36Sopenharmony_ci struct madera *madera; 1962306a36Sopenharmony_ci /* storage space for the gpio_chip we're using */ 2062306a36Sopenharmony_ci struct gpio_chip gpio_chip; 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int madera_gpio_get_direction(struct gpio_chip *chip, 2462306a36Sopenharmony_ci unsigned int offset) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct madera_gpio *madera_gpio = gpiochip_get_data(chip); 2762306a36Sopenharmony_ci struct madera *madera = madera_gpio->madera; 2862306a36Sopenharmony_ci unsigned int reg_offset = 2 * offset; 2962306a36Sopenharmony_ci unsigned int val; 3062306a36Sopenharmony_ci int ret; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci ret = regmap_read(madera->regmap, MADERA_GPIO1_CTRL_2 + reg_offset, 3362306a36Sopenharmony_ci &val); 3462306a36Sopenharmony_ci if (ret < 0) 3562306a36Sopenharmony_ci return ret; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (val & MADERA_GP1_DIR_MASK) 3862306a36Sopenharmony_ci return GPIO_LINE_DIRECTION_IN; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int madera_gpio_direction_in(struct gpio_chip *chip, unsigned int offset) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct madera_gpio *madera_gpio = gpiochip_get_data(chip); 4662306a36Sopenharmony_ci struct madera *madera = madera_gpio->madera; 4762306a36Sopenharmony_ci unsigned int reg_offset = 2 * offset; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return regmap_update_bits(madera->regmap, 5062306a36Sopenharmony_ci MADERA_GPIO1_CTRL_2 + reg_offset, 5162306a36Sopenharmony_ci MADERA_GP1_DIR_MASK, MADERA_GP1_DIR); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic int madera_gpio_get(struct gpio_chip *chip, unsigned int offset) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct madera_gpio *madera_gpio = gpiochip_get_data(chip); 5762306a36Sopenharmony_ci struct madera *madera = madera_gpio->madera; 5862306a36Sopenharmony_ci unsigned int reg_offset = 2 * offset; 5962306a36Sopenharmony_ci unsigned int val; 6062306a36Sopenharmony_ci int ret; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci ret = regmap_read(madera->regmap, MADERA_GPIO1_CTRL_1 + reg_offset, 6362306a36Sopenharmony_ci &val); 6462306a36Sopenharmony_ci if (ret < 0) 6562306a36Sopenharmony_ci return ret; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return !!(val & MADERA_GP1_LVL_MASK); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int madera_gpio_direction_out(struct gpio_chip *chip, 7162306a36Sopenharmony_ci unsigned int offset, int value) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct madera_gpio *madera_gpio = gpiochip_get_data(chip); 7462306a36Sopenharmony_ci struct madera *madera = madera_gpio->madera; 7562306a36Sopenharmony_ci unsigned int reg_offset = 2 * offset; 7662306a36Sopenharmony_ci unsigned int reg_val = value ? MADERA_GP1_LVL : 0; 7762306a36Sopenharmony_ci int ret; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci ret = regmap_update_bits(madera->regmap, 8062306a36Sopenharmony_ci MADERA_GPIO1_CTRL_2 + reg_offset, 8162306a36Sopenharmony_ci MADERA_GP1_DIR_MASK, 0); 8262306a36Sopenharmony_ci if (ret < 0) 8362306a36Sopenharmony_ci return ret; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return regmap_update_bits(madera->regmap, 8662306a36Sopenharmony_ci MADERA_GPIO1_CTRL_1 + reg_offset, 8762306a36Sopenharmony_ci MADERA_GP1_LVL_MASK, reg_val); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void madera_gpio_set(struct gpio_chip *chip, unsigned int offset, 9162306a36Sopenharmony_ci int value) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct madera_gpio *madera_gpio = gpiochip_get_data(chip); 9462306a36Sopenharmony_ci struct madera *madera = madera_gpio->madera; 9562306a36Sopenharmony_ci unsigned int reg_offset = 2 * offset; 9662306a36Sopenharmony_ci unsigned int reg_val = value ? MADERA_GP1_LVL : 0; 9762306a36Sopenharmony_ci int ret; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci ret = regmap_update_bits(madera->regmap, 10062306a36Sopenharmony_ci MADERA_GPIO1_CTRL_1 + reg_offset, 10162306a36Sopenharmony_ci MADERA_GP1_LVL_MASK, reg_val); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* set() doesn't return an error so log a warning */ 10462306a36Sopenharmony_ci if (ret) 10562306a36Sopenharmony_ci dev_warn(madera->dev, "Failed to write to 0x%x (%d)\n", 10662306a36Sopenharmony_ci MADERA_GPIO1_CTRL_1 + reg_offset, ret); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic const struct gpio_chip madera_gpio_chip = { 11062306a36Sopenharmony_ci .label = "madera", 11162306a36Sopenharmony_ci .owner = THIS_MODULE, 11262306a36Sopenharmony_ci .request = gpiochip_generic_request, 11362306a36Sopenharmony_ci .free = gpiochip_generic_free, 11462306a36Sopenharmony_ci .get_direction = madera_gpio_get_direction, 11562306a36Sopenharmony_ci .direction_input = madera_gpio_direction_in, 11662306a36Sopenharmony_ci .get = madera_gpio_get, 11762306a36Sopenharmony_ci .direction_output = madera_gpio_direction_out, 11862306a36Sopenharmony_ci .set = madera_gpio_set, 11962306a36Sopenharmony_ci .set_config = gpiochip_generic_config, 12062306a36Sopenharmony_ci .can_sleep = true, 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int madera_gpio_probe(struct platform_device *pdev) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct madera *madera = dev_get_drvdata(pdev->dev.parent); 12662306a36Sopenharmony_ci struct madera_pdata *pdata = &madera->pdata; 12762306a36Sopenharmony_ci struct madera_gpio *madera_gpio; 12862306a36Sopenharmony_ci int ret; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci madera_gpio = devm_kzalloc(&pdev->dev, sizeof(*madera_gpio), 13162306a36Sopenharmony_ci GFP_KERNEL); 13262306a36Sopenharmony_ci if (!madera_gpio) 13362306a36Sopenharmony_ci return -ENOMEM; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci madera_gpio->madera = madera; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* Construct suitable gpio_chip from the template in madera_gpio_chip */ 13862306a36Sopenharmony_ci madera_gpio->gpio_chip = madera_gpio_chip; 13962306a36Sopenharmony_ci madera_gpio->gpio_chip.parent = pdev->dev.parent; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci switch (madera->type) { 14262306a36Sopenharmony_ci case CS47L15: 14362306a36Sopenharmony_ci madera_gpio->gpio_chip.ngpio = CS47L15_NUM_GPIOS; 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci case CS47L35: 14662306a36Sopenharmony_ci madera_gpio->gpio_chip.ngpio = CS47L35_NUM_GPIOS; 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci case CS47L85: 14962306a36Sopenharmony_ci case WM1840: 15062306a36Sopenharmony_ci madera_gpio->gpio_chip.ngpio = CS47L85_NUM_GPIOS; 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci case CS47L90: 15362306a36Sopenharmony_ci case CS47L91: 15462306a36Sopenharmony_ci madera_gpio->gpio_chip.ngpio = CS47L90_NUM_GPIOS; 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci case CS42L92: 15762306a36Sopenharmony_ci case CS47L92: 15862306a36Sopenharmony_ci case CS47L93: 15962306a36Sopenharmony_ci madera_gpio->gpio_chip.ngpio = CS47L92_NUM_GPIOS; 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci default: 16262306a36Sopenharmony_ci dev_err(&pdev->dev, "Unknown chip variant %d\n", madera->type); 16362306a36Sopenharmony_ci return -EINVAL; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* We want to be usable on systems that don't use devicetree or acpi */ 16762306a36Sopenharmony_ci if (pdata->gpio_base) 16862306a36Sopenharmony_ci madera_gpio->gpio_chip.base = pdata->gpio_base; 16962306a36Sopenharmony_ci else 17062306a36Sopenharmony_ci madera_gpio->gpio_chip.base = -1; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci ret = devm_gpiochip_add_data(&pdev->dev, 17362306a36Sopenharmony_ci &madera_gpio->gpio_chip, 17462306a36Sopenharmony_ci madera_gpio); 17562306a36Sopenharmony_ci if (ret < 0) { 17662306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Could not register gpiochip, %d\n", ret); 17762306a36Sopenharmony_ci return ret; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* 18162306a36Sopenharmony_ci * This is part of a composite MFD device which can only be used with 18262306a36Sopenharmony_ci * the corresponding pinctrl driver. On all supported silicon the GPIO 18362306a36Sopenharmony_ci * to pinctrl mapping is fixed in the silicon, so we register it 18462306a36Sopenharmony_ci * explicitly instead of requiring a redundant gpio-ranges in the 18562306a36Sopenharmony_ci * devicetree. 18662306a36Sopenharmony_ci * In any case we also want to work on systems that don't use devicetree 18762306a36Sopenharmony_ci * or acpi. 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_ci ret = gpiochip_add_pin_range(&madera_gpio->gpio_chip, "madera-pinctrl", 19062306a36Sopenharmony_ci 0, 0, madera_gpio->gpio_chip.ngpio); 19162306a36Sopenharmony_ci if (ret) { 19262306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Failed to add pin range (%d)\n", ret); 19362306a36Sopenharmony_ci return ret; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic struct platform_driver madera_gpio_driver = { 20062306a36Sopenharmony_ci .driver = { 20162306a36Sopenharmony_ci .name = "madera-gpio", 20262306a36Sopenharmony_ci }, 20362306a36Sopenharmony_ci .probe = madera_gpio_probe, 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cimodule_platform_driver(madera_gpio_driver); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciMODULE_SOFTDEP("pre: pinctrl-madera"); 20962306a36Sopenharmony_ciMODULE_DESCRIPTION("GPIO interface for Madera codecs"); 21062306a36Sopenharmony_ciMODULE_AUTHOR("Nariman Poushin <nariman@opensource.cirrus.com>"); 21162306a36Sopenharmony_ciMODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); 21262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 21362306a36Sopenharmony_ciMODULE_ALIAS("platform:madera-gpio"); 214