// SPDX-License-Identifier: GPL-2.0+ /* * gpiolib support for different serdes chip * * Copyright (c) 2023-2028 Rockchip Electronics Co., Ltd. * * Author: luowei * */ #include "core.h" static int serdes_gpio_direction_in(struct gpio_chip *chip, unsigned int offset) { struct serdes_gpio *serdes_gpio = gpiochip_get_data(chip); struct serdes *serdes = serdes_gpio->parent->parent; int ret = 0; if (serdes->chip_data->gpio_ops->direction_input) ret = serdes->chip_data->gpio_ops->direction_input(serdes, offset); SERDES_DBG_MFD("%s: %s %s gpio=%d\n", __func__, dev_name(serdes->dev), serdes->chip_data->name, offset); return ret; } static int serdes_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct serdes_gpio *serdes_gpio = gpiochip_get_data(chip); struct serdes *serdes = serdes_gpio->parent->parent; int ret = 0; if (serdes->chip_data->gpio_ops->get_level) ret = serdes->chip_data->gpio_ops->get_level(serdes, offset); SERDES_DBG_MFD("%s: %s %s gpio=%d\n", __func__, dev_name(serdes->dev), serdes->chip_data->name, offset); return ret; } static void serdes_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct serdes_gpio *serdes_gpio = gpiochip_get_data(chip); struct serdes *serdes = serdes_gpio->parent->parent; int ret = 0; if (serdes->chip_data->gpio_ops->set_level) ret = serdes->chip_data->gpio_ops->set_level(serdes, offset, value); SERDES_DBG_MFD("%s: %s %s gpio=%d,val=%d\n", __func__, dev_name(serdes->dev), serdes->chip_data->name, offset, value); } static int serdes_gpio_direction_out(struct gpio_chip *chip, unsigned int offset, int value) { struct serdes_gpio *serdes_gpio = gpiochip_get_data(chip); struct serdes *serdes = serdes_gpio->parent->parent; int ret = 0; if (serdes->chip_data->gpio_ops->direction_output) ret = serdes->chip_data->gpio_ops->direction_output(serdes, offset, value); SERDES_DBG_MFD("%s: %s %s gpio=%d,val=%d\n", __func__, dev_name(serdes->dev), serdes->chip_data->name, offset, value); return ret; } static int serdes_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) { struct serdes_gpio *serdes_gpio = gpiochip_get_data(chip); struct serdes *serdes = serdes_gpio->parent->parent; int ret = 0; if (serdes->chip_data->gpio_ops->to_irq) ret = serdes->chip_data->gpio_ops->to_irq(serdes, offset); SERDES_DBG_MFD("%s: %s %s gpio=%d\n", __func__, dev_name(serdes->dev), serdes->chip_data->name, offset); return ret; } static int serdes_set_config(struct gpio_chip *chip, unsigned int offset, unsigned long config) { struct serdes_gpio *serdes_gpio = gpiochip_get_data(chip); struct serdes *serdes = serdes_gpio->parent->parent; //int param = pinconf_to_config_param(config); int ret = 0; //int gpio = offset; if (serdes->chip_data->gpio_ops->set_config) ret = serdes->chip_data->gpio_ops->set_config(serdes, offset, config); SERDES_DBG_MFD("%s: %s %s gpio=%d,config=%d\n", __func__, dev_name(serdes->dev), serdes->chip_data->name, offset, pinconf_to_config_param(config)); return ret; } #ifdef CONFIG_DEBUG_FS static void serdes_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) { struct serdes_gpio *serdes_gpio = gpiochip_get_data(chip); struct serdes *serdes = serdes_gpio->parent->parent; int i = 0; int ret = 0; for (i = 0; i < chip->ngpio; i++) { int gpio = i + chip->base; const char *level; seq_printf(s, "gpio-%02d ", gpio); if (serdes->chip_data->gpio_ops->get_level) ret = serdes->chip_data->gpio_ops->get_level(serdes, i); switch (ret) { case SERDES_GPIO_LEVEL_HIGH: level = "level-high"; break; case SERDES_GPIO_LEVEL_LOW: level = "level-low"; break; default: level = "invalid level"; break; } seq_printf(s, " %s\n", level); } } #else #define serdes_gpio_dbg_show NULL #endif static const struct gpio_chip serdes_gpio_chip = { .owner = THIS_MODULE, .request = gpiochip_generic_request, .free = gpiochip_generic_free, .direction_input = serdes_gpio_direction_in, .direction_output = serdes_gpio_direction_out, .get = serdes_gpio_get, .set = serdes_gpio_set, .set_config = serdes_set_config, .to_irq = serdes_gpio_to_irq, .dbg_show = serdes_gpio_dbg_show, .can_sleep = true, }; static int serdes_gpio_probe(struct platform_device *pdev) { struct serdes_pinctrl *serdes_pinctrl = dev_get_drvdata(pdev->dev.parent); struct serdes *serdes = serdes_pinctrl->parent; struct serdes_chip_data *chip_data = serdes->chip_data; struct device *dev = &pdev->dev; struct serdes_gpio *serdes_gpio; const char *list_name = "gpio-ranges"; struct of_phandle_args of_args; int ret; serdes_gpio = devm_kzalloc(&pdev->dev, sizeof(*serdes_gpio), GFP_KERNEL); if (serdes_gpio == NULL) return -ENOMEM; ret = of_parse_phandle_with_fixed_args(dev->of_node, list_name, 3, 0, &of_args); if (ret) { dev_err(dev, "Unable to parse %s list property\n", list_name); return ret; } serdes_pinctrl->gpio = serdes_gpio; serdes_gpio->dev = dev; serdes_gpio->parent = serdes_pinctrl; serdes_gpio->gpio_chip = serdes_gpio_chip; serdes_gpio->gpio_chip.parent = pdev->dev.parent; if (of_args.args[2]) { serdes_gpio->gpio_chip.base = of_args.args[1]; serdes_gpio->gpio_chip.ngpio = of_args.args[2]; } else { serdes_gpio->gpio_chip.base = -1; serdes_gpio->gpio_chip.ngpio = 8; } #ifdef CONFIG_OF_GPIO serdes_gpio->gpio_chip.of_node = serdes_gpio->dev->of_node; #endif serdes_gpio->gpio_chip.label = kasprintf(GFP_KERNEL, "%s-gpio", chip_data->name); /* Add gpiochip */ ret = devm_gpiochip_add_data(&pdev->dev, &serdes_gpio->gpio_chip, serdes_gpio); if (ret < 0) { dev_err(&pdev->dev, "Could not register serdes gpiochip, %d\n", ret); return ret; } platform_set_drvdata(pdev, serdes_gpio); dev_info(serdes_gpio->dev->parent->parent, "%s serdes_gpio_probe successful, base=%d, ngpio=%d\n", serdes->chip_data->name, serdes_gpio->gpio_chip.base, serdes_gpio->gpio_chip.ngpio); return ret; } static const struct of_device_id serdes_gpio_of_match[] = { { .compatible = "rohm,bu18tl82-gpio", }, { .compatible = "rohm,bu18rl82-gpio", }, { .compatible = "maxim,max96745-gpio", }, { .compatible = "maxim,max96752-gpio", }, { .compatible = "maxim,max96755-gpio", }, { .compatible = "maxim,max96772-gpio", }, { .compatible = "maxim,max96789-gpio", }, { .compatible = "rockchip,rkx111-gpio", }, { .compatible = "rockchip,rkx121-gpio", }, { .compatible = "novo,nca9539-gpio", }, { } }; static struct platform_driver serdes_gpio_driver = { .driver = { .name = "serdes-gpio", .of_match_table = of_match_ptr(serdes_gpio_of_match), }, .probe = serdes_gpio_probe, }; static int __init serdes_gpio_init(void) { return platform_driver_register(&serdes_gpio_driver); } device_initcall(serdes_gpio_init); static void __exit serdes_gpio_exit(void) { platform_driver_unregister(&serdes_gpio_driver); } module_exit(serdes_gpio_exit); MODULE_AUTHOR("Luo Wei "); MODULE_DESCRIPTION("display bridge interface for different serdes"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:serdes-bridge");