From patchwork Wed Dec 10 15:56:17 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ricardo Ribalda Delgado X-Patchwork-Id: 419732 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 171D3140079 for ; Thu, 11 Dec 2014 02:56:40 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932261AbaLJP4X (ORCPT ); Wed, 10 Dec 2014 10:56:23 -0500 Received: from mail-lb0-f172.google.com ([209.85.217.172]:38250 "EHLO mail-lb0-f172.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932113AbaLJP4V (ORCPT ); Wed, 10 Dec 2014 10:56:21 -0500 Received: by mail-lb0-f172.google.com with SMTP id u10so2568897lbd.31 for ; Wed, 10 Dec 2014 07:56:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=MUIUlr7OtEMLCe7ZMK1rk72+0clYoI/EOaxtv8l7KE4=; b=IxZybPo0x4jMNFXtWkRL0+PDjVRFyjchL/Yep7mquFSrSyQ0lwfd5JqFDaYMVcdtMT YTfB8/ywin/faOFZLNerIzOVEYO36fjiwESgbB7NrWskVizINsVoh4wLoNQnTUNKdNQP 4WHXDLoGBHO+LA/TL7hgDRBW2MWchUv+ib+/6RpX38Mi7SFd+X3uhkkzEmk6hlRgV8UT gdwXIbcAe60jL4JMLwTRjvtj8M7pwVc6NmjRZnAYt+iqVgNj1MOFyDUUSofpWlei/uPZ 7BgT+v9uMAwTA8f3eLnuvn+8wpPwjXC+7jwKA2tkKqVaIg+yEDU/8v0+mYas+WBofNJL T1KA== X-Received: by 10.152.25.168 with SMTP id d8mr4819213lag.41.1418226979697; Wed, 10 Dec 2014 07:56:19 -0800 (PST) Received: from neopili.qtec.com (cpe.xe-3-0-1-778.vbrnqe10.dk.customer.tdc.net. [80.197.57.18]) by mx.google.com with ESMTPSA id z6sm837228lae.2.2014.12.10.07.56.18 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 10 Dec 2014 07:56:18 -0800 (PST) From: Ricardo Ribalda Delgado To: Linus Walleij , Alexandre Courbot , Michal Simek , =?UTF-8?q?S=C3=B6ren=20Brinkmann?= , linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Ricardo Ribalda Delgado Subject: [PATCH v4 2/4] gpio/xilinx: Convert the driver to platform device interface Date: Wed, 10 Dec 2014 16:56:17 +0100 Message-Id: <1418226977-17128-1-git-send-email-ricardo.ribalda@gmail.com> X-Mailer: git-send-email 2.1.3 Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This way we do not need to transverse the device tree manually and we support hot plugged devices. Also Implement remove callback so the driver can be unloaded Signed-off-by: Ricardo Ribalda Delgado --- v4: Add missing module_exit drivers/gpio/gpio-xilinx.c | 83 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 17 deletions(-) diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index e668ec4..c7ed92b 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -44,12 +44,18 @@ * gpio_state: GPIO state shadow register * gpio_dir: GPIO direction shadow register * gpio_lock: Lock used for synchronization + * inited: True if the port has been inited */ struct xgpio_instance { struct of_mm_gpio_chip mmchip; u32 gpio_state; u32 gpio_dir; spinlock_t gpio_lock; + bool inited; +}; + +struct xgpio { + struct xgpio_instance port[2]; }; /** @@ -172,24 +178,56 @@ static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc) } /** + * xgpio_remove - Remove method for the GPIO device. + * @pdev: pointer to the platform device + * + * This function remove gpiochips and frees all the allocated resources. + */ +static int xgpio_remove(struct platform_device *pdev) +{ + struct xgpio *xgpio = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < 2; i++) { + if (!xgpio->port[i].inited) + continue; + gpiochip_remove(&xgpio->port[i].mmchip.gc); + + if (i == 1) + xgpio->port[i].mmchip.regs -= XGPIO_CHANNEL_OFFSET; + + iounmap(xgpio->port[i].mmchip.regs); + kfree(xgpio->port[i].mmchip.gc.label); + } + + return 0; +} + +/** * xgpio_of_probe - Probe method for the GPIO device. - * @np: pointer to device tree node + * @pdev: pointer to the platform device * * This function probes the GPIO device in the device tree. It initializes the * driver data structure. It returns 0, if the driver is bound to the GPIO * device, or a negative value if there is an error. */ -static int xgpio_of_probe(struct device_node *np) +static int xgpio_probe(struct platform_device *pdev) { + struct xgpio *xgpio; struct xgpio_instance *chip; int status = 0; + struct device_node *np = pdev->dev.of_node; const u32 *tree_info; u32 ngpio; - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (!chip) + xgpio = devm_kzalloc(&pdev->dev, sizeof(*xgpio), GFP_KERNEL); + if (!xgpio) return -ENOMEM; + platform_set_drvdata(pdev, xgpio); + + chip = &xgpio->port[0]; + /* Update GPIO state shadow register with default value */ of_property_read_u32(np, "xlnx,dout-default", &chip->gpio_state); @@ -209,6 +247,7 @@ static int xgpio_of_probe(struct device_node *np) spin_lock_init(&chip->gpio_lock); + chip->mmchip.gc.dev = &pdev->dev; chip->mmchip.gc.direction_input = xgpio_dir_in; chip->mmchip.gc.direction_output = xgpio_dir_out; chip->mmchip.gc.get = xgpio_get; @@ -219,20 +258,18 @@ static int xgpio_of_probe(struct device_node *np) /* Call the OF gpio helper to setup and register the GPIO device */ status = of_mm_gpiochip_add(np, &chip->mmchip); if (status) { - kfree(chip); pr_err("%s: error in probe function with status %d\n", np->full_name, status); return status; } + chip->inited = true; pr_info("XGpio: %s: registered, base is %d\n", np->full_name, chip->mmchip.gc.base); tree_info = of_get_property(np, "xlnx,is-dual", NULL); if (tree_info && be32_to_cpup(tree_info)) { - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; + chip = &xgpio->port[1]; /* Update GPIO state shadow register with default value */ of_property_read_u32(np, "xlnx,dout-default-2", @@ -254,6 +291,7 @@ static int xgpio_of_probe(struct device_node *np) spin_lock_init(&chip->gpio_lock); + chip->mmchip.gc.dev = &pdev->dev; chip->mmchip.gc.direction_input = xgpio_dir_in; chip->mmchip.gc.direction_output = xgpio_dir_out; chip->mmchip.gc.get = xgpio_get; @@ -264,7 +302,7 @@ static int xgpio_of_probe(struct device_node *np) /* Call the OF gpio helper to setup and register the GPIO dev */ status = of_mm_gpiochip_add(np, &chip->mmchip); if (status) { - kfree(chip); + xgpio_remove(pdev); pr_err("%s: error in probe function with status %d\n", np->full_name, status); return status; @@ -272,6 +310,7 @@ static int xgpio_of_probe(struct device_node *np) /* Add dual channel offset */ chip->mmchip.regs += XGPIO_CHANNEL_OFFSET; + chip->inited = true; pr_info("XGpio: %s: dual channel registered, base is %d\n", np->full_name, chip->mmchip.gc.base); @@ -285,19 +324,29 @@ static const struct of_device_id xgpio_of_match[] = { { /* end of list */ }, }; -static int __init xgpio_init(void) -{ - struct device_node *np; +MODULE_DEVICE_TABLE(of, xgpio_of_match); - for_each_matching_node(np, xgpio_of_match) - xgpio_of_probe(np); +static struct platform_driver xgpio_plat_driver = { + .probe = xgpio_probe, + .remove = xgpio_remove, + .driver = { + .name = "gpio-xilinx", + .of_match_table = xgpio_of_match, + }, +}; - return 0; +static int __init xgpio_init(void) +{ + return platform_driver_register(&xgpio_plat_driver); } -/* Make sure we get initialized before anyone else tries to use us */ subsys_initcall(xgpio_init); -/* No exit call at the moment as we cannot unregister of GPIO chips */ + +static void __exit xgpio_exit(void) +{ + platform_driver_unregister(&xgpio_plat_driver); +} +module_exit(xgpio_exit); MODULE_AUTHOR("Xilinx, Inc."); MODULE_DESCRIPTION("Xilinx GPIO driver");