From 51e38fe318c52ae164eb6b4c7951793f7ff108a2 Mon Sep 17 00:00:00 2001 From: vbendeb Date: Fri, 3 Sep 2010 13:03:23 -0700 Subject: [PATCH] CHROMIUM: gpio: nm10: Add ability to change gpio directions. This CL adds nm10_gpio.c driver methods to allow changing of GPIO pin directions. The driver code is being refactored to separate common input parameters check. Once the new methods are added, the gpiolib.c layer adss the appropriate attribute (direction) to every enabled nm10 gpio pin in /sys/class/gpio. Writing "in" or "out" into the direction attribute sets the pin's direction. Change-Id: I4116542c7424dfb7f5c6fdc35930caf0a5d2e11b BUG=chrome-os-partner:970 chromium-os:3612 TEST=see below 1. Install the new driver version on the system 2. Enable all GPIO pins localhost tmp # i=0; while [ "$i" != "64" ] ; do gpio=$(expr $i \+ 192) ; i=$(expr $i \+ 1) ; echo $gpio > /sys/class/gpio/export ; done 2. define a shell function to display gpio pins' status: gpiodump() { i=0; while [ "$i" != "64" ]; do gpio=$(expr $i \+ 192) ; i=$(expr $i \+ 1) ; cat /sys/class/gpio/gpio$gpio/value ; done | awk '{ if (i == 8) {print ""; i = 0;} printf " %s", $1; i = i + 1} END { print "" }' ; } 3. Set the recovery mode pin direction to output: localhost tmp # echo out > /sys/class/gpio/gpio230/direction 4. Run gpiodump with the button pressed and released, observe that there is no difference in the output. 5. Set the recovery mode pin direction to input: localhost tmp # echo in > /sys/class/gpio/gpio230/direction 6. Run gpiodump with the button released and the pressed, observe that there is difference in the output value of pin38: localhost tmp # gpiodump 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 localhost tmp # gpiodump 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Signed-off-by: Vadim Bendebury Review URL: http://codereview.chromium.org/3333016 --- drivers/gpio/nm10_gpio.c | 115 +++++++++++++++++++++++++++++++-------- 1 file changed, 93 insertions(+), 22 deletions(-) diff --git a/drivers/gpio/nm10_gpio.c b/drivers/gpio/nm10_gpio.c index f7e99530dba4..a87854efa0c0 100644 --- a/drivers/gpio/nm10_gpio.c +++ b/drivers/gpio/nm10_gpio.c @@ -95,6 +95,41 @@ struct nm10_gpio { u32 cached_select[NM10_GPIO_SECTIONS]; }; +/** + * nm10_get_parameters() - get value of a GPIO bit + * + * Inputs: + * @chip: generic gpio chip handle associated with this module + * @offset: zero based GPIO bit number (in this controller's scope). + * + * Outputs: + * @psection - pointer to the NM10 section number containing bit offset + * @pbit,- pointer to the offset's bit mask within the section + * @pgpio - pointer to address of this nm10_gpio instance. + * + * Returns zero on errors or nonzero on success. + */ +static int nm10_get_parameters(struct gpio_chip *chip, unsigned offset, + u8* psection, u32* pbit, + struct nm10_gpio **pgpio) +{ + *pgpio = container_of(chip, struct nm10_gpio, chip); + + *psection = offset / NM10_GPIO_BITS_PER_SECTION; + *pbit = BIT(offset % NM10_GPIO_BITS_PER_SECTION); + + if (*psection >= NM10_GPIO_SECTIONS) { + printk(KERN_ERR "%s: bad offset %d\n", + gpio_driver_name, offset); + return 0; + } + + if (!(*pbit & (*pgpio)->cached_select[*psection])) { + return 0; /* this bit is not used for GPIO */ + } + return ~0; +} + /** * nm10_gpio_get() - get value of a GPIO bit * @chip: generic gpio chip handle associated with this module @@ -110,26 +145,19 @@ static int nm10_gpio_get(struct gpio_chip *chip, unsigned offset) u32 bit; struct nm10_gpio *pgpio = container_of(chip, struct nm10_gpio, chip); - section = offset / NM10_GPIO_BITS_PER_SECTION; - bit = BIT(offset % NM10_GPIO_BITS_PER_SECTION); - - if (section >= NM10_GPIO_SECTIONS) { - printk(KERN_ERR "%s: bad offset %d\n", - gpio_driver_name, offset); + if (!nm10_get_parameters(chip, offset, §ion, &bit, &pgpio)) { return 0; } - if (!(bit & pgpio->cached_select[section])) { - return 0; /* this bit is not used for GPIO */ - } return inl(pgpio->io_base + nm10_gpio_sections[section].io_level_offset) & bit; } /** - * nm10_gpio_set() - get value of a GPIO bit + * nm10_gpio_set() - set value of a GPIO bit * @chip: generic gpio chip handle associated with this module * @offset: zero based GPIO bit number (in this controller's scope). + * @value: the value to set the output to * * If the offset is of a valid bit (used for GPIO output) - the bit state is * changed to reflect the value. All other in range offset values are ignored. @@ -139,19 +167,11 @@ static void nm10_gpio_set(struct gpio_chip *chip, unsigned offset, int value) u8 section; u32 bit; const struct nm10_gpio_info *pinfo; - struct nm10_gpio *pgpio = container_of(chip, struct nm10_gpio, chip); + struct nm10_gpio *pgpio; u32 gpio_reg_value; - section = offset / NM10_GPIO_BITS_PER_SECTION; - bit = BIT(offset % NM10_GPIO_BITS_PER_SECTION); - - if (section >= NM10_GPIO_SECTIONS) { - printk(KERN_ERR "%s: bad offset %d\n", - gpio_driver_name, offset); - } - - if (!(bit & pgpio->cached_select[section])) { - return; /* this bit is not used for GPIO */ + if (!nm10_get_parameters(chip, offset, §ion, &bit, &pgpio)) { + return; } pinfo = nm10_gpio_sections + section; @@ -169,8 +189,57 @@ static void nm10_gpio_set(struct gpio_chip *chip, unsigned offset, int value) outl(gpio_reg_value, pgpio->io_base + pinfo->io_level_offset); } +/** + * nm10_gpio_direction_inp() configure signal "offset" as input, or return error + * @chip: generic gpio chip handle associated with this module + * @offset: zero based GPIO bit number (in this controller's scope). + */ +static int nm10_gpio_direction_inp(struct gpio_chip *chip, + unsigned offset) +{ + u8 section; + u32 bit; + struct nm10_gpio *pgpio = container_of(chip, struct nm10_gpio, chip); + u32 io_select_offset; + + if (!nm10_get_parameters(chip, offset, §ion, &bit, &pgpio)) { + return -1; + } + + io_select_offset = pgpio->io_base + + nm10_gpio_sections[section].io_select_offset; + outl(inl(io_select_offset) | bit, io_select_offset); + return 0; +} + +/** + * nm10_gpio_direction_out() configure signal "offset" as output, + * or return error + * @chip: generic gpio chip handle associated with this module + * @offset: zero based GPIO bit number (in this controller's scope). + * @value: the value to set the output to + */ +static int nm10_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + u8 section; + u32 bit; + struct nm10_gpio *pgpio = container_of(chip, struct nm10_gpio, chip); + u32 io_select_offset; + + if (!nm10_get_parameters(chip, offset, §ion, &bit, &pgpio)) { + return -1; + } + + io_select_offset = pgpio->io_base + + nm10_gpio_sections[section].io_select_offset; + outl(inl(io_select_offset) & ~bit, io_select_offset); + nm10_gpio_set(chip, offset, value); + return 0; +} + static int nm10_gpio_probe(struct pci_dev *pdev, - const struct pci_device_id *id) + const struct pci_device_id *id) { int retval, ii; u32 value; @@ -214,6 +283,8 @@ static int nm10_gpio_probe(struct pci_dev *pdev, pgpio->chip.label = dev_name(&pdev->dev); pgpio->chip.get = nm10_gpio_get; pgpio->chip.set = nm10_gpio_set; + pgpio->chip.direction_input = nm10_gpio_direction_inp; + pgpio->chip.direction_output = nm10_gpio_direction_out; pgpio->chip.ngpio = NM10_MAX_GPIO_BITS; pgpio->chip.can_sleep = 0; pci_set_drvdata(pdev, pgpio);