Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

can add feature for gpio-poweroff if kernal without device tree option #9

Closed
junwushi opened this issue Jul 10, 2017 · 8 comments
Closed

Comments

@junwushi
Copy link

the file ./drivers/power/reset/gpio-poweroff.c i check that is only support device tree kennel for define power off GPIO pin,but i am not use device tree to build kernel, i want define it in dev.c. can add this feature?

@junwushi junwushi changed the title can add feature for gpio-poweroff if kenal without device tree option can add feature for gpio-poweroff if kernal without device tree option Jul 10, 2017
@junwushi
Copy link
Author

junwushi commented Jul 11, 2017

other question,please help me check.i want read wake up source register value in user application. but always read value is zero. why?

#define SYS_BA 0xb0000000
#define REG_WKUPSSR 0x5c
unsigned int getwakupsource(void) {
unsigned int value;
int mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
if (mem_fd == -1) {
printf("open /dev/mem fail\n");
return 0;
}
unsigned char virt_regaddr = (unsigned char) mmap(0, 0x1000,
PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, SYS_BA);
if (virt_regaddr == NULL) {
close(mem_fd);
printf("mmap mem fail\n");
return 0;
}
value = ((unsigned int) (virt_regaddr));
printf("PID:0x%x\n", value);
value = *((unsigned int *) (virt_regaddr + REG_WKUPSSR));
munmap(virt_regaddr, 0x1000);
close(mem_fd);
return value;
}

@yachen
Copy link

yachen commented Jul 11, 2017

Hi,

Because the wake up source is cleared in arch/arm/mach-nuc970/pm.c after resume. Please csearch following line in pm.c
__raw_writel(__raw_readl(REG_WKUPSER), REG_WKUPSSR); // clear wake source flag

Sincerely,

Yi-An Chen

@junwushi
Copy link
Author

i think that put it at fisrt line is better in the funcation of pm.c?

@yachen
Copy link

yachen commented Jul 11, 2017

Yup, updated. Relocate the clear wakeup flag code.

@yachen
Copy link

yachen commented Jul 11, 2017

Following code works fine here. Not sure what goes wrong at your environment.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>

#define DEV 	"/dev/mem"
#define BA	0xB0000000
int main(int argc, char **argv)
{
	int offset, fd;
	unsigned char *va;

	if(argc == 1) {
		printf("Usage: mem <offset>\n");
		return 0;
	}

	offset = atol(argv[1]);
	
	fd = open(DEV, O_RDWR | O_SYNC);
	if(fd < 0) {
		printf("Open /dev/mem failed\n");
		return 0;
	}

	va = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, BA);
	if(va == NULL) {
		printf("mmap failed\n");
		return 0;
	}

	printf("=>%08x\n", *(unsigned int volatile *)(va + offset));

	munmap(va, 0x1000);
	close(fd);
}

@junwushi
Copy link
Author

thanks

@yachen
Copy link

yachen commented Jul 12, 2017

If you simply want to use GPIO to detect power off event, you can put it in NUC970 GPIO driver.

@junwushi
Copy link
Author

junwushi commented Jul 12, 2017

no,i want after system shutdown to cut main power of device.so need override pm_power_0ff() funtion.it is lke below

/*

  • Toggles a GPIO pin to power down a device
  • Jamie Lentin jm@lentin.co.uk
  • Andrew Lunn andrew@lunn.ch
  • Copyright (C) 2012 Jamie Lentin
  • This program is free software; you can redistribute it and/or modify
  • it under the terms of the GNU General Public License version 2 as
  • published by the Free Software Foundation.

*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/module.h>

/*

  • Hold configuration here, cannot be more than one instance of the driver
  • since pm_power_off itself is global.
    */
    static int gpio_num = -1;
    //static int gpio_active_low;

static void gpio_poweroff_do_poweroff(void) {
//BUG_ON(!gpio_is_valid(gpio_num));

/* drive it active, also inactive->active edge */
//gpio_direction_output(gpio_num, !gpio_active_low);
//mdelay(100);
/* drive inactive, also active->inactive edge */
//gpio_set_value(gpio_num, gpio_active_low);
//mdelay(100);
/* drive it active, also inactive->active edge */
gpio_direction_output(gpio_num, 1);
//printk("power down now\n");
/* give it some time */

// mdelay(3000);
//WARN_ON(1);
}

static int gpio_poweroff_probe(struct platform_device pdev) {
// enum of_gpio_flags flags;
//bool input = false;
int ret;
//printk("%s:probe1\n",func);
/
If a pm_power_off function has already been added, leave it alone */
if (pm_power_off != NULL) {
pr_err("%s: pm_power_off function already registered", func);
return -EBUSY;
}
gpio_num = NUC970_PE4;
//gpio_num = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
if (!gpio_is_valid(gpio_num))
{
pr_err("%s: gpio num is not valid", func);
return gpio_num;
}
//gpio_active_low = flags & OF_GPIO_ACTIVE_LOW;

//input = of_property_read_bool(pdev->dev.of_node, "input");

ret = gpio_request(gpio_num, NULL);
if (ret) {
	pr_err("%s: Could not get GPIO %d", __func__, gpio_num);
	return ret;
}

if (gpio_direction_output(gpio_num, 0)) {
	pr_err("Could not set direction of GPIO %d", gpio_num);
	goto err;
}

pm_power_off = &gpio_poweroff_do_poweroff;
//printk("%s:probe2\n",__func__);
return 0;

err: gpio_free(gpio_num);
return -ENODEV;

}

static int gpio_poweroff_remove(struct platform_device *pdev) {
gpio_free(gpio_num);
if (pm_power_off == &gpio_poweroff_do_poweroff)
pm_power_off = NULL;

return 0;

}

#if defined(CONFIG_OF)
static const struct of_device_id of_gpio_poweroff_match[] = {
{ .compatible = "poweroff-gpio"},
{ },
};
MODULE_DEVICE_TABLE(of, of_gpio_poweroff_match);
#endif

static struct platform_driver gpio_poweroff_driver = {
.probe = gpio_poweroff_probe,
.remove = gpio_poweroff_remove,
.driver = {
.name = "poweroff-gpio",
.owner = THIS_MODULE,
#if defined(CONFIG_OF)
.of_match_table = of_match_ptr(of_gpio_poweroff_match),
#endif
},
};

module_platform_driver(gpio_poweroff_driver);

MODULE_DESCRIPTION("GPIO poweroff driver");
MODULE_LICENSE("GPL v2");

yachen pushed a commit that referenced this issue Jul 17, 2017
commit 3d46a44a0c01b15d385ccaae24b56f619613c256 upstream.

PID: 614    TASK: ffff882a739da580  CPU: 3   COMMAND: "ocfs2dc"
  #0 [ffff882ecc3759b0] machine_kexec at ffffffff8103b35d
  #1 [ffff882ecc375a20] crash_kexec at ffffffff810b95b5
  #2 [ffff882ecc375af0] oops_end at ffffffff815091d8
  #3 [ffff882ecc375b20] die at ffffffff8101868b
  #4 [ffff882ecc375b50] do_trap at ffffffff81508bb0
  #5 [ffff882ecc375ba0] do_invalid_op at ffffffff810165e5
  #6 [ffff882ecc375c40] invalid_op at ffffffff815116fb
     [exception RIP: ocfs2_ci_checkpointed+208]
     RIP: ffffffffa0a7e940  RSP: ffff882ecc375cf0  RFLAGS: 00010002
     RAX: 0000000000000001  RBX: 000000000000654b  RCX: ffff8812dc83f1f8
     RDX: 00000000000017d9  RSI: ffff8812dc83f1f8  RDI: ffffffffa0b2c318
     RBP: ffff882ecc375d20   R8: ffff882ef6ecfa60   R9: ffff88301f272200
     R10: 0000000000000000  R11: 0000000000000000  R12: ffffffffffffffff
     R13: ffff8812dc83f4f0  R14: 0000000000000000  R15: ffff8812dc83f1f8
     ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
  #7 [ffff882ecc375d28] ocfs2_check_meta_downconvert at ffffffffa0a7edbd [ocfs2]
  #8 [ffff882ecc375d38] ocfs2_unblock_lock at ffffffffa0a84af8 [ocfs2]
  #9 [ffff882ecc375dc8] ocfs2_process_blocked_lock at ffffffffa0a85285 [ocfs2]
assert is tripped because the tran is not checkpointed and the lock level is PR.

Some time ago, chmod command had been executed. As result, the following call
chain left the inode cluster lock in PR state, latter on causing the assert.
system_call_fastpath
  -> my_chmod
   -> sys_chmod
    -> sys_fchmodat
     -> notify_change
      -> ocfs2_setattr
       -> posix_acl_chmod
        -> ocfs2_iop_set_acl
         -> ocfs2_set_acl
          -> ocfs2_acl_set_mode
Here is how.
1119 int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
1120 {
1247         ocfs2_inode_unlock(inode, 1); <<< WRONG thing to do.
..
1258         if (!status && attr->ia_valid & ATTR_MODE) {
1259                 status =  posix_acl_chmod(inode, inode->i_mode);

519 posix_acl_chmod(struct inode *inode, umode_t mode)
520 {
..
539         ret = inode->i_op->set_acl(inode, acl, ACL_TYPE_ACCESS);

287 int ocfs2_iop_set_acl(struct inode *inode, struct posix_acl *acl, ...
288 {
289         return ocfs2_set_acl(NULL, inode, NULL, type, acl, NULL, NULL);

224 int ocfs2_set_acl(handle_t *handle,
225                          struct inode *inode, ...
231 {
..
252                                 ret = ocfs2_acl_set_mode(inode, di_bh,
253                                                          handle, mode);

168 static int ocfs2_acl_set_mode(struct inode *inode, struct buffer_head ...
170 {
183         if (handle == NULL) {
                    >>> BUG: inode lock not held in ex at this point <<<
184                 handle = ocfs2_start_trans(OCFS2_SB(inode->i_sb),
185                                            OCFS2_INODE_UPDATE_CREDITS);

ocfs2_setattr.#1247 we unlock and at #1259 call posix_acl_chmod. When we reach
ocfs2_acl_set_mode.#181 and do trans, the inode cluster lock is not held in EX
mode (it should be). How this could have happended?

We are the lock master, were holding lock EX and have released it in
ocfs2_setattr.#1247.  Note that there are no holders of this lock at
this point.  Another node needs the lock in PR, and we downconvert from
EX to PR.  So the inode lock is PR when do the trans in
ocfs2_acl_set_mode.#184.  The trans stays in core (not flushed to disc).
Now another node want the lock in EX, downconvert thread gets kicked
(the one that tripped assert abovt), finds an unflushed trans but the
lock is not EX (it is PR).  If the lock was at EX, it would have flushed
the trans ocfs2_ci_checkpointed -> ocfs2_start_checkpoint before
downconverting (to NULL) for the request.

ocfs2_setattr must not drop inode lock ex in this code path.  If it
does, takes it again before the trans, say in ocfs2_set_acl, another
cluster node can get in between, execute another setattr, overwriting
the one in progress on this node, resulting in a mode acl size combo
that is a mix of the two.

Orabug: 20189959
Signed-off-by: Tariq Saeed <tariq.x.saeed@oracle.com>
Reviewed-by: Mark Fasheh <mfasheh@suse.de>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Joseph Qi <joseph.qi@huawei.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Willy Tarreau <w@1wt.eu>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants