diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-16 06:18:40 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-16 06:18:40 +0300 |
commit | 8de262531f5fbb7458463224a7587429800c24bf (patch) | |
tree | c95d1d2bdeaff95cea17982f1c0e1e552591e40f /drivers/gpio | |
parent | be8454afc50f43016ca8b6130d9673bdd0bd56ec (diff) | |
parent | 7efd105c27fd2323789b41b64763a0e33ed79c08 (diff) | |
download | linux-8de262531f5fbb7458463224a7587429800c24bf.tar.xz |
Merge tag 'mfd-next-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
Pull MFD updates from Lee Jones:
"Core Frameworks:
- Set 'struct device' fwnode when registering a new device
New Drivers:
- Add support for ROHM BD70528 PMIC
New Device Support:
- Add support for LP87561 4-Phase Regulator to TI LP87565 PMIC
- Add support for RK809 and RK817 to Rockchip RK808
- Add support for Lid Angle to ChromeOS core
- Add support for CS47L15 CODEC to Madera core
- Add support for CS47L92 CODEC to Madera core
- Add support for ChromeOS (legacy) Accelerometers in ChromeOS core
- Add support for Add Intel Elkhart Lake PCH to Intel LPSS
New Functionality:
- Provide regulator supply information when registering; madera-core
- Additional Device Tree support; lp87565, madera, cros-ec, rohm,bd71837-pmic
- Allow over-riding power button press via Device Tree; rohm-bd718x7
- Differentiate between running processors; cros_ec_dev
Fix-ups:
- Big header file update; cros_ec_commands.h
- Split header per-subsystem; rohm-bd718x7
- Remove superfluous code; menelaus, cs5535-mfd, cs47lXX-tables
- Trivial; sorting, coding style; intel-lpss-pci
- Only remove Power Off functionality if set locally; rk808
- Make use for Power Off Prepare(); rk808
- Fix spelling mistake in header guards; stmfx
- Properly free IDA resources
- SPDX fixups; cs47lXX-tables, madera
- Error path fixups; hi655x-pmic
Bug Fixes:
- Add missing break in case() statement
- Repair undefined behaviour when not initialising variables; arizona-core, madera-core
- Fix reference to Device Tree documentation; madera"
* tag 'mfd-next-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (45 commits)
mfd: hi655x-pmic: Fix missing return value check for devm_regmap_init_mmio_clk
mfd: madera: Fixup SPDX headers
mfd: madera: Remove some unused registers and fix some defaults
mfd: intel-lpss: Release IDA resources
mfd: intel-lpss: Add Intel Elkhart Lake PCH PCI IDs
mfd: cs5535-mfd: Remove ifdef OLPC noise
mfd: stmfx: Fix macro definition spelling
dt-bindings: mfd: Add link to ROHM BD71847 Datasheet
MAINAINERS: Swap words in INTEL PMIC MULTIFUNCTION DEVICE DRIVERS
mfd: cros_ec_dev: Register cros_ec_accel_legacy driver as a subdevice
mfd: rk808: Prepare rk805 for poweroff
mfd: rk808: Check pm_power_off pointer
mfd: cros_ec: differentiate SCP from EC by feature bit
dt-bindings: Add binding for cros-ec-rpmsg
mfd: madera: Add Madera core support for CS47L92
mfd: madera: Add Madera core support for CS47L15
mfd: madera: Update DT bindings to add additional CODECs
mfd: madera: Add supply mapping for MICVDD
mfd: madera: Fix potential uninitialised use of variable
mfd: madera: Fix bad reference to pinctrl.txt file
...
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 11 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-bd70528.c | 232 |
3 files changed, 244 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index e4fee216d5a4..43d7d6a9d9ab 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -975,6 +975,17 @@ config GPIO_ARIZONA help Support for GPIOs on Wolfson Arizona class devices. +config GPIO_BD70528 + tristate "ROHM BD70528 GPIO support" + depends on MFD_ROHM_BD70528 + help + Support for GPIOs on ROHM BD70528 PMIC. There are four GPIOs + available on the ROHM PMIC in total. The GPIOs can also + generate interrupts. + + This driver can also be built as a module. If so, the module + will be called gpio-bd70528. + config GPIO_BD9571MWV tristate "ROHM BD9571 GPIO support" depends on MFD_BD9571MWV diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9e400e34e300..a4e91175c708 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o +obj-$(CONFIG_GPIO_BD70528) += gpio-bd70528.o obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o diff --git a/drivers/gpio/gpio-bd70528.c b/drivers/gpio/gpio-bd70528.c new file mode 100644 index 000000000000..fd85605d2dab --- /dev/null +++ b/drivers/gpio/gpio-bd70528.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 ROHM Semiconductors +// gpio-bd70528.c ROHM BD70528MWV gpio driver + +#include <linux/gpio/driver.h> +#include <linux/mfd/rohm-bd70528.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define GPIO_IN_REG(offset) (BD70528_REG_GPIO1_IN + (offset) * 2) +#define GPIO_OUT_REG(offset) (BD70528_REG_GPIO1_OUT + (offset) * 2) + +struct bd70528_gpio { + struct rohm_regmap_dev chip; + struct gpio_chip gpio; +}; + +static int bd70528_set_debounce(struct bd70528_gpio *bdgpio, + unsigned int offset, unsigned int debounce) +{ + u8 val; + + switch (debounce) { + case 0: + val = BD70528_DEBOUNCE_DISABLE; + break; + case 1 ... 15: + val = BD70528_DEBOUNCE_15MS; + break; + case 16 ... 30: + val = BD70528_DEBOUNCE_30MS; + break; + case 31 ... 50: + val = BD70528_DEBOUNCE_50MS; + break; + default: + dev_err(bdgpio->chip.dev, + "Invalid debouce value %u\n", debounce); + return -EINVAL; + } + return regmap_update_bits(bdgpio->chip.regmap, GPIO_IN_REG(offset), + BD70528_DEBOUNCE_MASK, val); +} + +static int bd70528_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); + int val, ret; + + /* Do we need to do something to IRQs here? */ + ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset), &val); + if (ret) { + dev_err(bdgpio->chip.dev, "Could not read gpio direction\n"); + return ret; + } + + return !(val & BD70528_GPIO_OUT_EN_MASK); +} + +static int bd70528_gpio_set_config(struct gpio_chip *chip, unsigned int offset, + unsigned long config) +{ + struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + return regmap_update_bits(bdgpio->chip.regmap, + GPIO_OUT_REG(offset), + BD70528_GPIO_DRIVE_MASK, + BD70528_GPIO_OPEN_DRAIN); + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + return regmap_update_bits(bdgpio->chip.regmap, + GPIO_OUT_REG(offset), + BD70528_GPIO_DRIVE_MASK, + BD70528_GPIO_PUSH_PULL); + break; + case PIN_CONFIG_INPUT_DEBOUNCE: + return bd70528_set_debounce(bdgpio, offset, + pinconf_to_config_argument(config)); + break; + default: + break; + } + return -ENOTSUPP; +} + +static int bd70528_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); + + /* Do we need to do something to IRQs here? */ + return regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset), + BD70528_GPIO_OUT_EN_MASK, + BD70528_GPIO_OUT_DISABLE); +} + +static void bd70528_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + int ret; + struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); + u8 val = (value) ? BD70528_GPIO_OUT_HI : BD70528_GPIO_OUT_LO; + + ret = regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset), + BD70528_GPIO_OUT_MASK, val); + if (ret) + dev_err(bdgpio->chip.dev, "Could not set gpio to %d\n", value); +} + +static int bd70528_direction_output(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); + + bd70528_gpio_set(chip, offset, value); + return regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset), + BD70528_GPIO_OUT_EN_MASK, + BD70528_GPIO_OUT_ENABLE); +} + +#define GPIO_IN_STATE_MASK(offset) (BD70528_GPIO_IN_STATE_BASE << (offset)) + +static int bd70528_gpio_get_o(struct bd70528_gpio *bdgpio, unsigned int offset) +{ + int ret; + unsigned int val; + + ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset), &val); + if (!ret) + ret = !!(val & BD70528_GPIO_OUT_MASK); + else + dev_err(bdgpio->chip.dev, "GPIO (out) state read failed\n"); + + return ret; +} + +static int bd70528_gpio_get_i(struct bd70528_gpio *bdgpio, unsigned int offset) +{ + unsigned int val; + int ret; + + ret = regmap_read(bdgpio->chip.regmap, BD70528_REG_GPIO_STATE, &val); + + if (!ret) + ret = !(val & GPIO_IN_STATE_MASK(offset)); + else + dev_err(bdgpio->chip.dev, "GPIO (in) state read failed\n"); + + return ret; +} + +static int bd70528_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + int ret = -EINVAL; + struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); + + /* + * There is a race condition where someone might be changing the + * GPIO direction after we get it but before we read the value. But + * application design where GPIO direction may be changed just when + * we read GPIO value would be pointless as reader could not know + * whether the returned high/low state is caused by input or output. + * Or then there must be other ways to mitigate the issue. Thus + * locking would make no sense. + */ + ret = bd70528_get_direction(chip, offset); + if (ret == 0) + ret = bd70528_gpio_get_o(bdgpio, offset); + else if (ret == 1) + ret = bd70528_gpio_get_i(bdgpio, offset); + else + dev_err(bdgpio->chip.dev, "failed to read GPIO direction\n"); + + return ret; +} + +static int bd70528_probe(struct platform_device *pdev) +{ + struct bd70528_gpio *bdgpio; + struct rohm_regmap_dev *bd70528; + int ret; + + bd70528 = dev_get_drvdata(pdev->dev.parent); + if (!bd70528) { + dev_err(&pdev->dev, "No MFD driver data\n"); + return -EINVAL; + } + + bdgpio = devm_kzalloc(&pdev->dev, sizeof(*bdgpio), + GFP_KERNEL); + if (!bdgpio) + return -ENOMEM; + bdgpio->chip.dev = &pdev->dev; + bdgpio->gpio.parent = pdev->dev.parent; + bdgpio->gpio.label = "bd70528-gpio"; + bdgpio->gpio.owner = THIS_MODULE; + bdgpio->gpio.get_direction = bd70528_get_direction; + bdgpio->gpio.direction_input = bd70528_direction_input; + bdgpio->gpio.direction_output = bd70528_direction_output; + bdgpio->gpio.set_config = bd70528_gpio_set_config; + bdgpio->gpio.can_sleep = true; + bdgpio->gpio.get = bd70528_gpio_get; + bdgpio->gpio.set = bd70528_gpio_set; + bdgpio->gpio.ngpio = 4; + bdgpio->gpio.base = -1; +#ifdef CONFIG_OF_GPIO + bdgpio->gpio.of_node = pdev->dev.parent->of_node; +#endif + bdgpio->chip.regmap = bd70528->regmap; + + ret = devm_gpiochip_add_data(&pdev->dev, &bdgpio->gpio, + bdgpio); + if (ret) + dev_err(&pdev->dev, "gpio_init: Failed to add bd70528-gpio\n"); + + return ret; +} + +static struct platform_driver bd70528_gpio = { + .driver = { + .name = "bd70528-gpio" + }, + .probe = bd70528_probe, +}; + +module_platform_driver(bd70528_gpio); + +MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); +MODULE_DESCRIPTION("BD70528 voltage regulator driver"); +MODULE_LICENSE("GPL"); |