From 9d77db3c730780c8ef5ccd4b66403ff5675cfe4e Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 13 May 2024 10:30:14 +0000 Subject: [PATCH] modify sin led gpio --- kernel/drivers/gpio/gpio-rockchip.c | 428 +++++++++++++++++++++++++++++++++-------------------- 1 files changed, 268 insertions(+), 160 deletions(-) diff --git a/kernel/drivers/gpio/gpio-rockchip.c b/kernel/drivers/gpio/gpio-rockchip.c index e183edf..b9327a3 100644 --- a/kernel/drivers/gpio/gpio-rockchip.c +++ b/kernel/drivers/gpio/gpio-rockchip.c @@ -1,31 +1,35 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2020 Rockchip Electronics Co. Ltd. + * Copyright (c) 2013 MundoReader S.L. + * Author: Heiko Stuebner <heiko@sntech.de> + * + * Copyright (c) 2021 Rockchip Electronics Co. Ltd. */ -#include <linux/init.h> -#include <linux/module.h> +#include <linux/acpi.h> #include <linux/bitops.h> #include <linux/clk.h> -#include <linux/err.h> #include <linux/device.h> +#include <linux/err.h> #include <linux/gpio/driver.h> +#include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/module.h> #include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_device.h> -#include <linux/of_irq.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/platform_device.h> +#include <linux/property.h> #include <linux/regmap.h> #include "../pinctrl/core.h" #include "../pinctrl/pinctrl-rockchip.h" -#define GPIO_TYPE_V1 (0) /* GPIO Version ID reserved */ -#define GPIO_TYPE_V2 (0x01000C2B) /* GPIO Version ID 0x01000C2B */ -#define GPIO_TYPE_V2_1 (0x0101157C) /* GPIO Version ID 0x0101157C */ +#define GPIO_TYPE_V1 (0) /* GPIO Version ID reserved */ +#define GPIO_TYPE_V2 (0x01000C2B) /* GPIO Version ID 0x01000C2B */ +#define GPIO_TYPE_V2_1 (0x0101157C) /* GPIO Version ID 0x0101157C */ -#define GPIO_BANK_PIN_NUM (32) +#define GPIO_MAX_PINS (32) static const struct rockchip_gpio_regs gpio_regs_v1 = { .port_dr = 0x00, @@ -134,7 +138,35 @@ return data & (0x1); } -static void rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +static int rockchip_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + struct rockchip_pin_bank *bank = gpiochip_get_data(chip); + u32 data; + + data = rockchip_gpio_readl_bit(bank, offset, bank->gpio_regs->port_ddr); + if (data) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + +static int rockchip_gpio_set_direction(struct gpio_chip *chip, + unsigned int offset, bool input) +{ + struct rockchip_pin_bank *bank = gpiochip_get_data(chip); + unsigned long flags; + u32 data = input ? 0 : 1; + + raw_spin_lock_irqsave(&bank->slock, flags); + rockchip_gpio_writel_bit(bank, offset, data, bank->gpio_regs->port_ddr); + raw_spin_unlock_irqrestore(&bank->slock, flags); + + return 0; +} + +static void rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct rockchip_pin_bank *bank = gpiochip_get_data(gc); unsigned long flags; @@ -156,41 +188,9 @@ return data; } -static int rockchip_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) -{ - struct rockchip_pin_bank *bank = gpiochip_get_data(chip); - u32 data; - - data = rockchip_gpio_readl_bit(bank, offset, bank->gpio_regs->port_ddr); - - return !data; -} - -static int rockchip_gpio_set_direction(struct gpio_chip *chip, unsigned int offset, bool input) -{ - struct rockchip_pin_bank *bank = gpiochip_get_data(chip); - u32 data = input ? 0 : 1; - - rockchip_gpio_writel_bit(bank, offset, data, bank->gpio_regs->port_ddr); - - return 0; -} - -static int rockchip_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) -{ - return rockchip_gpio_set_direction(gc, offset, true); -} - -static int rockchip_gpio_direction_output(struct gpio_chip *gc, - unsigned int offset, int value) -{ - rockchip_gpio_set(gc, offset, value); - - return rockchip_gpio_set_direction(gc, offset, false); -} - static int rockchip_gpio_set_debounce(struct gpio_chip *gc, - unsigned int offset, unsigned int debounce) + unsigned int offset, + unsigned int debounce) { struct rockchip_pin_bank *bank = gpiochip_get_data(gc); const struct rockchip_gpio_regs *reg = bank->gpio_regs; @@ -199,11 +199,13 @@ unsigned int cur_div_reg; u64 div; - if (!IS_ERR(bank->db_clk)) { + if (bank->gpio_type == GPIO_TYPE_V2 && !IS_ERR(bank->db_clk)) { div_debounce_support = true; freq = clk_get_rate(bank->db_clk); + if (!freq) + return -EINVAL; max_debounce = (GENMASK(23, 0) + 1) * 2 * 1000000 / freq; - if (debounce > max_debounce) + if ((unsigned long)debounce > max_debounce) return -EINVAL; div = debounce * freq; @@ -218,9 +220,11 @@ if (debounce) { if (div_debounce_support) { /* Configure the max debounce from consumers */ - cur_div_reg = readl(bank->reg_base + reg->dbclk_div_con); + cur_div_reg = readl(bank->reg_base + + reg->dbclk_div_con); if (cur_div_reg < div_reg) - writel(div_reg, bank->reg_base + reg->dbclk_div_con); + writel(div_reg, bank->reg_base + + reg->dbclk_div_con); rockchip_gpio_writel_bit(bank, offset, 1, reg->dbclk_div_en); } @@ -247,6 +251,20 @@ return 0; } +static int rockchip_gpio_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + return rockchip_gpio_set_direction(gc, offset, true); +} + +static int rockchip_gpio_direction_output(struct gpio_chip *gc, + unsigned int offset, int value) +{ + rockchip_gpio_set(gc, offset, value); + + return rockchip_gpio_set_direction(gc, offset, false); +} + /* * gpiolib set_config callback function. The setting of the pin * mux function as 'gpio output' will be handled by the pinctrl subsystem @@ -257,10 +275,10 @@ { enum pin_config_param param = pinconf_to_config_param(config); unsigned int debounce = pinconf_to_config_argument(config); - int ret = 0; switch (param) { case PIN_CONFIG_INPUT_DEBOUNCE: + rockchip_gpio_set_debounce(gc, offset, debounce); /* * Rockchip's gpio could only support up to one period * of the debounce clock(pclk), which is far away from @@ -272,15 +290,10 @@ * still return -ENOTSUPP as before, to make sure the caller * of gpiod_set_debounce won't change its behaviour. */ - rockchip_gpio_set_debounce(gc, offset, debounce); - ret = -ENOTSUPP; - break; + return -ENOTSUPP; default: - ret = -ENOTSUPP; - break; + return -ENOTSUPP; } - - return ret; } /* @@ -317,14 +330,13 @@ { struct irq_chip *chip = irq_desc_get_chip(desc); struct rockchip_pin_bank *bank = irq_desc_get_handler_data(desc); - const struct rockchip_gpio_regs *reg = bank->gpio_regs; u32 pend; dev_dbg(bank->dev, "got irq for bank %s\n", bank->name); chained_irq_enter(chip, desc); - pend = readl_relaxed(bank->reg_base + reg->int_status); + pend = readl_relaxed(bank->reg_base + bank->gpio_regs->int_status); while (pend) { unsigned int irq, virq; @@ -348,24 +360,26 @@ u32 data, data_old, polarity; unsigned long flags; - data = readl_relaxed(bank->reg_base + reg->ext_port); + data = readl_relaxed(bank->reg_base + + bank->gpio_regs->ext_port); do { raw_spin_lock_irqsave(&bank->slock, flags); polarity = readl_relaxed(bank->reg_base + - reg->int_polarity); + bank->gpio_regs->int_polarity); if (data & BIT(irq)) polarity &= ~BIT(irq); else polarity |= BIT(irq); - writel(polarity, bank->reg_base + - reg->int_polarity); + writel(polarity, + bank->reg_base + + bank->gpio_regs->int_polarity); raw_spin_unlock_irqrestore(&bank->slock, flags); data_old = data; data = readl_relaxed(bank->reg_base + - reg->ext_port); + bank->gpio_regs->ext_port); } while ((data & BIT(irq)) != (data_old & BIT(irq))); } @@ -415,8 +429,8 @@ level |= mask; /* - * Determine gpio state. If 1 next interrupt should be falling - * otherwise rising. + * Determine gpio state. If 1 next interrupt should be + * falling otherwise rising. */ data = readl(bank->reg_base + bank->gpio_regs->ext_port); if (data & mask) @@ -475,26 +489,39 @@ irq_reg_writel(gc, bank->saved_masks, bank->gpio_regs->int_mask); } +static void rockchip_irq_enable(struct irq_data *d) +{ + irq_gc_mask_clr_bit(d); +} + +static void rockchip_irq_disable(struct irq_data *d) +{ + irq_gc_mask_set_bit(d); +} + static int rockchip_interrupts_register(struct rockchip_pin_bank *bank) { unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; struct irq_chip_generic *gc; int ret; - bank->domain = irq_domain_add_linear(bank->of_node, 32, - &irq_generic_chip_ops, NULL); + bank->domain = irq_domain_create_linear(dev_fwnode(bank->dev), 32, + &irq_generic_chip_ops, NULL); if (!bank->domain) { - dev_warn(bank->dev, "could not initialize irq domain for bank %s\n", bank->name); + dev_warn(bank->dev, "could not init irq domain for bank %s\n", + bank->name); return -EINVAL; } ret = irq_alloc_domain_generic_chips(bank->domain, 32, 1, - bank->name, handle_level_irq, + "rockchip_gpio_irq", + handle_level_irq, clr, 0, 0); if (ret) { - dev_err(bank->dev, "could not alloc generic chips for bank %s\n", bank->name); + dev_err(bank->dev, "could not alloc generic chips for bank %s\n", + bank->name); irq_domain_remove(bank->domain); - return ret; + return -EINVAL; } gc = irq_get_domain_generic_chip(bank->domain, 0); @@ -502,6 +529,7 @@ gc->reg_writel = gpio_writel_v2; gc->reg_readl = gpio_readl_v2; } + gc->reg_base = bank->reg_base; gc->private = bank; gc->chip_types[0].regs.mask = bank->gpio_regs->int_mask; @@ -509,8 +537,8 @@ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit; gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit; gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit; - gc->chip_types[0].chip.irq_enable = irq_gc_mask_clr_bit; - gc->chip_types[0].chip.irq_disable = irq_gc_mask_set_bit; + gc->chip_types[0].chip.irq_enable = rockchip_irq_enable; + gc->chip_types[0].chip.irq_disable = rockchip_irq_disable; gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake; gc->chip_types[0].chip.irq_suspend = rockchip_irq_suspend; gc->chip_types[0].chip.irq_resume = rockchip_irq_resume; @@ -545,42 +573,22 @@ gc->ngpio = bank->nr_pins; gc->label = bank->name; gc->parent = bank->dev; -#ifdef CONFIG_OF_GPIO - gc->of_node = of_node_get(bank->of_node); -#endif + + if (!gc->base) + gc->base = GPIO_MAX_PINS * bank->bank_num; + if (!gc->ngpio) + gc->ngpio = GPIO_MAX_PINS; + if (!gc->label) { + gc->label = kasprintf(GFP_KERNEL, "gpio%d", bank->bank_num); + if (!gc->label) + return -ENOMEM; + } ret = gpiochip_add_data(gc, bank); if (ret) { - dev_err(bank->dev, "failed to add gpiochip %s, %d\n", gc->label, ret); + dev_err(bank->dev, "failed to add gpiochip %s, %d\n", + gc->label, ret); return ret; - } - - /* - * For DeviceTree-supported systems, the gpio core checks the - * pinctrl's device node for the "gpio-ranges" property. - * If it is present, it takes care of adding the pin ranges - * for the driver. In this case the driver can skip ahead. - * - * In order to remain compatible with older, existing DeviceTree - * files which don't set the "gpio-ranges" property or systems that - * utilize ACPI the driver has to call gpiochip_add_pin_range(). - */ - if (!of_property_read_bool(bank->of_node, "gpio-ranges")) { - struct device_node *pctlnp = of_get_parent(bank->of_node); - struct pinctrl_dev *pctldev = NULL; - - if (!pctlnp) - return -ENODATA; - - pctldev = of_pinctrl_get(pctlnp); - if (!pctldev) - return -ENODEV; - - ret = gpiochip_add_pin_range(gc, dev_name(pctldev->dev), 0, gc->base, gc->ngpio); - if (ret) { - dev_err(bank->dev, "Failed to add pin range\n"); - goto fail; - } } ret = rockchip_interrupts_register(bank); @@ -597,48 +605,22 @@ return ret; } -static int rockchip_get_bank_data(struct rockchip_pin_bank *bank) +static void rockchip_gpio_get_ver(struct rockchip_pin_bank *bank) { - struct resource res; - int id = 0; - - if (of_address_to_resource(bank->of_node, 0, &res)) - return -ENOENT; - - bank->reg_base = devm_ioremap_resource(bank->dev, &res); - if (IS_ERR(bank->reg_base)) - return PTR_ERR(bank->reg_base); - - bank->irq = irq_of_parse_and_map(bank->of_node, 0); - if (!bank->irq) - return -EINVAL; - - bank->clk = of_clk_get(bank->of_node, 0); - if (IS_ERR(bank->clk)) - return PTR_ERR(bank->clk); - - clk_prepare_enable(bank->clk); - id = readl(bank->reg_base + gpio_regs_v2.version_id); + int id = readl(bank->reg_base + gpio_regs_v2.version_id); /* If not gpio v2, that is default to v1. */ if (id == GPIO_TYPE_V2 || id == GPIO_TYPE_V2_1) { bank->gpio_regs = &gpio_regs_v2; bank->gpio_type = GPIO_TYPE_V2; - bank->db_clk = of_clk_get(bank->of_node, 1); - if (IS_ERR(bank->db_clk)) { - dev_err(bank->dev, "cannot find debounce clk\n"); - bank->db_clk = NULL; - return -EINVAL; - } } else { bank->gpio_regs = &gpio_regs_v1; bank->gpio_type = GPIO_TYPE_V1; } - - return 0; } -static struct rockchip_pin_bank *rockchip_gpio_find_bank(struct pinctrl_dev *pctldev, int id) +static struct rockchip_pin_bank * +rockchip_gpio_find_bank(struct pinctrl_dev *pctldev, int id) { struct rockchip_pinctrl *info; struct rockchip_pin_bank *bank; @@ -656,50 +638,175 @@ return found ? bank : NULL; } +static int rockchip_gpio_of_get_bank_id(struct device *dev) +{ + static int gpio; + int bank_id = -1; + + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { + bank_id = of_alias_get_id(dev->of_node, "gpio"); + if (bank_id < 0) + bank_id = gpio++; + } + + return bank_id; +} + +#ifdef CONFIG_ACPI +static int rockchip_gpio_acpi_get_bank_id(struct device *dev) +{ + struct acpi_device *adev; + unsigned long bank_id = -1; + const char *uid; + int ret; + + adev = ACPI_COMPANION(dev); + if (!adev) + return -ENXIO; + + uid = acpi_device_uid(adev); + if (!uid || !(*uid)) { + dev_err(dev, "Cannot retrieve UID\n"); + return -ENODEV; + } + + ret = kstrtoul(uid, 0, &bank_id); + + return !ret ? bank_id : -ERANGE; +} +#else +static int rockchip_gpio_acpi_get_bank_id(struct device *dev) +{ + return -ENOENT; +} +#endif /* CONFIG_ACPI */ + static int rockchip_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *np = pdev->dev.of_node; - struct device_node *pctlnp = of_get_parent(np); struct pinctrl_dev *pctldev = NULL; struct rockchip_pin_bank *bank = NULL; - static int gpio; - int id, ret; + int bank_id = 0; + int ret; - if (!np || !pctlnp) - return -ENODEV; + bank_id = rockchip_gpio_acpi_get_bank_id(dev); + if (bank_id < 0) { + bank_id = rockchip_gpio_of_get_bank_id(dev); + if (bank_id < 0) + return bank_id; + } - pctldev = of_pinctrl_get(pctlnp); - if (!pctldev) - return -EPROBE_DEFER; + if (!ACPI_COMPANION(dev)) { + struct device_node *pctlnp = of_get_parent(dev->of_node); - id = of_alias_get_id(np, "gpio"); - if (id < 0) - id = gpio++; + pctldev = of_pinctrl_get(pctlnp); + if (!pctldev) + return -EPROBE_DEFER; - bank = rockchip_gpio_find_bank(pctldev, id); - if (!bank) - return -EINVAL; + bank = rockchip_gpio_find_bank(pctldev, bank_id); + if (!bank) + return -ENODEV; + } + if (!bank) { + bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL); + if (!bank) + return -ENOMEM; + } + + bank->bank_num = bank_id; bank->dev = dev; - bank->of_node = dev->of_node; + + bank->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(bank->reg_base)) + return PTR_ERR(bank->reg_base); + + bank->irq = platform_get_irq(pdev, 0); + if (bank->irq < 0) + return bank->irq; raw_spin_lock_init(&bank->slock); - ret = rockchip_get_bank_data(bank); - if (ret) - return ret; + if (!ACPI_COMPANION(dev)) { + bank->clk = devm_clk_get(dev, "bus"); + if (IS_ERR(bank->clk)) { + bank->clk = of_clk_get(dev->of_node, 0); + if (IS_ERR(bank->clk)) { + dev_err(dev, "fail to get apb clock\n"); + return PTR_ERR(bank->clk); + } + } + + bank->db_clk = devm_clk_get(dev, "db"); + if (IS_ERR(bank->db_clk)) { + bank->db_clk = of_clk_get(dev->of_node, 1); + if (IS_ERR(bank->db_clk)) + bank->db_clk = NULL; + } + } + + clk_prepare_enable(bank->clk); + clk_prepare_enable(bank->db_clk); + + rockchip_gpio_get_ver(bank); + + /* + * Prevent clashes with a deferred output setting + * being added right at this moment. + */ + mutex_lock(&bank->deferred_lock); ret = rockchip_gpiolib_register(bank); - if (ret) - goto err_clk; + if (ret) { + dev_err(bank->dev, "Failed to register gpio %d\n", ret); + goto err_unlock; + } + + if (!device_property_read_bool(bank->dev, "gpio-ranges") && pctldev) { + struct gpio_chip *gc = &bank->gpio_chip; + + ret = gpiochip_add_pin_range(gc, dev_name(pctldev->dev), 0, + gc->base, gc->ngpio); + if (ret) { + dev_err(bank->dev, "Failed to add pin range\n"); + goto err_unlock; + } + } + + while (!list_empty(&bank->deferred_pins)) { + struct rockchip_pin_deferred *cfg; + + cfg = list_first_entry(&bank->deferred_pins, + struct rockchip_pin_deferred, head); + if (!cfg) + break; + + list_del(&cfg->head); + + switch (cfg->param) { + case PIN_CONFIG_OUTPUT: + ret = rockchip_gpio_direction_output(&bank->gpio_chip, cfg->pin, cfg->arg); + if (ret) + dev_warn(dev, "setting output pin %u to %u failed\n", cfg->pin, + cfg->arg); + break; + default: + dev_warn(dev, "unknown deferred config param %d\n", cfg->param); + break; + } + kfree(cfg); + } + + mutex_unlock(&bank->deferred_lock); platform_set_drvdata(pdev, bank); - dev_info(dev, "probed %s (%s)\n", bank->name, dev_name(dev)); + dev_info(dev, "probed %pfw\n", dev_fwnode(dev)); return 0; -err_clk: +err_unlock: + mutex_unlock(&bank->deferred_lock); clk_disable_unprepare(bank->clk); + clk_disable_unprepare(bank->db_clk); return ret; } @@ -709,6 +816,7 @@ struct rockchip_pin_bank *bank = platform_get_drvdata(pdev); clk_disable_unprepare(bank->clk); + clk_disable_unprepare(bank->db_clk); gpiochip_remove(&bank->gpio_chip); return 0; -- Gitblit v1.6.2