aboutsummaryrefslogtreecommitdiffstats
path: root/meta-amdfalconx86/recipes-kernel/amd-gpio/files/gpio-amd.c
diff options
context:
space:
mode:
Diffstat (limited to 'meta-amdfalconx86/recipes-kernel/amd-gpio/files/gpio-amd.c')
-rw-r--r--meta-amdfalconx86/recipes-kernel/amd-gpio/files/gpio-amd.c701
1 files changed, 701 insertions, 0 deletions
diff --git a/meta-amdfalconx86/recipes-kernel/amd-gpio/files/gpio-amd.c b/meta-amdfalconx86/recipes-kernel/amd-gpio/files/gpio-amd.c
new file mode 100644
index 00000000..3c43817a
--- /dev/null
+++ b/meta-amdfalconx86/recipes-kernel/amd-gpio/files/gpio-amd.c
@@ -0,0 +1,701 @@
+/*****************************************************************************
+*
+* Copyright (c) 2014, Advanced Micro Devices, Inc.
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+* * Neither the name of Advanced Micro Devices, Inc. nor the names of
+* its contributors may be used to endorse or promote products derived
+* from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+*
+***************************************************************************/
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <asm/io.h>
+
+#include <linux/cdev.h>
+#include <linux/fs.h> /* everything... */
+#include <asm/io.h>
+#include <linux/ioctl.h>
+#include <linux/device.h>
+#include <linux/string.h>
+
+#include "gpio-amd.h"
+
+static u32 gpiobase_phys;
+static u32 iomuxbase_phys;
+static struct pci_dev *amd_gpio_pci;
+static struct platform_device *amd_gpio_platform_device;
+
+
+static int dev_major;
+static int dev_minor = 0;
+
+static struct gpio_test_dev{
+ struct cdev cdev;
+ struct class *gpio_class;
+}gpio_test_dev;
+
+
+/* The following GPIO pins are reserved as per the specification. 184 max */
+static u8 mask[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0 to 11 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 12 to 23 */
+ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 24 to 35 */
+ 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, /* 36 to 47 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 48 to 59 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 to 71 */
+ 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, /* 72 to 83 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, /* 84 to 95 */
+ 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 96 to 107 */
+ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, /* 108 to 119 */
+ 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, /* 120 to 131 */
+ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 132 to 143 */
+ 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, /* 144 to 155 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 156 to 167 */
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, /* 168 to 179 */
+ 1, 1, 1, 1, /* 180 to 183 */
+};
+
+static unsigned char iomux_mode0[] = {3, 4, 5, 6, 7, 8, 9, 11, 40, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 76, 89, 90, 95,
+ 96, 97, 98, 99, 100, '\0'};
+static unsigned char iomux_mode1[] = {1, 2, 10, 13, 14, 15, 21, 22, 24, 25, 26,
+ 39, 42, 74, 75, 84, 85, 86, 87, 88, 91,
+ 93, 101, 102, 115, 116, 117, 118, 120,
+ 121, 122, 126, 129, 130, 133, 135, 136,
+ 137, 138, 139, 140, 141, 142, 143, 144,
+ 145, 146, 147, 148, '\0'};
+static unsigned char iomux_mode2[] = {11, 12, 16, 17, 18, 19, 20, 23, 113, 114,
+ 119, 132, '\0'};
+static unsigned char iomux_mode3[] = {92, 131, '\0'};
+
+static int gpio_mask[AMD_GPIO_NUM_PINS];
+static unsigned int num_mask;
+module_param_array(gpio_mask, int, &num_mask, 0);
+MODULE_PARM_DESC(gpio_mask, "GPIO mask which marks them as reserved");
+
+static int gpio_mode[AMD_GPIO_NUM_PINS];
+static unsigned int num_modes;
+module_param_array(gpio_mode, int, &num_modes, 0);
+MODULE_PARM_DESC(gpio_mode, "Specifies whether the GPIO mentioned "
+ "in gpio_mask is 0-reserved, 1-available, 2-GPI only, "
+ "3-GPO only");
+
+static struct amd_gpio_chip {
+ struct gpio_chip gpio;
+
+ void __iomem *gpiobase;
+ void __iomem *iomuxbase;
+
+ struct platform_device *pdev;
+ spinlock_t lock;
+} amd_gpio_chip;
+
+static int amd_gpio_request(struct gpio_chip *c, unsigned offset)
+{
+ struct amd_gpio_chip *chip = container_of(c, struct amd_gpio_chip,
+ gpio);
+ unsigned long flags;
+ u8 iomux_reg;
+ u32 gpio_reg = 0;
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ /* check if this pin is available */
+ if (mask[offset] == AMD_GPIO_MODE_RESV) {
+ spin_unlock_irqrestore(&chip->lock, flags);
+ pr_info("GPIO pin %u not available\n", offset);
+ return -EINVAL;
+ }
+
+ /* Program the GPIO Wake/Interrupt Switch offset is AMD_GPIO_MSWITCH */
+ gpio_reg = ioread32((u32 *)amd_gpio_chip.gpiobase + AMD_GPIO_MSWITCH);
+ /* to disable all GPIO wake and interrupt*/
+ gpio_reg &= (~AMD_GPIO_WAKE_EN & ~AMD_GPIO_INTERRUPT_EN);
+ iowrite32(gpio_reg, ((u32 *)amd_gpio_chip.gpiobase + AMD_GPIO_MSWITCH));
+
+ gpio_reg = ioread32((u32 *)amd_gpio_chip.gpiobase + offset);
+ /* clear wake status and interrupt status */
+ gpio_reg |= (AMD_GPIO_INTERPT_STATUS | AMD_GPIO_WAKE_STATUS);
+
+ /* Set disable both Pull Up and Pull Down and disable output */
+ gpio_reg &= (~AMD_GPIO_PULLUP_ENABLE & ~AMD_GPIO_PULLDN_ENABLE
+ & ~AMD_GPIO_DEB_CTRL & ~AMD_GPIO_SWCTRL_IN
+ & ~AMD_GPIO_WAKECTRL & ~AMD_GPIO_INTERPT_ENABLE);
+ iowrite32(gpio_reg, ((u32 *)amd_gpio_chip.gpiobase + offset));
+
+ /* Enable GPIO by writing to the corresponding IOMUX register */
+ iomux_reg = ioread8((u8 *)amd_gpio_chip.iomuxbase + offset);
+ iomux_reg &= ~AMD_IOMUX_GPIO_MASK;
+
+ if(offset == 10 || offset == 72 || offset == 73 ||offset == 76)
+ iomux_reg |= AMD_IOMUX_ENABLE_FUNC0;
+ else if(offset == 35 || offset == 64 || offset == 65 ||offset == 66 ||
+ offset == 71 || offset == 93 ||offset == 115 ||offset == 116)
+ iomux_reg |= AMD_IOMUX_ENABLE_FUNC2;
+ else if(offset == 67 || offset == 70)
+ iomux_reg |= AMD_IOMUX_ENABLE_FUNC3;
+ else
+ iomux_reg |= AMD_IOMUX_ENABLE_FUNC1;
+
+ iowrite8(iomux_reg, ((u8 *)amd_gpio_chip.iomuxbase + offset));
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+
+static int amd_gpio_get(struct gpio_chip *c, unsigned offset)
+{
+ struct amd_gpio_chip *chip = container_of(c, struct amd_gpio_chip,
+ gpio);
+ unsigned long flags;
+ u32 gpio_reg;
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ /* Read the GPIO register */
+ gpio_reg = ioread32((u32 *)amd_gpio_chip.gpiobase + offset);
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return (gpio_reg & AMD_GPIO_GET_INPUT) ? 1 : 0;
+}
+
+static void amd_gpio_set(struct gpio_chip *c, unsigned offset, int val)
+{
+ struct amd_gpio_chip *chip = container_of(c, struct amd_gpio_chip,
+ gpio);
+ unsigned long flags;
+ u32 gpio_reg;
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ gpio_reg = ioread32((u32 *)amd_gpio_chip.gpiobase + offset);
+
+ /* Set GPIO Output depending on 'val' */
+ if (val)
+ gpio_reg |= AMD_GPIO_SET_OUTPUT;
+ else
+ gpio_reg &= ~AMD_GPIO_SET_OUTPUT;
+
+ iowrite32(gpio_reg, ((u32 *)amd_gpio_chip.gpiobase + offset));
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int amd_gpio_direction_input(struct gpio_chip *c, unsigned offset)
+{
+ struct amd_gpio_chip *chip = container_of(c, struct amd_gpio_chip,
+ gpio);
+ unsigned long flags;
+ u32 gpio_reg;
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ /* If the mask says the pin should be GPO, we return from here */
+ if (mask[offset] == AMD_GPIO_MODE_OUTPUT) {
+ pr_info("GPIO %u can only be set in output mode\n", offset);
+ spin_unlock_irqrestore(&chip->lock, flags);
+ return -EINVAL;
+ }
+
+ gpio_reg = ioread32((u32 *)amd_gpio_chip.gpiobase + offset);
+ /* Disable output by set the bit to 0 */
+ gpio_reg &= ~AMD_GPIO_OUTPUT_ENABLE;
+ iowrite32(gpio_reg, ((u32 *)amd_gpio_chip.gpiobase + offset));
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+static int amd_gpio_direction_output(struct gpio_chip *c, unsigned offset,
+ int val)
+{
+ struct amd_gpio_chip *chip = container_of(c, struct amd_gpio_chip,
+ gpio);
+ unsigned long flags;
+ u32 gpio_reg;
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ /* If the mask says the pin should be GPI, we return from here */
+ if (mask[offset] == AMD_GPIO_MODE_INPUT) {
+ pr_info("GPIO %u can only be set in input mode\n", offset);
+ spin_unlock_irqrestore(&chip->lock, flags);
+ return -EINVAL;
+ }
+
+ gpio_reg = ioread32((u32 *)amd_gpio_chip.gpiobase + offset);
+
+
+ gpio_reg |= AMD_GPIO_DRV_STRENGTH(2);
+ /* Set disable both Pull Up and Pull Down */
+ gpio_reg &= (~AMD_GPIO_PULLUP_ENABLE & ~AMD_GPIO_PULLDN_ENABLE
+ & ~AMD_GPIO_DEB_CTRL & ~AMD_GPIO_SWCTRL_IN
+ & ~AMD_GPIO_WAKECTRL & ~AMD_GPIO_INTERPT_ENABLE);
+ /* Enable output */
+ gpio_reg |= AMD_GPIO_OUTPUT_ENABLE;
+
+ /* Set GPIO Output depending on 'val' */
+ if (val)
+ gpio_reg |= AMD_GPIO_SET_OUTPUT;
+ else
+ gpio_reg &= ~AMD_GPIO_SET_OUTPUT;
+
+ iowrite32(gpio_reg, ((u32 *)amd_gpio_chip.gpiobase + offset));
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+static struct amd_gpio_chip amd_gpio_chip = {
+ .gpio = {
+ .owner = THIS_MODULE,
+ .label = DRV_NAME,
+
+ .base = 0,
+ .ngpio = AMD_GPIO_NUM_PINS,
+ .names = NULL,
+ .request = amd_gpio_request,
+ .get = amd_gpio_get,
+ .set = amd_gpio_set,
+ .direction_input = amd_gpio_direction_input,
+ .direction_output = amd_gpio_direction_output,
+ },
+};
+
+/*
+* The PCI Device ID table below is used to identify the platform
+* the driver is supposed to work for. Since this is a platform
+* driver, we need a way for us to be able to find the correct
+* platform when the driver gets loaded, otherwise we should
+* bail out.
+*/
+static DEFINE_PCI_DEVICE_TABLE(amd_gpio_pci_tbl) = {
+ { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CARRIZO_SMBUS, PCI_ANY_ID,
+ PCI_ANY_ID, },
+ { 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, amd_gpio_pci_tbl);
+
+static void amd_update_gpio_mask(void)
+{
+ u8 iomux_reg;
+ int i, j;
+ unsigned char *iomux_modes[4] = {iomux_mode0, iomux_mode1,
+ iomux_mode2, iomux_mode3};
+ /*
+ * Some of the GPIO pins have an alternate function assigned to
+ * them. That will be reflected in the corresponding IOMUX
+ * registers. If so, we mark these GPIO pins as reserved.
+ */
+
+ /* GPIO0 */
+ iomux_reg = ioread8((u8 *)amd_gpio_chip.iomuxbase + 0);
+ if (((iomux_reg & AMD_IOMUX_GPIO_MASK) != 0x1) )
+ mask[0] = AMD_GPIO_MODE_RESV;
+
+ /* GPIO1 to GPIO183 */
+ for(j = 0; j < 4; j++) {
+ for(i = 0; i < strlen((char *)iomux_modes[j]); i++) {
+ iomux_reg = ioread8((u8 *)amd_gpio_chip.iomuxbase
+ + iomux_modes[j][i]);
+
+ if (((iomux_reg & AMD_IOMUX_GPIO_MASK) != j))
+ mask[iomux_modes[j][i]] = AMD_GPIO_MODE_RESV;
+ }
+ }
+}
+
+static int amd_gpio_init(struct platform_device *pdev)
+{
+ struct pci_dev *dev = NULL;
+ int i;
+ int err;
+
+ /* Match the PCI device */
+ for_each_pci_dev(dev) {
+ if (pci_match_id(amd_gpio_pci_tbl, dev) != NULL) {
+ amd_gpio_pci = dev;
+ break;
+ }
+ }
+
+ if (!amd_gpio_pci)
+ return -ENODEV;
+
+ /* GPIO registers range from AMD_GPIO_ACPIMMIO_BASE+1500h to
+ AMD_GPIO_ACPIMMIO_BASE+17FFh. */
+ if (!request_mem_region_exclusive(AMD_GPIO_ACPIMMIO_BASE
+ + AMD_GPIO_BANK_OFFSET, AMD_GPIO_MEM_MAP_SIZE, "AMD GPIO")) {
+ pr_err("mmio address 0x%04x already in use\n",
+ AMD_GPIO_ACPIMMIO_BASE + AMD_GPIO_BANK_OFFSET );
+ goto exit;
+ }
+ gpiobase_phys = AMD_GPIO_ACPIMMIO_BASE + AMD_GPIO_BANK_OFFSET;
+
+ amd_gpio_chip.gpiobase = ioremap(gpiobase_phys, AMD_GPIO_MEM_MAP_SIZE);
+ if (!amd_gpio_chip.gpiobase) {
+ pr_err("failed to get gpiobase address\n");
+ goto unreg_gpio_region;
+ }
+
+ /* IOMUX Base Address starts from ACPI MMIO Base Address + 0xD00 */
+ if (!request_mem_region_exclusive(AMD_GPIO_ACPIMMIO_BASE
+ + AMD_IOMUX_MEM_MAP_OFFSET,
+ AMD_IOMUX_MEM_MAP_SIZE, "AMD IOMUX")) {
+ pr_err("mmio address 0x%04x already in use\n",
+ AMD_GPIO_ACPIMMIO_BASE + AMD_IOMUX_MEM_MAP_OFFSET);
+ goto unmap_gpio_region;
+ }
+ iomuxbase_phys = AMD_GPIO_ACPIMMIO_BASE + AMD_IOMUX_MEM_MAP_OFFSET;
+
+ amd_gpio_chip.iomuxbase = ioremap(iomuxbase_phys,
+ AMD_IOMUX_MEM_MAP_SIZE);
+ if (!amd_gpio_chip.iomuxbase) {
+ pr_err("failed to get iomuxbase address\n");
+ goto unreg_iomux_region;
+ }
+
+ /* Set up driver specific struct */
+ amd_gpio_chip.pdev = pdev;
+ spin_lock_init(&amd_gpio_chip.lock);
+
+ /* Register ourself with the GPIO core */
+ err = gpiochip_add(&amd_gpio_chip.gpio);
+ if (err)
+ goto unmap_iomux_region;
+
+ /*
+ * Lets take care of special GPIO pins, and mark them as reserved
+ * as appropriate.
+ */
+ amd_update_gpio_mask();
+
+ /*
+ * If the number of GPIO pins provided during module loading does
+ * not match the number of GPIO modes, we fall back to the default
+ * mask.
+ */
+ if (num_mask == num_modes) {
+ /*
+ * If the number of masks or the number of modes specified
+ * is more than the maximum number of GPIO pins supported
+ * by the driver, we set the limit to the one supported
+ * driver.
+ */
+ if (num_mask > AMD_GPIO_NUM_PINS)
+ num_mask = num_modes = AMD_GPIO_NUM_PINS;
+
+ /*
+ * The default mask is our de facto standard. The GPIO
+ * pins marked reserved in the default mask stay reserved
+ * no matter what the module load parameter says. Also, we
+ * set the mode of the GPIO pins depending on the value
+ * of gpio_mode provided.
+ */
+ for (i = 0; i < num_mask; i++) {
+ if (mask[gpio_mask[i]] != AMD_GPIO_MODE_RESV) {
+ mask[gpio_mask[i]] = gpio_mode[i];
+
+ /*
+ * gpio_request() can fail, in which case we
+ * won't set the GPIO modes.
+ */
+ if(!gpio_request(gpio_mask[i], DRV_NAME)) {
+ if (gpio_mode[i] ==
+ AMD_GPIO_MODE_INPUT)
+ gpio_direction_input(gpio_mask[i]);
+ else if (gpio_mode[i] ==
+ AMD_GPIO_MODE_OUTPUT)
+ gpio_direction_output(gpio_mask[i],
+ 0);
+
+ gpio_free(gpio_mask[i]);
+ }
+ }
+ }
+ }
+
+ return 0;
+
+unmap_iomux_region:
+ iounmap(amd_gpio_chip.iomuxbase);
+unreg_iomux_region:
+ release_mem_region(iomuxbase_phys, AMD_IOMUX_MEM_MAP_SIZE);
+unmap_gpio_region:
+ iounmap(amd_gpio_chip.gpiobase);
+unreg_gpio_region:
+ release_mem_region(gpiobase_phys, AMD_GPIO_MEM_MAP_SIZE);
+exit:
+ return -ENODEV;
+}
+
+static int amd_gpio_remove(struct platform_device *pdev)
+{
+ int err;
+
+ err = gpiochip_remove(&amd_gpio_chip.gpio);
+ if (err) {
+ dev_err(&pdev->dev, "Unable to remove gpio chip\n");
+ return err;
+ }
+
+ iounmap(amd_gpio_chip.iomuxbase);
+ release_mem_region(iomuxbase_phys, AMD_IOMUX_MEM_MAP_SIZE);
+ iounmap(amd_gpio_chip.gpiobase);
+ release_mem_region(gpiobase_phys, AMD_GPIO_MEM_MAP_SIZE);
+
+ return 0;
+}
+
+static struct platform_driver amd_gpio_driver = {
+ .probe = amd_gpio_init,
+ .remove = amd_gpio_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = GPIO_MODULE_NAME,
+ },
+};
+
+/* interface to debug driver for gpio */
+
+static int amd_gpio_swctrlen(int offset, int value)
+{
+ struct amd_gpio_chip *chip = &amd_gpio_chip;
+ u32 gpio_reg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ /* If the mask says the pin should be GPO, we return from here */
+ if (mask[offset] == AMD_GPIO_MODE_RESV) {
+ pr_info("GPIO %u is reserved\n", offset);
+ spin_unlock_irqrestore(&chip->lock, flags);
+ return -EINVAL;
+ }
+
+ if (mask[offset] == AMD_GPIO_MODE_OUTPUT) {
+ pr_info("GPIO %u can only be set in output mode\n", offset);
+ spin_unlock_irqrestore(&chip->lock, flags);
+ return -EINVAL;
+ }
+
+ gpio_reg = ioread32((u32 *)amd_gpio_chip.gpiobase + offset);
+ /* Disable output by set the bit to 0 */
+ gpio_reg &= ~AMD_GPIO_OUTPUT_ENABLE;
+ /* enable or disable sw input */
+ if(value)
+ gpio_reg |= AMD_GPIO_SWCTRL_EN;
+ else
+ gpio_reg &= ~AMD_GPIO_SWCTRL_EN;
+
+ iowrite32(gpio_reg, ((u32 *)amd_gpio_chip.gpiobase + offset));
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+static int amd_gpio_swctrlin(int offset, int value)
+{
+ struct amd_gpio_chip *chip = &amd_gpio_chip;
+ u32 gpio_reg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ /* If the mask says the pin should be GPO, we return from here */
+ if (mask[offset] == AMD_GPIO_MODE_RESV) {
+ pr_info("GPIO %u is reserved\n", offset);
+ spin_unlock_irqrestore(&chip->lock, flags);
+ return -EINVAL;
+ }
+
+ if (mask[offset] == AMD_GPIO_MODE_OUTPUT) {
+ pr_info("GPIO %u can only be set in input mode\n", offset);
+ spin_unlock_irqrestore(&chip->lock, flags);
+ return -EINVAL;
+ }
+
+ gpio_reg = ioread32((u32 *)amd_gpio_chip.gpiobase + offset);
+ /* Disable output by set the bit to 0 */
+ gpio_reg &= ~AMD_GPIO_OUTPUT_ENABLE;
+ /* enable or disable sw input */
+ if(value)
+ gpio_reg |= AMD_GPIO_SWCTRL_IN;
+ else
+ gpio_reg &= ~AMD_GPIO_SWCTRL_IN;
+
+ iowrite32(gpio_reg, ((u32 *)amd_gpio_chip.gpiobase + offset));
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+
+static int gpio_test_open(struct inode *inode, struct file *filp)
+{
+ struct gpio_test_dev *dev; /* device information */
+
+ dev = container_of(inode->i_cdev, struct gpio_test_dev, cdev);
+ filp->private_data = dev; /* for other methods */
+
+ return 0; /* success */
+}
+
+static int gpio_test_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static long gpio_test_ioctl(struct file *filp,
+ unsigned cmd, unsigned long arg)
+{
+
+ debug_data tmp;
+ unsigned long retval = 0;
+
+ /* Check type and command number */
+ if (_IOC_TYPE(cmd) != GPIO_TEST_IOC_MAGIC)
+ return -ENOTTY;
+
+ retval = copy_from_user(&tmp, (void __user *)arg, sizeof(debug_data));
+ if(retval != 0)
+ return 0;
+
+ switch(cmd) {
+ case GPIO_IOC_SWCTRLIN:
+ retval = amd_gpio_swctrlin(tmp.offset,tmp.value);
+ break;
+ case GPIO_IOC_SWCTRLEN:
+ retval = amd_gpio_swctrlen(tmp.offset,tmp.value);
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ return retval;
+}
+
+struct file_operations gpio_test_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = gpio_test_ioctl,
+ .open = gpio_test_open,
+ .release = gpio_test_release,
+};
+
+
+static int __init amd_gpio_init_module(void)
+{
+ int err;
+ dev_t dev = 0;
+ struct device *gpio_device = 0;
+
+ pr_info("AMD GPIO Driver v%s\n", GPIO_VERSION);
+
+ err = platform_driver_register(&amd_gpio_driver);
+ if (err)
+ return err;
+
+ err = alloc_chrdev_region(&dev, dev_minor, 1,"gpio_test_driver");
+ if (err < 0) {
+ printk(KERN_WARNING " can't get major %d\n", dev_major);
+ goto unreg_platform_driver;
+ }
+
+ gpio_test_dev.gpio_class = class_create(THIS_MODULE, "gpio_test_driver");
+ if (IS_ERR(gpio_test_dev.gpio_class)) {
+ printk(" error in class create \n");
+ goto unregister_test_driver;
+ }
+
+ dev_major = MAJOR(dev);
+
+ gpio_device = device_create(gpio_test_dev.gpio_class, NULL, dev, NULL,
+ "gpio_test_driver");
+ if (IS_ERR(gpio_device)) {
+ printk(" error in device create \n");
+ goto destroy_class;
+ }
+
+ cdev_init(&gpio_test_dev.cdev, &gpio_test_fops);
+ err = cdev_add (&gpio_test_dev.cdev, dev, 1);
+ if(err)
+ goto destroy_device;
+
+ amd_gpio_platform_device = platform_device_register_simple(
+ GPIO_MODULE_NAME, -1, NULL, 0);
+ if (IS_ERR(amd_gpio_platform_device)) {
+ err = PTR_ERR(amd_gpio_platform_device);
+ goto cdev_delete;
+ }
+
+ return 0;
+
+cdev_delete:
+ cdev_del(&gpio_test_dev.cdev);
+destroy_device:
+ device_destroy(gpio_test_dev.gpio_class, dev);
+destroy_class:
+ class_destroy(gpio_test_dev.gpio_class);
+unregister_test_driver:
+ unregister_chrdev_region(dev, 1);
+unreg_platform_driver:
+ platform_driver_unregister(&amd_gpio_driver);
+ return err;
+}
+
+static void __exit amd_gpio_cleanup_module(void)
+{
+ dev_t dev = MKDEV(dev_major, dev_minor);
+
+ device_destroy(gpio_test_dev.gpio_class, dev);
+ class_destroy(gpio_test_dev.gpio_class);
+ cdev_del(&gpio_test_dev.cdev);
+ unregister_chrdev_region(dev, 1);
+ platform_device_unregister(amd_gpio_platform_device);
+ platform_driver_unregister(&amd_gpio_driver);
+ pr_info("AMD GPIO Module Unloaded\n");
+}
+
+module_init(amd_gpio_init_module);
+module_exit(amd_gpio_cleanup_module);
+
+
+
+MODULE_AUTHOR("Sudheesh Mavila <sudheesh.mavila@amd.com>");
+MODULE_AUTHOR("Sanjay Mehta <sanju.mehta@amd.com>");
+MODULE_DESCRIPTION("GPIO driver for AMD chipsets");
+MODULE_LICENSE("Dual BSD/GPL");