diff options
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.c | 701 |
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"); |