Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Mark Brown | 1074 | 86.61% | 9 | 36.00% |
Linus Walleij | 97 | 7.82% | 6 | 24.00% |
Mika Westerberg | 30 | 2.42% | 1 | 4.00% |
Andy Shevchenko | 17 | 1.37% | 1 | 4.00% |
Axel Lin | 7 | 0.56% | 2 | 8.00% |
Laxman Dewangan | 6 | 0.48% | 1 | 4.00% |
Tejun Heo | 3 | 0.24% | 1 | 4.00% |
Charles Keepax | 2 | 0.16% | 1 | 4.00% |
SF Markus Elfring | 2 | 0.16% | 1 | 4.00% |
Alexandru Ardelean | 1 | 0.08% | 1 | 4.00% |
Julia Lawall | 1 | 0.08% | 1 | 4.00% |
Total | 1240 | 25 |
// SPDX-License-Identifier: GPL-2.0+ /* * gpiolib support for Wolfson WM831x PMICs * * Copyright 2009 Wolfson Microelectronics PLC. * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/gpio/driver.h> #include <linux/mfd/core.h> #include <linux/platform_device.h> #include <linux/seq_file.h> #include <linux/mfd/wm831x/core.h> #include <linux/mfd/wm831x/pdata.h> #include <linux/mfd/wm831x/gpio.h> #include <linux/mfd/wm831x/irq.h> struct wm831x_gpio { struct wm831x *wm831x; struct gpio_chip gpio_chip; }; static int wm831x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) { struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip); struct wm831x *wm831x = wm831x_gpio->wm831x; int val = WM831X_GPN_DIR; if (wm831x->has_gpio_ena) val |= WM831X_GPN_TRI; return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, WM831X_GPN_DIR | WM831X_GPN_TRI | WM831X_GPN_FN_MASK, val); } static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset) { struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip); struct wm831x *wm831x = wm831x_gpio->wm831x; int ret; ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); if (ret < 0) return ret; if (ret & 1 << offset) return 1; else return 0; } static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip); struct wm831x *wm831x = wm831x_gpio->wm831x; wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset, value << offset); } static int wm831x_gpio_direction_out(struct gpio_chip *chip, unsigned offset, int value) { struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip); struct wm831x *wm831x = wm831x_gpio->wm831x; int val = 0; int ret; if (wm831x->has_gpio_ena) val |= WM831X_GPN_TRI; ret = wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, WM831X_GPN_DIR | WM831X_GPN_TRI | WM831X_GPN_FN_MASK, val); if (ret < 0) return ret; /* Can only set GPIO state once it's in output mode */ wm831x_gpio_set(chip, offset, value); return 0; } static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip); struct wm831x *wm831x = wm831x_gpio->wm831x; return irq_create_mapping(wm831x->irq_domain, WM831X_IRQ_GPIO_1 + offset); } static int wm831x_gpio_set_debounce(struct wm831x *wm831x, unsigned offset, unsigned debounce) { int reg = WM831X_GPIO1_CONTROL + offset; int ret, fn; ret = wm831x_reg_read(wm831x, reg); if (ret < 0) return ret; switch (ret & WM831X_GPN_FN_MASK) { case 0: case 1: break; default: /* Not in GPIO mode */ return -EBUSY; } if (debounce >= 32 && debounce <= 64) fn = 0; else if (debounce >= 4000 && debounce <= 8000) fn = 1; else return -EINVAL; return wm831x_set_bits(wm831x, reg, WM831X_GPN_FN_MASK, fn); } static int wm831x_set_config(struct gpio_chip *chip, unsigned int offset, unsigned long config) { struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip); struct wm831x *wm831x = wm831x_gpio->wm831x; int reg = WM831X_GPIO1_CONTROL + offset; switch (pinconf_to_config_param(config)) { case PIN_CONFIG_DRIVE_OPEN_DRAIN: return wm831x_set_bits(wm831x, reg, WM831X_GPN_OD_MASK, WM831X_GPN_OD); case PIN_CONFIG_DRIVE_PUSH_PULL: return wm831x_set_bits(wm831x, reg, WM831X_GPN_OD_MASK, 0); case PIN_CONFIG_INPUT_DEBOUNCE: return wm831x_gpio_set_debounce(wm831x, offset, pinconf_to_config_argument(config)); default: break; } return -ENOTSUPP; } #ifdef CONFIG_DEBUG_FS static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) { struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip); struct wm831x *wm831x = wm831x_gpio->wm831x; int i, tristated; for (i = 0; i < chip->ngpio; i++) { int gpio = i + chip->base; int reg; const char *label, *pull, *powerdomain; /* We report the GPIO even if it's not requested since * we're also reporting things like alternate * functions which apply even when the GPIO is not in * use as a GPIO. */ label = gpiochip_is_requested(chip, i); if (!label) label = "Unrequested"; seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label); reg = wm831x_reg_read(wm831x, WM831X_GPIO1_CONTROL + i); if (reg < 0) { dev_err(wm831x->dev, "GPIO control %d read failed: %d\n", gpio, reg); seq_putc(s, '\n'); continue; } switch (reg & WM831X_GPN_PULL_MASK) { case WM831X_GPIO_PULL_NONE: pull = "nopull"; break; case WM831X_GPIO_PULL_DOWN: pull = "pulldown"; break; case WM831X_GPIO_PULL_UP: pull = "pullup"; break; default: pull = "INVALID PULL"; break; } switch (i + 1) { case 1 ... 3: case 7 ... 9: if (reg & WM831X_GPN_PWR_DOM) powerdomain = "VPMIC"; else powerdomain = "DBVDD"; break; case 4 ... 6: case 10 ... 12: if (reg & WM831X_GPN_PWR_DOM) powerdomain = "SYSVDD"; else powerdomain = "DBVDD"; break; case 13 ... 16: powerdomain = "TPVDD"; break; default: BUG(); break; } tristated = reg & WM831X_GPN_TRI; if (wm831x->has_gpio_ena) tristated = !tristated; seq_printf(s, " %s %s %s %s%s\n" " %s%s (0x%4x)\n", reg & WM831X_GPN_DIR ? "in" : "out", wm831x_gpio_get(chip, i) ? "high" : "low", pull, powerdomain, reg & WM831X_GPN_POL ? "" : " inverted", reg & WM831X_GPN_OD ? "open-drain" : "push-pull", tristated ? " tristated" : "", reg); } } #else #define wm831x_gpio_dbg_show NULL #endif static const struct gpio_chip template_chip = { .label = "wm831x", .owner = THIS_MODULE, .direction_input = wm831x_gpio_direction_in, .get = wm831x_gpio_get, .direction_output = wm831x_gpio_direction_out, .set = wm831x_gpio_set, .to_irq = wm831x_gpio_to_irq, .set_config = wm831x_set_config, .dbg_show = wm831x_gpio_dbg_show, .can_sleep = true, }; static int wm831x_gpio_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_pdata *pdata = &wm831x->pdata; struct wm831x_gpio *wm831x_gpio; device_set_node(&pdev->dev, dev_fwnode(pdev->dev.parent)); wm831x_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm831x_gpio), GFP_KERNEL); if (wm831x_gpio == NULL) return -ENOMEM; wm831x_gpio->wm831x = wm831x; wm831x_gpio->gpio_chip = template_chip; wm831x_gpio->gpio_chip.ngpio = wm831x->num_gpio; wm831x_gpio->gpio_chip.parent = &pdev->dev; if (pdata && pdata->gpio_base) wm831x_gpio->gpio_chip.base = pdata->gpio_base; else wm831x_gpio->gpio_chip.base = -1; return devm_gpiochip_add_data(&pdev->dev, &wm831x_gpio->gpio_chip, wm831x_gpio); } static struct platform_driver wm831x_gpio_driver = { .driver.name = "wm831x-gpio", .probe = wm831x_gpio_probe, }; static int __init wm831x_gpio_init(void) { return platform_driver_register(&wm831x_gpio_driver); } subsys_initcall(wm831x_gpio_init); static void __exit wm831x_gpio_exit(void) { platform_driver_unregister(&wm831x_gpio_driver); } module_exit(wm831x_gpio_exit); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_DESCRIPTION("GPIO interface for WM831x PMICs"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:wm831x-gpio");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1