From patchwork Wed Dec 10 13:21:49 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: 419659 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 56AC71400B7 for ; Thu, 11 Dec 2014 00:23:55 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756579AbaLJNWh (ORCPT ); Wed, 10 Dec 2014 08:22:37 -0500 Received: from mail-lb0-f182.google.com ([209.85.217.182]:54690 "EHLO mail-lb0-f182.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756248AbaLJNV5 (ORCPT ); Wed, 10 Dec 2014 08:21:57 -0500 Received: by mail-lb0-f182.google.com with SMTP id f15so2424538lbj.41 for ; Wed, 10 Dec 2014 05:21:56 -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:in-reply-to:references; bh=o6Ie3wpKoPjQ4oO62FnObvoIm76Sgb5Ldkkgt4DXkGs=; b=irBKBPEaTTEGC+xLzWWnuLHLhvEGjaA33kAxeYa2/y/lKNBhZSaMsZn+3ptB2DsHiN VqT9R7383ughJoIstEkK4yFsBHepMaDN6ALyN9gsA19UzUOmvRqfPbvdYRqjaaZTULAZ IVqK0Jnn3uYQ6EL6coH4XR6sKEoKeCXSJjDmJG1yhv7u3WeC1E0T6dBIJNo7t5EQyedb bVZdOoHfhMUgzODdQ2flGotTBmKRRj0qLO9AVrZI8XH68YpLJZmeOzCFhcOflBXS3uRR YjVGTBvjYJcfG3oN82SB3pSb+CTZEKz+rm7ExbLVEd3yuZ+ti+5ZaNfZZNFpkRutTGpZ Wfjg== X-Received: by 10.112.199.40 with SMTP id jh8mr4103213lbc.5.1418217716229; Wed, 10 Dec 2014 05:21:56 -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 xs6sm1239210lbb.13.2014.12.10.05.21.54 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 10 Dec 2014 05:21:54 -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 v2 2/3] gpio/xilinx: Convert the driver to platform device interface Date: Wed, 10 Dec 2014 14:21:49 +0100 Message-Id: <1418217710-11987-3-git-send-email-ricardo.ribalda@gmail.com> X-Mailer: git-send-email 2.1.3 In-Reply-To: <1418217710-11987-1-git-send-email-ricardo.ribalda@gmail.com> References: <1418217710-11987-1-git-send-email-ricardo.ribalda@gmail.com> 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 --- v2: Suggested by Alexandre Courbot Merge convert to platform device and implement remove callback in one patch drivers/gpio/gpio-xilinx.c | 79 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 20 deletions(-) diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 9483950..f946d96 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -51,6 +51,11 @@ struct xgpio_instance { u32 gpio_state; u32 gpio_dir; spinlock_t gpio_lock; + bool inited; +}; + +struct xgpio { + struct xgpio_instance port[2]; }; /** @@ -173,24 +178,57 @@ 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 = (struct 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); @@ -210,6 +248,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; @@ -220,20 +259,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", @@ -255,6 +292,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; @@ -265,7 +303,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; @@ -273,6 +311,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); @@ -286,19 +325,19 @@ static const struct of_device_id xgpio_of_match[] = { { /* end of list */ }, }; -static int __init xgpio_init(void) -{ - struct device_node *np; - - for_each_matching_node(np, xgpio_of_match) - xgpio_of_probe(np); +MODULE_DEVICE_TABLE(of, xgpio_of_match); - return 0; -} +static struct platform_driver xgpio_plat_driver = { + .probe = xgpio_probe, + .remove = xgpio_remove, + .driver = { + .name = "gpio-xilinx", + .owner = THIS_MODULE, + .of_match_table = xgpio_of_match, + }, +}; -/* 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 */ +module_platform_driver(xgpio_plat_driver); MODULE_AUTHOR("Xilinx, Inc."); MODULE_DESCRIPTION("Xilinx GPIO driver");