brintos

brintos / linux-shallow public Read only

0
0
Text · 7.4 KiB · bf05c9b Raw
308 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * gpiolib support for Wolfson WM89944 *5 * Copyright 2009 Wolfson Microelectronics PLC.6 *7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>8 *9 */10 11#include <linux/cleanup.h>12#include <linux/kernel.h>13#include <linux/slab.h>14#include <linux/module.h>15#include <linux/gpio/driver.h>16#include <linux/mfd/core.h>17#include <linux/platform_device.h>18#include <linux/seq_file.h>19#include <linux/regmap.h>20 21#include <linux/mfd/wm8994/core.h>22#include <linux/mfd/wm8994/pdata.h>23#include <linux/mfd/wm8994/gpio.h>24#include <linux/mfd/wm8994/registers.h>25 26struct wm8994_gpio {27	struct wm8994 *wm8994;28	struct gpio_chip gpio_chip;29};30 31static int wm8994_gpio_request(struct gpio_chip *chip, unsigned offset)32{33	struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip);34	struct wm8994 *wm8994 = wm8994_gpio->wm8994;35 36	switch (wm8994->type) {37	case WM8958:38		switch (offset) {39		case 1:40		case 2:41		case 3:42		case 4:43		case 6:44			return -EINVAL;45		}46		break;47	default:48		break;49	}50 51	return 0;52}53 54static int wm8994_gpio_direction_in(struct gpio_chip *chip, unsigned offset)55{56	struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip);57	struct wm8994 *wm8994 = wm8994_gpio->wm8994;58 59	return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset,60			       WM8994_GPN_DIR, WM8994_GPN_DIR);61}62 63static int wm8994_gpio_get(struct gpio_chip *chip, unsigned offset)64{65	struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip);66	struct wm8994 *wm8994 = wm8994_gpio->wm8994;67	int ret;68 69	ret = wm8994_reg_read(wm8994, WM8994_GPIO_1 + offset);70	if (ret < 0)71		return ret;72 73	if (ret & WM8994_GPN_LVL)74		return 1;75	else76		return 0;77}78 79static int wm8994_gpio_direction_out(struct gpio_chip *chip,80				     unsigned offset, int value)81{82	struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip);83	struct wm8994 *wm8994 = wm8994_gpio->wm8994;84 85	if (value)86		value = WM8994_GPN_LVL;87 88	return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset,89			       WM8994_GPN_DIR | WM8994_GPN_LVL, value);90}91 92static void wm8994_gpio_set(struct gpio_chip *chip, unsigned offset, int value)93{94	struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip);95	struct wm8994 *wm8994 = wm8994_gpio->wm8994;96 97	if (value)98		value = WM8994_GPN_LVL;99 100	wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, WM8994_GPN_LVL, value);101}102 103static int wm8994_gpio_set_config(struct gpio_chip *chip, unsigned int offset,104				  unsigned long config)105{106	struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip);107	struct wm8994 *wm8994 = wm8994_gpio->wm8994;108 109	switch (pinconf_to_config_param(config)) {110	case PIN_CONFIG_DRIVE_OPEN_DRAIN:111		return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset,112				       WM8994_GPN_OP_CFG_MASK,113				       WM8994_GPN_OP_CFG);114	case PIN_CONFIG_DRIVE_PUSH_PULL:115		return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset,116				       WM8994_GPN_OP_CFG_MASK, 0);117	default:118		break;119	}120 121	return -ENOTSUPP;122}123 124static int wm8994_gpio_to_irq(struct gpio_chip *chip, unsigned offset)125{126	struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip);127	struct wm8994 *wm8994 = wm8994_gpio->wm8994;128 129	return regmap_irq_get_virq(wm8994->irq_data, offset);130}131 132 133#ifdef CONFIG_DEBUG_FS134static const char *wm8994_gpio_fn(u16 fn)135{136	switch (fn) {137	case WM8994_GP_FN_PIN_SPECIFIC:138		return "pin-specific";139	case WM8994_GP_FN_GPIO:140		return "GPIO";141	case WM8994_GP_FN_SDOUT:142		return "SDOUT";143	case WM8994_GP_FN_IRQ:144		return "IRQ";145	case WM8994_GP_FN_TEMPERATURE:146		return "Temperature";147	case WM8994_GP_FN_MICBIAS1_DET:148		return "MICBIAS1 detect";149	case WM8994_GP_FN_MICBIAS1_SHORT:150		return "MICBIAS1 short";151	case WM8994_GP_FN_MICBIAS2_DET:152		return "MICBIAS2 detect";153	case WM8994_GP_FN_MICBIAS2_SHORT:154		return "MICBIAS2 short";155	case WM8994_GP_FN_FLL1_LOCK:156		return "FLL1 lock";157	case WM8994_GP_FN_FLL2_LOCK:158		return "FLL2 lock";159	case WM8994_GP_FN_SRC1_LOCK:160		return "SRC1 lock";161	case WM8994_GP_FN_SRC2_LOCK:162		return "SRC2 lock";163	case WM8994_GP_FN_DRC1_ACT:164		return "DRC1 activity";165	case WM8994_GP_FN_DRC2_ACT:166		return "DRC2 activity";167	case WM8994_GP_FN_DRC3_ACT:168		return "DRC3 activity";169	case WM8994_GP_FN_WSEQ_STATUS:170		return "Write sequencer";171	case WM8994_GP_FN_FIFO_ERROR:172		return "FIFO error";173	case WM8994_GP_FN_OPCLK:174		return "OPCLK";175	case WM8994_GP_FN_THW:176		return "Thermal warning";177	case WM8994_GP_FN_DCS_DONE:178		return "DC servo";179	case WM8994_GP_FN_FLL1_OUT:180		return "FLL1 output";181	case WM8994_GP_FN_FLL2_OUT:182		return "FLL1 output";183	default:184		return "Unknown";185	}186}187 188static void wm8994_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)189{190	struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip);191	struct wm8994 *wm8994 = wm8994_gpio->wm8994;192	int i;193 194	for (i = 0; i < chip->ngpio; i++) {195		int gpio = i + chip->base;196		int reg;197 198		/* We report the GPIO even if it's not requested since199		 * we're also reporting things like alternate200		 * functions which apply even when the GPIO is not in201		 * use as a GPIO.202		 */203		char *label __free(kfree) = gpiochip_dup_line_label(chip, i);204		if (IS_ERR(label)) {205			dev_err(wm8994->dev, "Failed to duplicate label\n");206			continue;207		}208 209		seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio,210			   label ?: "Unrequested");211 212		reg = wm8994_reg_read(wm8994, WM8994_GPIO_1 + i);213		if (reg < 0) {214			dev_err(wm8994->dev,215				"GPIO control %d read failed: %d\n",216				gpio, reg);217			seq_printf(s, "\n");218			continue;219		}220 221		if (reg & WM8994_GPN_DIR)222			seq_printf(s, "in ");223		else224			seq_printf(s, "out ");225 226		if (reg & WM8994_GPN_PU)227			seq_printf(s, "pull up ");228 229		if (reg & WM8994_GPN_PD)230			seq_printf(s, "pull down ");231 232		if (reg & WM8994_GPN_POL)233			seq_printf(s, "inverted ");234		else235			seq_printf(s, "noninverted ");236 237		if (reg & WM8994_GPN_OP_CFG)238			seq_printf(s, "open drain ");239		else240			seq_printf(s, "push-pull ");241 242		seq_printf(s, "%s (%x)\n",243			   wm8994_gpio_fn(reg & WM8994_GPN_FN_MASK), reg);244	}245}246#else247#define wm8994_gpio_dbg_show NULL248#endif249 250static const struct gpio_chip template_chip = {251	.label			= "wm8994",252	.owner			= THIS_MODULE,253	.request		= wm8994_gpio_request,254	.direction_input	= wm8994_gpio_direction_in,255	.get			= wm8994_gpio_get,256	.direction_output	= wm8994_gpio_direction_out,257	.set			= wm8994_gpio_set,258	.set_config		= wm8994_gpio_set_config,259	.to_irq			= wm8994_gpio_to_irq,260	.dbg_show		= wm8994_gpio_dbg_show,261	.can_sleep		= true,262};263 264static int wm8994_gpio_probe(struct platform_device *pdev)265{266	struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent);267	struct wm8994_pdata *pdata = dev_get_platdata(wm8994->dev);268	struct wm8994_gpio *wm8994_gpio;269 270	wm8994_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm8994_gpio),271				   GFP_KERNEL);272	if (wm8994_gpio == NULL)273		return -ENOMEM;274 275	wm8994_gpio->wm8994 = wm8994;276	wm8994_gpio->gpio_chip = template_chip;277	wm8994_gpio->gpio_chip.ngpio = WM8994_GPIO_MAX;278	wm8994_gpio->gpio_chip.parent = &pdev->dev;279	if (pdata && pdata->gpio_base)280		wm8994_gpio->gpio_chip.base = pdata->gpio_base;281	else282		wm8994_gpio->gpio_chip.base = -1;283 284	return devm_gpiochip_add_data(&pdev->dev, &wm8994_gpio->gpio_chip, wm8994_gpio);285}286 287static struct platform_driver wm8994_gpio_driver = {288	.driver.name	= "wm8994-gpio",289	.probe		= wm8994_gpio_probe,290};291 292static int __init wm8994_gpio_init(void)293{294	return platform_driver_register(&wm8994_gpio_driver);295}296subsys_initcall(wm8994_gpio_init);297 298static void __exit wm8994_gpio_exit(void)299{300	platform_driver_unregister(&wm8994_gpio_driver);301}302module_exit(wm8994_gpio_exit);303 304MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");305MODULE_DESCRIPTION("GPIO interface for WM8994");306MODULE_LICENSE("GPL");307MODULE_ALIAS("platform:wm8994-gpio");308